mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-21 13:45:44 +01:00
commit
1d0515a957
2
dist/engine.bundle.js
vendored
2
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
80
dist/vendor.bundle.js
vendored
80
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -29,7 +29,7 @@ List of all Source-Files
|
||||
| | * Each level of this Source-File opens up more of the Singularity Functions to use |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-5: Artificial Intelligence | * Unlocks :ref:`gameplay_intelligence` |
|
||||
| | * Unlocks getBitNodeMultipliers() Netscript function |
|
||||
| | * Unlocks :js:func:`getBitNodeMultipliers` Netscript function |
|
||||
| | * Increases all of the player's hacking-related multipliers by 8%/12%/14% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-6: Bladeburners | * Unlocks the Bladeburner feature in other BitNodes |
|
||||
|
@ -76,3 +76,135 @@ Notes
|
||||
^^^^^
|
||||
|
||||
* The *scp* Terminal command does not work on Coding Contracts
|
||||
|
||||
List of all Problem Types
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following is a list of all of the problem types that a Coding Contract can contain.
|
||||
The list contains the name of (i.e. the value returned by
|
||||
:js:func:`getContractType`) and a brief summary of the problem it poses.
|
||||
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Name | Problem Summary |
|
||||
+====================================+==========================================================================================+
|
||||
| Find Largest Prime Factor | | Given a number, find its largest prime factor. A prime factor |
|
||||
| | | is a factor that is a prime number. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Subarray with Maximum Sum | | Given an array of integers, find the contiguous subarray (containing |
|
||||
| | | at least one number) which has the largest sum and return that sum. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Total Ways to Sum | | Given a number, how many different ways can that number be written as |
|
||||
| | | a sum of at least two positive integers? |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Spiralize Matrix | | Given an array of array of numbers representing a 2D matrix, return the |
|
||||
| | | elements of that matrix in clockwise spiral order. |
|
||||
| | | |
|
||||
| | | Example: The spiral order of |
|
||||
| | | |
|
||||
| | | [1, 2, 3, 4] |
|
||||
| | | [5, 6, 7, 8] |
|
||||
| | | [9, 10, 11, 12] |
|
||||
| | | |
|
||||
| | | is [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7] |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Array Jumping Game | | You are given an array of integers where each element represents the |
|
||||
| | | maximum possible jump distance from that position. For example, if you |
|
||||
| | | are at position i and your maximum jump length is n, then you can jump |
|
||||
| | | to any position from i to i+n. |
|
||||
| | | |
|
||||
| | | Assuming you are initially positioned at the start of the array, determine |
|
||||
| | | whether you are able to reach the last index of the array EXACTLY. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Merge Overlapping Intervals | | Given an array of intervals, merge all overlapping intervals. An interval |
|
||||
| | | is an array with two numbers, where the first number is always less than |
|
||||
| | | the second (e.g. [1, 5]). |
|
||||
| | | |
|
||||
| | | The intervals must be returned in ASCENDING order. |
|
||||
| | | |
|
||||
| | | Example: |
|
||||
| | | [[1, 3], [8, 10], [2, 6], [10, 16]] |
|
||||
| | | merges into [[1, 6], [8, 16]] |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Generate IP Addresses | | Given a string containing only digits, return an array with all possible |
|
||||
| | | valid IP address combinations that can be created from the string. |
|
||||
| | | |
|
||||
| | | An octet in the IP address cannot begin with '0' unless the number itself |
|
||||
| | | is actually 0. For example, "192.168.010.1" is NOT a valid IP. |
|
||||
| | | |
|
||||
| | | Examples: |
|
||||
| | | 25525511135 -> [255.255.11.135, 255.255.111.35] |
|
||||
| | | 1938718066 -> [193.87.180.66] |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Algorithmic Stock Trader I | | You are given an array of numbers representing stock prices, where the |
|
||||
| | | i-th element represents the stock price on day i. |
|
||||
| | | |
|
||||
| | | Determine the maximum possible profit you can earn using at most one |
|
||||
| | | transaction (i.e. you can buy an sell the stock once). If no profit |
|
||||
| | | can be made, then the answer should be 0. Note that you must buy the stock |
|
||||
| | | before you can sell it. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Algorithmic Stock Trader II | | You are given an array of numbers representing stock prices, where the |
|
||||
| | | i-th element represents the stock price on day i. |
|
||||
| | | |
|
||||
| | | Determine the maximum possible profit you can earn using as many transactions |
|
||||
| | | as you'd like. A transaction is defined as buying and then selling one |
|
||||
| | | share of the stock. Note that you cannot engage in multiple transactions at |
|
||||
| | | once. In other words, you must sell the stock before you buy it again. If no |
|
||||
| | | profit can be made, then the answer should be 0. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Algorithmic Stock Trader III | | You are given an array of numbers representing stock prices, where the |
|
||||
| | | i-th element represents the stock price on day i. |
|
||||
| | | |
|
||||
| | | Determine the maximum possible profit you can earn using at most two |
|
||||
| | | transactions. A transaction is defined as buying and then selling one share |
|
||||
| | | of the stock. Note that you cannot engage in multiple transactions at once. |
|
||||
| | | In other words, you must sell the stock before you buy it again. If no profit |
|
||||
| | | can be made, then the answer should be 0. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Algorithmic Stock Trader IV | | You are given an array with two elements. The first element is an integer k. |
|
||||
| | | The second element is an array of numbers representing stock prices, where the |
|
||||
| | | i-th element represents the stock price on day i. |
|
||||
| | | |
|
||||
| | | Determine the maximum possible profit you can earn using at most k transactions. |
|
||||
| | | A transaction is defined as buying and then selling one share of the stock. |
|
||||
| | | Note that you cannot engage in multiple transactions at once. In other words, |
|
||||
| | | you must sell the stock before you can buy it. If no profit can be made, then |
|
||||
| | | the answer should be 0. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Minimum Path Sum in a Triangle | | You are given a 2D array of numbers (array of array of numbers) that represents a |
|
||||
| | | triangle (the first array has one element, and each array has one more element than |
|
||||
| | | the one before it, forming a triangle). Find the minimum path sum from the top to the |
|
||||
| | | bottom of the triangle. In each step of the path, you may only move to adjacent |
|
||||
| | | numbers in the row below. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Unique Paths in a Grid I | | You are given an array with two numbers: [m, n]. These numbers represent a |
|
||||
| | | m x n grid. Assume you are initially positioned in the top-left corner of that |
|
||||
| | | grid and that you are trying to reach the bottom-right corner. On each step, |
|
||||
| | | you may only move down or to the right. |
|
||||
| | | |
|
||||
| | |
|
||||
| | | Determine how many unique paths there are from start to finish. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Unique Paths in a Grid II | | You are given a 2D array of numbers (array of array of numbers) representing |
|
||||
| | | a grid. The 2D array contains 1's and 0's, where 1 represents an obstacle and |
|
||||
| | |
|
||||
| | | 0 represents a free space. |
|
||||
| | | |
|
||||
| | | Assume you are initially positioned in top-left corner of that grid and that you |
|
||||
| | | are trying to reach the bottom-right corner. In each step, you may only move down |
|
||||
| | | or to the right. Furthermore, you cannot move onto spaces which have obstacles. |
|
||||
| | | |
|
||||
| | | Determine how many unique paths there are from start to finish. |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
| Sanitize Parentheses in Expression | | Given a string with parentheses and letters, remove the minimum number of invalid |
|
||||
| | | parentheses in order to validate the string. If there are multiple minimal ways |
|
||||
| | | to validate the string, provide all of the possible results. |
|
||||
| | | |
|
||||
| | | The answer should be provided as an array of strings. If it is impossible to validate |
|
||||
| | | the string, the result should be an array with only an empty string. |
|
||||
| | | |
|
||||
| | | Examples: |
|
||||
| | | ()())() -> ["()()()", "(())()"] |
|
||||
| | | (a)())() -> ["(a)()()", "(a())()"] |
|
||||
| | | )( -> [""] |
|
||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||
|
@ -454,3 +454,48 @@ do not allow cross-origin origin sharing (CORS). This includes websites such
|
||||
as gist and pastebin. One notable site it will work on is rawgithub. Example::
|
||||
|
||||
$ wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt
|
||||
|
||||
Argument Parsing
|
||||
----------------
|
||||
When evaluating a terminal command, arguments are initially parsed based on whitespace (usually spaces).
|
||||
Each whitespace character signifies the end of an argument, and potentially the start
|
||||
of new one. For most terminal commands, this is all you need to know.
|
||||
|
||||
When running scripts, however, it is important to know in more detail how arguments are parsed.
|
||||
There are two main points:
|
||||
|
||||
1. Quotation marks can be used to wrap a single argument and force it to be parsed as
|
||||
a string. Any whitespace inside the quotation marks will not cause a new argument
|
||||
to be parsed.
|
||||
2. Anything that can represent a number is automatically cast to a number, unless its
|
||||
surrounded by quotation marks.
|
||||
|
||||
Here's an example to show how these rules work. Consider the following script `argType.script`::
|
||||
|
||||
tprint("Number of args: " + args.length);
|
||||
for (var i = 0; i < args.length; ++i) {
|
||||
tprint(typeof args[i]);
|
||||
}
|
||||
|
||||
Then if we run the following terminal command::
|
||||
|
||||
$ run argType.script 123 1e3 "5" "this is a single argument"
|
||||
|
||||
We'll see the following in the Terminal::
|
||||
|
||||
Running script with 1 thread(s) and args: [123, 1000, "5", "this is a single argument"].
|
||||
May take a few seconds to start up the process...
|
||||
argType.script: Number of args: 4
|
||||
argType.script: number
|
||||
argType.script: number
|
||||
argType.script: string
|
||||
argType.script: string
|
||||
|
||||
Chaining Commands
|
||||
-----------------
|
||||
You can run multiple Terminal commands at once by separating each command
|
||||
with a semicolon (;).
|
||||
|
||||
Example::
|
||||
|
||||
$ run foo.script; tail foo.script
|
||||
|
@ -3,6 +3,28 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v0.43.1 - 2/11/2019
|
||||
-------------------
|
||||
* Terminal changes:
|
||||
* Quoted arguments are now properly parsed. (e.g. 'run f.script "this is one argument"' will be correctly parsed)
|
||||
* Errors are now shown in red text
|
||||
* 'unalias' command now has a different format and no longer needs the quotations
|
||||
* Bug Fix: Fixed several edge cases where autocomplete wasn't working properly
|
||||
|
||||
* Added two new Bladeburner skills for increasing money and experience gain
|
||||
* Made some minor adjustments to Bladeburner UI
|
||||
* Corporation "Smart Factories" and "Smart Storage" upgrades have slightly lower price multipliers
|
||||
* Added nFormat Netscript function
|
||||
* Added 6 new Coding Contract problems
|
||||
* Updated documentation with list of all Coding Contract problems
|
||||
* Minor improvements for 'Active Scripts' UI
|
||||
* Implemented several optimizations for active scripts. The game should now use less memory and the savefile should be slightly smaller when there are many scripts running
|
||||
* Bug Fix: A Stock Forecast should no longer go above 1 (i.e. 100%)
|
||||
* Bug Fix: The cost of Resleeves should no longer be affected by buying Augs
|
||||
* Bug Fix: Duplicate Sleeves now use their own stats to determine crime success rate, instead of the host consciousness' stats
|
||||
* Bug Fix: You can now call the prompt() Netscript function from multiple scripts simultaneously
|
||||
|
||||
|
||||
v0.43.0 - 2/4/2019
|
||||
------------------
|
||||
|
||||
|
@ -1211,6 +1211,26 @@ vsprintf
|
||||
|
||||
See `this link <https://github.com/alexei/sprintf.js>`_ for details.
|
||||
|
||||
nFormat
|
||||
^^^^^^^
|
||||
|
||||
.. js:function:: nFormat(n, format)
|
||||
|
||||
:param number n: Number to format
|
||||
:param string format: Formatter
|
||||
|
||||
Converts a number into a string with the specified formatter. This uses the
|
||||
`numeraljs <http://numeraljs.com/>`_ library, so the formatters must be compatible
|
||||
with that.
|
||||
|
||||
This is the same function that the game itself uses to display numbers.
|
||||
|
||||
Examples::
|
||||
|
||||
nFormat(1.23e9, "$0.000a"); // Returns "$1.230b"
|
||||
nFormat(12345.678, "0,0"); // Returns "12,346"
|
||||
nFormat(0.84, "0.0%"); // Returns "84.0%
|
||||
|
||||
prompt
|
||||
^^^^^^
|
||||
|
||||
|
@ -312,6 +312,9 @@ getStockForecast
|
||||
that the stock's price has a 30% chance of increasing and a 70% chance of
|
||||
decreasing during the next tick.
|
||||
|
||||
In order to use this function, you must first purchase access to the Four Sigma (4S)
|
||||
Market Data TIX API.
|
||||
|
||||
purchase4SMarketData
|
||||
--------------------
|
||||
|
||||
|
@ -209,7 +209,19 @@ to specify a namespace for the import::
|
||||
//...
|
||||
}
|
||||
|
||||
Note that exporting functions is not required.
|
||||
.. warning:: For those who are experienced with JavaScript, note that the `export`
|
||||
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
|
||||
-------------------------------------
|
||||
|
@ -13,7 +13,6 @@
|
||||
"ajv-keywords": "^2.0.0",
|
||||
"async": "^2.6.1",
|
||||
"autosize": "^4.0.2",
|
||||
"bluebird": "^3.5.1",
|
||||
"brace": "^0.11.1",
|
||||
"codemirror": "^5.43.0",
|
||||
"decimal.js": "7.2.3",
|
||||
|
@ -1,21 +1,22 @@
|
||||
import {workerScripts,
|
||||
killWorkerScript} from "./NetscriptWorker";
|
||||
import {Player} from "./Player";
|
||||
import {getServer} from "./Server";
|
||||
import {numeralWrapper} from "./ui/numeralFormat";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
|
||||
import {arrayToString} from "../utils/helpers/arrayToString";
|
||||
import {createElement} from "../utils/uiHelpers/createElement";
|
||||
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
|
||||
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
|
||||
import {getElementById} from "../utils/uiHelpers/getElementById";
|
||||
import {logBoxCreate} from "../utils/LogBox";
|
||||
import {formatNumber} from "../utils/StringHelperFunctions";
|
||||
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
import {removeElement} from "../utils/uiHelpers/removeElement";
|
||||
import {roundToTwo} from "../utils/helpers/roundToTwo";
|
||||
import {Page, routing} from "./ui/navigationTracking";
|
||||
killWorkerScript} from "./NetscriptWorker";
|
||||
import {Player} from "./Player";
|
||||
import {getServer} from "./Server";
|
||||
import {numeralWrapper} from "./ui/numeralFormat";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
|
||||
import {arrayToString} from "../utils/helpers/arrayToString";
|
||||
import {createElement} from "../utils/uiHelpers/createElement";
|
||||
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
|
||||
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
|
||||
import {getElementById} from "../utils/uiHelpers/getElementById";
|
||||
import {logBoxCreate} from "../utils/LogBox";
|
||||
import {formatNumber,
|
||||
convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
import {removeElement} from "../utils/uiHelpers/removeElement";
|
||||
import {roundToTwo} from "../utils/helpers/roundToTwo";
|
||||
import {Page, routing} from "./ui/navigationTracking";
|
||||
|
||||
/* {
|
||||
* serverName: {
|
||||
@ -166,7 +167,7 @@ function addActiveScriptsItem(workerscript) {
|
||||
margin: "4px",
|
||||
padding: "4px",
|
||||
clickListener: () => {
|
||||
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.scriptRef.server);
|
||||
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.server);
|
||||
dialogBoxCreate("Killing script, may take a few minutes to complete...");
|
||||
return false;
|
||||
}
|
||||
@ -242,9 +243,9 @@ function updateActiveScriptsItems(maxTasks=150) {
|
||||
}
|
||||
}
|
||||
|
||||
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.format(total, '$0.000a');
|
||||
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.format(Player.scriptProdSinceLastAug, '$0.000a');
|
||||
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.format(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000), '$0.000a');
|
||||
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.formatMoney(total);
|
||||
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug);
|
||||
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
|
||||
return total;
|
||||
}
|
||||
|
||||
@ -252,7 +253,7 @@ function updateActiveScriptsItems(maxTasks=150) {
|
||||
function updateActiveScriptsItemContent(workerscript) {
|
||||
var server = getServer(workerscript.serverIp);
|
||||
if (server == null) {
|
||||
console.log("ERROR: Invalid server IP for workerscript.");
|
||||
console.log("ERROR: Invalid server IP for workerscript in updateActiveScriptsItemContent().");
|
||||
return;
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
@ -280,7 +281,7 @@ function updateActiveScriptsItemContent(workerscript) {
|
||||
function updateActiveScriptsText(workerscript, item, itemName) {
|
||||
var server = getServer(workerscript.serverIp);
|
||||
if (server == null) {
|
||||
console.log("ERROR: Invalid server IP for workerscript.");
|
||||
console.log("ERROR: Invalid server IP for workerscript for updateActiveScriptsText()");
|
||||
return;
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
@ -298,24 +299,27 @@ function updateActiveScriptsText(workerscript, item, itemName) {
|
||||
|
||||
removeChildrenFromElement(item);
|
||||
|
||||
//Online
|
||||
var onlineTotalMoneyMade = "Total online production: $" + formatNumber(workerscript.scriptRef.onlineMoneyMade, 2);
|
||||
var onlineTotalExpEarned = (Array(26).join(" ") + formatNumber(workerscript.scriptRef.onlineExpGained, 2) + " hacking exp").replace( / /g, " ");
|
||||
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
|
||||
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
|
||||
|
||||
var onlineMpsText = "Online production rate: $" + formatNumber(onlineMps, 2) + "/second";
|
||||
//Online
|
||||
var onlineTotalMoneyMade = "Total online production: " + numeralWrapper.formatMoney(workerscript.scriptRef.onlineMoneyMade);
|
||||
var onlineTotalExpEarned = (Array(26).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.onlineExpGained) + " hacking exp").replace( / /g, " ");
|
||||
|
||||
var onlineMpsText = "Online production rate: " + numeralWrapper.formatMoney(onlineMps) + " / second";
|
||||
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
|
||||
var onlineEpsText = (Array(25).join(" ") + formatNumber(onlineEps, 4) + " hacking exp/second").replace( / /g, " ");
|
||||
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ");
|
||||
|
||||
//Offline
|
||||
var offlineTotalMoneyMade = "Total offline production: $" + formatNumber(workerscript.scriptRef.offlineMoneyMade, 2);
|
||||
var offlineTotalExpEarned = (Array(27).join(" ") + formatNumber(workerscript.scriptRef.offlineExpGained, 2) + " hacking exp").replace( / /g, " ");
|
||||
var offlineTotalMoneyMade = "Total offline production: " + numeralWrapper.formatMoney(workerscript.scriptRef.offlineMoneyMade);
|
||||
var offlineTotalExpEarned = (Array(27).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.offlineExpGained) + " hacking exp").replace( / /g, " ");
|
||||
|
||||
var offlineMps = workerscript.scriptRef.offlineMoneyMade / workerscript.scriptRef.offlineRunningTime;
|
||||
var offlineMpsText = "Offline production rate: $" + formatNumber(offlineMps, 2) + "/second";
|
||||
var offlineMpsText = "Offline production rate: " + numeralWrapper.formatMoney(offlineMps) + " / second";
|
||||
var offlineEps = workerscript.scriptRef.offlineExpGained / workerscript.scriptRef.offlineRunningTime;
|
||||
var offlineEpsText = (Array(26).join(" ") + formatNumber(offlineEps, 4) + " hacking exp/second").replace( / /g, " ");
|
||||
var offlineEpsText = (Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second").replace( / /g, " ");
|
||||
|
||||
item.innerHTML = onlineTotalMoneyMade + "<br>" + onlineTotalExpEarned + "<br>" +
|
||||
item.innerHTML = onlineTime + "<br>" + offlineTime + "<br>" + onlineTotalMoneyMade + "<br>" + onlineTotalExpEarned + "<br>" +
|
||||
onlineMpsText + "<br>" + onlineEpsText + "<br>" + offlineTotalMoneyMade + "<br>" + offlineTotalExpEarned + "<br>" +
|
||||
offlineMpsText + "<br>" + offlineEpsText + "<br>";
|
||||
return onlineMps;
|
||||
|
@ -90,7 +90,10 @@ function removeAlias(name) {
|
||||
//Aliases only applied to "whole words", one level deep
|
||||
function substituteAliases(origCommand) {
|
||||
var commandArray = origCommand.split(" ");
|
||||
if (commandArray.length>0){
|
||||
if (commandArray.length > 0){
|
||||
// For the unalias command, dont substite
|
||||
if (commandArray[0] === "unalias") { return commandArray.join(" "); }
|
||||
|
||||
var alias = getAlias(commandArray[0]);
|
||||
if (alias != null) {
|
||||
commandArray[0] = alias;
|
||||
|
@ -78,6 +78,9 @@ export class Augmentation {
|
||||
// The Player/Person classes
|
||||
mults: IMap<number> = {}
|
||||
|
||||
// Initial cost. Doesn't change when you purchase multiple Augmentation
|
||||
startingCost: number = 0;
|
||||
|
||||
constructor(params: IConstructorParams={ info: "", moneyCost: 0, name: "", repCost: 0 }) {
|
||||
this.name = params.name;
|
||||
this.info = params.info;
|
||||
@ -85,6 +88,7 @@ export class Augmentation {
|
||||
|
||||
this.baseRepRequirement = params.repCost * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
|
||||
this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||
this.startingCost = this.baseCost;
|
||||
|
||||
this.level = 0;
|
||||
|
||||
|
@ -364,6 +364,11 @@ function initBitNodeMultipliers() {
|
||||
var inc = Math.pow(1.02, sf12Lvl);
|
||||
var dec = 1/inc;
|
||||
BitNodeMultipliers.HackingLevelMultiplier = dec;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = dec;
|
||||
BitNodeMultipliers.DefenseLevelMultiplier = dec;
|
||||
BitNodeMultipliers.DexterityLevelMultiplier = dec;
|
||||
BitNodeMultipliers.AgilityLevelMultiplier = dec;
|
||||
BitNodeMultipliers.CharismaLevelMultiplier = dec;
|
||||
|
||||
BitNodeMultipliers.ServerMaxMoney = dec;
|
||||
BitNodeMultipliers.ServerStartingMoney = dec;
|
||||
@ -373,6 +378,10 @@ function initBitNodeMultipliers() {
|
||||
//Does not scale, otherwise security might start at 300+
|
||||
BitNodeMultipliers.ServerStartingSecurity = 1.5;
|
||||
|
||||
BitNodeMultipliers.PurchasedServerCost = inc;
|
||||
BitNodeMultipliers.PurchasedServerLimit = dec;
|
||||
BitNodeMultipliers.PurchasedServerMaxRam = dec;
|
||||
|
||||
BitNodeMultipliers.ManualHackMoney = dec;
|
||||
BitNodeMultipliers.ScriptHackMoney = dec;
|
||||
BitNodeMultipliers.CompanyWorkMoney = dec;
|
||||
|
@ -156,7 +156,6 @@ $(document).keydown(function(event) {
|
||||
if (!(Player.bladeburner instanceof Bladeburner)) {return;}
|
||||
let consoleHistory = Player.bladeburner.consoleHistory;
|
||||
|
||||
//NOTE: Keycodes imported from Terminal.js
|
||||
if (event.keyCode === KEY.ENTER) {
|
||||
event.preventDefault();
|
||||
var command = DomElems.consoleInput.value;
|
||||
@ -355,6 +354,8 @@ function Skill(params={name:"foo", desc:"foo"}) {
|
||||
if (params.effCha) {this.effCha = params.effCha;}
|
||||
|
||||
if (params.stamina) {this.stamina = params.stamina;}
|
||||
if (params.money) {this.money = params.money;}
|
||||
if (params.expGain) {this.expGain = params.expGain;}
|
||||
|
||||
//Equipment
|
||||
if (params.weaponAbility) {this.weaponAbility = params.weaponAbility;}
|
||||
@ -367,17 +368,19 @@ Skill.prototype.calculateCost = function(currentLevel) {
|
||||
var Skills = {};
|
||||
var SkillNames = {
|
||||
BladesIntuition: "Blade's Intuition",
|
||||
Reaper: "Reaper",
|
||||
Cloak: "Cloak",
|
||||
Marksman: "Marksman",
|
||||
WeaponProficiency: "Weapon Proficiency",
|
||||
Overclock: "Overclock",
|
||||
EvasiveSystem: "Evasive System",
|
||||
ShortCircuit: "Short-Circuit",
|
||||
DigitalObserver: "Digital Observer",
|
||||
Datamancer: "Datamancer",
|
||||
Tracer: "Tracer",
|
||||
CybersEdge: "Cyber's Edge"
|
||||
Overclock: "Overclock",
|
||||
Reaper: "Reaper",
|
||||
EvasiveSystem: "Evasive System",
|
||||
Datamancer: "Datamancer",
|
||||
CybersEdge: "Cyber's Edge",
|
||||
HandsOfMidas: "Hands of Midas",
|
||||
Hyperdrive: "Hyperdrive",
|
||||
}
|
||||
|
||||
//Base Class for Contracts, Operations, and BlackOps
|
||||
@ -998,23 +1001,25 @@ Bladeburner.prototype.getCurrentCity = function() {
|
||||
|
||||
Bladeburner.prototype.resetSkillMultipliers = function() {
|
||||
this.skillMultipliers = {
|
||||
successChanceAll:1,
|
||||
successChanceStealth:1,
|
||||
successChanceKill:1,
|
||||
successChanceContract:1,
|
||||
successChanceOperation:1,
|
||||
successChanceEstimate:1,
|
||||
actionTime:1,
|
||||
effHack:1,
|
||||
effStr:1,
|
||||
effDef:1,
|
||||
effDex:1,
|
||||
effAgi:1,
|
||||
effCha:1,
|
||||
effInt:1,
|
||||
stamina:1,
|
||||
weaponAbility:1,
|
||||
gunAbility:1,
|
||||
successChanceAll: 1,
|
||||
successChanceStealth: 1,
|
||||
successChanceKill: 1,
|
||||
successChanceContract: 1,
|
||||
successChanceOperation: 1,
|
||||
successChanceEstimate: 1,
|
||||
actionTime: 1,
|
||||
effHack: 1,
|
||||
effStr: 1,
|
||||
effDef: 1,
|
||||
effDex: 1,
|
||||
effAgi: 1,
|
||||
effCha: 1,
|
||||
effInt: 1,
|
||||
stamina: 1,
|
||||
money: 1,
|
||||
expGain: 1,
|
||||
weaponAbility: 1,
|
||||
gunAbility: 1,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1197,7 +1202,7 @@ Bladeburner.prototype.completeAction = function() {
|
||||
//Earn money for contracts
|
||||
var moneyGain = 0;
|
||||
if (!isOperation) {
|
||||
moneyGain = ContractBaseMoneyGain * rewardMultiplier;
|
||||
moneyGain = ContractBaseMoneyGain * rewardMultiplier * this.skillMultipliers.money;
|
||||
Player.gainMoney(moneyGain);
|
||||
}
|
||||
|
||||
@ -1526,13 +1531,14 @@ Bladeburner.prototype.gainActionStats = function(action, success) {
|
||||
|
||||
var unweightedGain = time * BaseStatGain * successMult * difficultyMult;
|
||||
var unweightedIntGain = time * BaseIntGain * successMult * difficultyMult;
|
||||
Player.gainHackingExp(unweightedGain * action.weights.hack * Player.hacking_exp_mult);
|
||||
Player.gainStrengthExp(unweightedGain * action.weights.str * Player.strength_exp_mult);
|
||||
Player.gainDefenseExp(unweightedGain * action.weights.def * Player.defense_exp_mult);
|
||||
Player.gainDexterityExp(unweightedGain * action.weights.dex * Player.dexterity_exp_mult);
|
||||
Player.gainAgilityExp(unweightedGain * action.weights.agi * Player.agility_exp_mult);
|
||||
Player.gainCharismaExp(unweightedGain * action.weights.cha * Player.charisma_exp_mult);
|
||||
Player.gainIntelligenceExp(unweightedIntGain * action.weights.int);
|
||||
const skillMult = this.skillMultipliers.expGain;
|
||||
Player.gainHackingExp(unweightedGain * action.weights.hack * Player.hacking_exp_mult * skillMult);
|
||||
Player.gainStrengthExp(unweightedGain * action.weights.str * Player.strength_exp_mult * skillMult);
|
||||
Player.gainDefenseExp(unweightedGain * action.weights.def * Player.defense_exp_mult * skillMult);
|
||||
Player.gainDexterityExp(unweightedGain * action.weights.dex * Player.dexterity_exp_mult * skillMult);
|
||||
Player.gainAgilityExp(unweightedGain * action.weights.agi * Player.agility_exp_mult * skillMult);
|
||||
Player.gainCharismaExp(unweightedGain * action.weights.cha * Player.charisma_exp_mult * skillMult);
|
||||
Player.gainIntelligenceExp(unweightedIntGain * action.weights.int * skillMult);
|
||||
}
|
||||
|
||||
Bladeburner.prototype.randomEvent = function() {
|
||||
@ -2193,6 +2199,12 @@ Bladeburner.prototype.createSkillsContent = function() {
|
||||
case "stamina":
|
||||
DomElems.actionsAndSkillsDesc.innerHTML += "Stamina: x" + mult + "<br>";
|
||||
break;
|
||||
case "money":
|
||||
DomElems.actionsAndSkillsDesc.innerHTML += "Contract Money: x" + mult + "<br>";
|
||||
break;
|
||||
case "expGain":
|
||||
DomElems.actionsAndSkillsDesc.innerHTML += "Exp Gain: x" + mult + "<br>";
|
||||
break;
|
||||
case "weaponAbility":
|
||||
//DomElems.actionsAndSkillsDesc.innerHTML +=
|
||||
break;
|
||||
@ -2862,12 +2874,23 @@ Bladeburner.prototype.parseCommandArguments = function(command) {
|
||||
//Returns an array with command and its arguments in each index.
|
||||
//e.g. skill "blade's intuition" foo returns [skill, blade's intuition, foo]
|
||||
//The input to this fn will be trimmed and will have all whitespace replaced w/ a single space
|
||||
var args = [];
|
||||
var start = 0, i = 0;
|
||||
const args = [];
|
||||
let start = 0, i = 0;
|
||||
while (i < command.length) {
|
||||
var c = command.charAt(i);
|
||||
if (c === '"') {
|
||||
var endQuote = command.indexOf('"', i+1);
|
||||
const c = command.charAt(i);
|
||||
if (c === '"') { // Double quotes
|
||||
const endQuote = command.indexOf('"', i+1);
|
||||
if (endQuote !== -1 && (endQuote === command.length-1 || command.charAt(endQuote+1) === " ")) {
|
||||
args.push(command.substr(i+1, (endQuote - i - 1)));
|
||||
if (endQuote === command.length-1) {
|
||||
start = i = endQuote+1;
|
||||
} else {
|
||||
start = i = endQuote+2; //Skip the space
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (c === "'") { // Single quotes, same thing as above
|
||||
const endQuote = command.indexOf("'", i+1);
|
||||
if (endQuote !== -1 && (endQuote === command.length-1 || command.charAt(endQuote+1) === " ")) {
|
||||
args.push(command.substr(i+1, (endQuote - i - 1)));
|
||||
if (endQuote === command.length-1) {
|
||||
@ -2884,7 +2907,7 @@ Bladeburner.prototype.parseCommandArguments = function(command) {
|
||||
++i;
|
||||
}
|
||||
if (start !== i) {args.push(command.substr(start, i-start));}
|
||||
console.log("Bladeburner.parseCommandArguments returned: " + args);
|
||||
console.log("Bladeburner console command parsing returned: " + args);
|
||||
return args;
|
||||
}
|
||||
|
||||
@ -3417,12 +3440,12 @@ Bladeburner.prototype.startActionNetscriptFn = function(type, name, workerScript
|
||||
try {
|
||||
this.startAction(actionId);
|
||||
if (workerScript.shouldLog("startAction")) {
|
||||
workerScript.scriptRef.log("Starting bladeburner action with type " + type + " and name " + name);
|
||||
workerScript.log("Starting bladeburner action with type " + type + " and name " + name);
|
||||
}
|
||||
return true;
|
||||
} catch(e) {
|
||||
this.resetAction();
|
||||
workerScript.scriptRef.log("ERROR: bladeburner.startAction() failed to start action of type " + type + " due to invalid name: " + name +
|
||||
workerScript.log("ERROR: bladeburner.startAction() failed to start action of type " + type + " due to invalid name: " + name +
|
||||
"Note that this name is case-sensitive and whitespace-sensitive");
|
||||
return false;
|
||||
}
|
||||
@ -3764,13 +3787,6 @@ function initBladeburner() {
|
||||
baseCost:5, costInc:2,
|
||||
successChanceAll:3
|
||||
});
|
||||
Skills[SkillNames.Reaper] = new Skill({
|
||||
name:SkillNames.Reaper,
|
||||
desc:"Each level of this skill increases your " +
|
||||
"effective combat stats for Bladeburner actions by 3%",
|
||||
baseCost:3, costInc:2,
|
||||
effStr:3, effDef:3, effDex:3, effAgi:3
|
||||
});
|
||||
Skills[SkillNames.Cloak] = new Skill({
|
||||
name:SkillNames.Cloak,
|
||||
desc:"Each level of this skill increases your " +
|
||||
@ -3782,20 +3798,6 @@ function initBladeburner() {
|
||||
//TODO Marksman
|
||||
//TODO Weapon Proficiency
|
||||
|
||||
Skills[SkillNames.Overclock] = new Skill({
|
||||
name:SkillNames.Overclock,
|
||||
desc:"Each level of this skill decreases the time it takes " +
|
||||
"to attempt a Contract, Operation, and BlackOp by 1% (Max Level: 95)",
|
||||
baseCost:5, costInc:1.1, maxLvl:95,
|
||||
actionTime:1
|
||||
});
|
||||
Skills[SkillNames.EvasiveSystem] = new Skill({
|
||||
name:SkillNames.EvasiveSystem,
|
||||
desc:"Each level of this skill increases your effective " +
|
||||
"dexterity and agility for Bladeburner actions by 5%",
|
||||
baseCost:2, costInc: 1,
|
||||
effDex:5, effAgi:5
|
||||
});
|
||||
Skills[SkillNames.ShortCircuit] = new Skill({
|
||||
name:SkillNames.ShortCircuit,
|
||||
desc:"Each level of this skill increases your success chance " +
|
||||
@ -3810,15 +3812,6 @@ function initBladeburner() {
|
||||
baseCost:5, costInc:2,
|
||||
successChanceOperation:4
|
||||
});
|
||||
Skills[SkillNames.Datamancer] = new Skill({
|
||||
name:SkillNames.Datamancer,
|
||||
desc:"Each level of this skill increases your effectiveness in " +
|
||||
"synthoid population analysis and investigation by 5%. " +
|
||||
"This affects all actions that can potentially increase " +
|
||||
"the accuracy of your synthoid population/community estimates.",
|
||||
baseCost:3,costInc:1,
|
||||
successChanceEstimate:5
|
||||
});
|
||||
Skills[SkillNames.Tracer] = new Skill({
|
||||
name:SkillNames.Tracer,
|
||||
desc:"Each level of this skill increases your success chance in " +
|
||||
@ -3826,13 +3819,53 @@ function initBladeburner() {
|
||||
baseCost:3, costInc:2,
|
||||
successChanceContract:4
|
||||
});
|
||||
Skills[SkillNames.Overclock] = new Skill({
|
||||
name:SkillNames.Overclock,
|
||||
desc:"Each level of this skill decreases the time it takes " +
|
||||
"to attempt a Contract, Operation, and BlackOp by 1% (Max Level: 95)",
|
||||
baseCost:4, costInc:1.1, maxLvl:95,
|
||||
actionTime:1
|
||||
});
|
||||
Skills[SkillNames.Reaper] = new Skill({
|
||||
name:SkillNames.Reaper,
|
||||
desc:"Each level of this skill increases your effective combat stats for Bladeburner actions by 3%",
|
||||
baseCost:3, costInc:2,
|
||||
effStr:3, effDef:3, effDex:3, effAgi:3
|
||||
});
|
||||
Skills[SkillNames.EvasiveSystem] = new Skill({
|
||||
name:SkillNames.EvasiveSystem,
|
||||
desc:"Each level of this skill increases your effective " +
|
||||
"dexterity and agility for Bladeburner actions by 5%",
|
||||
baseCost:2, costInc: 1,
|
||||
effDex:5, effAgi:5
|
||||
});
|
||||
Skills[SkillNames.Datamancer] = new Skill({
|
||||
name:SkillNames.Datamancer,
|
||||
desc:"Each level of this skill increases your effectiveness in " +
|
||||
"synthoid population analysis and investigation by 5%. " +
|
||||
"This affects all actions that can potentially increase " +
|
||||
"the accuracy of your synthoid population/community estimates.",
|
||||
baseCost:3, costInc:1,
|
||||
successChanceEstimate:5
|
||||
});
|
||||
Skills[SkillNames.CybersEdge] = new Skill({
|
||||
name:SkillNames.CybersEdge,
|
||||
desc:"Each level of this skill increases your max " +
|
||||
"stamina by 2%",
|
||||
desc:"Each level of this skill increases your max stamina by 2%",
|
||||
baseCost:1, costInc:3,
|
||||
stamina:2
|
||||
});
|
||||
Skills[SkillNames.HandsOfMidas] = new Skill({
|
||||
name: SkillNames.HandsOfMidas,
|
||||
desc: "Each level of this skill increases the amount of money you receive from Contracts by 5%",
|
||||
baseCost: 2, costInc: 2.5,
|
||||
money: 5,
|
||||
});
|
||||
Skills[SkillNames.Hyperdrive] = new Skill({
|
||||
name: SkillNames.Hyperdrive,
|
||||
desc: "Each level of this skill increases the experience earned from Contracts, Operations, and BlackOps by 4%",
|
||||
baseCost: 1, costInc: 3,
|
||||
expGain: 4,
|
||||
});
|
||||
|
||||
//General Actions
|
||||
var actionName = "Training";
|
||||
|
@ -10,9 +10,7 @@ import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
|
||||
export function generateRandomContract() {
|
||||
// First select a random problem type
|
||||
const problemTypes = Object.keys(CodingContractTypes);
|
||||
let randIndex = getRandomInt(0, problemTypes.length - 1);
|
||||
let problemType = problemTypes[randIndex];
|
||||
let problemType = getRandomProblemType();
|
||||
|
||||
// Then select a random reward type. 'Money' will always be the last reward type
|
||||
const reward = getRandomReward();
|
||||
@ -26,6 +24,22 @@ export function generateRandomContract() {
|
||||
randServer.addContract(contract);
|
||||
}
|
||||
|
||||
export function generateRandomContractOnHome() {
|
||||
// First select a random problem type
|
||||
let problemType = getRandomProblemType();
|
||||
|
||||
// Then select a random reward type. 'Money' will always be the last reward type
|
||||
const reward = getRandomReward();
|
||||
|
||||
// Choose random server
|
||||
const serv = Player.getHomeComputer();
|
||||
|
||||
let contractFn = getRandomFilename(serv, reward);
|
||||
let contract = new CodingContract(contractFn, problemType, reward);
|
||||
|
||||
serv.addContract(contract);
|
||||
}
|
||||
|
||||
export function generateContract(params) {
|
||||
// Problem Type
|
||||
let problemType;
|
||||
@ -33,8 +47,7 @@ export function generateContract(params) {
|
||||
if (params.problemType != null && problemTypes.includes(params.problemType)) {
|
||||
problemType = params.problemType;
|
||||
} else {
|
||||
let randIndex = getRandomInt(0, problemTypes.length - 1);
|
||||
problemType = problemTypes[randIndex];
|
||||
problemType = getRandomProblemType();
|
||||
}
|
||||
|
||||
// Reward Type - This is always random for now
|
||||
@ -91,6 +104,13 @@ function sanitizeRewardType(rewardType) {
|
||||
return type;
|
||||
}
|
||||
|
||||
function getRandomProblemType() {
|
||||
const problemTypes = Object.keys(CodingContractTypes);
|
||||
let randIndex = getRandomInt(0, problemTypes.length - 1);
|
||||
|
||||
return problemTypes[randIndex];
|
||||
}
|
||||
|
||||
function getRandomReward() {
|
||||
let reward = {};
|
||||
reward.type = getRandomInt(0, CodingContractRewardType.Money);
|
||||
|
@ -1,9 +1,17 @@
|
||||
import { codingContractTypesMetadata,
|
||||
DescriptionFunc,
|
||||
GeneratorFunc,
|
||||
SolverFunc } from "./data/codingcontracttypes";
|
||||
|
||||
import { IMap } from "./types";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { KEY } from "../utils/helpers/keyCodes";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc } from "./data/codingcontracttypes";
|
||||
import { IMap } from "./types";
|
||||
|
||||
|
||||
|
||||
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
|
||||
|
||||
@ -171,18 +179,22 @@ export class CodingContract {
|
||||
const contractType: CodingContractType = CodingContractTypes[this.type];
|
||||
const popupId: string = `coding-contract-prompt-popup-${this.fn}`;
|
||||
const txt: HTMLElement = createElement("p", {
|
||||
innerText: ["You are attempting to solve a Coding Contract. You have",
|
||||
innerHTML: ["You are attempting to solve a Coding Contract. You have",
|
||||
`${this.getMaxNumTries() - this.tries} tries remaining,`,
|
||||
"after which the contract will self-destruct.\n\n",
|
||||
`${contractType.desc(this.data)}`].join(" "),
|
||||
"after which the contract will self-destruct.<br><br>",
|
||||
`${contractType.desc(this.data).replace(/\n/g, "<br>")}`].join(" "),
|
||||
});
|
||||
let answerInput: HTMLInputElement;
|
||||
let solveBtn: HTMLElement;
|
||||
let cancelBtn: HTMLElement;
|
||||
answerInput = createElement("input", {
|
||||
onkeydown: (e: any) => {
|
||||
if (e.keyCode === 13 && answerInput.value !== "") {
|
||||
if (e.keyCode === KEY.ENTER && answerInput.value !== "") {
|
||||
e.preventDefault();
|
||||
solveBtn.click();
|
||||
} else if (e.keyCode === KEY.ESC) {
|
||||
e.preventDefault();
|
||||
cancelBtn.click();
|
||||
}
|
||||
},
|
||||
placeholder: "Enter Solution here",
|
||||
@ -200,7 +212,7 @@ export class CodingContract {
|
||||
},
|
||||
innerText: "Solve",
|
||||
});
|
||||
const cancelBtn: HTMLElement = createElement("a", {
|
||||
cancelBtn = createElement("a", {
|
||||
class: "a-link-button",
|
||||
clickListener: () => {
|
||||
resolve(CodingContractResult.Cancelled);
|
||||
|
@ -8,6 +8,7 @@ import { IMap } from "../../types";
|
||||
const AllSoftwarePositions: IMap<boolean> = {};
|
||||
const AllITPositions: IMap<boolean> = {};
|
||||
const AllNetworkEngineerPositions: IMap<boolean> = {};
|
||||
const SecurityEngineerPositions: IMap<boolean> = {};
|
||||
const AllTechnologyPositions: IMap<boolean> = {};
|
||||
const AllBusinessPositions: IMap<boolean> = {};
|
||||
const AllAgentPositions: IMap<boolean> = {};
|
||||
@ -40,6 +41,7 @@ posNames.NetworkEngineerCompanyPositions.forEach((e) => {
|
||||
});
|
||||
|
||||
AllTechnologyPositions[posNames.SecurityEngineerCompanyPositions[0]] = true;
|
||||
SecurityEngineerPositions[posNames.SecurityEngineerCompanyPositions[0]] = true;
|
||||
|
||||
posNames.BusinessCompanyPositions.forEach((e) => {
|
||||
AllBusinessPositions[e] = true;
|
||||
@ -373,6 +375,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
AllNetworkEngineerPositions,
|
||||
SecurityEngineerPositions,
|
||||
AllITPositions,
|
||||
AllSecurityPositions,
|
||||
AllAgentPositions
|
||||
@ -387,6 +390,7 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
companyPositions: Object.assign({},
|
||||
SoftwarePositionsUpToHeadOfEngineering,
|
||||
AllNetworkEngineerPositions,
|
||||
SecurityEngineerPositions,
|
||||
AllITPositions,
|
||||
AllSecurityPositions,
|
||||
AllAgentPositions
|
||||
@ -492,8 +496,9 @@ export const companiesMetadata: IConstructorParams[] = [
|
||||
info: "",
|
||||
companyPositions: Object.assign({},
|
||||
AllTechnologyPositions,
|
||||
AllSoftwareConsultantPositions,
|
||||
AllAgentPositions,
|
||||
AllSecurityPositions
|
||||
AllSecurityPositions,
|
||||
),
|
||||
expMultiplier: 1.2,
|
||||
salaryMultiplier: 1.2,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {IMap} from "./types";
|
||||
|
||||
export let CONSTANTS: IMap<any> = {
|
||||
Version: "0.43.0",
|
||||
Version: "0.43.1",
|
||||
|
||||
//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
|
||||
@ -510,27 +510,25 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.43.0
|
||||
* Added BitNode-10: Digital Carbon
|
||||
v0.43.1
|
||||
* Terminal changes:
|
||||
** Quoted arguments are now properly parsed. (e.g. 'run f.script "this is one argument"' will be correctly parsed)
|
||||
** Errors are now shown in red text
|
||||
** 'unalias' command now has a different format and no longer needs the quotations
|
||||
** Bug Fix: Fixed several edge cases where autocomplete wasnt working properly
|
||||
|
||||
* Stock Market Changes:
|
||||
** Each stock now has a maximum number of shares you can purchase (both Long and Short positions combined)
|
||||
** Added getStockMaxShares() Netscript function to the TIX API
|
||||
** The cost of 4S Market Data TIX API Access increased from $20b to $25b
|
||||
|
||||
* Job Changes:
|
||||
** You can now hold multiple jobs at once. This means you no longer lose reputation when leaving a company
|
||||
** Because of this change, the getCharacterInformation() Netscript function returns a slightly different value
|
||||
|
||||
* Script Editor Changes:
|
||||
** Added new script editor: CodeMirror. You can choose between the old editor (Ace) or CodeMirror
|
||||
** Navigation keyboard shortcuts no longer work if the script editor is focused
|
||||
|
||||
* Trying to programmatically run a script (run(), exec()) with a 'threads' argument of 0 will now cause the function to return false without running the script
|
||||
* Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB)
|
||||
* The maximum amount, maximum RAM, and cost of purchasing servers can now vary between different BitNodes (new BitNode multipliers)
|
||||
* Pop-up dialog boxes are a little bit bigger
|
||||
* Bug Fix: When importing scripts, "./" will now be properly ignored (e.g. import { foo } from "./lib.script" )
|
||||
* Added two new Bladeburner skills for increasing money and experience gain
|
||||
* Made some minor adjustments to Bladeburner UI
|
||||
* Corporation "Smart Factories" and "Smart Storage" upgrades have slightly lower price multipliers
|
||||
* Added nFormat Netscript function
|
||||
* Added 6 new Coding Contract problems
|
||||
* Updated documentation with list of all Coding Contract problems
|
||||
* Minor improvements for 'Active Scripts' UI
|
||||
* Implemented several optimizations for active scripts. The game should now use less memory and the savefile should be slightly smaller when there are many scripts running
|
||||
* Bug Fix: A Stock Forecast should no longer go above 1 (i.e. 100%)
|
||||
* Bug Fix: The cost of Resleeves should no longer be affected by buying Augs
|
||||
* Bug Fix: Duplicate Sleeves now use their own stats to determine crime success rate, instead of the host consciousness' stats
|
||||
* Bug Fix: You can now call the prompt() Netscript function from multiple scripts simultaneously
|
||||
`
|
||||
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ import { IMap } from "../../types";
|
||||
// [index in Corporation upgrades array, base price, price mult, benefit mult (additive), name, desc]
|
||||
export const CorporationUpgrades: IMap<any[]> = {
|
||||
//Smart factories, increases production
|
||||
"0": [0, 2e9, 1.07, 0.03,
|
||||
"0": [0, 2e9, 1.06, 0.03,
|
||||
"Smart Factories", "Advanced AI automatically optimizes the operation and productivity " +
|
||||
"of factories. Each level of this upgrade increases your global production by 3% (additive)."],
|
||||
|
||||
//Smart warehouses, increases storage size
|
||||
"1": [1, 2e9, 1.07, .1,
|
||||
"1": [1, 2e9, 1.06, .1,
|
||||
"Smart Storage", "Advanced AI automatically optimizes your warehouse storage methods. " +
|
||||
"Each level of this upgrade increases your global warehouse storage size by 10% (additive)."],
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve";
|
||||
|
||||
export interface IConstructorParams {
|
||||
hacking_success_weight?: number;
|
||||
strength_success_weight?: number;
|
||||
@ -17,29 +20,6 @@ export interface IConstructorParams {
|
||||
kills?: number;
|
||||
}
|
||||
|
||||
interface IPlayer {
|
||||
startCrime(crimeType: string,
|
||||
hackExp: number,
|
||||
strExp: number,
|
||||
defExp: number,
|
||||
dexExp: number,
|
||||
agiExp: number,
|
||||
chaExp: number,
|
||||
money: number,
|
||||
time: number,
|
||||
singParams: any): void;
|
||||
|
||||
hacking_skill: number;
|
||||
strength: number;
|
||||
defense: number;
|
||||
dexterity: number;
|
||||
agility: number;
|
||||
charisma: number;
|
||||
intelligence: number;
|
||||
|
||||
crime_success_mult: number;
|
||||
}
|
||||
|
||||
export class Crime {
|
||||
// Number representing the difficulty of the crime. Used for success chance calculations
|
||||
difficulty: number = 0;
|
||||
@ -129,7 +109,7 @@ export class Crime {
|
||||
return this.time;
|
||||
}
|
||||
|
||||
successRate(p: IPlayer): number {
|
||||
successRate(p: IPlayerOrSleeve): number {
|
||||
let chance: number = (this.hacking_success_weight * p.hacking_skill +
|
||||
this.strength_success_weight * p.strength +
|
||||
this.defense_success_weight * p.defense +
|
||||
|
@ -1,23 +1,26 @@
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { generateRandomContract } from "./CodingContractGenerator";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { Player } from "./Player";
|
||||
import { AllServers } from "./Server";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { CodingContractTypes } from "./CodingContracts";
|
||||
import { generateContract,
|
||||
generateRandomContract,
|
||||
generateRandomContractOnHome } from "./CodingContractGenerator";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
import { Player } from "./Player";
|
||||
import { AllServers } from "./Server";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { StockMarket,
|
||||
SymbolToStockMap } from "./StockMarket/StockMarket";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import { Terminal } from "./Terminal";
|
||||
SymbolToStockMap } from "./StockMarket/StockMarket";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import { Terminal } from "./Terminal";
|
||||
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
const devMenuContainerId = "dev-menu-container";
|
||||
|
||||
@ -226,6 +229,24 @@ export function createDevMenu() {
|
||||
innerText: "Receive Invite to Faction",
|
||||
});
|
||||
|
||||
const factionsReputationInput = createElement("input", {
|
||||
placeholder: "Rep to add to faction",
|
||||
type: "number",
|
||||
});
|
||||
|
||||
const factionsReputationButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Add rep to faction",
|
||||
clickListener: () => {
|
||||
const facName = getSelectText(factionsDropdown);
|
||||
const fac = Factions[facName];
|
||||
const rep = parseFloat(factionsReputationInput.value);
|
||||
if (fac != null && !isNaN(rep)) {
|
||||
fac.playerReputation += rep;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Augmentations
|
||||
const augmentationsHeader = createElement("h2", {innerText: "Augmentations"});
|
||||
|
||||
@ -429,6 +450,31 @@ export function createDevMenu() {
|
||||
innerText: "Generate Random Contract",
|
||||
});
|
||||
|
||||
const generateRandomContractOnHomeBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
generateRandomContractOnHome();
|
||||
},
|
||||
innerText: "Generate Random Contract on Home Comp",
|
||||
});
|
||||
|
||||
const generateContractWithTypeSelector = createElement("select", { margin: "5px" });
|
||||
const contractTypes = Object.keys(CodingContractTypes);
|
||||
for (let i = 0; i < contractTypes.length; ++i) {
|
||||
generateContractWithTypeSelector.add(createOptionElement(contractTypes[i]));
|
||||
}
|
||||
|
||||
const generateContractWithTypeBtn = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
generateContract({
|
||||
problemType: getSelectText(generateContractWithTypeSelector),
|
||||
server: "home",
|
||||
});
|
||||
},
|
||||
innerText: "Generate Specified Contract Type on Home Comp",
|
||||
});
|
||||
|
||||
// Stock Market
|
||||
const stockmarketHeader = createElement("h2", {innerText: "Stock Market"});
|
||||
|
||||
@ -535,6 +581,9 @@ export function createDevMenu() {
|
||||
devMenuContainer.appendChild(factionsHeader);
|
||||
devMenuContainer.appendChild(factionsDropdown);
|
||||
devMenuContainer.appendChild(factionsAddButton);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(factionsReputationInput);
|
||||
devMenuContainer.appendChild(factionsReputationButton);
|
||||
devMenuContainer.appendChild(augmentationsHeader);
|
||||
devMenuContainer.appendChild(augmentationsDropdown);
|
||||
devMenuContainer.appendChild(augmentationsQueueButton);
|
||||
@ -563,6 +612,10 @@ export function createDevMenu() {
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(contractsHeader);
|
||||
devMenuContainer.appendChild(generateRandomContractBtn);
|
||||
devMenuContainer.appendChild(generateRandomContractOnHomeBtn);
|
||||
devMenuContainer.appendChild(createElement("br"));
|
||||
devMenuContainer.appendChild(generateContractWithTypeSelector);
|
||||
devMenuContainer.appendChild(generateContractWithTypeBtn);
|
||||
devMenuContainer.appendChild(stockmarketHeader);
|
||||
devMenuContainer.appendChild(stockInput);
|
||||
devMenuContainer.appendChild(stockPriceChangeInput);
|
||||
|
@ -33,7 +33,7 @@ export const TerminalHelpText: string =
|
||||
"tail [script] [args...] Displays dynamic logs for the specified script<br>" +
|
||||
"theme [preset] | bg txt hlgt Change the color scheme of the UI<br>" +
|
||||
"top Displays all running scripts and their RAM usage<br>" +
|
||||
'unalias "[alias name]" Deletes the specified alias<br>' +
|
||||
'unalias [alias name] Deletes the specified alias<br>' +
|
||||
"wget [url] [target file] Retrieves code/text from a web server<br>";
|
||||
|
||||
interface IMap<T> {
|
||||
@ -215,12 +215,12 @@ export const HelpTexts: IMap<string> = {
|
||||
top: "top<br>" +
|
||||
"Prints a list of all scripts running on the current server as well as their thread count and how much " +
|
||||
"RAM they are using in total.",
|
||||
unalias: 'unalias "[alias name]"<br>' +
|
||||
unalias: 'unalias [alias name]<br>' +
|
||||
"Deletes the specified alias. Note that the double quotation marks are required. <br><br>" +
|
||||
"As an example, if an alias was declared using:<br><br>" +
|
||||
'alias r="run"<br><br>' +
|
||||
"Then it could be removed using:<br><br>" +
|
||||
'unalias "r"<br><br>' +
|
||||
'unalias r<br><br>' +
|
||||
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
|
||||
wget: "wget [url] [target file]<br>" +
|
||||
"Retrieves data from a URL and downloads it to a file on the current server. The data can only " +
|
||||
|
@ -24,8 +24,8 @@ function initLiterature() {
|
||||
title = "The Beginner's Guide to Hacking";
|
||||
fn = "hackers-starting-handbook.lit";
|
||||
txt = "Some resources:<br><br>" +
|
||||
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscriptlearntoprogram.html' target='_blank' style='margin:4px'>Learn to Program</a><br><br>" +
|
||||
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscriptjs.html' target='_blank' style='margin:4px'>For Experienced JavaScript Developers: NetscriptJS</a><br><br>" +
|
||||
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscript/netscriptlearntoprogram.html' target='_blank' style='margin:4px'>Learn to Program</a><br><br>" +
|
||||
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscript/netscriptjs.html' target='_blank' style='margin:4px'>For Experienced JavaScript Developers: NetscriptJS</a><br><br>" +
|
||||
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/netscript.html' target='_blank' style='margin:4px'>Netscript Documentation</a><br><br>" +
|
||||
"When starting out, hacking is the most profitable way to earn money and progress. This " +
|
||||
"is a brief collection of tips/pointers on how to make the most out of your hacking scripts.<br><br>" +
|
||||
|
@ -13,10 +13,10 @@ function unknownBladeburnerExceptionMessage(functionName, err) {
|
||||
}
|
||||
|
||||
function checkBladeburnerAccess(workerScript, functionName) {
|
||||
const accessDenied = `${functionName}() failed because you do not` +
|
||||
" currently have access to the Bladeburner API. This is either" +
|
||||
" because you are not currently employed at the Bladeburner division" +
|
||||
" or because you do not have Source-File 7";
|
||||
const accessDenied = `${functionName}() failed because you do not ` +
|
||||
"currently have access to the Bladeburner API. To access the Bladeburner API" +
|
||||
"you must be employed at the Bladeburner division, AND you must either be in " +
|
||||
"BitNode-7 or have Source-File 7.";
|
||||
const hasAccess = Player.bladeburner instanceof Bladeburner && (Player.bitNodeN === 7 || Player.sourceFiles.some(a=>{return a.n === 7}));
|
||||
if(!hasAccess) {
|
||||
throw makeRuntimeRejectMsg(workerScript, accessDenied);
|
||||
|
@ -13,692 +13,7 @@ import {arrayToString} from "../utils/helpers/arrayToString
|
||||
import {isValidIPAddress} from "../utils/helpers/isValidIPAddress";
|
||||
import {isString} from "../utils/helpers/isString";
|
||||
|
||||
var Promise = require("bluebird");
|
||||
|
||||
Promise.config({
|
||||
warnings: false,
|
||||
longStackTraces: false,
|
||||
cancellation: true,
|
||||
monitoring: false
|
||||
});
|
||||
/* Evaluator
|
||||
* Evaluates/Interprets the Abstract Syntax Tree generated by Acorns parser
|
||||
*
|
||||
* Returns a promise
|
||||
*/
|
||||
function evaluate(exp, workerScript) {
|
||||
return Promise.delay(Settings.CodeInstructionRunTime).then(function() {
|
||||
var env = workerScript.env;
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
if (exp == null) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Error: NULL expression", exp));
|
||||
}
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
switch (exp.type) {
|
||||
case "BlockStatement":
|
||||
case "Program":
|
||||
var evaluateProgPromise = evaluateProg(exp, workerScript, 0); //TODO: make every block/program use individual enviroment
|
||||
return evaluateProgPromise.then(function(w) {
|
||||
return Promise.resolve(workerScript);
|
||||
}).catch(function(e) {
|
||||
if (e.constructor === Array && e.length === 2 && e[0] === "RETURNSTATEMENT") {
|
||||
return Promise.reject(e);
|
||||
} else if (isString(e)) {
|
||||
workerScript.errorMessage = e;
|
||||
return Promise.reject(workerScript);
|
||||
} else if (e instanceof WorkerScript) {
|
||||
return Promise.reject(e);
|
||||
} else {
|
||||
return Promise.reject(workerScript);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "Literal":
|
||||
return Promise.resolve(exp.value);
|
||||
break;
|
||||
case "Identifier":
|
||||
//Javascript constructor() method can be used as an exploit to run arbitrary code
|
||||
if (exp.name == "constructor") {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it.", exp));
|
||||
}
|
||||
|
||||
if (!(exp.name in env.vars)){
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.name + " not defined", exp));
|
||||
}
|
||||
return Promise.resolve(env.get(exp.name))
|
||||
break;
|
||||
case "ExpressionStatement":
|
||||
return evaluate(exp.expression, workerScript);
|
||||
break;
|
||||
case "ArrayExpression":
|
||||
var argPromises = exp.elements.map(function(arg) {
|
||||
return evaluate(arg, workerScript);
|
||||
});
|
||||
return Promise.all(argPromises).then(function(array) {
|
||||
return Promise.resolve(array)
|
||||
});
|
||||
break;
|
||||
case "CallExpression":
|
||||
return evaluate(exp.callee, workerScript).then(function(func) {
|
||||
return Promise.map(exp.arguments, function(arg) {
|
||||
return evaluate(arg, workerScript);
|
||||
}).then(function(args) {
|
||||
if (func instanceof Node) { //Player-defined function
|
||||
//Create new Environment for the function
|
||||
//Should be automatically garbage collected...
|
||||
var funcEnv = env.extend();
|
||||
|
||||
//Define function arguments in this new environment
|
||||
for (var i = 0; i < func.params.length; ++i) {
|
||||
var arg;
|
||||
if (i >= args.length) {
|
||||
arg = null;
|
||||
} else {
|
||||
arg = args[i];
|
||||
}
|
||||
funcEnv.def(func.params[i].name, arg);
|
||||
}
|
||||
|
||||
//Create a new WorkerScript for this function evaluation
|
||||
var funcWorkerScript = new WorkerScript(workerScript.scriptRef);
|
||||
funcWorkerScript.serverIp = workerScript.serverIp;
|
||||
funcWorkerScript.env = funcEnv;
|
||||
workerScript.fnWorker = funcWorkerScript;
|
||||
|
||||
return evaluate(func.body, funcWorkerScript).then(function(res) {
|
||||
//If the function finished successfuly, that means there
|
||||
//was no return statement since a return statement rejects. So resolve to null
|
||||
workerScript.fnWorker = null;
|
||||
return Promise.resolve(null);
|
||||
}).catch(function(e) {
|
||||
if (e.constructor === Array && e.length === 2 && e[0] === "RETURNSTATEMENT") {
|
||||
//Return statement from function
|
||||
return Promise.resolve(e[1]);
|
||||
workerScript.fnWorker = null;
|
||||
} else if (isString(e)) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, e));
|
||||
} else if (e instanceof WorkerScript) {
|
||||
//Parse out the err message from the WorkerScript and re-reject
|
||||
var errorMsg = e.errorMessage;
|
||||
var errorTextArray = errorMsg.split("|");
|
||||
if (errorTextArray.length === 4) {
|
||||
errorMsg = errorTextArray[3];
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, errorMsg));
|
||||
} else {
|
||||
if (env.stopFlag) {
|
||||
return Promise.reject(workerScript);
|
||||
} else {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Error in one of your functions. Could not identify which function"));
|
||||
}
|
||||
}
|
||||
} else if (e instanceof Error) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, e.toString()));
|
||||
}
|
||||
});
|
||||
} else if (exp.callee.type === "MemberExpression"){
|
||||
return evaluate(exp.callee.object, workerScript).then(function(object) {
|
||||
try {
|
||||
if (func === "NETSCRIPTFOREACH") {
|
||||
return evaluateForeach(object, args, workerScript).then(function(res) {
|
||||
return Promise.resolve(res);
|
||||
}).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
}
|
||||
var res = func.apply(object,args);
|
||||
return Promise.resolve(res);
|
||||
} catch (e) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, e, exp));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
var out = func.apply(null,args);
|
||||
if (out instanceof Promise){
|
||||
return out.then(function(res) {
|
||||
return Promise.resolve(res)
|
||||
}).catch(function(e) {
|
||||
if (isScriptErrorMessage(e)) {
|
||||
//Functions don't have line number appended in error message, so add it
|
||||
var num = getErrorLineNumber(exp, workerScript);
|
||||
e += " (Line " + num + ")";
|
||||
}
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve(out);
|
||||
}
|
||||
} catch (e) {
|
||||
if (isScriptErrorMessage(e)) {
|
||||
if (isScriptErrorMessage(e)) {
|
||||
//Functions don't have line number appended in error message, so add it
|
||||
var num = getErrorLineNumber(exp, workerScript);
|
||||
e += " (Line " + num + ")";
|
||||
}
|
||||
return Promise.reject(e);
|
||||
} else {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, e, exp));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "MemberExpression":
|
||||
return evaluate(exp.object, workerScript).then(function(object) {
|
||||
if (exp.computed){
|
||||
return evaluate(exp.property, workerScript).then(function(index) {
|
||||
if (index >= object.length) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid index for arrays", exp));
|
||||
}
|
||||
return Promise.resolve(object[index]);
|
||||
}).catch(function(e) {
|
||||
if (e instanceof WorkerScript || isScriptErrorMessage(e)) {
|
||||
return Promise.reject(e);
|
||||
} else {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid MemberExpression", exp));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (exp.property.name === "constructor") {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it.", exp));
|
||||
}
|
||||
if (object != null && object instanceof Array && exp.property.name === "forEach") {
|
||||
return "NETSCRIPTFOREACH";
|
||||
}
|
||||
try {
|
||||
return Promise.resolve(object[exp.property.name])
|
||||
} catch (e) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to get property: " + e.toString(), exp));
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "LogicalExpression":
|
||||
case "BinaryExpression":
|
||||
return evalBinary(exp, workerScript);
|
||||
break;
|
||||
case "UnaryExpression":
|
||||
return evalUnary(exp, workerScript);
|
||||
break;
|
||||
case "AssignmentExpression":
|
||||
return evalAssignment(exp, workerScript);
|
||||
break;
|
||||
case "VariableDeclaration":
|
||||
return evalVariableDeclaration(exp, workerScript);
|
||||
break;
|
||||
case "UpdateExpression":
|
||||
if (exp.argument.type==="Identifier"){
|
||||
if (exp.argument.name in env.vars){
|
||||
if (exp.operator === "++" || exp.operator === "--") {
|
||||
switch (exp.operator) {
|
||||
case "++":
|
||||
env.set(exp.argument.name,env.get(exp.argument.name)+1);
|
||||
break;
|
||||
case "--":
|
||||
env.set(exp.argument.name,env.get(exp.argument.name)-1);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return Promise.resolve(env.get(exp.argument.name));
|
||||
}
|
||||
//Not sure what prefix UpdateExpressions there would be besides ++/--
|
||||
if (exp.prefix){
|
||||
return Promise.resolve(env.get(exp.argument.name))
|
||||
}
|
||||
switch (exp.operator){
|
||||
default:
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". You are trying to use code that is currently unsupported", exp));
|
||||
}
|
||||
return Promise.resolve(env.get(exp.argument.name))
|
||||
} else {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.argument.name + " not defined", exp));
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "argument must be an identifier", exp));
|
||||
}
|
||||
break;
|
||||
case "EmptyStatement":
|
||||
return Promise.resolve(false);
|
||||
break;
|
||||
case "ReturnStatement":
|
||||
return evaluate(exp.argument, workerScript).then(function(res) {
|
||||
return Promise.reject(["RETURNSTATEMENT", res]);
|
||||
});
|
||||
break;
|
||||
case "BreakStatement":
|
||||
return Promise.reject("BREAKSTATEMENT");
|
||||
break;
|
||||
case "ContinueStatement":
|
||||
return Promise.reject("CONTINUESTATEMENT");
|
||||
break;
|
||||
case "IfStatement":
|
||||
return evaluateIf(exp, workerScript);
|
||||
break;
|
||||
case "SwitchStatement":
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Switch statements are not yet implemented in Netscript", exp));
|
||||
break;
|
||||
case "WhileStatement":
|
||||
return evaluateWhile(exp, workerScript).then(function(res) {
|
||||
return Promise.resolve(res);
|
||||
}).catch(function(e) {
|
||||
if (e == "BREAKSTATEMENT" ||
|
||||
(e instanceof WorkerScript && e.errorMessage == "BREAKSTATEMENT")) {
|
||||
return Promise.resolve("whileLoopBroken");
|
||||
} else {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "ForStatement":
|
||||
return evaluate(exp.init, workerScript).then(function(expInit) {
|
||||
return evaluateFor(exp, workerScript);
|
||||
}).then(function(forLoopRes) {
|
||||
return Promise.resolve("forLoopDone");
|
||||
}).catch(function(e) {
|
||||
if (e == "BREAKSTATEMENT" ||
|
||||
(e instanceof WorkerScript && e.errorMessage == "BREAKSTATEMENT")) {
|
||||
return Promise.resolve("forLoopBroken");
|
||||
} else {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "FunctionDeclaration":
|
||||
if (exp.id && exp.id.name) {
|
||||
env.set(exp.id.name, exp);
|
||||
return Promise.resolve(true);
|
||||
} else {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid function declaration", exp));
|
||||
}
|
||||
break;
|
||||
case "ImportDeclaration":
|
||||
return evaluateImport(exp, workerScript).then(function(res) {
|
||||
return Promise.resolve(res);
|
||||
}).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
break;
|
||||
case "ThrowStatement":
|
||||
return evaluate(exp.argument, workerScript).then(function(res) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, res));
|
||||
});
|
||||
break;
|
||||
default:
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unrecognized token: " + exp.type + ". This is currently unsupported in Netscript", exp));
|
||||
break;
|
||||
} //End switch
|
||||
}).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
}); // End Promise
|
||||
}
|
||||
|
||||
function evalBinary(exp, workerScript){
|
||||
return evaluate(exp.left, workerScript).then(function(expLeft) {
|
||||
//Short circuiting
|
||||
if (expLeft && exp.operator === "||") {
|
||||
return Promise.resolve(expLeft);
|
||||
}
|
||||
if (!expLeft && exp.operator === "&&") {
|
||||
return Promise.resolve(expLeft);
|
||||
}
|
||||
return evaluate(exp.right, workerScript).then(function(expRight) {
|
||||
switch (exp.operator){
|
||||
case "===":
|
||||
case "==":
|
||||
return Promise.resolve(expLeft===expRight);
|
||||
break;
|
||||
case "!==":
|
||||
case "!=":
|
||||
return Promise.resolve(expLeft!==expRight);
|
||||
break;
|
||||
case "<":
|
||||
return Promise.resolve(expLeft<expRight);
|
||||
break;
|
||||
case "<=":
|
||||
return Promise.resolve(expLeft<=expRight);
|
||||
break;
|
||||
case ">":
|
||||
return Promise.resolve(expLeft>expRight);
|
||||
break;
|
||||
case ">=":
|
||||
return Promise.resolve(expLeft>=expRight);
|
||||
break;
|
||||
case "+":
|
||||
return Promise.resolve(expLeft+expRight);
|
||||
break;
|
||||
case "-":
|
||||
return Promise.resolve(expLeft-expRight);
|
||||
break;
|
||||
case "*":
|
||||
return Promise.resolve(expLeft*expRight);
|
||||
break;
|
||||
case "/":
|
||||
if (expRight === 0) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "ERROR: Divide by zero"));
|
||||
} else {
|
||||
return Promise.resolve(expLeft/expRight);
|
||||
}
|
||||
break;
|
||||
case "%":
|
||||
return Promise.resolve(expLeft%expRight);
|
||||
break;
|
||||
case "in":
|
||||
return Promise.resolve(expLeft in expRight);
|
||||
break;
|
||||
case "instanceof":
|
||||
return Promise.resolve(expLeft instanceof expRight);
|
||||
break;
|
||||
case "||":
|
||||
return Promise.resolve(expLeft || expRight);
|
||||
break;
|
||||
case "&&":
|
||||
return Promise.resolve(expLeft && expRight);
|
||||
break;
|
||||
default:
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unsupported operator: " + exp.operator));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function evalUnary(exp, workerScript){
|
||||
var env = workerScript.env;
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
return evaluate(exp.argument, workerScript).then(function(res) {
|
||||
if (exp.operator == "!") {
|
||||
return Promise.resolve(!res);
|
||||
} else if (exp.operator == "-") {
|
||||
if (isNaN(res)) {
|
||||
return Promise.resolve(res);
|
||||
} else {
|
||||
return Promise.resolve(-1 * res);
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Unimplemented unary operator: " + exp.operator));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Takes in a MemberExpression that should represent a Netscript array (possible multidimensional)
|
||||
//The return value is an array of the form:
|
||||
// [0th index (leftmost), array name, 1st index, 2nd index, ...]
|
||||
function getArrayElement(exp, workerScript) {
|
||||
var indices = [];
|
||||
return evaluate(exp.property, workerScript).then(function(idx) {
|
||||
if (isNaN(idx)) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid access to array. Index is not a number: " + idx));
|
||||
} else {
|
||||
if (exp.object.name === undefined && exp.object.object) {
|
||||
return getArrayElement(exp.object, workerScript).then(function(res) {
|
||||
res.push(idx);
|
||||
indices = res;
|
||||
return Promise.resolve(indices);
|
||||
}).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
indices.push(idx);
|
||||
indices.push(exp.object.name);
|
||||
return Promise.resolve(indices);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function evalAssignment(exp, workerScript) {
|
||||
var env = workerScript.env;
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
|
||||
if (exp.left.type != "Identifier" && exp.left.type != "MemberExpression") {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Cannot assign to " + JSON.stringify(exp.left)));
|
||||
}
|
||||
|
||||
if (exp.operator !== "=" && !(exp.left.name in env.vars)){
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "variable " + exp.left.name + " not defined"));
|
||||
}
|
||||
|
||||
return evaluate(exp.right, workerScript).then(function(expRight) {
|
||||
if (exp.left.type == "MemberExpression") {
|
||||
if (!exp.left.computed) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Cannot assign to an object's property. This is currently unsupported in Netscript", exp));
|
||||
}
|
||||
//Assign to array element
|
||||
//Array object designed by exp.left.object.name
|
||||
//Index designated by exp.left.property
|
||||
return getArrayElement(exp.left, workerScript).then(function(res) {
|
||||
if (!(res instanceof Array) || res.length < 2) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Error evaluating array assignment. This is (probably) a bug please report to game dev"));
|
||||
}
|
||||
|
||||
//The array name is the second value
|
||||
var arrName = res.splice(1, 1);
|
||||
arrName = arrName[0];
|
||||
|
||||
var res;
|
||||
try {
|
||||
res = env.setArrayElement(arrName, res, expRight);
|
||||
} catch (e) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, e));
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
}).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
//Other assignments
|
||||
try {
|
||||
var assign;
|
||||
switch (exp.operator) {
|
||||
case "=":
|
||||
assign = expRight; break;
|
||||
case "+=":
|
||||
assign = env.get(exp.left.name) + expRight; break;
|
||||
case "-=":
|
||||
assign = env.get(exp.left.name) - expRight; break;
|
||||
case "*=":
|
||||
assign = env.get(exp.left.name) * expRight; break;
|
||||
case "/=":
|
||||
assign = env.get(exp.left.name) / expRight; break;
|
||||
case "%=":
|
||||
assign = env.get(exp.left.name) % expRight; break;
|
||||
default:
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Bitwise assignment is not implemented"));
|
||||
}
|
||||
env.set(exp.left.name, assign);
|
||||
return Promise.resolve(assign);
|
||||
} catch (e) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Failed to set environment variable: " + e.toString()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function evalVariableDeclaration(exp, workerScript) {
|
||||
var env = workerScript.env;
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
|
||||
if (!(exp.declarations instanceof Array)) {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Variable declarations parsed incorrectly. This may be a syntax error"));
|
||||
}
|
||||
|
||||
if (exp.kind !== "var") {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Only 'var' declarations are currently allowed (let, const, etc. are not allowed)"));
|
||||
}
|
||||
|
||||
return Promise.all(exp.declarations.map((decl)=>{
|
||||
evalVariableDeclarator(decl, workerScript);
|
||||
})).then(function(res) {
|
||||
return Promise.resolve(res);
|
||||
});
|
||||
}
|
||||
|
||||
//A Variable Declaration contains an array of Variable Declarators
|
||||
function evalVariableDeclarator(exp, workerScript) {
|
||||
var env = workerScript.env;
|
||||
if (exp.type !== "VariableDeclarator") {
|
||||
return Promise.reject(makeRuntimeRejectMsg(workerScript, "Invalid AST Node passed into evalVariableDeclarator: " + exp.type));
|
||||
}
|
||||
if (exp.init == null) {
|
||||
env.set(exp.id.name, null);
|
||||
return Promise.resolve(null);
|
||||
} else {
|
||||
return evaluate(exp.init, workerScript).then(function(initValue) {
|
||||
env.set(exp.id.name, initValue);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateIf(exp, workerScript, i) {
|
||||
var env = workerScript.env;
|
||||
return evaluate(exp.test, workerScript).then(function(condRes) {
|
||||
if (condRes) {
|
||||
return evaluate(exp.consequent, workerScript).then(function(res) {
|
||||
return Promise.resolve(true);
|
||||
}, function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else if (exp.alternate) {
|
||||
return evaluate(exp.alternate, workerScript).then(function(res) {
|
||||
return Promise.resolve(true);
|
||||
}, function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve("endIf");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Evaluate the looping part of a for loop (Initialization block is NOT done in here)
|
||||
function evaluateFor(exp, workerScript) {
|
||||
var env = workerScript.env;
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
return new Promise(function(resolve, reject) {
|
||||
function recurse() {
|
||||
//Don't return a promise so the promise chain is broken on each recursion (saving memory)
|
||||
evaluate(exp.test, workerScript).then(function(resCond) {
|
||||
if (resCond) {
|
||||
return evaluate(exp.body, workerScript).then(function(res) {
|
||||
return evaluate(exp.update, workerScript);
|
||||
}).catch(function(e) {
|
||||
if (e == "CONTINUESTATEMENT" ||
|
||||
(e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) {
|
||||
//Continue statement, recurse to next iteration
|
||||
return evaluate(exp.update, workerScript).then(function(resPostloop) {
|
||||
return evaluateFor(exp, workerScript);
|
||||
}).then(function(foo) {
|
||||
return Promise.resolve("endForLoop");
|
||||
}).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}).then(recurse, reject).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}).catch(function(e) {
|
||||
reject(e);
|
||||
});
|
||||
}
|
||||
recurse();
|
||||
});
|
||||
}
|
||||
|
||||
function evaluateForeach(arr, args, workerScript) {
|
||||
console.log("evaluateForeach called");
|
||||
if (!(arr instanceof Array)) {
|
||||
return Promise.reject("Invalid array passed into forEach");
|
||||
}
|
||||
if (!(args instanceof Array) && args.length != 1) {
|
||||
return Promise.reject("Invalid argument passed into forEach");
|
||||
}
|
||||
var func = args[0];
|
||||
if (typeof func !== "function") {
|
||||
return Promise.reject("Invalid function passed into forEach");
|
||||
}
|
||||
console.log(func);
|
||||
return new Promise(function(resolve, reject) {
|
||||
//Don't return a promise so the promise chain is broken on each recursion
|
||||
function recurse(i) {
|
||||
console.log("recurse() called with i: " + i);
|
||||
if (i >= arr.length) {
|
||||
resolve();
|
||||
} else {
|
||||
return Promise.delay(Settings.CodeInstructionRunTime).then(function() {
|
||||
console.log("About to apply function");
|
||||
var res = func.apply(null, [arr[i]]);
|
||||
console.log("Applied function");
|
||||
++i;
|
||||
Promise.resolve(res).then(function(val) {
|
||||
recurse(i);
|
||||
}, reject).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
recurse(0);
|
||||
});
|
||||
}
|
||||
|
||||
function evaluateWhile(exp, workerScript) {
|
||||
var env = workerScript.env;
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
return new Promise(function (resolve, reject) {
|
||||
function recurse() {
|
||||
//Don't return a promise so the promise chain is broken on each recursion (saving memory)
|
||||
evaluate(exp.test, workerScript).then(function(resCond) {
|
||||
if (resCond) {
|
||||
return evaluate(exp.body, workerScript).catch(function(e) {
|
||||
if (e == "CONTINUESTATEMENT" ||
|
||||
(e instanceof WorkerScript && e.errorMessage == "CONTINUESTATEMENT")) {
|
||||
//Continue statement, recurse
|
||||
return evaluateWhile(exp, workerScript).then(function(foo) {
|
||||
return Promise.resolve("endWhileLoop");
|
||||
}, function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}).then(recurse, reject).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}).catch(function(e) {
|
||||
reject(e);
|
||||
});
|
||||
}
|
||||
recurse();
|
||||
});
|
||||
}
|
||||
|
||||
function evaluateProg(exp, workerScript, index) {
|
||||
var env = workerScript.env;
|
||||
if (env.stopFlag) {return Promise.reject(workerScript);}
|
||||
if (index >= exp.body.length) {
|
||||
return Promise.resolve("progFinished");
|
||||
} else {
|
||||
//Evaluate this line of code in the prog
|
||||
//After the code finishes evaluating, evaluate the next line recursively
|
||||
return evaluate(exp.body[index], workerScript).then(function(res) {
|
||||
return evaluateProg(exp, workerScript, index + 1);
|
||||
}).then(function(res) {
|
||||
return Promise.resolve(workerScript);
|
||||
}).catch(function(e) {
|
||||
return Promise.reject(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateImport(exp, workerScript, checkingRam=false) {
|
||||
export function evaluateImport(exp, workerScript, checkingRam=false) {
|
||||
//When its checking RAM, it exports an array of nodes for each imported function
|
||||
var ramCheckRes = [];
|
||||
|
||||
@ -808,7 +123,7 @@ function evaluateImport(exp, workerScript, checkingRam=false) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
function killNetscriptDelay(workerScript) {
|
||||
export function killNetscriptDelay(workerScript) {
|
||||
if (workerScript instanceof WorkerScript) {
|
||||
if (workerScript.delay) {
|
||||
clearTimeout(workerScript.delay);
|
||||
@ -817,7 +132,7 @@ function killNetscriptDelay(workerScript) {
|
||||
}
|
||||
}
|
||||
|
||||
function netscriptDelay(time, workerScript) {
|
||||
export function netscriptDelay(time, workerScript) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
workerScript.delay = setTimeout(()=>{
|
||||
workerScript.delay = null;
|
||||
@ -827,7 +142,7 @@ function netscriptDelay(time, workerScript) {
|
||||
});
|
||||
}
|
||||
|
||||
function makeRuntimeRejectMsg(workerScript, msg, exp=null) {
|
||||
export function makeRuntimeRejectMsg(workerScript, msg, exp=null) {
|
||||
var lineNum = "";
|
||||
if (exp != null) {
|
||||
var num = getErrorLineNumber(exp, workerScript);
|
||||
@ -837,7 +152,7 @@ function makeRuntimeRejectMsg(workerScript, msg, exp=null) {
|
||||
}
|
||||
|
||||
//Run a script from inside a script using run() command
|
||||
function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
|
||||
export function runScriptFromScript(server, scriptname, args, workerScript, threads=1) {
|
||||
//Check if the script is already running
|
||||
var runningScriptObj = findRunningScript(scriptname, args, server);
|
||||
if (runningScriptObj != null) {
|
||||
@ -887,8 +202,8 @@ function runScriptFromScript(server, scriptname, args, workerScript, threads=1)
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
function getErrorLineNumber(exp, workerScript) {
|
||||
var code = workerScript.scriptRef.scriptRef.code;
|
||||
export function getErrorLineNumber(exp, workerScript) {
|
||||
var code = workerScript.scriptRef.codeCode();
|
||||
|
||||
//Split code up to the start of the node
|
||||
try {
|
||||
@ -899,7 +214,7 @@ function getErrorLineNumber(exp, workerScript) {
|
||||
}
|
||||
}
|
||||
|
||||
function isScriptErrorMessage(msg) {
|
||||
export function isScriptErrorMessage(msg) {
|
||||
if (!isString(msg)) {return false;}
|
||||
let splitMsg = msg.split("|");
|
||||
if (splitMsg.length != 4){
|
||||
@ -911,6 +226,3 @@ function isScriptErrorMessage(msg) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export {makeRuntimeRejectMsg, netscriptDelay, runScriptFromScript, evaluate,
|
||||
isScriptErrorMessage, killNetscriptDelay, evaluateImport};
|
||||
|
@ -51,6 +51,7 @@ import {StockMarket, StockSymbols, SymbolToStockMap,
|
||||
sellStock, updateStockPlayerPosition,
|
||||
shortStock, sellShort, OrderTypes,
|
||||
PositionTypes, placeOrder, cancelOrder} from "./StockMarket/StockMarket";
|
||||
import {numeralWrapper} from "./ui/numeralFormat";
|
||||
import {post} from "./ui/postToTerminal";
|
||||
import {TextFile, getTextFile, createTextFile} from "./TextFile";
|
||||
|
||||
@ -72,9 +73,10 @@ import {arrayToString} from "../utils/helpers/array
|
||||
import {createRandomIp} from "../utils/IPAddress";
|
||||
import {formatNumber, isHTML} from "../utils/StringHelperFunctions";
|
||||
import {isString} from "../utils/helpers/isString";
|
||||
import {yesNoBoxClose, yesNoBoxGetYesButton,
|
||||
yesNoBoxGetNoButton, yesNoBoxCreate,
|
||||
yesNoBoxOpen} from "../utils/YesNoBox";
|
||||
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
var hasCorporationSF = false, //Source-File 3
|
||||
hasSingularitySF = false, //Source-File 4
|
||||
@ -2453,6 +2455,14 @@ function NetscriptFunctions(workerScript) {
|
||||
return runningScriptObj.onlineExpGained / runningScriptObj.onlineRunningTime;
|
||||
}
|
||||
},
|
||||
nFormat : function(n, format) {
|
||||
if (workerScript.checkingRam) { return 0; }
|
||||
if (isNaN(n) || isNaN(parseFloat(n)) || typeof format !== "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return numeralWrapper.format(parseFloat(n), format);
|
||||
},
|
||||
getTimeSinceLastAug : function() {
|
||||
if (workerScript.checkingRam) {
|
||||
return updateStaticRam("getTimeSinceLastAug", CONSTANTS.ScriptGetHackTimeRamCost);
|
||||
@ -2462,24 +2472,33 @@ function NetscriptFunctions(workerScript) {
|
||||
},
|
||||
prompt : function(txt) {
|
||||
if (workerScript.checkingRam) {return 0;}
|
||||
if (yesNoBoxOpen) {
|
||||
workerScript.scriptRef.log("ERROR: confirm() failed because a pop-up dialog box is already open");
|
||||
return false;
|
||||
}
|
||||
if (!isString(txt)) {txt = String(txt);}
|
||||
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
|
||||
yesBtn.innerHTML = "Yes";
|
||||
noBtn.innerHTML = "No";
|
||||
|
||||
// The id for this popup will consist of the first 20 characters of the prompt string..
|
||||
// Thats hopefully good enough to be unique
|
||||
const popupId = `prompt-popup-${txt.slice(0, 20)}`;
|
||||
const textElement = createElement("p", { innerHTML: txt });
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
yesBtn.addEventListener("click", ()=>{
|
||||
yesNoBoxClose();
|
||||
resolve(true);
|
||||
const yesBtn = createElement("button", {
|
||||
class: "popup-box-button",
|
||||
innerText: "Yes",
|
||||
clickListener: () => {
|
||||
removeElementById(popupId);
|
||||
resolve(true);
|
||||
},
|
||||
});
|
||||
noBtn.addEventListener("click", ()=>{
|
||||
yesNoBoxClose();
|
||||
resolve(false);
|
||||
|
||||
const noBtn = createElement("button", {
|
||||
class: "popup-box-button",
|
||||
innerText: "No",
|
||||
clickListener: () => {
|
||||
removeElementById(popupId);
|
||||
resolve(false);
|
||||
},
|
||||
});
|
||||
yesNoBoxCreate(txt);
|
||||
|
||||
createPopup(popupId, [textElement, yesBtn, noBtn]);
|
||||
});
|
||||
},
|
||||
wget : async function(url, target, ip=workerScript.serverIp) {
|
||||
@ -4702,8 +4721,17 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
let data = contract.getData();
|
||||
if (data.constructor === Array) {
|
||||
// Pass a copy
|
||||
return data.slice();
|
||||
// For two dimensional arrays, we have to copy the internal arrays using
|
||||
// slice() as well. As of right now, no contract has arrays that have
|
||||
// more than two dimensions
|
||||
const copy = data.slice();
|
||||
for (let i = 0; i < copy.length; ++i) {
|
||||
if (data[i].constructor === Array) {
|
||||
copy[i] = data[i].slice();
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ const walk = require("acorn/dist/walk");
|
||||
function WorkerScript(runningScriptObj) {
|
||||
this.name = runningScriptObj.filename;
|
||||
this.running = false;
|
||||
this.serverIp = null;
|
||||
this.code = runningScriptObj.scriptRef.code;
|
||||
this.serverIp = runningScriptObj.server;
|
||||
this.code = runningScriptObj.getCode();
|
||||
this.env = new Environment(this);
|
||||
this.env.set("args", runningScriptObj.args.slice());
|
||||
this.output = "";
|
||||
@ -219,6 +219,8 @@ function startNetscript1Script(workerScript) {
|
||||
let fnPromise = entry.apply(null, fnArgs);
|
||||
fnPromise.then(function(res) {
|
||||
cb(res);
|
||||
}).catch(function(e) {
|
||||
// Do nothing?
|
||||
});
|
||||
}
|
||||
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
|
||||
@ -278,7 +280,7 @@ function startNetscript1Script(workerScript) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
function runInterpreter() {
|
||||
try {
|
||||
if (workerScript.env.stopFlag) {return reject(workerScript);}
|
||||
if (workerScript.env.stopFlag) { return reject(workerScript); }
|
||||
|
||||
if (interpreter.step()) {
|
||||
window.setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
|
||||
@ -498,7 +500,7 @@ function runScriptsLoop() {
|
||||
p = startNetscript2Script(workerScripts[i]);
|
||||
} else {
|
||||
p = startNetscript1Script(workerScripts[i]);
|
||||
if (!(p instanceof Promise)) {continue;}
|
||||
if (!(p instanceof Promise)) { continue; }
|
||||
}
|
||||
|
||||
//Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
@ -539,7 +541,6 @@ function runScriptsLoop() {
|
||||
}
|
||||
w.running = false;
|
||||
w.env.stopFlag = true;
|
||||
|
||||
} else if (isScriptErrorMessage(w)) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.log("ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
@ -588,7 +589,7 @@ function addWorkerScript(runningScriptObj, server) {
|
||||
} else {
|
||||
runningScriptObj.threads = 1;
|
||||
}
|
||||
var ramUsage = roundToTwo(runningScriptObj.scriptRef.ramUsage * threads);
|
||||
var ramUsage = roundToTwo(runningScriptObj.getRamUsage() * threads);
|
||||
var ramAvailable = server.maxRam - server.ramUsed;
|
||||
if (ramUsage > ramAvailable) {
|
||||
dialogBoxCreate("Not enough RAM to run script " + runningScriptObj.filename + " with args " +
|
||||
@ -601,7 +602,6 @@ function addWorkerScript(runningScriptObj, server) {
|
||||
|
||||
//Create the WorkerScript
|
||||
var s = new WorkerScript(runningScriptObj);
|
||||
s.serverIp = server.ip;
|
||||
s.ramUsage = ramUsage;
|
||||
|
||||
//Add the WorkerScript to the Active Scripts list
|
||||
|
@ -24,6 +24,7 @@ export interface IPlayer {
|
||||
queuedAugmentations: IPlayerOwnedAugmentation[];
|
||||
resleeves: Resleeve[];
|
||||
sleeves: Sleeve[];
|
||||
sleevesFromCovenant: number;
|
||||
sourceFiles: IPlayerOwnedSourceFile[];
|
||||
|
||||
// Stats
|
||||
|
24
src/PersonObjects/IPlayerOrSleeve.ts
Normal file
24
src/PersonObjects/IPlayerOrSleeve.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// Interface that represents either the player (PlayerObject) or
|
||||
// a Sleeve. Used for functions that need to take in both.
|
||||
|
||||
export interface IPlayerOrSleeve {
|
||||
// Stats
|
||||
hacking_skill: number;
|
||||
strength: number;
|
||||
defense: number;
|
||||
dexterity: number;
|
||||
agility: number;
|
||||
charisma: number;
|
||||
intelligence: number;
|
||||
|
||||
// Experience
|
||||
hacking_exp: number;
|
||||
strength_exp: number;
|
||||
defense_exp: number;
|
||||
dexterity_exp: number;
|
||||
agility_exp: number;
|
||||
charisma_exp: number;
|
||||
|
||||
// Multipliers
|
||||
crime_success_mult: number;
|
||||
}
|
@ -41,6 +41,7 @@ export abstract class Person {
|
||||
dexterity: number = 1;
|
||||
agility: number = 1;
|
||||
charisma: number = 1;
|
||||
intelligence: number = 1;
|
||||
hp: number = 10;
|
||||
max_hp: number = 10;
|
||||
|
||||
|
@ -44,7 +44,7 @@ export class Resleeve extends Person {
|
||||
console.error(`Could not find Augmentation ${this.augmentations[i].name}`);
|
||||
continue;
|
||||
}
|
||||
totalAugmentationCost += aug!.baseCost;
|
||||
totalAugmentationCost += aug!.startingCost;
|
||||
}
|
||||
|
||||
return (totalExp * CostPerExp) + (totalAugmentationCost * Math.pow(NumAugsExponent, this.augmentations.length));
|
||||
|
@ -184,7 +184,7 @@ export class Sleeve extends Person {
|
||||
this.resetTaskStatus();
|
||||
return retValue;
|
||||
}
|
||||
if (Math.random() < crime.successRate(p)) {
|
||||
if (Math.random() < crime.successRate(this)) {
|
||||
// Success
|
||||
const successGainRates: ITaskTracker = createTaskTracker();
|
||||
|
||||
|
48
src/PersonObjects/Sleeve/SleeveCovenantPurchases.ts
Normal file
48
src/PersonObjects/Sleeve/SleeveCovenantPurchases.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Implements the purchasing of extra Duplicate Sleeves from The Covenant
|
||||
*/
|
||||
import { Sleeve } from "./Sleeve";
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { yesNoBoxCreate,
|
||||
yesNoBoxClose,
|
||||
yesNoBoxGetYesButton,
|
||||
yesNoBoxGetNoButton } from "../../../utils/YesNoBox";
|
||||
|
||||
export const MaxSleevesFromCovenant: number = 5;
|
||||
|
||||
export function createPurchaseSleevesFromCovenantPopup(p: IPlayer) {
|
||||
if (p.sleevesFromCovenant >= MaxSleevesFromCovenant) { return; }
|
||||
|
||||
// First sleeve purchased costs the base amount. Then, the price of
|
||||
// each successive one increases by the same amount
|
||||
const baseCostPerExtraSleeve: number = 10e12;
|
||||
const cost: number = (p.sleevesFromCovenant + 1) * baseCostPerExtraSleeve;
|
||||
|
||||
const yesBtn = yesNoBoxGetYesButton();
|
||||
const noBtn = yesNoBoxGetNoButton();
|
||||
|
||||
yesBtn!.addEventListener("click", () => {
|
||||
if (p.canAfford(cost)) {
|
||||
p.loseMoney(cost);
|
||||
p.sleevesFromCovenant += 1;
|
||||
p.sleeves.push(new Sleeve());
|
||||
yesNoBoxClose();
|
||||
} else {
|
||||
dialogBoxCreate("You cannot afford to purchase a Duplicate Sleeve", false);
|
||||
}
|
||||
});
|
||||
|
||||
noBtn!.addEventListener("click", () => {
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
const txt = `Would you like to purchase an additional Duplicate Sleeve from The Covenant for ` +
|
||||
`${numeralWrapper.formatMoney(cost)}?<br><br>` +
|
||||
`These Duplicate Sleeves are permanent. You can purchase a total of 5 Duplicate ` +
|
||||
`Sleeves from The Covenant`;
|
||||
yesNoBoxCreate(txt);
|
||||
}
|
@ -652,10 +652,12 @@ function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): boolean {
|
||||
res = sleeve.workoutAtGym(playerRef!, detailValue2, detailValue);
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
sleeve.finishTask(playerRef!);
|
||||
sleeve.currentTask = SleeveTaskType.Recovery;
|
||||
res = true;
|
||||
break;
|
||||
case "Synchronize":
|
||||
sleeve.finishTask(playerRef!);
|
||||
sleeve.currentTask = SleeveTaskType.Sync;
|
||||
res = true;
|
||||
break;
|
||||
@ -715,7 +717,7 @@ function updateSleeveTaskDescription(sleeve: Sleeve, elems: ISleeveUIElems): voi
|
||||
elems.taskDescription!.innerText = `This sleeve is currently doing ${detailValue2} for ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Commit Crime":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently attempting to ${Crimes[detailValue].type} (Success Rate: ${numeralWrapper.formatPercentage(Crimes[detailValue].successRate(playerRef))}).`;
|
||||
elems.taskDescription!.innerText = `This sleeve is currently attempting to ${Crimes[detailValue].type} (Success Rate: ${numeralWrapper.formatPercentage(Crimes[detailValue].successRate(sleeve))}).`;
|
||||
break;
|
||||
case "Take University Course":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently studying/taking a course at ${sleeve.currentTaskLocation}.`;
|
||||
|
@ -34,6 +34,9 @@ import {initStockMarket, initSymbolToStockMap,
|
||||
stockMarketContentCreated,
|
||||
setStockMarketContentCreated} from "./StockMarket/StockMarket";
|
||||
import {Terminal, postNetburnerText} from "./Terminal";
|
||||
|
||||
import {Page, routing} from "./ui/navigationTracking";
|
||||
|
||||
import Decimal from "decimal.js";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {removeElementById} from "../utils/uiHelpers/removeElementById";
|
||||
@ -45,6 +48,13 @@ let BitNode8StartingMoney = 250e6;
|
||||
|
||||
//Prestige by purchasing augmentation
|
||||
function prestigeAugmentation() {
|
||||
// Load Terminal Screen
|
||||
var mainMenu = document.getElementById("mainmenu-container");
|
||||
mainMenu.style.visibility = "visible";
|
||||
Terminal.resetTerminalInput();
|
||||
Engine.loadTerminalContent();
|
||||
routing.navigateTo(Page.Terminal);
|
||||
|
||||
initBitNodeMultipliers();
|
||||
|
||||
Player.prestigeAugmentation();
|
||||
@ -146,12 +156,6 @@ function prestigeAugmentation() {
|
||||
var watchlist = document.getElementById("stock-market-watchlist-filter");
|
||||
watchlist.value = ""; //Reset watchlist filter
|
||||
|
||||
//Load Terminal Screen
|
||||
var mainMenu = document.getElementById("mainmenu-container");
|
||||
mainMenu.style.visibility = "visible";
|
||||
Terminal.resetTerminalInput();
|
||||
Engine.loadTerminalContent();
|
||||
|
||||
// Refresh Main Menu (the 'World' menu, specifically)
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
|
108
src/Script.js
108
src/Script.js
@ -650,7 +650,8 @@ async function calculateRamUsage(codeCopy) {
|
||||
var workerScript = new WorkerScript({
|
||||
filename:"foo",
|
||||
scriptRef: {code:""},
|
||||
args:[]
|
||||
args:[],
|
||||
getCode: function() { return ""; }
|
||||
});
|
||||
workerScript.checkingRam = true; //Netscript functions will return RAM usage
|
||||
workerScript.serverIp = currServ.ip;
|
||||
@ -778,10 +779,9 @@ Reviver.constructors.Script = Script;
|
||||
//Called when the game is loaded. Loads all running scripts (from all servers)
|
||||
//into worker scripts so that they will start running
|
||||
function loadAllRunningScripts() {
|
||||
var count = 0;
|
||||
var total = 0;
|
||||
let skipScriptLoad = (window.location.href.toLowerCase().indexOf("?noscripts") !== -1);
|
||||
if (skipScriptLoad) {console.log("Skipping the load of any scripts during startup");}
|
||||
if (skipScriptLoad) { console.info("Skipping the load of any scripts during startup"); }
|
||||
for (var property in AllServers) {
|
||||
if (AllServers.hasOwnProperty(property)) {
|
||||
var server = AllServers[property];
|
||||
@ -799,8 +799,6 @@ function loadAllRunningScripts() {
|
||||
server.runningScripts.length = 0;
|
||||
} else {
|
||||
for (var j = 0; j < server.runningScripts.length; ++j) {
|
||||
count++;
|
||||
server.runningScripts[j].scriptRef.module = "";
|
||||
addWorkerScript(server.runningScripts[j], server);
|
||||
|
||||
//Offline production
|
||||
@ -809,8 +807,8 @@ function loadAllRunningScripts() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
console.log("Loaded " + count.toString() + " running scripts");
|
||||
}
|
||||
|
||||
function scriptCalculateOfflineProduction(runningScriptObj) {
|
||||
@ -827,7 +825,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
|
||||
|
||||
//Data map: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
|
||||
//Grow
|
||||
// Grow
|
||||
for (var ip in runningScriptObj.dataMap) {
|
||||
if (runningScriptObj.dataMap.hasOwnProperty(ip)) {
|
||||
if (runningScriptObj.dataMap[ip][2] == 0 || runningScriptObj.dataMap[ip][2] == null) {continue;}
|
||||
@ -841,6 +839,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
|
||||
}
|
||||
}
|
||||
|
||||
// Money from hacking
|
||||
var totalOfflineProduction = 0;
|
||||
for (var ip in runningScriptObj.dataMap) {
|
||||
if (runningScriptObj.dataMap.hasOwnProperty(ip)) {
|
||||
@ -862,19 +861,19 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
|
||||
}
|
||||
}
|
||||
|
||||
//Offline EXP gain
|
||||
//A script's offline production will always be at most half of its online production.
|
||||
// Offline EXP gain
|
||||
// A script's offline production will always be at most half of its online production.
|
||||
var expGain = 0.5 * (runningScriptObj.onlineExpGained / runningScriptObj.onlineRunningTime) * timePassed;
|
||||
expGain *= confidence;
|
||||
|
||||
Player.gainHackingExp(expGain);
|
||||
|
||||
//Update script stats
|
||||
// Update script stats
|
||||
runningScriptObj.offlineMoneyMade += totalOfflineProduction;
|
||||
runningScriptObj.offlineRunningTime += timePassed;
|
||||
runningScriptObj.offlineExpGained += expGain;
|
||||
|
||||
//Fortify a server's security based on how many times it was hacked
|
||||
// Fortify a server's security based on how many times it was hacked
|
||||
for (var ip in runningScriptObj.dataMap) {
|
||||
if (runningScriptObj.dataMap.hasOwnProperty(ip)) {
|
||||
if (runningScriptObj.dataMap[ip][1] == 0 || runningScriptObj.dataMap[ip][1] == null) {continue;}
|
||||
@ -887,7 +886,7 @@ function scriptCalculateOfflineProduction(runningScriptObj) {
|
||||
}
|
||||
}
|
||||
|
||||
//Weaken
|
||||
// Weaken
|
||||
for (var ip in runningScriptObj.dataMap) {
|
||||
if (runningScriptObj.dataMap.hasOwnProperty(ip)) {
|
||||
if (runningScriptObj.dataMap[ip][3] == 0 || runningScriptObj.dataMap[ip][3] == null) {continue;}
|
||||
@ -916,11 +915,11 @@ function findRunningScript(filename, args, server) {
|
||||
}
|
||||
|
||||
function RunningScript(script, args) {
|
||||
if (script == null || script == undefined) {return;}
|
||||
if (script == null || script == undefined) { return; }
|
||||
this.filename = script.filename;
|
||||
this.args = args;
|
||||
this.scriptRef = script;
|
||||
this.server = script.server; //IP Address only
|
||||
this.ramUsage = script.ramUsage;
|
||||
|
||||
this.logs = []; //Script logging. Array of strings, with each element being a log entry
|
||||
this.logUpd = false;
|
||||
@ -935,8 +934,40 @@ function RunningScript(script, args) {
|
||||
|
||||
this.threads = 1;
|
||||
|
||||
//[MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
this.dataMap = new AllServersMap([0, 0, 0, 0], true);
|
||||
// Holds a map of all servers, where server = key and the value for each
|
||||
// server is an array of four numbers. The four numbers represent:
|
||||
// [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
// This data is used for offline progress
|
||||
this.dataMap = {};
|
||||
}
|
||||
|
||||
RunningScript.prototype.getCode = function() {
|
||||
const server = AllServers[this.server];
|
||||
if (server == null) { return ""; }
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename === this.filename) {
|
||||
return server.scripts[i].code;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
RunningScript.prototype.getRamUsage = function() {
|
||||
if (this.ramUsage != null && this.ramUsage > 0) { return this.ramUsage; } // Use cached value
|
||||
|
||||
const server = AllServers[this.server];
|
||||
if (server == null) { return 0; }
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename === this.filename) {
|
||||
// Cache the ram usage for the next call
|
||||
this.ramUsage = server.scripts[i].ramUsage;
|
||||
return this.ramUsage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RunningScript.prototype.log = function(txt) {
|
||||
@ -966,9 +997,8 @@ RunningScript.prototype.clearLog = function() {
|
||||
|
||||
//Update the moneyStolen and numTimesHack maps when hacking
|
||||
RunningScript.prototype.recordHack = function(serverIp, moneyGained, n=1) {
|
||||
if (this.dataMap == null) {
|
||||
//[MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
this.dataMap = new AllServersMap([0, 0, 0, 0], true);
|
||||
if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) {
|
||||
this.dataMap[serverIp] = [0, 0, 0, 0];
|
||||
}
|
||||
this.dataMap[serverIp][0] += moneyGained;
|
||||
this.dataMap[serverIp][1] += n;
|
||||
@ -976,18 +1006,16 @@ RunningScript.prototype.recordHack = function(serverIp, moneyGained, n=1) {
|
||||
|
||||
//Update the grow map when calling grow()
|
||||
RunningScript.prototype.recordGrow = function(serverIp, n=1) {
|
||||
if (this.dataMap == null) {
|
||||
//[MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
this.dataMap = new AllServersMap([0, 0, 0, 0], true);
|
||||
if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) {
|
||||
this.dataMap[serverIp] = [0, 0, 0, 0];
|
||||
}
|
||||
this.dataMap[serverIp][2] += n;
|
||||
}
|
||||
|
||||
//Update the weaken map when calling weaken() {
|
||||
RunningScript.prototype.recordWeaken = function(serverIp, n=1) {
|
||||
if (this.dataMap == null) {
|
||||
//[MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
this.dataMap = new AllServersMap([0, 0, 0, 0], true);
|
||||
if (this.dataMap[serverIp] == null || this.dataMap[serverIp].constructor !== Array) {
|
||||
this.dataMap[serverIp] = [0, 0, 0, 0];
|
||||
}
|
||||
this.dataMap[serverIp][3] += n;
|
||||
}
|
||||
@ -1003,33 +1031,5 @@ RunningScript.fromJSON = function(value) {
|
||||
|
||||
Reviver.constructors.RunningScript = RunningScript;
|
||||
|
||||
//Creates an object that creates a map/dictionary with the IP of each existing server as
|
||||
//a key. Initializes every key with a specified value that can either by a number or an array
|
||||
function AllServersMap(arr=false, filterOwned=false) {
|
||||
for (var ip in AllServers) {
|
||||
if (AllServers.hasOwnProperty(ip)) {
|
||||
if (filterOwned && (AllServers[ip].purchasedByPlayer || AllServers[ip].hostname === "home")) {
|
||||
continue;
|
||||
}
|
||||
if (arr) {
|
||||
this[ip] = [0, 0, 0, 0];
|
||||
} else {
|
||||
this[ip] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AllServersMap.prototype.toJSON = function() {
|
||||
return Generic_toJSON("AllServersMap", this);
|
||||
}
|
||||
|
||||
|
||||
AllServersMap.fromJSON = function(value) {
|
||||
return Generic_fromJSON(AllServersMap, value.data);
|
||||
}
|
||||
|
||||
Reviver.constructors.AllServersMap = AllServersMap;
|
||||
|
||||
export {loadAllRunningScripts, findRunningScript,
|
||||
RunningScript, Script, AllServersMap, scriptEditorInit, isScriptFilename};
|
||||
RunningScript, Script, scriptEditorInit, isScriptFilename};
|
||||
|
@ -33,8 +33,10 @@ export function getPurchaseServerLimit() {
|
||||
}
|
||||
|
||||
export function getPurchaseServerMaxRam() {
|
||||
// TODO ensure this is a power of 2?
|
||||
return Math.round(CONSTANTS.PurchasedServerMaxRam * BitNodeMultipliers.PurchasedServerMaxRam);
|
||||
const ram = Math.round(CONSTANTS.PurchasedServerMaxRam * BitNodeMultipliers.PurchasedServerMaxRam);
|
||||
|
||||
// Round this to the nearest power of 2
|
||||
return 1 << 31 - Math.clz32(ram);
|
||||
}
|
||||
|
||||
// Manually purchase a server (NOT through Netscript)
|
||||
|
@ -640,6 +640,7 @@ function processStockPrices(numCycles=1) {
|
||||
} else {
|
||||
stock.otlkMag -= otlkMagChange;
|
||||
}
|
||||
if (stock.otlkMag > 50) { stock.otlkMag = 50; } // Cap so the "forecast" is between 0 and 100
|
||||
if (stock.otlkMag < 0) {
|
||||
stock.otlkMag *= -1;
|
||||
stock.b = !stock.b;
|
||||
|
1061
src/Terminal.js
1061
src/Terminal.js
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,14 @@ function removeBracketsFromArrayString(str: string) {
|
||||
return strCpy;
|
||||
}
|
||||
|
||||
function removeQuotesFromString(str: string) {
|
||||
let strCpy: string = str;
|
||||
if (strCpy.startsWith('"') || strCpy.startsWith("'")) { strCpy = strCpy.slice(1); }
|
||||
if (strCpy.endsWith('"') || strCpy.endsWith("'")) { strCpy = strCpy.slice(0, -1); }
|
||||
|
||||
return strCpy;
|
||||
}
|
||||
|
||||
function convert2DArrayToString(arr: any[][]) {
|
||||
const components: string[] = [];
|
||||
arr.forEach((e: any) => {
|
||||
@ -73,7 +81,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
desc: (n: number[]) => {
|
||||
return ["Given the following integer array, find the contiguous subarray",
|
||||
"(containing at least one number) which has the largest sum and return that sum.",
|
||||
"'Sum' refers to the sum of all the numbers in the subarray.",
|
||||
"'Sum' refers to the sum of all the numbers in the subarray.\n",
|
||||
`${n.toString()}`].join(" ");
|
||||
},
|
||||
difficulty: 1,
|
||||
@ -152,8 +160,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 2,
|
||||
gen: () => {
|
||||
const m: number = getRandomInt(1, 10);
|
||||
const n: number = getRandomInt(1, 10);
|
||||
const m: number = getRandomInt(1, 15);
|
||||
const n: number = getRandomInt(1, 15);
|
||||
const matrix: number[][] = [];
|
||||
matrix.length = m;
|
||||
for (let i: number = 0; i < m; ++i) {
|
||||
@ -493,4 +501,422 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return release2.toString() === ans;
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: any[]) => {
|
||||
const k: number = (<number>data[0]);
|
||||
const prices: number[] = (<number[]>data[1]);
|
||||
return ["You are given the following array with two elements:\n\n",
|
||||
`[${k}, [${prices}]]\n\n`,
|
||||
"The first element is an integer k. The second element is an",
|
||||
"array of stock prices (which are numbers) where the i-th element",
|
||||
"represents the stock price on day i.\n\n",
|
||||
"Determine the maximum possible profit you can earn using at most",
|
||||
"k transactions. A transaction is defined as buying and then selling",
|
||||
"one share of the stock. Note that you cannot engage in multiple",
|
||||
"transactions at once. In other words, you must sell the stock before",
|
||||
"you can buy it again.\n\n",
|
||||
"If no profit can be made, then the answer should be 0."].join(" ");
|
||||
},
|
||||
difficulty: 8,
|
||||
gen: () => {
|
||||
const k: number = getRandomInt(2, 10);
|
||||
const len: number = getRandomInt(1, 50);
|
||||
const prices: number[] = [];
|
||||
prices.length = len;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
prices[i] = getRandomInt(1, 200);
|
||||
}
|
||||
|
||||
return [k, prices];
|
||||
},
|
||||
name: "Algorithmic Stock Trader IV",
|
||||
numTries: 10,
|
||||
solver: (data: any[], ans: string) => {
|
||||
const k: number = (<number>data[0]);
|
||||
const prices: number[] = (<number[]>data[1]);
|
||||
|
||||
const len = prices.length;
|
||||
if (len < 2) { return (parseInt(ans) === 0); }
|
||||
if (k > len / 2) {
|
||||
let res: number = 0;
|
||||
for (let i = 1; i < len; ++i) {
|
||||
res += Math.max(prices[i] - prices[i-1], 0);
|
||||
}
|
||||
|
||||
return (parseInt(ans) === res);
|
||||
}
|
||||
|
||||
const hold: number[] = [];
|
||||
const rele: number[] = [];
|
||||
hold.length = k + 1;
|
||||
rele.length = k + 1;
|
||||
for (let i = 0; i <= k; ++i) {
|
||||
hold[i] = Number.MIN_SAFE_INTEGER;
|
||||
rele[i] = 0;
|
||||
}
|
||||
|
||||
let cur: number;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
cur = prices[i];
|
||||
for (let j = k; j > 0; --j) {
|
||||
rele[j] = Math.max(rele[j], hold[j] + cur);
|
||||
hold[j] = Math.max(hold[j], rele[j-1] - cur);
|
||||
}
|
||||
}
|
||||
|
||||
return (parseInt(ans) === rele[k]);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: number[][]) => {
|
||||
function createTriangleRecurse(data: number[][], level: number = 0): string {
|
||||
const numLevels: number = data.length;
|
||||
if (level >= numLevels) { return ""; }
|
||||
const numSpaces = numLevels - level + 1;
|
||||
|
||||
let str: string = [" ".repeat(numSpaces), "[", data[level].toString(), "]"].join("");
|
||||
if (level < numLevels - 1) {
|
||||
str += ",";
|
||||
}
|
||||
|
||||
return str + "\n" + createTriangleRecurse(data, level+1);
|
||||
}
|
||||
|
||||
function createTriangle(data: number[][]) {
|
||||
return ["[\n", createTriangleRecurse(data), "]"].join("");
|
||||
}
|
||||
|
||||
const triangle = createTriangle(data);
|
||||
|
||||
return ["Given a triangle, find the minimum path sum from top to bottom. In each step",
|
||||
"of the path, you may only move to adjacent numbers in the row below.",
|
||||
"The triangle is represented as a 2D array of numbers:\n\n",
|
||||
`${triangle}\n\n`,
|
||||
"Example: If you are given the following triangle:\n\n" +
|
||||
"[\n",
|
||||
" [2],\n",
|
||||
" [3,4],\n",
|
||||
" [6,5,7],\n",
|
||||
" [4,1,8,3]\n",
|
||||
"]\n\n",
|
||||
"The minimum path sum is 11 (2 -> 3 -> 5 -> 1)."].join(" ");
|
||||
},
|
||||
difficulty: 5,
|
||||
gen: () => {
|
||||
const triangle: number[][] = [];
|
||||
const levels: number = getRandomInt(1, 12);
|
||||
triangle.length = levels;
|
||||
|
||||
for (let row = 0; row < levels; ++row) {
|
||||
triangle[row] = [];
|
||||
triangle[row].length = row + 1;
|
||||
for (let i = 0; i < triangle[row].length; ++i) {
|
||||
triangle[row][i] = getRandomInt(1, 9);
|
||||
}
|
||||
}
|
||||
|
||||
return triangle;
|
||||
},
|
||||
name: "Minimum Path Sum in a Triangle",
|
||||
numTries: 10,
|
||||
solver: (data: number[][], ans: string) => {
|
||||
let n: number = data.length;
|
||||
let dp: number[] = data[n-1];
|
||||
for (let i = n-2; i > -1; --i) {
|
||||
for (let j = 0; j < data[i].length; ++j) {
|
||||
dp[j] = Math.min(dp[j], dp[j + 1]) + data[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
return dp[0] === parseInt(ans);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: number[]) => {
|
||||
const numRows = data[0];
|
||||
const numColumns = data[1];
|
||||
return ["You are in a grid with",
|
||||
`${numRows} rows and ${numColumns} columns, and you are`,
|
||||
"positioned in the top-left corner of that grid. You are trying to",
|
||||
"reach the bottom-right corner of the grid, but you can only",
|
||||
"move down or right on each step. Determine how many",
|
||||
"unique paths there are from start to finish.\n\n",
|
||||
"NOTE: The data returned for this contract is an array",
|
||||
"with the number or rows and columns:\n\n",
|
||||
`[${numRows}, ${numColumns}]`].join(" ");
|
||||
},
|
||||
difficulty: 3,
|
||||
gen: () => {
|
||||
const numRows: number = getRandomInt(1, 14);
|
||||
const numColumns: number = getRandomInt(1, 14);
|
||||
|
||||
return [numRows, numColumns];
|
||||
},
|
||||
name: "Unique Paths in a Grid I",
|
||||
numTries: 10,
|
||||
solver: (data: number[], ans: string) => {
|
||||
let n: number = data[0]; // Number of rows
|
||||
let m: number = data[1]; // Number of columns
|
||||
let currentRow: number[] = [];
|
||||
currentRow.length = n;
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
currentRow[i] = 1;
|
||||
}
|
||||
for (let row = 1; row < m; row++) {
|
||||
for (let i = 1; i < n; i++) {
|
||||
currentRow[i] += currentRow[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
return parseInt(ans) === currentRow[n - 1];
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: number[][]) => {
|
||||
let gridString: string = "";
|
||||
for (const line of data) {
|
||||
gridString += `${line.toString()},\n`;
|
||||
}
|
||||
return ["You are located in the top-left corner of the following grid:\n\n",
|
||||
`${gridString}\n`,
|
||||
"You are trying reach the bottom-right corner of the grid, but you can only",
|
||||
"move down or right on each step. Furthermore, there are obstacles on the grid",
|
||||
"that you cannot move onto. These obstacles are denoted by '1', while empty",
|
||||
"spaces are denoted by 0.\n\n",
|
||||
"Determine how many unique paths there are from start to finish.\n\n",
|
||||
"NOTE: The data returned for this contract is an 2D array of numbers representing the grid."].join(" ");
|
||||
},
|
||||
difficulty: 5,
|
||||
gen: () => {
|
||||
const numRows: number = getRandomInt(1, 12);
|
||||
const numColumns: number = getRandomInt(1, 12);
|
||||
|
||||
const grid: number[][] = [];
|
||||
grid.length = numRows;
|
||||
for (let i = 0; i < numRows; ++i) {
|
||||
grid[i] = [];
|
||||
grid[i].length = numColumns;
|
||||
grid[i].fill(0);
|
||||
}
|
||||
|
||||
for (let r = 0; r < numRows; ++r) {
|
||||
for (let c = 0; c < numColumns; ++c) {
|
||||
if (r === 0 && c === 0) { continue; }
|
||||
if (r === numRows - 1 && c === numColumns - 1) { continue; }
|
||||
|
||||
// 15% chance of an element being an obstacle
|
||||
if (Math.random() < 0.15) {
|
||||
grid[r][c] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
},
|
||||
name: "Unique Paths in a Grid II",
|
||||
numTries: 10,
|
||||
solver: (data: number[][], ans: string) => {
|
||||
let obstacleGrid: number[][] = [];
|
||||
obstacleGrid.length = data.length;
|
||||
for (let i = 0; i < obstacleGrid.length; ++i) {
|
||||
obstacleGrid[i] = data[i].slice();
|
||||
}
|
||||
|
||||
for (let i = 0; i < obstacleGrid.length; i++) {
|
||||
for (let j = 0; j < obstacleGrid[0].length; j++) {
|
||||
if (obstacleGrid[i][j] == 1) {
|
||||
obstacleGrid[i][j] = 0;
|
||||
} else if (i==0 && j==0) {
|
||||
obstacleGrid[0][0] = 1;
|
||||
} else {
|
||||
obstacleGrid[i][j] = (i > 0 ? obstacleGrid[i-1][j] : 0) + ( j > 0 ? obstacleGrid[i][j-1] : 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return (obstacleGrid[obstacleGrid.length -1][obstacleGrid[0].length-1] === parseInt(ans));
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: string) => {
|
||||
return ["Given the following string:\n\n",
|
||||
`${data}\n\n`,
|
||||
"remove the minimum number of invalid parentheses in order to validate",
|
||||
"the string. If there are multiple minimal ways to validate the string,",
|
||||
"provide all of the possible results. The answer should be provided",
|
||||
"as an array of strings. If it is impossible to validate the string",
|
||||
"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`,
|
||||
`")( -> [""]`].join(" ");
|
||||
},
|
||||
difficulty: 10,
|
||||
gen: () => {
|
||||
const len: number = getRandomInt(2, 20);
|
||||
let chars: string[] = [];
|
||||
chars.length = len;
|
||||
|
||||
// 80% chance of the first parenthesis being (
|
||||
Math.random() < 0.8 ? chars[0] = "(" : chars[0] = ")";
|
||||
|
||||
for (let i = 1; i < len; ++i) {
|
||||
const roll = Math.random();
|
||||
if (roll < 0.4) {
|
||||
chars[i] = "(";
|
||||
} else if (roll < 0.8) {
|
||||
chars[i] = ")";
|
||||
} else {
|
||||
chars[i] = "a";
|
||||
}
|
||||
}
|
||||
|
||||
return chars.join("");
|
||||
},
|
||||
name: "Sanitize Parentheses in Expression",
|
||||
numTries: 10,
|
||||
solver: (data: string, ans: string) => {
|
||||
let left = 0;
|
||||
let right = 0;
|
||||
let res: string[] = [];
|
||||
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
if (data[i] === '(') {
|
||||
++left;
|
||||
} else if (data[i] === ')') {
|
||||
(left > 0) ? --left : ++right;
|
||||
}
|
||||
}
|
||||
|
||||
function dfs(pair: number, index: number, left: number, right: number, s: string, solution: string, res: string[]) {
|
||||
if (s.length === index) {
|
||||
if (left === 0 && right === 0 && pair === 0) {
|
||||
for(var i = 0; i < res.length; i++) {
|
||||
if(res[i] === solution) { return; }
|
||||
}
|
||||
res.push(solution);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (s[index] === '(') {
|
||||
if (left > 0) {
|
||||
dfs(pair, index + 1, left - 1, right, s, solution, res);
|
||||
}
|
||||
dfs(pair + 1, index + 1, left, right, s, solution + s[index], res);
|
||||
} else if (s[index] === ')') {
|
||||
if (right > 0) dfs(pair, index + 1, left, right - 1, s, solution, res);
|
||||
if (pair > 0) dfs(pair - 1, index + 1, left, right, s, solution + s[index], res);
|
||||
} else {
|
||||
dfs(pair, index + 1, left, right, s, solution + s[index], res);
|
||||
}
|
||||
}
|
||||
|
||||
dfs(0, 0, left, right, data, "", res);
|
||||
|
||||
const sanitizedPlayerAns = removeBracketsFromArrayString(ans)
|
||||
.replace(/\s/g, "");
|
||||
|
||||
const playerAnsArray: string[] = sanitizedPlayerAns.split(",");
|
||||
if (playerAnsArray.length !== res.length) { return false; }
|
||||
for (const resultInAnswer of res) {
|
||||
if (!playerAnsArray.includes(resultInAnswer)) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: any[]) => {
|
||||
const digits: string = data[0];
|
||||
const target: number = data[1];
|
||||
|
||||
return ["You are given the following string which contains only digits between 0 and 9:\n\n",
|
||||
`${digits}\n\n`,
|
||||
`You are also given a target number of ${target}. Return all possible ways`,
|
||||
"you can add the +, -, and * operators to the string such that it evaluates",
|
||||
"to the target number.\n\n",
|
||||
"The provided answer should be an array of strings containing the valid expressions.",
|
||||
"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`,
|
||||
"Examples:\n\n",
|
||||
`Input: digits = "123", target = 6\n`,
|
||||
`Output: ["1+2+3", "1*2*3"]\n\n`,
|
||||
`Input: digits = "105", target = 5\n`,
|
||||
`Output: ["1*0+5", "10-5"]`].join(" ");
|
||||
},
|
||||
difficulty: 10,
|
||||
gen: () => {
|
||||
const numDigits = getRandomInt(4, 12);
|
||||
const digitsArray: string[] = [];
|
||||
digitsArray.length = numDigits;
|
||||
for (let i = 0; i < digitsArray.length; ++i) {
|
||||
if (i === 0) {
|
||||
digitsArray[i] = String(getRandomInt(1, 9));
|
||||
} else {
|
||||
digitsArray[i] = String(getRandomInt(0, 9));
|
||||
}
|
||||
}
|
||||
|
||||
const target: number = getRandomInt(-100, 100);
|
||||
const digits: string = digitsArray.join("");
|
||||
|
||||
return [digits, target];
|
||||
},
|
||||
name: "Find All Valid Math Expressions",
|
||||
numTries: 10,
|
||||
solver: (data: any[], ans: string) => {
|
||||
const num: string = data[0];
|
||||
const target: number = data[1];
|
||||
|
||||
function helper(res: string[], path: string, num: string, target: number, pos: number, evaluated: number, multed: number) {
|
||||
if (pos === num.length) {
|
||||
if (target === evaluated) {
|
||||
res.push(path);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = pos; i < num.length; ++i) {
|
||||
if (i != pos && num[pos] == '0') { break; }
|
||||
let cur = parseInt(num.substring(pos, i+1));
|
||||
|
||||
if (pos === 0) {
|
||||
helper(res, path + cur, num, target, i + 1, cur, cur);
|
||||
} else {
|
||||
helper(res, path + "+" + cur, num, target, i + 1, evaluated + cur, cur);
|
||||
helper(res, path + "-" + cur, num, target, i + 1, evaluated - cur, -cur);
|
||||
helper(res, path + "*" + cur, num, target, i + 1, evaluated - multed + multed * cur, multed * cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sanitizedPlayerAns: string = removeBracketsFromArrayString(ans);
|
||||
const sanitizedPlayerAnsArr: string[] = sanitizedPlayerAns.split(",");
|
||||
for (let i = 0; i < sanitizedPlayerAnsArr.length; ++i) {
|
||||
sanitizedPlayerAnsArr[i] = removeQuotesFromString(sanitizedPlayerAnsArr[i]);
|
||||
}
|
||||
|
||||
if (num == null || num.length === 0) {
|
||||
if (sanitizedPlayerAnsArr.length === 0) { return true; }
|
||||
if (sanitizedPlayerAnsArr.length === 1 && sanitizedPlayerAnsArr[0] === "") { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
let result: string[] = [];
|
||||
helper(result, "", num, target, 0, 0, 0);
|
||||
|
||||
for (const expr of result) {
|
||||
if (!sanitizedPlayerAnsArr.includes(expr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -1032,7 +1032,6 @@ const Engine = {
|
||||
//is necessary and then resets the counter
|
||||
checkCounters: function() {
|
||||
if (Engine.Counters.autoSaveCounter <= 0) {
|
||||
saveObject.saveGame(indexedDb);
|
||||
if (Settings.AutosaveInterval == null) {
|
||||
Settings.AutosaveInterval = 60;
|
||||
}
|
||||
@ -1040,6 +1039,7 @@ const Engine = {
|
||||
Engine.Counters.autoSaveCounter = Infinity;
|
||||
} else {
|
||||
Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5;
|
||||
saveObject.saveGame(indexedDb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,16 @@ export function post(input: string) {
|
||||
postContent(input);
|
||||
}
|
||||
|
||||
export function postError(input: string) {
|
||||
postContent(`ERROR: ${input}`, { color: "#ff2929" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds some output to the terminal with an identifier of "hack-progress-bar"
|
||||
* @param input Text or HTML to output to the terminal
|
||||
*/
|
||||
export function hackProgressBarPost(input: string) {
|
||||
postContent(input, "hack-progress-bar");
|
||||
postContent(input, { id: "hack-progress-bar" });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -21,14 +25,19 @@ export function hackProgressBarPost(input: string) {
|
||||
* @param input Text or HTML to output to the terminal
|
||||
*/
|
||||
export function hackProgressPost(input: string) {
|
||||
postContent(input, "hack-progress");
|
||||
postContent(input, { id: "hack-progress" });
|
||||
}
|
||||
|
||||
function postContent(input: string, id?: string) {
|
||||
interface IPostContentConfig {
|
||||
id?: string; // Replaces class, if specified
|
||||
color?: string; // Additional class for terminal-line. Does NOT replace
|
||||
}
|
||||
|
||||
function postContent(input: string, config: IPostContentConfig = {}) {
|
||||
// tslint:disable-next-line:max-line-length
|
||||
const style: string = `color: var(--my-font-color); background-color:var(--my-background-color);${id === undefined ? " white-space:pre-wrap;" : ""}`;
|
||||
const style: string = `color: ${config.color != null ? config.color : "var(--my-font-color)"}; background-color:var(--my-background-color);${config.id === undefined ? " white-space:pre-wrap;" : ""}`;
|
||||
// tslint:disable-next-line:max-line-length
|
||||
const content: string = `<tr class="posted"><td ${id === undefined ? 'class="terminal-line"' : `id="${id}"`} style="${style}">${input}</td></tr>`;
|
||||
const content: string = `<tr class="posted"><td ${config.id === undefined ? `class="terminal-line"` : `id="${config.id}"`} style="${style}">${input}</td></tr>`;
|
||||
const inputElement: HTMLElement = getElementById("terminal-input");
|
||||
inputElement.insertAdjacentHTML("beforebegin", content);
|
||||
scrollTerminalToBottom();
|
||||
|
@ -10,8 +10,8 @@ function replaceAt(base: string, index: number, character: string): string {
|
||||
|
||||
/*
|
||||
Converts a date representing time in milliseconds to a string with the format H hours M minutes and S seconds
|
||||
e.g. 10000 -> "0 hours 0 minutes and 10 seconds"
|
||||
120000 -> "0 0 hours 2 minutes and 0 seconds"
|
||||
e.g. 10000 -> "10 seconds"
|
||||
120000 -> "2 minutes and 0 seconds"
|
||||
*/
|
||||
function convertTimeMsToTimeElapsedString(time: number): string {
|
||||
const millisecondsPerSecond: number = 1000;
|
||||
|
@ -1,6 +1,19 @@
|
||||
/**
|
||||
* Returns the input array as a comma separated string.
|
||||
*
|
||||
* Does several things that Array.toString() doesn't do
|
||||
* - Adds brackets around the array
|
||||
* - Adds quotation marks around strings
|
||||
*/
|
||||
export function arrayToString<T>(a: T[]) {
|
||||
return `[${a.join(", ")}]`;
|
||||
const vals: any[] = [];
|
||||
for (let i = 0; i < a.length; ++i) {
|
||||
let elem: any = a[i];
|
||||
if (typeof elem === "string") {
|
||||
elem = `"${elem}"`;
|
||||
}
|
||||
vals.push(elem);
|
||||
}
|
||||
|
||||
return `[${vals.join(", ")}]`;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ export const KEY: IMap<number> = {
|
||||
DOWNARROW: 40,
|
||||
E: 69,
|
||||
ENTER: 13,
|
||||
ESC: 27,
|
||||
F: 70,
|
||||
H: 72,
|
||||
J: 74,
|
||||
|
@ -13,8 +13,6 @@ interface ICreatePopupCloseButtonOptions {
|
||||
export function createPopupCloseButton(popup: Element | string, options: ICreatePopupCloseButtonOptions) {
|
||||
let button: HTMLButtonElement;
|
||||
|
||||
// TODO event listener works with escape. Add and remove event listener
|
||||
// from document
|
||||
function closePopupWithEscFn(e: any): void {
|
||||
if (e.keyCode === 27) {
|
||||
button.click();
|
||||
|
Loading…
Reference in New Issue
Block a user