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

67172
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

@ -428,11 +428,7 @@
</li>
</ul>
<ul id="generic-locations-list">
<li id="generic-location-wse-li">
<a id="generic-location-wse" class="a-link-button">World Stock Exchange</a>
</li>
</ul>
<ul id="generic-locations-list"></ul>
</div>
<!-- Create a program(executable) -->
@ -447,144 +443,15 @@
</div>
<!-- Factions -->
<div id="factions-container" class="generic-menupage-container">
<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>
<div id="factions-container" class="generic-menupage-container"></div>
<!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container">
<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-container" class="generic-menupage-container"></div>
<div id="faction-hack-mission-div" class="faction-work-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>
<div id="faction-augmentations-container" class="generic-menupage-container"></div>
<!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container">
<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>
<div id="augmentations-container" class="generic-menupage-container"></div>
<!-- Tutorial content -->
<div id="tutorial-container" class="generic-menupage-container">

@ -1,12 +1,16 @@
import {BitNodeMultipliers} from "./BitNode.js";
import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js";
import {Factions, getNextNeurofluxLevel} from "./Faction.js";
import {addWorkerScript} from "./NetscriptWorker.js";
import {Player} from "./Player.js";
import {prestigeAugmentation} from "./Prestige.js";
import {Script, RunningScript} from "./Script.js";
import {Server} from "./Server.js";
import {SourceFiles} from "./SourceFile.js";
import {dialogBoxCreate} from "../utils/DialogBox.js";
import {createElement, createAccordionElement,
removeChildrenFromElement} from "../utils/HelperFunctions.js";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js";
import {isString} from "../utils/StringHelperFunctions.js";
@ -2083,5 +2087,172 @@ function giveAllAugmentations() {
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,
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 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={}) {
this.name = params.name ? params.name : "";
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.prd = 0; //How much of this material is being produced per second
this.exp = []; //Exports of this material to another warehouse/industry
this.totalExp = 0; //Total export amount for last cycle
this.imp = 0;
this.bCost = 0; //$ Cost/sec to buy material
this.sCost = 0; //$ Cost/sec to sell material
@ -323,7 +326,7 @@ Product.prototype.finishProduct = function(employeeProd, industry) {
console.log("designMult: " + designMult);
var balanceMult = (1.2 * engrRatio) + (0.9 * mgmtRatio) + (1.3 * rndRatio) +
(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;
this.qlt = totalMult * ((0.10 * employeeProd[EmployeePositions.Engineer]) +
@ -358,7 +361,7 @@ Product.prototype.finishProduct = function(employeeProd, industry) {
(0.05 * employeeProd[EmployeePositions.Business]));
this.calculateRating(industry);
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.cmp = getRandomInt(0, 70);
@ -516,6 +519,14 @@ var ProductRatingWeights = {
Aesthetics: 0.05,
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]: {
Quality: 0.1,
Performance: 0.2,
@ -553,11 +564,11 @@ var ProductRatingWeights = {
var IndustryUpgrades = {
"0": [0, 500e3, 1, 1.05,
"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 " +
"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 " +
"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."]
}
@ -645,28 +656,28 @@ Industry.prototype.init = function() {
this.sciFac = 0.7;
this.robFac = 0.05;
this.aiFac = 0.3;
this.advFac = 0.07;
this.advFac = 0.08;
this.reqMats = {
"Hardware": 0.1,
"Metal": 0.25,
"Metal": 0.2,
};
this.prodMats = ["Energy"];
break;
case Industries.Utilities:
case "Utilities":
this.reFac = 0.4;
this.reFac = 0.5;
this.sciFac = 0.6;
this.robFac = 0.3;
this.aiFac = 0.3;
this.advFac = 0.07;
this.robFac = 0.4;
this.aiFac = 0.4;
this.advFac = 0.08;
this.reqMats = {
"Hardware": 0.1,
"Metal": 0.2,
"Metal": 0.1,
}
this.prodMats = ["Water"];
break;
case Industries.Agriculture:
this.reFac = 0.8;
this.reFac = 0.75;
this.sciFac = 0.5;
this.hwFac = 0.2;
this.robFac = 0.3;
@ -684,7 +695,7 @@ Industry.prototype.init = function() {
this.hwFac = 0.35;
this.robFac = 0.5;
this.aiFac = 0.2;
this.advFac = 0.06;
this.advFac = 0.08;
this.reqMats = {
"Energy": 0.5,
}
@ -692,11 +703,11 @@ Industry.prototype.init = function() {
break;
case Industries.Mining:
this.reFac = 0.3;
this.sciFac = 0.25;
this.sciFac = 0.26;
this.hwFac = 0.4;
this.robFac = 0.5;
this.aiFac = 0.5;
this.advFac = 0.04;
this.robFac = 0.45;
this.aiFac = 0.45;
this.advFac = 0.06;
this.reqMats = {
"Energy": 0.8,
}
@ -736,7 +747,7 @@ Industry.prototype.init = function() {
this.hwFac = 0.2;
this.robFac = 0.25;
this.aiFac = 0.2;
this.advFac = 0.05;
this.advFac = 0.07;
this.reqMats = {
"Plants": 1,
"Energy": 0.5,
@ -750,7 +761,7 @@ Industry.prototype.init = function() {
this.hwFac = 0.15;
this.robFac = 0.25;
this.aiFac = 0.2;
this.advFac = 0.15;
this.advFac = 0.16;
this.reqMats = {
"Chemicals": 2,
"Energy": 1,
@ -762,9 +773,9 @@ Industry.prototype.init = function() {
case Industries.Computer:
case "Computer":
this.reFac = 0.2;
this.sciFac = 0.65;
this.robFac = 0.4;
this.aiFac = 0.2;
this.sciFac = 0.62;
this.robFac = 0.36;
this.aiFac = 0.19;
this.advFac = 0.17;
this.reqMats = {
"Metal": 2.5,
@ -774,11 +785,11 @@ Industry.prototype.init = function() {
this.makesProducts = true;
break;
case Industries.Robotics:
this.reFac = 0.35;
this.sciFac = 0.7;
this.aiFac = 0.4;
this.advFac = 0.2;
this.hwFac = 0.2;
this.reFac = 0.32;
this.sciFac = 0.65;
this.aiFac = 0.36;
this.advFac = 0.18;
this.hwFac = 0.19;
this.reqMats = {
"Hardware": 5,
"Energy": 3,
@ -787,7 +798,7 @@ Industry.prototype.init = function() {
this.makesProducts = true;
break;
case Industries.Software:
this.sciFac = 0.65;
this.sciFac = 0.62;
this.advFac = 0.16;
this.hwFac = 0.25;
this.reFac = 0.1;
@ -801,9 +812,9 @@ Industry.prototype.init = function() {
this.makesProducts = true;
break;
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.advFac = 0.1;
this.advFac = 0.11;
this.hwFac = 0.1;
this.robFac = 0.1;
this.aiFac = 0.1;
@ -904,8 +915,8 @@ Industry.prototype.updateWarehouseSizeUsed = function(warehouse) {
if (this.products.hasOwnProperty(prodName)) {
var prod = this.products[prodName];
warehouse.sizeUsed += (prod.data[warehouse.loc][0] * prod.siz);
if (prod.data[warehouse.loc][0] > 0 && warehouse.loc === currentCityUi) {
industryWarehouseStorageBreakdownText += (prodName + ": " + formatNumber(prod.data[warehouse.loc][0] * prod.siz, 0) + "<br>");
if (prod.data[warehouse.loc][0] > 0) {
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) {
var revenue = 0, expenses = 0, industry = this;
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) {
var city = Cities[i], office = this.offices[city];
@ -1103,6 +1132,8 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
prod = Math.min(maxAmt, prod);
}
if (prod < 0) {prod = 0;}
//Keep track of production for smart supply (/s)
warehouse.smartSupplyStore += (prod / (SecsPerMarketCycle * marketCycles));
@ -1202,16 +1233,32 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
company.getSalesMultiplier() * advertisingFactor;
var sellAmt;
if (mat.sllman[1] !== -1) {
//Sell amount is manually limited
sellAmt = Math.min(maxSell, mat.sllman[1]);
} else {
sellAmt = maxSell;
if (isString(mat.sllman[1])) {
//Dynamically evaluated
var tmp = mat.sllman[1].replace(/MAX/g, maxSell);
tmp = tmp.replace(/PROD/g, mat.prd);
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 = Math.min(mat.qty, sellAmt);
if (sellAmt < 0) {
console.log("ERROR: sellAmt is negative");
console.log("sellAmt calculated to be negative");
mat.sll = 0;
continue;
}
if (sellAmt && sCost >= 0) {
@ -1229,10 +1276,26 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
for (var matName in warehouse.materials) {
if (warehouse.materials.hasOwnProperty(matName)) {
var mat = warehouse.materials[matName];
mat.totalExp = 0; //Reset export
for (var expI = 0; expI < mat.exp.length; ++expI) {
var exp = mat.exp[expI];
var amt = exp.amt * SecsPerMarketCycle * marketCycles;
if (mat.qty <= amt) {
var amt = exp.amt.replace(/MAX/g, mat.qty / (SecsPerMarketCycle * marketCycles));
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;
}
if (amt === 0) {
@ -1246,12 +1309,26 @@ Industry.prototype.processMaterials = function(marketCycles=1, company) {
console.log("ERROR: Invalid export! " + expIndustry.name + " " + exp.city);
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].qlt = mat.qlt;
mat.qty -= amt;
mat.totalExp += amt;
expIndustry.updateWarehouseSizeUsed(expWarehouse);
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
//Scientific Research can be produced without a warehouse
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());
}
}
@ -1397,32 +1474,53 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
}
//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;
if (product.sCost > product.pCost) {
if ((product.sCost - product.pCost) > markupLimit) {
markup = markupLimit / (product.sCost - product.pCost);
if (sCost > product.pCost) {
if ((sCost - product.pCost) > markupLimit) {
markup = markupLimit / (sCost - product.pCost);
}
}
//var businessFactor = 1 + (office.employeeProd[EmployeePositions.Business] / office.employeeProd["total"]);
var businessFactor = this.getBusinessFactor(office); //Business employee productivity
var advertisingFactor = this.getAdvertisingFactors()[0]; //Awareness + popularity
var marketFactor = this.getMarketFactor(product); //Competition + demand
var maxSell = 0.5 * Math.pow(product.rat, 0.65) * marketFactor * corporation.getSalesMultiplier() *
Math.pow(markup, 2) * businessFactor * advertisingFactor;
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
sellAmt = Math.min(maxSell, product.sllman[city][1]);
} else {
//Backwards compatibility, -1 = 0
sellAmt = maxSell;
}
sellAmt = sellAmt * SecsPerMarketCycle * marketCycles;
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
totalProfit += (sellAmt * product.sCost);
totalProfit += (sellAmt * sCost);
product.data[city][2] = sellAmt / (SecsPerMarketCycle * marketCycles); //data[2] is sell property
} else {
product.data[city][2] = 0; //data[2] is sell property
@ -1470,10 +1568,10 @@ Industry.prototype.upgrade = function(upgrade, refs) {
break;
case 1: //AdVert.Inc,
var advMult = corporation.getAdvertisingMultiplier();
this.awareness += (4 * advMult);
this.awareness += (3 * advMult);
this.popularity += (1 * advMult);
this.awareness *= (1.01 * advMult);
this.popularity *= ((1 + getRandomInt(2, 4) / 100) * advMult);
this.popularity *= ((1 + getRandomInt(1, 3) / 100) * advMult);
break;
default:
console.log("ERROR: Un-implemented function index: " + upgN);
@ -1507,7 +1605,7 @@ Industry.prototype.getBusinessFactor = function(office) {
if (office.employeeProd["total"] > 0) {
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
@ -1987,6 +2085,7 @@ function Warehouse(params={}) {
this.level = 0;
this.sizeUsed = 0;
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.
//The production tracked by smart supply is always based on the previous cycle,
@ -2010,14 +2109,14 @@ function Warehouse(params={}) {
Warehouse.prototype.updateMaterialSizeUsed = function() {
this.sizeUsed = 0;
if (this.loc === currentCityUi) {industryWarehouseStorageBreakdownText = ""; }
this.breakdown = "";
for (var matName in this.materials) {
if (this.materials.hasOwnProperty(matName)) {
var mat = this.materials[matName];
if (MaterialSizes.hasOwnProperty(matName)) {
this.sizeUsed += (mat.qty * MaterialSizes[matName]);
if (mat.qty > 0 && this.loc === currentCityUi) {
industryWarehouseStorageBreakdownText += (matName + ": " + formatNumber(mat.qty * MaterialSizes[matName], 0) + "<br>");
if (mat.qty > 0) {
this.breakdown += (matName + ": " + formatNumber(mat.qty * MaterialSizes[matName], 0) + "<br>");
}
}
}
@ -2050,7 +2149,7 @@ Warehouse.prototype.createUI = function(parentRefs) {
industryWarehousePanel.appendChild(industryWarehouseStorageText);
//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", {
innerText:"Upgrade Warehouse Size - " + numeral(upgradeCost).format('$0.000a'),
display:"inline-block",
@ -2157,15 +2256,14 @@ Warehouse.prototype.updateUI = function(parentRefs) {
var storageText = "Storage: " +
(this.sizedUsed >= this.size ? formatNumber(this.sizeUsed, 3) : formatNumber(this.sizeUsed, 3)) +
"/" + formatNumber(this.size, 3);
if (industryWarehouseStorageBreakdownText != null &&
industryWarehouseStorageBreakdownText != "") {
if (this.breakdown != null && this.breakdown != "") {
storageText += ("<span class='tooltiptext'>" +
industryWarehouseStorageBreakdownText + "</span>");
this.breakdown + "</span>");
}
industryWarehouseStorageText.innerHTML = storageText;
//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)) {
industryWarehouseUpgradeSizeButton.className = "a-link-button-inactive";
} else {
@ -2209,8 +2307,8 @@ Warehouse.prototype.updateUI = function(parentRefs) {
}
//Products
if (industry.makesProducts && Object.keys(industry.products).length > 0) {
removeChildrenFromElement(industryWarehouseProducts);
if (industry.makesProducts && Object.keys(industry.products).length > 0) {
for (var productName in industry.products) {
if (industry.products.hasOwnProperty(productName) && industry.products[productName] instanceof Product) {
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",
});
var totalExport = 0;
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;
var totalGain = mat.buy + mat.prd + mat.imp - mat.sll - mat.totalExp;
//If Market Research upgrades are unlocked, add competition and demand info
var cmpAndDmdText = "";
@ -2249,7 +2343,7 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"(" + formatNumber(totalGain, 3) + "/s)" +
"<span class='tooltiptext'>Buy: " + formatNumber(mat.buy, 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>" +
"<p class='tooltip'>MP: $" + formatNumber(mat.bCost, 2) +
"<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
var exportAmount = createElement("input", {
type:"number", placeholder:"Export amount / s"
placeholder:"Export amount / s"
});
var exportBtn = createElement("a", {
@ -2388,28 +2482,24 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
clickListener:()=>{
var industryName = industrySelector.options[industrySelector.selectedIndex].text,
cityName = citySelector.options[citySelector.selectedIndex].text,
amt = parseFloat(exportAmount.value);
if (isNaN(amt)) {
amt = exportAmount.value;
//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");
return;
}
var exportObj = {ind:industryName, city:cityName, amt:amt};
var exportObj = {ind:industryName, city:cityName, amt:sanitizedAmt};
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);
return false;
}
@ -2436,17 +2526,6 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"City: " + mat.exp[i].city + "<br>" +
"Amount/s: " + mat.exp[i].amt,
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
removeElementById(popupId);
createExportPopup();
@ -2493,6 +2572,9 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
"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 " +
"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 " +
"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.",
@ -2519,11 +2601,16 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
innerText:"Confirm", class:"a-link-button", margin:"6px",
clickListener:()=>{
//Parse price
//Sanitize cost
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 = 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)) {
dialogBoxCreate("Invalid value or expression for sell price field");
return false;
@ -2536,9 +2623,25 @@ Warehouse.prototype.createMaterialUI = function(mat, matName, parentRefs) {
}
//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[1] = -1;
mat.sllman[1] = qty; //Use sanitized input
} else if (isNaN(inputQty.value)) {
dialogBoxCreate("Invalid value for sell quantity field! Must be numeric or 'MAX'");
return false;
@ -2611,10 +2714,8 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
"Aesthetics: " + formatNumber(product.aes, 3) + "<br>" +
"Features: " + formatNumber(product.fea, 3) +
cmpAndDmdText + "</span></p><br>" +
"<p class='tooltip'>Est. Production Cost: " + numeral(product.pCost).format("$0.000a") +
"<span class='tooltiptext'>An estimate of how much it costs to produce one unit of this product. " +
"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. Production Cost: " + numeral(product.pCost / ProductProductionCostRatio).format("$0.000a") +
"<span class='tooltiptext'>An estimate of the material cost it takes to create this Product.</span></p><br>" +
"<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. " +
"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)" :
"Sell (" + formatNumber(product.data[city][2], 3) + "/" + formatNumber(product.sllman[city][1], 3) + ")");
if (product.sCost) {
if (isString(product.sCost)) {
sellInnerTextString += (" @ " + product.sCost);
} else {
sellInnerTextString += (" @ " + numeral(product.sCost).format("$0.000a"));
}
}
div.appendChild(createElement("a", {
innerText:sellInnerTextString, class:"a-link-button", display:"inline-block",margin:"6px",
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 " +
"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 " +
"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 inputQty = createElement("input", {
@ -2663,17 +2776,52 @@ Warehouse.prototype.createProductUI = function(product, parentRefs) {
class:"a-link-button", innerText:"Confirm",
clickListener:()=>{
//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);
if (isNaN(cost)) {
dialogBoxCreate("Invalid value for sell price field");
return false;
}
product.sCost = cost;
}
//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][1] = -1;
product.sllman[city][1] = qty; //Use sanitized input
} else if (isNaN(inputQty.value)) {
dialogBoxCreate("Invalid value for sell quantity field! Must be numeric");
return false;
@ -2970,13 +3118,13 @@ Corporation.prototype.process = function() {
Corporation.prototype.determineValuation = function() {
var val, profit = (this.revenue.minus(this.expenses)).toNumber();
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.max(val, 0);
} else {
val = 10e9 + Math.max(this.funds.toNumber(), 0) / 3; //Base valuation
if (profit > 0) {
val += (profit * 350e3);
val += (profit * 320e3);
val *= (Math.pow(1.1, this.divisions.length));
} else {
val = 10e9 * Math.pow(1.1, this.divisions.length);
@ -3206,7 +3354,6 @@ var companyManagementDiv, companyManagementHeaderTabs, companyManagementPanel,
//Industry Warehouse Panel
industryWarehousePanel, industrySmartSupplyCheckbox, industryWarehouseStorageText,
industryWarehouseStorageBreakdownText,
industryWarehouseUpgradeSizeButton, industryWarehouseStateText,
industryWarehouseMaterials, industryWarehouseProducts,
headerTabs, cityTabs;
@ -3667,7 +3814,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
stockSharesInput = createElement("input", {
type:"number", placeholder:"Stock Shares", margin: "5px",
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 stockShares = stockSharesInput.value == null || stockSharesInput.value == "" ? 0 : Math.round(stockSharesInput.value);
if (isNaN(money) || isNaN(stockShares) || money < 0 || stockShares < 0) {
@ -3702,7 +3849,7 @@ Corporation.prototype.displayCorporationOverviewContent = function() {
dialogBoxCreate("ERROR: Invalid value(s) entered");
} else if (this.funds.lt(money)) {
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");
} else {
var totalAmount = money + (stockShares * stockPrice);

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

@ -11,33 +11,15 @@ import {Settings} from "./Settings.js";
import {dialogBoxCreate} from "../utils/DialogBox.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,
Generic_fromJSON} from "../utils/JSONReviver.js";
import numeral from "../utils/numeral.min.js";
import {formatNumber, isPositiveNumber} from "../utils/StringHelperFunctions.js";
import {formatNumber} from "../utils/StringHelperFunctions.js";
import {yesNoBoxCreate, yesNoBoxGetYesButton,
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="") {
this.name = name;
this.augmentations = []; //Name of augmentation only
@ -398,6 +380,7 @@ function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) {
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
Engine.loadFactionsContent();
} else {
factionInvitationBoxCreate(faction);
}
@ -442,92 +425,206 @@ function joinFaction(faction) {
//Displays the HTML content for a specific faction
function displayFactionContent(factionName) {
var faction = Factions[factionName];
document.getElementById("faction-name").innerHTML = factionName;
document.getElementById("faction-info").innerHTML = "<i>" + faction.info + "</i>";
var repGain = faction.getFavorGain();
if (repGain.length != 2) {repGain = 0;}
repGain = repGain[0];
document.getElementById("faction-reputation").innerHTML = "Reputation: " + formatNumber(faction.playerReputation, 4) +
"<span class='tooltiptext'>You will earn " +
formatNumber(repGain, 4) +
" faction favor upon resetting after installing an Augmentation</span>";
document.getElementById("faction-favor").innerHTML = "Faction Favor: " + formatNumber(faction.favor, 4) +
"<span class='tooltiptext'>Faction favor increases the rate at which " +
if (faction == null) {
throw new Error("Invalid factionName passed into displayFactionContent: " + factionName);
}
removeChildrenFromElement(Engine.Display.factionContent);
var elements = [];
//Header and faction info
elements.push(createElement("h1", {
innerText:factionName
}));
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 " +
"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");
var hackDiv = document.getElementById("faction-hack-div");
var fieldWorkDiv = document.getElementById("faction-fieldwork-div");
var securityWorkDiv = document.getElementById("faction-securitywork-div");
var donateDiv = document.getElementById("faction-donate-div");
var gangDiv = document.getElementById("faction-gang-div");
//Faction Work Description Text
elements.push(createElement("pre", {
id:"faction-work-description-text",
innerText:"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!"
}));
elements.push(createElement("br"));
var newHackMissionButton = clearEventListeners("faction-hack-mission-button");
var newHackButton = clearEventListeners("faction-hack-button");
var newFieldWorkButton = clearEventListeners("faction-fieldwork-button");
var newSecurityWorkButton = clearEventListeners("faction-securitywork-button");
var newDonateWorkButton = clearEventListeners("faction-donate-button");
newHackMissionButton.addEventListener("click", function() {
//Hacking Mission Option
var hackMissionDiv = createElement("div", {
id:"faction-hack-mission-div", class:"faction-work-div",
});
var hackMissionDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
hackMissionDiv.appendChild(hackMissionDivWrapper);
hackMissionDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Hacking Mission",
clickListener:()=>{
Engine.loadMissionContent();
var mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true
mission.init();
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);
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);
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);
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;
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");
displayFactionContent(factionName);
} else {
dialogBoxCreate("Invalid amount entered!");
}
return false;
});
}
}));
donateDivWrapper.appendChild(donateAmountInput);
donateDivWrapper.appendChild(donateRepGain);
elements.push(donateDiv);
var newPurchaseAugmentationsButton = clearEventListeners("faction-purchase-augmentations");
newPurchaseAugmentationsButton.addEventListener("click", function() {
//Purchase Augmentations
elements.push(createElement("pre", {
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.Display.factionAugmentationsContent.style.display = "block";
var newBackButton = clearEventListeners("faction-augmentations-back-button");
newBackButton.addEventListener("click", function() {
Engine.loadFactionContent();
displayFactionContent(factionName);
return false;
});
displayFactionAugmentations(factionName);
return false;
});
}
}));
//Gang (BitNode-2)
if (Player.bitNodeN == 2 && (factionName == "Slum Snakes" || factionName == "Tetrads" ||
factionName == "The Syndicate" || factionName == "The Dark Army" || factionName == "Speakers for the Dead" ||
factionName == "NiteSec" || factionName == "The Black Hand")) {
@ -538,45 +635,15 @@ function displayFactionContent(factionName) {
securityWorkDiv.style.display = "none";
donateDiv.style.display = "none";
var gangDiv = document.getElementById("faction-gang-div");
if (Player.inGang() && Player.gang.facName != factionName) {
//If the player has a gang but its not for this faction
if (gangDiv) {
gangDiv.style.display = "none";
}
return;
}
//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() {
//Create the 'Manage Gang' button
var gangDiv = createElement("div", {
id:"faction-gang-div", class:"faction-work-div", display:"inline"
});
var gangDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
gangDiv.appendChild(gangDivWrapper);
gangDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Manage Gang",
clickListener:()=>{
if (!Player.inGang()) {
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Create Gang";
@ -598,12 +665,24 @@ function displayFactionContent(factionName) {
} else {
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;
} else {
if (gangDiv) {gangDiv.style.display = "none";}
}
if (faction.isMember) {
@ -805,28 +884,138 @@ function displayFactionContent(factionName) {
break;
}
} 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) {
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 augmentationsList = document.getElementById("faction-augmentations-list");
while (augmentationsList.firstChild) {
augmentationsList.removeChild(augmentationsList.firstChild);
if (faction == null) {
throw new Error("Could not find faction " + factionName + " in displayFactionAugmentations");
}
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 () {
var aug = Augmentations[faction.augmentations[i]];
var aug = Augmentations[augs[i]];
if (aug == null) {
console.log("ERROR: Invalid Augmentation");
return;
throw new Error("Invalid Augmentation when trying to create Augmentation display Elements");
}
var owned = false;
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
@ -842,11 +1031,26 @@ function displayFactionAugmentations(factionName) {
}
}
var item = document.createElement("li");
var span = document.createElement("span");
var aDiv = document.createElement("div");
var aElem = document.createElement("a");
var pElem = document.createElement("p");
var item = createElement("li");
var span = createElement("span", {display:"inline-block"});
var aDiv = createElement("div", {tooltip:aug.info});
var aElem = createElement("a", {
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 hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
@ -858,35 +1062,16 @@ function displayFactionAugmentations(factionName) {
pElem.innerHTML = "ALREADY OWNED";
} else if (faction.playerReputation >= req) {
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");
} else {
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.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);
aElem.addEventListener("click", function() {
purchaseAugmentationBoxCreate(aug, faction);
});
span.appendChild(aDiv);
span.appendChild(pElem);
item.appendChild(span);
augmentationsList.appendChild(item);
}()); //Immediate invocation closure
}

@ -1,6 +1,7 @@
import {CONSTANTS} from "./Constants.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 {dialogBoxCreate} from "../utils/DialogBox.js";
import {Reviver, Generic_toJSON,
@ -949,6 +950,16 @@ function displayGangContent() {
wanted = Player.gang.wanted,
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
managementButton = createElement("a", {
id:"gang-management-subpage-button", class:"a-link-button-inactive",

@ -535,7 +535,7 @@ function updateInfiltrationButtons(inst, scenario) {
"<span class='tooltiptext'>" +
"Attempt to disable the security bots by hacking them. You have a " +
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>";
document.getElementById("infiltration-sneak").innerHTML = "Sneak" +
@ -717,7 +717,7 @@ function getInfiltrationDestroySecurityChance(inst) {
//Hack security
//Success: 1%, Failure: 5%
//Success: 3%, Failure: 5%
function attemptInfiltrationHack(inst) {
var chance = getInfiltrationHackChance(inst);
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";
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";
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 " +
@ -385,6 +425,7 @@ function initLiterature() {
fn = "the-secret-war.lit";
txt = ""
Literatures[fn] = new Literature(title, fn, txt);
}
export {Literatures, initLiterature, showLiterature};

@ -34,7 +34,7 @@ import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
yesNoTxtInpBoxClose} from "../utils/YesNoBox.js";
/* Display Location Content when visiting somewhere in the World*/
let Locations = {
var Locations = {
//Cities
Aevum: "Aevum",
//AevumDesc: ""
@ -1576,14 +1576,6 @@ function initLocationButtons() {
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.)
var softwareJob = document.getElementById("location-software-job");
var softwareConsultantJob = document.getElementById("location-software-consultant-job")

@ -11,7 +11,8 @@ import {loxBoxCreate, logBoxUpdateText,
import {setActiveScriptsClickHandlers,
updateActiveScriptsItems} from "./ActiveScriptsUI.js";
import {Augmentations, installAugmentations,
initAugmentations, AugmentationNames} from "./Augmentations.js";
initAugmentations, AugmentationNames,
displayAugmentationsContent} from "./Augmentations.js";
import {BitNodes, initBitNodes,
initBitNodeMultipliers} from "./BitNode.js";
import {CompanyPositions, initCompanies} from "./Company.js";
@ -300,7 +301,7 @@ let Engine = {
loadAugmentationsContent: function() {
Engine.hideAllContent();
Engine.Display.augmentationsContent.style.display = "block";
Engine.displayAugmentationsContent();
displayAugmentationsContent();
Engine.currentPage = Engine.Page.Augmentations;
document.getElementById("augmentations-menu-link").classList.add("active");
},
@ -628,10 +629,25 @@ let Engine = {
break;
}
//Generic Locations (common to every city):
// World Stock Exchange
// Corporation (if applicable)
var genericLocationsList = document.getElementById("generic-locations-list");
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) {
var li = createElement("li", {});
var li = createElement("li");
li.appendChild(createElement("a", {
innerText:Player.corporation.name, id:"location-corporation-button",
class:"a-link-button",
@ -645,58 +661,63 @@ let Engine = {
},
displayFactionsInfo: function() {
//Clear the list of joined factions
var factionsList = document.getElementById("factions-list");
while (factionsList.firstChild) {
factionsList.removeChild(factionsList.firstChild);
}
removeChildrenFromElement(Engine.Display.factionsContent);
//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) {
(function () {
var factionName = Player.factions[i];
//Add the faction to the Factions page content
var item = document.createElement("li");
var aElem = document.createElement("a");
aElem.setAttribute("class", "a-link-button");
aElem.innerHTML = factionName;
aElem.addEventListener("click", function() {
factionsList.appendChild(createElement("a", {
class:"a-link-button", innerText:factionName, padding:"4px", margin:"4px",
display:"inline-block",
clickListener:()=>{
Engine.loadFactionContent();
displayFactionContent(factionName);
return false;
});
item.appendChild(aElem);
factionsList.appendChild(item);
}
}));
factionsList.appendChild(createElement("br"));
}()); //Immediate invocation
}
Engine.Display.factionsContent.appendChild(factionsList);
Engine.Display.factionsContent.appendChild(createElement("br"));
//Clear the list of invitations
var invitationsList = document.getElementById("outstanding-faction-invitations-list");
while (invitationsList.firstChild) {
invitationsList.removeChild(invitationsList.firstChild);
}
//Invited Factions
Engine.Display.factionsContent.appendChild(createElement("h1", {
innerText:"Outstanding Faction Invitations"
}));
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) {
(function () {
var factionName = Player.factionInvitations[i];
var item = document.createElement("li");
var pElem = document.createElement("p");
pElem.innerText = factionName;
pElem.style.display = "inline";
pElem.style.margin = "4px";
pElem.style.padding = "4px";
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() {
var item = createElement("li", {padding:"6px", margin:"6px"});
item.appendChild(createElement("p", {
innerText:factionName, display:"inline", margin:"4px", padding:"4px"
}));
item.appendChild(createElement("a", {
innerText:"Accept Faction Invitation",
class:"a-link-button", display:"inline", margin:"4px", padding:"4px",
clickListener:()=>{
joinFaction(Factions[factionName]);
for (var i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == factionName) {
@ -706,128 +727,14 @@ let Engine = {
}
Engine.displayFactionsInfo();
return false;
});
}
}));
item.appendChild(pElem);
item.appendChild(aElem);
item.style.margin = "6px";
item.style.padding = "6px";
invitationsList.appendChild(item);
}());
}
},
displayAugmentationsContent: function() {
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);
Engine.Display.factionsContent.appendChild(invitationsList);
},
displayTutorialContent: function() {
@ -1367,61 +1274,47 @@ let Engine = {
Engine.currentPage = Engine.Page.Terminal;
Engine.Display.characterContent = document.getElementById("character-container");
//Engine.Display.characterContent.style.visibility = "hidden";
Engine.Display.characterContent.style.display = "none";
Engine.Display.scriptEditorContent = document.getElementById("script-editor-container");
//Engine.Display.scriptEditorContent.style.visibility = "hidden";
Engine.Display.scriptEditorContent.style.display = "none";
Engine.Display.activeScriptsContent = document.getElementById("active-scripts-container");
//Engine.Display.activeScriptsContent.style.visibility = "hidden";
Engine.Display.activeScriptsContent.style.display = "none";
Engine.Display.hacknetNodesContent = document.getElementById("hacknet-nodes-container");
//Engine.Display.hacknetNodesContent.style.visibility = "hidden";
Engine.Display.hacknetNodesContent.style.display = "none";
Engine.Display.worldContent = document.getElementById("world-container");
//Engine.Display.worldContent.style.visibility = "hidden";
Engine.Display.worldContent.style.display = "none";
Engine.Display.createProgramContent = document.getElementById("create-program-container");
//Engine.Display.createProgramContent.style.visibility = "hidden";
Engine.Display.createProgramContent.style.display = "none";
Engine.Display.factionsContent = document.getElementById("factions-container");
//Engine.Display.factionsContent.style.visibility = "hidden";
Engine.Display.factionsContent.style.display = "none";
Engine.Display.factionContent = document.getElementById("faction-container");
//Engine.Display.factionContent.style.visibility = "hidden";
Engine.Display.factionContent.style.display = "none";
Engine.Display.factionAugmentationsContent = document.getElementById("faction-augmentations-container");
//Engine.Display.factionAugmentationsContent.style.visibility = "hidden";
Engine.Display.factionAugmentationsContent.style.display = "none";
Engine.Display.augmentationsContent = document.getElementById("augmentations-container");
//Engine.Display.augmentationsContent.style.visibility = "hidden";
Engine.Display.augmentationsContent.style.display = "none";
Engine.Display.tutorialContent = document.getElementById("tutorial-container");
//Engine.Display.tutorialContent.style.visibility = "hidden";
Engine.Display.tutorialContent.style.display = "none";
Engine.Display.infiltrationContent = document.getElementById("infiltration-container");
//Engine.Display.infiltrationContent.style.visibility = "hidden";
Engine.Display.infiltrationContent.style.display = "none";
Engine.Display.stockMarketContent = document.getElementById("stock-market-container");
//Engine.Display.stockMarketContent.style.visibility = "hidden";
Engine.Display.stockMarketContent.style.display = "none";
Engine.Display.missionContent = document.getElementById("mission-container");
//Engine.Display.missionContent.style.visibility = "hidden";
Engine.Display.missionContent.style.display = "none";
//Character info

@ -1,5 +1,6 @@
//General helper functions
import {isString} from "./StringHelperFunctions.js";
import {dialogBoxCreate} from "./DialogBox.js";
//Returns the size (number of keys) of an object
function sizeOfObject(obj) {
@ -207,7 +208,15 @@ function powerOfTwo(n) {
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,
compareArrays, printArray, powerOfTwo, clearEventListenersEl,
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);
}
//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
function containsAllStrings(arr) {
return arr.every(isString);
@ -151,5 +146,5 @@ function generateRandomString(n) {
}
export {getIndicesOf, convertTimeMsToTimeElapsedString, longestCommonStart,
isString, isPositiveNumber, containsAllStrings, formatNumber,
isString, containsAllStrings, formatNumber,
numOccurrences, numNetscriptOperators, isHTML, generateRandomString};