Merge pull request #153 from danielyxie/dev

Dev v0.31.0
This commit is contained in:
danielyxie 2017-10-15 21:21:13 -05:00 committed by GitHub
commit f1d6171169
24 changed files with 3711 additions and 2178 deletions

@ -35,9 +35,6 @@
font-family: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman'; font-family: 'Lucida Console', 'Lucida Sans Unicode', 'Fira Mono', 'Consolas', 'Courier New', Courier, monospace, 'Times New Roman';
} }
#javascript-editor textarea {
}
.ace_line, .ace_line,
.ace_line * { .ace_line * {
background-color:transparent; background-color:transparent;
@ -629,8 +626,6 @@ div.faction-clear {
padding: 6px; padding: 6px;
} }
.gang-member-header { .gang-member-header {
background-color: #444; background-color: #444;
font-size: 20px; font-size: 20px;

@ -24,7 +24,6 @@
} }
.hack-mission-node { .hack-mission-node {
color:gray;
z-index:5; z-index:5;
background-color:gray; background-color:gray;
align-self: center; align-self: center;
@ -33,9 +32,9 @@
} }
.hack-mission-node p { .hack-mission-node p {
margin-top:8px;
color:white; color:white;
font-size:12px; font-size:12px;
margin-top: 8px;
text-align:center; text-align:center;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
@ -50,12 +49,11 @@
.hack-mission-player-node-active { .hack-mission-player-node-active {
border: 2px solid white; border: 2px solid white;
color: #6666ff;
background-color: #6666ff; background-color: #6666ff;
} }
.hack-mission-enemy-node { .hack-mission-enemy-node {
color:red; color:white;
background-color:red; background-color:red;
} }
@ -84,10 +82,39 @@
-webkit-transform: skew(-20deg); -webkit-transform: skew(-20deg);
-moz-transform: skew(-20deg); -moz-transform: skew(-20deg);
-o-transform: skew(-20deg); -o-transform: skew(-20deg);
color:white;
font-size:12px;
margin-top: 8px;
text-align:center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.hack-mission-transfer-node {
width: 100%;
height: 90%;
-webkit-transform: skew(-20deg);
-moz-transform: skew(-20deg);
-o-transform: skew(-20deg);
}
.hack-mission-transfer-node p {
-webkit-transform: skew(20deg);
-moz-transform: skew(20deg);
-o-transform: skew(20deg);
color:white;
font-size:12px;
margin-top: 8px;
text-align:center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
} }
.hack-mission-spam-node, .hack-mission-spam-node,
.hack-mission-transfer-node,
.hack-mission-shield-node { .hack-mission-shield-node {
height:100%; height:100%;
width: 100%; width: 100%;

@ -398,3 +398,59 @@ a:link, a:visited {
.scan-analyze-link:hover { .scan-analyze-link:hover {
text-decoration:none; text-decoration:none;
} }
/* Accordion menus (Header with collapsible panel) */
.accordion-header {
background-color: #444;
font-size: 20px;
color: white;
margin: 6px 6px 0px 6px;
padding: 6px;
cursor: pointer;
width: 80%;
text-align: left;
border: none;
outline: none;
}
.accordion-header.active,
.accordion-header:hover {
background-color: #555;
}
.accordion-header.active:hover {
background-color: #666;
}
.accordion-header:after {
content: '\02795'; /* "plus" sign (+) */
font-size: 13px;
color: white;
float: right;
margin-left: 5px;
}
.accordion-header.active:after {
content: "\2796"; /* "minus" sign (-) */
font-size: 13px;
color: white;
float: right;
margin-left: 5px;
}
.accordion-panel {
margin: 0px 6px 6px 6px;
padding: 0px 6px 6px 6px;
width: 75%;
margin-left: 5%;
display: none;
background-color: #555;
overflow:auto;
}
.accordion-panel div,
.accordion-panel ul,
.accordion-panel p,
.accordion-panel ul > li {
background-color: #555;
}

4705
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

@ -733,8 +733,9 @@
<a id="location-slums-mug" class="a-link-button tooltip"> Mug someone </a> <a id="location-slums-mug" class="a-link-button tooltip"> Mug someone </a>
<a id="location-slums-larceny" class="a-link-button tooltip"> Commit Larceny </a> <a id="location-slums-larceny" class="a-link-button tooltip"> Commit Larceny </a>
<a id="location-slums-deal-drugs" class="a-link-button tooltip"> Deal Drugs </a> <a id="location-slums-deal-drugs" class="a-link-button tooltip"> Deal Drugs </a>
<a id="location-slums-traffic-arms" class="a-link-button tooltip"> Traffick Illegal Arms </a> <a id="location-slums-bond-forgery" class="a-link-button tooltip">Bond Forgery</a>
<a id="location-slums-homicide" class="a-link-button tooltip"> Homicide </a> <a id="location-slums-traffic-arms" class="a-link-button tooltip">Traffick Illegal Arms</a>
<a id="location-slums-homicide" class="a-link-button tooltip">Homicide</a>
<a id="location-slums-gta" class="a-link-button tooltip"> Grand Theft Auto </a> <a id="location-slums-gta" class="a-link-button tooltip"> Grand Theft Auto </a>
<a id="location-slums-kidnap" class="a-link-button tooltip"> Kidnap and Ransom </a> <a id="location-slums-kidnap" class="a-link-button tooltip"> Kidnap and Ransom </a>
<a id="location-slums-assassinate" class="a-link-button tooltip"> Assassinate </a> <a id="location-slums-assassinate" class="a-link-button tooltip"> Assassinate </a>

@ -28,7 +28,7 @@ function initBitNodes() {
"people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " + "people quickly succumbed to the innate human impulse of evil and savagery. The organized crime " +
"factions quickly rose to the top of the modern world.<br><br>" + "factions quickly rose to the top of the modern world.<br><br>" +
"In this BitNode:<br><br>The maximum amount of money available on a server is significantly decreased<br>" + "In this BitNode:<br><br>The maximum amount of money available on a server is significantly decreased<br>" +
"The amount of money gained from crimes is doubled<br>" + "The amount of money gained from crimes and Infiltration is tripled<br>" +
"Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " + "Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, " +
"NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " + "NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs " +
"will earn the player money and reputation with the corresponding Faction<br>" + "will earn the player money and reputation with the corresponding Faction<br>" +
@ -61,6 +61,7 @@ function initBitNodes() {
"The base security level of servers is doubled<br>" + "The base security level of servers is doubled<br>" +
"The starting money on servers is halved, but the maximum money is doubled<br>" + "The starting money on servers is halved, but the maximum money is doubled<br>" +
"Most methods of earning money now give significantly less<br>" + "Most methods of earning money now give significantly less<br>" +
"Infiltration gives 50% more reputation and money<br>" +
"Augmentations are more expensive<br>" + "Augmentations are more expensive<br>" +
"Hacking experience gain rates are reduced<br><br>" + "Hacking experience gain rates are reduced<br><br>" +
"Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " + "Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will " +
@ -74,11 +75,11 @@ function initBitNodes() {
"Level 1: 4%<br>" + "Level 1: 4%<br>" +
"Level 2: 6%<br>" + "Level 2: 6%<br>" +
"Level 3: 7%"); "Level 3: 7%");
BitNodes["BitNode6"] = new BitNode(6, "Hacktocracy", "COMING SOON"); //Healthy Hacknet balancing mechanic BitNodes["BitNode6"] = new BitNode(6, "Do Androids Dream?", "COMING SOON"); //Build androids for automation
BitNodes["BitNode7"] = new BitNode(7, "Do Androids Dream?", "COMING SOON"); //Build androids for automation BitNodes["BitNode7"] = new BitNode(7, "Waste Runner", "COMING SOON"); //Postapocalyptic wasteland + blade runner
BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "COMING SOON"); //Trading only viable strategy BitNodes["BitNode8"] = new BitNode(8, "Ghost of Wall Street", "COMING SOON"); //Trading only viable strategy
BitNodes["BitNode9"] = new BitNode(9, "MegaCorp", "COMING SOON"); //Single corp/server with increasing difficulty BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "COMING SOON"); //Healthy Hacknet balancing mechanic
BitNodes["BitNode10"] = new BitNode(10, "Wasteland", "COMING SOON"); //Postapocalyptic BitNodes["BitNode10"] = new BitNode(10, "MegaCorp", "COMING SOON"); //Not sure yet
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.", BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " + "The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " + "of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
@ -92,6 +93,7 @@ function initBitNodes() {
"Weakening a server is twice as effective<br>" + "Weakening a server is twice as effective<br>" +
"Company wages are decreased by 50%<br>" + "Company wages are decreased by 50%<br>" +
"Hacknet Node production is significantly decreased<br>" + "Hacknet Node production is significantly decreased<br>" +
"Crime and Infiltration are more lucrative<br>" +
"Augmentations are twice as expensive<br><br>" + "Augmentations are twice as expensive<br><br>" +
"Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " + "Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " + "upgrade its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH " +
@ -149,6 +151,9 @@ let BitNodeMultipliers = {
AugmentationRepCost: 1, AugmentationRepCost: 1,
AugmentationMoneyCost: 1, AugmentationMoneyCost: 1,
InfiltrationMoney: 1,
InfiltrationRep: 1,
} }
function initBitNodeMultipliers() { function initBitNodeMultipliers() {
@ -166,12 +171,15 @@ function initBitNodeMultipliers() {
break; break;
case 2: //Rise of the Underworld case 2: //Rise of the Underworld
BitNodeMultipliers.ServerMaxMoney = 0.2; BitNodeMultipliers.ServerMaxMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 2; BitNodeMultipliers.ServerStartingMoney = 0.4;
BitNodeMultipliers.CrimeMoney = 3;
BitNodeMultipliers.InfiltrationMoney = 3;
BitNodeMultipliers.FactionWorkRepGain = 0.5; BitNodeMultipliers.FactionWorkRepGain = 0.5;
BitNodeMultipliers.FactionPassiveRepGain = 0; BitNodeMultipliers.FactionPassiveRepGain = 0;
break; break;
case 4: //The Singularity case 4: //The Singularity
BitNodeMultipliers.ServerMaxMoney = 0.15; BitNodeMultipliers.ServerMaxMoney = 0.15;
BitNodeMultipliers.ServerStartingMoney = 0.75;
BitNodeMultipliers.ScriptHackMoney = 0.2; BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.CompanyWorkMoney = 0.1; BitNodeMultipliers.CompanyWorkMoney = 0.1;
BitNodeMultipliers.CrimeMoney = 0.2; BitNodeMultipliers.CrimeMoney = 0.2;
@ -187,9 +195,11 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.ServerMaxMoney = 2; BitNodeMultipliers.ServerMaxMoney = 2;
BitNodeMultipliers.ServerStartingSecurity = 2; BitNodeMultipliers.ServerStartingSecurity = 2;
BitNodeMultipliers.ServerStartingMoney = 0.5; BitNodeMultipliers.ServerStartingMoney = 0.5;
BitNodeMultipliers.ScriptHackMoney = 0.25; BitNodeMultipliers.ScriptHackMoney = 0.2;
BitNodeMultipliers.HacknetNodeMoney = 0.2; BitNodeMultipliers.HacknetNodeMoney = 0.2;
BitNodeMultipliers.CrimeMoney = 0.5; BitNodeMultipliers.CrimeMoney = 0.5;
BitNodeMultipliers.InfiltrationRep = 1.5;
BitNodeMultipliers.InfiltrationMoney = 1.5;
BitNodeMultipliers.AugmentationMoneyCost = 2; BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.HackExpGain = 0.5; BitNodeMultipliers.HackExpGain = 0.5;
break; break;
@ -198,9 +208,12 @@ function initBitNodeMultipliers() {
BitNodeMultipliers.ServerStartingMoney = 0.1; BitNodeMultipliers.ServerStartingMoney = 0.1;
BitNodeMultipliers.ServerGrowthRate = 0.5; BitNodeMultipliers.ServerGrowthRate = 0.5;
BitNodeMultipliers.ServerWeakenRate = 2; BitNodeMultipliers.ServerWeakenRate = 2;
BitNodeMultipliers.CrimeMoney = 3;
BitNodeMultipliers.CompanyWorkMoney = 0.5; BitNodeMultipliers.CompanyWorkMoney = 0.5;
BitNodeMultipliers.HacknetNodeMoney = 0.1; BitNodeMultipliers.HacknetNodeMoney = 0.1;
BitNodeMultipliers.AugmentationMoneyCost = 2; BitNodeMultipliers.AugmentationMoneyCost = 2;
BitNodeMultipliers.InfiltrationMoney = 2.5;
BitNodeMultipliers.InfiltrationRep = 2.5;
break; break;
default: default:
console.log("WARNING: Player.bitNodeN invalid"); console.log("WARNING: Player.bitNodeN invalid");

@ -1,5 +1,5 @@
let CONSTANTS = { let CONSTANTS = {
Version: "0.30.0", Version: "0.31.0",
//Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience //Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
//and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then //and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -58,7 +58,7 @@ let CONSTANTS = {
ScriptKillRamCost: 0.5, //Kill and killall ScriptKillRamCost: 0.5, //Kill and killall
ScriptHasRootAccessRamCost: 0.05, ScriptHasRootAccessRamCost: 0.05,
ScriptGetHostnameRamCost: 0.05, //getHostname() and getIp() ScriptGetHostnameRamCost: 0.05, //getHostname() and getIp()
ScriptGetHackingLevelRamCost: 0.05, //getHackingLevel() and getIntelligence() ScriptGetHackingLevelRamCost: 0.05, //getHackingLevel()
ScriptGetMultipliersRamCost: 4.0, //getHackingMultipliers() and getBitNodeMultipliers() ScriptGetMultipliersRamCost: 4.0, //getHackingMultipliers() and getBitNodeMultipliers()
ScriptGetServerCost: 0.1, ScriptGetServerCost: 0.1,
ScriptFileExistsRamCost: 0.1, ScriptFileExistsRamCost: 0.1,
@ -102,7 +102,8 @@ let CONSTANTS = {
//Infiltration constants //Infiltration constants
InfiltrationBribeBaseAmount: 100000, //Amount per clearance level InfiltrationBribeBaseAmount: 100000, //Amount per clearance level
InfiltrationMoneyValue: 2000, //Convert "secret" value to money InfiltrationMoneyValue: 2500, //Convert "secret" value to money
InfiltrationRepValue: 1.4, //Convert "secret" value to faction reputation
//Stock market constants //Stock market constants
WSEAccountCost: 200000000, WSEAccountCost: 200000000,
@ -118,7 +119,7 @@ let CONSTANTS = {
IntelligenceCrimeBaseExpGain: 0.001, IntelligenceCrimeBaseExpGain: 0.001,
IntelligenceProgramBaseExpGain: 500, //Program required hack level divided by this to determine int exp gain IntelligenceProgramBaseExpGain: 500, //Program required hack level divided by this to determine int exp gain
IntelligenceTerminalHackBaseExpGain: 200, //Hacking exp divided by this to determine int exp gain IntelligenceTerminalHackBaseExpGain: 200, //Hacking exp divided by this to determine int exp gain
IntelligenceSingFnBaseExpGain: 0.001, IntelligenceSingFnBaseExpGain: 0.002,
IntelligenceClassBaseExpGain: 0.000001, IntelligenceClassBaseExpGain: 0.000001,
IntelligenceHackingMissionBaseExpGain: 0.03, //Hacking Mission difficulty multiplied by this to get exp gain IntelligenceHackingMissionBaseExpGain: 0.03, //Hacking Mission difficulty multiplied by this to get exp gain
@ -133,7 +134,7 @@ let CONSTANTS = {
"In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes " + "In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes " +
"are colored blue, while the enemy's are red. There are also other nodes on the map colored gray " + "are colored blue, while the enemy's are red. There are also other nodes on the map colored gray " +
"that initially belong to neither you nor the enemy. The goal of the game is " + "that initially belong to neither you nor the enemy. The goal of the game is " +
"to capture all of the enemy's database nodes, which are the parallelogram-shaped ones, within the time limit. " + "to capture all of the enemy's database nodes within the time limit. " +
"If you cannot capture all of the enemy's database nodes in the time limit, you will lose.<br><br>" + "If you cannot capture all of the enemy's database nodes in the time limit, you will lose.<br><br>" +
"Each Node has three stats: Attack, Defense, and HP. There are five different actions that " + "Each Node has three stats: Attack, Defense, and HP. There are five different actions that " +
"a Node can take:<br><br> " + "a Node can take:<br><br> " +
@ -146,7 +147,7 @@ let CONSTANTS = {
"Fortify - Raises the Node's Defense. The effectiveness is determined by your hacking level.<br>" + "Fortify - Raises the Node's Defense. The effectiveness is determined by your hacking level.<br>" +
"Overflow - Raises the Node's Attack but lowers its Defense. The effectiveness is determined by your hacking level.<br><br>" + "Overflow - Raises the Node's Attack but lowers its Defense. The effectiveness is determined by your hacking level.<br><br>" +
"Note that when determining the effectiveness of the above actions, the TOTAL Attack or Defense of the team is used, not just the " + "Note that when determining the effectiveness of the above actions, the TOTAL Attack or Defense of the team is used, not just the " +
"Attack/Defense of the individual Node that is performing the action.<br><br." + "Attack/Defense of the individual Node that is performing the action.<br><br>" +
"To capture a Node, you must lower its HP down to 0.<br><br>" + "To capture a Node, you must lower its HP down to 0.<br><br>" +
"There are six different types of Nodes:<br><br>" + "There are six different types of Nodes:<br><br>" +
"CPU Core - These are your main Nodes that are used to perform actions. Capable of performing every action<br>" + "CPU Core - These are your main Nodes that are used to perform actions. Capable of performing every action<br>" +
@ -161,7 +162,7 @@ let CONSTANTS = {
"To assign an action to a Node, you must first select one of your Nodes. This can be done by simply clicking on it. Only " + "To assign an action to a Node, you must first select one of your Nodes. This can be done by simply clicking on it. Only " +
"one Node can be selected at a time, and it will be denoted with a white highlight. After selecting the Node, " + "one Node can be selected at a time, and it will be denoted with a white highlight. After selecting the Node, " +
"select its action using the Action Buttons near the top of the screen. Every action also has a corresponding keyboard " + "select its action using the Action Buttons near the top of the screen. Every action also has a corresponding keyboard " +
"shortcut that can be used as well.<br><br>" + "shortcut.<br><br>" +
"For certain actions such as attacking, scanning, and weakening, the Node performing the action must have a target. To target " + "For certain actions such as attacking, scanning, and weakening, the Node performing the action must have a target. To target " +
"another node, simply click-and-drag from the 'source' Node to a target. A Node can only have one target, and you can target " + "another node, simply click-and-drag from the 'source' Node to a target. A Node can only have one target, and you can target " +
"any Node that is adjacent to one of your Nodes (immediately above, below, or to the side. NOT diagonal). Furthermore, only CPU Cores and Transfer Nodes " + "any Node that is adjacent to one of your Nodes (immediately above, below, or to the side. NOT diagonal). Furthermore, only CPU Cores and Transfer Nodes " +
@ -173,7 +174,7 @@ let CONSTANTS = {
"are not actively being targeted will increase by a fixed percentage.<br><br>" + "are not actively being targeted will increase by a fixed percentage.<br><br>" +
"-Whenever a Node is conquered, its stats are significantly reduced<br><br>" + "-Whenever a Node is conquered, its stats are significantly reduced<br><br>" +
"-Miscellaneous Nodes slowly raise their defense over time<br><br>" + "-Miscellaneous Nodes slowly raise their defense over time<br><br>" +
"-Nodes slowly regenerate health and raise over time.", "-Nodes slowly regenerate health over time.",
//Gang constants //Gang constants
@ -238,11 +239,13 @@ let CONSTANTS = {
ClassLeadershipBaseCost: 320, ClassLeadershipBaseCost: 320,
ClassGymBaseCost: 120, ClassGymBaseCost: 120,
CrimeSingFnDivider: 2, //Factor by which exp/profit is reduced when commiting crime through Sing Fn
CrimeShoplift: "shoplift", CrimeShoplift: "shoplift",
CrimeRobStore: "rob a store", CrimeRobStore: "rob a store",
CrimeMug: "mug someone", CrimeMug: "mug someone",
CrimeLarceny: "commit larceny", CrimeLarceny: "commit larceny",
CrimeDrugs: "deal drugs", CrimeDrugs: "deal drugs",
CrimeBondForgery: "forge corporate bonds",
CrimeTraffickArms: "traffick illegal arms", CrimeTraffickArms: "traffick illegal arms",
CrimeHomicide: "commit homicide", CrimeHomicide: "commit homicide",
CrimeGrandTheftAuto: "commit grand theft auto", CrimeGrandTheftAuto: "commit grand theft auto",
@ -539,7 +542,6 @@ let CONSTANTS = {
"<i>getIp()</i><br>Returns a string with the IP Address of the server that the script is running on <br><br>" + "<i>getIp()</i><br>Returns a string with the IP Address of the server that the script is running on <br><br>" +
"<i>getHostname()</i><br>Returns a string with the hostname of the server that the script is running on<br><br>" + "<i>getHostname()</i><br>Returns a string with the hostname of the server that the script is running on<br><br>" +
"<i>getHackingLevel()</i><br>Returns the Player's current hacking level.<br><br> " + "<i>getHackingLevel()</i><br>Returns the Player's current hacking level.<br><br> " +
"<i>getIntelligence()</i><br>Returns the Player's current intelligence level. Requires Source-File 5 to run<br><br>" +
"<i>getHackingMultipliers()</i><br>Returns an object containing the Player's hacking related multipliers. " + "<i>getHackingMultipliers()</i><br>Returns an object containing the Player's hacking related multipliers. " +
"These multipliers are returned in integer forms, not percentages (e.g. 1.5 instead of 150%). " + "These multipliers are returned in integer forms, not percentages (e.g. 1.5 instead of 150%). " +
"The object has the following structure:<br><br>" + "The object has the following structure:<br><br>" +
@ -844,6 +846,15 @@ let CONSTANTS = {
"The cost of purchasing programs using this function is the same as if you were purchasing them through the Dark Web (using " + "The cost of purchasing programs using this function is the same as if you were purchasing them through the Dark Web (using " +
"the buy Terminal command).<br><br>" + "the buy Terminal command).<br><br>" +
"This function will return true if the specified program is purchased, and false otherwise.<br><br>" + "This function will return true if the specified program is purchased, and false otherwise.<br><br>" +
"<i>getStats()</i><br>If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this " +
"function.<br><br>Returns an object with the Player's stats. The object has the following properties:<br><br>" +
"Player.hacking<br>Player.strength<br>Player.defense<br>Player.dexterity<br>Player.agility<br>Player.charisma<br>Player.intelligence<br><br>" +
"Example: <br><br>" +
"res = getStats();<br>print('My charisma level is: ' + res.charisma);<br><br>" +
"<i>isBusy()</i><br>If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to run this " +
"function.<br><br>Returns a boolean indicating whether or not the player is currently performing an 'action'. " +
"These actions include working for a company/faction, studying at a univeristy, working out at a gym, " +
"creating a program, or committing a crime.<br><br>" +
"<i>upgradeHomeRam()</i><br>" + "<i>upgradeHomeRam()</i><br>" +
"If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" + "If you are not in BitNode-4, then you must have Level 2 of Source-File 4 in order to use this function.<br><br>" +
"This function will upgrade amount of RAM on the player's home computer. The cost is the same as if you were to do it manually.<br><br>" + "This function will upgrade amount of RAM on the player's home computer. The cost is the same as if you were to do it manually.<br><br>" +
@ -905,6 +916,36 @@ let CONSTANTS = {
"BruteSSH.exe: 50<br>FTPCrack.exe: 100<br>relaySMTP.exe: 250<br>HTTPWorm.exe: 500<br>SQLInject.exe: 750<br>" + "BruteSSH.exe: 50<br>FTPCrack.exe: 100<br>relaySMTP.exe: 250<br>HTTPWorm.exe: 500<br>SQLInject.exe: 750<br>" +
"DeepscanV1.exe: 75<br>DeepscanV2.exe: 400<br>ServerProfiler.exe: 75<br>AutoLink.exe: 25<br><br>" + "DeepscanV1.exe: 75<br>DeepscanV2.exe: 400<br>ServerProfiler.exe: 75<br>AutoLink.exe: 25<br><br>" +
"This function returns true if you successfully start working on the specified program, and false otherwise.<br><br>" + "This function returns true if you successfully start working on the specified program, and false otherwise.<br><br>" +
"<i>commitCrime(crime)</i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function is used to automatically attempt to commit crimes. If you are already in the middle of some 'working' " +
"action (such as working for a company or training at a gym), then running this function will automatically cancel " +
"that action and give you your earnings.<br><br>" +
"The function takes a string that specifies what crime to attempt. This argument is not case-sensitive and is fairly " +
"lenient in terms of what inputs it accepts. Here is a list of valid inputs for all of the crimes:<br><br>" +
"shoplift, rob store, mug, larceny, deal drugs, bond forgery, traffick arms, homicide, grand theft auto, " +
"kidnap, assassinate, heist<br><br> " +
"This function returns the number of seconds it takes to attempt the specified crime (e.g It takes 60 seconds to attempt " +
"the 'Rob Store' crime, so running commitCrime('rob store') will return 60). Warning: I do not recommend using the time " +
"returned from this function to try and schedule your crime attempts. Instead, I would use the isBusy() Singularity function " +
"to check whether you have finished attempting a crime. This is because although the game sets a certain crime to be X amount of seconds, " +
"there is no guarantee that your browser will follow that time limit.<br><br>" +
"<i>getCrimeChance(crime)</i><br>If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to " +
"use this function.<br><br>" +
"This function returns your chance of success at commiting the specified crime. The chance is returned as a decimal " +
"(i.e. 60% would be returned as 0.6). The argument for this function is a string. It is not case-sensitive and is fairly " +
"lenient in terms of what inputs it accepts. Check the documentation for the commitCrime() Singularity Function to see " +
"examples of valid inputs.<br><br>" +
"<i>getOwnedAugmentations(purchased=false)</i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function returns an array of the names of all Augmentations you own as strings. It takes a single optional " +
"boolean argument that specifies whether the returned array should include Augmentations you have purchased " +
"but not yet installed. If it is true, then the returned array will include these Augmentations. By default, " +
"this argument is false.<br><br>" +
"<i>getAugmentationsFromFaction(facName)</i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"Returns an array containing the names (as strings) of all Augmentations that are available from the specified faction. " +
"The argument must be a string with the faction's name. This argument is case-sensitive.<br><br>" +
"<i>getAugmentationCost(augName)</i><br>" + "<i>getAugmentationCost(augName)</i><br>" +
"If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" + "If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.<br><br>" +
"This function returns an array with two elements that gives the cost for the specified Augmentation" + "This function returns an array with two elements that gives the cost for the specified Augmentation" +
@ -1024,24 +1065,27 @@ let CONSTANTS = {
"World Stock Exchange account and TIX API Access<br>", "World Stock Exchange account and TIX API Access<br>",
LatestUpdate: LatestUpdate:
"v0.30.0<br>" + "v0.31.0<br>" +
"-Added getAugmentations() and getAugmentationsFromFaction() Netscript Singularity Functions<br>" + "-Game now saves to IndexedDb (if your browser supports it). This means you should " +
"-Increased the rate of Intelligence exp gain<br>" + "no longer have trouble saving the game when your save file gets too big (from running " +
"-Added a new upgrade for home computers: CPU Cores. Each CPU core on the home computer " + "too many scripts). " +
"grants an additional starting Core Node in Hacking Missions. I may add in other benefits later. Like RAM upgrades, upgrading " + "The game will still be saved to localStorage as well<br>" +
"the CPU Core on your home computer persists until you enter a new BitNode.<br>" + "-New file type: text files (.txt). You can read or write to text files using the read()/write() Netscript commands. " +
"-Added lscpu Terminal command to check number of CPU Cores<br>" + "You can view text files in Terminal using 'cat'. Eventually I will make it so you can edit them in the editor " +
"-Changed the effect of Source-File 5 and made BitNode-5 a little bit harder<br>" + "but that's not available yet. You can also download files to your real computer using the 'download' Terminal command<br>" +
"-Fixed a bug with Netscript functions (the ones you create yourself)<br>" + "-Added a new Crime: Bond Forgery. This crime takes 5 minutes to attempt " +
"-Hacking Missions officially released (they give reputation now). Notable changes in the last few updates:<br><br>" + "and gives $4,500,000 if successful. It is meant for mid game.<br>" +
"---Misc Nodes slowly gain hp/defense over time<br>" + "-Added commitCrime(), getCrimeChance(), isBusy(), and getStats() Singularity Functions.<br>" +
"---Conquering a Misc Node will increase the defense of all remaining Misc Nodes that are not being targeted by a certain percentage<br>" + "-Removed getIntelligence() Netscript function<br>" +
"---Reputation reward for winning a Mission is now affected by faction favor and Player's faction rep multiplier<br>" + "-Added sprintf and vsprintf to Netscript. See <a href='https://github.com/alexei/sprintf.js' target='_blank'>this Github page for details</a><br>" +
"---Whenever a Node is conquered, its stats are reduced<br><br>" + "-Increased the amount of money gained from Infiltration by 20%, and the amount of faction reputation by 12%<br>" +
"v0.29.3<br>" + "-Rebalanced BitNode-2 so that Crime and Infiltration are more profitable but hacking is less profitable. Infiltration also gives more faction rep<br>" +
"-Fixed bug for killing scripts and showing error messages when there are errors in a player-defined function<br>" + "-Rebalanced BitNode-4 so that hacking is slightly less profitable<br>" +
"-Added function name autocompletion in Script Editor. Press Ctrl+space on a prefix to show autocompletion options.<br>" + "-Rebalanced BitNode-5 so that Infiltration is more profitable and gives more faction rep<br>" +
"-Minor rebalancing and bug fixes for Infiltration and Hacking Missions<br><br>" "-Rebalanced BitNode-11 so that Crime and Infiltration are more profitable. Infiltration also gives more faction rep.<br>" +
"-Fixed an annoying issue in Hacking Missions where sometimes you would click a Node but it wouldnt actually get selected<br>" +
"-Made the Hacking Mission gameplay a bit slower by lowering the effect of Scan and reducing Attack damage<br>" +
"-Slightly increased the base reputation gain rate for factions when doing Field Work and Security Work<br>"
} }
export {CONSTANTS}; export {CONSTANTS};

@ -3,59 +3,100 @@ import {Player} from "./Player.js";
import {dialogBoxCreate} from "../utils/DialogBox.js"; import {dialogBoxCreate} from "../utils/DialogBox.js";
/* Crimes.js */ /* Crimes.js */
function commitShopliftCrime() { function commitShopliftCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeShoplift; Player.crimeType = CONSTANTS.CrimeShoplift;
Player.startCrime(0, 0, 0, 2, 2, 0, 15000, 2000); //$7500/s, 1 exp/s var time = 2000;
Player.startCrime(0, 0, 0, 2/div, 2/div, 0, 15000/div, time, singParams); //$7500/s, 1 exp/s
return time;
} }
function commitRobStoreCrime() { function commitRobStoreCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeRobStore; Player.crimeType = CONSTANTS.CrimeRobStore;
Player.startCrime(30, 0, 0, 45, 45, 0, 400000, 60000); //$6666,6/2, 0.5exp/s, 0.75exp/s var time = 60000;
Player.startCrime(30/div, 0, 0, 45/div, 45/div, 0, 400000/div, time, singParams); //$6666,6/2, 0.5exp/s, 0.75exp/s
return time;
} }
function commitMugCrime() { function commitMugCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeMug; Player.crimeType = CONSTANTS.CrimeMug;
Player.startCrime(0, 3, 3, 3, 3, 0, 36000, 4000); //$9000/s, .66 exp/s var time = 4000;
Player.startCrime(0, 3/div, 3/div, 3/div, 3/div, 0, 36000/div, time, singParams); //$9000/s, .66 exp/s
return time;
} }
function commitLarcenyCrime() { function commitLarcenyCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeLarceny; Player.crimeType = CONSTANTS.CrimeLarceny;
Player.startCrime(45, 0, 0, 60, 60, 0, 800000, 90000) // $8888.88/s, .5 exp/s, .66 exp/s var time = 90000;
Player.startCrime(45/div, 0, 0, 60/div, 60/div, 0, 800000/div, time, singParams) // $8888.88/s, .5 exp/s, .66 exp/s
return time;
} }
function commitDealDrugsCrime() { function commitDealDrugsCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeDrugs; Player.crimeType = CONSTANTS.CrimeDrugs;
Player.startCrime(0, 0, 0, 5, 5, 10, 120000, 10000); //$12000/s, .5 exp/s, 1 exp/s var time = 10000;
Player.startCrime(0, 0, 0, 5/div, 5/div, 10/div, 120000/div, time, singParams); //$12000/s, .5 exp/s, 1 exp/s
return time;
} }
function commitTraffickArmsCrime() { function commitBondForgeryCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeBondForgery;
var time = 300000;
Player.startCrime(100/div, 0, 0, 150/div, 0, 15/div, 4500000/div, time, singParams); //$15000/s, 0.33 hack exp/s, .5 dex exp/s, .05 cha exp
return time;
}
function commitTraffickArmsCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeTraffickArms; Player.crimeType = CONSTANTS.CrimeTraffickArms;
Player.startCrime(0, 20, 20, 20, 20, 40, 600000, 40000); //$15000/s, .5 combat exp/s, 1 cha exp/s var time = 40000;
Player.startCrime(0, 20/div, 20/div, 20/div, 20/div, 40/div, 600000/div, time, singParams); //$15000/s, .5 combat exp/s, 1 cha exp/s
return time;
} }
function commitHomicideCrime() { function commitHomicideCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeHomicide; Player.crimeType = CONSTANTS.CrimeHomicide;
Player.startCrime(0, 2, 2, 2, 2, 0, 45000, 3000); //$15000/s, 0.66 combat exp/s var time = 3000;
Player.startCrime(0, 2/div, 2/div, 2/div, 2/div, 0, 45000/div, time, singParams); //$15000/s, 0.66 combat exp/s
return time;
} }
function commitGrandTheftAutoCrime() { function commitGrandTheftAutoCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeGrandTheftAuto; Player.crimeType = CONSTANTS.CrimeGrandTheftAuto;
Player.startCrime(0, 20, 20, 20, 80, 40, 1600000, 80000); //$20000/s, .25 exp/s, 1 exp/s, .5 exp/s var time = 80000;
Player.startCrime(0, 20/div, 20/div, 20/div, 80/div, 40/div, 1600000/div, time, singParams); //$20000/s, .25 exp/s, 1 exp/s, .5 exp/s
return time;
} }
function commitKidnapCrime() { function commitKidnapCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeKidnap; Player.crimeType = CONSTANTS.CrimeKidnap;
Player.startCrime(0, 80, 80, 80, 80, 80, 3600000, 120000); //$30000/s. .66 exp/s var time = 120000;
Player.startCrime(0, 80/div, 80/div, 80/div, 80/div, 80/div, 3600000/div, time, singParams); //$30000/s. .66 exp/s
return time;
} }
function commitAssassinationCrime() { function commitAssassinationCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeAssassination; Player.crimeType = CONSTANTS.CrimeAssassination;
Player.startCrime(0, 300, 300, 300, 300, 0, 12000000, 300000); //$40000/s, 1 exp/s var time = 300000;
Player.startCrime(0, 300/div, 300/div, 300/div, 300/div, 0, 12000000/div, time, singParams); //$40000/s, 1 exp/s
return time;
} }
function commitHeistCrime() { function commitHeistCrime(div=1, singParams=null) {
if (div <= 0) {div = 1;}
Player.crimeType = CONSTANTS.CrimeHeist; Player.crimeType = CONSTANTS.CrimeHeist;
Player.startCrime(450, 450, 450, 450, 450, 450, 120000000, 600000); //$200000/s, .75exp/s var time = 600000;
Player.startCrime(450/div, 450/div, 450/div, 450/div, 450/div, 450/div, 120000000/div, time, singParams); //$200000/s, .75exp/s
return time;
} }
function determineCrimeSuccess(crime, moneyGained) { function determineCrimeSuccess(crime, moneyGained) {
@ -76,6 +117,9 @@ function determineCrimeSuccess(crime, moneyGained) {
case CONSTANTS.CrimeDrugs: case CONSTANTS.CrimeDrugs:
chance = determineCrimeChanceDealDrugs(); chance = determineCrimeChanceDealDrugs();
break; break;
case CONSTANTS.CrimeBondForgery:
chance = determineCrimeChanceBondForgery();
break;
case CONSTANTS.CrimeTraffickArms: case CONSTANTS.CrimeTraffickArms:
chance = determineCrimeChanceTraffickArms(); chance = determineCrimeChanceTraffickArms();
break; break;
@ -158,6 +202,14 @@ function determineCrimeChanceDealDrugs() {
return Math.min(chance, 1); return Math.min(chance, 1);
} }
function determineCrimeChanceBondForgery() {
var chance = (0.1*Player.hacking_skill / maxLvl +
2.5*Player.dexterity / maxLvl +
2*intWgt*Player.intelligence / maxLvl);
chance *= Player.crime_success_mult;
return Math.min(chance, 1);
}
function determineCrimeChanceTraffickArms() { function determineCrimeChanceTraffickArms() {
var chance = (Player.charisma / maxLvl + var chance = (Player.charisma / maxLvl +
Player.strength / maxLvl + Player.strength / maxLvl +
@ -222,12 +274,14 @@ function determineCrimeChanceHeist() {
} }
export {commitShopliftCrime, commitRobStoreCrime, commitMugCrime, export {commitShopliftCrime, commitRobStoreCrime, commitMugCrime,
commitLarcenyCrime, commitDealDrugsCrime, commitTraffickArmsCrime, commitLarcenyCrime, commitDealDrugsCrime, commitBondForgeryCrime,
commitTraffickArmsCrime,
commitHomicideCrime, commitGrandTheftAutoCrime, commitKidnapCrime, commitHomicideCrime, commitGrandTheftAutoCrime, commitKidnapCrime,
commitAssassinationCrime, commitHeistCrime, determineCrimeSuccess, commitAssassinationCrime, commitHeistCrime, determineCrimeSuccess,
determineCrimeChanceShoplift, determineCrimeChanceRobStore, determineCrimeChanceShoplift, determineCrimeChanceRobStore,
determineCrimeChanceMug, determineCrimeChanceLarceny, determineCrimeChanceMug, determineCrimeChanceLarceny,
determineCrimeChanceDealDrugs, determineCrimeChanceTraffickArms, determineCrimeChanceDealDrugs, determineCrimeChanceBondForgery,
determineCrimeChanceTraffickArms,
determineCrimeChanceHomicide, determineCrimeChanceGrandTheftAuto, determineCrimeChanceHomicide, determineCrimeChanceGrandTheftAuto,
determineCrimeChanceKidnap, determineCrimeChanceAssassination, determineCrimeChanceKidnap, determineCrimeChanceAssassination,
determineCrimeChanceHeist}; determineCrimeChanceHeist};

@ -9,6 +9,7 @@ let TerminalHelpText =
"clear Clear all text on the terminal <br>" + "clear Clear all text on the terminal <br>" +
"cls See 'clear' command <br>" + "cls See 'clear' command <br>" +
"connect [ip/hostname] Connects to a remote server<br>" + "connect [ip/hostname] Connects to a remote server<br>" +
"download [text file] Downloads a text (.txt) file to your computer<br>" +
"free Check the machine's memory (RAM) usage<br>" + "free Check the machine's memory (RAM) usage<br>" +
"hack Hack the current machine<br>" + "hack Hack the current machine<br>" +
"help [command] Display this help text, or the help text for a command<br>" + "help [command] Display this help text, or the help text for a command<br>" +
@ -85,6 +86,9 @@ let HelpTexts = {
"Connect to a remote server. The hostname or IP address of the remote server must be given as the argument " + "Connect to a remote server. The hostname or IP address of the remote server must be given as the argument " +
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To " + "to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To " +
"see which servers can be connected to, use the 'scan' command.", "see which servers can be connected to, use the 'scan' command.",
download: "download [text file]<br>" +
"Downloads a text file to your computer (like your real life computer). Only works on text files, " +
"which are the ones with a .txt extension.",
free: "free<br>" + free: "free<br>" +
"Display's the memory usage on the current machine. Print the amount of RAM that is available on the current server as well as " + "Display's the memory usage on the current machine. Print the amount of RAM that is available on the current server as well as " +
"how much of it is being used.", "how much of it is being used.",

@ -1,3 +1,4 @@
import {BitNodeMultipliers} from "./BitNode.js";
import {CONSTANTS} from "./Constants.js"; import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js"; import {Engine} from "./engine.js";
import {Player} from "./Player.js"; import {Player} from "./Player.js";
@ -405,8 +406,10 @@ function endInfiltrationLevel(inst) {
//Check if you gained any secrets //Check if you gained any secrets
if (inst.clearanceLevel % 5 == 0) { if (inst.clearanceLevel % 5 == 0) {
var baseSecretValue = inst.baseValue * inst.clearanceLevel / 2; var baseSecretValue = inst.baseValue * inst.clearanceLevel / 2;
var secretValue = baseSecretValue * Player.faction_rep_mult * 1.25; var secretValue = baseSecretValue * Player.faction_rep_mult *
var secretMoneyValue = baseSecretValue * CONSTANTS.InfiltrationMoneyValue; CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep;
var secretMoneyValue = baseSecretValue * CONSTANTS.InfiltrationMoneyValue *
BitNodeMultipliers.InfiltrationMoney;
inst.secretsStolen.push(baseSecretValue); inst.secretsStolen.push(baseSecretValue);
dialogBoxCreate("You found and stole a set of classified documents from the company. " + dialogBoxCreate("You found and stole a set of classified documents from the company. " +
"These classified secrets could probably be sold for money ($" + "These classified secrets could probably be sold for money ($" +
@ -441,8 +444,10 @@ function updateInfiltrationLevelText(inst) {
var totalValue = 0; var totalValue = 0;
var totalMoneyValue = 0; var totalMoneyValue = 0;
for (var i = 0; i < inst.secretsStolen.length; ++i) { for (var i = 0; i < inst.secretsStolen.length; ++i) {
totalValue += (inst.secretsStolen[i] * Player.faction_rep_mult * 1.25); totalValue += (inst.secretsStolen[i] * Player.faction_rep_mult *
totalMoneyValue += inst.secretsStolen[i] * CONSTANTS.InfiltrationMoneyValue; CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep);
totalMoneyValue += inst.secretsStolen[i] * CONSTANTS.InfiltrationMoneyValue *
BitNodeMultipliers.InfiltrationMoney;
} }
document.getElementById("infiltration-level-text").innerHTML = document.getElementById("infiltration-level-text").innerHTML =
"Facility name: " + inst.companyName + "<br>" + "Facility name: " + inst.companyName + "<br>" +

@ -2,12 +2,14 @@ import {CompanyPositions, initCompanies,
Companies, getJobRequirementText} from "./Company.js"; Companies, getJobRequirementText} from "./Company.js";
import {CONSTANTS} from "./Constants.js"; import {CONSTANTS} from "./Constants.js";
import {commitShopliftCrime, commitRobStoreCrime, commitMugCrime, import {commitShopliftCrime, commitRobStoreCrime, commitMugCrime,
commitLarcenyCrime, commitDealDrugsCrime, commitTraffickArmsCrime, commitLarcenyCrime, commitDealDrugsCrime, commitBondForgeryCrime,
commitTraffickArmsCrime,
commitHomicideCrime, commitGrandTheftAutoCrime, commitKidnapCrime, commitHomicideCrime, commitGrandTheftAutoCrime, commitKidnapCrime,
commitAssassinationCrime, commitHeistCrime, determineCrimeSuccess, commitAssassinationCrime, commitHeistCrime, determineCrimeSuccess,
determineCrimeChanceShoplift, determineCrimeChanceRobStore, determineCrimeChanceShoplift, determineCrimeChanceRobStore,
determineCrimeChanceMug, determineCrimeChanceLarceny, determineCrimeChanceMug, determineCrimeChanceLarceny,
determineCrimeChanceDealDrugs, determineCrimeChanceTraffickArms, determineCrimeChanceDealDrugs, determineCrimeChanceBondForgery,
determineCrimeChanceTraffickArms,
determineCrimeChanceHomicide, determineCrimeChanceGrandTheftAuto, determineCrimeChanceHomicide, determineCrimeChanceGrandTheftAuto,
determineCrimeChanceKidnap, determineCrimeChanceAssassination, determineCrimeChanceKidnap, determineCrimeChanceAssassination,
determineCrimeChanceHeist} from "./Crimes.js"; determineCrimeChanceHeist} from "./Crimes.js";
@ -194,6 +196,7 @@ function displayLocationContent() {
var slumsMug = document.getElementById("location-slums-mug"); var slumsMug = document.getElementById("location-slums-mug");
var slumsLarceny = document.getElementById("location-slums-larceny"); var slumsLarceny = document.getElementById("location-slums-larceny");
var slumsDealDrugs = document.getElementById("location-slums-deal-drugs"); var slumsDealDrugs = document.getElementById("location-slums-deal-drugs");
var slumsBondForgery = document.getElementById("location-slums-bond-forgery");
var slumsTrafficArms = document.getElementById("location-slums-traffic-arms"); var slumsTrafficArms = document.getElementById("location-slums-traffic-arms");
var slumsHomicide = document.getElementById("location-slums-homicide"); var slumsHomicide = document.getElementById("location-slums-homicide");
var slumsGta = document.getElementById("location-slums-gta"); var slumsGta = document.getElementById("location-slums-gta");
@ -298,6 +301,7 @@ function displayLocationContent() {
slumsMug.style.display = "none"; slumsMug.style.display = "none";
slumsLarceny.style.display = "none"; slumsLarceny.style.display = "none";
slumsDealDrugs.style.display = "none"; slumsDealDrugs.style.display = "none";
slumsBondForgery.style.display = "none";
slumsTrafficArms.style.display = "none"; slumsTrafficArms.style.display = "none";
slumsHomicide.style.display = "none"; slumsHomicide.style.display = "none";
slumsGta.style.display = "none"; slumsGta.style.display = "none";
@ -1026,6 +1030,7 @@ function displayLocationContent() {
var mugChance = determineCrimeChanceMug(); var mugChance = determineCrimeChanceMug();
var larcenyChance = determineCrimeChanceLarceny(); var larcenyChance = determineCrimeChanceLarceny();
var drugsChance = determineCrimeChanceDealDrugs(); var drugsChance = determineCrimeChanceDealDrugs();
var bondChance = determineCrimeChanceBondForgery();
var armsChance = determineCrimeChanceTraffickArms(); var armsChance = determineCrimeChanceTraffickArms();
var homicideChance = determineCrimeChanceHomicide(); var homicideChance = determineCrimeChanceHomicide();
var gtaChance = determineCrimeChanceGrandTheftAuto(); var gtaChance = determineCrimeChanceGrandTheftAuto();
@ -1049,6 +1054,9 @@ function displayLocationContent() {
slumsDealDrugs.style.display = "block"; slumsDealDrugs.style.display = "block";
slumsDealDrugs.innerHTML = "Deal Drugs (" + (drugsChance*100).toFixed(3) + "% chance of success)"; slumsDealDrugs.innerHTML = "Deal Drugs (" + (drugsChance*100).toFixed(3) + "% chance of success)";
slumsDealDrugs.innerHTML += '<span class="tooltiptext"> Attempt to deal drugs </span>'; slumsDealDrugs.innerHTML += '<span class="tooltiptext"> Attempt to deal drugs </span>';
slumsBondForgery.style.display = "block";
slumsBondForgery.innerHTML = "Bond Forgery(" + (bondChance*100).toFixed(3) + "% chance of success)";
slumsBondForgery.innerHTML += "<span class='tooltiptext'> Attempt to forge corporate bonds</span>";
slumsTrafficArms.style.display = "block"; slumsTrafficArms.style.display = "block";
slumsTrafficArms.innerHTML = "Traffick Illegal Arms (" + (armsChance*100).toFixed(3) + "% chance of success)"; slumsTrafficArms.innerHTML = "Traffick Illegal Arms (" + (armsChance*100).toFixed(3) + "% chance of success)";
slumsTrafficArms.innerHTML += '<span class="tooltiptext"> Attempt to smuggle illegal arms into the city and sell them to gangs and criminal organizations </span>'; slumsTrafficArms.innerHTML += '<span class="tooltiptext"> Attempt to smuggle illegal arms into the city and sell them to gangs and criminal organizations </span>';
@ -1598,6 +1606,7 @@ function initLocationButtons() {
var slumsMug = document.getElementById("location-slums-mug"); var slumsMug = document.getElementById("location-slums-mug");
var slumsLarceny = document.getElementById("location-slums-larceny"); var slumsLarceny = document.getElementById("location-slums-larceny");
var slumsDealDrugs = document.getElementById("location-slums-deal-drugs"); var slumsDealDrugs = document.getElementById("location-slums-deal-drugs");
var slumsBondForgery = document.getElementById("location-slums-bond-forgery");
var slumsTrafficArms = document.getElementById("location-slums-traffic-arms"); var slumsTrafficArms = document.getElementById("location-slums-traffic-arms");
var slumsHomicide = document.getElementById("location-slums-homicide"); var slumsHomicide = document.getElementById("location-slums-homicide");
var slumsGta = document.getElementById("location-slums-gta"); var slumsGta = document.getElementById("location-slums-gta");
@ -1844,6 +1853,11 @@ function initLocationButtons() {
return false; return false;
}); });
slumsBondForgery.addEventListener("click", function() {
commitBondForgeryCrime();
return false;
});
slumsTrafficArms.addEventListener("click", function() { slumsTrafficArms.addEventListener("click", function() {
commitTraffickArmsCrime(); commitTraffickArmsCrime();
return false; return false;

@ -238,8 +238,8 @@ HackingMission.prototype.init = function() {
for (var i = 0; i < numNodes; ++i) { for (var i = 0; i < numNodes; ++i) {
var stats = { var stats = {
atk: randMult * getRandomInt(75, 85), atk: randMult * getRandomInt(75, 85),
def: randMult * getRandomInt(20, 40), def: randMult * getRandomInt(25, 35),
hp: randMult * getRandomInt(200, 220) hp: randMult * getRandomInt(210, 230)
} }
this.enemyCores.push(new Node(NodeTypes.Core, stats)); this.enemyCores.push(new Node(NodeTypes.Core, stats));
this.enemyCores[i].setControlledByEnemy(); this.enemyCores[i].setControlledByEnemy();
@ -248,8 +248,8 @@ HackingMission.prototype.init = function() {
for (var i = 0; i < numFirewalls; ++i) { for (var i = 0; i < numFirewalls; ++i) {
var stats = { var stats = {
atk: 0, atk: 0,
def: randMult * getRandomInt(80, 100), def: randMult * getRandomInt(80, 90),
hp: randMult * getRandomInt(250, 275) hp: randMult * getRandomInt(275, 300)
} }
this.enemyNodes.push(new Node(NodeTypes.Firewall, stats)); this.enemyNodes.push(new Node(NodeTypes.Firewall, stats));
this.enemyNodes[i].setControlledByEnemy(); this.enemyNodes[i].setControlledByEnemy();
@ -258,8 +258,8 @@ HackingMission.prototype.init = function() {
for (var i = 0; i < numDatabases; ++i) { for (var i = 0; i < numDatabases; ++i) {
var stats = { var stats = {
atk: 0, atk: 0,
def: randMult * getRandomInt(75, 100), def: randMult * getRandomInt(70, 85),
hp: randMult * getRandomInt(200, 250) hp: randMult * getRandomInt(210, 275)
} }
var node = new Node(NodeTypes.Database, stats); var node = new Node(NodeTypes.Database, stats);
node.setControlledByEnemy(); node.setControlledByEnemy();
@ -630,16 +630,16 @@ HackingMission.prototype.createMap = function() {
case 0: //Spam case 0: //Spam
var stats = { var stats = {
atk: 0, atk: 0,
def: averageAttack * 1.2 + getRandomInt(10, 50), def: averageAttack * 1.15 + getRandomInt(10, 50),
hp: randMult * getRandomInt(160, 180) hp: randMult * getRandomInt(200, 225)
} }
node = new Node(NodeTypes.Spam, stats); node = new Node(NodeTypes.Spam, stats);
break; break;
case 1: //Transfer case 1: //Transfer
var stats = { var stats = {
atk: 0, atk: 0,
def: averageAttack * 1.2 + getRandomInt(10, 50), def: averageAttack * 1.15 + getRandomInt(10, 50),
hp: randMult * getRandomInt(210, 230) hp: randMult * getRandomInt(250, 275)
} }
node = new Node(NodeTypes.Transfer, stats); node = new Node(NodeTypes.Transfer, stats);
break; break;
@ -647,8 +647,8 @@ HackingMission.prototype.createMap = function() {
default: default:
var stats = { var stats = {
atk: 0, atk: 0,
def: averageAttack * 1.2 + getRandomInt(25, 75), def: averageAttack * 1.15 + getRandomInt(25, 75),
hp: randMult * getRandomInt(275, 300) hp: randMult * getRandomInt(300, 320)
} }
node = new Node(NodeTypes.Shield, stats); node = new Node(NodeTypes.Shield, stats);
break; break;
@ -675,14 +675,13 @@ HackingMission.prototype.createMap = function() {
} }
HackingMission.prototype.createNodeDomElement = function(nodeObj) { HackingMission.prototype.createNodeDomElement = function(nodeObj) {
var nodeDiv = document.createElement("a"); var nodeDiv = document.createElement("a"), txtEl = document.createElement('p');
nodeObj.el = nodeDiv; nodeObj.el = nodeDiv;
document.getElementById("hacking-mission-map").appendChild(nodeDiv);
//Set the node element's id based on its coordinates //Set the node element's id based on its coordinates
nodeDiv.setAttribute("id", "hacking-mission-node-" + var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
nodeObj.pos[0] + "-" + nodeDiv.setAttribute("id", id);
nodeObj.pos[1]); txtEl.setAttribute("id", id + "-txt");
//Set node classes for owner //Set node classes for owner
nodeDiv.classList.add("hack-mission-node"); nodeDiv.classList.add("hack-mission-node");
@ -696,41 +695,44 @@ HackingMission.prototype.createNodeDomElement = function(nodeObj) {
var txt; var txt;
switch (nodeObj.type) { switch (nodeObj.type) {
case NodeTypes.Core: case NodeTypes.Core:
txt = "<p>CPU Core<br>" + "HP: " + txt = "CPU Core<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
nodeDiv.classList.add("hack-mission-cpu-node"); nodeDiv.classList.add("hack-mission-cpu-node");
break; break;
case NodeTypes.Firewall: case NodeTypes.Firewall:
txt = "<p>Firewall<br>" + "HP: " + txt = "Firewall<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
nodeDiv.classList.add("hack-mission-firewall-node"); nodeDiv.classList.add("hack-mission-firewall-node");
break; break;
case NodeTypes.Database: case NodeTypes.Database:
txt = "<p>Database<br>" + "HP: " + txt = "Database<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
nodeDiv.classList.add("hack-mission-database-node"); nodeDiv.classList.add("hack-mission-database-node");
break; break;
case NodeTypes.Spam: case NodeTypes.Spam:
txt = "<p>Spam<br>" + "HP: " + txt = "Spam<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
nodeDiv.classList.add("hack-mission-spam-node"); nodeDiv.classList.add("hack-mission-spam-node");
break; break;
case NodeTypes.Transfer: case NodeTypes.Transfer:
txt = "<p>Transfer<br>" + "HP: " + txt = "Transfer<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
nodeDiv.classList.add("hack-mission-transfer-node"); nodeDiv.classList.add("hack-mission-transfer-node");
break; break;
case NodeTypes.Shield: case NodeTypes.Shield:
default: default:
txt = "<p>Shield<br>" + "HP: " + txt = "Shield<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
nodeDiv.classList.add("hack-mission-shield-node"); nodeDiv.classList.add("hack-mission-shield-node");
break; break;
} }
txt += "<br>Atk: " + formatNumber(nodeObj.atk, 1) + txt += "<br>Atk: " + formatNumber(nodeObj.atk, 1) +
"<br>Def: " + formatNumber(nodeObj.def, 1) + "</p>"; "<br>Def: " + formatNumber(nodeObj.def, 1);
nodeDiv.innerHTML = txt; txtEl.innerHTML = txt;
nodeDiv.appendChild(txtEl);
document.getElementById("hacking-mission-map").appendChild(nodeDiv);
} }
HackingMission.prototype.updateNodeDomElement = function(nodeObj) { HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
@ -739,36 +741,35 @@ HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
return; return;
} }
var nodeDiv = document.getElementById("hacking-mission-node-" + var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
nodeObj.pos[0] + "-" + var nodeDiv = document.getElementById(id), txtEl = document.getElementById(id + "-txt");
nodeObj.pos[1]);
//Set node classes based on type //Set node classes based on type
var txt; var txt;
switch (nodeObj.type) { switch (nodeObj.type) {
case NodeTypes.Core: case NodeTypes.Core:
txt = "<p>CPU Core<br>" + "HP: " + txt = "CPU Core<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
break; break;
case NodeTypes.Firewall: case NodeTypes.Firewall:
txt = "<p>Firewall<br>" + "HP: " + txt = "Firewall<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
break; break;
case NodeTypes.Database: case NodeTypes.Database:
txt = "<p>Database<br>" + "HP: " + txt = "Database<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
break; break;
case NodeTypes.Spam: case NodeTypes.Spam:
txt = "<p>Spam<br>" + "HP: " + txt = "Spam<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
break; break;
case NodeTypes.Transfer: case NodeTypes.Transfer:
txt = "<p>Transfer<br>" + "HP: " + txt = "Transfer<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
break; break;
case NodeTypes.Shield: case NodeTypes.Shield:
default: default:
txt = "<p>Shield<br>" + "HP: " + txt = "Shield<br>" + "HP: " +
formatNumber(nodeObj.hp, 1); formatNumber(nodeObj.hp, 1);
break; break;
} }
@ -778,8 +779,7 @@ HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
if (nodeObj.action) { if (nodeObj.action) {
txt += "<br>" + nodeObj.action; txt += "<br>" + nodeObj.action;
} }
txt += "</p>"; txtEl.innerHTML = txt;
nodeDiv.innerHTML = txt;
} }
//Gets a Node DOM element's corresponding Node object using its //Gets a Node DOM element's corresponding Node object using its
@ -806,6 +806,19 @@ HackingMission.prototype.getNodeFromElement = function(el) {
return this.map[x][y]; return this.map[x][y];
} }
function selectNode(hackMissionInst, el) {
var nodeObj = hackMissionInst.getNodeFromElement(el);
if (nodeObj === null) {console.log("Error getting Node object");}
if (!nodeObj.plyrCtrl) {return;}
if (hackMissionInst.selectedNode instanceof Node) {
hackMissionInst.selectedNode.deselect(hackMissionInst.actionButtons);
hackMissionInst.selectedNode = null;
}
nodeObj.select(hackMissionInst.actionButtons);
hackMissionInst.selectedNode = nodeObj;
}
//Configures a DOM element representing a player-owned node to //Configures a DOM element representing a player-owned node to
//be selectable and actionable //be selectable and actionable
//Note: Does NOT change its css class. This is handled by Node.setControlledBy... //Note: Does NOT change its css class. This is handled by Node.setControlledBy...
@ -814,15 +827,15 @@ HackingMission.prototype.configurePlayerNodeElement = function(el) {
if (nodeObj === null) {console.log("Error getting Node object");} if (nodeObj === null) {console.log("Error getting Node object");}
//Add event listener //Add event listener
el.addEventListener("click", ()=>{ var self = this;
if (this.selectedNode instanceof Node) { function selectNodeWrapper() {
this.selectedNode.deselect(this.actionButtons); selectNode(self, el);
this.selectedNode = null; }
el.addEventListener("click", selectNodeWrapper);
if (el.firstChild) {
el.firstChild.addEventListener("click", selectNodeWrapper);
} }
console.log("Selecting node :" + el.id);
nodeObj.select(this.actionButtons);
this.selectedNode = nodeObj;
});
} }
//Configures a DOM element representing an enemy-node by removing //Configures a DOM element representing an enemy-node by removing
@ -833,8 +846,6 @@ HackingMission.prototype.configureEnemyNodeElement = function(el) {
if (this.selectedNode == nodeObj) { if (this.selectedNode == nodeObj) {
nodeObj.deselect(this.actionButtons); nodeObj.deselect(this.actionButtons);
} }
//TODO Need to remove event listeners
} }
//Returns bool indicating whether a node is reachable by player //Returns bool indicating whether a node is reachable by player
@ -1134,8 +1145,8 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
} }
//The conquered node has its stats reduced //The conquered node has its stats reduced
targetNode.atk /= 3; targetNode.atk /= 2;
targetNode.def /= 3; targetNode.def /= 3.5;
//Flag for whether the target node was a misc node //Flag for whether the target node was a misc node
var isMiscNode = !targetNode.plyrCtrl && !targetNode.enmyCtrl; var isMiscNode = !targetNode.plyrCtrl && !targetNode.enmyCtrl;
@ -1356,11 +1367,11 @@ var hackEffWeightAttack = 80; //Weight for Attack action
//Returns damage per cycle based on stats //Returns damage per cycle based on stats
HackingMission.prototype.calculateAttackDamage = function(atk, def, hacking = 0) { HackingMission.prototype.calculateAttackDamage = function(atk, def, hacking = 0) {
return Math.max(atk + (hacking / hackEffWeightAttack) - def, 1); return Math.max(0.75 * (atk + (hacking / hackEffWeightAttack) - def), 1);
} }
HackingMission.prototype.calculateScanEffect = function(atk, def, hacking=0) { HackingMission.prototype.calculateScanEffect = function(atk, def, hacking=0) {
return Math.max(0.85 * ((atk) + hacking / hackEffWeightTarget - (def * 0.95)), 2); return Math.max(0.7 * ((atk) + hacking / hackEffWeightTarget - (def * 0.95)), 2);
} }
HackingMission.prototype.calculateWeakenEffect = function(atk, def, hacking=0) { HackingMission.prototype.calculateWeakenEffect = function(atk, def, hacking=0) {

@ -97,16 +97,20 @@ function evaluate(exp, workerScript) {
//Create a new WorkerScript for this function evaluation //Create a new WorkerScript for this function evaluation
var funcWorkerScript = new WorkerScript(workerScript.scriptRef); var funcWorkerScript = new WorkerScript(workerScript.scriptRef);
funcWorkerScript.serverIp = workerScript.serverIp;
funcWorkerScript.env = funcEnv; funcWorkerScript.env = funcEnv;
workerScript.fnWorker = funcWorkerScript;
evaluate(func.body, funcWorkerScript).then(function(res) { evaluate(func.body, funcWorkerScript).then(function(res) {
//If the function finished successfuly, that means there //If the function finished successfuly, that means there
//was no return statement since a return statement rejects. So resolve to null //was no return statement since a return statement rejects. So resolve to null
resolve(null); resolve(null);
workerScript.fnWorker = null;
}).catch(function(e) { }).catch(function(e) {
if (e.constructor === Array && e.length === 2 && e[0] === "RETURNSTATEMENT") { if (e.constructor === Array && e.length === 2 && e[0] === "RETURNSTATEMENT") {
//Return statement from function //Return statement from function
resolve(e[1]); resolve(e[1]);
workerScript.fnWorker = null;
} else if (isString(e)) { } else if (isString(e)) {
reject(makeRuntimeRejectMsg(workerScript, e)); reject(makeRuntimeRejectMsg(workerScript, e));
} else if (e instanceof WorkerScript) { } else if (e instanceof WorkerScript) {
@ -116,9 +120,13 @@ function evaluate(exp, workerScript) {
if (errorTextArray.length === 4) { if (errorTextArray.length === 4) {
errorMsg = errorTextArray[3]; errorMsg = errorTextArray[3];
reject(makeRuntimeRejectMsg(workerScript, errorMsg)); reject(makeRuntimeRejectMsg(workerScript, errorMsg));
} else {
if (env.stopFlag) {
reject(workerScript);
} else { } else {
reject(makeRuntimeRejectMsg(workerScript, "Error in one of your functions. Could not identify which function")); reject(makeRuntimeRejectMsg(workerScript, "Error in one of your functions. Could not identify which function"));
} }
}
} else if (e instanceof Error) { } else if (e instanceof Error) {
reject(makeRuntimeRejectMsg(workerScript, e.toString())); reject(makeRuntimeRejectMsg(workerScript, e.toString()));
} }

@ -1,8 +1,23 @@
var sprintf = require('sprintf-js').sprintf,
vsprintf = require('sprintf-js').vsprintf
import {updateActiveScriptsItems} from "./ActiveScriptsUI.js"; import {updateActiveScriptsItems} from "./ActiveScriptsUI.js";
import {Augmentations, Augmentation, import {Augmentations, Augmentation,
augmentationExists, installAugmentations, augmentationExists, installAugmentations,
AugmentationNames} from "./Augmentations.js"; AugmentationNames} from "./Augmentations.js";
import {BitNodeMultipliers} from "./BitNode.js"; import {BitNodeMultipliers} from "./BitNode.js";
import {commitShopliftCrime, commitRobStoreCrime, commitMugCrime,
commitLarcenyCrime, commitDealDrugsCrime, commitBondForgeryCrime,
commitTraffickArmsCrime,
commitHomicideCrime, commitGrandTheftAutoCrime, commitKidnapCrime,
commitAssassinationCrime, commitHeistCrime, determineCrimeSuccess,
determineCrimeChanceShoplift, determineCrimeChanceRobStore,
determineCrimeChanceMug, determineCrimeChanceLarceny,
determineCrimeChanceDealDrugs, determineCrimeChanceBondForgery,
determineCrimeChanceTraffickArms,
determineCrimeChanceHomicide, determineCrimeChanceGrandTheftAuto,
determineCrimeChanceKidnap, determineCrimeChanceAssassination,
determineCrimeChanceHeist} from "./Crimes.js";
import {Companies, Company, CompanyPosition, import {Companies, Company, CompanyPosition,
CompanyPositions, companyExists} from "./Company.js"; CompanyPositions, companyExists} from "./Company.js";
import {CONSTANTS} from "./Constants.js"; import {CONSTANTS} from "./Constants.js";
@ -28,6 +43,7 @@ import {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
updateStockTicker, updateStockPlayerPosition, updateStockTicker, updateStockPlayerPosition,
Stock} from "./StockMarket.js"; Stock} from "./StockMarket.js";
import {post} from "./Terminal.js"; import {post} from "./Terminal.js";
import {TextFile, getTextFile, createTextFile} from "./TextFile.js";
import {WorkerScript, workerScripts, import {WorkerScript, workerScripts,
killWorkerScript, NetscriptPorts} from "./NetscriptWorker.js"; killWorkerScript, NetscriptPorts} from "./NetscriptWorker.js";
@ -67,6 +83,8 @@ function NetscriptFunctions(workerScript) {
Math : Math, Math : Math,
Date : Date, Date : Date,
hacknetnodes : Player.hacknetNodes, hacknetnodes : Player.hacknetNodes,
sprintf : sprintf,
vsprintf: vsprintf,
scan : function(ip=workerScript.serverIp, hostnames=true){ scan : function(ip=workerScript.serverIp, hostnames=true){
var server = getServer(ip); var server = getServer(ip);
if (server == null) { if (server == null) {
@ -662,14 +680,6 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getHackingLevel() returned " + Player.hacking_skill); workerScript.scriptRef.log("getHackingLevel() returned " + Player.hacking_skill);
return Player.hacking_skill; return Player.hacking_skill;
}, },
getIntelligence : function () {
if (!hasAISF) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getIntelligence(). It requires Source-File 5 to run.");
}
Player.updateSkillLevels();
workerScript.scriptRef.log("getHackingLevel() returned " + Player.intelligence);
return Player.intelligence;
},
getHackingMultipliers : function() { getHackingMultipliers : function() {
return { return {
chance: Player.hacking_chance_mult, chance: Player.hacking_chance_mult,
@ -845,12 +855,12 @@ function NetscriptFunctions(workerScript) {
if (stock == null) { if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()"); throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()");
} }
if (shares == 0) {return false;}
if (stock == null || shares < 0 || isNaN(shares)) { if (stock == null || shares < 0 || isNaN(shares)) {
workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to buyStock()"); workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to buyStock()");
return false; return false;
} }
shares = Math.round(shares); shares = Math.round(shares);
if (shares === 0) {return false;}
var totalPrice = stock.price * shares; var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) { if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
@ -880,13 +890,14 @@ function NetscriptFunctions(workerScript) {
if (stock == null) { if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()"); throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()");
} }
if (shares == 0) {return false;}
if (stock == null || shares < 0 || isNaN(shares)) { if (stock == null || shares < 0 || isNaN(shares)) {
workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to sellStock()"); workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to sellStock()");
return false; return false;
} }
shares = Math.round(shares);
if (shares > stock.playerShares) {shares = stock.playerShares;} if (shares > stock.playerShares) {shares = stock.playerShares;}
if (shares == 0) {return false;} if (shares === 0) {return false;}
var gains = stock.price * shares - CONSTANTS.StockMarketCommission; var gains = stock.price * shares - CONSTANTS.StockMarketCommission;
Player.gainMoney(gains); Player.gainMoney(gains);
@ -1026,8 +1037,8 @@ function NetscriptFunctions(workerScript) {
if (isNaN(n)) {return 0;} if (isNaN(n)) {return 0;}
return Math.round(n); return Math.round(n);
}, },
write : function(port, data="") { write : function(port, data="", mode="a") {
if (!isNaN(port)) { if (!isNaN(port)) { //Write to port
//Port 1-10 //Port 1-10
if (port < 1 || port > 10) { if (port < 1 || port > 10) {
throw makeRuntimeRejectMsg(workerScript, "Trying to write to invalid port: " + port + ". Only ports 1-10 are valid."); throw makeRuntimeRejectMsg(workerScript, "Trying to write to invalid port: " + port + ". Only ports 1-10 are valid.");
@ -1043,12 +1054,29 @@ function NetscriptFunctions(workerScript) {
return true; return true;
} }
return false; return false;
} else if (isString(port)) { //Write to text file
var fn = port;
var server = getServer(workerScript.serverIp);
if (server === null) {
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in write(). This is a bug please contact game dev");
}
var txtFile = getTextFile(fn, server);
if (txtFile === null) {
txtFile = createTextFile(fn, data, server);
return true;
}
if (mode === "w") {
txtFile.write(data);
} else {
txtFile.append(data);
}
return true;
} else { } else {
throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for port: " + port + ". Must be a number between 1 and 10"); throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for port: " + port + ". Must be a number between 1 and 10");
} }
}, },
read : function(port) { read : function(port) {
if (!isNaN(port)) { if (!isNaN(port)) { //Read from port
//Port 1-10 //Port 1-10
if (port < 1 || port > 10) { if (port < 1 || port > 10) {
throw makeRuntimeRejectMsg(workerScript, "Trying to write to invalid port: " + port + ". Only ports 1-10 are valid."); throw makeRuntimeRejectMsg(workerScript, "Trying to write to invalid port: " + port + ". Only ports 1-10 are valid.");
@ -1063,6 +1091,18 @@ function NetscriptFunctions(workerScript) {
} else { } else {
return port.shift(); return port.shift();
} }
} else if (isString(port)) { //Read from text file
var fn = port;
var server = getServer(workerScript.serverIp);
if (server === null) {
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in read(). This is a bug please contact game dev");
}
var txtFile = getTextFile(fn, server);
if (txtFile !== null) {
return txtFile.text;
} else {
return "";
}
} else { } else {
throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for port: " + port + ". Must be a number between 1 and 10"); throw makeRuntimeRejectMsg(workerScript, "Invalid argument passed in for port: " + port + ". Must be a number between 1 and 10");
} }
@ -1513,6 +1553,33 @@ function NetscriptFunctions(workerScript) {
} }
return true; return true;
}, },
getStats : function() {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 1)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getStats(). It is a Singularity Function and requires SourceFile-4 (level 1) to run.");
return {};
}
}
return {
hacking: Player.hacking_skill,
strength: Player.strength,
defense: Player.defense,
dexterity: Player.dexterity,
agility: Player.agility,
charisma: Player.charisma,
intelligence: Player.intelligence
}
},
isBusy : function() {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 1)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run isBusy(). It is a Singularity Function and requires SourceFile-4 (level 1) to run.");
return;
}
}
return Player.isWorking;
},
upgradeHomeRam() { upgradeHomeRam() {
if (Player.bitNodeN != 4) { if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 2)) { if (!(hasSingularitySF && singularitySFLvl >= 2)) {
@ -1899,6 +1966,97 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("Began creating program: " + name); workerScript.scriptRef.log("Began creating program: " + name);
return true; return true;
}, },
commitCrime(crime) {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 3)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run commitCrime(). It is a Singularity Function and requires SourceFile-4 (level 3) to run.");
return;
}
}
if (Player.isWorking) {
var txt = Player.singularityStopWork();
workerScript.scriptRef.log(txt);
}
crime = crime.toLowerCase();
if (crime.includes("shoplift")) {
workerScript.scriptRef.log("Attempting to shoplift...");
return commitShopliftCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("rob") && crime.includes("store")) {
workerScript.scriptRef.log("Attempting to rob a store...");
return commitRobStoreCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("mug")) {
workerScript.scriptRef.log("Attempting to mug someone...");
return commitMugCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("larceny")) {
workerScript.scriptRef.log("Attempting to commit larceny...");
return commitLarcenyCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("drugs")) {
workerScript.scriptRef.log("Attempting to deal drugs...");
return commitDealDrugsCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("bond") && crime.includes("forge")) {
workerScript.scriptRef.log("Attempting to forge corporate bonds...");
return commitBondForgeryCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("traffick") && crime.includes("arms")) {
workerScript.scriptRef.log("Attempting to traffick illegal arms...");
return commitTraffickArmsCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("homicide")) {
workerScript.scriptRef.log("Attempting to commit homicide...");
return commitHomicideCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("grand") && crime.includes("auto")) {
workerScript.scriptRef.log("Attempting to commit grand theft auto...");
return commitGrandTheftAutoCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("kidnap")) {
workerScript.scriptRef.log("Attempting to kidnap and ransom a high-profile target...");
return commitKidnapCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else if (crime.includes("assassinate")) {
workerScript.scriptRef.log("Attempting to assassinate a high-profile target...");
return commitAssassinationCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript})
} else if (crime.includes("heist")) {
workerScript.scriptRef.log("Attempting to pull off a heist...");
return commitHeistCrime(CONSTANTS.CrimeSingFnDivider, {workerscript: workerScript});
} else {
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into commitCrime(): " + crime);
}
},
getCrimeChance(crime) {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 3)) {
throw makeRuntimeRejectMsg(workerScript, "Cannot run getCrimeChance(). It is a Singularity Function and requires SourceFile-4 (level 3) to run.");
return;
}
}
crime = crime.toLowerCase();
if (crime.includes("shoplift")) {
return determineCrimeChanceShoplift();
} else if (crime.includes("rob") && crime.includes("store")) {
return determineCrimeChanceRobStore();
} else if (crime.includes("mug")) {
return determineCrimeChanceMug();
} else if (crime.includes("larceny")) {
return determineCrimeChanceLarceny();
} else if (crime.includes("drugs")) {
return determineCrimeChanceDealDrugs();
} else if (crime.includes("bond") && crime.includes("forge")) {
return determineCrimeChanceBondForgery();
} else if (crime.includes("traffick") && crime.includes("arms")) {
return determineCrimeChanceTraffickArms();
} else if (crime.includes("homicide")) {
return determineCrimeChanceHomicide();
} else if (crime.includes("grand") && crime.includes("auto")) {
return determineCrimeChanceGrandTheftAuto();
} else if (crime.includes("kidnap")) {
return determineCrimeChanceKidnap();
} else if (crime.includes("assassinate")) {
return determineCrimeChanceAssassination();
} else if (crime.includes("heist")) {
return determineCrimeChanceHeist();
} else {
throw makeRuntimeRejectMsg(workerScript, "Invalid crime passed into getCrimeChance(): " + crime);
}
},
getOwnedAugmentations(purchased=false) { getOwnedAugmentations(purchased=false) {
if (Player.bitNodeN != 4) { if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 3)) { if (!(hasSingularitySF && singularitySFLvl >= 3)) {

@ -25,6 +25,7 @@ function WorkerScript(runningScriptObj) {
this.errorMessage = ""; this.errorMessage = "";
this.args = runningScriptObj.args; this.args = runningScriptObj.args;
this.killTrigger = function() {}; //CB func used to clear any delays (netscriptDelay()) this.killTrigger = function() {}; //CB func used to clear any delays (netscriptDelay())
this.fnWorker = null; //Workerscript for a function call
} }
//Returns the server on which the workerScript is running //Returns the server on which the workerScript is running
@ -165,6 +166,10 @@ function killWorkerScript(runningScriptObj, serverIp) {
compareArrays(workerScripts[i].args, runningScriptObj.args)) { compareArrays(workerScripts[i].args, runningScriptObj.args)) {
workerScripts[i].env.stopFlag = true; workerScripts[i].env.stopFlag = true;
workerScripts[i].killTrigger(); workerScripts[i].killTrigger();
if (workerScripts[i].fnWorker) {
workerScripts[i].fnWorker.env.stopFlag = true;
workerScripts[i].fnWorker.killTrigger();
}
return true; return true;
} }
} }

@ -24,6 +24,7 @@ import {clearEventListeners} from "../utils/HelperFunctions.j
import {createRandomIp} from "../utils/IPAddress.js"; import {createRandomIp} from "../utils/IPAddress.js";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js"; Generic_fromJSON} from "../utils/JSONReviver.js";
import numeral from "../utils/numeral.min.js";
import {formatNumber, import {formatNumber,
convertTimeMsToTimeElapsedString} from "../utils/StringHelperFunctions.js"; convertTimeMsToTimeElapsedString} from "../utils/StringHelperFunctions.js";
@ -1141,7 +1142,7 @@ PlayerObject.prototype.getFactionSecurityWorkRepGain = function() {
this.strength / CONSTANTS.MaxSkillLevel + this.strength / CONSTANTS.MaxSkillLevel +
this.defense / CONSTANTS.MaxSkillLevel + this.defense / CONSTANTS.MaxSkillLevel +
this.dexterity / CONSTANTS.MaxSkillLevel + this.dexterity / CONSTANTS.MaxSkillLevel +
this.agility / CONSTANTS.MaxSkillLevel) / 5; this.agility / CONSTANTS.MaxSkillLevel) / 4.5;
return t * this.faction_rep_mult; return t * this.faction_rep_mult;
} }
@ -1152,7 +1153,7 @@ PlayerObject.prototype.getFactionFieldWorkRepGain = function() {
this.dexterity / CONSTANTS.MaxSkillLevel + this.dexterity / CONSTANTS.MaxSkillLevel +
this.agility / CONSTANTS.MaxSkillLevel + this.agility / CONSTANTS.MaxSkillLevel +
this.charisma / CONSTANTS.MaxSkillLevel + this.charisma / CONSTANTS.MaxSkillLevel +
this.intelligence / CONSTANTS.MaxSkillLevel) / 6; this.intelligence / CONSTANTS.MaxSkillLevel) / 5.5;
return t * this.faction_rep_mult; return t * this.faction_rep_mult;
} }
@ -1408,11 +1409,16 @@ PlayerObject.prototype.finishClass = function(sing=false) {
} }
//The EXP and $ gains are hardcoded. Time is in ms //The EXP and $ gains are hardcoded. Time is in ms
PlayerObject.prototype.startCrime = function(hackExp, strExp, defExp, dexExp, agiExp, chaExp, money, time) { PlayerObject.prototype.startCrime = function(hackExp, strExp, defExp, dexExp, agiExp, chaExp, money, time, singParams=null) {
this.resetWorkStatus(); this.resetWorkStatus();
this.isWorking = true; this.isWorking = true;
this.workType = CONSTANTS.WorkTypeCrime; this.workType = CONSTANTS.WorkTypeCrime;
if (singParams && singParams.workerscript) {
this.committingCrimeThruSingFn = true;
this.singFnCrimeWorkerScript = singParams.workerscript;
}
this.workHackExpGained = hackExp * this.hacking_exp_mult * BitNodeMultipliers.CrimeExpGain; this.workHackExpGained = hackExp * this.hacking_exp_mult * BitNodeMultipliers.CrimeExpGain;
this.workStrExpGained = strExp * this.strength_exp_mult * BitNodeMultipliers.CrimeExpGain; this.workStrExpGained = strExp * this.strength_exp_mult * BitNodeMultipliers.CrimeExpGain;
this.workDefExpGained = defExp * this.defense_exp_mult * BitNodeMultipliers.CrimeExpGain; this.workDefExpGained = defExp * this.defense_exp_mult * BitNodeMultipliers.CrimeExpGain;
@ -1476,6 +1482,10 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
case CONSTANTS.CrimeDrugs: case CONSTANTS.CrimeDrugs:
this.karma -= 0.5; this.karma -= 0.5;
break; break;
case CONSTANTS.CrimeBondForgery:
this.karma -= 0.1;
this.gainIntelligenceExp(2 * CONSTANTS.IntelligenceCrimeBaseExpGain);
break;
case CONSTANTS.CrimeTraffickArms: case CONSTANTS.CrimeTraffickArms:
this.karma -= 1; this.karma -= 1;
break; break;
@ -1513,7 +1523,16 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
this.workDexExpGained *= 2; this.workDexExpGained *= 2;
this.workAgiExpGained *= 2; this.workAgiExpGained *= 2;
this.workChaExpGained *= 2; this.workChaExpGained *= 2;
if (this.committingCrimeThruSingFn) {
this.singFnCrimeWorkerScript.scriptRef.log("Crime successful! Gained " +
numeral(this.workMoneyGained).format("$0.000a") + ", " +
formatNumber(this.workHackExpGained, 3) + " hack exp, " +
formatNumber(this.workStrExpGained, 3) + " str exp, " +
formatNumber(this.workDefExpGained, 3) + " def exp, " +
formatNumber(this.workDexExpGained, 3) + " dex exp, " +
formatNumber(this.workAgiExpGained, 3) + " agi exp, " +
formatNumber(this.workChaExpGained, 3) + " cha exp.");
} else {
dialogBoxCreate("Crime successful! <br><br>" + dialogBoxCreate("Crime successful! <br><br>" +
"You gained:<br>"+ "You gained:<br>"+
"$" + formatNumber(this.workMoneyGained, 2) + "<br>" + "$" + formatNumber(this.workMoneyGained, 2) + "<br>" +
@ -1523,6 +1542,8 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
formatNumber(this.workDexExpGained, 4) + " dexterity experience<br>" + formatNumber(this.workDexExpGained, 4) + " dexterity experience<br>" +
formatNumber(this.workAgiExpGained, 4) + " agility experience<br>" + formatNumber(this.workAgiExpGained, 4) + " agility experience<br>" +
formatNumber(this.workChaExpGained, 4) + " charisma experience"); formatNumber(this.workChaExpGained, 4) + " charisma experience");
}
} else { } else {
//Exp halved on failure //Exp halved on failure
this.workHackExpGained /= 2; this.workHackExpGained /= 2;
@ -1531,7 +1552,15 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
this.workDexExpGained /= 2; this.workDexExpGained /= 2;
this.workAgiExpGained /= 2; this.workAgiExpGained /= 2;
this.workChaExpGained /= 2; this.workChaExpGained /= 2;
if (this.committingCrimeThruSingFn) {
this.singFnCrimeWorkerScript.scriptRef.log("Crime failed! Gained " +
formatNumber(this.workHackExpGained, 3) + " hack exp, " +
formatNumber(this.workStrExpGained, 3) + " str exp, " +
formatNumber(this.workDefExpGained, 3) + " def exp, " +
formatNumber(this.workDexExpGained, 3) + " dex exp, " +
formatNumber(this.workAgiExpGained, 3) + " agi exp, " +
formatNumber(this.workChaExpGained, 3) + " chaexp.");
} else {
dialogBoxCreate("Crime failed! <br><br>" + dialogBoxCreate("Crime failed! <br><br>" +
"You gained:<br>"+ "You gained:<br>"+
formatNumber(this.workHackExpGained, 4) + " hacking experience <br>" + formatNumber(this.workHackExpGained, 4) + " hacking experience <br>" +
@ -1541,10 +1570,12 @@ PlayerObject.prototype.finishCrime = function(cancelled) {
formatNumber(this.workAgiExpGained, 4) + " agility experience<br>" + formatNumber(this.workAgiExpGained, 4) + " agility experience<br>" +
formatNumber(this.workChaExpGained, 4) + " charisma experience"); formatNumber(this.workChaExpGained, 4) + " charisma experience");
} }
}
this.gainWorkExp(); this.gainWorkExp();
} }
this.committingCrimeThruSingFn = false;
this.singFnCrimeWorkerScript = null;
var mainMenu = document.getElementById("mainmenu-container"); var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible"; mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
@ -1572,6 +1603,9 @@ PlayerObject.prototype.singularityStopWork = function() {
case CONSTANTS.WorkTypeCreateProgram: case CONSTANTS.WorkTypeCreateProgram:
res = this.finishCreateProgramWork(true, true); res = this.finishCreateProgramWork(true, true);
break; break;
case CONSTANTS.WorkTypeCrime:
res = this.finishCrime(true);
break;
default: default:
console.log("ERROR: Unrecognized work type"); console.log("ERROR: Unrecognized work type");
return ""; return "";

@ -43,12 +43,11 @@ function BitburnerSaveObject() {
this.VersionSave = ""; this.VersionSave = "";
} }
BitburnerSaveObject.prototype.saveGame = function() { BitburnerSaveObject.prototype.saveGame = function(db) {
this.PlayerSave = JSON.stringify(Player); this.PlayerSave = JSON.stringify(Player);
//Delete all logs from all running scripts //Delete all logs from all running scripts
var TempAllServers = JSON.parse(JSON.stringify(AllServers), Reviver); var TempAllServers = JSON.parse(JSON.stringify(AllServers), Reviver);
//var TempAllServers = jQuery.extend(true, {}, AllServers); //Deep copy
for (var ip in TempAllServers) { for (var ip in TempAllServers) {
var server = TempAllServers[ip]; var server = TempAllServers[ip];
if (server == null) {continue;} if (server == null) {continue;}
@ -73,28 +72,49 @@ BitburnerSaveObject.prototype.saveGame = function() {
this.AllGangsSave = JSON.stringify(AllGangs); this.AllGangsSave = JSON.stringify(AllGangs);
} }
var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this)))); var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this))));
//We'll save to both localstorage and indexedDb
var objectStore = db.transaction(["savestring"], "readwrite").objectStore("savestring");
var request = objectStore.put(saveString, "save");
request.onerror = function(e) {
console.log("Error saving game to IndexedDB: " + e);
}
request.onsuccess = function(e) {
console.log("Successfully saved game to IndexedDB!");
}
try { try {
window.localStorage.setItem("bitburnerSave", saveString); window.localStorage.setItem("bitburnerSave", saveString);
} catch(e) { } catch(e) {
if (e.code == 22) { if (e.code == 22) {
dialogBoxCreate("Failed to save game because the size of the save file " + console.log("Failed to save game to localStorage because the size of the save file " +
"is too large. Consider killing several of your scripts to " + "is too large. However, the game will still be saved to IndexedDb if your browser " +
"supports it. If you would like to save to localStorage as well, then " +
"consider killing several of your scripts to " +
"fix this, or increasing the size of your browsers localStorage"); "fix this, or increasing the size of your browsers localStorage");
} }
} }
console.log("Game saved!"); console.log("Game saved!");
Engine.createStatusText("Game saved!"); Engine.createStatusText("Game saved!");
} }
function loadGame(saveObj) { function loadGame(saveString) {
if (saveString === "" || saveString === null || saveString === undefined) {
if (!window.localStorage.getItem("bitburnerSave")) { if (!window.localStorage.getItem("bitburnerSave")) {
console.log("No save file to load"); console.log("No save file to load");
return false; return false;
} }
var saveString = decodeURIComponent(escape(atob(window.localStorage.getItem("bitburnerSave")))); saveString = decodeURIComponent(escape(atob(window.localStorage.getItem("bitburnerSave"))));
saveObj = JSON.parse(saveString, Reviver); console.log("Loading game from localStorage");
} else {
saveString = decodeURIComponent(escape(atob(saveString)));
console.log("Loading game from IndexedDB");
}
var saveObj = JSON.parse(saveString, Reviver);
loadPlayer(saveObj.PlayerSave); loadPlayer(saveObj.PlayerSave);
loadAllServers(saveObj.AllServersSave); loadAllServers(saveObj.AllServersSave);
@ -504,10 +524,20 @@ BitburnerSaveObject.prototype.importGame = function() {
} }
BitburnerSaveObject.prototype.deleteGame = function() { BitburnerSaveObject.prototype.deleteGame = function(db) {
//Delete from local storage
if (window.localStorage.getItem("bitburnerSave")) { if (window.localStorage.getItem("bitburnerSave")) {
window.localStorage.removeItem("bitburnerSave"); window.localStorage.removeItem("bitburnerSave");
} }
//Delete from indexedDB
var request = db.transaction(["savestring"], "readwrite").objectStore("savestring").delete("save");
request.onsuccess = function(e) {
console.log("Successfully deleted save from indexedDb");
}
request.onerror = function(e) {
console.log("Failed to delete save from indexedDb: " + e);
}
Engine.createStatusText("Game deleted!"); Engine.createStatusText("Game deleted!");
} }

@ -263,8 +263,7 @@ function calculateRamUsage(codeCopy) {
var hasRootAccessCount = numOccurrences(codeCopy, "hasRootAccess("); var hasRootAccessCount = numOccurrences(codeCopy, "hasRootAccess(");
var getHostnameCount = numOccurrences(codeCopy, "getHostname(") + var getHostnameCount = numOccurrences(codeCopy, "getHostname(") +
numOccurrences(codeCopy, "getIp("); numOccurrences(codeCopy, "getIp(");
var getHackingLevelCount = numOccurrences(codeCopy, "getHackingLevel(") + var getHackingLevelCount = numOccurrences(codeCopy, "getHackingLevel(");
numOccurrences(codeCopy, "getIntelligence(");
var getMultipliersCount = numOccurrences(codeCopy, "getHackingMultipliers(") + var getMultipliersCount = numOccurrences(codeCopy, "getHackingMultipliers(") +
numOccurrences(codeCopy, "getBitNodeMultipliers("); numOccurrences(codeCopy, "getBitNodeMultipliers(");
var getServerCount = numOccurrences(codeCopy, "getServerMoneyAvailable(") + var getServerCount = numOccurrences(codeCopy, "getServerMoneyAvailable(") +
@ -307,7 +306,8 @@ function calculateRamUsage(codeCopy) {
numOccurrences(codeCopy, "gymWorkout(") + numOccurrences(codeCopy, "gymWorkout(") +
numOccurrences(codeCopy, "travelToCity(") + numOccurrences(codeCopy, "travelToCity(") +
numOccurrences(codeCopy, "purchaseTor(") + numOccurrences(codeCopy, "purchaseTor(") +
numOccurrences(codeCopy, "purchaseProgram("); numOccurrences(codeCopy, "purchaseProgram(") +
numOccurrences(codeCopy, "getStats(");
var singFn2Count = numOccurrences(codeCopy, "upgradeHomeRam(") + var singFn2Count = numOccurrences(codeCopy, "upgradeHomeRam(") +
numOccurrences(codeCopy, "getUpgradeHomeRamCost(") + numOccurrences(codeCopy, "getUpgradeHomeRamCost(") +
numOccurrences(codeCopy, "workForCompany(") + numOccurrences(codeCopy, "workForCompany(") +
@ -318,6 +318,9 @@ function calculateRamUsage(codeCopy) {
numOccurrences(codeCopy, "workForFaction(") + numOccurrences(codeCopy, "workForFaction(") +
numOccurrences(codeCopy, "getFactionRep("); numOccurrences(codeCopy, "getFactionRep(");
var singFn3Count = numOccurrences(codeCopy, "createProgram(") + var singFn3Count = numOccurrences(codeCopy, "createProgram(") +
numOccurrences(codeCopy, "commitCrime(") +
numOccurrences(codeCopy, "getOwnedAugmentations(") +
numOccurrences(codeCopy, "getAugmentationsFromFaction(") +
numOccurrences(codeCopy, "getAugmentationCost(") + numOccurrences(codeCopy, "getAugmentationCost(") +
numOccurrences(codeCopy, "purchaseAugmentation(") + numOccurrences(codeCopy, "purchaseAugmentation(") +
numOccurrences(codeCopy, "installAugmentations("); numOccurrences(codeCopy, "installAugmentations(");

@ -15,12 +15,13 @@ function Server(ip=createRandomIp(), hostname="", organizationName="",
//Connection information //Connection information
this.ip = ip; this.ip = ip;
var i = 0; var i = 0;
while (GetServerByHostname(hostname) != null) { var suffix = "";
while (GetServerByHostname(hostname+suffix) != null) {
//Server already exists //Server already exists
hostname = hostname + "-" + i; suffix = "-" + i;
++i; ++i;
} }
this.hostname = hostname; this.hostname = hostname + suffix;
this.organizationName = organizationName; this.organizationName = organizationName;
this.isConnectedTo = isConnectedTo; //Whether the player is connected to this server this.isConnectedTo = isConnectedTo; //Whether the player is connected to this server
@ -32,12 +33,13 @@ function Server(ip=createRandomIp(), hostname="", organizationName="",
//RAM, CPU speed and Scripts //RAM, CPU speed and Scripts
this.maxRam = maxRam; //GB this.maxRam = maxRam; //GB
this.ramUsed = 0; this.ramUsed = 0;
this.cpuCores = 1; //Max of 8, affects hacking times and Hacking MIssion starting Cores this.cpuCores = 1; //Max of 8, affects hacking times and Hacking Mission starting Cores
this.scripts = []; this.scripts = [];
this.runningScripts = []; //Stores RunningScript objects this.runningScripts = []; //Stores RunningScript objects
this.programs = []; this.programs = [];
this.messages = []; this.messages = [];
this.textFiles = [];
this.dir = 0; //new Directory(this, null, ""); this.dir = 0; //new Directory(this, null, "");
/* Hacking information (only valid for "foreign" aka non-purchased servers) */ /* Hacking information (only valid for "foreign" aka non-purchased servers) */
@ -54,7 +56,6 @@ function Server(ip=createRandomIp(), hostname="", organizationName="",
this.baseDifficulty = 1; //Starting difficulty this.baseDifficulty = 1; //Starting difficulty
this.minDifficulty = 1; this.minDifficulty = 1;
this.serverGrowth = 0; //Affects how the moneyAvailable increases (0-100) this.serverGrowth = 0; //Affects how the moneyAvailable increases (0-100)
this.timesHacked = 0;
//The IP's of all servers reachable from this one (what shows up if you run scan/netstat) //The IP's of all servers reachable from this one (what shows up if you run scan/netstat)
// NOTE: Only contains IP and not the Server objects themselves // NOTE: Only contains IP and not the Server objects themselves

@ -1,12 +1,14 @@
import {CONSTANTS} from "./Constants.js"; import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js"; import {Engine} from "./engine.js";
import {Locations} from "./Location.js"; import {Locations} from "./Location.js";
import {WorkerScript} from "./NetscriptWorker.js";
import {Player} from "./Player.js"; import {Player} from "./Player.js";
import {dialogBoxCreate} from "../utils/DialogBox.js"; import {dialogBoxCreate} from "../utils/DialogBox.js";
import {clearEventListeners, getRandomInt} from "../utils/HelperFunctions.js"; import {clearEventListeners, getRandomInt} from "../utils/HelperFunctions.js";
import {Reviver, Generic_toJSON, import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js"; Generic_fromJSON} from "../utils/JSONReviver.js";
import numeral from "../utils/numeral.min.js";
import {formatNumber} from "../utils/StringHelperFunctions.js"; import {formatNumber} from "../utils/StringHelperFunctions.js";
/* StockMarket.js */ /* StockMarket.js */
@ -17,6 +19,8 @@ function Stock(name, symbol, mv, b, otlkMag, initPrice=10000) {
this.playerShares = 0; this.playerShares = 0;
this.playerAvgPx = 0; this.playerAvgPx = 0;
this.playerShortShares = 0;
this.playerAvgShortPx = 0;
this.mv = mv; this.mv = mv;
this.b = b; this.b = b;
this.otlkMag = otlkMag; this.otlkMag = otlkMag;
@ -32,6 +36,10 @@ Stock.fromJSON = function(value) {
Reviver.constructors.Stock = Stock; Reviver.constructors.Stock = Stock;
//Order types (long and short for each):
// - Limit Order
// - Stop Order
let StockMarket = {} //Full name to stock object let StockMarket = {} //Full name to stock object
let StockSymbols = {} //Full name to symbol let StockSymbols = {} //Full name to symbol
let SymbolToStockMap = {}; //Symbol to Stock object let SymbolToStockMap = {}; //Symbol to Stock object
@ -253,12 +261,12 @@ function stockMarketCycle() {
//Returns true if successful, false otherwise //Returns true if successful, false otherwise
function buyStock(stock, shares) { function buyStock(stock, shares) {
if (shares == 0) {return false;}
if (stock == null || shares < 0 || isNaN(shares)) { if (stock == null || shares < 0 || isNaN(shares)) {
dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer"); dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer");
return false; return false;
} }
shares = Math.round(shares); shares = Math.round(shares);
if (shares == 0) {return false;}
var totalPrice = stock.price * shares; var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) { if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
@ -279,7 +287,6 @@ function buyStock(stock, shares) {
return true; return true;
} }
//Returns true if successful and false otherwise //Returns true if successful and false otherwise
function sellStock(stock, shares) { function sellStock(stock, shares) {
if (shares == 0) {return false;} if (shares == 0) {return false;}
@ -287,8 +294,9 @@ function sellStock(stock, shares) {
dialogBoxCreate("Failed to sell stock. This may be a bug, contact developer"); dialogBoxCreate("Failed to sell stock. This may be a bug, contact developer");
return false; return false;
} }
shares = Math.round(shares);
if (shares > stock.playerShares) {shares = stock.playerShares;} if (shares > stock.playerShares) {shares = stock.playerShares;}
if (shares == 0) {return false;} if (shares === 0) {return false;}
var gains = stock.price * shares - CONSTANTS.StockMarketCommission; var gains = stock.price * shares - CONSTANTS.StockMarketCommission;
Player.gainMoney(gains); Player.gainMoney(gains);
stock.playerShares -= shares; stock.playerShares -= shares;
@ -302,6 +310,100 @@ function sellStock(stock, shares) {
return true; return true;
} }
//Returns true if successful and false otherwise
function shortStock(stock, shares, workerScript=null) {
var tixApi = (workerScript instanceof WorkerScript);
if (stock === null || isNaN(shares) || shares < 0) {
if (tixApi) {
workerScript.scriptRef.log("ERROR: shortStock() failed because of invalid arguments.");
} else {
dialogBoxCreate("Failed to initiate a short position in a stock. This is probably " +
"due to an invalid quantity. Otherwise, this may be a bug, so contact developer");
}
return false;
}
shares = Math.round(shares);
if (shares === 0) {return false;}
var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
if (tixApi) {
workerScript.scriptRef.log("ERROR: shortStock() failed because you do not have " +
"money to purchase this short position. You need " +
numeral(totalPrice + CONSTANTS.StockMarketCommission).format('($0.000a)'));
} else {
dialogBoxCreate("You do not have enough money to purchase this short position. You need $" +
formatNumber(totalPrice + CONSTANTS.StockMarketCommission, 2) + ".");
}
return false;
}
var origTotal = stock.playerShortShares * stock.playerAvgShortPx;
Player.loseMoney(totalPrice + CONSTANTS.StockMarketCommission);
var newTotal = origTotal + totalPrice;
stock.playerShortShares += shares;
stock.playerAvgShortPx = newTotal / stock.playerShortShares;
if (Engine.currentPage === Engine.Page.StockMarket) {
updateStockPlayerPosition(stock);
}
if (tixApi) {
workerScript.scriptRef.log("Bought a short position of " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. Paid " +
numeral(CONSTANTS.StockMarketCommission).format('($0.000a)') + " in commission fees.");
} else {
dialogBoxCreate("Bought a short position of " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at $" +
formatNumber(stock.price, 2) + " per share. You also paid $" +
formatNumber(CONSTANTS.StockMarketCommission, 2) + " in commission fees.");
}
return true;
}
//Returns true if successful and false otherwise
function sellShort(stock, shares, workerScript=null) {
var tixApi = (workerScript instanceof WorkerScript);
if (stock === null || isNaN(shares) || shares < 0) {
if (tixApi) {
workerScript.scriptRef.log("ERROR: sellShort() failed because of invalid arguments.");
} else {
dialogBoxCreate("Failed to sell a short position in a stock. This is probably " +
"due to an invalid quantity. Otherwise, this may be a bug, so contact developer");
}
return false;
}
shares = Math.round(shares);
if (shares > stock.playerShortShares) {shares = stock.playerShortShares;}
if (shares === 0) {return false;}
var origCost = shares * stock.playerAvgShortPx;
var profit = ((stock.playerAvgShortPx - stock.price) * shares) - CONSTANTS.StockMarketCommission;
if (isNaN(profit)) {profit = 0;}
Player.gainMoney(origCost + profit);
if (tixApi) {
workerScript.scriptRef.onlineMoneyMade += profit;
Player.scriptProdSinceLastAug += profit;
}
stock.playerShortShares -= shares;
if (stock.playerShortShares === 0) {
stock.playerAvgShortPx = 0;
}
if (Engine.currentPage === Engine.Page.StockMarket) {
updateStockPlayerPosition(stock);
}
if (tixApi) {
workerScript.scriptRef.log("Sold your short position of " + shares + " shares of " + stock.symbol + " at " +
numeral(stock.price).format('($0.000a)') + " per share. After commissions, you gained " +
"a total of " + numeral(origCost + profit).format('($0.000a)'));
} else {
dialogBoxCreate("Sold your short position of " + formatNumber(shares, 0) + " shares of " + stock.symbol + " at $" +
formatNumber(stock.price, 2) + " per share. After commissions, you gained " +
"a total of $" + formatNumber(origCost + profit, 2));
}
return true;
}
function updateStockPrices() { function updateStockPrices() {
var v = Math.random(); var v = Math.random();
for (var name in StockMarket) { for (var name in StockMarket) {
@ -402,6 +504,7 @@ function displayStockMarketContent() {
return; return;
} }
//Create stock market content if you have an account
if (!stockMarketContentCreated && Player.hasWseAccount) { if (!stockMarketContentCreated && Player.hasWseAccount) {
console.log("Creating Stock Market UI"); console.log("Creating Stock Market UI");
document.getElementById("stock-market-commission").innerHTML = document.getElementById("stock-market-commission").innerHTML =

@ -25,6 +25,8 @@ import {AllServers, GetServerByHostname,
getServer, Server} from "./Server.js"; getServer, Server} from "./Server.js";
import {SpecialServerIps, import {SpecialServerIps,
SpecialServerNames} from "./SpecialServerIps.js"; SpecialServerNames} from "./SpecialServerIps.js";
import {TextFile, getTextFile,
createTextFile} from "./TextFile.js";
import {containsAllStrings, longestCommonStart, import {containsAllStrings, longestCommonStart,
formatNumber, isString} from "../utils/StringHelperFunctions.js"; formatNumber, isString} from "../utils/StringHelperFunctions.js";
@ -295,7 +297,7 @@ function determineAllPossibilitiesForTabCompletion(input, index=0) {
//Autocomplete the command //Autocomplete the command
if (index == -1) { if (index == -1) {
return ["alias", "analyze", "cat", "check", "clear", "cls", "connect", "free", return ["alias", "analyze", "cat", "check", "clear", "cls", "connect", "download", "free",
"hack", "help", "home", "hostname", "ifconfig", "kill", "killall", "hack", "help", "home", "hostname", "ifconfig", "kill", "killall",
"ls", "lscpu", "mem", "nano", "ps", "rm", "run", "scan", "scan-analyze", "ls", "lscpu", "mem", "nano", "ps", "rm", "run", "scan", "scan-analyze",
"scp", "sudov", "tail", "theme", "top"].concat(Object.keys(Aliases)).concat(Object.keys(GlobalAliases)); "scp", "sudov", "tail", "theme", "top"].concat(Object.keys(Aliases)).concat(Object.keys(GlobalAliases));
@ -362,6 +364,9 @@ function determineAllPossibilitiesForTabCompletion(input, index=0) {
allPos.push(currServ.messages[i]); allPos.push(currServ.messages[i]);
} }
} }
for (var i = 0; i < currServ.textFiles.length; ++i) {
allPos.push(currServ.textFiles[i].fn);
}
return allPos; return allPos;
} }
@ -386,10 +391,18 @@ function determineAllPossibilitiesForTabCompletion(input, index=0) {
} else { } else {
allPos.push(currServ.messages[i]); allPos.push(currServ.messages[i]);
} }
}
for (var i = 0; i < currServ.textFiles.length; ++i) {
allPos.push(currServ.textFiles[i].fn);
} }
return allPos; return allPos;
} }
if (input.startsWith("download ")) {
for (var i = 0; i < currServ.textFiles.length; ++i) {
allPos.push(currServ.textFiles[i].fn);
}
}
return allPos; return allPos;
} }
@ -739,9 +752,8 @@ let Terminal = {
post("Incorrect usage of cat command. Usage: cat [file]"); return; post("Incorrect usage of cat command. Usage: cat [file]"); return;
} }
var filename = commandArray[1]; var filename = commandArray[1];
//Can only edit script files if (!filename.endsWith(".msg") && !filename.endsWith(".lit") && !filename.endsWith(".txt")) {
if (!filename.endsWith(".msg") && !filename.endsWith(".lit")) { post("Error: Only .msg, .txt, and .lit files are viewable with cat (filename must end with .msg, .txt, or .lit)"); return;
post("Error: Only .msg and .lit files are viewable with cat (filename must end with .msg or .lit)"); return;
} }
for (var i = 0; i < s.messages.length; ++i) { for (var i = 0; i < s.messages.length; ++i) {
if (filename.endsWith(".lit") && s.messages[i] == filename) { if (filename.endsWith(".lit") && s.messages[i] == filename) {
@ -752,6 +764,12 @@ let Terminal = {
return; return;
} }
} }
for (var i = 0; i < s.textFiles.length; ++i) {
if (s.textFiles[i].fn === filename) {
s.textFiles[i].show();
return;
}
}
post("Error: No such file " + filename); post("Error: No such file " + filename);
break; break;
case "check": case "check":
@ -805,6 +823,19 @@ let Terminal = {
post("Host not found"); post("Host not found");
break; break;
case "download":
if (commandArray.length != 2) {
post("Incorrect usage of download command. Usage: download [text file]");
return;
}
var fn = commandArray[1];
var txtFile = getTextFile(fn, s);
if (txtFile !== null) {
txtFile.download();
} else {
post("Error: " + fn + " does not exist");
}
break;
case "free": case "free":
Terminal.executeFreeCommand(commandArray); Terminal.executeFreeCommand(commandArray);
break; break;
@ -978,14 +1009,15 @@ let Terminal = {
//Check programs //Check programs
var delTarget = commandArray[1]; var delTarget = commandArray[1];
if (delTarget.endsWith(".exe")) {
for (var i = 0; i < s.programs.length; ++i) { for (var i = 0; i < s.programs.length; ++i) {
if (s.programs[i] == delTarget) { if (s.programs[i] == delTarget) {
s.programs.splice(i, 1); s.programs.splice(i, 1);
return; return;
} }
} }
} else if (delTarget.endsWith(".script")) {
//Check scripts
for (var i = 0; i < s.scripts.length; ++i) { for (var i = 0; i < s.scripts.length; ++i) {
if (s.scripts[i].filename == delTarget) { if (s.scripts[i].filename == delTarget) {
//Check that the script isnt currently running //Check that the script isnt currently running
@ -999,8 +1031,7 @@ let Terminal = {
return; return;
} }
} }
} else if (delTarget.endsWith(".lit")) {
//Check literature files
for (var i = 0; i < s.messages.length; ++i) { for (var i = 0; i < s.messages.length; ++i) {
var f = s.messages[i]; var f = s.messages[i];
if (!(f instanceof Message) && isString(f) && f === delTarget) { if (!(f instanceof Message) && isString(f) && f === delTarget) {
@ -1008,8 +1039,15 @@ let Terminal = {
return; return;
} }
} }
} else if (delTarget.endsWith(".txt")) {
post("No such file exists"); for (var i = 0; i < s.textFiles.length; ++i) {
if (s.textFiles[i].fn === delTarget) {
s.textFiles.splice(i, 1);
return;
}
}
}
post("Error: No such file exists");
break; break;
case "run": case "run":
//Run a program or a script //Run a program or a script
@ -1346,6 +1384,15 @@ let Terminal = {
} }
} }
} }
for (var i = 0; i < s.textFiles.length; ++i) {
if (filter) {
if (s.textFiles[i].fn.includes(filter)) {
allFiles.push(s.textFiles[i].fn);
}
} else {
allFiles.push(s.textFiles[i].fn);
}
}
//Sort the files alphabetically then print each //Sort the files alphabetically then print each
allFiles.sort(); allFiles.sort();

@ -896,7 +896,7 @@ let Engine = {
//is necessary and then resets the counter //is necessary and then resets the counter
checkCounters: function() { checkCounters: function() {
if (Engine.Counters.autoSaveCounter <= 0) { if (Engine.Counters.autoSaveCounter <= 0) {
saveObject.saveGame(); saveObject.saveGame(indexedDb);
Engine.Counters.autoSaveCounter = 300; Engine.Counters.autoSaveCounter = 300;
} }
@ -1112,7 +1112,7 @@ let Engine = {
} }
}, },
load: function() { load: function(saveString) {
//Initialize main menu accordion panels to all start as "open" //Initialize main menu accordion panels to all start as "open"
var terminal = document.getElementById("terminal-tab"); var terminal = document.getElementById("terminal-tab");
var createScript = document.getElementById("create-script-tab"); var createScript = document.getElementById("create-script-tab");
@ -1129,7 +1129,7 @@ let Engine = {
var options = document.getElementById("options-tab"); var options = document.getElementById("options-tab");
//Load game from save or create new game //Load game from save or create new game
if (loadGame(saveObject)) { if (loadGame(saveString)) {
console.log("Loaded game from save"); console.log("Loaded game from save");
initBitNodes(); initBitNodes();
initBitNodeMultipliers(); initBitNodeMultipliers();
@ -1579,13 +1579,13 @@ let Engine = {
//Save, Delete, Import/Export buttons //Save, Delete, Import/Export buttons
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link"); Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
Engine.Clickables.saveMainMenuButton.addEventListener("click", function() { Engine.Clickables.saveMainMenuButton.addEventListener("click", function() {
saveObject.saveGame(); saveObject.saveGame(indexedDb);
return false; return false;
}); });
Engine.Clickables.deleteMainMenuButton = document.getElementById("delete-game-link"); Engine.Clickables.deleteMainMenuButton = document.getElementById("delete-game-link");
Engine.Clickables.deleteMainMenuButton.addEventListener("click", function() { Engine.Clickables.deleteMainMenuButton.addEventListener("click", function() {
saveObject.deleteGame(); saveObject.deleteGame(indexedDb);
return false; return false;
}); });
@ -1596,7 +1596,7 @@ let Engine = {
//Character Overview buttons //Character Overview buttons
document.getElementById("character-overview-save-button").addEventListener("click", function() { document.getElementById("character-overview-save-button").addEventListener("click", function() {
saveObject.saveGame(); saveObject.saveGame(indexedDb);
return false; return false;
}); });
@ -1672,8 +1672,43 @@ let Engine = {
} }
}; };
var indexedDb, indexedDbRequest;
window.onload = function() { window.onload = function() {
Engine.load(); if (!window.indexedDB) {
return Engine.load(null); //Will try to load from localstorage
}
//DB is called bitburnerSave
//Object store is called savestring
//key for the Object store is called save
indexedDbRequest = window.indexedDB.open("bitburnerSave", 1);
indexedDbRequest.onerror = function(e) {
console.log("Error opening indexedDB: ");
console.log(e);
return Engine.load(null); //Try to load from localstorage
};
indexedDbRequest.onsuccess = function(e) {
console.log("Opening bitburnerSave database successful!");
indexedDb = e.target.result;
var transaction = indexedDb.transaction(["savestring"]);
var objectStore = transaction.objectStore("savestring");
var request = objectStore.get("save");
request.onerror = function(e) {
console.log("Error in Database request to get savestring: " + e);
return Engine.load(null); //Try to load from localstorage
}
request.onsuccess = function(e) {
Engine.load(request.result); //Is this right?
}
};
indexedDbRequest.onupgradeneeded = function(e) {
var db = e.target.result;
var objectStore = db.createObjectStore("savestring");
}
}; };
export {Engine}; export {Engine};

@ -44,7 +44,7 @@ function dialogBoxCreate(txt) {
closeButton.innerHTML = "&times;" closeButton.innerHTML = "&times;"
var textE = document.createElement("p"); var textE = document.createElement("p");
textE.innerHTML = txt; textE.innerHTML = txt.replace(/(?:\r\n|\r|\n)/g, '<br>');
content.appendChild(closeButton); content.appendChild(closeButton);
content.appendChild(textE); content.appendChild(textE);

@ -1,3 +1,4 @@
import {BitNodeMultipliers} from "../src/BitNode.js";
import {CONSTANTS} from "../src/Constants.js"; import {CONSTANTS} from "../src/Constants.js";
import {Factions, Faction} from "../src/Faction.js"; import {Factions, Faction} from "../src/Faction.js";
import {Player} from "../src/Player.js"; import {Player} from "../src/Player.js";
@ -47,8 +48,9 @@ function infiltrationBoxCreate(inst) {
formatNumber(inst.chaExpGained, 3) + " cha exp<br>"); formatNumber(inst.chaExpGained, 3) + " cha exp<br>");
return; return;
} }
var facValue = totalValue * Player.faction_rep_mult * 1.25 var facValue = totalValue * Player.faction_rep_mult *
var moneyValue = totalValue * CONSTANTS.InfiltrationMoneyValue; CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep;
var moneyValue = totalValue * CONSTANTS.InfiltrationMoneyValue * BitNodeMultipliers.InfiltrationMoney;
infiltrationSetText("You can sell the classified documents and secrets " + infiltrationSetText("You can sell the classified documents and secrets " +
"you stole from " + inst.companyName + " for $" + "you stole from " + inst.companyName + " for $" +
formatNumber(moneyValue, 2) + " on the black market or you can give it " + formatNumber(moneyValue, 2) + " on the black market or you can give it " +