This commit is contained in:
danielyxie 2018-03-26 19:46:21 -05:00
parent 6255971e96
commit 521b81aec0
17 changed files with 34992 additions and 34254 deletions

1
.gitignore vendored

@ -1,4 +1,3 @@
Changelog.txt Changelog.txt
Netburner.txt Netburner.txt
README.md
/node_modules /node_modules

31
README.md Normal file

@ -0,0 +1,31 @@
# Bitburner
Bitburner is a cyberpunk hacking-themed incremental game. The game can be
played [here](https://danielyxie.github.io/bitburner).
# Documentation
The game's official documentation can be found [here](http://bitburner.readthedocs.io/en/latest/index.html). Please note that
this is still a work-in-progress and is in its early stages.
The documentation is created using Sphinx and is hosted on Read The Docs.
Anyone is welcome to contribute to the documentation by editing the source files
in /doc/ and then making a pull request with your changes/contributions.
# Wiki
The game's wiki can be found [here](http://bitburner.wikia.com/wiki/Bitburner_Wiki).
Please note that the wiki is in the process of being deprecated. Eventually all of the
wiki content will be moved into the Read The Docs documentation.
# Contributing
If you would like to make any small change or addition to the game, you may go ahead
and do so by submitting a pull request.
You will retain all ownership of the Copyright of any contributions you make,
and will have the same rights to use or license your contributions. By
submitting a pull request you agree to grant me perpetual, worldwide, non-exclusive,
transferable, royalty-free, and irrevocable rights to use, publish, and distribute
your contributions to the project. A formal Contributor's License Agreement
will be drawn up in the future.
If you would like to make significant contributions to the project as a
collaborator, please message me privately.

@ -367,32 +367,17 @@
padding-top: 10px; padding-top: 10px;
} }
#factions-list li {
padding: 6px;
margin: 6px;
}
#faction-favor, #faction-reputation {
display: inline;
}
.faction-work-div { .faction-work-div {
overflow: hidden;
width: 70%; width: 70%;
height: 100%; height: 100%;
} }
.faction-work-div-wrapper { .faction-work-div-wrapper {
float: left; overflow:hidden;
border: 2px solid #333; border: 2px solid #333;
padding: 14px 6px 4px 6px; padding: 6px;
margin: 6px; margin: 6px;
} width:70%;
#faction-hack-button,
#faction-fieldwork-button,
#faction-securitywork-button {
margin: 8px;
} }
#faction-donate-amount-txt, #faction-donate-amount-txt,
@ -408,18 +393,14 @@
width:50%; width:50%;
} }
div.faction-clear {
clear: both;
}
#faction-container p, #faction-container p,
#faction-container pre { #faction-container pre {
padding: 6px; padding: 6px;
margin: 6px; margin: 6px;
width: 70%;
} }
#faction-info { #faction-container pre {
width: 70%;
white-space: pre-wrap; /* Since CSS 2.1 */ white-space: pre-wrap; /* Since CSS 2.1 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */ white-space: -pre-wrap; /* Opera 4-6 */
@ -441,11 +422,6 @@ div.faction-clear {
padding: 4px; padding: 4px;
} }
#faction-augmentations-list > li{
margin: 4px;
padding: 4px;
}
/* World */ /* World */
#world-container li { #world-container li {
margin: 0 0 15px 0; margin: 0 0 15px 0;
@ -458,29 +434,13 @@ div.faction-clear {
padding-top: 10px; padding-top: 10px;
} }
.augmentations-list button,
#augmentations-list li, .augmentations-list div {
#queued-augmentations-list li {
width: 70%;
background-color: #333;
}
#augmentations-list h2,
#augmentations-list p,
#queued-augmentations-list h2,
#queued-augmentations-list p {
margin: 4px;
color: var(--my-font-color); color: var(--my-font-color);
padding: 8px; padding: 8px;
width: 70%;
background-color: #333;
text-decoration: none; text-decoration: none;
} }
#augmentations-list li p {
width: 95%;
}
/* Tutorial */ /* Tutorial */
#tutorial-container { #tutorial-container {
position: fixed; position: fixed;

67172
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

@ -428,11 +428,7 @@
</li> </li>
</ul> </ul>
<ul id="generic-locations-list"> <ul id="generic-locations-list"></ul>
<li id="generic-location-wse-li">
<a id="generic-location-wse" class="a-link-button">World Stock Exchange</a>
</li>
</ul>
</div> </div>
<!-- Create a program(executable) --> <!-- Create a program(executable) -->
@ -447,144 +443,15 @@
</div> </div>
<!-- Factions --> <!-- Factions -->
<div id="factions-container" class="generic-menupage-container"> <div id="factions-container" class="generic-menupage-container"></div>
<h1> Factions </h1>
<p> Lists all factions you have joined </p>
<ul class="factions-list" id="factions-list"></ul>
<br><br>
<h1> Outstanding Faction Invitations </h1>
<p style="width:70%;"> Lists factions you have been invited to, as well as factions you have previously rejected.
You can accept these faction invitations at any times </p>
<ul class="factions-list" id="outstanding-faction-invitations-list"></ul>
</div>
<!-- Single Faction info (when you select a faction from the Factions menu) --> <!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container"> <div id="faction-container" class="generic-menupage-container"></div>
<h1 id="faction-name"></h1>
<pre id="faction-info"></pre>
<p> --------------- </p>
<p id="faction-reputation" class="tooltip"></p>
<p> --------------- </p>
<p id="faction-favor" class="tooltip"></p>
<p> --------------- </p>
<p id="faction-work-description-text">
Perform work/carry out assignments for your faction to help further its cause! By doing so
you will earn reputation for your faction. You will also gain reputation passively over time,
although at a very slow rate. Earning reputation will allow you to purchase Augmentations
through this faction, which are powerful upgrades that enhance your abilities. Note that you cannot
use your terminal or create scripts when you are performing a task! <br><br><br><br>
</p>
<div id="faction-hack-mission-div" class="faction-work-div"> <div id="faction-augmentations-container" class="generic-menupage-container"></div>
<div id="faction-hack-mission-div-wrapper" class="faction-work-div-wrapper">
<a id="faction-hack-mission-button" class="a-link-button">Hacking Mission</a>
<p id="faction-hack-mission-text">
Attempt a hacking mission for your faction.
A mission is a mini game that, if won, earns you significant reputation with this faction. (Recommended hacking level: 200+)
</p>
</div>
<div class="faction-clear"></div>
</div>
<div id="faction-hack-div" class="faction-work-div">
<div id="faction-hack-div-wrapper" class="faction-work-div-wrapper">
<a id="faction-hack-button" class="a-link-button">Hacking Contracts</a>
<p id="faction-hack-text">
Complete hacking contracts for your faction.
Your effectiveness, which determines how much reputation you gain for this faction, is based on your hacking skill.
You will gain hacking exp.
</p>
</div>
<div class="faction-clear"></div>
</div>
<div id="faction-fieldwork-div" class="faction-work-div">
<div id="faction-fieldwork-div-wrapper" class="faction-work-div-wrapper">
<a id="faction-fieldwork-button" class="a-link-button">Field Work</a>
<p id="faction-fieldwork-text">
Carry out field missions for your faction.
Your effectiveness, which determines how much reputation you gain for this faction, is based on all of your stats.
You will gain exp for all stats.
</p>
</div>
<div class="faction-clear"></div>
</div>
<div id="faction-securitywork-div" class="faction-work-div">
<div id="faction-securitywork-div-wrapper" class="faction-work-div-wrapper">
<a id="faction-securitywork-button" class="a-link-button">Security Work</a>
<p id="faction-securitywork-text">
Serve in a security detail for your faction.
Your effectiveness, which determines how much reputation you gain for this faction, is based on your combat stats.
You will gain exp for all combat stats.
</p>
</div>
<div class="faction-clear"></div>
</div>
<div id="faction-donate-div" class="faction-work-div">
<div id="faction-donate-div-wrapper" class="faction-work-div-wrapper">
<a id="faction-donate-button" class="a-link-button">Donate Money</a>
<p id="faction-donate-text">
Donate money to your faction. You will gain reputation based on how much money you donate
</p>
<div>
<label id="faction-donate-amount-txt">Enter amount to donate: $</label>
<input id="faction-donate-input" type="number"> </input>
</div>
<p id="faction-donate-rep-gain"> This donation will result in 0 reputation gain</p>
</div>
<div class="faction-clear"></div>
</div>
<p>
<br>
As your reputation with this faction rises, you will unlock Augmentations, which you
can purchase to enhance your abilities.
<br><br>
</p>
<a id="faction-purchase-augmentations" class="a-link-button">Purchase Augmentations</a>
<br><br><br><br>
</div>
<div id="faction-augmentations-container" class="generic-menupage-container">
<a id="faction-augmentations-back-button" class="a-link-button"> Back </a>
<h1> Faction Augmentations </h1>
<p id="faction-augmentations-page-desc"> Lists all augmentations that are available to purchase from </p>
<ul class="faction-augmentations-list" id="faction-augmentations-list">
</ul>
</div>
<!-- Augmentations --> <!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container"> <div id="augmentations-container" class="generic-menupage-container"></div>
<h1> Purchased Augmentations </h1>
<p style="width:70%;">
Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to install them.
<br>WARNING: Installing your Augmentations resets most of your progress, including: <br><br>
Stats/Skill levels and Experience <br>
Money <br>
Scripts on every computer but your home computer<br>
Purchased servers <br>
Hacknet Nodes <br>
Faction/Company reputation <br>
Stocks<br><br>
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>
<br><br>
<ul id="queued-augmentations-list"></ul>
<br>
<a id="install-augmentations-button" class="a-link-button"> Install Augmentations </a>
<a id="install-augmentations-backup-button" class="a-link-button"> Backup Save (Export) </a>
<br><br>
<h1> Installed Augmentations </h1>
<p style="width:70%;"> List of all augmentations (including Source Files) that have been installed. You have gained the effects of these augmentations </p>
<ul id="augmentations-list">
</ul>
</div>
<!-- Tutorial content --> <!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container"> <div id="tutorial-container" class="generic-menupage-container">

@ -1,12 +1,16 @@
import {BitNodeMultipliers} from "./BitNode.js"; import {BitNodeMultipliers} from "./BitNode.js";
import {CONSTANTS} from "./Constants.js"; import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js";
import {Factions, getNextNeurofluxLevel} from "./Faction.js"; import {Factions, getNextNeurofluxLevel} from "./Faction.js";
import {addWorkerScript} from "./NetscriptWorker.js"; import {addWorkerScript} from "./NetscriptWorker.js";
import {Player} from "./Player.js"; import {Player} from "./Player.js";
import {prestigeAugmentation} from "./Prestige.js"; import {prestigeAugmentation} from "./Prestige.js";
import {Script, RunningScript} from "./Script.js"; import {Script, RunningScript} from "./Script.js";
import {Server} from "./Server.js"; import {Server} from "./Server.js";
import {SourceFiles} from "./SourceFile.js";
import {dialogBoxCreate} from "../utils/DialogBox.js"; import {dialogBoxCreate} from "../utils/DialogBox.js";
import {createElement, createAccordionElement,
removeChildrenFromElement} from "../utils/HelperFunctions.js";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js"; Generic_fromJSON} from "../utils/JSONReviver.js";
import {isString} from "../utils/StringHelperFunctions.js"; import {isString} from "../utils/StringHelperFunctions.js";
@ -2083,5 +2087,172 @@ function giveAllAugmentations() {
Player.reapplyAllAugmentations(); Player.reapplyAllAugmentations();
} }
function displayAugmentationsContent() {
removeChildrenFromElement(Engine.Display.augmentationsContent);
Engine.Display.augmentationsContent.appendChild(createElement("h1", {
innerText:"Purchased Augmentations",
}));
Engine.Display.augmentationsContent.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\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
Engine.Display.augmentationsContent.appendChild(createElement("a", {
class:"a-link-button", innerText:"Install Augmentations",
tooltip:"'I never asked for this'",
clickListener:()=>{
installAugmentations();
return false;
}
}));
//Backup button
Engine.Display.augmentationsContent.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]);
}
Engine.Display.augmentationsContent.appendChild(queuedAugmentationsList);
//Installed augmentations list
Engine.Display.augmentationsContent.appendChild(createElement("h1", {
innerText:"Installed Augmentations", marginTop:"8px",
}));
Engine.Display.augmentationsContent.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
Engine.Display.augmentationsContent.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();}
}
}
}));
Engine.Display.augmentationsContent.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
Engine.Display.augmentationsContent.appendChild(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);
}
}));
Engine.Display.augmentationsContent.appendChild(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);
}
}));
//Source Files - Temporary...Will probably put in a separate pane Later
displaySourceFiles(augmentationsList, Player.sourceFiles);
displayAugmentations(augmentationsList, Player.augmentations);
Engine.Display.augmentationsContent.appendChild(augmentationsList);
}
//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;
}
var accordion = createAccordionElement({
hdrText:sourceFileObject.name + "<br>" + "Level " + (sourceFiles[i].lvl) + " / 3",
panelText:sourceFileObject.info
});
listElement.appendChild(accordion[0]);
}
}
export {AugmentationNames, Augmentations, PlayerOwnedAugmentation, installAugmentations, export {AugmentationNames, Augmentations, PlayerOwnedAugmentation, installAugmentations,
initAugmentations, applyAugmentation, augmentationExists, Augmentation}; initAugmentations, applyAugmentation, augmentationExists, Augmentation,
displayAugmentationsContent};

@ -69,6 +69,8 @@ var OfficeUpgradeBaseCost = 1e9;
var BribeThreshold = 100e12; //Money needed to be able to bribe for faction rep var BribeThreshold = 100e12; //Money needed to be able to bribe for faction rep
var BribeToRepRatio = 1e9; //Bribe Value divided by this = rep gain var BribeToRepRatio = 1e9; //Bribe Value divided by this = rep gain
var ProductProductionCostRatio = 5; //Ratio of material cost of a product to its production cost
function Material(params={}) { function Material(params={}) {
this.name = params.name ? params.name : ""; this.name = params.name ? params.name : "";
this.qty = 0; //Quantity this.qty = 0; //Quantity
@ -90,6 +92,7 @@ function Material(params={}) {
this.sll = 0; //How much of this material is being sold per second this.sll = 0; //How much of this material is being sold per second
this.prd = 0; //How much of this material is being produced per second this.prd = 0; //How much of this material is being produced per second
this.exp = []; //Exports of this material to another warehouse/industry this.exp = []; //Exports of this material to another warehouse/industry
this.totalExp = 0; //Total export amount for last cycle
this.imp = 0; this.imp = 0;
this.bCost = 0; //$ Cost/sec to buy material this.bCost = 0; //$ Cost/sec to buy material
this.sCost = 0; //$ Cost/sec to sell material this.sCost = 0; //$ Cost/sec to sell material
@ -323,7 +326,7 @@ Product.prototype.finishProduct = function(employeeProd, industry) {
console.log("designMult: " + designMult); console.log("designMult: " + designMult);
var balanceMult = (1.2 * engrRatio) + (0.9 * mgmtRatio) + (1.3 * rndRatio) + var balanceMult = (1.2 * engrRatio) + (0.9 * mgmtRatio) + (1.3 * rndRatio) +
(1.5 * opsRatio) + (busRatio); (1.5 * opsRatio) + (busRatio);
var sciMult = 1 + (Math.pow(industry.sciResearch.qty, industry.sciFac) / 1000); var sciMult = 1 + (Math.pow(industry.sciResearch.qty, industry.sciFac) / 800);
var totalMult = progrMult * balanceMult * designMult * sciMult; var totalMult = progrMult * balanceMult * designMult * sciMult;
this.qlt = totalMult * ((0.10 * employeeProd[EmployeePositions.Engineer]) + this.qlt = totalMult * ((0.10 * employeeProd[EmployeePositions.Engineer]) +
@ -358,7 +361,7 @@ Product.prototype.finishProduct = function(employeeProd, industry) {
(0.05 * employeeProd[EmployeePositions.Business])); (0.05 * employeeProd[EmployeePositions.Business]));
this.calculateRating(industry); this.calculateRating(industry);
var advMult = 1 + (Math.pow(this.advCost, 0.1) / 100); var advMult = 1 + (Math.pow(this.advCost, 0.1) / 100);
this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.6) * (busRatio + mgmtRatio)); this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.65) * (busRatio + mgmtRatio));
this.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness))); this.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));
this.cmp = getRandomInt(0, 70); this.cmp = getRandomInt(0, 70);
@ -516,6 +519,14 @@ var ProductRatingWeights = {
Aesthetics: 0.05, Aesthetics: 0.05,
Features: 0.1, Features: 0.1,
}, },
"Computer" : { //Repeat
Quality: 0.15,
Performance: 0.25,
Durability: 0.25,
Reliability: 0.2,
Aesthetics: 0.05,
Features: 0.1,
},
[Industries.Robotics]: { [Industries.Robotics]: {
Quality: 0.1, Quality: 0.1,
Performance: 0.2, Performance: 0.2,
@ -553,11 +564,11 @@ var ProductRatingWeights = {
var IndustryUpgrades = { var IndustryUpgrades = {
"0": [0, 500e3, 1, 1.05, "0": [0, 500e3, 1, 1.05,
"Coffee", "Provide your employees with coffee, increasing their energy by 5%."], "Coffee", "Provide your employees with coffee, increasing their energy by 5%."],
"1": [1, 1e9, 1.05, 1.03, "1": [1, 1e9, 1.06, 1.03,
"AdVert.Inc", "Hire AdVert.Inc to advertise your company. Each level of " + "AdVert.Inc", "Hire AdVert.Inc to advertise your company. Each level of " +
"this upgrade grants your company a static increase of 4 and 1 to its awareness and " + "this upgrade grants your company a static increase of 3 and 1 to its awareness and " +
"popularity, respectively. It will then increase your company's awareness by 1%, and its popularity " + "popularity, respectively. It will then increase your company's awareness by 1%, and its popularity " +
"by a random percentage between 2% and 4%. These effects are increased by other upgrades " + "by a random percentage between 1% and 3%. These effects are increased by other upgrades " +
"that increase the power of your advertising."] "that increase the power of your advertising."]
} }
@ -645,28 +656,28 @@ Industry.prototype.init = function() {
this.sciFac = 0.7; this.sciFac = 0.7;
this.robFac = 0.05; this.robFac = 0.05;
this.aiFac = 0.3; this.aiFac = 0.3;
this.advFac = 0.07; this.advFac = 0.08;
this.reqMats = { this.reqMats = {
"Hardware": 0.1, "Hardware": 0.1,
"Metal": 0.25, "Metal": 0.2,
}; };
this.prodMats = ["Energy"]; this.prodMats = ["Energy"];
break; break;
case Industries.Utilities: case Industries.Utilities:
case "Utilities": case "Utilities":
this.reFac = 0.4; this.reFac = 0.5;
this.sciFac = 0.6; this.sciFac = 0.6;
this.robFac = 0.3; this.robFac = 0.4;
this.aiFac = 0.3; this.aiFac = 0.4;
this.advFac = 0.07; this.advFac = 0.08;
this.reqMats = { this.reqMats = {
"Hardware": 0.1, "Hardware": 0.1,
"Metal": 0.2, "Metal": 0.1,
} }
this.prodMats = ["Water"]; this.prodMats = ["Water"];
break; break;
case Industries.Agriculture: case Industries.Agriculture:
this.reFac = 0.8; this.reFac = 0.75;
this.sciFac = 0.5; this.sciFac = 0.5;
this.hwFac = 0.2; this.hwFac = 0.2;
this.robFac = 0.3; this.robFac = 0.3;
@ -684,7 +695,7 @@ Industry.prototype.init = function() {
this.hwFac = 0.35; this.hwFac = 0.35;
this.robFac = 0.5; this.robFac = 0.5;
this.aiFac = 0.2; this.aiFac = 0.2;
this.advFac = 0.06; this.advFac = 0.08;
this.reqMats = { this.reqMats = {
"Energy": 0.5, "Energy": 0.5,
} }
@ -692,11 +703,11 @@ Industry.prototype.init = function() {
break; break;
case Industries.Mining: case Industries.Mining:
this.reFac = 0.3; this.reFac = 0.3;
this.sciFac = 0.25; this.sciFac = 0.26;
this.hwFac = 0.4; this.hwFac = 0.4;
this.robFac = 0.5; this.robFac = 0.45;
this.aiFac = 0.5; this.aiFac = 0.45;
this.advFac = 0.04; this.advFac = 0.06;
this.reqMats = { this.reqMats = {
"Energy": 0.8, "Energy": 0.8,
} }
@ -736,7 +747,7 @@ Industry.prototype.init = function() {
this.hwFac = 0.2; this.hwFac = 0.2;
this.robFac = 0.25; this.robFac = 0.25;
this.aiFac = 0.2; this.aiFac = 0.2;
this.advFac = 0.05; this.advFac = 0.07;
this.reqMats = { this.reqMats = {
"Plants": 1, "Plants": 1,
"Energy": 0.5, "Energy": 0.5,
@ -750,7 +761,7 @@ Industry.prototype.init = function() {
this.hwFac = 0.15; this.hwFac = 0.15;
this.robFac = 0.25; this.robFac = 0.25;
this.aiFac = 0.2; this.aiFac = 0.2;
this.advFac = 0.15; this.advFac = 0.16;
this.reqMats = { this.reqMats = {
"Chemicals": 2, "Chemicals": 2,
"Energy": 1, "Energy": 1,
@ -762,9 +773,9 @@ Industry.prototype.init = function() {
case Industries.Computer: case Industries.Computer:
case "Computer": case "Computer":
this.reFac = 0.2; this.reFac = 0.2;
this.sciFac = 0.65; this.sciFac = 0.62;
this.robFac = 0.4; this.robFac = 0.36;
this.aiFac = 0.2; this.aiFac = 0.19;
this.advFac = 0.17; this.advFac = 0.17;
this.reqMats = { this.reqMats = {
"Metal": 2.5, "Metal": 2.5,
@ -774,11 +785,11 @@ Industry.prototype.init = function() {
this.makesProducts = true; this.makesProducts = true;
break; break;
case Industries.Robotics: case Industries.Robotics:
this.reFac = 0.35; this.reFac = 0.32;
this.sciFac = 0.7; this.sciFac = 0.65;
this.aiFac = 0.4; this.aiFac = 0.36;
this.advFac = 0.2; this.advFac = 0.18;
this.hwFac = 0.2; this.hwFac = 0.19;
this.reqMats = { this.reqMats = {
"Hardware": 5, "Hardware": 5,
"Energy": 3, "Energy": 3,
@ -787,7 +798,7 @@ Industry.prototype.init = function() {
this.makesProducts = true; this.makesProducts = true;
break; break;
case Industries.Software: case Industries.Software:
this.sciFac = 0.65; this.sciFac = 0.62;
this.advFac = 0.16; this.advFac = 0.16;
this.hwFac = 0.25; this.hwFac = 0.25;
this.reFac = 0.1; this.reFac = 0.1;
@ -801,9 +812,9 @@ Industry.prototype.init = function() {
this.makesProducts = true; this.makesProducts = true;
break; break;
case Industries.Healthcare: case Industries.Healthcare:
//reFac is unique for this bc it diminishes greatly per city. Handle this separately in code? this.reFac = 0.1;
this.sciFac = 0.75; this.sciFac = 0.75;
this.advFac = 0.1; this.advFac = 0.11;
this.hwFac = 0.1; this.hwFac = 0.1;
this.robFac = 0.1; this.robFac = 0.1;
this.aiFac = 0.1; this.aiFac = 0.1;
@ -904,8 +915,8 @@ Industry.prototype.updateWarehouseSizeUsed = function(warehouse) {
if (this.products.hasOwnProperty(prodName)) { if (this.products.hasOwnProperty(prodName)) {
var prod = this.products[prodName]; var prod = this.products[prodName];
warehouse.sizeUsed += (prod.data[warehouse.loc][0] * prod.siz); warehouse.sizeUsed += (prod.data[warehouse.loc][0] * prod.siz);
if (prod.data[warehouse.loc][0] > 0 && warehouse.loc === currentCityUi) { if (prod.data[warehouse.loc][0] > 0) {
industryWarehouseStorageBreakdownText += (prodName + ": " + formatNumber(prod.data[warehouse.loc][0] * prod.siz, 0) + "<br>"); warehouse.breakdown += (prodName + ": " + formatNumber(prod.data[warehouse.loc][0] * prod.siz, 0) + "<br>");
} }
} }
} }
@ -1029,6 +1040,24 @@ Industry.prototype.processProductMarket = function(marketCycles=1) {
Industry.prototype.processMaterials = function(marketCycles=1, company) { Industry.prototype.processMaterials = function(marketCycles=1, company) {
var revenue = 0, expenses = 0, industry = this; var revenue = 0, expenses = 0, industry = this;
this.calculateProductionFactors(); this.calculateProductionFactors();
//At the start of the export state, set the imports of everything to 0
if (this.state === "EXPORT") {
for (var i = 0; i < Cities.length; ++i) {
var city = Cities[i], office = this.offices[city];
if (!(this.warehouses[city] instanceof Warehouse)) {
continue;
}
var warehouse = this.warehouses[city];
for (var matName in warehouse.materials) {
if (warehouse.materials.hasOwnProperty(matName)) {
var mat = warehouse.materials[matName];
mat.imp = 0;
}
}
}
}
for (var i = 0; i < Cities.length; ++i) { for (var i = 0; i < Cities.length; ++i) {
var city = Cities[i], office = this.offices[city]; var city = Cities[i], office = this.offices[city];
@ -1103,6 +1132,8 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
prod = Math.min(maxAmt, prod); prod = Math.min(maxAmt, prod);
} }
if (prod < 0) {prod = 0;}
//Keep track of production for smart supply (/s) //Keep track of production for smart supply (/s)
warehouse.smartSupplyStore += (prod / (SecsPerMarketCycle * marketCycles)); warehouse.smartSupplyStore += (prod / (SecsPerMarketCycle * marketCycles));
@ -1202,16 +1233,32 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
company.getSalesMultiplier() * advertisingFactor; company.getSalesMultiplier() * advertisingFactor;
var sellAmt; var sellAmt;
if (mat.sllman[1] !== -1) { if (isString(mat.sllman[1])) {
//Sell amount is manually limited //Dynamically evaluated
sellAmt = Math.min(maxSell, mat.sllman[1]); var tmp = mat.sllman[1].replace(/MAX/g, maxSell);
} else { tmp = tmp.replace(/PROD/g, mat.prd);
sellAmt = maxSell; try {
sellAmt = eval(tmp);
} catch(e) {
dialogBoxCreate("Error evaluating your sell amount for material " + mat.name +
" in " + this.name + "'s " + city + " office. The sell amount " +
"is being set to zero");
sellAmt = 0;
} }
sellAmt = Math.min(maxSell, sellAmt);
} else if (mat.sllman[1] === -1) {
//Backwards compatibility, -1 = MAX
sellAmt = maxSell;
} else {
//Player's input value is just a number
sellAmt = Math.min(maxSell, mat.sllman[1]);
}
sellAmt = (sellAmt * SecsPerMarketCycle * marketCycles); sellAmt = (sellAmt * SecsPerMarketCycle * marketCycles);
sellAmt = Math.min(mat.qty, sellAmt); sellAmt = Math.min(mat.qty, sellAmt);
if (sellAmt < 0) { if (sellAmt < 0) {
console.log("ERROR: sellAmt is negative"); console.log("sellAmt calculated to be negative");
mat.sll = 0;
continue; continue;
} }
if (sellAmt && sCost >= 0) { if (sellAmt && sCost >= 0) {
@ -1229,10 +1276,26 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
for (var matName in warehouse.materials) { for (var matName in warehouse.materials) {
if (warehouse.materials.hasOwnProperty(matName)) { if (warehouse.materials.hasOwnProperty(matName)) {
var mat = warehouse.materials[matName]; var mat = warehouse.materials[matName];
mat.totalExp = 0; //Reset export
for (var expI = 0; expI < mat.exp.length; ++expI) { for (var expI = 0; expI < mat.exp.length; ++expI) {
var exp = mat.exp[expI]; var exp = mat.exp[expI];
var amt = exp.amt * SecsPerMarketCycle * marketCycles; var amt = exp.amt.replace(/MAX/g, mat.qty / (SecsPerMarketCycle * marketCycles));
if (mat.qty <= amt) { try {
amt = eval(amt);
} catch(e) {
dialogBoxCreate("Calculating export for " + mat.name + " in " +
this.name + "'s " + city + " division failed with " +
"error: " + e);
continue;
}
if (isNaN(amt)) {
dialogBoxCreate("Error calculating export amount for " + mat.name + " in " +
this.name + "'s " + city + " division.");
continue;
}
amt = amt * SecsPerMarketCycle * marketCycles;
if (mat.qty < amt) {
amt = mat.qty; amt = mat.qty;
} }
if (amt === 0) { if (amt === 0) {
@ -1246,12 +1309,26 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
console.log("ERROR: Invalid export! " + expIndustry.name + " " + exp.city); console.log("ERROR: Invalid export! " + expIndustry.name + " " + exp.city);
break; break;
} }
//Make sure theres enough space in warehouse
if (expWarehouse.sizeUsed >= expWarehouse.size) {
return; //Warehouse at capacity
} else {
var maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialSizes[matName]);
amt = Math.min(maxAmt, amt);
}
expWarehouse.materials[matName].imp += (amt / (SecsPerMarketCycle * marketCycles));
expWarehouse.materials[matName].qty += amt; expWarehouse.materials[matName].qty += amt;
expWarehouse.materials[matName].qlt = mat.qlt;
mat.qty -= amt; mat.qty -= amt;
mat.totalExp += amt;
expIndustry.updateWarehouseSizeUsed(expWarehouse);
break; break;
} }
} }
} }
//totalExp should be per second
mat.totalExp /= (SecsPerMarketCycle * marketCycles);
} }
} }
@ -1270,7 +1347,7 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
//Produce Scientific Research based on R&D employees //Produce Scientific Research based on R&D employees
//Scientific Research can be produced without a warehouse //Scientific Research can be produced without a warehouse
if (office instanceof OfficeSpace) { if (office instanceof OfficeSpace) {
this.sciResearch.qty += (.01 * Math.pow(office.employeeProd[EmployeePositions.RandD], 0.5) this.sciResearch.qty += (.005 * Math.pow(office.employeeProd[EmployeePositions.RandD], 0.5)
* company.getScientificResearchMultiplier()); * company.getScientificResearchMultiplier());
} }
} }
@ -1397,32 +1474,53 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
} }
//Since its a product, its production cost is increased for labor //Since its a product, its production cost is increased for labor
product.pCost *= 3; product.pCost *= ProductProductionCostRatio;
//Calculate Sale Cost (sCost), which could be dynamically evaluated
var sCost;
if (isString(product.sCost)) {
sCost = product.sCost.replace(/MP/g, product.pCost + product.rat / product.mku);
sCost = eval(sCost);
} else {
sCost = product.sCost;
}
var markup = 1, markupLimit = product.rat / product.mku; var markup = 1, markupLimit = product.rat / product.mku;
if (product.sCost > product.pCost) { if (sCost > product.pCost) {
if ((product.sCost - product.pCost) > markupLimit) { if ((sCost - product.pCost) > markupLimit) {
markup = markupLimit / (product.sCost - product.pCost); markup = markupLimit / (sCost - product.pCost);
} }
} }
//var businessFactor = 1 + (office.employeeProd[EmployeePositions.Business] / office.employeeProd["total"]);
var businessFactor = this.getBusinessFactor(office); //Business employee productivity var businessFactor = this.getBusinessFactor(office); //Business employee productivity
var advertisingFactor = this.getAdvertisingFactors()[0]; //Awareness + popularity var advertisingFactor = this.getAdvertisingFactors()[0]; //Awareness + popularity
var marketFactor = this.getMarketFactor(product); //Competition + demand var marketFactor = this.getMarketFactor(product); //Competition + demand
var maxSell = 0.5 * Math.pow(product.rat, 0.65) * marketFactor * corporation.getSalesMultiplier() * var maxSell = 0.5 * Math.pow(product.rat, 0.65) * marketFactor * corporation.getSalesMultiplier() *
Math.pow(markup, 2) * businessFactor * advertisingFactor; Math.pow(markup, 2) * businessFactor * advertisingFactor;
var sellAmt; var sellAmt;
if (product.sllman[city][0] && product.sllman[city][1] > 0) { if (product.sllman[city][0] && isString(product.sllman[city][1])) {
//Sell amount is dynamically evaluated
var tmp = product.sllman[city][1].replace(/MAX/g, maxSell);
tmp = tmp.replace(/PROD/g, product.data[city][1]);
try {
tmp = eval(tmp);
} catch(e) {
dialogBoxCreate("Error evaluating your sell price expression for " + product.name +
" in " + this.name + "'s " + city + " office. Sell price is being set to MAX");
tmp = maxSell;
}
sellAmt = Math.min(maxSell, tmp);
} else if (product.sllman[city][0] && product.sllman[city][1] > 0) {
//Sell amount is manually limited //Sell amount is manually limited
sellAmt = Math.min(maxSell, product.sllman[city][1]); sellAmt = Math.min(maxSell, product.sllman[city][1]);
} else { } else {
//Backwards compatibility, -1 = 0
sellAmt = maxSell; sellAmt = maxSell;
} }
sellAmt = sellAmt * SecsPerMarketCycle * marketCycles; sellAmt = sellAmt * SecsPerMarketCycle * marketCycles;
sellAmt = Math.min(product.data[city][0], sellAmt); //data[0] is qty sellAmt = Math.min(product.data[city][0], sellAmt); //data[0] is qty
if (sellAmt && product.sCost) { if (sellAmt && sCost) {
product.data[city][0] -= sellAmt; //data[0] is qty product.data[city][0] -= sellAmt; //data[0] is qty
totalProfit += (sellAmt * product.sCost); totalProfit += (sellAmt * sCost);
product.data[city][2] = sellAmt / (SecsPerMarketCycle * marketCycles); //data[2] is sell property product.data[city][2] = sellAmt / (SecsPerMarketCycle * marketCycles); //data[2] is sell property
} else { } else {
product.data[city][2] = 0; //data[2] is sell property product.data[city][2] = 0; //data[2] is sell property
@ -1470,10 +1568,10 @@ Industry.prototype.upgrade = function(upgrade, refs) {
break; break;
case 1: //AdVert.Inc, case 1: //AdVert.Inc,
var advMult = corporation.getAdvertisingMultiplier(); var advMult = corporation.getAdvertisingMultiplier();
this.awareness += (4 * advMult); this.awareness += (3 * advMult);
this.popularity += (1 * advMult); this.popularity += (1 * advMult);
this.awareness *= (1.01 * advMult); this.awareness *= (1.01 * advMult);
this.popularity *= ((1 + getRandomInt(2, 4) / 100) * advMult); this.popularity *= ((1 + getRandomInt(1, 3) / 100) * advMult);
break; break;
default: default:
console.log("ERROR: Un-implemented function index: " + upgN); console.log("ERROR: Un-implemented function index: " + upgN);
@ -1507,7 +1605,7 @@ Industry.prototype.getBusinessFactor = function(office) {
if (office.employeeProd["total"] > 0) { if (office.employeeProd["total"] > 0) {
ratioMult = 1 + (office.employeeProd[EmployeePositions.Business] / office.employeeProd["total"]); ratioMult = 1 + (office.employeeProd[EmployeePositions.Business] / office.employeeProd["total"]);
} }
return ratioMult * Math.pow(1 + office.employeeProd[EmployeePositions.Business], 0.1); return ratioMult * Math.pow(1 + office.employeeProd[EmployeePositions.Business], 0.15);
} }
//Returns a set of multipliers based on the Industry's awareness, popularity, and advFac. This //Returns a set of multipliers based on the Industry's awareness, popularity, and advFac. This
@ -1987,6 +2085,7 @@ function Warehouse(params={}) {
this.level = 0; this.level = 0;
this.sizeUsed = 0; this.sizeUsed = 0;
this.smartSupplyEnabled = false; //Whether or not smart supply is enabled this.smartSupplyEnabled = false; //Whether or not smart supply is enabled
this.breakdown = "";
//Stores the amount of product to be produced. Used for Smart Supply unlock. //Stores the amount of product to be produced. Used for Smart Supply unlock.
//The production tracked by smart supply is always based on the previous cycle, //The production tracked by smart supply is always based on the previous cycle,
@ -2010,14 +2109,14 @@ function Warehouse(params={}) {
Warehouse.prototype.updateMaterialSizeUsed = function() { Warehouse.prototype.updateMaterialSizeUsed = function() {
this.sizeUsed = 0; this.sizeUsed = 0;
if (this.loc === currentCityUi) {industryWarehouseStorageBreakdownText = ""; } this.breakdown = "";
for (var matName in this.materials) { for (var matName in this.materials) {
if (this.materials.hasOwnProperty(matName)) { if (this.materials.hasOwnProperty(matName)) {
var mat = this.materials[matName]; var mat = this.materials[matName];
if (MaterialSizes.hasOwnProperty(matName)) { if (MaterialSizes.hasOwnProperty(matName)) {
this.sizeUsed += (mat.qty * MaterialSizes[matName]); this.sizeUsed += (mat.qty * MaterialSizes[matName]);
if (mat.qty > 0 && this.loc === currentCityUi) { if (mat.qty > 0) {
industryWarehouseStorageBreakdownText += (matName + ": " + formatNumber(mat.qty * MaterialSizes[matName], 0) + "<br>"); this.breakdown += (matName + ": " + formatNumber(mat.qty * MaterialSizes[matName], 0) + "<br>");
} }
} }
} }
@ -2050,7 +2149,7 @@ Warehouse.prototype.createUI = function(parentRefs) {
industryWarehousePanel.appendChild(industryWarehouseStorageText); industryWarehousePanel.appendChild(industryWarehouseStorageText);
//Upgrade warehouse size button //Upgrade warehouse size button
var upgradeCost = WarehouseUpgradeBaseCost * Math.pow(1.07, Math.round(this.size / 100) - 1); var upgradeCost = WarehouseUpgradeBaseCost * Math.pow(1.07, this.level+1);
industryWarehouseUpgradeSizeButton = createElement("a", { industryWarehouseUpgradeSizeButton = createElement("a", {
innerText:"Upgrade Warehouse Size - " + numeral(upgradeCost).format('$0.000a'), innerText:"Upgrade Warehouse Size - " + numeral(upgradeCost).format('$0.000a'),
display:"inline-block", display:"inline-block",
@ -2157,15 +2256,14 @@ Warehouse.prototype.updateUI = function(parentRefs) {
var storageText = "Storage: " + var storageText = "Storage: " +
(this.sizedUsed >= this.size ? formatNumber(this.sizeUsed, 3) : formatNumber(this.sizeUsed, 3)) + (this.sizedUsed >= this.size ? formatNumber(this.sizeUsed, 3) : formatNumber(this.sizeUsed, 3)) +
"/" + formatNumber(this.size, 3); "/" + formatNumber(this.size, 3);
if (industryWarehouseStorageBreakdownText != null && if (this.breakdown != null && this.breakdown != "") {
industryWarehouseStorageBreakdownText != "") {
storageText += ("<span class='tooltiptext'>" + storageText += ("<span class='tooltiptext'>" +
industryWarehouseStorageBreakdownText + "</span>"); this.breakdown + "</span>");
} }
industryWarehouseStorageText.innerHTML = storageText; industryWarehouseStorageText.innerHTML = storageText;
//Upgrade warehouse size button //Upgrade warehouse size button
var upgradeCost = WarehouseUpgradeBaseCost * Math.pow(1.07, Math.round(this.size / 100) - 1); var upgradeCost = WarehouseUpgradeBaseCost * Math.pow(1.07, this.level+1);
if (company.funds.lt(upgradeCost)) { if (company.funds.lt(upgradeCost)) {
industryWarehouseUpgradeSizeButton.className = "a-link-button-inactive"; industryWarehouseUpgradeSizeButton.className = "a-link-button-inactive";
} else { } else {
@ -2209,8 +2307,8 @@ Warehouse.prototype.updateUI = function(parentRefs) {
} }
//Products //Products
if (industry.makesProducts && Object.keys(industry.products).length > 0) {
removeChildrenFromElement(industryWarehouseProducts); removeChildrenFromElement(industryWarehouseProducts);
if (industry.makesProducts && Object.keys(industry.products).length > 0) {
for (var productName in industry.products) { for (var productName in industry.products) {
if (industry.products.hasOwnProperty(productName) && industry.products[productName] instanceof Product) { if (industry.products.hasOwnProperty(productName) && industry.products[productName] instanceof Product) {
industryWarehouseProducts.appendChild(this.createProductUI(industry.products[productName], parentRefs)); industryWarehouseProducts.appendChild(this.createProductUI(industry.products[productName], parentRefs));
@ -2231,11 +2329,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
class:"cmpy-mgmt-warehouse-material-div", class:"cmpy-mgmt-warehouse-material-div",
}); });
var totalExport = 0; var totalGain = mat.buy + mat.prd + mat.imp - mat.sll - mat.totalExp;
for (var i = 0; i < mat.exp.length; ++i) {
totalExport += mat.exp[i].amt;
}
var totalGain = mat.buy + mat.prd + mat.imp - mat.sll - totalExport;
//If Market Research upgrades are unlocked, add competition and demand info //If Market Research upgrades are unlocked, add competition and demand info
var cmpAndDmdText = ""; var cmpAndDmdText = "";
@ -2249,7 +2343,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"(" + formatNumber(totalGain, 3) + "/s)" + "(" + formatNumber(totalGain, 3) + "/s)" +
"<span class='tooltiptext'>Buy: " + formatNumber(mat.buy, 3) + "<span class='tooltiptext'>Buy: " + formatNumber(mat.buy, 3) +
"/s<br>Prod: " + formatNumber(mat.prd, 3) + "/s<br>Sell: " + formatNumber(mat.sll, 3) + "/s<br>Prod: " + formatNumber(mat.prd, 3) + "/s<br>Sell: " + formatNumber(mat.sll, 3) +
"/s<br>Export: " + formatNumber(totalExport, 3) + "/s<br>Import: " + "/s<br>Export: " + formatNumber(mat.totalExp, 3) + "/s<br>Import: " +
formatNumber(mat.imp, 3) + "/s" + cmpAndDmdText + "</span></p><br>" + formatNumber(mat.imp, 3) + "/s" + cmpAndDmdText + "</span></p><br>" +
"<p class='tooltip'>MP: $" + formatNumber(mat.bCost, 2) + "<p class='tooltip'>MP: $" + formatNumber(mat.bCost, 2) +
"<span class='tooltiptext'>Market Price: The price you would pay if " + "<span class='tooltiptext'>Market Price: The price you would pay if " +
@ -2380,7 +2474,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
//Select amount to export //Select amount to export
var exportAmount = createElement("input", { var exportAmount = createElement("input", {
type:"number", placeholder:"Export amount / s" placeholder:"Export amount / s"
}); });
var exportBtn = createElement("a", { var exportBtn = createElement("a", {
@ -2388,28 +2482,24 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
clickListener:()=>{ clickListener:()=>{
var industryName = industrySelector.options[industrySelector.selectedIndex].text, var industryName = industrySelector.options[industrySelector.selectedIndex].text,
cityName = citySelector.options[citySelector.selectedIndex].text, cityName = citySelector.options[citySelector.selectedIndex].text,
amt = parseFloat(exportAmount.value); amt = exportAmount.value;
if (isNaN(amt)) { //Sanitize amt
var sanitizedAmt = amt.replace(/\s+/g, '');
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAX]/g, '');
var temp = sanitizedAmt.replace(/MAX/g, 1);
try {
temp = eval(temp);
} catch(e) {
dialogBoxCreate("Invalid expression entered for export amount: " + e);
return false;
}
if (temp == null || isNaN(temp)) {
dialogBoxCreate("Invalid amount entered for export"); dialogBoxCreate("Invalid amount entered for export");
return; return;
} }
var exportObj = {ind:industryName, city:cityName, amt:amt}; var exportObj = {ind:industryName, city:cityName, amt:sanitizedAmt};
mat.exp.push(exportObj); mat.exp.push(exportObj);
//Go to the target city and increase the mat.imp attribute for the corresponding material
for (var i = 0; i < company.divisions.length; ++i) {
if (company.divisions[i].name === industryName) {
var warehouse = company.divisions[i].warehouses[cityName];
if (warehouse instanceof Warehouse) {
warehouse.materials[matName].imp += amt;
removeElementById(popupId);
return false;
} else {
console.log("ERROR: Target city for export does not have warehouse in specified city");
}
}
}
console.log("ERROR: Could not find target industry/city for export");
removeElementById(popupId); removeElementById(popupId);
return false; return false;
} }
@ -2436,17 +2526,6 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"City: " + mat.exp[i].city + "<br>" + "City: " + mat.exp[i].city + "<br>" +
"Amount/s: " + mat.exp[i].amt, "Amount/s: " + mat.exp[i].amt,
clickListener:()=>{ clickListener:()=>{
//Go to the target city and decrease the mat.imp attribute for the corresponding material
for (var j = 0; j < company.divisions.length; ++j) {
if (company.divisions[j].name === mat.exp[i].ind) {
var warehouse = company.divisions[j].warehouses[mat.exp[i].city];
if (warehouse instanceof Warehouse) {
warehouse.materials[matName].imp -= mat.exp[i].amt;
} else {
console.log("ERROR: Target city for export does not have warehouse in specified city");
}
}
}
mat.exp.splice(i, 1); //Remove export object mat.exp.splice(i, 1); //Remove export object
removeElementById(popupId); removeElementById(popupId);
createExportPopup(); createExportPopup();
@ -2493,6 +2572,9 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"if set to 0, then the material will be discarded<br><br>" + "if set to 0, then the material will be discarded<br><br>" +
"Setting the sell amount to 'MAX' will result in you always selling the " + "Setting the sell amount to 'MAX' will result in you always selling the " +
"maximum possible amount of the material.<br><br>" + "maximum possible amount of the material.<br><br>" +
"When setting the sell amount, you can use the 'PROD' variable to designate a dynamically " +
"changing amount that depends on your production. For example, if you set the sell amount " +
"to 'PROD-5' then you will always sell 5 less of the material than you produce.<br><br>" +
"When setting the sell price, you can use the 'MP' variable to designate a dynamically " + "When setting the sell price, you can use the 'MP' variable to designate a dynamically " +
"changing price that depends on the market price. For example, if you set the sell price " + "changing price that depends on the market price. For example, if you set the sell price " +
"to 'MP+10' then it will always be sold at $10 above the market price.", "to 'MP+10' then it will always be sold at $10 above the market price.",
@ -2519,11 +2601,16 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
innerText:"Confirm", class:"a-link-button", margin:"6px", innerText:"Confirm", class:"a-link-button", margin:"6px",
clickListener:()=>{ clickListener:()=>{
//Parse price //Parse price
//Sanitize cost
var cost = inputPx.value.replace(/\s+/g, ''); var cost = inputPx.value.replace(/\s+/g, '');
cost = cost.replace(/[^-()\d/*+.MP]/g, ''); cost = cost.replace(/[^-()\d/*+.MP]/g, ''); //Sanitize cost
var temp = cost.replace(/MP/g, mat.bCost); var temp = cost.replace(/MP/g, mat.bCost);
var temp = eval(temp); try {
temp = eval(temp);
} catch(e) {
dialogBoxCreate("Invalid value or expression for sell price field: " + e);
return false;
}
if (temp == null || isNaN(temp)) { if (temp == null || isNaN(temp)) {
dialogBoxCreate("Invalid value or expression for sell price field"); dialogBoxCreate("Invalid value or expression for sell price field");
return false; return false;
@ -2536,9 +2623,25 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
} }
//Parse quantity //Parse quantity
if (inputQty.value === "MAX") { if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) {
var qty = inputQty.value.replace(/\s+/g, '');
qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, '');
var temp = qty.replace(/MAX/g, 1);
temp = temp.replace(/PROD/g, 1);
try {
temp = eval(temp);
} catch(e) {
dialogBoxCreate("Invalid value or expression for sell price field: " + e);
return false;
}
if (temp == null || isNaN(temp)) {
dialogBoxCreate("Invalid value or expression for sell price field");
return false;
}
mat.sllman[0] = true; mat.sllman[0] = true;
mat.sllman[1] = -1; mat.sllman[1] = qty; //Use sanitized input
} else if (isNaN(inputQty.value)) { } else if (isNaN(inputQty.value)) {
dialogBoxCreate("Invalid value for sell quantity field! Must be numeric or 'MAX'"); dialogBoxCreate("Invalid value for sell quantity field! Must be numeric or 'MAX'");
return false; return false;
@ -2611,10 +2714,8 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
"Aesthetics: " + formatNumber(product.aes, 3) + "<br>" + "Aesthetics: " + formatNumber(product.aes, 3) + "<br>" +
"Features: " + formatNumber(product.fea, 3) + "Features: " + formatNumber(product.fea, 3) +
cmpAndDmdText + "</span></p><br>" + cmpAndDmdText + "</span></p><br>" +
"<p class='tooltip'>Est. Production Cost: " + numeral(product.pCost).format("$0.000a") + "<p class='tooltip'>Est. Production Cost: " + numeral(product.pCost / ProductProductionCostRatio).format("$0.000a") +
"<span class='tooltiptext'>An estimate of how much it costs to produce one unit of this product. " + "<span class='tooltiptext'>An estimate of the material cost it takes to create this Product.</span></p><br>" +
"If your sell price exceeds this by too much, people won't buy your product. The better your " +
"product is, the higher you can mark up its price.</span></p><br>" +
"<p class='tooltip'>Est. Market Price: " + numeral(product.pCost + product.rat / product.mku).format("$0.000a") + "<p class='tooltip'>Est. Market Price: " + numeral(product.pCost + product.rat / product.mku).format("$0.000a") +
"<span class='tooltiptext'>An estimate of how much consumers are willing to pay for this product. " + "<span class='tooltiptext'>An estimate of how much consumers are willing to pay for this product. " +
"Setting the sale price above this may result in less sales. Setting the sale price below this may result " + "Setting the sale price above this may result in less sales. Setting the sale price below this may result " +
@ -2629,8 +2730,12 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
var sellInnerTextString = (product.sllman[city][1] === -1 ? "Sell (" + formatNumber(product.data[city][2], 3) + "/MAX)" : var sellInnerTextString = (product.sllman[city][1] === -1 ? "Sell (" + formatNumber(product.data[city][2], 3) + "/MAX)" :
"Sell (" + formatNumber(product.data[city][2], 3) + "/" + formatNumber(product.sllman[city][1], 3) + ")"); "Sell (" + formatNumber(product.data[city][2], 3) + "/" + formatNumber(product.sllman[city][1], 3) + ")");
if (product.sCost) { if (product.sCost) {
if (isString(product.sCost)) {
sellInnerTextString += (" @ " + product.sCost);
} else {
sellInnerTextString += (" @ " + numeral(product.sCost).format("$0.000a")); sellInnerTextString += (" @ " + numeral(product.sCost).format("$0.000a"));
} }
}
div.appendChild(createElement("a", { div.appendChild(createElement("a", {
innerText:sellInnerTextString, class:"a-link-button", display:"inline-block",margin:"6px", innerText:sellInnerTextString, class:"a-link-button", display:"inline-block",margin:"6px",
clickListener:()=>{ clickListener:()=>{
@ -2642,7 +2747,15 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
"If the sell amount is set to 0, then the product will not be sold. If the " + "If the sell amount is set to 0, then the product will not be sold. If the " +
"sell price is set to 0, then the product will be discarded.<br><br>" + "sell price is set to 0, then the product will be discarded.<br><br>" +
"Setting the sell amount to 'MAX' will result in you always selling the " + "Setting the sell amount to 'MAX' will result in you always selling the " +
"maximum possible amount of the material.<br><br>", "maximum possible amount of the material.<br><br>" +
"When setting the sell amount, you can use the 'PROD' variable to designate a " +
"dynamically changing amount that depends on your production. For example, " +
"if you set the sell amount to 'PROD-1' then you will always sell 1 less of " +
"the material than you produce.<br><br>" +
"When setting the sell price, you can use the 'MP' variable to set a " +
"dynamically changing price that depends on the Product's estimated " +
"market price. For example, if you set it to 'MP*5' then it " +
"will always be sold at five times the estimated market price.",
}); });
var confirmBtn; var confirmBtn;
var inputQty = createElement("input", { var inputQty = createElement("input", {
@ -2663,17 +2776,52 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
class:"a-link-button", innerText:"Confirm", class:"a-link-button", innerText:"Confirm",
clickListener:()=>{ clickListener:()=>{
//Parse price //Parse price
if (inputPx.value.includes("MP")) {
//Dynamically evaluated quantity. First test to make sure its valid
//Sanitize input, then replace dynamic variables with arbitrary numbers
var price = inputPx.value.replace(/\s+/g, '');
price = price.replace(/[^-()\d/*+.MP]/g, '');
var temp = price.replace(/MP/g, 1);
try {
temp = eval(temp);
} catch(e) {
dialogBoxCreate("Invalid value or expression for sell quantity field: " + e);
return false;
}
if (temp == null || isNaN(temp)) {
dialogBoxCreate("Invalid value or expression for sell quantity field.");
return false;
}
product.sCost = price; //Use sanitized price
} else {
var cost = parseFloat(inputPx.value); var cost = parseFloat(inputPx.value);
if (isNaN(cost)) { if (isNaN(cost)) {
dialogBoxCreate("Invalid value for sell price field"); dialogBoxCreate("Invalid value for sell price field");
return false; return false;
} }
product.sCost = cost; product.sCost = cost;
}
//Parse quantity //Parse quantity
if (inputQty.value === "MAX") { if (inputQty.value.includes("MAX") || inputQty.value.includes("PROD")) {
//Dynamically evaluated quantity. First test to make sure its valid
var qty = inputQty.value.replace(/\s+/g, '');
qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, '');
var temp = qty.replace(/MAX/g, 1);
temp = temp.replace(/PROD/g, 1);
try {
temp = eval(temp);
} catch(e) {
dialogBoxCreate("Invalid value or expression for sell price field: " + e);
return false;
}
if (temp == null || isNaN(temp)) {
dialogBoxCreate("Invalid value or expression for sell price field");
return false;
}
product.sllman[city][0] = true; product.sllman[city][0] = true;
product.sllman[city][1] = -1; product.sllman[city][1] = qty; //Use sanitized input
} else if (isNaN(inputQty.value)) { } else if (isNaN(inputQty.value)) {
dialogBoxCreate("Invalid value for sell quantity field! Must be numeric"); dialogBoxCreate("Invalid value for sell quantity field! Must be numeric");
return false; return false;
@ -2970,13 +3118,13 @@ Corporation.prototype.process = function() {
Corporation.prototype.determineValuation = function() { Corporation.prototype.determineValuation = function() {
var val, profit = (this.revenue.minus(this.expenses)).toNumber(); var val, profit = (this.revenue.minus(this.expenses)).toNumber();
if (this.public) { if (this.public) {
val = this.funds.toNumber() + (profit * 90e3); val = this.funds.toNumber() + (profit * 85e3);
val *= (Math.pow(1.1, this.divisions.length)); val *= (Math.pow(1.1, this.divisions.length));
val = Math.max(val, 0); val = Math.max(val, 0);
} else { } else {
val = 10e9 + Math.max(this.funds.toNumber(), 0) / 3; //Base valuation val = 10e9 + Math.max(this.funds.toNumber(), 0) / 3; //Base valuation
if (profit > 0) { if (profit > 0) {
val += (profit * 350e3); val += (profit * 320e3);
val *= (Math.pow(1.1, this.divisions.length)); val *= (Math.pow(1.1, this.divisions.length));
} else { } else {
val = 10e9 * Math.pow(1.1, this.divisions.length); val = 10e9 * Math.pow(1.1, this.divisions.length);
@ -3206,7 +3354,6 @@ var companyManagementDiv, companyManagementHeaderTabs, companyManagementPanel,
//Industry Warehouse Panel //Industry Warehouse Panel
industryWarehousePanel, industrySmartSupplyCheckbox, industryWarehouseStorageText, industryWarehousePanel, industrySmartSupplyCheckbox, industryWarehouseStorageText,
industryWarehouseStorageBreakdownText,
industryWarehouseUpgradeSizeButton, industryWarehouseStateText, industryWarehouseUpgradeSizeButton, industryWarehouseStateText,
industryWarehouseMaterials, industryWarehouseProducts, industryWarehouseMaterials, industryWarehouseProducts,
headerTabs, cityTabs; headerTabs, cityTabs;
@ -3667,7 +3814,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
stockSharesInput = createElement("input", { stockSharesInput = createElement("input", {
type:"number", placeholder:"Stock Shares", margin: "5px", type:"number", placeholder:"Stock Shares", margin: "5px",
inputListener:()=>{ inputListener:()=>{
var money = moneyInput.value == null || moneyInput.value == "" ? 0 : moneyInput.value; var money = moneyInput.value == null || moneyInput.value == "" ? 0 : parseFloat(moneyInput.value);
var stockPrice = this.sharePrice; var stockPrice = this.sharePrice;
var stockShares = stockSharesInput.value == null || stockSharesInput.value == "" ? 0 : Math.round(stockSharesInput.value); var stockShares = stockSharesInput.value == null || stockSharesInput.value == "" ? 0 : Math.round(stockSharesInput.value);
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) { if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
@ -3702,7 +3849,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
dialogBoxCreate("ERROR: Invalid value(s) entered"); dialogBoxCreate("ERROR: Invalid value(s) entered");
} else if (this.funds.lt(money)) { } else if (this.funds.lt(money)) {
dialogBoxCreate("ERROR: You do not have this much money to bribe with"); dialogBoxCreate("ERROR: You do not have this much money to bribe with");
} else if (this.stockShares > this.numShares) { } else if (stockShares > this.numShares) {
dialogBoxCreate("ERROR: You do not have this many shares to bribe with"); dialogBoxCreate("ERROR: You do not have this many shares to bribe with");
} else { } else {
var totalAmount = money + (stockShares * stockPrice); var totalAmount = money + (stockShares * stockPrice);

@ -1,5 +1,5 @@
let CONSTANTS = { let CONSTANTS = {
Version: "0.35.1", Version: "0.35.2",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience //Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then //and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -1138,28 +1138,25 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>", "World Stock Exchange account and TIX API Access<br>",
LatestUpdate: LatestUpdate:
"v0.35.1<br>" + "v0.35.2<br>" +
"* You can now easily download all of your scripts/text files as zip folders. Use the 'help download' Terminal command for details<br>" + "* Corporation Changes: <br>" +
"* Scripts are now downloaded with the .script.js extension at the end of their filename<br>" + "*** Fixed an issue with Warehouse upgrade cost. Should now be significantly cheaper than before.<br>" +
"* Corporation Management Changes:<br>" + "*** Scientific Research now has a slightly more significant effect on Product quality<br>" +
"*** Implemented Smart Supply unlock<br>" + "*** The Energy and Water Utilities industries are now slightly more profitable<br>" +
"*** Changed the way a division's Production Multiplier is calculated. It is now the sum of the individual Production Multiplier " + "*** The Robotics and Computer Hardware industries are now less profitable<br>" +
"for every city. Therefore, it is now beneficial to open offices in different cities<br." + "*** The Software industry is slightly less profitable<br>" +
"*** The breakdown of what is taking up Warehouse space is now listed as a tooltip<br>" + "*** When selling Materials and Products, the 'PROD' qualifier can now be used " +
"*** Several small UI/UX improvements<br>" + "to set dynamic sell amounts based on your production<br>" +
"*** Numerous balance changes. The significant ones are listed below.<br>" + "*** Exporting MAX should now work properly<br>" +
"*** Product descriptions will now display their estimated market price<br>" + "*** You can no longer export past storage limits<br>" +
"*** The sale price of Products can no longer be marked up as high as before<br>" + "*** Scientific Research production reduced<br>" +
"*** Scientific Research now affects the rating of Products<br>" + "*** Effects of AdVert. Inc upgrade were reduced, but the effect that popularity and " +
"*** In general, the maximum amount of product you are able to sell is reduced<br>" + "awareness have on sales was increased to compensate (popularity/awareness numbers were getting " +
"*** Sale bonus from advertising (popularity/awareness) now has diminishing returns rather than scaling linearly<br>" + "too big with Advert. Inc)<br>" +
"* Experience gained during Infiltration now scales linearly based on the clearance level you reach. Compared to before, " + "*** Bug Fix: Products from Computer Hardware division should now properly have ratings<br>" +
"the experience gained will be much less at lower clearance levels, but much more at higher clearance levels<br>" + "* Improved Augmentation UI/UX. Now contains collapsible headers and sort buttons<br>" +
"* The editor can now be used to edit both scripts and text files<br>" + "* Improved Faction Augmentations display UI/UX. Now contains sort buttons. There is also an option " +
"* New Terminal config file that can be edited using the command 'nano .fconf'. Right now there is only one option, but there " + "to disable confirmation when purchasing Augmentations<br>"
"will be more in the future.<br>" +
"* You can now enable Bash-style Terminal hotkeys using the .fconf file referenced above<br>" +
"* Bug Fix: Fixed an issue with the UI elements of Gang Management persisting across different instances of BitNode-2"
} }
export {CONSTANTS}; export {CONSTANTS};

@ -11,33 +11,15 @@ import {Settings} from "./Settings.js";
import {dialogBoxCreate} from "../utils/DialogBox.js"; import {dialogBoxCreate} from "../utils/DialogBox.js";
import {factionInvitationBoxCreate} from "../utils/FactionInvitationBox.js"; import {factionInvitationBoxCreate} from "../utils/FactionInvitationBox.js";
import {clearEventListeners} from "../utils/HelperFunctions.js"; import {clearEventListeners, createElement,
removeChildrenFromElement} from "../utils/HelperFunctions.js";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js"; Generic_fromJSON} from "../utils/JSONReviver.js";
import numeral from "../utils/numeral.min.js"; import numeral from "../utils/numeral.min.js";
import {formatNumber, isPositiveNumber} from "../utils/StringHelperFunctions.js"; import {formatNumber} from "../utils/StringHelperFunctions.js";
import {yesNoBoxCreate, yesNoBoxGetYesButton, import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox.js"; yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox.js";
//Netburner Faction class
function factionInit() {
$('#faction-donate-input').on('input', function() {
if (Engine.currentPage == Engine.Page.Faction) {
var val = document.getElementById("faction-donate-input").value;
if (isPositiveNumber(val)) {
var numMoneyDonate = Number(val);
document.getElementById("faction-donate-rep-gain").innerHTML =
"This donation will result in " + formatNumber(numMoneyDonate/1000000 * Player.faction_rep_mult, 3) + " reputation gain";
} else {
document.getElementById("faction-donate-rep-gain").innerHTML =
"This donation will result in 0 reputation gain";
}
}
});
}
document.addEventListener("DOMContentLoaded", factionInit, false);
function Faction(name="") { function Faction(name="") {
this.name = name; this.name = name;
this.augmentations = []; //Name of augmentation only this.augmentations = []; //Name of augmentation only
@ -398,6 +380,7 @@ function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) { if (Settings.SuppressFactionInvites) {
faction.alreadyInvited = true; faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name); Player.factionInvitations.push(faction.name);
Engine.loadFactionsContent();
} else { } else {
factionInvitationBoxCreate(faction); factionInvitationBoxCreate(faction);
} }
@ -442,92 +425,206 @@ function joinFaction(faction) {
//Displays the HTML content for a specific faction //Displays the HTML content for a specific faction
function displayFactionContent(factionName) { function displayFactionContent(factionName) {
var faction = Factions[factionName]; var faction = Factions[factionName];
document.getElementById("faction-name").innerHTML = factionName; if (faction == null) {
document.getElementById("faction-info").innerHTML = "<i>" + faction.info + "</i>"; throw new Error("Invalid factionName passed into displayFactionContent: " + factionName);
var repGain = faction.getFavorGain(); }
if (repGain.length != 2) {repGain = 0;} removeChildrenFromElement(Engine.Display.factionContent);
repGain = repGain[0]; var elements = [];
document.getElementById("faction-reputation").innerHTML = "Reputation: " + formatNumber(faction.playerReputation, 4) +
"<span class='tooltiptext'>You will earn " + //Header and faction info
formatNumber(repGain, 4) + elements.push(createElement("h1", {
" faction favor upon resetting after installing an Augmentation</span>"; innerText:factionName
document.getElementById("faction-favor").innerHTML = "Faction Favor: " + formatNumber(faction.favor, 4) + }));
"<span class='tooltiptext'>Faction favor increases the rate at which " + elements.push(createElement("pre", {
innerHTML:"<i>" + faction.info + "</i>"
}));
elements.push(createElement("p", {
innerText:"---------------",
}));
//Faction reputation and favor
var favorGain = faction.getFavorGain();
if (favorGain.length != 2) {favorGain = 0;}
favorGain = favorGain[0];
elements.push(createElement("p", {
innerText: "Reputation: " + formatNumber(faction.playerReputation, 4),
tooltip:"You will earn " + formatNumber(favorGain, 4) +
" faction favor upon resetting after installing an Augmentation"
}))
elements.push(createElement("p", {
innerText:"---------------",
}));
elements.push(createElement("p", {
innerText:"Faction Favor: " + formatNumber(faction.favor, 4),
tooltip:"Faction favor increases the rate at which " +
"you earn reputation for this faction by 1% per favor. Faction favor " + "you earn reputation for this faction by 1% per favor. Faction favor " +
"is gained whenever you reset after installing an Augmentation. The amount of " + "is gained whenever you reset after installing an Augmentation. The amount of " +
"favor you gain depends on how much reputation you have with the faction</span>"; "favor you gain depends on how much reputation you have with the faction"
}));
elements.push(createElement("p", {
innerText:"---------------",
}));
var hackMissionDiv = document.getElementById("faction-hack-mission-div"); //Faction Work Description Text
var hackDiv = document.getElementById("faction-hack-div"); elements.push(createElement("pre", {
var fieldWorkDiv = document.getElementById("faction-fieldwork-div"); id:"faction-work-description-text",
var securityWorkDiv = document.getElementById("faction-securitywork-div"); innerText:"Perform work/carry out assignments for your faction to help further its cause! By doing so " +
var donateDiv = document.getElementById("faction-donate-div"); "you will earn reputation for your faction. You will also gain reputation passively over time, " +
var gangDiv = document.getElementById("faction-gang-div"); "although at a very slow rate. Earning reputation will allow you to purchase Augmentations " +
"through this faction, which are powerful upgrades that enhance your abilities. Note that you cannot " +
"use your terminal or create scripts when you are performing a task!"
}));
elements.push(createElement("br"));
var newHackMissionButton = clearEventListeners("faction-hack-mission-button"); //Hacking Mission Option
var newHackButton = clearEventListeners("faction-hack-button"); var hackMissionDiv = createElement("div", {
var newFieldWorkButton = clearEventListeners("faction-fieldwork-button"); id:"faction-hack-mission-div", class:"faction-work-div",
var newSecurityWorkButton = clearEventListeners("faction-securitywork-button"); });
var newDonateWorkButton = clearEventListeners("faction-donate-button"); var hackMissionDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
newHackMissionButton.addEventListener("click", function() { hackMissionDiv.appendChild(hackMissionDivWrapper);
hackMissionDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Hacking Mission",
clickListener:()=>{
Engine.loadMissionContent(); Engine.loadMissionContent();
var mission = new HackingMission(faction.playerReputation, faction); var mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true setInMission(true, mission); //Sets inMission flag to true
mission.init(); mission.init();
return false; return false;
}); }
}));
hackMissionDivWrapper.appendChild(createElement("p", {
innerText:"Attempt a hacking mission for your faction. " +
"A mission is a mini game that, if won, earns you " +
"significant reputation with this faction. (Recommended hacking level: 200+)"
}));
elements.push(hackMissionDiv);
newHackButton.addEventListener("click", function() { //Hacking Contracts Option
var hackDiv = createElement("div", {
id:"faction-hack-div", class:"faction-work-div",
});
var hackDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
hackDiv.appendChild(hackDivWrapper);
hackDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Hacking Contracts",
clickListener:()=>{
Player.startFactionHackWork(faction); Player.startFactionHackWork(faction);
return false; return false;
}); }
}));
hackDivWrapper.appendChild(createElement("p", {
innerText:"Complete hacking contracts for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your hacking skill. " +
"You will gain hacking exp."
}));
elements.push(hackDiv);
newFieldWorkButton.addEventListener("click", function() { //Field Work Option
var fieldWorkDiv = createElement("div", {
id:"faction-fieldwork-div", class:"faction-work-div"
});
var fieldWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
fieldWorkDiv.appendChild(fieldWorkDivWrapper);
fieldWorkDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Field Work",
clickListener:()=>{
Player.startFactionFieldWork(faction); Player.startFactionFieldWork(faction);
return false; return false;
}); }
}));
fieldWorkDivWrapper.appendChild(createElement("p", {
innerText:"Carry out field missions for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on all of your stats. " +
"You will gain exp for all stats."
}));
elements.push(fieldWorkDiv);
newSecurityWorkButton.addEventListener("click", function() { //Security Work Option
var securityWorkDiv = createElement("div", {
id:"faction-securitywork-div", class:"faction-work-div"
});
var securityWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
securityWorkDiv.appendChild(securityWorkDivWrapper);
securityWorkDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Security Work",
clickListener:()=>{
Player.startFactionSecurityWork(faction); Player.startFactionSecurityWork(faction);
return false; return false;
});
newDonateWorkButton.addEventListener("click", function() {
var donateAmountVal = document.getElementById("faction-donate-input").value;
if (isPositiveNumber(donateAmountVal)) {
var numMoneyDonate = Number(donateAmountVal);
if (Player.money.lt(numMoneyDonate)) {
dialogBoxCreate("You cannot afford to donate this much money!");
return;
} }
Player.loseMoney(numMoneyDonate); }));
var repGain = numMoneyDonate / 1000000 * Player.faction_rep_mult; securityWorkDivWrapper.appendChild(createElement("p", {
innerText:"Serve in a security detail for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your combat stats. " +
"You will gain exp for all combat stats."
}));
elements.push(securityWorkDiv);
//Donate for reputation
var donateDiv = createElement("div", {
id:"faction-donate-div", class:"faction-work-div"
});
var donateDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
donateDiv.appendChild(donateDivWrapper);
var donateRepGain = createElement("p", {
innerText:"This donation will result in 0 reputation gain"
});
var donateAmountInput = createElement("input", {
placeholder:"Donation amount",
inputListener:()=>{
var amt = parseFloat(donateAmountInput.value);
if (isNaN(amt) || amt < 0) {
donateRepGain.innerText = "Invalid donate amount entered!";
} else {
var repGain = amt / 1e6 * Player.faction_rep_mult;
donateRepGain.innerText = "This donation will result in " +
formatNumber(repGain, 3) + " reputation gain";
}
},
});
donateDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Donate Money",
clickListener:()=>{
var amt = parseFloat(donateAmountInput.value);
if (isNaN(amt) || amt < 0) {
dialogBoxCreate("Invalid amount entered!");
} else if (Player.money.lt(amt)) {
dialogBoxCreate("You cannot afford to donate this much money!");
} else {
Player.loseMoney(amt);
var repGain = amt / 1e6 * Player.faction_rep_mult;
faction.playerReputation += repGain; faction.playerReputation += repGain;
dialogBoxCreate("You just donated $" + formatNumber(numMoneyDonate, 2) + " to " + dialogBoxCreate("You just donated " + numeral(amt).format("$0.000a") + " to " +
faction.name + " to gain " + formatNumber(repGain, 3) + " reputation"); faction.name + " to gain " + formatNumber(repGain, 3) + " reputation");
displayFactionContent(factionName); displayFactionContent(factionName);
} else {
dialogBoxCreate("Invalid amount entered!");
} }
return false; }
}); }));
donateDivWrapper.appendChild(donateAmountInput);
donateDivWrapper.appendChild(donateRepGain);
elements.push(donateDiv);
//Purchase Augmentations
var newPurchaseAugmentationsButton = clearEventListeners("faction-purchase-augmentations"); elements.push(createElement("pre", {
newPurchaseAugmentationsButton.addEventListener("click", function() { innerHTML: "<br>As your reputation with this faction rises, you will " +
"unlock Augmentations, which you can purchase to enhance " +
"your abilities.<br><br>"
}));
elements.push(createElement("a", {
class:"a-link-button", innerText:"Purchase Augmentations",
clickListener:()=>{
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.factionAugmentationsContent.style.display = "block"; Engine.Display.factionAugmentationsContent.style.display = "block";
var newBackButton = clearEventListeners("faction-augmentations-back-button");
newBackButton.addEventListener("click", function() {
Engine.loadFactionContent();
displayFactionContent(factionName);
return false;
});
displayFactionAugmentations(factionName); displayFactionAugmentations(factionName);
return false; return false;
}); }
}));
//Gang (BitNode-2)
if (Player.bitNodeN == 2 && (factionName == "Slum Snakes" || factionName == "Tetrads" || if (Player.bitNodeN == 2 && (factionName == "Slum Snakes" || factionName == "Tetrads" ||
factionName == "The Syndicate" || factionName == "The Dark Army" || factionName == "Speakers for the Dead" || factionName == "The Syndicate" || factionName == "The Dark Army" || factionName == "Speakers for the Dead" ||
factionName == "NiteSec" || factionName == "The Black Hand")) { factionName == "NiteSec" || factionName == "The Black Hand")) {
@ -538,45 +635,15 @@ function displayFactionContent(factionName) {
securityWorkDiv.style.display = "none"; securityWorkDiv.style.display = "none";
donateDiv.style.display = "none"; donateDiv.style.display = "none";
var gangDiv = document.getElementById("faction-gang-div"); //Create the 'Manage Gang' button
var gangDiv = createElement("div", {
if (Player.inGang() && Player.gang.facName != factionName) { id:"faction-gang-div", class:"faction-work-div", display:"inline"
//If the player has a gang but its not for this faction });
if (gangDiv) { var gangDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
gangDiv.style.display = "none"; gangDiv.appendChild(gangDivWrapper);
} gangDivWrapper.appendChild(createElement("a", {
return; class:"a-link-button", innerText:"Manage Gang",
} clickListener:()=>{
//Create the "manage gang" button if it doesn't exist
if (gangDiv == null) {
gangDiv = document.createElement("div");
gangDiv.setAttribute("id", "faction-gang-div");
gangDiv.setAttribute("class", "faction-work-div");
gangDiv.style.display = "inline";
gangDiv.innerHTML =
'<div id="faction-gang-div-wrapper" class="faction-work-div-wrapper">' +
'<a id="faction-gang-button" class="a-link-button">Manage Gang</a>' +
'<p id="faction-gang-text">' +
'Create and manage a gang for this Faction. ' +
'Gangs will earn you money and faction reputation.' +
'</p>' +
'</div>' +
'<div class="faction-clear"></div>';
var descText = document.getElementById("faction-work-description-text");
if (descText) {
descText.parentNode.insertBefore(gangDiv, descText.nextSibling);
} else {
console.log("ERROR: faciton-work-description-text not found");
dialogBoxCreate("Error loading this page. This is a bug please report to game developer");
return;
}
}
gangDiv.style.display = "inline";
var gangButton = clearEventListeners("faction-gang-button");
gangButton.addEventListener("click", function() {
if (!Player.inGang()) { if (!Player.inGang()) {
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton(); var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Create Gang"; yesBtn.innerHTML = "Create Gang";
@ -598,12 +665,24 @@ function displayFactionContent(factionName) {
} else { } else {
Engine.loadGangContent(); Engine.loadGangContent();
} }
}
}));
gangDivWrapper.appendChild(createElement("p", {
innerText:"Create and manage a gang for this Faction. " +
"Gangs will earn you money and faction reputation."
}));
//Manage Gang button goes before Faction work stuff
elements.splice(7, 1, gangDiv);
}); if (Player.inGang() && Player.gang.facName != factionName) {
//If the player has a gang but its not for this faction
gangDiv.style.display = "none";
}
//Display all elements
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionContent.appendChild(elements[i]);
}
return; return;
} else {
if (gangDiv) {gangDiv.style.display = "none";}
} }
if (faction.isMember) { if (faction.isMember) {
@ -805,28 +884,138 @@ function displayFactionContent(factionName) {
break; break;
} }
} else { } else {
console.log("Not a member of this faction, cannot display faction information"); throw new Error("Not a member of this faction, cannot display faction information");
}
//Display all elements
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionContent.appendChild(elements[i]);
} }
} }
var confirmingPurchases = true;
var sortOption = null;
function displayFactionAugmentations(factionName) { function displayFactionAugmentations(factionName) {
document.getElementById("faction-augmentations-page-desc").innerHTML =
"Lists all Augmentations that are available to purchase from " + factionName + "<br><br>" +
"Augmentations are powerful upgrades that will enhance your abilities.";
var faction = Factions[factionName]; var faction = Factions[factionName];
if (faction == null) {
var augmentationsList = document.getElementById("faction-augmentations-list"); throw new Error("Could not find faction " + factionName + " in displayFactionAugmentations");
while (augmentationsList.firstChild) {
augmentationsList.removeChild(augmentationsList.firstChild);
} }
for (var i = 0; i < faction.augmentations.length; ++i) { removeChildrenFromElement(Engine.Display.factionAugmentationsContent);
var elements = [];
//Back button
elements.push(createElement("a", {
innerText:"Back", class:"a-link-button",
clickListener:()=>{
Engine.loadFactionContent();
displayFactionContent(factionName);
return false;
}
}));
//Header text
elements.push(createElement("h1", {innerText:"Faction Augmentations"}));
elements.push(createElement("p", {
id:"faction-augmentations-page-desc",
innerHTML:"Lists all Augmentations that are available to purchase from " + factionName + "<br><br>" +
"Augmentations are powerful upgrades that will enhance your abilities."
}));
//Confirming not confirming button
elements.push(createElement("label", {
for:"faction-augmentations-confirming-checkbox",innerText:"Confirm Purchases",
color:"white", margin:"4px", padding:"4px",
}));
var confirmingPurchasesCheckbox = createElement("input", {
type:"checkbox", id:"faction-augmentations-confirming-checkbox", checked:confirmingPurchases,
margin:"4px", padding:"4px",
clickListener:()=>{
confirmingPurchases = confirmingPurchasesCheckbox.checked;
}
});
elements.push(confirmingPurchasesCheckbox);
elements.push(createElement("br"));
elements.push(createElement("br"));
//Augmentations List
var augmentationsList = createElement("ul");
//Sort buttons
var sortByCostBtn = createElement("a", {
innerText:"Sort by Cost", class:"a-link-button",
clickListener:()=>{
sortOption = "cost";
var augs = faction.augmentations.slice();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseCost - aug2.baseCost;
});
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
}
});
var sortByRepBtn = createElement("a", {
innerText:"Sort by Reputation", class:"a-link-button",
clickListener:()=>{
sortOption = "reputation";
var augs = faction.augmentations.slice();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseRepRequirement - aug2.baseRepRequirement;
});
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
}
});
var defaultSortBtn = createElement("a", {
innerText:"Sort by Default Order", class:"a-link-button",
clickListener:()=>{
sortOption = "default";
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, faction.augmentations, faction);
}
});
elements.push(sortByCostBtn);
elements.push(sortByRepBtn);
elements.push(defaultSortBtn);
switch(sortOption) {
case "cost":
sortByCostBtn.click();
break;
case "reputation":
sortByRepBtn.click();
break;
default:
defaultSortBtn.click();
break;
}
elements.push(augmentationsList);
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionAugmentationsContent.appendChild(elements[i]);
}
}
//Takes in an array of Augmentation Names, constructs DOM elements
//to list them on the faction page, and appends them to the given
//DOM element
// @augmentationsList DOM List to append Aug DOM elements to
// @augs Array of Aug names
// @faction Faction for which to display Augmentations
function createFactionAugmentationDisplayElements(augmentationsList, augs, faction) {
for (var i = 0; i < augs.length; ++i) {
(function () { (function () {
var aug = Augmentations[faction.augmentations[i]]; var aug = Augmentations[augs[i]];
if (aug == null) { if (aug == null) {
console.log("ERROR: Invalid Augmentation"); throw new Error("Invalid Augmentation when trying to create Augmentation display Elements");
return;
} }
var owned = false; var owned = false;
for (var j = 0; j < Player.queuedAugmentations.length; ++j) { for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
@ -842,11 +1031,26 @@ function displayFactionAugmentations(factionName) {
} }
} }
var item = document.createElement("li"); var item = createElement("li");
var span = document.createElement("span"); var span = createElement("span", {display:"inline-block"});
var aDiv = document.createElement("div"); var aDiv = createElement("div", {tooltip:aug.info});
var aElem = document.createElement("a"); var aElem = createElement("a", {
var pElem = document.createElement("p"); innerText:aug.name, display:"inline",
clickListener:()=>{
if (confirmingPurchases) {
purchaseAugmentationBoxCreate(aug, faction);
} else {
purchaseAugmentation(aug, faction);
}
return false;
}
});
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
aElem.innerText += " - Level " + (getNextNeurofluxLevel());
}
var pElem = createElement("p", {
display:"inline",
})
var req = aug.baseRepRequirement * faction.augmentationRepRequirementMult; var req = aug.baseRepRequirement * faction.augmentationRepRequirementMult;
var hasPrereqs = hasAugmentationPrereqs(aug); var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) { if (!hasPrereqs) {
@ -858,35 +1062,16 @@ function displayFactionAugmentations(factionName) {
pElem.innerHTML = "ALREADY OWNED"; pElem.innerHTML = "ALREADY OWNED";
} else if (faction.playerReputation >= req) { } else if (faction.playerReputation >= req) {
aElem.setAttribute("class", "a-link-button"); aElem.setAttribute("class", "a-link-button");
//pElem.innerHTML = "UNLOCKED - $" + formatNumber(aug.baseCost * faction.augmentationPriceMult, 2);
pElem.innerHTML = "UNLOCKED - " + numeral(aug.baseCost * faction.augmentationPriceMult).format("$0.000a"); pElem.innerHTML = "UNLOCKED - " + numeral(aug.baseCost * faction.augmentationPriceMult).format("$0.000a");
} else { } else {
aElem.setAttribute("class", "a-link-button-inactive"); aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeral(aug.baseCost * faction.augmentationPriceMult).format("$0.000a"); pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeral(aug.baseCost * faction.augmentationPriceMult).format("$0.000a");
pElem.style.color = "red"; pElem.style.color = "red";
} }
aElem.style.display = "inline";
pElem.style.display = "inline";
aElem.innerHTML = aug.name;
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
aElem.innerHTML += " - Level " + (getNextNeurofluxLevel());
}
span.style.display = "inline-block"
//The div will have the tooltip.
aDiv.setAttribute("class", "tooltip");
aDiv.innerHTML = '<span class="tooltiptext">' + aug.info + " </span>";
aDiv.appendChild(aElem); aDiv.appendChild(aElem);
aElem.addEventListener("click", function() {
purchaseAugmentationBoxCreate(aug, faction);
});
span.appendChild(aDiv); span.appendChild(aDiv);
span.appendChild(pElem); span.appendChild(pElem);
item.appendChild(span); item.appendChild(span);
augmentationsList.appendChild(item); augmentationsList.appendChild(item);
}()); //Immediate invocation closure }()); //Immediate invocation closure
} }

@ -1,6 +1,7 @@
import {CONSTANTS} from "./Constants.js"; import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js"; import {Engine} from "./engine.js";
import {Faction, Factions} from "./Faction.js"; import {Faction, Factions,
displayFactionContent} from "./Faction.js";
import {Player} from "./Player.js"; import {Player} from "./Player.js";
import {dialogBoxCreate} from "../utils/DialogBox.js"; import {dialogBoxCreate} from "../utils/DialogBox.js";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
@ -949,6 +950,16 @@ function displayGangContent() {
wanted = Player.gang.wanted, wanted = Player.gang.wanted,
respect = Player.gang.respect; respect = Player.gang.respect;
//Back button
gangContainer.appendChild(createElement("a", {
class:"a-link-button", display:"inline-block", innerText:"Back",
clickListener:()=>{
Engine.loadFactionContent();
displayFactionContent(facName);
return false;
}
}));
//Buttons to switch between panels //Buttons to switch between panels
managementButton = createElement("a", { managementButton = createElement("a", {
id:"gang-management-subpage-button", class:"a-link-button-inactive", id:"gang-management-subpage-button", class:"a-link-button-inactive",

@ -535,7 +535,7 @@ function updateInfiltrationButtons(inst, scenario) {
"<span class='tooltiptext'>" + "<span class='tooltiptext'>" +
"Attempt to disable the security bots by hacking them. You have a " + "Attempt to disable the security bots by hacking them. You have a " +
formatNumber(hackChance*100, 2) + "% chance of success. " + formatNumber(hackChance*100, 2) + "% chance of success. " +
"If you succeed, the security level will increase by 1%. If you fail, " + "If you succeed, the security level will increase by 3%. If you fail, " +
"the security level will increase by 5%. </span>"; "the security level will increase by 5%. </span>";
document.getElementById("infiltration-sneak").innerHTML = "Sneak" + document.getElementById("infiltration-sneak").innerHTML = "Sneak" +
@ -717,7 +717,7 @@ function getInfiltrationDestroySecurityChance(inst) {
//Hack security //Hack security
//Success: 1%, Failure: 5% //Success: 3%, Failure: 5%
function attemptInfiltrationHack(inst) { function attemptInfiltrationHack(inst) {
var chance = getInfiltrationHackChance(inst); var chance = getInfiltrationHackChance(inst);
inst.gainHackingExp(inst.securityLevel / 40) * Player.hacking_exp_mult; inst.gainHackingExp(inst.securityLevel / 40) * Player.hacking_exp_mult;

@ -88,6 +88,46 @@ function initLiterature() {
"-Corporations do not reset when installing Augmentations, but they do reset when destroying a BitNode"; "-Corporations do not reset when installing Augmentations, but they do reset when destroying a BitNode";
Literatures[fn] = new Literature(title, fn, txt); Literatures[fn] = new Literature(title, fn, txt);
title = "A Brief History of Synthoids";
fn = "history-of-synthoids.lit";
txt = "Synthetic androids, or Synthoids for short, are genetically engineered robots and, short of Augmentations, " +
"are composed entirely of organic substances. For this reason, Synthoids are virtually identical to " +
"humans in form, composition, and appearance.<br><br>" +
"Synthoids were first designed and manufactured by OmniTek Incorporated sometime around the turn of the century. " +
"Their original purpose was to be used for manual labor and as emergency responders for disasters. As such, they " +
"were initially programmed only for their specific tasks. Each iteration that followed improved upon the " +
"intelligence and capabilities of the Synthoids. By the 6th iteration, called MK-VI, the Synthoids were " +
"so smart and capable enough of making their own decisions that many argued OmniTek had created the first " +
"sentient AI. These MK-VI Synthoids were produced in mass quantities (estimates up to 50 billion) with the hopes of increasing society's " +
"productivity and bolstering the global economy. Stemming from humanity's desire for technological advancement, optimism " +
"and excitement about the future had never been higher.<br><br>" +
"All of that excitement and optimism quickly turned to fear, panic, and dread in 2070, when a terrorist group " +
"called Ascendis Totalis hacked into OmniTek and uploaded a rogue AI into severeal of their Synthoid manufacturing facilities. " +
"This hack went undetected and for months OmniTek unknowingly churned out legions of Synthoids embedded with this " +
"rogue AI. Then, on December 24th, 2070, Omnica activated dormant protocols in the rogue AI, causing all of the " +
"infected Synthoids to immediately launch a military campaign to seek and destroy all of humanity.<br><br>" +
"What ensued was the deadlist conflict in human history. This crisis, now commonly known as the Synthoid Uprising, " +
"resulted in almost ten billion deaths over the course of a year. Despite the nations of the world banding together " +
"to combat the threat, the MK-VI Synthoids were simply stronger, faster, more intelligent, and more adaptable than humans, " +
"outsmarting them at every turn.<br><br>" +
"It wasn't until the sacrifice of an elite international military taskforce, called the Bladeburners, that humanity " +
"was finally able to defeat the Synthoids. The Bladeburners' final act was a suicide bombing mission that " +
"destroyed a large portion of the MK-VI Synthoids, including many of its leaders. In the following " +
"weeks militaries from around the world were able to round up and shut down the remaining rogue MK-VI Synthoids, ending " +
"the Synthoid Uprising.<br><br>" +
"In the aftermath of the bloodshed, the Synthoid Accords were drawn up. These Accords banned OmniTek Incorporated " +
"from manufacturing any Synthoids beyond the MK-III series. They also banned any other corporation " +
"from constructing androids with advanced, near-sentient AI. MK-VI Synthoids that did not have the rogue Ascendis Totalis " +
"AI were allowed to continue their existence, but they were stripped of all rights and protections as they " +
"were not considered humans. They were also banned from doing anything that may pose a global security threat, such " +
"as working for any military/defense organization or conducting any bioengineering, computing, or robotics related research.<br><br>" +
"Unfortunately, many believe that not all of the rogue MK-VI Synthoids from the Uprising were found and destroyed, " +
"and that many of them are blending in as normal humans in society today. In response, many nations have created " +
"Bladeburner divisions, special military branches that are tasked with investigating and dealing with any Synthoid threads.<br><br>" +
"To this day, tensions still exist between the remaining Synthoids and humans as a result of the Uprising.<br><br>" +
"Nobody knows what happened to the terrorist group Ascendis Totalis.";
Literatures[fn] = new Literature(title, fn, txt);
title = "A Green Tomorrow"; title = "A Green Tomorrow";
fn = "A-Green-Tomorrow.lit"; fn = "A-Green-Tomorrow.lit";
txt = "Starting a few decades ago, there was a massive global movement towards the generation of renewable energy in an effort to " + txt = "Starting a few decades ago, there was a massive global movement towards the generation of renewable energy in an effort to " +
@ -385,6 +425,7 @@ function initLiterature() {
fn = "the-secret-war.lit"; fn = "the-secret-war.lit";
txt = "" txt = ""
Literatures[fn] = new Literature(title, fn, txt); Literatures[fn] = new Literature(title, fn, txt);
} }
export {Literatures, initLiterature, showLiterature}; export {Literatures, initLiterature, showLiterature};

@ -34,7 +34,7 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoTxtInpBoxClose} from "../utils/YesNoBox.js"; yesNoTxtInpBoxClose} from "../utils/YesNoBox.js";
/* Display Location Content when visiting somewhere in the World*/ /* Display Location Content when visiting somewhere in the World*/
let Locations = { var Locations = {
//Cities //Cities
Aevum: "Aevum", Aevum: "Aevum",
//AevumDesc: "" //AevumDesc: ""
@ -1576,14 +1576,6 @@ function initLocationButtons() {
return false; return false;
}); });
let worldStockExchange = document.getElementById("generic-location-wse");
worldStockExchange.addEventListener("click", function() {
Player.location = Locations.WorldStockExchange;
Engine.loadStockMarketContent();
return false;
});
//Buttons to interact at a location (apply for job/promotion, train, purchase, etc.) //Buttons to interact at a location (apply for job/promotion, train, purchase, etc.)
var softwareJob = document.getElementById("location-software-job"); var softwareJob = document.getElementById("location-software-job");
var softwareConsultantJob = document.getElementById("location-software-consultant-job") var softwareConsultantJob = document.getElementById("location-software-consultant-job")

@ -11,7 +11,8 @@ import {loxBoxCreate, logBoxUpdateText,
import {setActiveScriptsClickHandlers, import {setActiveScriptsClickHandlers,
updateActiveScriptsItems} from "./ActiveScriptsUI.js"; updateActiveScriptsItems} from "./ActiveScriptsUI.js";
import {Augmentations, installAugmentations, import {Augmentations, installAugmentations,
initAugmentations, AugmentationNames} from "./Augmentations.js"; initAugmentations, AugmentationNames,
displayAugmentationsContent} from "./Augmentations.js";
import {BitNodes, initBitNodes, import {BitNodes, initBitNodes,
initBitNodeMultipliers} from "./BitNode.js"; initBitNodeMultipliers} from "./BitNode.js";
import {CompanyPositions, initCompanies} from "./Company.js"; import {CompanyPositions, initCompanies} from "./Company.js";
@ -300,7 +301,7 @@ let Engine = {
loadAugmentationsContent: function() { loadAugmentationsContent: function() {
Engine.hideAllContent(); Engine.hideAllContent();
Engine.Display.augmentationsContent.style.display = "block"; Engine.Display.augmentationsContent.style.display = "block";
Engine.displayAugmentationsContent(); displayAugmentationsContent();
Engine.currentPage = Engine.Page.Augmentations; Engine.currentPage = Engine.Page.Augmentations;
document.getElementById("augmentations-menu-link").classList.add("active"); document.getElementById("augmentations-menu-link").classList.add("active");
}, },
@ -628,10 +629,25 @@ let Engine = {
break; break;
} }
//Generic Locations (common to every city):
// World Stock Exchange
// Corporation (if applicable)
var genericLocationsList = document.getElementById("generic-locations-list"); var genericLocationsList = document.getElementById("generic-locations-list");
genericLocationsList.style.display = "inline"; genericLocationsList.style.display = "inline";
removeChildrenFromElement(genericLocationsList);
var li = createElement("li");
li.appendChild(createElement("a", {
innerText:"World Stock Exchange", class:"a-link-button",
clickListener:()=>{
Player.location = Locations.WorldStockExchange;
Engine.loadStockMarketContent();
return false;
}
}));
genericLocationsList.appendChild(li);
if (Player.corporation instanceof Corporation && document.getElementById("location-corporation-button") == null) { if (Player.corporation instanceof Corporation && document.getElementById("location-corporation-button") == null) {
var li = createElement("li", {}); var li = createElement("li");
li.appendChild(createElement("a", { li.appendChild(createElement("a", {
innerText:Player.corporation.name, id:"location-corporation-button", innerText:Player.corporation.name, id:"location-corporation-button",
class:"a-link-button", class:"a-link-button",
@ -645,58 +661,63 @@ let Engine = {
}, },
displayFactionsInfo: function() { displayFactionsInfo: function() {
//Clear the list of joined factions removeChildrenFromElement(Engine.Display.factionsContent);
var factionsList = document.getElementById("factions-list");
while (factionsList.firstChild) {
factionsList.removeChild(factionsList.firstChild);
}
//Re-add a link for each faction you are a member of //Factions
Engine.Display.factionsContent.appendChild(createElement("h1", {
innerText:"Factions"
}));
Engine.Display.factionsContent.appendChild(createElement("p", {
innerText:"Lists all factions you have joined"
}));
var factionsList = createElement("ul");
Engine.Display.factionsContent.appendChild(createElement("br"));
//Add a button for each faction you are a member of
for (var i = 0; i < Player.factions.length; ++i) { for (var i = 0; i < Player.factions.length; ++i) {
(function () { (function () {
var factionName = Player.factions[i]; var factionName = Player.factions[i];
//Add the faction to the Factions page content factionsList.appendChild(createElement("a", {
var item = document.createElement("li"); class:"a-link-button", innerText:factionName, padding:"4px", margin:"4px",
var aElem = document.createElement("a"); display:"inline-block",
aElem.setAttribute("class", "a-link-button"); clickListener:()=>{
aElem.innerHTML = factionName;
aElem.addEventListener("click", function() {
Engine.loadFactionContent(); Engine.loadFactionContent();
displayFactionContent(factionName); displayFactionContent(factionName);
return false; return false;
}); }
item.appendChild(aElem); }));
factionsList.appendChild(item); factionsList.appendChild(createElement("br"));
}()); //Immediate invocation }()); //Immediate invocation
} }
Engine.Display.factionsContent.appendChild(factionsList);
Engine.Display.factionsContent.appendChild(createElement("br"));
//Clear the list of invitations //Invited Factions
var invitationsList = document.getElementById("outstanding-faction-invitations-list"); Engine.Display.factionsContent.appendChild(createElement("h1", {
while (invitationsList.firstChild) { innerText:"Outstanding Faction Invitations"
invitationsList.removeChild(invitationsList.firstChild); }));
} Engine.Display.factionsContent.appendChild(createElement("p", {
width:"70%",
innerText:"Lists factions you have been invited to, as well as " +
"factions you have previously rejected. You can accept " +
"these faction invitations at any time."
}));
var invitationsList = createElement("ul");
//Add a link to accept for each faction you have invitiations for //Add a button to accept for each faction you have invitiations for
for (var i = 0; i < Player.factionInvitations.length; ++i) { for (var i = 0; i < Player.factionInvitations.length; ++i) {
(function () { (function () {
var factionName = Player.factionInvitations[i]; var factionName = Player.factionInvitations[i];
var item = document.createElement("li"); var item = createElement("li", {padding:"6px", margin:"6px"});
item.appendChild(createElement("p", {
var pElem = document.createElement("p"); innerText:factionName, display:"inline", margin:"4px", padding:"4px"
pElem.innerText = factionName; }));
pElem.style.display = "inline"; item.appendChild(createElement("a", {
pElem.style.margin = "4px"; innerText:"Accept Faction Invitation",
pElem.style.padding = "4px"; class:"a-link-button", display:"inline", margin:"4px", padding:"4px",
clickListener:()=>{
var aElem = document.createElement("a");
aElem.innerText = "Accept Faction Invitation";
aElem.setAttribute("class", "a-link-button");
aElem.style.display = "inline";
aElem.style.margin = "4px";
aElem.style.padding = "4px";
aElem.addEventListener("click", function() {
joinFaction(Factions[factionName]); joinFaction(Factions[factionName]);
for (var i = 0; i < Player.factionInvitations.length; ++i) { for (var i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == factionName) { if (Player.factionInvitations[i] == factionName) {
@ -706,128 +727,14 @@ let Engine = {
} }
Engine.displayFactionsInfo(); Engine.displayFactionsInfo();
return false; return false;
}); }
}));
item.appendChild(pElem);
item.appendChild(aElem);
item.style.margin = "6px";
item.style.padding = "6px";
invitationsList.appendChild(item); invitationsList.appendChild(item);
}()); }());
} }
},
displayAugmentationsContent: function() { Engine.Display.factionsContent.appendChild(invitationsList);
removeChildrenFromElement(Engine.Display.augmentationsContent);
Engine.Display.augmentationsContent.appendChild(createElement("h1", {
innerText:"Purchased Augmentations"
}));
Engine.Display.augmentationsContent.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\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)."
}));
//Purchased/queued augmentations
var queuedAugmentationsList = createElement("ul", {
id:"queued-augmentations-list"
});
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
var augName = Player.queuedAugmentations[i].name;
var aug = Augmentations[augName];
var item = createElement("li", {class:"installed-augmentation"});
var displayName = augName;
if (augName === AugmentationNames.NeuroFluxGovernor) {
displayName += " - Level " + (Player.queuedAugmentations[i].level);
}
item.appendChild(createElement("h2", {innerHTML:displayName}));
item.appendChild(createElement("p", {innerHTML:aug.info}));
queuedAugmentationsList.appendChild(item);
}
Engine.Display.augmentationsContent.appendChild(queuedAugmentationsList);
//Install Augmentations button
Engine.Display.augmentationsContent.appendChild(createElement("a", {
class:"a-link-button", innerText:"Install Augmentations",
tooltip:"'I never asked for this'",
clickListener:()=>{
installAugmentations();
return false;
}
}));
//Backup button
Engine.Display.augmentationsContent.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;
}
}));
//Installed augmentations list
Engine.Display.augmentationsContent.appendChild(createElement("h1", {
innerText:"Installed Augmentations"
}));
Engine.Display.augmentationsContent.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", {
id:"augmentations-list"
});
//Source Files - Temporary...Will probably put in a separate pane Later
for (var i = 0; i < Player.sourceFiles.length; ++i) {
var srcFileKey = "SourceFile" + Player.sourceFiles[i].n;
var sourceFileObject = SourceFiles[srcFileKey];
if (sourceFileObject == null) {
console.log("ERROR: Invalid source file number: " + Player.sourceFiles[i].n);
continue;
}
var item = createElement("li", {class:"installed-augmentation", });
item.appendChild(createElement("h2", {
innerHTML:sourceFileObject.name + "<br>" + "Level " + (Player.sourceFiles[i].lvl) + " / 3",
}));
item.appendChild(createElement("p", {
innerHTML:sourceFileObject.info,
}));
augmentationsList.appendChild(item);
}
for (var i = 0; i < Player.augmentations.length; ++i) {
var augName = Player.augmentations[i].name;
var aug = Augmentations[augName];
var item = createElement("li", {class:"installed-augmentation"});
var displayName = augName;
if (augName === AugmentationNames.NeuroFluxGovernor) {
displayName += " - Level " + (Player.augmentations[i].level);
}
item.appendChild(createElement("h2", {innerHTML:displayName}));
item.appendChild(createElement("p", {innerHTML:aug.info}));
augmentationsList.appendChild(item);
}
Engine.Display.augmentationsContent.appendChild(augmentationsList);
}, },
displayTutorialContent: function() { displayTutorialContent: function() {
@ -1367,61 +1274,47 @@ let Engine = {
Engine.currentPage = Engine.Page.Terminal; Engine.currentPage = Engine.Page.Terminal;
Engine.Display.characterContent = document.getElementById("character-container"); Engine.Display.characterContent = document.getElementById("character-container");
//Engine.Display.characterContent.style.visibility = "hidden";
Engine.Display.characterContent.style.display = "none"; Engine.Display.characterContent.style.display = "none";
Engine.Display.scriptEditorContent = document.getElementById("script-editor-container"); Engine.Display.scriptEditorContent = document.getElementById("script-editor-container");
//Engine.Display.scriptEditorContent.style.visibility = "hidden";
Engine.Display.scriptEditorContent.style.display = "none"; Engine.Display.scriptEditorContent.style.display = "none";
Engine.Display.activeScriptsContent = document.getElementById("active-scripts-container"); Engine.Display.activeScriptsContent = document.getElementById("active-scripts-container");
//Engine.Display.activeScriptsContent.style.visibility = "hidden";
Engine.Display.activeScriptsContent.style.display = "none"; Engine.Display.activeScriptsContent.style.display = "none";
Engine.Display.hacknetNodesContent = document.getElementById("hacknet-nodes-container"); Engine.Display.hacknetNodesContent = document.getElementById("hacknet-nodes-container");
//Engine.Display.hacknetNodesContent.style.visibility = "hidden";
Engine.Display.hacknetNodesContent.style.display = "none"; Engine.Display.hacknetNodesContent.style.display = "none";
Engine.Display.worldContent = document.getElementById("world-container"); Engine.Display.worldContent = document.getElementById("world-container");
//Engine.Display.worldContent.style.visibility = "hidden";
Engine.Display.worldContent.style.display = "none"; Engine.Display.worldContent.style.display = "none";
Engine.Display.createProgramContent = document.getElementById("create-program-container"); Engine.Display.createProgramContent = document.getElementById("create-program-container");
//Engine.Display.createProgramContent.style.visibility = "hidden";
Engine.Display.createProgramContent.style.display = "none"; Engine.Display.createProgramContent.style.display = "none";
Engine.Display.factionsContent = document.getElementById("factions-container"); Engine.Display.factionsContent = document.getElementById("factions-container");
//Engine.Display.factionsContent.style.visibility = "hidden";
Engine.Display.factionsContent.style.display = "none"; Engine.Display.factionsContent.style.display = "none";
Engine.Display.factionContent = document.getElementById("faction-container"); Engine.Display.factionContent = document.getElementById("faction-container");
//Engine.Display.factionContent.style.visibility = "hidden";
Engine.Display.factionContent.style.display = "none"; Engine.Display.factionContent.style.display = "none";
Engine.Display.factionAugmentationsContent = document.getElementById("faction-augmentations-container"); Engine.Display.factionAugmentationsContent = document.getElementById("faction-augmentations-container");
//Engine.Display.factionAugmentationsContent.style.visibility = "hidden";
Engine.Display.factionAugmentationsContent.style.display = "none"; Engine.Display.factionAugmentationsContent.style.display = "none";
Engine.Display.augmentationsContent = document.getElementById("augmentations-container"); Engine.Display.augmentationsContent = document.getElementById("augmentations-container");
//Engine.Display.augmentationsContent.style.visibility = "hidden";
Engine.Display.augmentationsContent.style.display = "none"; Engine.Display.augmentationsContent.style.display = "none";
Engine.Display.tutorialContent = document.getElementById("tutorial-container"); Engine.Display.tutorialContent = document.getElementById("tutorial-container");
//Engine.Display.tutorialContent.style.visibility = "hidden";
Engine.Display.tutorialContent.style.display = "none"; Engine.Display.tutorialContent.style.display = "none";
Engine.Display.infiltrationContent = document.getElementById("infiltration-container"); Engine.Display.infiltrationContent = document.getElementById("infiltration-container");
//Engine.Display.infiltrationContent.style.visibility = "hidden";
Engine.Display.infiltrationContent.style.display = "none"; Engine.Display.infiltrationContent.style.display = "none";
Engine.Display.stockMarketContent = document.getElementById("stock-market-container"); Engine.Display.stockMarketContent = document.getElementById("stock-market-container");
//Engine.Display.stockMarketContent.style.visibility = "hidden";
Engine.Display.stockMarketContent.style.display = "none"; Engine.Display.stockMarketContent.style.display = "none";
Engine.Display.missionContent = document.getElementById("mission-container"); Engine.Display.missionContent = document.getElementById("mission-container");
//Engine.Display.missionContent.style.visibility = "hidden";
Engine.Display.missionContent.style.display = "none"; Engine.Display.missionContent.style.display = "none";
//Character info //Character info

@ -1,5 +1,6 @@
//General helper functions //General helper functions
import {isString} from "./StringHelperFunctions.js"; import {isString} from "./StringHelperFunctions.js";
import {dialogBoxCreate} from "./DialogBox.js";
//Returns the size (number of keys) of an object //Returns the size (number of keys) of an object
function sizeOfObject(obj) { function sizeOfObject(obj) {
@ -207,7 +208,15 @@ function powerOfTwo(n) {
return n && (n & (n-1)) === 0; return n && (n & (n-1)) === 0;
} }
function exceptionAlert(e) {
dialogBoxCreate("Caught an exception: " + e + "<br><br>" +
"This is a bug, please report to game developer with this " +
"message as well as details about how to reproduce the bug.<br><br>" +
"If you want to be safe, I suggest refreshing the game WITHOUT saving so that your " +
"safe doesn't get corrupted");
}
export {sizeOfObject, addOffset, clearEventListeners, getRandomInt, export {sizeOfObject, addOffset, clearEventListeners, getRandomInt,
compareArrays, printArray, powerOfTwo, clearEventListenersEl, compareArrays, printArray, powerOfTwo, clearEventListenersEl,
removeElementById, removeElement, createElement, createAccordionElement, removeElementById, removeElement, createElement, createAccordionElement,
removeChildrenFromElement, createPopup, clearSelector}; removeChildrenFromElement, createPopup, clearSelector, exceptionAlert};

@ -70,11 +70,6 @@ function isString(str) {
return (typeof str === 'string' || str instanceof String); return (typeof str === 'string' || str instanceof String);
} }
//Returns true if string contains only digits (meaning it would be a positive number)
function isPositiveNumber(str) {
return /^\d+$/.test(str);
}
//Returns whether an array contains entirely of string objects //Returns whether an array contains entirely of string objects
function containsAllStrings(arr) { function containsAllStrings(arr) {
return arr.every(isString); return arr.every(isString);
@ -151,5 +146,5 @@ function generateRandomString(n) {
} }
export {getIndicesOf, convertTimeMsToTimeElapsedString, longestCommonStart, export {getIndicesOf, convertTimeMsToTimeElapsedString, longestCommonStart,
isString, isPositiveNumber, containsAllStrings, formatNumber, isString, containsAllStrings, formatNumber,
numOccurrences, numNetscriptOperators, isHTML, generateRandomString}; numOccurrences, numNetscriptOperators, isHTML, generateRandomString};