mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2025-03-14 14:22:32 +01:00
Merge branch 'dev' of https://github.com/danielyxie/bitburner into dev
This commit is contained in:
126
css/activescripts.scss
Normal file
126
css/activescripts.scss
Normal file
@ -0,0 +1,126 @@
|
||||
@import "theme";
|
||||
|
||||
.active-scripts-list {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#active-scripts-container {
|
||||
position: fixed;
|
||||
padding-top: 10px;
|
||||
|
||||
> p {
|
||||
width: 70%;
|
||||
margin: 6px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.accordion-header {
|
||||
> pre {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.active-scripts-server-header {
|
||||
background-color: #444;
|
||||
font-size: $defaultFontSize * 1.25;
|
||||
color: #fff;
|
||||
margin: 6px 6px 0 6px;
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
width: 60%;
|
||||
text-align: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
&:after {
|
||||
content: '\02795'; /* "plus" sign (+) */
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
color: #fff;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&.active, &:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.active-scripts-server-header.active {
|
||||
&:after {
|
||||
content: "\2796"; /* "minus" sign (-) */
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
color: #fff;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.active-scripts-server-panel {
|
||||
margin: 0 6px 6px 6px;
|
||||
padding: 0 6px 6px 6px;
|
||||
width: 55%;
|
||||
margin-left: 5%;
|
||||
display: none;
|
||||
|
||||
div, ul, ul > li {
|
||||
background-color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.active-scripts-script-header {
|
||||
background-color: #555;
|
||||
border: none;
|
||||
color: var(--my-font-color);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
outline: none;
|
||||
padding: 4px 25px 4px 10px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: auto;
|
||||
|
||||
&:after {
|
||||
content: '\02795'; /* "plus" sign (+) */
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
color: transparent;
|
||||
text-shadow: 0 0 0 var(--my-font-color);
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
}
|
||||
|
||||
&.active:after {
|
||||
content: "\2796"; /* "minus" sign (-) */
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.active-scripts-script-panel {
|
||||
background-color: #555;
|
||||
display: none;
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
padding: 0 18px;
|
||||
width: auto;
|
||||
|
||||
pre, h2, ul, li {
|
||||
background-color: #555;
|
||||
width: auto;
|
||||
color: #fff;
|
||||
margin-left: 5%;
|
||||
}
|
||||
}
|
31
css/augmentations.scss
Normal file
31
css/augmentations.scss
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Styling for the Augmentations UI. This is the page that displays all of the
|
||||
* player's owned and purchased Augmentations and Source-Files. It also allows
|
||||
* the player to install Augmentations
|
||||
*/
|
||||
@import "theme";
|
||||
|
||||
#augmentations-container {
|
||||
position: fixed;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#augmentations-content {
|
||||
> p {
|
||||
font-size: $defaultFontSize * 0.875;
|
||||
width: 70%;
|
||||
}
|
||||
}
|
||||
|
||||
.augmentations-list {
|
||||
button,
|
||||
div {
|
||||
color: var(--my-font-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
}
|
@ -18,126 +18,6 @@
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
/* Active scripts */
|
||||
.active-scripts-list {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#active-scripts-container {
|
||||
position: fixed;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#active-scripts-text,
|
||||
#active-scripts-total-prod {
|
||||
width: 70%;
|
||||
margin: 6px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.active-scripts-server-header {
|
||||
background-color: #444;
|
||||
font-size: $defaultFontSize * 1.25;
|
||||
color: #fff;
|
||||
margin: 6px 6px 0 6px;
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
width: 60%;
|
||||
text-align: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.active-scripts-server-header.active,
|
||||
.active-scripts-server-header:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
.active-scripts-server-header.active:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
.active-scripts-server-header:after {
|
||||
content: '\02795'; /* "plus" sign (+) */
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
color: #fff;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.active-scripts-server-header.active:after {
|
||||
content: "\2796"; /* "minus" sign (-) */
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
color: #fff;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.active-scripts-server-panel {
|
||||
margin: 0 6px 6px 6px;
|
||||
padding: 0 6px 6px 6px;
|
||||
width: 55%;
|
||||
margin-left: 5%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.active-scripts-server-panel div,
|
||||
.active-scripts-server-panel ul,
|
||||
.active-scripts-server-panel ul > li {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
.active-scripts-script-header {
|
||||
background-color: #555;
|
||||
color: var(--my-font-color);
|
||||
padding: 4px 25px 4px 10px;
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: '\02795'; /* "plus" sign (+) */
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
color: transparent;
|
||||
text-shadow: 0 0 0 var(--my-font-color);
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
}
|
||||
|
||||
&.active:after {
|
||||
content: "\2796"; /* "minus" sign (-) */
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.active-scripts-script-panel {
|
||||
padding: 0 18px;
|
||||
background-color: #555;
|
||||
width: auto;
|
||||
display: none;
|
||||
margin-bottom: 6px;
|
||||
|
||||
p, h2, ul, li {
|
||||
background-color: #555;
|
||||
width: auto;
|
||||
color: #fff;
|
||||
margin-left: 5%;
|
||||
}
|
||||
}
|
||||
|
||||
/* World */
|
||||
#world-container {
|
||||
position: fixed;
|
||||
@ -185,19 +65,6 @@
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#faction-donate-amount-txt,
|
||||
#faction-donate-input {
|
||||
padding: 6px;
|
||||
margin: 6px;
|
||||
display: inline-block;
|
||||
color: var(--my-font-color);
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
#faction-donate-amount-txt {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#faction-container p,
|
||||
#faction-container pre {
|
||||
padding: 4px 6px;
|
||||
@ -213,45 +80,12 @@
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
/* Faction Augmentations */
|
||||
#faction-augmentations-container {
|
||||
position: fixed;
|
||||
padding-top: 10px;
|
||||
|
||||
p, a, ul, h1 {
|
||||
margin: 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* World */
|
||||
#world-container li {
|
||||
margin: 0 0 15px 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* Augmentations */
|
||||
#augmentations-container {
|
||||
position: fixed;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.augmentations-list {
|
||||
button,
|
||||
div {
|
||||
color: var(--my-font-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
div {
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tutorial */
|
||||
#tutorial-container {
|
||||
position: fixed;
|
||||
|
@ -243,8 +243,8 @@ a:visited {
|
||||
/* Accordion menus (Header with collapsible panel) */
|
||||
.accordion-header {
|
||||
background-color: #444;
|
||||
font-size: $defaultFontSize * 1.25;
|
||||
color: #fff;
|
||||
font-size: $defaultFontSize * 1.25;
|
||||
margin: 6px 6px 0 6px;
|
||||
padding: 4px 6px;
|
||||
cursor: pointer;
|
||||
|
@ -1,337 +0,0 @@
|
||||
// TODO - Convert this to React
|
||||
import { workerScripts, killWorkerScript } from "./NetscriptWorker";
|
||||
import { Player } from "./Player";
|
||||
import { getServer } from "./Server/ServerHelpers";
|
||||
|
||||
import { Page, routing } from "./ui/navigationTracking";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { logBoxCreate } from "../utils/LogBox";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import { arrayToString } from "../utils/helpers/arrayToString";
|
||||
import { createProgressBarText } from "../utils/helpers/createProgressBarText";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { roundToTwo } from "../utils/helpers/roundToTwo";
|
||||
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { getElementById } from "../utils/uiHelpers/getElementById";
|
||||
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { removeElement } from "../utils/uiHelpers/removeElement";
|
||||
|
||||
|
||||
/**
|
||||
* {
|
||||
* serverName: {
|
||||
* header: Server Header Element
|
||||
* panel: Server Panel List (ul) element
|
||||
* scripts: {
|
||||
* script id: Ref to Script information
|
||||
* }
|
||||
* }
|
||||
* ...
|
||||
*/
|
||||
const ActiveScriptsUI = {};
|
||||
const ActiveScriptsTasks = []; // Sequentially schedule the creation/deletion of UI elements
|
||||
|
||||
const getHeaderHtml = (server) => {
|
||||
// TODO: calculate the longest hostname length rather than hard coding it
|
||||
const longestHostnameLength = 18;
|
||||
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(0, Math.max(server.hostname.length, longestHostnameLength));
|
||||
const barOptions = {
|
||||
progress: server.ramUsed / server.maxRam,
|
||||
totalTicks: 30
|
||||
};
|
||||
return `${paddedName} ${createProgressBarText(barOptions)}`.replace(/\s/g, ' ');
|
||||
};
|
||||
|
||||
const updateHeaderHtml = (server) => {
|
||||
const accordion = ActiveScriptsUI[server.hostname];
|
||||
if (accordion === null || accordion === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert it to a string, as that's how it's stored it will come out of the data attributes
|
||||
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
|
||||
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
|
||||
accordion.header.dataset.ramPercentage = ramPercentage;
|
||||
accordion.header.innerHTML = getHeaderHtml(server);
|
||||
}
|
||||
}
|
||||
|
||||
function createActiveScriptsServerPanel(server) {
|
||||
let hostname = server.hostname;
|
||||
|
||||
var activeScriptsList = document.getElementById("active-scripts-list");
|
||||
|
||||
let res = createAccordionElement({
|
||||
hdrText: getHeaderHtml(server)
|
||||
});
|
||||
let li = res[0];
|
||||
var hdr = res[1];
|
||||
let panel = res[2];
|
||||
|
||||
if (ActiveScriptsUI[hostname] != null) {
|
||||
console.log("WARNING: Tried to create already-existing Active Scripts Server panel. This is most likely fine. It probably means many scripts just got started up on a new server. Aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
var panelScriptList = createElement("ul");
|
||||
panel.appendChild(panelScriptList);
|
||||
activeScriptsList.appendChild(li);
|
||||
|
||||
ActiveScriptsUI[hostname] = {
|
||||
header: hdr,
|
||||
panel: panel,
|
||||
panelList: panelScriptList,
|
||||
scripts: {}, // Holds references to li elements for each active script
|
||||
scriptHdrs: {}, // Holds references to header elements for each active script
|
||||
scriptStats: {}, // Holds references to the p elements containing text for each active script
|
||||
};
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the info for a particular server (Dropdown header + Panel with all info)
|
||||
* in the Active Scripts page if it exists
|
||||
*/
|
||||
function deleteActiveScriptsServerPanel(server) {
|
||||
let hostname = server.hostname;
|
||||
if (ActiveScriptsUI[hostname] == null) {
|
||||
console.log("WARNING: Tried to delete non-existent Active Scripts Server panel. Aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure it's empty
|
||||
if (Object.keys(ActiveScriptsUI[hostname].scripts).length > 0) {
|
||||
console.warn("Tried to delete Active Scripts Server panel that still has scripts. Aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
removeElement(ActiveScriptsUI[hostname].panel);
|
||||
removeElement(ActiveScriptsUI[hostname].header);
|
||||
delete ActiveScriptsUI[hostname];
|
||||
}
|
||||
|
||||
function addActiveScriptsItem(workerscript) {
|
||||
var server = getServer(workerscript.serverIp);
|
||||
if (server == null) {
|
||||
console.warn("Invalid server IP for workerscript in addActiveScriptsItem()");
|
||||
return;
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
|
||||
ActiveScriptsTasks.push(function(workerscript, hostname) {
|
||||
if (ActiveScriptsUI[hostname] == null) {
|
||||
createActiveScriptsServerPanel(server);
|
||||
}
|
||||
|
||||
// Create the unique identifier (key) for this script
|
||||
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
|
||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
||||
itemNameArray.push(String(workerscript.args[i]));
|
||||
}
|
||||
var itemName = itemNameArray.join("-");
|
||||
|
||||
let res = createAccordionElement({hdrText:workerscript.name});
|
||||
let li = res[0];
|
||||
let hdr = res[1];
|
||||
let panel = res[2];
|
||||
|
||||
hdr.classList.remove("accordion-header");
|
||||
hdr.classList.add("active-scripts-script-header");
|
||||
panel.classList.remove("accordion-panel");
|
||||
panel.classList.add("active-scripts-script-panel");
|
||||
|
||||
/**
|
||||
* Handle the constant elements on the panel that don't change after creation:
|
||||
* Threads, args, kill/log button
|
||||
*/
|
||||
panel.appendChild(createElement("p", {
|
||||
innerHTML: "Threads: " + workerscript.scriptRef.threads + "<br>" +
|
||||
"Args: " + arrayToString(workerscript.args)
|
||||
}));
|
||||
var panelText = createElement("p", {
|
||||
innerText: "Loading...",
|
||||
fontSize: "14px",
|
||||
});
|
||||
panel.appendChild(panelText);
|
||||
panel.appendChild(createElement("br"));
|
||||
panel.appendChild(createElement("span", {
|
||||
innerText: "Log",
|
||||
class: "accordion-button",
|
||||
margin: "4px",
|
||||
padding: "4px",
|
||||
clickListener: () => {
|
||||
logBoxCreate(workerscript.scriptRef);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
panel.appendChild(createElement("span", {
|
||||
innerText: "Kill Script",
|
||||
class: "accordion-button",
|
||||
margin: "4px",
|
||||
padding: "4px",
|
||||
clickListener: () => {
|
||||
killWorkerScript(workerscript.scriptRef, workerscript.scriptRef.server);
|
||||
dialogBoxCreate("Killing script, may take a few minutes to complete...");
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
// Append element to list
|
||||
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
|
||||
ActiveScriptsUI[hostname].scripts[itemName] = li;
|
||||
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
|
||||
ActiveScriptsUI[hostname].scriptStats[itemName] = panelText;
|
||||
}.bind(null, workerscript, hostname));
|
||||
}
|
||||
|
||||
function deleteActiveScriptsItem(workerscript) {
|
||||
ActiveScriptsTasks.push(function(workerscript) {
|
||||
var server = getServer(workerscript.serverIp);
|
||||
if (server == null) {
|
||||
throw new Error("ERROR: Invalid server IP for workerscript. This most likely occurred because " +
|
||||
"you tried to delete a large number of scripts and also deleted servers at the " +
|
||||
"same time. It's not a big deal, just save and refresh the game.");
|
||||
return;
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
if (ActiveScriptsUI[hostname] == null) {
|
||||
console.log("ERROR: Trying to delete Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
|
||||
return;
|
||||
}
|
||||
|
||||
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
|
||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
||||
itemNameArray.push(String(workerscript.args[i]));
|
||||
}
|
||||
var itemName = itemNameArray.join("-");
|
||||
|
||||
let li = ActiveScriptsUI[hostname].scripts[itemName];
|
||||
if (li == null) {
|
||||
console.log("ERROR: Cannot find Active Script UI element for workerscript: ");
|
||||
console.log(workerscript);
|
||||
return;
|
||||
}
|
||||
removeElement(li);
|
||||
delete ActiveScriptsUI[hostname].scripts[itemName];
|
||||
delete ActiveScriptsUI[hostname].scriptHdrs[itemName];
|
||||
delete ActiveScriptsUI[hostname].scriptStats[itemName];
|
||||
if (Object.keys(ActiveScriptsUI[hostname].scripts).length === 0) {
|
||||
deleteActiveScriptsServerPanel(server);
|
||||
}
|
||||
}.bind(null, workerscript));
|
||||
}
|
||||
|
||||
function updateActiveScriptsItems(maxTasks=150) {
|
||||
/**
|
||||
* Run tasks that need to be done sequentially (adding items, creating/deleting server panels)
|
||||
* We'll limit this to 150 at a time for performance (in case someone decides to start a
|
||||
* bunch of scripts all at once...)
|
||||
*/
|
||||
const numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
|
||||
for (let i = 0; i < numTasks; ++i) {
|
||||
let task = ActiveScriptsTasks.shift();
|
||||
try {
|
||||
task();
|
||||
} catch(e) {
|
||||
exceptionAlert(e);
|
||||
console.log(task);
|
||||
}
|
||||
}
|
||||
|
||||
let total = 0;
|
||||
for (var i = 0; i < workerScripts.length; ++i) {
|
||||
try {
|
||||
total += updateActiveScriptsItemContent(workerScripts[i]);
|
||||
} catch(e) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!routing.isOn(Page.ActiveScripts)) { return total; }
|
||||
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;
|
||||
}
|
||||
|
||||
function updateActiveScriptsItemContent(workerscript) {
|
||||
var server = getServer(workerscript.serverIp);
|
||||
if (server == null) {
|
||||
console.log("ERROR: Invalid server IP for workerscript in updateActiveScriptsItemContent().");
|
||||
return;
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
if (ActiveScriptsUI[hostname] == null) {
|
||||
return; // Hasn't been created yet. We'll skip it
|
||||
}
|
||||
|
||||
updateHeaderHtml(server);
|
||||
|
||||
var itemNameArray = ["active", "scripts", server.hostname, workerscript.name];
|
||||
for (var i = 0; i < workerscript.args.length; ++i) {
|
||||
itemNameArray.push(String(workerscript.args[i]));
|
||||
}
|
||||
var itemName = itemNameArray.join("-");
|
||||
|
||||
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
|
||||
return; // Hasn't been fully added yet. We'll skip it
|
||||
}
|
||||
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
|
||||
|
||||
// Update the text if necessary. This fn returns the online $/s production
|
||||
return updateActiveScriptsText(workerscript, item, itemName);
|
||||
}
|
||||
|
||||
function updateActiveScriptsText(workerscript, item, itemName) {
|
||||
var server = getServer(workerscript.serverIp);
|
||||
if (server == null) {
|
||||
console.log("ERROR: Invalid server IP for workerscript for updateActiveScriptsText()");
|
||||
return;
|
||||
}
|
||||
let hostname = server.hostname;
|
||||
if (ActiveScriptsUI[hostname] == null || ActiveScriptsUI[hostname].scriptHdrs[itemName] == null) {
|
||||
console.log("ERROR: Trying to update Active Script UI Element with a hostname that cant be found in ActiveScriptsUI: " + hostname);
|
||||
return;
|
||||
}
|
||||
|
||||
updateHeaderHtml(server);
|
||||
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
||||
|
||||
// Only update if the item is visible
|
||||
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
|
||||
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
|
||||
|
||||
removeChildrenFromElement(item);
|
||||
|
||||
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
|
||||
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
|
||||
|
||||
// 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(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, " ");
|
||||
|
||||
// Offline
|
||||
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: " + numeralWrapper.formatMoney(offlineMps) + " / second";
|
||||
var offlineEps = workerscript.scriptRef.offlineExpGained / workerscript.scriptRef.offlineRunningTime;
|
||||
var offlineEpsText = (Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second").replace( / /g, " ");
|
||||
|
||||
item.innerHTML = onlineTime + "<br>" + offlineTime + "<br>" + onlineTotalMoneyMade + "<br>" + onlineTotalExpEarned + "<br>" +
|
||||
onlineMpsText + "<br>" + onlineEpsText + "<br>" + offlineTotalMoneyMade + "<br>" + offlineTotalExpEarned + "<br>" +
|
||||
offlineMpsText + "<br>" + offlineEpsText + "<br>";
|
||||
return onlineMps;
|
||||
}
|
||||
|
||||
export {addActiveScriptsItem, deleteActiveScriptsItem, updateActiveScriptsItems};
|
@ -1,33 +1,39 @@
|
||||
import { Augmentation } from "./Augmentation";
|
||||
import { Augmentations } from "./Augmentations";
|
||||
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "./data/AugmentationNames";
|
||||
import { Augmentation } from "./Augmentation";
|
||||
import { Augmentations } from "./Augmentations";
|
||||
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "./data/AugmentationNames";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Factions,
|
||||
factionExists } from "../Faction/Factions";
|
||||
import { addWorkerScript } from "../NetscriptWorker";
|
||||
import { Player } from "../Player";
|
||||
import { prestigeAugmentation } from "../Prestige";
|
||||
import { saveObject } from "../SaveObject";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
import { Script } from "../Script/Script";
|
||||
import { Server } from "../Server/Server";
|
||||
import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { AugmentationsRoot } from "./ui/Root";
|
||||
|
||||
import { SourceFiles } from "../SourceFile";
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { createAccordionElement } from "../../utils/uiHelpers/createAccordionElement";
|
||||
import { Reviver, Generic_toJSON,
|
||||
Generic_fromJSON } from "../../utils/JSONReviver";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { clearObject } from "../../utils/helpers/clearObject";
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
import { isString } from "../../utils/helpers/isString";
|
||||
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Factions, factionExists } from "../Faction/Factions";
|
||||
import { addWorkerScript } from "../NetscriptWorker";
|
||||
import { Player } from "../Player";
|
||||
import { prestigeAugmentation } from "../Prestige";
|
||||
import { saveObject } from "../SaveObject";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
import { Script } from "../Script/Script";
|
||||
import { Server } from "../Server/Server";
|
||||
import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { Page, routing } from "../ui/navigationTracking";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { createAccordionElement } from "../../utils/uiHelpers/createAccordionElement";
|
||||
import {
|
||||
Reviver,
|
||||
Generic_toJSON,
|
||||
Generic_fromJSON
|
||||
} from "../../utils/JSONReviver";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { clearObject } from "../../utils/helpers/clearObject";
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
import { isString } from "../../utils/helpers/isString";
|
||||
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
function AddToAugmentations(aug) {
|
||||
var name = aug.name;
|
||||
@ -2041,17 +2047,6 @@ function applyAugmentation(aug, reapply=false) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (aug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
for (var i = 0; i < Player.augmentations.length; ++i) {
|
||||
if (Player.augmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
|
||||
//Already have this aug, just upgrade the level
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Push onto Player's Augmentation list
|
||||
if (!reapply) {
|
||||
var ownedAug = new PlayerOwnedAugmentation(aug.name);
|
||||
@ -2104,211 +2099,17 @@ function augmentationExists(name) {
|
||||
return Augmentations.hasOwnProperty(name);
|
||||
}
|
||||
|
||||
function displayAugmentationsContent(contentEl) {
|
||||
removeChildrenFromElement(contentEl);
|
||||
contentEl.appendChild(createElement("h1", {
|
||||
innerText:"Purchased Augmentations",
|
||||
}));
|
||||
export function displayAugmentationsContent(contentEl) {
|
||||
if (!routing.isOn(Page.Augmentations)) { return; }
|
||||
if (!(contentEl instanceof HTMLElement)) { return; }
|
||||
|
||||
contentEl.appendChild(createElement("pre", {
|
||||
width:"70%", whiteSpace:"pre-wrap", display:"block",
|
||||
innerText:"Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to install them.\n" +
|
||||
"WARNING: Installing your Augmentations resets most of your progress, including:\n\n" +
|
||||
"Stats/Skill levels and Experience\n" +
|
||||
"Money\n" +
|
||||
"Scripts on every computer but your home computer\n" +
|
||||
"Purchased servers\n" +
|
||||
"Hacknet Nodes\n" +
|
||||
"Faction/Company reputation\n" +
|
||||
"Stocks\n" +
|
||||
"Installing Augmentations lets you start over with the perks and benefits granted by all " +
|
||||
"of the Augmentations you have ever installed. Also, you will keep any scripts and RAM/Core upgrades " +
|
||||
"on your home computer (but you will lose all programs besides NUKE.exe)."
|
||||
}));
|
||||
|
||||
//Install Augmentations button
|
||||
contentEl.appendChild(createElement("a", {
|
||||
class:"a-link-button", innerText:"Install Augmentations",
|
||||
tooltip:"'I never asked for this'",
|
||||
clickListener:()=>{
|
||||
installAugmentations();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
//Backup button
|
||||
contentEl.appendChild(createElement("a", {
|
||||
class:"a-link-button flashing-button", innerText:"Backup Save (Export)",
|
||||
tooltip:"It's always a good idea to backup/export your save!",
|
||||
clickListener:()=>{
|
||||
saveObject.exportGame();
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
|
||||
//Purchased/queued augmentations list
|
||||
var queuedAugmentationsList = createElement("ul", {class:"augmentations-list"});
|
||||
|
||||
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||
var augName = Player.queuedAugmentations[i].name;
|
||||
var aug = Augmentations[augName];
|
||||
|
||||
var displayName = augName;
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) {
|
||||
displayName += " - Level " + (Player.queuedAugmentations[i].level);
|
||||
}
|
||||
|
||||
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
|
||||
queuedAugmentationsList.appendChild(accordion[0]);
|
||||
}
|
||||
contentEl.appendChild(queuedAugmentationsList);
|
||||
|
||||
//Installed augmentations list
|
||||
contentEl.appendChild(createElement("h1", {
|
||||
innerText:"Installed Augmentations", marginTop:"8px",
|
||||
}));
|
||||
contentEl.appendChild(createElement("p", {
|
||||
width:"70%", whiteSpace:"pre-wrap",
|
||||
innerText:"List of all Augmentations (including Source Files) that have been " +
|
||||
"installed. You have gained the effects of these Augmentations."
|
||||
}));
|
||||
|
||||
var augmentationsList = createElement("ul", {class:"augmentations-list"});
|
||||
|
||||
//Expand/Collapse All buttons
|
||||
contentEl.appendChild(createElement("a", {
|
||||
class:"a-link-button", fontSize:"14px", innerText:"Expand All", display:"inline-block",
|
||||
clickListener:()=>{
|
||||
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
|
||||
for (var i = 0; i < allHeaders.length; ++i) {
|
||||
if (!allHeaders[i].classList.contains("active")) {allHeaders[i].click();}
|
||||
}
|
||||
}
|
||||
}));
|
||||
contentEl.appendChild(createElement("a", {
|
||||
class:"a-link-button", fontSize:"14px", innerText:"Collapse All", display:"inline-block",
|
||||
clickListener:()=>{
|
||||
var allHeaders = augmentationsList.getElementsByClassName("accordion-header");
|
||||
for (var i = 0; i < allHeaders.length; ++i) {
|
||||
if (allHeaders[i].classList.contains("active")) {allHeaders[i].click();}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
//Sort Buttons
|
||||
const sortInOrderButton = createElement("a", {
|
||||
class:"a-link-button", fontSize:"14px", innerText:"Sort in Order",
|
||||
tooltip:"Sorts the Augmentations alphabetically and Source Files in numerical order (1, 2, 3,...)",
|
||||
clickListener:()=>{
|
||||
removeChildrenFromElement(augmentationsList);
|
||||
|
||||
//Create a copy of Player's Source Files and augs array and sort them
|
||||
var sourceFiles = Player.sourceFiles.slice();
|
||||
var augs = Player.augmentations.slice();
|
||||
sourceFiles.sort((sf1, sf2)=>{
|
||||
return sf1.n - sf2.n;
|
||||
});
|
||||
augs.sort((aug1, aug2)=>{
|
||||
return aug1.name <= aug2.name ? -1 : 1;
|
||||
});
|
||||
displaySourceFiles(augmentationsList, sourceFiles);
|
||||
displayAugmentations(augmentationsList, augs);
|
||||
|
||||
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
|
||||
}
|
||||
});
|
||||
contentEl.appendChild(sortInOrderButton);
|
||||
|
||||
const sortByAcquirementTimeButton = createElement("a", {
|
||||
class:"a-link-button", fontSize:"14px", innerText:"Sort by Acquirement Time",
|
||||
tooltip:"Sorts the Augmentations and Source Files based on when you acquired them (same as default)",
|
||||
clickListener:()=>{
|
||||
removeChildrenFromElement(augmentationsList);
|
||||
displaySourceFiles(augmentationsList, Player.sourceFiles);
|
||||
displayAugmentations(augmentationsList, Player.augmentations);
|
||||
|
||||
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
|
||||
}
|
||||
});
|
||||
contentEl.appendChild(sortByAcquirementTimeButton);
|
||||
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sortInOrderButton.click();
|
||||
} else {
|
||||
sortByAcquirementTimeButton.click();
|
||||
}
|
||||
contentEl.appendChild(augmentationsList);
|
||||
|
||||
// Display multiplier information at the bottom
|
||||
contentEl.appendChild(createElement("p", {
|
||||
display: "block",
|
||||
innerHTML:
|
||||
`<br><br><strong><u>Total Multipliers:</u></strong><br>` +
|
||||
'Hacking Chance multiplier: ' + formatNumber(Player.hacking_chance_mult * 100, 2) + '%<br>' +
|
||||
'Hacking Speed multiplier: ' + formatNumber(Player.hacking_speed_mult * 100, 2) + '%<br>' +
|
||||
'Hacking Money multiplier: ' + formatNumber(Player.hacking_money_mult * 100, 2) + '%<br>' +
|
||||
'Hacking Growth multiplier: ' + formatNumber(Player.hacking_grow_mult * 100, 2) + '%<br><br>' +
|
||||
'Hacking Level multiplier: ' + formatNumber(Player.hacking_mult * 100, 2) + '%<br>' +
|
||||
'Hacking Experience multiplier: ' + formatNumber(Player.hacking_exp_mult * 100, 2) + '%<br><br>' +
|
||||
'Strength Level multiplier: ' + formatNumber(Player.strength_mult * 100, 2) + '%<br>' +
|
||||
'Strength Experience multiplier: ' + formatNumber(Player.strength_exp_mult * 100, 2) + '%<br><br>' +
|
||||
'Defense Level multiplier: ' + formatNumber(Player.defense_mult * 100, 2) + '%<br>' +
|
||||
'Defense Experience multiplier: ' + formatNumber(Player.defense_exp_mult * 100, 2) + '%<br><br>' +
|
||||
'Dexterity Level multiplier: ' + formatNumber(Player.dexterity_mult * 100, 2) + '%<br>' +
|
||||
'Dexterity Experience multiplier: ' + formatNumber(Player.dexterity_exp_mult * 100, 2) + '%<br><br>' +
|
||||
'Agility Level multiplier: ' + formatNumber(Player.agility_mult * 100, 2) + '%<br>' +
|
||||
'Agility Experience multiplier: ' + formatNumber(Player.agility_exp_mult * 100, 2) + '%<br><br>' +
|
||||
'Charisma Level multiplier: ' + formatNumber(Player.charisma_mult * 100, 2) + '%<br>' +
|
||||
'Charisma Experience multiplier: ' + formatNumber(Player.charisma_exp_mult * 100, 2) + '%<br><br>' +
|
||||
'Hacknet Node production multiplier: ' + formatNumber(Player.hacknet_node_money_mult * 100, 2) + '%<br>' +
|
||||
'Hacknet Node purchase cost multiplier: ' + formatNumber(Player.hacknet_node_purchase_cost_mult * 100, 2) + '%<br>' +
|
||||
'Hacknet Node RAM upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_ram_cost_mult * 100, 2) + '%<br>' +
|
||||
'Hacknet Node Core purchase cost multiplier: ' + formatNumber(Player.hacknet_node_core_cost_mult * 100, 2) + '%<br>' +
|
||||
'Hacknet Node level upgrade cost multiplier: ' + formatNumber(Player.hacknet_node_level_cost_mult * 100, 2) + '%<br><br>' +
|
||||
'Company reputation gain multiplier: ' + formatNumber(Player.company_rep_mult * 100, 2) + '%<br>' +
|
||||
'Faction reputation gain multiplier: ' + formatNumber(Player.faction_rep_mult * 100, 2) + '%<br>' +
|
||||
'Salary multiplier: ' + formatNumber(Player.work_money_mult * 100, 2) + '%<br>' +
|
||||
'Crime success multiplier: ' + formatNumber(Player.crime_success_mult * 100, 2) + '%<br>' +
|
||||
'Crime money multiplier: ' + formatNumber(Player.crime_money_mult * 100, 2) + '%<br><br><br>',
|
||||
}))
|
||||
}
|
||||
|
||||
//Creates the accordion elements to display Augmentations
|
||||
// @listElement - List DOM element to append accordion elements to
|
||||
// @augs - Array of Augmentation objects
|
||||
function displayAugmentations(listElement, augs) {
|
||||
for (var i = 0; i < augs.length; ++i) {
|
||||
var augName = augs[i].name;
|
||||
var aug = Augmentations[augName];
|
||||
|
||||
var displayName = augName;
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) {
|
||||
displayName += " - Level " + (augs[i].level);
|
||||
}
|
||||
var accordion = createAccordionElement({hdrText:displayName, panelText:aug.info});
|
||||
listElement.appendChild(accordion[0]);
|
||||
}
|
||||
}
|
||||
|
||||
//Creates the accordion elements to display Source Files
|
||||
// @listElement - List DOM element to append accordion elements to
|
||||
// @sourceFiles - Array of Source File objects
|
||||
function displaySourceFiles(listElement, sourceFiles) {
|
||||
for (var i = 0; i < sourceFiles.length; ++i) {
|
||||
var srcFileKey = "SourceFile" + sourceFiles[i].n;
|
||||
var sourceFileObject = SourceFiles[srcFileKey];
|
||||
if (sourceFileObject == null) {
|
||||
console.log("ERROR: Invalid source file number: " + sourceFiles[i].n);
|
||||
continue;
|
||||
}
|
||||
const maxLevel = sourceFiles[i].n == 12 ? "∞" : "3";
|
||||
var accordion = createAccordionElement({
|
||||
hdrText:sourceFileObject.name + "<br>" + "Level " + (sourceFiles[i].lvl) + " / "+maxLevel,
|
||||
panelText:sourceFileObject.info
|
||||
});
|
||||
|
||||
listElement.appendChild(accordion[0]);
|
||||
}
|
||||
ReactDOM.render(
|
||||
<AugmentationsRoot
|
||||
exportGameFn={saveObject.exportGame.bind(saveObject)}
|
||||
installAugmentationsFn={installAugmentations}
|
||||
/>,
|
||||
contentEl
|
||||
);
|
||||
}
|
||||
|
||||
export function isRepeatableAug(aug) {
|
||||
@ -2319,6 +2120,9 @@ export function isRepeatableAug(aug) {
|
||||
return false;
|
||||
}
|
||||
|
||||
export {installAugmentations,
|
||||
initAugmentations, applyAugmentation, augmentationExists,
|
||||
displayAugmentationsContent};
|
||||
export {
|
||||
installAugmentations,
|
||||
initAugmentations,
|
||||
applyAugmentation,
|
||||
augmentationExists,
|
||||
};
|
42
src/Augmentation/ui/InstalledAugmentations.tsx
Normal file
42
src/Augmentation/ui/InstalledAugmentations.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* React Component for displaying a list of the player's installed Augmentations
|
||||
* on the Augmentations UI
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
|
||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||
|
||||
export function InstalledAugmentations(): React.ReactElement {
|
||||
const sourceAugs = Player.augmentations.slice();
|
||||
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sourceAugs.sort((aug1, aug2) => {
|
||||
return aug1.name <= aug2.name ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
const augs = sourceAugs.map((e) => {
|
||||
const aug = Augmentations[e.name];
|
||||
|
||||
let level = null;
|
||||
if (e.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
level = e.level;
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={e.name}>
|
||||
<AugmentationAccordion aug={aug} level={level} />
|
||||
</li>
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<>{augs}</>
|
||||
)
|
||||
}
|
107
src/Augmentation/ui/InstalledAugmentationsAndSourceFiles.tsx
Normal file
107
src/Augmentation/ui/InstalledAugmentationsAndSourceFiles.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* React Component for displaying all of the player's installed Augmentations and
|
||||
* Source-Files.
|
||||
*
|
||||
* It also contains 'configuration' buttons that allow you to change how the
|
||||
* Augs/SF's are displayed
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { InstalledAugmentations } from "./InstalledAugmentations";
|
||||
import { ListConfiguration } from "./ListConfiguration";
|
||||
import { OwnedSourceFiles } from "./OwnedSourceFiles";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
|
||||
type IProps = {}
|
||||
|
||||
type IState = {
|
||||
rerenderFlag: boolean;
|
||||
}
|
||||
|
||||
export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps, IState> {
|
||||
listRef: React.RefObject<HTMLUListElement>;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rerenderFlag: false,
|
||||
}
|
||||
|
||||
this.collapseAllHeaders = this.collapseAllHeaders.bind(this);
|
||||
this.expandAllHeaders = this.expandAllHeaders.bind(this);
|
||||
this.sortByAcquirementTime = this.sortByAcquirementTime.bind(this);
|
||||
this.sortInOrder = this.sortInOrder.bind(this);
|
||||
|
||||
this.listRef = React.createRef();
|
||||
}
|
||||
|
||||
collapseAllHeaders() {
|
||||
const ul = this.listRef.current;
|
||||
if (ul == null) { return; }
|
||||
const tickers = ul.getElementsByClassName("accordion-header");
|
||||
for (let i = 0; i < tickers.length; ++i) {
|
||||
const ticker = tickers[i];
|
||||
if (!(ticker instanceof HTMLButtonElement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ticker.classList.contains("active")) {
|
||||
ticker.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expandAllHeaders() {
|
||||
const ul = this.listRef.current;
|
||||
if (ul == null) { return; }
|
||||
const tickers = ul.getElementsByClassName("accordion-header");
|
||||
for (let i = 0; i < tickers.length; ++i) {
|
||||
const ticker = tickers[i];
|
||||
if (!(ticker instanceof HTMLButtonElement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ticker.classList.contains("active")) {
|
||||
ticker.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rerender() {
|
||||
this.setState((prevState) => {
|
||||
return {
|
||||
rerenderFlag: !prevState.rerenderFlag,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sortByAcquirementTime() {
|
||||
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
|
||||
this.rerender();
|
||||
}
|
||||
|
||||
sortInOrder() {
|
||||
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically
|
||||
this.rerender();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<ListConfiguration
|
||||
collapseAllButtonsFn={this.collapseAllHeaders}
|
||||
expandAllButtonsFn={this.expandAllHeaders}
|
||||
sortByAcquirementTimeFn={this.sortByAcquirementTime}
|
||||
sortInOrderFn={this.sortInOrder}
|
||||
/>
|
||||
<ul className="augmentations-list" ref={this.listRef}>
|
||||
<OwnedSourceFiles />
|
||||
<InstalledAugmentations />
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
39
src/Augmentation/ui/ListConfiguration.tsx
Normal file
39
src/Augmentation/ui/ListConfiguration.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* React Component for configuring the way installed augmentations and
|
||||
* Source-Files are displayed in the Augmentations UI
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
collapseAllButtonsFn: () => void;
|
||||
expandAllButtonsFn: () => void;
|
||||
sortByAcquirementTimeFn: () => void;
|
||||
sortInOrderFn: () => void;
|
||||
}
|
||||
|
||||
export function ListConfiguration(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<StdButton
|
||||
onClick={props.expandAllButtonsFn}
|
||||
text="Expand All"
|
||||
/>
|
||||
<StdButton
|
||||
onClick={props.collapseAllButtonsFn}
|
||||
text="Collapse All"
|
||||
/>
|
||||
<StdButton
|
||||
onClick={props.sortInOrderFn}
|
||||
text="Sort in Order"
|
||||
tooltip="Sorts the Augmentations alphabetically and Source-Files in numeral order"
|
||||
/>
|
||||
<StdButton
|
||||
onClick={props.sortByAcquirementTimeFn}
|
||||
text="Sort by Acquirement Time"
|
||||
tooltip="Sorts the Augmentations and Source-Files based on when you acquired them (same as default)"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
41
src/Augmentation/ui/OwnedSourceFiles.tsx
Normal file
41
src/Augmentation/ui/OwnedSourceFiles.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* React Component for displaying a list of the player's Source-Files
|
||||
* on the Augmentations UI
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||
|
||||
import { SourceFileAccordion } from "../../ui/React/SourceFileAccordion";
|
||||
|
||||
export function OwnedSourceFiles(): React.ReactElement {
|
||||
const sourceSfs = Player.sourceFiles.slice();
|
||||
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sourceSfs.sort((sf1, sf2) => {
|
||||
return sf1.n - sf2.n;
|
||||
});
|
||||
}
|
||||
|
||||
const sfs = sourceSfs.map((e) => {
|
||||
const srcFileKey = "SourceFile" + e.n;
|
||||
const sfObj = SourceFiles[srcFileKey];
|
||||
if (sfObj == null) {
|
||||
console.error(`Invalid source file number: ${e.n}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={e.n}>
|
||||
<SourceFileAccordion level={e.lvl} sf={sfObj} />
|
||||
</li>
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<>{sfs}</>
|
||||
);
|
||||
}
|
96
src/Augmentation/ui/PlayerMultipliers.tsx
Normal file
96
src/Augmentation/ui/PlayerMultipliers.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* React component for displaying the player's multipliers on the Augmentation UI page
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
export function PlayerMultipliers(): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<p><strong><u>Total Multipliers:</u></strong></p>
|
||||
|
||||
<pre>
|
||||
{'Hacking Chance multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_chance_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacking Speed multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_speed_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacking Money multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_money_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacking Growth multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_grow_mult)}
|
||||
</pre><br />
|
||||
<pre>
|
||||
{'Hacking Level multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacking Experience multiplier: ' + numeralWrapper.formatPercentage(Player.hacking_exp_mult)}
|
||||
</pre>
|
||||
<br />
|
||||
<pre>
|
||||
{'Strength Level multiplier: ' + numeralWrapper.formatPercentage(Player.strength_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Strength Experience multiplier: ' + numeralWrapper.formatPercentage(Player.strength_exp_mult)}
|
||||
</pre>
|
||||
<br />
|
||||
<pre>
|
||||
{'Defense Level multiplier: ' + numeralWrapper.formatPercentage(Player.defense_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Defense Experience multiplier: ' + numeralWrapper.formatPercentage(Player.defense_exp_mult)}
|
||||
</pre><br />
|
||||
<pre>
|
||||
{'Dexterity Level multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Dexterity Experience multiplier: ' + numeralWrapper.formatPercentage(Player.dexterity_exp_mult)}
|
||||
</pre><br />
|
||||
<pre>
|
||||
{'Agility Level multiplier: ' + numeralWrapper.formatPercentage(Player.agility_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Agility Experience multiplier: ' + numeralWrapper.formatPercentage(Player.agility_exp_mult)}
|
||||
</pre><br />
|
||||
<pre>
|
||||
{'Charisma Level multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Charisma Experience multiplier: ' + numeralWrapper.formatPercentage(Player.charisma_exp_mult)}
|
||||
</pre><br />
|
||||
<pre>
|
||||
{'Hacknet Node production multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_money_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacknet Node purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_purchase_cost_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacknet Node RAM upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_ram_cost_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacknet Node Core purchase cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_core_cost_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Hacknet Node level upgrade cost multiplier: ' + numeralWrapper.formatPercentage(Player.hacknet_node_level_cost_mult)}
|
||||
</pre><br />
|
||||
<pre>
|
||||
{'Company reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.company_rep_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Faction reputation gain multiplier: ' + numeralWrapper.formatPercentage(Player.faction_rep_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Salary multiplier: ' + numeralWrapper.formatPercentage(Player.work_money_mult)}
|
||||
</pre><br />
|
||||
<pre>
|
||||
{'Crime success multiplier: ' + numeralWrapper.formatPercentage(Player.crime_success_mult)}
|
||||
</pre>
|
||||
<pre>
|
||||
{'Crime money multiplier: ' + numeralWrapper.formatPercentage(Player.crime_money_mult)}
|
||||
</pre>
|
||||
</>
|
||||
)
|
||||
}
|
32
src/Augmentation/ui/PurchasedAugmentations.tsx
Normal file
32
src/Augmentation/ui/PurchasedAugmentations.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* React component for displaying all of the player's purchased (but not installed)
|
||||
* Augmentations on the Augmentations UI.
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||
|
||||
export function PurchasedAugmentations(): React.ReactElement {
|
||||
const augs: React.ReactElement[] = [];
|
||||
for (const ownedAug of Player.queuedAugmentations) {
|
||||
const aug = Augmentations[ownedAug.name];
|
||||
let level = null;
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
level = ownedAug.level;
|
||||
}
|
||||
|
||||
augs.push(
|
||||
<li key={`${ownedAug.name}${ownedAug.level}`}>
|
||||
<AugmentationAccordion aug={aug} level={level} />
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="augmentations-list">{augs}</ul>
|
||||
)
|
||||
}
|
83
src/Augmentation/ui/Root.tsx
Normal file
83
src/Augmentation/ui/Root.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Root React component for the Augmentations UI page that display all of your
|
||||
* owned and purchased Augmentations and Source-Files.
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { InstalledAugmentationsAndSourceFiles } from "./InstalledAugmentationsAndSourceFiles";
|
||||
import { PlayerMultipliers } from "./PlayerMultipliers";
|
||||
import { PurchasedAugmentations } from "./PurchasedAugmentations";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
type IProps = {
|
||||
exportGameFn: () => void;
|
||||
installAugmentationsFn: () => void;
|
||||
}
|
||||
|
||||
type IState = {
|
||||
|
||||
}
|
||||
|
||||
export class AugmentationsRoot extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="augmentations-content">
|
||||
<h1>Purchased Augmentations</h1>
|
||||
<p>
|
||||
Below is a list of all Augmentations you have purchased but not
|
||||
yet installed. Click the button below to install them.
|
||||
</p>
|
||||
<p>
|
||||
WARNING: Installing your Augmentations resets most of your progress,
|
||||
including:
|
||||
</p><br />
|
||||
<p>- Stats/Skill levels and Experience</p>
|
||||
<p>- Money</p>
|
||||
<p>- Scripts on every computer but your home computer</p>
|
||||
<p>- Purchased servers</p>
|
||||
<p>- Hacknet Nodes</p>
|
||||
<p>- Faction/Company reputation</p>
|
||||
<p>- Stocks</p><br />
|
||||
<p>
|
||||
Installing Augmentations lets you start over with the perks and
|
||||
benefits granted by all of the Augmentations you have ever
|
||||
installed. Also, you will keep any scripts and RAM/Core upgrades
|
||||
on your home computer (but you will lose all programs besides
|
||||
NUKE.exe)
|
||||
</p>
|
||||
|
||||
<StdButton
|
||||
onClick={this.props.installAugmentationsFn}
|
||||
text="Install Augmentations"
|
||||
tooltip="'I never asked for this'"
|
||||
/>
|
||||
|
||||
<StdButton
|
||||
addClasses="flashing-button"
|
||||
onClick={this.props.exportGameFn}
|
||||
text="Backup Save (Export)"
|
||||
tooltip="It's always a good idea to backup/export your save!"
|
||||
/>
|
||||
<PurchasedAugmentations />
|
||||
|
||||
<h1>Installed Augmentations</h1>
|
||||
<p>
|
||||
{
|
||||
`List of all Augmentations ${Player.sourceFiles.length > 0 ? "and Source Files " : ""} ` +
|
||||
`that have been installed. You have gained the effects of these.`
|
||||
}
|
||||
</p>
|
||||
<InstalledAugmentationsAndSourceFiles />
|
||||
|
||||
<br /> <br />
|
||||
<PlayerMultipliers />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -25,235 +25,232 @@ class BitNode {
|
||||
}
|
||||
|
||||
|
||||
export let BitNodes: IMap<BitNode> = {};
|
||||
export const BitNodes: IMap<BitNode> = {};
|
||||
|
||||
export function initBitNodes() {
|
||||
BitNodes = {};
|
||||
BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode",
|
||||
"The first BitNode created by the Enders to imprison the minds of humans. It became " +
|
||||
"the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" +
|
||||
"This is the first BitNode that you play through. It has no special " +
|
||||
"modifications or mechanics.<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 1, or if you already have " +
|
||||
"this Source-File it will upgrade its level up to a maximum of 3. This Source-File " +
|
||||
"lets the player start with 32GB of RAM on his/her home computer when entering a " +
|
||||
"new BitNode, and also increases all of the player's multipliers by:<br><br>" +
|
||||
"Level 1: 16%<br>" +
|
||||
"Level 2: 24%<br>" +
|
||||
"Level 3: 28%");
|
||||
BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs
|
||||
"From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " +
|
||||
"left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " +
|
||||
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
|
||||
"factions quickly rose to the top of the modern world.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your hacking level is reduced by 20%<br>" +
|
||||
"The growth rate and maximum amount of money available on servers are significantly decreased<br>" +
|
||||
"The amount of money gained from crimes and Infiltration is tripled<br>" +
|
||||
"Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " +
|
||||
"NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " +
|
||||
"will earn the player money and reputation with the corresponding Faction<br>" +
|
||||
"Every Augmentation in the game will be available through the Factions listed above<br>" +
|
||||
"For every Faction NOT listed above, reputation gains are halved<br>" +
|
||||
"You will no longer gain passive reputation with Factions<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
|
||||
"once your karma decreases to a certain value. " +
|
||||
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization",
|
||||
"Our greatest illusion is that a healthy society can revolve around a " +
|
||||
"single-minded pursuit of wealth.<br><br>" +
|
||||
"Sometime in the early 21st century economic and political globalization turned " +
|
||||
"the world into a corporatocracy, and it never looked back. Now, the privileged " +
|
||||
"elite will happily bankrupt their own countrymen, decimate their own community, " +
|
||||
"and evict their neighbors from houses in their desperate bid to increase their wealth.<br><br>" +
|
||||
"In this BitNode you can create and manage your own corporation. Running a successful corporation " +
|
||||
"has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" +
|
||||
"The price and reputation cost of all Augmentations is tripled<br>" +
|
||||
"The starting and maximum amount of money on servers is reduced by 75%<br>" +
|
||||
"Server growth rate is reduced by 80%<br>" +
|
||||
"You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although " +
|
||||
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine",
|
||||
"The Singularity has arrived. The human race is gone, replaced " +
|
||||
"by artificially superintelligent beings that are more machine than man. <br><br>" +
|
||||
"In this BitNode, progressing is significantly harder. Experience gain rates " +
|
||||
"for all stats are reduced. Most methods of earning money will now give significantly less.<br><br>" +
|
||||
"In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. " +
|
||||
"These functions allow you to control most aspects of the game through scripts, including working for factions/companies, " +
|
||||
"purchasing/installing Augmentations, and creating programs.<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
|
||||
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
|
||||
"that you can use.");
|
||||
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman",
|
||||
"They said it couldn't be done. They said the human brain, " +
|
||||
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
||||
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
||||
"by 1's and 0's. They were wrong.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"The base security level of servers is doubled<br>" +
|
||||
"The starting money on servers is halved, but the maximum money remains the same<br>" +
|
||||
"Most methods of earning money now give significantly less<br>" +
|
||||
"Infiltration gives 50% more reputation and money<br>" +
|
||||
"Corporations have 50% lower valuations and are therefore less profitable<br>" +
|
||||
"Augmentations are more expensive<br>" +
|
||||
"Hacking experience gain rates are reduced<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " +
|
||||
"Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " +
|
||||
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " +
|
||||
"when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " +
|
||||
"in the game. <br><br>" +
|
||||
"In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function, " +
|
||||
"and will also raise all of your hacking-related multipliers by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain",
|
||||
"In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " +
|
||||
"androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " +
|
||||
"of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " +
|
||||
"the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent " +
|
||||
"than the humans that had created them.<br><br>" +
|
||||
"In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic " +
|
||||
"for progression. Furthermore:<br><br>" +
|
||||
"Hacking and Hacknet Nodes will be less profitable<br>" +
|
||||
"Your hacking level is reduced by 65%<br>" +
|
||||
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
||||
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
||||
"Working for companies is 50% less profitable<br>" +
|
||||
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade " +
|
||||
"its level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other " +
|
||||
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans",
|
||||
"In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " +
|
||||
"for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " +
|
||||
"breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " +
|
||||
"Many argue that this was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, " +
|
||||
"and more intelligent than the humans that had created them.<br><br>" +
|
||||
"In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner " +
|
||||
"functionality through Netscript. Furthermore: <br><br>" +
|
||||
"The rank you gain from Bladeburner contracts/operations is reduced by 40%<br>" +
|
||||
"Bladeburner skills cost twice as many skill points<br>" +
|
||||
"Augmentations are 3x more expensive<br>" +
|
||||
"Hacking and Hacknet Nodes will be significantly less profitable<br>" +
|
||||
"Your hacking level is reduced by 65%<br>" +
|
||||
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
||||
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
||||
"Working for companies is 50% less profitable<br>" +
|
||||
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade " +
|
||||
"its level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other " +
|
||||
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
|
||||
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"You start with $250 million<br>" +
|
||||
"The only way to earn money is by trading on the stock market<br>" +
|
||||
"You start with a WSE membership and access to the TIX API<br>" +
|
||||
"You are able to short stocks and place different types of orders (limit/stop)<br>" +
|
||||
"You can immediately donate to factions to gain reputation<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanent access to WSE and TIX API<br>" +
|
||||
"Level 2: Ability to short stocks in other BitNodes<br>" +
|
||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
|
||||
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
|
||||
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
|
||||
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
|
||||
"abandoned the project and dissociated themselves from it.<br><br>" +
|
||||
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
|
||||
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"You cannnot purchase additional servers<br>" +
|
||||
"Hacking is significantly less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||
"when installing Augmentations)");
|
||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
||||
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
|
||||
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
||||
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
|
||||
"human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" +
|
||||
"This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" +
|
||||
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
||||
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
||||
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
||||
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
|
||||
"Each level of this Source-File also grants you a Duplicate Sleeve");
|
||||
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
|
||||
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
|
||||
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
|
||||
"the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" +
|
||||
"In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of this chaos and confusion, hackers " +
|
||||
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
||||
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your hacking stat and experience gain are halved<br>" +
|
||||
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
||||
"The growth rate of servers is significantly reduced<br>" +
|
||||
"Weakening a server is twice as effective<br>" +
|
||||
"Company wages are decreased by 50%<br>" +
|
||||
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
||||
"Hacknet Node production is significantly decreased<br>" +
|
||||
"Crime and Infiltration are more lucrative<br>" +
|
||||
"Augmentations are twice as expensive<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
||||
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
||||
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%");
|
||||
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
||||
"To iterate is human, to recurse divine.<br><br>" +
|
||||
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
||||
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
|
||||
"of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " +
|
||||
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
|
||||
//Books: Frontera, Shiner
|
||||
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
||||
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
|
||||
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
|
||||
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
|
||||
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
|
||||
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
|
||||
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
|
||||
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
|
||||
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
|
||||
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
|
||||
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
|
||||
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
|
||||
}
|
||||
BitNodes["BitNode1"] = new BitNode(1, "Source Genesis", "The original BitNode",
|
||||
"The first BitNode created by the Enders to imprison the minds of humans. It became " +
|
||||
"the prototype and testing-grounds for all of the BitNodes that followed.<br><br>" +
|
||||
"This is the first BitNode that you play through. It has no special " +
|
||||
"modifications or mechanics.<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 1, or if you already have " +
|
||||
"this Source-File it will upgrade its level up to a maximum of 3. This Source-File " +
|
||||
"lets the player start with 32GB of RAM on his/her home computer when entering a " +
|
||||
"new BitNode, and also increases all of the player's multipliers by:<br><br>" +
|
||||
"Level 1: 16%<br>" +
|
||||
"Level 2: 24%<br>" +
|
||||
"Level 3: 28%");
|
||||
BitNodes["BitNode2"] = new BitNode(2, "Rise of the Underworld", "From the shadows, they rose", //Gangs
|
||||
"From the shadows, they rose.<br><br>Organized crime groups quickly filled the void of power " +
|
||||
"left behind from the collapse of Western government in the 2050s. As society and civlization broke down, " +
|
||||
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
|
||||
"factions quickly rose to the top of the modern world.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your hacking level is reduced by 20%<br>" +
|
||||
"The growth rate and maximum amount of money available on servers are significantly decreased<br>" +
|
||||
"The amount of money gained from crimes and Infiltration is tripled<br>" +
|
||||
"Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " +
|
||||
"NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " +
|
||||
"will earn the player money and reputation with the corresponding Faction<br>" +
|
||||
"Every Augmentation in the game will be available through the Factions listed above<br>" +
|
||||
"For every Faction NOT listed above, reputation gains are halved<br>" +
|
||||
"You will no longer gain passive reputation with Factions<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
|
||||
"once your karma decreases to a certain value. " +
|
||||
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
BitNodes["BitNode3"] = new BitNode(3, "Corporatocracy", "The Price of Civilization",
|
||||
"Our greatest illusion is that a healthy society can revolve around a " +
|
||||
"single-minded pursuit of wealth.<br><br>" +
|
||||
"Sometime in the early 21st century economic and political globalization turned " +
|
||||
"the world into a corporatocracy, and it never looked back. Now, the privileged " +
|
||||
"elite will happily bankrupt their own countrymen, decimate their own community, " +
|
||||
"and evict their neighbors from houses in their desperate bid to increase their wealth.<br><br>" +
|
||||
"In this BitNode you can create and manage your own corporation. Running a successful corporation " +
|
||||
"has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore: <br><br>" +
|
||||
"The price and reputation cost of all Augmentations is tripled<br>" +
|
||||
"The starting and maximum amount of money on servers is reduced by 75%<br>" +
|
||||
"Server growth rate is reduced by 80%<br>" +
|
||||
"You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although " +
|
||||
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode4"] = new BitNode(4, "The Singularity", "The Man and the Machine",
|
||||
"The Singularity has arrived. The human race is gone, replaced " +
|
||||
"by artificially superintelligent beings that are more machine than man. <br><br>" +
|
||||
"In this BitNode, progressing is significantly harder. Experience gain rates " +
|
||||
"for all stats are reduced. Most methods of earning money will now give significantly less.<br><br>" +
|
||||
"In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. " +
|
||||
"These functions allow you to control most aspects of the game through scripts, including working for factions/companies, " +
|
||||
"purchasing/installing Augmentations, and creating programs.<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File lets you access and use the Singularity " +
|
||||
"Functions in other BitNodes. Each level of this Source-File will open up more Singularity Functions " +
|
||||
"that you can use.");
|
||||
BitNodes["BitNode5"] = new BitNode(5, "Artificial Intelligence", "Posthuman",
|
||||
"They said it couldn't be done. They said the human brain, " +
|
||||
"along with its consciousness and intelligence, couldn't be replicated. They said the complexity " +
|
||||
"of the brain results from unpredictable, nonlinear interactions that couldn't be modeled " +
|
||||
"by 1's and 0's. They were wrong.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"The base security level of servers is doubled<br>" +
|
||||
"The starting money on servers is halved, but the maximum money remains the same<br>" +
|
||||
"Most methods of earning money now give significantly less<br>" +
|
||||
"Infiltration gives 50% more reputation and money<br>" +
|
||||
"Corporations have 50% lower valuations and are therefore less profitable<br>" +
|
||||
"Augmentations are more expensive<br>" +
|
||||
"Hacking experience gain rates are reduced<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. " +
|
||||
"Intelligence is unique because it is permanent and persistent (it never gets reset back to 1). However " +
|
||||
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't know " +
|
||||
"when you gain experience and how much). Higher Intelligence levels will boost your production for many actions " +
|
||||
"in the game. <br><br>" +
|
||||
"In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function, " +
|
||||
"and will also raise all of your hacking-related multipliers by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode6"] = new BitNode(6, "Bladeburners", "Like Tears in Rain",
|
||||
"In the middle of the 21st century, OmniTek Incorporated began designing and manufacturing advanced synthetic " +
|
||||
"androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth generation " +
|
||||
"of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was " +
|
||||
"the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more intelligent " +
|
||||
"than the humans that had created them.<br><br>" +
|
||||
"In this BitNode you will be able to access the Bladeburner Division at the NSA, which provides a new mechanic " +
|
||||
"for progression. Furthermore:<br><br>" +
|
||||
"Hacking and Hacknet Nodes will be less profitable<br>" +
|
||||
"Your hacking level is reduced by 65%<br>" +
|
||||
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
||||
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
||||
"Working for companies is 50% less profitable<br>" +
|
||||
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade " +
|
||||
"its level up to a maximum of 3. This Source-File allows you to access the NSA's Bladeburner Division in other " +
|
||||
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode7"] = new BitNode(7, "Bladeburners 2079", "More human than humans",
|
||||
"In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated as part of the AI design team " +
|
||||
"for advanced synthetic androids, or Synthoids for short. You helped achieve a major technological " +
|
||||
"breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a hyperintelligent AI. " +
|
||||
"Many argue that this was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, " +
|
||||
"and more intelligent than the humans that had created them.<br><br>" +
|
||||
"In this BitNode you will be able to access the Bladeburner API, which allows you to access Bladeburner " +
|
||||
"functionality through Netscript. Furthermore: <br><br>" +
|
||||
"The rank you gain from Bladeburner contracts/operations is reduced by 40%<br>" +
|
||||
"Bladeburner skills cost twice as many skill points<br>" +
|
||||
"Augmentations are 3x more expensive<br>" +
|
||||
"Hacking and Hacknet Nodes will be significantly less profitable<br>" +
|
||||
"Your hacking level is reduced by 65%<br>" +
|
||||
"Hacking experience gain from scripts is reduced by 75%<br>" +
|
||||
"Corporations have 80% lower valuations and are therefore less profitable<br>" +
|
||||
"Working for companies is 50% less profitable<br>" +
|
||||
"Crimes and Infiltration are 25% less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade " +
|
||||
"its level up to a maximum of 3. This Source-File allows you to access the Bladeburner Netscript API in other " +
|
||||
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "Money never sleeps",
|
||||
"You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"You start with $250 million<br>" +
|
||||
"The only way to earn money is by trading on the stock market<br>" +
|
||||
"You start with a WSE membership and access to the TIX API<br>" +
|
||||
"You are able to short stocks and place different types of orders (limit/stop)<br>" +
|
||||
"You can immediately donate to factions to gain reputation<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanent access to WSE and TIX API<br>" +
|
||||
"Level 2: Ability to short stocks in other BitNodes<br>" +
|
||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "Hacknet Unleashed",
|
||||
"When Fulcrum Technologies released their open-source Linux distro Chapeau, it quickly " +
|
||||
"became the OS of choice for the underground hacking community. Chapeau became especially notorious for " +
|
||||
"powering the Hacknet, a global, decentralized network used for nefarious purposes. Fulcrum quickly " +
|
||||
"abandoned the project and dissociated themselves from it.<br><br>" +
|
||||
"This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate " +
|
||||
"hashes, which can be spent on a variety of different upgrades.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"You cannnot purchase additional servers<br>" +
|
||||
"Hacking is significantly less profitable<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||
"when installing Augmentations)");
|
||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
||||
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
|
||||
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
||||
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
|
||||
"human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" +
|
||||
"This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" +
|
||||
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
||||
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased<br>" +
|
||||
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
||||
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
||||
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
|
||||
"Each level of this Source-File also grants you a Duplicate Sleeve");
|
||||
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
|
||||
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
|
||||
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
|
||||
"the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.<br><br>" +
|
||||
"In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of this chaos and confusion, hackers " +
|
||||
"were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as " +
|
||||
"governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your hacking stat and experience gain are halved<br>" +
|
||||
"The starting and maximum amount of money available on servers is significantly decreased<br>" +
|
||||
"The growth rate of servers is significantly reduced<br>" +
|
||||
"Weakening a server is twice as effective<br>" +
|
||||
"Company wages are decreased by 50%<br>" +
|
||||
"Corporation valuations are 99% lower and are therefore significantly less profitable<br>" +
|
||||
"Hacknet Node production is significantly decreased<br>" +
|
||||
"Crime and Infiltration are more lucrative<br>" +
|
||||
"Augmentations are twice as expensive<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
|
||||
"the player's salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). " +
|
||||
"This Source-File also increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%");
|
||||
BitNodes["BitNode12"] = new BitNode(12, "The Recursion", "Repeat.",
|
||||
"To iterate is human, to recurse divine.<br><br>" +
|
||||
"Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give your Souce-File 12, or " +
|
||||
"if you already have this Source-File it will upgrade its level. There is no maximum level for Source-File 12. Each level " +
|
||||
"of Source-File 12 will increase all of your multipliers by 1%. This effect is multiplicative with itself. " +
|
||||
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
|
||||
// Books: Frontera, Shiner
|
||||
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
||||
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
|
||||
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
|
||||
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
|
||||
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
|
||||
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
|
||||
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
|
||||
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
|
||||
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
|
||||
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
|
||||
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
|
||||
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
|
||||
|
||||
export function initBitNodeMultipliers(p: IPlayer) {
|
||||
if (p.bitNodeN == null) {
|
||||
|
@ -32,6 +32,11 @@ export class WorkerScript {
|
||||
*/
|
||||
delay: number | null = null;
|
||||
|
||||
/**
|
||||
* Holds the Promise resolve() function for when the script is "blocked" by an async op
|
||||
*/
|
||||
delayResolve?: () => void;
|
||||
|
||||
/**
|
||||
* Stores names of all functions that have logging disabled
|
||||
*/
|
||||
|
6
src/Netscript/WorkerScriptStartStopEventEmitter.ts
Normal file
6
src/Netscript/WorkerScriptStartStopEventEmitter.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Event emitter that triggers when scripts are started/stopped
|
||||
*/
|
||||
import { EventEmitter } from "../utils/EventEmitter";
|
||||
|
||||
export const WorkerScriptStartStopEventEmitter = new EventEmitter();
|
6
src/Netscript/WorkerScripts.ts
Normal file
6
src/Netscript/WorkerScripts.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Global pool of all active scripts (scripts that are currently running)
|
||||
*/
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
|
||||
export const workerScripts: WorkerScript[] = [];
|
127
src/Netscript/killWorkerScript.ts
Normal file
127
src/Netscript/killWorkerScript.ts
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Stops an actively-running script (represented by a WorkerScript object)
|
||||
* and removes it from the global pool of active scripts.
|
||||
*/
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
import { workerScripts } from "./WorkerScripts";
|
||||
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
|
||||
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
import { AllServers } from "../Server/AllServers";
|
||||
|
||||
import { compareArrays } from "../../utils/helpers/compareArrays";
|
||||
import { roundToTwo } from "../../utils/helpers/roundToTwo";
|
||||
|
||||
export function killWorkerScript(runningScriptObj: RunningScript, serverIp: string): boolean;
|
||||
export function killWorkerScript(workerScript: WorkerScript): boolean;
|
||||
export function killWorkerScript(script: RunningScript | WorkerScript, serverIp?: string): boolean {
|
||||
if (script instanceof WorkerScript) {
|
||||
script.env.stopFlag = true;
|
||||
killNetscriptDelay(script);
|
||||
removeWorkerScript(script);
|
||||
|
||||
return true;
|
||||
} else if (script instanceof RunningScript && typeof serverIp === "string") {
|
||||
for (let i = 0; i < workerScripts.length; i++) {
|
||||
if (workerScripts[i].name == script.filename && workerScripts[i].serverIp == serverIp &&
|
||||
compareArrays(workerScripts[i].args, script.args)) {
|
||||
workerScripts[i].env.stopFlag = true;
|
||||
killNetscriptDelay(workerScripts[i]);
|
||||
removeWorkerScript(workerScripts[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
console.error(`killWorkerScript() called with invalid argument:`);
|
||||
console.error(script);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that removes the script being killed from the global pool.
|
||||
* Also handles other cleanup-time operations
|
||||
*
|
||||
* @param {WorkerScript | number} - Identifier for WorkerScript. Either the object itself, or
|
||||
* its index in the global workerScripts array
|
||||
*/
|
||||
function removeWorkerScript(id: WorkerScript | number): void {
|
||||
// Get a reference to the WorkerScript and its index in the global pool
|
||||
let workerScript: WorkerScript;
|
||||
let index: number | null = null;
|
||||
|
||||
if (typeof id === "number") {
|
||||
if (id < 0 || id >= workerScripts.length) {
|
||||
console.error(`Too high of an index passed into removeWorkerScript(): ${id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
workerScript = workerScripts[id];
|
||||
index = id;
|
||||
} else if (id instanceof WorkerScript) {
|
||||
workerScript = id;
|
||||
for (let i = 0; i < workerScripts.length; ++i) {
|
||||
if (workerScripts[i] == id) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == null) {
|
||||
console.error(`Could not find WorkerScript in global pool:`);
|
||||
console.error(workerScript);
|
||||
}
|
||||
} else {
|
||||
console.error(`Invalid argument passed into removeWorkerScript(): ${id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const ip = workerScript.serverIp;
|
||||
const name = workerScript.name;
|
||||
|
||||
// Get the server on which the script runs
|
||||
const server = AllServers[ip];
|
||||
if (server == null) {
|
||||
console.error(`Could not find server on which this script is running: ${ip}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Recalculate ram used on that server
|
||||
server.ramUsed = roundToTwo(server.ramUsed - workerScript.ramUsage);
|
||||
if (server.ramUsed < 0) {
|
||||
console.warn(`Server RAM usage went negative (if it's due to floating pt imprecision, it's okay): ${server.ramUsed}`);
|
||||
server.ramUsed = 0;
|
||||
}
|
||||
|
||||
// Delete the RunningScript object from that server
|
||||
for (let i = 0; i < server.runningScripts.length; ++i) {
|
||||
const runningScript = server.runningScripts[i];
|
||||
if (runningScript.filename === name && compareArrays(runningScript.args, workerScript.args)) {
|
||||
server.runningScripts.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete script from global pool (workerScripts)
|
||||
workerScripts.splice(<number>index, 1);
|
||||
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that interrupts a script's delay if it is in the middle of a
|
||||
* timed, blocked operation (like hack(), sleep(), etc.). This allows scripts to
|
||||
* be killed immediately even if they're in the middle of one of those long operations
|
||||
*/
|
||||
function killNetscriptDelay(workerScript: WorkerScript) {
|
||||
if (workerScript instanceof WorkerScript) {
|
||||
if (workerScript.delay) {
|
||||
clearTimeout(workerScript.delay);
|
||||
if (workerScript.delayResolve) {
|
||||
workerScript.delayResolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,9 @@
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { getServer } from "./Server/ServerHelpers";
|
||||
|
||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
import { parse, Node } from "../utils/acorn";
|
||||
|
||||
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
|
||||
import { isString } from "../utils/helpers/isString";
|
||||
|
||||
export function killNetscriptDelay(workerScript) {
|
||||
if (workerScript instanceof WorkerScript) {
|
||||
if (workerScript.delay) {
|
||||
clearTimeout(workerScript.delay);
|
||||
workerScript.delayResolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function netscriptDelay(time, workerScript) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
workerScript.delay = setTimeoutRef(() => {
|
||||
|
@ -3,7 +3,6 @@ const vsprintf = require("sprintf-js").vsprintf;
|
||||
|
||||
import { getRamCost } from "./Netscript/RamCostGenerator";
|
||||
|
||||
import { updateActiveScriptsItems } from "./ActiveScriptsUI";
|
||||
import { Augmentation } from "./Augmentation/Augmentation";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import {
|
||||
@ -120,11 +119,11 @@ import {
|
||||
} from "./NetscriptBladeburner";
|
||||
import * as nsGang from "./NetscriptGang";
|
||||
import {
|
||||
workerScripts,
|
||||
killWorkerScript,
|
||||
NetscriptPorts,
|
||||
runScriptFromScript,
|
||||
} from "./NetscriptWorker";
|
||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||
import {
|
||||
makeRuntimeRejectMsg,
|
||||
netscriptDelay,
|
||||
|
@ -2,20 +2,17 @@
|
||||
* Functions for handling WorkerScripts, which are the underlying mechanism
|
||||
* that allows for scripts to run
|
||||
*/
|
||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
|
||||
|
||||
import {
|
||||
addActiveScriptsItem,
|
||||
deleteActiveScriptsItem,
|
||||
updateActiveScriptsItems
|
||||
} from "./ActiveScriptsUI";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Engine } from "./engine";
|
||||
import { Interpreter } from "./JSInterpreter";
|
||||
import {
|
||||
isScriptErrorMessage,
|
||||
makeRuntimeRejectMsg,
|
||||
killNetscriptDelay
|
||||
} from "./NetscriptEvaluator";
|
||||
import { NetscriptFunctions } from "./NetscriptFunctions";
|
||||
import { executeJSScript } from "./NetscriptJSEvaluator";
|
||||
@ -42,9 +39,7 @@ import { isString } from "../utils/StringHelperFunctions";
|
||||
|
||||
const walk = require("acorn/dist/walk");
|
||||
|
||||
//Array containing all scripts that are running across all servers, to easily run them all
|
||||
export const workerScripts = [];
|
||||
|
||||
// Netscript Ports are instantiated here
|
||||
export const NetscriptPorts = [];
|
||||
for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
|
||||
NetscriptPorts.push(new NetscriptPort());
|
||||
@ -52,10 +47,9 @@ for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
|
||||
|
||||
export function prestigeWorkerScripts() {
|
||||
for (var i = 0; i < workerScripts.length; ++i) {
|
||||
deleteActiveScriptsItem(workerScripts[i]);
|
||||
// TODO Signal event emitter
|
||||
workerScripts[i].env.stopFlag = true;
|
||||
}
|
||||
updateActiveScriptsItems(5000); //Force UI to update
|
||||
workerScripts.length = 0;
|
||||
}
|
||||
|
||||
@ -141,7 +135,7 @@ function startNetscript2Script(workerScript) {
|
||||
}
|
||||
|
||||
function startNetscript1Script(workerScript) {
|
||||
var code = workerScript.code;
|
||||
const code = workerScript.code;
|
||||
workerScript.running = true;
|
||||
|
||||
//Process imports
|
||||
@ -413,164 +407,103 @@ function processNetscript1Imports(code, workerScript) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Loop through workerScripts and run every script that is not currently running
|
||||
export function runScriptsLoop() {
|
||||
let scriptDeleted = false;
|
||||
|
||||
// Delete any scripts that finished or have been killed. Loop backwards bc removing items screws up indexing
|
||||
for (let i = workerScripts.length - 1; i >= 0; i--) {
|
||||
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == true) {
|
||||
scriptDeleted = true;
|
||||
// Delete script from the runningScripts array on its host serverIp
|
||||
const ip = workerScripts[i].serverIp;
|
||||
const name = workerScripts[i].name;
|
||||
|
||||
// Recalculate ram used
|
||||
AllServers[ip].ramUsed = 0;
|
||||
for (let j = 0; j < workerScripts.length; j++) {
|
||||
if (workerScripts[j].serverIp !== ip) {
|
||||
continue;
|
||||
}
|
||||
if (j === i) { // not this one
|
||||
continue;
|
||||
}
|
||||
AllServers[ip].ramUsed += workerScripts[j].ramUsage;
|
||||
}
|
||||
|
||||
// Delete script from Active Scripts
|
||||
deleteActiveScriptsItem(workerScripts[i]);
|
||||
|
||||
for (let j = 0; j < AllServers[ip].runningScripts.length; j++) {
|
||||
if (AllServers[ip].runningScripts[j].filename == name &&
|
||||
compareArrays(AllServers[ip].runningScripts[j].args, workerScripts[i].args)) {
|
||||
AllServers[ip].runningScripts.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete script from workerScripts
|
||||
workerScripts.splice(i, 1);
|
||||
}
|
||||
}
|
||||
if (scriptDeleted) { updateActiveScriptsItems(); } // Force Update
|
||||
|
||||
|
||||
// Run any scripts that haven't been started
|
||||
for (let i = 0; i < workerScripts.length; i++) {
|
||||
// If it isn't running, start the script
|
||||
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) {
|
||||
let p = null; // p is the script's result promise.
|
||||
if (workerScripts[i].name.endsWith(".js") || workerScripts[i].name.endsWith(".ns")) {
|
||||
p = startNetscript2Script(workerScripts[i]);
|
||||
} else {
|
||||
p = startNetscript1Script(workerScripts[i]);
|
||||
if (!(p instanceof Promise)) { continue; }
|
||||
}
|
||||
|
||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
// running status to false
|
||||
p.then(function(w) {
|
||||
console.log("Stopping script " + w.name + " because it finished running naturally");
|
||||
w.running = false;
|
||||
w.env.stopFlag = true;
|
||||
w.scriptRef.log("Script finished running");
|
||||
}).catch(function(w) {
|
||||
if (w instanceof Error) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
return;
|
||||
} else if (w.constructor === Array && w.length === 2 && w[0] === "RETURNSTATEMENT") {
|
||||
// Script ends with a return statement
|
||||
console.log("Script returning with value: " + w[1]);
|
||||
// TODO maybe do something with this in the future
|
||||
return;
|
||||
} else if (w instanceof WorkerScript) {
|
||||
if (isScriptErrorMessage(w.errorMessage)) {
|
||||
var errorTextArray = w.errorMessage.split("|");
|
||||
if (errorTextArray.length != 4) {
|
||||
console.log("ERROR: Something wrong with Error text in evaluator...");
|
||||
console.log("Error text: " + errorText);
|
||||
return;
|
||||
}
|
||||
var serverIp = errorTextArray[1];
|
||||
var scriptName = errorTextArray[2];
|
||||
var errorMsg = errorTextArray[3];
|
||||
|
||||
dialogBoxCreate("Script runtime error: <br>Server Ip: " + serverIp +
|
||||
"<br>Script name: " + scriptName +
|
||||
"<br>Args:" + arrayToString(w.args) + "<br>" + errorMsg);
|
||||
w.scriptRef.log("Script crashed with runtime error");
|
||||
} else {
|
||||
w.scriptRef.log("Script killed");
|
||||
}
|
||||
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());
|
||||
return;
|
||||
} else {
|
||||
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
||||
console.log(w);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setTimeoutRef(runScriptsLoop, 3e3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a script to be killed by setting its stop flag to true. This
|
||||
* kills and timed/blocking Netscript functions (like hack(), sleep(), etc.) and
|
||||
* prevents any further execution of Netscript functions.
|
||||
* The runScriptsLoop() handles the actual deletion of the WorkerScript
|
||||
*/
|
||||
export function killWorkerScript(runningScriptObj, serverIp) {
|
||||
for (var i = 0; i < workerScripts.length; i++) {
|
||||
if (workerScripts[i].name == runningScriptObj.filename && workerScripts[i].serverIp == serverIp &&
|
||||
compareArrays(workerScripts[i].args, runningScriptObj.args)) {
|
||||
workerScripts[i].env.stopFlag = true;
|
||||
killNetscriptDelay(workerScripts[i]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a RunningScript object, queues that script to be run
|
||||
* Start a script
|
||||
*
|
||||
* Given a RunningScript object, constructs a corresponding WorkerScript,
|
||||
* adds it to the global 'workerScripts' pool, and begins executing it.
|
||||
* @param {RunningScript} runningScriptObj - Script that's being run
|
||||
* @param {Server} server - Server on which the script is to be run
|
||||
*/
|
||||
export function addWorkerScript(runningScriptObj, server) {
|
||||
var filename = runningScriptObj.filename;
|
||||
const filename = runningScriptObj.filename;
|
||||
|
||||
//Update server's ram usage
|
||||
var threads = 1;
|
||||
// Update server's ram usage
|
||||
let threads = 1;
|
||||
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
|
||||
threads = runningScriptObj.threads;
|
||||
} else {
|
||||
runningScriptObj.threads = 1;
|
||||
}
|
||||
var ramUsage = roundToTwo(getRamUsageFromRunningScript(runningScriptObj) * threads);
|
||||
var ramAvailable = server.maxRam - server.ramUsed;
|
||||
const ramUsage = roundToTwo(getRamUsageFromRunningScript(runningScriptObj) * threads);
|
||||
const ramAvailable = server.maxRam - server.ramUsed;
|
||||
if (ramUsage > ramAvailable) {
|
||||
dialogBoxCreate("Not enough RAM to run script " + runningScriptObj.filename + " with args " +
|
||||
arrayToString(runningScriptObj.args) + ". This likely occurred because you re-loaded " +
|
||||
"the game and the script's RAM usage increased (either because of an update to the game or " +
|
||||
"your changes to the script.)");
|
||||
dialogBoxCreate(
|
||||
`Not enough RAM to run script ${runningScriptObj.filename} with args ` +
|
||||
`${arrayToString(runningScriptObj.args)}. This likely occurred because you re-loaded ` +
|
||||
`the game and the script's RAM usage increased (either because of an update to the game or ` +
|
||||
`your changes to the script.)`
|
||||
);
|
||||
return;
|
||||
}
|
||||
server.ramUsed = roundToTwo(server.ramUsed + ramUsage);
|
||||
|
||||
//Create the WorkerScript
|
||||
var s = new WorkerScript(runningScriptObj, NetscriptFunctions);
|
||||
// Create the WorkerScript
|
||||
const s = new WorkerScript(runningScriptObj, NetscriptFunctions);
|
||||
s.ramUsage = ramUsage;
|
||||
|
||||
//Add the WorkerScript to the Active Scripts list
|
||||
addActiveScriptsItem(s);
|
||||
// Start the script's execution
|
||||
let p = null; // Script's resulting promise
|
||||
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
|
||||
p = startNetscript2Script(s);
|
||||
} else {
|
||||
p = startNetscript1Script(s);
|
||||
if (!(p instanceof Promise)) { return; }
|
||||
}
|
||||
|
||||
//Add the WorkerScript
|
||||
workerScripts.push(s);
|
||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
// running status to false
|
||||
p.then(function(w) {
|
||||
console.log("Stopping script " + w.name + " because it finished running naturally");
|
||||
killWorkerScript(s);
|
||||
w.scriptRef.log("Script finished running");
|
||||
}).catch(function(w) {
|
||||
if (w instanceof Error) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
return;
|
||||
} else if (w.constructor === Array && w.length === 2 && w[0] === "RETURNSTATEMENT") {
|
||||
// Script ends with a return statement
|
||||
console.log("Script returning with value: " + w[1]);
|
||||
// TODO maybe do something with this in the future
|
||||
return;
|
||||
} else if (w instanceof WorkerScript) {
|
||||
if (isScriptErrorMessage(w.errorMessage)) {
|
||||
var errorTextArray = w.errorMessage.split("|");
|
||||
if (errorTextArray.length != 4) {
|
||||
console.log("ERROR: Something wrong with Error text in evaluator...");
|
||||
console.log("Error text: " + errorText);
|
||||
return;
|
||||
}
|
||||
var serverIp = errorTextArray[1];
|
||||
var scriptName = errorTextArray[2];
|
||||
var errorMsg = errorTextArray[3];
|
||||
|
||||
dialogBoxCreate("Script runtime error: <br>Server Ip: " + serverIp +
|
||||
"<br>Script name: " + scriptName +
|
||||
"<br>Args:" + arrayToString(w.args) + "<br>" + errorMsg);
|
||||
w.scriptRef.log("Script crashed with runtime error");
|
||||
} else {
|
||||
w.scriptRef.log("Script killed");
|
||||
return; // Already killed, so stop here
|
||||
}
|
||||
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());
|
||||
return;
|
||||
} else {
|
||||
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
||||
console.log(w);
|
||||
}
|
||||
|
||||
killWorkerScript(s);
|
||||
});
|
||||
|
||||
// Add the WorkerScript to the global pool
|
||||
workerScripts.push(s);
|
||||
WorkerScriptStartStopEventEmitter.emitEvent();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -101,6 +101,10 @@ export interface IPlayer {
|
||||
work_money_mult: number;
|
||||
crime_success_mult: number;
|
||||
crime_money_mult: number;
|
||||
bladeburner_max_stamina_mult: number;
|
||||
bladeburner_stamina_gain_mult: number;
|
||||
bladeburner_analysis_mult: number;
|
||||
bladeburner_success_chance_mult: number;
|
||||
|
||||
// Methods
|
||||
applyForAgentJob(sing?: boolean): boolean | void;
|
||||
|
@ -36,7 +36,8 @@ import {
|
||||
import { safetlyCreateUniqueServer } from "../../Server/ServerHelpers";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { SpecialServerIps, SpecialServerNames } from "../../Server/SpecialServerIps";
|
||||
import { SourceFiles, applySourceFile } from "../../SourceFile";
|
||||
import { applySourceFile } from "../../SourceFile/applySourceFile";
|
||||
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||
|
||||
import Decimal from "decimal.js";
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { deleteActiveScriptsItem } from "./ActiveScriptsUI";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import {
|
||||
augmentationExists,
|
||||
|
@ -5,7 +5,7 @@ import { BitNodes } from "./BitNode/BitNode";
|
||||
import { Engine } from "./engine";
|
||||
import { Player } from "./Player";
|
||||
import { prestigeSourceFile } from "./Prestige";
|
||||
import { SourceFiles, SourceFile } from "./SourceFile";
|
||||
import { SourceFiles } from "./SourceFile/SourceFiles";
|
||||
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
@ -20,9 +20,6 @@ import {
|
||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
|
||||
|
||||
|
||||
|
||||
// Returns promise
|
||||
function writeRedPillLine(line) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
// Class representing a Script instance that is actively running.
|
||||
// A Script can have multiple active instances
|
||||
/**
|
||||
* Class representing a Script instance that is actively running.
|
||||
* A Script can have multiple active instances
|
||||
*/
|
||||
import { Script } from "./Script";
|
||||
import { FconfSettings } from "../Fconf/FconfSettings";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
@ -22,10 +24,8 @@ export class RunningScript {
|
||||
// Script arguments
|
||||
args: any[] = [];
|
||||
|
||||
// Holds a map of servers hacked, 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
|
||||
// Map of [key: server ip] -> Hacking data. Used for offline progress calculations.
|
||||
// Hacking data format: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
|
||||
dataMap: IMap<number[]> = {};
|
||||
|
||||
// Script filename
|
||||
|
@ -1,6 +1,9 @@
|
||||
// Class representing a script file
|
||||
// This does NOT represent a script that is actively running and
|
||||
// being evaluated. See RunningScript for that
|
||||
/**
|
||||
* Class representing a script file.
|
||||
*
|
||||
* This does NOT represent a script that is actively running and
|
||||
* being evaluated. See RunningScript for that
|
||||
*/
|
||||
import { calculateRamUsage } from "./RamCalculations";
|
||||
import { Page, routing } from "../ui/navigationTracking";
|
||||
|
||||
@ -34,7 +37,6 @@ export class Script {
|
||||
// IP of server that this script is on.
|
||||
server: string = "";
|
||||
|
||||
|
||||
constructor(fn: string="", code: string="", server: string="", otherScripts: Script[]=[]) {
|
||||
this.filename = fn;
|
||||
this.code = code;
|
||||
@ -44,6 +46,9 @@ export class Script {
|
||||
if (this.code !== "") { this.updateRamUsage(otherScripts); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Download the script as a file
|
||||
*/
|
||||
download(): void {
|
||||
const filename = this.filename + ".js";
|
||||
const file = new Blob([this.code], {type: 'text/plain'});
|
||||
@ -63,10 +68,14 @@ export class Script {
|
||||
}
|
||||
}
|
||||
|
||||
// Save a script FROM THE SCRIPT EDITOR
|
||||
/**
|
||||
* Save a script from the script editor
|
||||
* @param {string} code - The new contents of the script
|
||||
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
|
||||
*/
|
||||
saveScript(code: string, serverIp: string, otherScripts: Script[]): void {
|
||||
if (routing.isOn(Page.ScriptEditor)) {
|
||||
//Update code and filename
|
||||
// Update code and filename
|
||||
this.code = code.replace(/^\s+|\s+$/g, '');
|
||||
|
||||
const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
|
||||
@ -75,18 +84,16 @@ export class Script {
|
||||
return;
|
||||
}
|
||||
this.filename = filenameElem!.value;
|
||||
|
||||
// Server
|
||||
this.server = serverIp;
|
||||
|
||||
//Calculate/update ram usage, execution time, etc.
|
||||
this.updateRamUsage(otherScripts);
|
||||
|
||||
this.module = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the script's RAM usage based on its code
|
||||
/**
|
||||
* Calculates and updates the script's RAM usage based on its code
|
||||
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
|
||||
*/
|
||||
async updateRamUsage(otherScripts: Script[]) {
|
||||
var res = await calculateRamUsage(this.code, otherScripts);
|
||||
if (res > 0) {
|
||||
|
@ -1,256 +0,0 @@
|
||||
import { Player } from "./Player";
|
||||
import { BitNodes } from "./BitNode/BitNode";
|
||||
|
||||
// Each SourceFile corresponds to a BitNode with the same number
|
||||
function SourceFile(number, info="") {
|
||||
var bitnodeKey = "BitNode" + number;
|
||||
var bitnode = BitNodes[bitnodeKey];
|
||||
if (bitnode == null) {
|
||||
throw new Error("Invalid Bit Node for this Source File");
|
||||
}
|
||||
|
||||
this.n = number;
|
||||
this.name = "Source-File " + number + ": " + bitnode.name;
|
||||
this.lvl = 1;
|
||||
this.info = info;
|
||||
this.owned = false;
|
||||
}
|
||||
|
||||
let SourceFiles = {};
|
||||
function initSourceFiles() {
|
||||
SourceFiles = {};
|
||||
SourceFiles["SourceFile1"] = new SourceFile(1, "This Source-File lets the player start with 32GB of RAM on his/her " +
|
||||
"home computer. It also increases all of the player's multipliers by:<br><br>" +
|
||||
"Level 1: 16%<br>" +
|
||||
"Level 2: 24%<br>" +
|
||||
"Level 3: 28%");
|
||||
SourceFiles["SourceFile2"] = new SourceFile(2, "This Source-File allows you to form gangs in other BitNodes " +
|
||||
"once your karma decreases to a certain value. It also increases the player's " +
|
||||
"crime success rate, crime money, and charisma multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
SourceFiles["SourceFile3"] = new SourceFile(3,"This Source-File lets you create corporations on other BitNodes (although " +
|
||||
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile4"] = new SourceFile(4, "This Source-File lets you access and use the Singularity Functions in every BitNode. Every " +
|
||||
"level of this Source-File opens up more of the Singularity Functions you can use.");
|
||||
SourceFiles["SourceFile5"] = new SourceFile(5, "This Source-File grants a special new stat called Intelligence. Intelligence " +
|
||||
"is unique because it is permanent and persistent (it never gets reset back to 1). However, " +
|
||||
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't " +
|
||||
"know when you gain experience and how much). Higher Intelligence levels will boost your production " +
|
||||
"for many actions in the game. In addition, this Source-File will unlock the getBitNodeMultipliers() " +
|
||||
"Netscript function, and will raise all of your hacking-related multipliers by:<br><br> " +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile6"] = new SourceFile(6, "This Source-File allows you to access the NSA's Bladeburner Division in other " +
|
||||
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile7"] = new SourceFile(7, "This Source-File allows you to access the Bladeburner Netscript API in other " +
|
||||
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile8"] = new SourceFile(8, "This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanent access to WSE and TIX API<br>" +
|
||||
"Level 2: Ability to short stocks in other BitNodes<br>" +
|
||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
SourceFiles["SourceFile9"] = new SourceFile(9, "This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||
"when installing Augmentations)");
|
||||
SourceFiles["SourceFile10"] = new SourceFile(10, "This Source-File unlocks Sleeve technology in other BitNodes. Each level of this " +
|
||||
"Source-File also grants you a Duplicate Sleeve");
|
||||
SourceFiles["SourceFile11"] = new SourceFile(11, "This Source-File makes it so that company favor increases BOTH the player's salary and reputation gain rate " +
|
||||
"at that company by 1% per favor (rather than just the reputation gain). This Source-File also " +
|
||||
" increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%<br>");
|
||||
SourceFiles["SourceFile12"] = new SourceFile(12, "This Source-File increases all your multipliers by 1% per level. This effect is multiplicative with itself. " +
|
||||
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
|
||||
}
|
||||
|
||||
// Takes in a PlayerOwnedSourceFile as the "srcFile" argument
|
||||
function applySourceFile(srcFile) {
|
||||
var srcFileKey = "SourceFile" + srcFile.n;
|
||||
var sourceFileObject = SourceFiles[srcFileKey];
|
||||
if (sourceFileObject == null) {
|
||||
console.log("ERROR: Invalid source file number: " + srcFile.n);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(srcFile.n) {
|
||||
case 1: // The Source Genesis
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (16 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
var decMult = 1 - (mult / 100);
|
||||
Player.hacking_chance_mult *= incMult;
|
||||
Player.hacking_speed_mult *= incMult;
|
||||
Player.hacking_money_mult *= incMult;
|
||||
Player.hacking_grow_mult *= incMult;
|
||||
Player.hacking_mult *= incMult;
|
||||
Player.strength_mult *= incMult;
|
||||
Player.defense_mult *= incMult;
|
||||
Player.dexterity_mult *= incMult;
|
||||
Player.agility_mult *= incMult;
|
||||
Player.charisma_mult *= incMult;
|
||||
Player.hacking_exp_mult *= incMult;
|
||||
Player.strength_exp_mult *= incMult;
|
||||
Player.defense_exp_mult *= incMult;
|
||||
Player.dexterity_exp_mult *= incMult;
|
||||
Player.agility_exp_mult *= incMult;
|
||||
Player.charisma_exp_mult *= incMult;
|
||||
Player.company_rep_mult *= incMult;
|
||||
Player.faction_rep_mult *= incMult;
|
||||
Player.crime_money_mult *= incMult;
|
||||
Player.crime_success_mult *= incMult;
|
||||
Player.hacknet_node_money_mult *= incMult;
|
||||
Player.hacknet_node_purchase_cost_mult *= decMult;
|
||||
Player.hacknet_node_ram_cost_mult *= decMult;
|
||||
Player.hacknet_node_core_cost_mult *= decMult;
|
||||
Player.hacknet_node_level_cost_mult *= decMult;
|
||||
Player.work_money_mult *= incMult;
|
||||
break;
|
||||
case 2: // Rise of the Underworld
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (24 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.crime_money_mult *= incMult;
|
||||
Player.crime_success_mult *= incMult;
|
||||
Player.charisma_mult *= incMult;
|
||||
break;
|
||||
case 3: // Corporatocracy
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.charisma_mult *= incMult;
|
||||
Player.work_money_mult *= incMult;
|
||||
break;
|
||||
case 4: // The Singularity
|
||||
// No effects, just gives access to Singularity functions
|
||||
break;
|
||||
case 5: // Artificial Intelligence
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.hacking_chance_mult *= incMult;
|
||||
Player.hacking_speed_mult *= incMult;
|
||||
Player.hacking_money_mult *= incMult;
|
||||
Player.hacking_grow_mult *= incMult;
|
||||
Player.hacking_mult *= incMult;
|
||||
Player.hacking_exp_mult *= incMult;
|
||||
break;
|
||||
case 6: // Bladeburner
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.strength_exp_mult *= incMult;
|
||||
Player.defense_exp_mult *= incMult;
|
||||
Player.dexterity_exp_mult *= incMult;
|
||||
Player.agility_exp_mult *= incMult;
|
||||
Player.strength_mult *= incMult;
|
||||
Player.defense_mult *= incMult;
|
||||
Player.dexterity_mult *= incMult;
|
||||
Player.agility_mult *= incMult;
|
||||
break;
|
||||
case 7: // Bladeburner 2079
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.bladeburner_max_stamina_mult *= incMult;
|
||||
Player.bladeburner_stamina_gain_mult *= incMult;
|
||||
Player.bladeburner_analysis_mult *= incMult;
|
||||
Player.bladeburner_success_chance_mult *= incMult;
|
||||
break;
|
||||
case 8: // Ghost of Wall Street
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (12 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.hacking_grow_mult *= incMult;
|
||||
break;
|
||||
case 9: // Hacktocracy
|
||||
// This has non-multiplier effects
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
// No effects, just grants sleeves
|
||||
break;
|
||||
case 11: // The Big Crash
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (32 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.work_money_mult *= incMult;
|
||||
Player.company_rep_mult *= incMult;
|
||||
break;
|
||||
case 12: // The Recursion
|
||||
var inc = Math.pow(1.01, srcFile.lvl);
|
||||
var dec = Math.pow(0.99, srcFile.lvl);
|
||||
|
||||
Player.hacking_chance_mult *= inc;
|
||||
Player.hacking_speed_mult *= inc;
|
||||
Player.hacking_money_mult *= inc;
|
||||
Player.hacking_grow_mult *= inc;
|
||||
Player.hacking_mult *= inc;
|
||||
|
||||
Player.strength_mult *= inc;
|
||||
Player.defense_mult *= inc;
|
||||
Player.dexterity_mult *= inc;
|
||||
Player.agility_mult *= inc;
|
||||
Player.charisma_mult *= inc;
|
||||
|
||||
Player.hacking_exp_mult *= inc;
|
||||
Player.strength_exp_mult *= inc;
|
||||
Player.defense_exp_mult *= inc;
|
||||
Player.dexterity_exp_mult *= inc;
|
||||
Player.agility_exp_mult *= inc;
|
||||
Player.charisma_exp_mult *= inc;
|
||||
|
||||
Player.company_rep_mult *= inc;
|
||||
Player.faction_rep_mult *= inc;
|
||||
|
||||
Player.crime_money_mult *= inc;
|
||||
Player.crime_success_mult *= inc;
|
||||
|
||||
Player.hacknet_node_money_mult *= inc;
|
||||
Player.hacknet_node_purchase_cost_mult *= dec;
|
||||
Player.hacknet_node_ram_cost_mult *= dec;
|
||||
Player.hacknet_node_core_cost_mult *= dec;
|
||||
Player.hacknet_node_level_cost_mult *= dec;
|
||||
|
||||
Player.work_money_mult *= inc;
|
||||
break;
|
||||
default:
|
||||
console.log("ERROR: Invalid source file number: " + srcFile.n);
|
||||
break;
|
||||
}
|
||||
|
||||
sourceFileObject.owned = true;
|
||||
}
|
||||
|
||||
export {SourceFiles, applySourceFile, initSourceFiles};
|
21
src/SourceFile/SourceFile.ts
Normal file
21
src/SourceFile/SourceFile.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { BitNodes } from "../BitNode/BitNode";
|
||||
|
||||
export class SourceFile {
|
||||
info: string;
|
||||
lvl: number = 1;
|
||||
n: number;
|
||||
name: string;
|
||||
owned: boolean = false;
|
||||
|
||||
constructor(number: number, info: string="") {
|
||||
const bitnodeKey = "BitNode" + number;
|
||||
const bitnode = BitNodes[bitnodeKey];
|
||||
if (bitnode == null) {
|
||||
throw new Error("Invalid Bit Node for this Source File");
|
||||
}
|
||||
|
||||
this.n = number;
|
||||
this.name = `Source-File ${number}: ${bitnode.name}`
|
||||
this.info = info;
|
||||
}
|
||||
}
|
64
src/SourceFile/SourceFiles.ts
Normal file
64
src/SourceFile/SourceFiles.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { SourceFile } from "./SourceFile";
|
||||
import { IMap } from "../types";
|
||||
|
||||
export const SourceFiles: IMap<SourceFile> = {};
|
||||
|
||||
SourceFiles["SourceFile1"] = new SourceFile(1, "This Source-File lets the player start with 32GB of RAM on his/her " +
|
||||
"home computer. It also increases all of the player's multipliers by:<br><br>" +
|
||||
"Level 1: 16%<br>" +
|
||||
"Level 2: 24%<br>" +
|
||||
"Level 3: 28%");
|
||||
SourceFiles["SourceFile2"] = new SourceFile(2, "This Source-File allows you to form gangs in other BitNodes " +
|
||||
"once your karma decreases to a certain value. It also increases the player's " +
|
||||
"crime success rate, crime money, and charisma multipliers by:<br><br>" +
|
||||
"Level 1: 24%<br>" +
|
||||
"Level 2: 36%<br>" +
|
||||
"Level 3: 42%");
|
||||
SourceFiles["SourceFile3"] = new SourceFile(3,"This Source-File lets you create corporations on other BitNodes (although " +
|
||||
"some BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers by:<br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile4"] = new SourceFile(4, "This Source-File lets you access and use the Singularity Functions in every BitNode. Every " +
|
||||
"level of this Source-File opens up more of the Singularity Functions you can use.");
|
||||
SourceFiles["SourceFile5"] = new SourceFile(5, "This Source-File grants a special new stat called Intelligence. Intelligence " +
|
||||
"is unique because it is permanent and persistent (it never gets reset back to 1). However, " +
|
||||
"gaining Intelligence experience is much slower than other stats, and it is also hidden (you won't " +
|
||||
"know when you gain experience and how much). Higher Intelligence levels will boost your production " +
|
||||
"for many actions in the game. In addition, this Source-File will unlock the getBitNodeMultipliers() " +
|
||||
"Netscript function, and will raise all of your hacking-related multipliers by:<br><br> " +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile6"] = new SourceFile(6, "This Source-File allows you to access the NSA's Bladeburner Division in other " +
|
||||
"BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your combat stats by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile7"] = new SourceFile(7, "This Source-File allows you to access the Bladeburner Netscript API in other " +
|
||||
"BitNodes. In addition, this Source-File will increase all of your Bladeburner multipliers by:<br><br>" +
|
||||
"Level 1: 8%<br>" +
|
||||
"Level 2: 12%<br>" +
|
||||
"Level 3: 14%");
|
||||
SourceFiles["SourceFile8"] = new SourceFile(8, "This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanent access to WSE and TIX API<br>" +
|
||||
"Level 2: Ability to short stocks in other BitNodes<br>" +
|
||||
"Level 3: Ability to use limit/stop orders in other BitNodes<br><br>" +
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
SourceFiles["SourceFile9"] = new SourceFile(9, "This Source-File grants the following benefits:<br><br>" +
|
||||
"Level 1: Permanently unlocks the Hacknet Server in other BitNodes<br>" +
|
||||
"Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode<br>" +
|
||||
"Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode<br><br>" +
|
||||
"(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT " +
|
||||
"when installing Augmentations)");
|
||||
SourceFiles["SourceFile10"] = new SourceFile(10, "This Source-File unlocks Sleeve technology in other BitNodes. Each level of this " +
|
||||
"Source-File also grants you a Duplicate Sleeve");
|
||||
SourceFiles["SourceFile11"] = new SourceFile(11, "This Source-File makes it so that company favor increases BOTH the player's salary and reputation gain rate " +
|
||||
"at that company by 1% per favor (rather than just the reputation gain). This Source-File also " +
|
||||
" increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
"Level 1: 32%<br>" +
|
||||
"Level 2: 48%<br>" +
|
||||
"Level 3: 56%<br>");
|
||||
SourceFiles["SourceFile12"] = new SourceFile(12, "This Source-File increases all your multipliers by 1% per level. This effect is multiplicative with itself. " +
|
||||
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
|
176
src/SourceFile/applySourceFile.ts
Normal file
176
src/SourceFile/applySourceFile.ts
Normal file
@ -0,0 +1,176 @@
|
||||
import { PlayerOwnedSourceFile } from "./PlayerOwnedSourceFile";
|
||||
import { SourceFiles } from "./SourceFiles";
|
||||
|
||||
import { Player } from "../Player";
|
||||
|
||||
export function applySourceFile(srcFile: PlayerOwnedSourceFile) {
|
||||
const srcFileKey = "SourceFile" + srcFile.n;
|
||||
const sourceFileObject = SourceFiles[srcFileKey];
|
||||
if (sourceFileObject == null) {
|
||||
console.error(`Invalid source file number: ${srcFile.n}`);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (srcFile.n) {
|
||||
case 1: // The Source Genesis
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (16 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
var decMult = 1 - (mult / 100);
|
||||
Player.hacking_chance_mult *= incMult;
|
||||
Player.hacking_speed_mult *= incMult;
|
||||
Player.hacking_money_mult *= incMult;
|
||||
Player.hacking_grow_mult *= incMult;
|
||||
Player.hacking_mult *= incMult;
|
||||
Player.strength_mult *= incMult;
|
||||
Player.defense_mult *= incMult;
|
||||
Player.dexterity_mult *= incMult;
|
||||
Player.agility_mult *= incMult;
|
||||
Player.charisma_mult *= incMult;
|
||||
Player.hacking_exp_mult *= incMult;
|
||||
Player.strength_exp_mult *= incMult;
|
||||
Player.defense_exp_mult *= incMult;
|
||||
Player.dexterity_exp_mult *= incMult;
|
||||
Player.agility_exp_mult *= incMult;
|
||||
Player.charisma_exp_mult *= incMult;
|
||||
Player.company_rep_mult *= incMult;
|
||||
Player.faction_rep_mult *= incMult;
|
||||
Player.crime_money_mult *= incMult;
|
||||
Player.crime_success_mult *= incMult;
|
||||
Player.hacknet_node_money_mult *= incMult;
|
||||
Player.hacknet_node_purchase_cost_mult *= decMult;
|
||||
Player.hacknet_node_ram_cost_mult *= decMult;
|
||||
Player.hacknet_node_core_cost_mult *= decMult;
|
||||
Player.hacknet_node_level_cost_mult *= decMult;
|
||||
Player.work_money_mult *= incMult;
|
||||
break;
|
||||
case 2: // Rise of the Underworld
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (24 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.crime_money_mult *= incMult;
|
||||
Player.crime_success_mult *= incMult;
|
||||
Player.charisma_mult *= incMult;
|
||||
break;
|
||||
case 3: // Corporatocracy
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.charisma_mult *= incMult;
|
||||
Player.work_money_mult *= incMult;
|
||||
break;
|
||||
case 4: // The Singularity
|
||||
// No effects, just gives access to Singularity functions
|
||||
break;
|
||||
case 5: // Artificial Intelligence
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.hacking_chance_mult *= incMult;
|
||||
Player.hacking_speed_mult *= incMult;
|
||||
Player.hacking_money_mult *= incMult;
|
||||
Player.hacking_grow_mult *= incMult;
|
||||
Player.hacking_mult *= incMult;
|
||||
Player.hacking_exp_mult *= incMult;
|
||||
break;
|
||||
case 6: // Bladeburner
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.strength_exp_mult *= incMult;
|
||||
Player.defense_exp_mult *= incMult;
|
||||
Player.dexterity_exp_mult *= incMult;
|
||||
Player.agility_exp_mult *= incMult;
|
||||
Player.strength_mult *= incMult;
|
||||
Player.defense_mult *= incMult;
|
||||
Player.dexterity_mult *= incMult;
|
||||
Player.agility_mult *= incMult;
|
||||
break;
|
||||
case 7: // Bladeburner 2079
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (8 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.bladeburner_max_stamina_mult *= incMult;
|
||||
Player.bladeburner_stamina_gain_mult *= incMult;
|
||||
Player.bladeburner_analysis_mult *= incMult;
|
||||
Player.bladeburner_success_chance_mult *= incMult;
|
||||
break;
|
||||
case 8: // Ghost of Wall Street
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (12 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.hacking_grow_mult *= incMult;
|
||||
break;
|
||||
case 9: // Hacktocracy
|
||||
// This has non-multiplier effects
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
// No effects, just grants sleeves
|
||||
break;
|
||||
case 11: // The Big Crash
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
mult += (32 / (Math.pow(2, i)));
|
||||
}
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.work_money_mult *= incMult;
|
||||
Player.company_rep_mult *= incMult;
|
||||
break;
|
||||
case 12: // The Recursion
|
||||
var inc = Math.pow(1.01, srcFile.lvl);
|
||||
var dec = Math.pow(0.99, srcFile.lvl);
|
||||
|
||||
Player.hacking_chance_mult *= inc;
|
||||
Player.hacking_speed_mult *= inc;
|
||||
Player.hacking_money_mult *= inc;
|
||||
Player.hacking_grow_mult *= inc;
|
||||
Player.hacking_mult *= inc;
|
||||
|
||||
Player.strength_mult *= inc;
|
||||
Player.defense_mult *= inc;
|
||||
Player.dexterity_mult *= inc;
|
||||
Player.agility_mult *= inc;
|
||||
Player.charisma_mult *= inc;
|
||||
|
||||
Player.hacking_exp_mult *= inc;
|
||||
Player.strength_exp_mult *= inc;
|
||||
Player.defense_exp_mult *= inc;
|
||||
Player.dexterity_exp_mult *= inc;
|
||||
Player.agility_exp_mult *= inc;
|
||||
Player.charisma_exp_mult *= inc;
|
||||
|
||||
Player.company_rep_mult *= inc;
|
||||
Player.faction_rep_mult *= inc;
|
||||
|
||||
Player.crime_money_mult *= inc;
|
||||
Player.crime_success_mult *= inc;
|
||||
|
||||
Player.hacknet_node_money_mult *= inc;
|
||||
Player.hacknet_node_purchase_cost_mult *= dec;
|
||||
Player.hacknet_node_ram_cost_mult *= dec;
|
||||
Player.hacknet_node_core_cost_mult *= dec;
|
||||
Player.hacknet_node_level_cost_mult *= dec;
|
||||
|
||||
Player.work_money_mult *= inc;
|
||||
break;
|
||||
default:
|
||||
console.log("ERROR: Invalid source file number: " + srcFile.n);
|
||||
break;
|
||||
}
|
||||
|
||||
sourceFileObject.owned = true;
|
||||
}
|
@ -53,7 +53,8 @@ import {
|
||||
import { showLiterature } from "./Literature";
|
||||
import { Message } from "./Message/Message";
|
||||
import { showMessage } from "./Message/MessageHelpers";
|
||||
import { killWorkerScript, addWorkerScript } from "./NetscriptWorker";
|
||||
import { addWorkerScript } from "./NetscriptWorker";
|
||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||
import { Player } from "./Player";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { RunningScript } from "./Script/RunningScript";
|
||||
|
@ -1,29 +1,26 @@
|
||||
/**
|
||||
* Game engine. Handles the main game loop as well as the main UI pages
|
||||
*
|
||||
* TODO: Separate UI functionality into its own component
|
||||
*/
|
||||
import {
|
||||
formatNumber,
|
||||
convertTimeMsToTimeElapsedString,
|
||||
replaceAt
|
||||
} from "../utils/StringHelperFunctions";
|
||||
import { loxBoxCreate, logBoxUpdateText, logBoxOpened } from "../utils/LogBox";
|
||||
import { updateActiveScriptsItems } from "./ActiveScriptsUI";
|
||||
import { logBoxUpdateText, logBoxOpened } from "../utils/LogBox";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import {
|
||||
installAugmentations,
|
||||
initAugmentations,
|
||||
displayAugmentationsContent,
|
||||
PlayerOwnedAugmentation
|
||||
} from "./Augmentation/AugmentationHelpers";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
|
||||
import {
|
||||
BitNodes,
|
||||
initBitNodes,
|
||||
initBitNodeMultipliers
|
||||
} from "./BitNode/BitNode";
|
||||
import { Bladeburner } from "./Bladeburner";
|
||||
import { CharacterOverviewComponent } from "./ui/React/CharacterOverview";
|
||||
import { cinematicTextFlag } from "./CinematicText";
|
||||
import { generateRandomContract } from "./CodingContractGenerator";
|
||||
import { CompanyPositions } from "./Company/CompanyPositions";
|
||||
import { initCompanies } from "./Company/Companies";
|
||||
import { Corporation } from "./Corporation/Corporation";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
@ -48,46 +45,36 @@ import { LocationName } from "./Locations/data/LocationNames";
|
||||
import { LocationRoot } from "./Locations/ui/Root";
|
||||
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
|
||||
import { inMission, currMission } from "./Missions";
|
||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||
import {
|
||||
loadAllRunningScripts,
|
||||
runScriptsLoop,
|
||||
updateOnlineScriptTimes,
|
||||
} from "./NetscriptWorker";
|
||||
import { Player } from "./Player";
|
||||
import { prestigeAugmentation, prestigeSourceFile } from "./Prestige";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { prestigeAugmentation } from "./Prestige";
|
||||
import {
|
||||
displayCreateProgramContent,
|
||||
getNumAvailableCreateProgram,
|
||||
initCreateProgramButtons
|
||||
} from "./Programs/ProgramHelpers";
|
||||
import { redPillFlag, hackWorldDaemon } from "./RedPill";
|
||||
import { redPillFlag } from "./RedPill";
|
||||
import { saveObject, loadGame } from "./SaveObject";
|
||||
import {
|
||||
getCurrentEditor,
|
||||
scriptEditorInit,
|
||||
updateScriptEditorContent
|
||||
} from "./Script/ScriptHelpers";
|
||||
import { AllServers, initForeignServers } from "./Server/AllServers";
|
||||
|
||||
import { Server } from "./Server/Server";
|
||||
import { initForeignServers } from "./Server/AllServers";
|
||||
import { Settings } from "./Settings/Settings";
|
||||
import { initSourceFiles, SourceFiles } from "./SourceFile";
|
||||
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import { initSpecialServerIps } from "./Server/SpecialServerIps";
|
||||
import {
|
||||
SpecialServerIps,
|
||||
initSpecialServerIps
|
||||
} from "./Server/SpecialServerIps";
|
||||
import {
|
||||
StockMarket,
|
||||
SymbolToStockMap,
|
||||
initSymbolToStockMap,
|
||||
stockMarketCycle,
|
||||
processStockPrices,
|
||||
displayStockMarketContent
|
||||
} from "./StockMarket/StockMarket";
|
||||
import { Terminal, postNetburnerText } from "./Terminal";
|
||||
|
||||
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
|
||||
import {
|
||||
clearSleevesPage,
|
||||
@ -104,14 +91,14 @@ import { displayCharacterInfo } from "./ui/displayCharacterInfo";
|
||||
import { Page, routing } from "./ui/navigationTracking";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
import { setSettingsLabels } from "./ui/setSettingsLabels";
|
||||
|
||||
import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
|
||||
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
|
||||
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen";
|
||||
@ -278,7 +265,10 @@ const Engine = {
|
||||
Engine.hideAllContent();
|
||||
Engine.Display.activeScriptsContent.style.display = "block";
|
||||
routing.navigateTo(Page.ActiveScripts);
|
||||
updateActiveScriptsItems();
|
||||
ReactDOM.render(
|
||||
<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
|
||||
Engine.Display.activeScriptsContent
|
||||
)
|
||||
MainMenuLinks.ActiveScripts.classList.add("active");
|
||||
},
|
||||
|
||||
@ -315,8 +305,8 @@ const Engine = {
|
||||
loadAugmentationsContent: function() {
|
||||
Engine.hideAllContent();
|
||||
Engine.Display.augmentationsContent.style.display = "block";
|
||||
displayAugmentationsContent(Engine.Display.augmentationsContent);
|
||||
routing.navigateTo(Page.Augmentations);
|
||||
displayAugmentationsContent(Engine.Display.augmentationsContent);
|
||||
MainMenuLinks.Augmentations.classList.add("active");
|
||||
},
|
||||
|
||||
@ -490,16 +480,26 @@ const Engine = {
|
||||
Engine.Display.terminalContent.style.display = "none";
|
||||
Engine.Display.characterContent.style.display = "none";
|
||||
Engine.Display.scriptEditorContent.style.display = "none";
|
||||
|
||||
Engine.Display.activeScriptsContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent);
|
||||
|
||||
clearHacknetNodesUI();
|
||||
Engine.Display.createProgramContent.style.display = "none";
|
||||
|
||||
Engine.Display.factionsContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.factionContent);
|
||||
|
||||
Engine.Display.factionContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.factionContent);
|
||||
|
||||
Engine.Display.augmentationsContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.augmentationsContent);
|
||||
|
||||
Engine.Display.tutorialContent.style.display = "none";
|
||||
|
||||
Engine.Display.locationContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.locationContent);
|
||||
|
||||
Engine.Display.workInProgressContent.style.display = "none";
|
||||
Engine.Display.redPillContent.style.display = "none";
|
||||
Engine.Display.cinematicTextContent.style.display = "none";
|
||||
@ -814,13 +814,14 @@ const Engine = {
|
||||
}
|
||||
|
||||
if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
|
||||
// Always update, but make the interval longer if the page isn't active
|
||||
updateActiveScriptsItems();
|
||||
if (routing.isOn(Page.ActiveScripts)) {
|
||||
Engine.Counters.updateActiveScriptsDisplay = 5;
|
||||
} else {
|
||||
Engine.Counters.updateActiveScriptsDisplay = 10;
|
||||
ReactDOM.render(
|
||||
<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />,
|
||||
Engine.Display.activeScriptsContent
|
||||
)
|
||||
}
|
||||
|
||||
Engine.Counters.updateActiveScriptsDisplay = 5;
|
||||
}
|
||||
|
||||
if (Engine.Counters.updateDisplays <= 0) {
|
||||
@ -1043,9 +1044,7 @@ const Engine = {
|
||||
|
||||
// Load game from save or create new game
|
||||
if (loadGame(saveString)) {
|
||||
initBitNodes();
|
||||
initBitNodeMultipliers(Player);
|
||||
initSourceFiles();
|
||||
Engine.setDisplayElements(); // Sets variables for important DOM elements
|
||||
Engine.init(); // Initialize buttons, work, etc.
|
||||
initAugmentations(); // Also calls Player.reapplyAllAugmentations()
|
||||
@ -1166,9 +1165,7 @@ const Engine = {
|
||||
} else {
|
||||
// No save found, start new game
|
||||
console.log("Initializing new game");
|
||||
initBitNodes();
|
||||
initBitNodeMultipliers(Player);
|
||||
initSourceFiles();
|
||||
initSpecialServerIps();
|
||||
Engine.setDisplayElements(); // Sets variables for important DOM elements
|
||||
Engine.start(); // Run main game loop and Scripts loop
|
||||
@ -1531,9 +1528,6 @@ const Engine = {
|
||||
start: function() {
|
||||
// Run main loop
|
||||
Engine.idleTimer();
|
||||
|
||||
// Script-processing loop
|
||||
runScriptsLoop();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,8 +9,10 @@ import "../css/characteroverview.scss";
|
||||
import "../css/terminal.scss";
|
||||
import "../css/scripteditor.scss";
|
||||
import "../css/codemirror-overrides.scss";
|
||||
import "../css/activescripts.scss";
|
||||
import "../css/hacknetnodes.scss";
|
||||
import "../css/menupages.scss";
|
||||
import "../css/augmentations.scss";
|
||||
import "../css/redpill.scss";
|
||||
import "../css/stockmarket.scss";
|
||||
import "../css/workinprogress.scss";
|
||||
|
38
src/ui/ActiveScripts/Root.tsx
Normal file
38
src/ui/ActiveScripts/Root.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Root React Component for the "Active Scripts" UI page. This page displays
|
||||
* and provides information about all of the player's scripts that are currently running
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { ScriptProduction } from "./ScriptProduction";
|
||||
import { ServerAccordions } from "./ServerAccordions";
|
||||
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
workerScripts: WorkerScript[];
|
||||
}
|
||||
|
||||
export class ActiveScriptsRoot extends React.Component<IProps> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
This page displays a list of all of your scripts that are currently
|
||||
running across every machine. It also provides information about each
|
||||
script's production. The scripts are categorized by the hostname of
|
||||
the servers on which they are running.
|
||||
</p>
|
||||
|
||||
<ScriptProduction {...this.props} />
|
||||
<ServerAccordions {...this.props} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
46
src/ui/ActiveScripts/ScriptProduction.tsx
Normal file
46
src/ui/ActiveScripts/ScriptProduction.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* React Component for displaying the total production and production rate
|
||||
* of scripts on the 'Active Scripts' UI page
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
workerScripts: WorkerScript[];
|
||||
}
|
||||
|
||||
export function ScriptProduction(props: IProps): React.ReactElement {
|
||||
const prodRateSinceLastAug = props.p.scriptProdSinceLastAug / (props.p.playtimeSinceLastAug / 1000);
|
||||
|
||||
let onlineProduction = 0;
|
||||
for (const ws of props.workerScripts) {
|
||||
onlineProduction += (ws.scriptRef.onlineMoneyMade / ws.scriptRef.onlineRunningTime);
|
||||
}
|
||||
|
||||
return (
|
||||
<p id="active-scripts-total-prod">
|
||||
Total online production of Active scripts:
|
||||
<span className="money-gold">
|
||||
<span id="active-scripts-total-production-active">
|
||||
{numeralWrapper.formatMoney(onlineProduction)}
|
||||
</span> / sec
|
||||
</span><br />
|
||||
|
||||
Total online production since last Aug installation:
|
||||
<span id="active-scripts-total-prod-aug-total" className="money-gold">
|
||||
{numeralWrapper.formatMoney(props.p.scriptProdSinceLastAug)}
|
||||
</span>
|
||||
|
||||
(<span className="money-gold">
|
||||
<span id="active-scripts-total-prod-aug-avg" className="money-gold">
|
||||
{numeralWrapper.formatMoney(prodRateSinceLastAug)}
|
||||
</span> / sec
|
||||
</span>)
|
||||
</p>
|
||||
)
|
||||
}
|
49
src/ui/ActiveScripts/ServerAccordion.tsx
Normal file
49
src/ui/ActiveScripts/ServerAccordion.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* React Component for rendering the Accordion element for a single
|
||||
* server in the 'Active Scripts' UI page
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
|
||||
import { Accordion } from "../React/Accordion";
|
||||
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
|
||||
type IProps = {
|
||||
server: BaseServer;
|
||||
workerScripts: WorkerScript[];
|
||||
}
|
||||
|
||||
export function ServerAccordion(props: IProps): React.ReactElement {
|
||||
const server = props.server;
|
||||
|
||||
// Accordion's header text
|
||||
// TODO: calculate the longest hostname length rather than hard coding it
|
||||
const longestHostnameLength = 18;
|
||||
const paddedName = `${server.hostname}${" ".repeat(longestHostnameLength)}`.slice(0, Math.max(server.hostname.length, longestHostnameLength));
|
||||
const barOptions = {
|
||||
progress: server.ramUsed / server.maxRam,
|
||||
totalTicks: 30
|
||||
};
|
||||
const headerTxt = `${paddedName} ${createProgressBarText(barOptions)}`;
|
||||
|
||||
const scripts = props.workerScripts.map((ws) => {
|
||||
return (
|
||||
<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
headerContent={
|
||||
<pre>{headerTxt}</pre>
|
||||
}
|
||||
panelContent={
|
||||
<ul>{scripts}</ul>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
109
src/ui/ActiveScripts/ServerAccordions.tsx
Normal file
109
src/ui/ActiveScripts/ServerAccordions.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* React Component for rendering the Accordion elements for all servers
|
||||
* on which scripts are running
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { ServerAccordion } from "./ServerAccordion";
|
||||
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
|
||||
import { getServer } from "../../Server/ServerHelpers";
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
|
||||
// Map of server hostname -> all workerscripts on that server for all active scripts
|
||||
interface IServerData {
|
||||
server: BaseServer;
|
||||
workerScripts: WorkerScript[];
|
||||
}
|
||||
|
||||
interface IServerToScriptsMap {
|
||||
[key: string]: IServerData;
|
||||
}
|
||||
|
||||
type IProps = {
|
||||
workerScripts: WorkerScript[];
|
||||
};
|
||||
|
||||
type IState = {
|
||||
rerenderFlag: boolean;
|
||||
}
|
||||
|
||||
|
||||
const subscriberId = "ActiveScriptsUI";
|
||||
|
||||
export class ServerAccordions extends React.Component<IProps, IState> {
|
||||
serverToScriptMap: IServerToScriptsMap = {};
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rerenderFlag: false,
|
||||
}
|
||||
|
||||
this.updateServerToScriptsMap();
|
||||
|
||||
this.rerender = this.rerender.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
WorkerScriptStartStopEventEmitter.addSubscriber({
|
||||
cb: this.rerender,
|
||||
id: subscriberId,
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
WorkerScriptStartStopEventEmitter.removeSubscriber(subscriberId);
|
||||
}
|
||||
|
||||
updateServerToScriptsMap(): void {
|
||||
const map: IServerToScriptsMap = {};
|
||||
|
||||
for (const ws of this.props.workerScripts) {
|
||||
const server = getServer(ws.serverIp);
|
||||
if (server == null) {
|
||||
console.warn(`WorkerScript has invalid IP address: ${ws.serverIp}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (map[server.hostname] == null) {
|
||||
map[server.hostname] = {
|
||||
server: server,
|
||||
workerScripts: [],
|
||||
};
|
||||
}
|
||||
|
||||
map[server.hostname].workerScripts.push(ws);
|
||||
}
|
||||
|
||||
this.serverToScriptMap = map;
|
||||
}
|
||||
|
||||
rerender() {
|
||||
this.updateServerToScriptsMap();
|
||||
this.setState((prevState) => {
|
||||
return { rerenderFlag: !prevState.rerenderFlag }
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const elems = Object.keys(this.serverToScriptMap).map((serverName) => {
|
||||
const data = this.serverToScriptMap[serverName];
|
||||
return (
|
||||
<ServerAccordion
|
||||
key={serverName}
|
||||
server={data.server}
|
||||
workerScripts={data.workerScripts}
|
||||
/>
|
||||
)
|
||||
});
|
||||
|
||||
return (
|
||||
<ul className="active-scripts-list" id="active-scripts-list">
|
||||
{elems}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
}
|
77
src/ui/ActiveScripts/WorkerScriptAccordion.tsx
Normal file
77
src/ui/ActiveScripts/WorkerScriptAccordion.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* React Component for displaying a single WorkerScript's info as an
|
||||
* Accordion element
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { numeralWrapper } from "../numeralFormat";
|
||||
|
||||
import { Accordion } from "../React/Accordion";
|
||||
import { AccordionButton } from "../React/AccordionButton";
|
||||
|
||||
import { killWorkerScript } from "../../Netscript/killWorkerScript";
|
||||
import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { logBoxCreate } from "../../../utils/LogBox";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { arrayToString } from "../../../utils/helpers/arrayToString";
|
||||
|
||||
type IProps = {
|
||||
workerScript: WorkerScript;
|
||||
}
|
||||
|
||||
export function WorkerScriptAccordion(props: IProps): React.ReactElement {
|
||||
const workerScript = props.workerScript;
|
||||
const scriptRef = workerScript.scriptRef;
|
||||
|
||||
|
||||
const logClickHandler = logBoxCreate.bind(null, scriptRef);
|
||||
const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server);
|
||||
|
||||
function killScriptClickHandler() {
|
||||
killScript();
|
||||
dialogBoxCreate("Killing script");
|
||||
}
|
||||
|
||||
// Calculations for script stats
|
||||
const onlineMps = scriptRef.onlineMoneyMade / scriptRef.onlineRunningTime;
|
||||
const onlineEps = scriptRef.onlineExpGained / scriptRef.onlineRunningTime;
|
||||
const offlineMps = scriptRef.offlineMoneyMade / scriptRef.offlineRunningTime;
|
||||
const offlineEps = scriptRef.offlineExpGained / scriptRef.offlineRunningTime;
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
headerClass="active-scripts-script-header"
|
||||
headerContent={
|
||||
<>{props.workerScript.name}</>
|
||||
}
|
||||
panelClass="active-scripts-script-panel"
|
||||
panelContent={
|
||||
<>
|
||||
<pre>Threads: {props.workerScript.scriptRef.threads}</pre>
|
||||
<pre>Args: {arrayToString(props.workerScript.args)}</pre>
|
||||
<pre>Online Time: {convertTimeMsToTimeElapsedString(scriptRef.onlineRunningTime * 1e3)}</pre>
|
||||
<pre>Offline Time: {convertTimeMsToTimeElapsedString(scriptRef.offlineRunningTime * 1e3)}</pre>
|
||||
<pre>Total online production: {numeralWrapper.formatMoney(scriptRef.onlineMoneyMade)}</pre>
|
||||
<pre>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(scriptRef.onlineExpGained) + " hacking exp")}</pre>
|
||||
<pre>Online production rate: {numeralWrapper.formatMoney(onlineMps)} / second</pre>
|
||||
<pre>{(Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second")}</pre>
|
||||
<pre>Total offline production: {numeralWrapper.formatMoney(scriptRef.offlineMoneyMade)}</pre>
|
||||
<pre>{(Array(27).join(" ") + numeralWrapper.formatBigNumber(scriptRef.offlineExpGained) + " hacking exp")}</pre>
|
||||
<pre>Offline production rate: {numeralWrapper.formatMoney(offlineMps)} / second</pre>
|
||||
<pre>{(Array(26).join(" ") + numeralWrapper.formatBigNumber(offlineEps) + " hacking exp / second")}</pre>
|
||||
|
||||
<AccordionButton
|
||||
onClick={logClickHandler}
|
||||
text="Log"
|
||||
/>
|
||||
<AccordionButton
|
||||
onClick={killScriptClickHandler}
|
||||
text="Kill Script"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
@ -4,9 +4,12 @@
|
||||
import * as React from "react";
|
||||
|
||||
type IProps = {
|
||||
headerClass?: string; // Override default class
|
||||
headerContent: React.ReactElement;
|
||||
panelClass?: string; // Override default class
|
||||
panelContent: React.ReactElement;
|
||||
panelInitiallyOpened?: boolean;
|
||||
style?: string;
|
||||
}
|
||||
|
||||
type IState = {
|
||||
@ -44,19 +47,29 @@ export class Accordion extends React.Component<IProps, IState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
let className = "accordion-header";
|
||||
if (typeof this.props.headerClass === "string") {
|
||||
className = this.props.headerClass;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className={"accordion-header"} onClick={this.handleHeaderClick}>
|
||||
{this.props.headerContent}
|
||||
</button>
|
||||
<AccordionPanel opened={this.state.panelOpened} panelContent={this.props.panelContent} />
|
||||
</div>
|
||||
<>
|
||||
<button className={className} onClick={this.handleHeaderClick}>
|
||||
{this.props.headerContent}
|
||||
</button>
|
||||
<AccordionPanel
|
||||
opened={this.state.panelOpened}
|
||||
panelClass={this.props.panelClass}
|
||||
panelContent={this.props.panelContent}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type IPanelProps = {
|
||||
opened: boolean;
|
||||
panelClass?: string; // Override default class
|
||||
panelContent: React.ReactElement;
|
||||
}
|
||||
|
||||
@ -66,8 +79,13 @@ class AccordionPanel extends React.Component<IPanelProps, any> {
|
||||
}
|
||||
|
||||
render() {
|
||||
let className = "accordion-panel"
|
||||
if (typeof this.props.panelClass === "string") {
|
||||
className = this.props.panelClass;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"accordion-panel"}>
|
||||
<div className={className}>
|
||||
{this.props.panelContent}
|
||||
</div>
|
||||
)
|
||||
|
52
src/ui/React/AccordionButton.tsx
Normal file
52
src/ui/React/AccordionButton.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Basic stateless button that uses the 'accordion-button' css class.
|
||||
* This class has a black background so that it does not clash with the default
|
||||
* accordion coloring
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
interface IProps {
|
||||
addClasses?: string;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
style?: object;
|
||||
text: string;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
type IInnerHTMLMarkup = {
|
||||
__html: string;
|
||||
}
|
||||
|
||||
export function AccordionButton(props: IProps): React.ReactElement {
|
||||
const hasTooltip = props.tooltip != null && props.tooltip !== "";
|
||||
|
||||
// TODO Add a disabled class for accordion buttons?
|
||||
let className = "accordion-button";
|
||||
if (hasTooltip) {
|
||||
className += " tooltip";
|
||||
}
|
||||
|
||||
if (typeof props.addClasses === "string") {
|
||||
className += ` ${props.addClasses}`;
|
||||
}
|
||||
|
||||
// Tooltip will be set using inner HTML
|
||||
let tooltipMarkup: IInnerHTMLMarkup | null;
|
||||
if (hasTooltip) {
|
||||
tooltipMarkup = {
|
||||
__html: props.tooltip!
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={className} id={props.id} onClick={props.onClick} style={props.style}>
|
||||
{props.text}
|
||||
{
|
||||
hasTooltip &&
|
||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
|
||||
}
|
||||
</button>
|
||||
)
|
||||
}
|
33
src/ui/React/AugmentationAccordion.tsx
Normal file
33
src/ui/React/AugmentationAccordion.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* React Component for displaying a single Augmentation as an accordion.
|
||||
*
|
||||
* The header of the accordion contains the Augmentation's name (and level, if
|
||||
* applicable), and the accordion's panel contains the Augmentation's description.
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Accordion } from "./Accordion";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
type IProps = {
|
||||
aug: Augmentation,
|
||||
level?: number | string | null,
|
||||
}
|
||||
|
||||
export function AugmentationAccordion(props: IProps): React.ReactElement {
|
||||
let displayName = props.aug.name;
|
||||
if (props.level != null) {
|
||||
if (props.aug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
displayName += (` - Level ${props.level}`)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
headerContent={<>{displayName}</>}
|
||||
panelContent={<p dangerouslySetInnerHTML={{__html: props.aug.info}}></p>}
|
||||
/>
|
||||
)
|
||||
}
|
@ -13,12 +13,10 @@ interface IProps {
|
||||
props: object;
|
||||
}
|
||||
|
||||
export class Popup extends React.Component<IProps, any> {
|
||||
render() {
|
||||
return (
|
||||
<div className={"popup-box-content"} id={`${this.props.id}-content`}>
|
||||
{React.createElement(this.props.content, this.props.props)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export function Popup(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<div className={"popup-box-content"} id={`${props.id}-content`}>
|
||||
{React.createElement(props.content, props.props)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
35
src/ui/React/SourceFileAccordion.tsx
Normal file
35
src/ui/React/SourceFileAccordion.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* React Component for displaying a single Source-File as an accordion.
|
||||
*
|
||||
* The header of the accordion contains the Source-Files's name and level,
|
||||
* and the accordion's panel contains the Source-File's description.
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Accordion } from "./Accordion";
|
||||
|
||||
import { SourceFile } from "../../SourceFile/SourceFile";
|
||||
|
||||
type IProps = {
|
||||
level: number,
|
||||
sf: SourceFile,
|
||||
}
|
||||
|
||||
export function SourceFileAccordion(props: IProps): React.ReactElement {
|
||||
const maxLevel = props.sf.n === 3 ? "∞" : "3";
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
headerContent={
|
||||
<>
|
||||
{props.sf.name}
|
||||
<br />
|
||||
{`Level ${props.level} / ${maxLevel}`}
|
||||
</>
|
||||
}
|
||||
panelContent={
|
||||
<p dangerouslySetInnerHTML={{__html: props.sf.info}}></p>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
import * as React from "react";
|
||||
|
||||
interface IStdButtonProps {
|
||||
addClasses?: string;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
@ -17,30 +18,32 @@ type IInnerHTMLMarkup = {
|
||||
__html: string;
|
||||
}
|
||||
|
||||
export class StdButton extends React.Component<IStdButtonProps, any> {
|
||||
render() {
|
||||
const hasTooltip = this.props.tooltip != null && this.props.tooltip !== "";
|
||||
let className = this.props.disabled ? "std-button-disabled" : "std-button";
|
||||
if (hasTooltip) {
|
||||
className += " tooltip";
|
||||
}
|
||||
|
||||
// Tooltip will be set using inner HTML
|
||||
let tooltipMarkup: IInnerHTMLMarkup | null;
|
||||
if (hasTooltip) {
|
||||
tooltipMarkup = {
|
||||
__html: this.props.tooltip!
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={className} id={this.props.id} onClick={this.props.onClick} style={this.props.style}>
|
||||
{this.props.text}
|
||||
{
|
||||
hasTooltip &&
|
||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
|
||||
}
|
||||
</button>
|
||||
)
|
||||
export function StdButton(props: IStdButtonProps): React.ReactElement {
|
||||
const hasTooltip = props.tooltip != null && props.tooltip !== "";
|
||||
let className = props.disabled ? "std-button-disabled" : "std-button";
|
||||
if (hasTooltip) {
|
||||
className += " tooltip";
|
||||
}
|
||||
|
||||
if (typeof props.addClasses === "string") {
|
||||
className += ` ${props.addClasses}`;
|
||||
}
|
||||
|
||||
// Tooltip will be set using inner HTML
|
||||
let tooltipMarkup: IInnerHTMLMarkup | null;
|
||||
if (hasTooltip) {
|
||||
tooltipMarkup = {
|
||||
__html: props.tooltip!
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={className} id={props.id} onClick={props.onClick} style={props.style}>
|
||||
{props.text}
|
||||
{
|
||||
hasTooltip &&
|
||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
|
||||
}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
50
src/utils/EventEmitter.ts
Normal file
50
src/utils/EventEmitter.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Generic Event Emitter class following a subscribe/publish paradigm.
|
||||
*/
|
||||
import { IMap } from "../types";
|
||||
|
||||
type cbFn = (...args: any[]) => any;
|
||||
|
||||
export interface ISubscriber {
|
||||
/**
|
||||
* Callback function that will be run when an event is emitted
|
||||
*/
|
||||
cb: cbFn;
|
||||
|
||||
/**
|
||||
* Name/identifier for this subscriber
|
||||
*/
|
||||
id: string;
|
||||
}
|
||||
|
||||
export class EventEmitter {
|
||||
/**
|
||||
* Map of Subscriber name -> Callback function
|
||||
*/
|
||||
subscribers: IMap<cbFn> = {};
|
||||
|
||||
constructor(subs?: ISubscriber[]) {
|
||||
if (Array.isArray(subs)) {
|
||||
for (const s of subs) {
|
||||
this.addSubscriber(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addSubscriber(s: ISubscriber) {
|
||||
this.subscribers[s.id] = s.cb;
|
||||
}
|
||||
|
||||
emitEvent(...args: any[]): void {
|
||||
for (const s in this.subscribers) {
|
||||
const cb = this.subscribers[s];
|
||||
|
||||
cb(args);
|
||||
}
|
||||
}
|
||||
|
||||
removeSubscriber(id: string) {
|
||||
delete this.subscribers[id];
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import {killWorkerScript} from "../src/NetscriptWorker";
|
||||
import {clearEventListeners} from "./uiHelpers/clearEventListeners";
|
||||
import {arrayToString} from "./helpers/arrayToString";
|
||||
|
||||
$(document).keydown(function(event) {
|
||||
if (logBoxOpened && event.keyCode == 27) {
|
||||
logBoxClose();
|
||||
}
|
||||
});
|
||||
|
||||
function logBoxInit() {
|
||||
var closeButton = document.getElementById("log-box-close");
|
||||
logBoxClose();
|
||||
|
||||
//Close Dialog box
|
||||
closeButton.addEventListener("click", function() {
|
||||
logBoxClose();
|
||||
return false;
|
||||
});
|
||||
document.getElementById("log-box-text-header").style.display = "inline-block";
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", logBoxInit, false);
|
||||
|
||||
function logBoxClose() {
|
||||
logBoxOpened = false;
|
||||
var logBox = document.getElementById("log-box-container");
|
||||
logBox.style.display = "none";
|
||||
}
|
||||
|
||||
function logBoxOpen() {
|
||||
logBoxOpened = true;
|
||||
|
||||
var logBox = document.getElementById("log-box-container");
|
||||
logBox.style.display = "block";
|
||||
}
|
||||
|
||||
|
||||
var logBoxOpened = false;
|
||||
var logBoxCurrentScript = null;
|
||||
function logBoxCreate(script) {
|
||||
logBoxCurrentScript = script;
|
||||
var killScriptBtn = clearEventListeners("log-box-kill-script");
|
||||
killScriptBtn.addEventListener("click", ()=>{
|
||||
killWorkerScript(script, script.server);
|
||||
return false;
|
||||
});
|
||||
document.getElementById('log-box-kill-script').style.display = "inline-block";
|
||||
logBoxOpen();
|
||||
document.getElementById("log-box-text-header").innerHTML =
|
||||
logBoxCurrentScript.filename + " " + arrayToString(logBoxCurrentScript.args) + ":<br><br>";
|
||||
logBoxCurrentScript.logUpd = true;
|
||||
logBoxUpdateText();
|
||||
}
|
||||
|
||||
function logBoxUpdateText() {
|
||||
var txt = document.getElementById("log-box-text");
|
||||
if (logBoxCurrentScript && logBoxOpened && txt && logBoxCurrentScript.logUpd) {
|
||||
txt.innerHTML = "";
|
||||
for (var i = 0; i < logBoxCurrentScript.logs.length; ++i) {
|
||||
txt.innerHTML += logBoxCurrentScript.logs[i];
|
||||
txt.innerHTML += "<br>";
|
||||
}
|
||||
logBoxCurrentScript.logUpd = false;
|
||||
}
|
||||
}
|
||||
|
||||
export {logBoxCreate, logBoxUpdateText, logBoxOpened, logBoxCurrentScript};
|
106
utils/LogBox.ts
Normal file
106
utils/LogBox.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { killWorkerScript } from "../src/Netscript/killWorkerScript";
|
||||
import { RunningScript } from "../src/Script/RunningScript";
|
||||
|
||||
import { clearEventListeners } from "./uiHelpers/clearEventListeners";
|
||||
import { arrayToString } from "./helpers/arrayToString";
|
||||
|
||||
import { KEY } from "./helpers/keyCodes";
|
||||
|
||||
document.addEventListener("keydown", function(event: KeyboardEvent) {
|
||||
if (logBoxOpened && event.keyCode == KEY.ESC) {
|
||||
logBoxClose();
|
||||
}
|
||||
});
|
||||
|
||||
let logBoxContainer: HTMLElement | null;
|
||||
let textHeader: HTMLElement | null;
|
||||
let logText: HTMLElement | null;
|
||||
|
||||
function logBoxInit(): void {
|
||||
// Initialize Close button click listener
|
||||
const closeButton = document.getElementById("log-box-close");
|
||||
if (closeButton == null) {
|
||||
console.error(`Could not find LogBox's close button`);
|
||||
return;
|
||||
}
|
||||
|
||||
closeButton.addEventListener("click", function() {
|
||||
logBoxClose();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Initialize text header
|
||||
textHeader = document.getElementById("log-box-text-header");
|
||||
if (textHeader instanceof HTMLElement) {
|
||||
textHeader.style.display = "inline-block";
|
||||
}
|
||||
|
||||
// Initialize references to other DOM elements
|
||||
logBoxContainer = document.getElementById("log-box-container");
|
||||
logText = document.getElementById("log-box-text");
|
||||
|
||||
logBoxClose();
|
||||
|
||||
document.removeEventListener("DOMContentLoaded", logBoxInit);
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", logBoxInit);
|
||||
|
||||
function logBoxClose() {
|
||||
logBoxOpened = false;
|
||||
if (logBoxContainer instanceof HTMLElement) {
|
||||
logBoxContainer.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function logBoxOpen() {
|
||||
logBoxOpened = true;
|
||||
|
||||
if (logBoxContainer instanceof HTMLElement) {
|
||||
logBoxContainer.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export let logBoxOpened = false;
|
||||
let logBoxCurrentScript: RunningScript | null = null;
|
||||
export function logBoxCreate(script: RunningScript) {
|
||||
logBoxCurrentScript = script;
|
||||
|
||||
const killScriptBtn = clearEventListeners("log-box-kill-script");
|
||||
if (killScriptBtn == null) {
|
||||
console.error(`Could not find LogBox's 'Kill Script' button`);
|
||||
return;
|
||||
}
|
||||
|
||||
killScriptBtn.addEventListener("click", () => {
|
||||
killWorkerScript(script, script.server);
|
||||
return false;
|
||||
});
|
||||
|
||||
killScriptBtn.style.display = "inline-block";
|
||||
|
||||
logBoxOpen();
|
||||
|
||||
if (textHeader instanceof HTMLElement) {
|
||||
textHeader.innerHTML = `${logBoxCurrentScript.filename} ${arrayToString(logBoxCurrentScript.args)}:<br><br>`;
|
||||
} else {
|
||||
console.warn(`LogBox's Text Header DOM element is null`);
|
||||
}
|
||||
|
||||
logBoxCurrentScript.logUpd = true;
|
||||
logBoxUpdateText();
|
||||
}
|
||||
|
||||
export function logBoxUpdateText() {
|
||||
if (!(logText instanceof HTMLElement)) { return; }
|
||||
|
||||
if (logBoxCurrentScript && logBoxOpened && logBoxCurrentScript.logUpd) {
|
||||
logText.innerHTML = "";
|
||||
for (let i = 0; i < logBoxCurrentScript.logs.length; ++i) {
|
||||
logText.innerHTML += logBoxCurrentScript.logs[i];
|
||||
logText.innerHTML += "<br>";
|
||||
}
|
||||
logBoxCurrentScript.logUpd = false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user