Fixed save issue. Rebalancing

This commit is contained in:
danielyxie 2017-10-14 20:55:41 -05:00
parent 8f0dad0d10
commit cf471acad6
8 changed files with 624 additions and 338 deletions

665
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

@ -75,11 +75,11 @@ function initBitNodes() {
"Level 1: 4%<br>" +
"Level 2: 6%<br>" +
"Level 3: 7%");
BitNodes["BitNode6"] = new BitNode(6, "Hacktocracy", "COMING SOON"); //Healthy Hacknet balancing mechanic
BitNodes["BitNode7"] = new BitNode(7, "Do Androids Dream?", "COMING SOON"); //Build androids for automation
BitNodes["BitNode6"] = new BitNode(6, "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["BitNode9"] = new BitNode(9, "MegaCorp", "COMING SOON"); //Single corp/server with increasing difficulty
BitNodes["BitNode10"] = new BitNode(10, "Wasteland", "COMING SOON"); //Postapocalyptic
BitNodes["BitNode9"] = new BitNode(9, "Hacktocracy", "COMING SOON"); //Healthy Hacknet balancing mechanic
BitNodes["BitNode10"] = new BitNode(10, "MegaCorp", "COMING SOON"); //Not sure yet
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +

@ -119,7 +119,7 @@ let CONSTANTS = {
IntelligenceCrimeBaseExpGain: 0.001,
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
IntelligenceSingFnBaseExpGain: 0.001,
IntelligenceSingFnBaseExpGain: 0.002,
IntelligenceClassBaseExpGain: 0.000001,
IntelligenceHackingMissionBaseExpGain: 0.03, //Hacking Mission difficulty multiplied by this to get exp gain
@ -134,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 " +
"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 " +
"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>" +
"Each Node has three stats: Attack, Defense, and HP. There are five different actions that " +
"a Node can take:<br><br> " +
@ -147,7 +147,7 @@ let CONSTANTS = {
"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>" +
"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>" +
"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>" +
@ -162,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 " +
"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 " +
"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 " +
"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 " +
@ -174,7 +174,7 @@ let CONSTANTS = {
"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>" +
"-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
@ -1051,7 +1051,7 @@ let CONSTANTS = {
"but that's not available yet. You can also download files to your real computer using the 'download' Terminal command<br>" +
"-Added a new Crime: Bond Forgery. This crime takes 5 minutes to attempt " +
"and gives $4,500,000 if successful. It is meant for mid game.<br>" +
"-Added commitCrime() and getStats() Singularity Functions.<br>" +
"-Added commitCrime(), getCrimeChance(), and getStats() Singularity Functions.<br>" +
"-Removed getIntelligence() Netscript function<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>" +
"-Increased the amount of money gained from Infiltration by 20%, and the amount of faction reputation by 12%<br>" +
@ -1059,7 +1059,9 @@ let CONSTANTS = {
"-Rebalanced BitNode-4 so that hacking is slightly less profitable<br>" +
"-Rebalanced BitNode-5 so that Infiltration is more profitable and gives more faction rep<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>"
"-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};

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

@ -10,7 +10,14 @@ import {commitShopliftCrime, commitRobStoreCrime, commitMugCrime,
commitLarcenyCrime, commitDealDrugsCrime, commitBondForgeryCrime,
commitTraffickArmsCrime,
commitHomicideCrime, commitGrandTheftAutoCrime, commitKidnapCrime,
commitAssassinationCrime, commitHeistCrime} from "./Crimes.js";
commitAssassinationCrime, commitHeistCrime, determineCrimeSuccess,
determineCrimeChanceShoplift, determineCrimeChanceRobStore,
determineCrimeChanceMug, determineCrimeChanceLarceny,
determineCrimeChanceDealDrugs, determineCrimeChanceBondForgery,
determineCrimeChanceTraffickArms,
determineCrimeChanceHomicide, determineCrimeChanceGrandTheftAuto,
determineCrimeChanceKidnap, determineCrimeChanceAssassination,
determineCrimeChanceHeist} from "./Crimes.js";
import {Companies, Company, CompanyPosition,
CompanyPositions, companyExists} from "./Company.js";
import {CONSTANTS} from "./Constants.js";
@ -848,12 +855,12 @@ function NetscriptFunctions(workerScript) {
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()");
}
if (shares == 0) {return false;}
if (stock == null || shares < 0 || isNaN(shares)) {
workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to buyStock()");
return false;
}
shares = Math.round(shares);
if (shares === 0) {return false;}
var totalPrice = stock.price * shares;
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
@ -883,13 +890,14 @@ function NetscriptFunctions(workerScript) {
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockPrice()");
}
if (shares == 0) {return false;}
if (stock == null || shares < 0 || isNaN(shares)) {
workerScript.scriptRef.log("Error: Invalid 'shares' argument passed to sellStock()");
return false;
}
shares = Math.round(shares);
if (shares > stock.playerShares) {shares = stock.playerShares;}
if (shares == 0) {return false;}
if (shares === 0) {return false;}
var gains = stock.price * shares - CONSTANTS.StockMarketCommission;
Player.gainMoney(gains);
@ -2003,6 +2011,43 @@ function NetscriptFunctions(workerScript) {
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) {
if (Player.bitNodeN != 4) {
if (!(hasSingularitySF && singularitySFLvl >= 3)) {

@ -1141,7 +1141,7 @@ PlayerObject.prototype.getFactionSecurityWorkRepGain = function() {
this.strength / CONSTANTS.MaxSkillLevel +
this.defense / CONSTANTS.MaxSkillLevel +
this.dexterity / CONSTANTS.MaxSkillLevel +
this.agility / CONSTANTS.MaxSkillLevel) / 5;
this.agility / CONSTANTS.MaxSkillLevel) / 4.5;
return t * this.faction_rep_mult;
}
@ -1152,7 +1152,7 @@ PlayerObject.prototype.getFactionFieldWorkRepGain = function() {
this.dexterity / CONSTANTS.MaxSkillLevel +
this.agility / CONSTANTS.MaxSkillLevel +
this.charisma / CONSTANTS.MaxSkillLevel +
this.intelligence / CONSTANTS.MaxSkillLevel) / 6;
this.intelligence / CONSTANTS.MaxSkillLevel) / 5.5;
return t * this.faction_rep_mult;
}

@ -1,12 +1,14 @@
import {CONSTANTS} from "./Constants.js";
import {Engine} from "./engine.js";
import {Locations} from "./Location.js";
import {WorkerScript} from "./NetscriptWorker.js";
import {Player} from "./Player.js";
import {dialogBoxCreate} from "../utils/DialogBox.js";
import {clearEventListeners, getRandomInt} from "../utils/HelperFunctions.js";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver.js";
import numeral from "../utils/numeral.min.js";
import {formatNumber} from "../utils/StringHelperFunctions.js";
/* StockMarket.js */
@ -17,6 +19,8 @@ function Stock(name, symbol, mv, b, otlkMag, initPrice=10000) {
this.playerShares = 0;
this.playerAvgPx = 0;
this.playerShortShares = 0;
this.playerAvgShortPx = 0;
this.mv = mv;
this.b = b;
this.otlkMag = otlkMag;
@ -32,6 +36,10 @@ Stock.fromJSON = function(value) {
Reviver.constructors.Stock = Stock;
//Order types (long and short for each):
// - Limit Order
// - Stop Order
let StockMarket = {} //Full name to stock object
let StockSymbols = {} //Full name to symbol
let SymbolToStockMap = {}; //Symbol to Stock object
@ -253,12 +261,12 @@ function stockMarketCycle() {
//Returns true if successful, false otherwise
function buyStock(stock, shares) {
if (shares == 0) {return false;}
if (stock == null || shares < 0 || isNaN(shares)) {
dialogBoxCreate("Failed to buy stock. This may be a bug, 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)) {
@ -279,7 +287,6 @@ function buyStock(stock, shares) {
return true;
}
//Returns true if successful and false otherwise
function sellStock(stock, shares) {
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");
return false;
}
shares = Math.round(shares);
if (shares > stock.playerShares) {shares = stock.playerShares;}
if (shares == 0) {return false;}
if (shares === 0) {return false;}
var gains = stock.price * shares - CONSTANTS.StockMarketCommission;
Player.gainMoney(gains);
stock.playerShares -= shares;
@ -302,6 +310,100 @@ function sellStock(stock, shares) {
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() {
var v = Math.random();
for (var name in StockMarket) {

@ -1684,7 +1684,8 @@ window.onload = function() {
indexedDbRequest = window.indexedDB.open("bitburnerSave", 1);
indexedDbRequest.onerror = function(e) {
console.log("Error opening indexedDB: " + e);
console.log("Error opening indexedDB: ");
console.log(e);
return Engine.load(null); //Try to load from localstorage
};
@ -1705,7 +1706,7 @@ window.onload = function() {
};
indexedDbRequest.onupgradeneeded = function(e) {
var db = event.target.result;
var db = e.target.result;
var objectStore = db.createObjectStore("savestring");
}
};