Finished implementing Hacknet Nodes. Seems to be working fine

This commit is contained in:
Daniel Xie 2017-04-27 15:02:58 -05:00
parent e2947cc8c3
commit b61754ddd0
7 changed files with 293 additions and 47 deletions

@ -116,10 +116,43 @@
height: 100%;
margin-left: 10%;
width: 99%;
overflow-y: scroll;
}
.hacknet-node li {
#hacknet-nodes-container li{
padding: 6px;
margin: 6px;
}
#hacknet-nodes-text,
#hacknet-nodes-money {
width: 80%;
margin: 10px;
padding: 10px;
}
.hacknet-node {
margin: 6px;
padding: 6px;
border: 2px solid white;
-webkit-box-shadow:
inset 0 0 8px rgba(0,0,0,0.1),
0 0 16px rgba(0,0,0,0.1);
-moz-box-shadow:
inset 0 0 8px rgba(0,0,0,0.1),
0 0 16px rgba(0,0,0,0.1);
box-shadow:
inset 0 0 8px rgba(0,0,0,0.1),
0 0 16px rgba(0,0,0,0.1);
}
.hacknet-node-button-div a {
display: block;
}
.hacknet-node-button-div:not(:last-child) {
border-bottom: none;
}
/* World */

@ -22,6 +22,7 @@ h2 {
}
ul {
padding: 6px;
list-style-type: none;
}
@ -87,10 +88,14 @@ span {
border-left: 1px solid #333333;
}
.a-link-button:hover {
background-color: #666;
}
/* Make anchor tags ("a" elements) inactive (not clickable) */
.a-link-button-inactive {
text-decoration: none;
background-color: #555;
background-color: #333;
color: #FFFFFF;
padding: 6px;
margin: 6px;

@ -73,6 +73,10 @@
<li class="active-scripts-tab">
<a href="#" id="active-scripts-menu-link"> Active Scripts </a>
</li>
<li class="hacknet-nodes-tab">
<a href="#" id="hacknet-nodes-menu-link"> Hacknet Nodes </a>
</li>
<li class="world-tab">
<a href="#" id="world-menu-link"> World </a>
@ -140,8 +144,22 @@
<!-- Hacknet Nodes -->
<div id="hacknet-nodes-container">
<h1 id="hacknet-nodes-title"> Hacknet Nodes </h1>
<p id="hacknet-nodes-text">
The Hacknet is a global, decentralized network of machines. It is used by hackers all around
the world to anonymously share computing power and perform distributed cyberattacks without the
fear of being traced.
<br><br>
Here, you can purchase a Hacknet Node, a specialized machine that can connect and contribute its
resources to the Hacknet network. This allows you to take a small percentage of profits
from hacks performed on the network. Essentially, you are renting out your Node's computing power.
<br><br>
Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node can be upgraded
in order to increase its computing power and thereby increase the profit you earn from it.
</p>
<a href="#" id="hacknet-nodes-purchase-button" class="a-link-button"> Purchase Hacknet Node </a>
<p id="hacknet-nodes-money"> </p>
<ul id="hacknet-nodes-list" style="list-style : none;">
</ul>
</div>

@ -8,8 +8,18 @@ CONSTANTS = {
//How much reputation is needed to join a megacorporation's faction
CorpFactionRepRequirement: 250000,
//Base cost for 1GB of RAM
BaseCostFor1GBOfRam: 50000,
/* Base costs */
BaseCostFor1GBOfRam: 50000, //1 GB of RAM
BaseCostForHacknetNode: 1000,
BaseCostForHacknetNodeCore: 1000000,
/* Hacknet Node constants */
HacknetNodeMoneyGainPerLevel: 0.25,
HacknetNodePurchaseNextMult: 1.2; //Multiplier when purchasing an additional hacknet node
HacknetNodeUpgradeLevelMult: 1.08, //Multiplier for cost when upgrading level
HacknetNodeUpgradeRamMult: 1.2, //Multiplier for cost when upgrading RAM
HacknetNodeUpgradeCoreMult: 1.5, //Multiplier for cost when buying another core
/* Script related things */
//Time (ms) it takes to run one operation in Netscript.

@ -1,7 +1,7 @@
/* HacknetNode.js */
function HacknetNode(name) {
this.level = 1;
this.ram = 0; //GB
this.ram = 1; //GB
this.numCores = 1;
this.name = name;
@ -14,17 +14,19 @@ function HacknetNode(name) {
HacknetNode.prototype.updateMoneyGainRate = function() {
//How much extra $/s is gained per level
var gainPerLevel = 0.25;
var gainPerLevel = CONSTANTS.HacknetNodeMoneyGainPerLevel;
//Each CPU core doubles the speed. Every 1GB of ram adds 10% increase
this.moneyGainRatePerSecond = (this.level * gainPerLevel) * Math.pow(1.1, ram) * this.numCores;
this.moneyGainRatePerSecond = (this.level * gainPerLevel) * Math.pow(1.1, this.ram-1) * this.numCores;
if (isNaN(this.moneyGainRatePerSecond)) {
throw new Error("Money gain rate calculated for Hacknet Node is NaN");
}
}
HacknetNode.prototype.calculateLevelUpgradeCost = function() {
//Upgrade cost = Base cost * multiplier ^ level
var baseCost = 10000;
var mult = 1.08;
return baseCost * Math.pow(mult, this.level);
var mult = CONSTANTS.HacknetNodeUpgradeLevelMult;
return CONSTANTS.BaseCostForHacknetNode * Math.pow(mult, this.level);
}
HacknetNode.prototype.purchaseLevelUpgrade = function() {
@ -32,6 +34,7 @@ HacknetNode.prototype.purchaseLevelUpgrade = function() {
if (cost > Player.money) {return;}
Player.loseMoney(cost);
++this.level;
this.updateMoneyGainRate();
}
HacknetNode.prototype.calculateRamUpgradeCost = function() {
@ -40,8 +43,8 @@ HacknetNode.prototype.calculateRamUpgradeCost = function() {
//Calculate cost
//Base cost of RAM is 50k per 1GB...but lets have this increase by 10% for every time
//the RAM has been upgraded
var cost = currentRam * CONSTANTS.BaseCostFor1GBOfRam;
var mult = Math.pow(1.1, numUpgrades);
var cost = this.ram * CONSTANTS.BaseCostFor1GBOfRam;
var mult = Math.pow(CONSTANTS.HacknetNodeUpgradeRamMult, numUpgrades);
return cost * mult;
}
@ -50,12 +53,13 @@ HacknetNode.prototype.purchaseRamUpgrade = function() {
if (cost > Player.money) {return;}
Player.loseMoney(cost);
this.ram *= 2; //Ram is always doubled
this.updateMoneyGainRate();
}
HacknetNode.prototype.calculateCoreUpgradeCost = function() {
var coreBaseCost = 1000000;
var mult = 1.5;
return coreBaseCost * Math.pow(mult, this.numCores);
var coreBaseCost = CONSTANTS.BaseCostForHacknetNodeCore;
var mult = CONSTANTS.HacknetNodeUpgradeCoreMult;
return coreBaseCost * Math.pow(mult, this.numCores-1);
}
HacknetNode.prototype.purchaseCoreUpgrade = function() {
@ -63,6 +67,7 @@ HacknetNode.prototype.purchaseCoreUpgrade = function() {
if (cost > Player.money) {return;}
Player.loseMoney(cost);
++this.numCores;
this.updateMoneyGainRate();
}
/* Saving and loading HackNets */
@ -74,54 +79,199 @@ HacknetNode.fromJSON = function(value) {
return Generic_fromJSON(HacknetNode, value.data);
}
Reviver.constructors.HacknetNode = HacknetNode;
purchaseHacknet = function() {
var cost = getCostOfNextHacknetNode();
if (cost > Player.money) {
dialogBoxCreate("You cannot afford to purchase a Hacknet Node!");
return;
}
//Auto generate a name for the node for now...TODO
var numOwned = Player.hacknetNodes.length;
var name = "hacknet-node-" + numOwned;
var node = new HacknetNode(name);
node.updateMoneyGainRate();
Player.loseMoney(cost);
Player.hacknetNodes.push(node);
displayHacknetNodesContent();
}
getCostOfNextHacknetNode = function() {
//Cost increases exponentially based on how many you own
var numOwned = Player.hacknetNodes.length;
var mult = CONSTANTS.HacknetNodePurchaseNextMult;
return CONSTANTS.BaseCostForHacknetNode * Math.pow(mult, numOwned);
}
//Creates Hacknet Node DOM elements when the page is opened
displayHacknetNodesContent = function() {
//Update Hacknet Nodes button
var purchaseButton = document.getElementById("hacknet-nodes-purchase-button");
var newPurchaseButton = purchaseButton.cloneNode(true);
purchaseButton.parentNode.replaceChild(newPurchaseButton, purchaseButton);
newPurchaseButton.addEventListener("click", function() {
purchaseHacknet();
return false;
});
//Remove all old hacknet Node DOM elements
var hacknetNodesList = document.getElementById("hacknet-nodes-list");
while (hacknetNodesList.firstChild) {
hacknetNodesList.removeChild(hacknetNodesList.firstChild);
}
//Then re-create them
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
createHacknetNodeDomElement(Player.hacknetNodes[i]);
}
updateHacknetNodesContent();
}
//Update information on all Hacknet Node DOM elements
updateHacknetNodesContent = function() {
//Set purchase button to inactive if not enough money, and update its price display
var cost = getCostOfNextHacknetNode();
var purchaseButton = document.getElementById("hacknet-nodes-purchase-button");
purchaseButton.innerHTML = "Purchase Hacknet Node - $" + cost.toFixed(2);
if (cost > Player.money) {
purchaseButton.setAttribute("class", "a-link-button-inactive");
} else {
purchaseButton.setAttribute("class", "a-link-button");
}
//Update player's money
document.getElementById("hacknet-nodes-money").innerHTML = "Money: $" + Player.money.toFixed(2);
//Update information in each owned hacknet node
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
updateHacknetNodeDomElement(Player.hacknetNodes[i]);
}
}
//Creates a single Hacknet Node DOM element
createHacknetNodeDomElement = function(nodeObj) {
var nodeName = nodeObj.name;
var list = document.getElementById("hacknet-nodes-list");
var listItem = document.createElement("li");
item.setAttribute("class", "hacknet-node");
listItem.setAttribute("class", "hacknet-node");
var span = document.createElement("span");
span.style.display = "inline-block";
span.style.display = "inline";
var buttonDiv = document.createElement("div");
buttonDiv.setAttribute("class", "hacknet-node-button-div");
//Text
var txt = document.createElement("p");
txt.setAttribute("id", "hacknet-node-text-" + nodeName);
txt.innerHTML = "Node name: " + nodeName + "<br>"
"Production: " + nodeObj.totalMoneyGenerated +
" ($" + nodeObj.moneyGainRatePerSecond + ") <br>" +
"Level: " + nodeObj.level + "<br>" +
"RAM: " + nodeObj.ram + "GB<br>" +
"Cores: " + nodeObj.numCores;
//txt.setAttribute("id", "hacknet-node-text-" + nodeName);
txt.id = "hacknet-node-text-" + nodeName;
//Upgrade buttons
var upgradeLevelButton = document.createElement("a");
var upgradeRamButton = document.createElement("a");
var upgradeCoreButton = document.createElement("a");
upgradeLevelButton.setAttribute("id", "hacknet-node-upgrade-level-" + nodeName);
//upgradeLevelButton.setAttribute("id", "hacknet-node-upgrade-level-" + nodeName);
upgradeLevelButton.id = "hacknet-node-upgrade-level-" + nodeName;
upgradeLevelButton.setAttribute("class", "a-link-button-inactive");
upgradeRamButton.setAttribute("id", "hacknet-node-upgrade-ram-" + nodeName);
upgradeLevelButton.addEventListener("click", function() {
nodeObj.purchaseLevelUpgrade();
updateHacknetNodesContent();
return false;
});
//upgradeRamButton.setAttribute("id", "hacknet-node-upgrade-ram-" + nodeName);
upgradeRamButton.id = "hacknet-node-upgrade-ram-" + nodeName;
upgradeRamButton.setAttribute("class", "a-link-button-inactive");
upgradeCoreButton.setAttribute("id", "hacknet-node-upgrade-core-" + nodeName);
upgradeRamButton.addEventListener("click", function() {
nodeObj.purchaseRamUpgrade();
updateHacknetNodesContent();
return false;
});
//upgradeCoreButton.setAttribute("id", "hacknet-node-upgrade-core-" + nodeName);
upgradeCoreButton.id = "hacknet-node-upgrade-core-" + nodeName;
upgradeCoreButton.setAttribute("class", "a-link-button-inactive");
upgradeLevelButton.innerHTML = "Upgrade Hacknet Node Level";
upgradeRamButton.innerHTML = "Upgrade Hacknet Node RAM";
upgradeCoreButton.innerHTML = "Purchase additional CPU Core for Hacknet Node";
upgradeCoreButton.addEventListener("click", function() {
nodeObj.purchaseCoreUpgrade();
updateHacknetNodesContent();
return false;
});
//Put all the components together in the li element
span.appendChild(txt);
buttonDiv.appendChild(upgradeLevelButton);
buttonDiv.appendChild(upgradeRamButton);
buttonDiv.appendChild(upgradeCoreButton);
span.appendChild(buttonDiv);
listItem.appendChild(span);
updateHacknetNodeDomElement(item, nodeObj);
document.getElementById("hacknet-nodes-list").appendChild(listItem);
list.appendChild(item);
//Set the text and stuff inside the DOM element
updateHacknetNodeDomElement(nodeObj);
}
updateHacknetNodeDomElement = function(li, nodeObj) {
//Updates information on a single hacknet node DOM element
updateHacknetNodeDomElement = function(nodeObj) {
var nodeName = nodeObj.name;
}
Reviver.constructors.HacknetNode = HacknetNode;
purchaseHacknet = function() {
var txt = document.getElementById("hacknet-node-text-" + nodeName);
if (txt == null) {throw new Error("Cannot find text element");}
txt.innerHTML = "Node name: " + nodeName + "<br>" +
"Production: $" + nodeObj.totalMoneyGenerated.toFixed(2) +
" ($" + nodeObj.moneyGainRatePerSecond + " / second) <br>" +
"Level: " + nodeObj.level + "<br>" +
"RAM: " + nodeObj.ram + "GB<br>" +
"Cores: " + nodeObj.numCores;
var upgradeLevelButton = document.getElementById("hacknet-node-upgrade-level-" + nodeName);
if (upgradeLevelButton == null) {throw new Error("Cannot find upgrade level button element");}
var upgradeLevelCost = nodeObj.calculateLevelUpgradeCost();
upgradeLevelButton.innerHTML = "Upgrade Hacknet Node Level - $" + upgradeLevelCost.toFixed(2);
if (upgradeLevelCost > Player.money) {
upgradeLevelButton.setAttribute("class", "a-link-button-inactive");
} else {
upgradeLevelButton.setAttribute("class", "a-link-button");
}
var upgradeRamButton = document.getElementById("hacknet-node-upgrade-ram-" + nodeName);
if (upgradeRamButton == null) {throw new Error("Cannot find upgrade ram button element");}
var upgradeRamCost = nodeObj.calculateRamUpgradeCost();
upgradeRamButton.innerHTML = "Upgrade Hacknet Node RAM -$" + upgradeRamCost.toFixed(2);
if (upgradeRamCost > Player.money) {
upgradeRamButton.setAttribute("class", "a-link-button-inactive");
} else {
upgradeRamButton.setAttribute("class", "a-link-button");
}
var upgradeCoreButton = document.getElementById("hacknet-node-upgrade-core-" + nodeName);
if (upgradeCoreButton == null) {throw new Error("Cannot find upgrade cores button element");}
var upgradeCoreCost = nodeObj.calculateCoreUpgradeCost();
upgradeCoreButton.innerHTML = "Purchase additional CPU Core - $" + upgradeCoreCost.toFixed(2);
if (upgradeCoreCost > Player.money) {
upgradeCoreButton.setAttribute("class", "a-link-button-inactive");
} else {
upgradeCoreButton.setAttribute("class", "a-link-button");
}
}
processAllHacknetNodeEarnings = function(numCycles) {
for (var i = 0; i < Player.hacknetNodes.length; ++i) {
processSingleHacknetNodeEarnings(numCycles, Player.hacknetNodes[i]);
}
}
processSingleHacknetNodeEarnings = function(numCycles, nodeObj) {
var cyclesPerSecond = 1000 / Engine._idleSpeed;
var earningPerCycle = nodeObj.moneyGainRatePerSecond / cyclesPerSecond;
if (isNaN(earningPerCycle)) {throw new Error("Calculated Earnings is not a number");}
var totalEarnings = numCycles * earningPerCycle;
nodeObj.totalMoneyGenerated += totalEarnings;
nodeObj.onlineTimeSeconds += (numCycles * (Engine._idleSpeed / 1000));
Player.gainMoney(totalEarnings);
}

@ -297,7 +297,7 @@ var Terminal = {
for (var i = 0; i < Player.getCurrentServer().runningScripts.length; i++) {
if (Player.getCurrentServer().runningScripts[i] == scriptName) {
killWorkerScript(scriptName, Player.getCurrentServer().ip);
post("Killing " + scriptName + ". May take a few seconds");
post("Killing " + scriptName + ". May take up to a few minutes for the scripts to die...");
return;
}
}

@ -8,6 +8,7 @@ var Engine = {
characterMainMenuButton: null,
scriptEditorMainMenuButton: null,
activeScriptsMainMenuButton: null,
hacknetNodesMainMenuButton: null,
worldMainMenuButton: null,
createProgramMainMenuButton: null,
factionsMainMenuButton: null,
@ -43,6 +44,7 @@ var Engine = {
characterContent: null,
scriptEditorContent: null,
activeScriptsContent: null,
hacknetNodesContent: null,
worldContent: null,
createProgramContent: null,
factionsContent: null,
@ -66,6 +68,7 @@ var Engine = {
CharacterInfo: "CharacterInfo",
ScriptEditor: "ScriptEditor",
ActiveScripts: "ActiveScripts",
HacknetNodes: "HacknetNodes",
World: "World",
CreateProgram: "CreateProgram",
Factions: "Factions",
@ -202,6 +205,14 @@ var Engine = {
Engine.currentPage = Engine.Page.ActiveScripts;
},
loadHacknetNodesContent: function() {
Engine.hideAllContent();
Engine.Display.hacknetNodesContent.style.visibility = "visible";
displayHacknetNodesContent();
Engine.currentPage = Engine.Page.HacknetNodes;
},
loadWorldContent: function() {
Engine.hideAllContent();
Engine.Display.worldContent.style.visibility = "visible";
@ -274,6 +285,7 @@ var Engine = {
Engine.Display.characterContent.style.visibility = "hidden";
Engine.Display.scriptEditorContent.style.visibility = "hidden";
Engine.Display.activeScriptsContent.style.visibility = "hidden";
Engine.Display.hacknetNodesContent.style.visibility = "hidden";
Engine.Display.worldContent.style.visibility = "hidden";
Engine.Display.createProgramContent.style.visibility = "hidden";
Engine.Display.factionsContent.style.visibility = "hidden";
@ -310,6 +322,7 @@ var Engine = {
'Agility: ' + (Player.agility).toLocaleString() + '<br><br>' +
'Charisma: ' + (Player.charisma).toLocaleString() + '<br><br>' +
'Servers owned: ' + Player.purchasedServers.length + '<br><br>' +
'Hacknet Nodes owned: ' + Player.hacknetNodes.length + '<br><br>' +
'Hacking experience: ' + (Player.hacking_exp.toFixed(4)).toLocaleString() + '<br><br>' +
'Strength experience: ' + (Player.strength_exp.toFixed(4)).toLocaleString() + '<br><br>' +
'Defense experience: ' + (Player.defense_exp.toFixed(4)).toLocaleString() + '<br><br>' +
@ -585,6 +598,9 @@ var Engine = {
//Update the running time of all active scripts
updateOnlineScriptTimes(numCycles);
//Hacknet Nodes
processAllHacknetNodeEarnings(numCycles);
},
//Counters for the main event loop. Represent the number of game cycles are required
@ -592,7 +608,7 @@ var Engine = {
Counters: {
autoSaveCounter: 300, //Autosave every minute
updateSkillLevelsCounter: 10, //Only update skill levels every 2 seconds. Might improve performance
updateDisplays: 5, //Update displays such as Active Scripts display and character display
updateDisplays: 4, //Update displays such as Active Scripts display and character display
serverGrowth: 450, //Process server growth every minute and a half
checkFactionInvitations: 1500, //Check whether you qualify for any faction invitations every 5 minutes
},
@ -623,9 +639,11 @@ var Engine = {
Engine.updateActiveScriptsItems();
} else if (Engine.currentPage == Engine.Page.CharacterInfo) {
Engine.displayCharacterInfo();
}
} else if (Engine.currentPage == Engine.Page.HacknetNodes) {
updateHacknetNodesContent();
}
Engine.Counters.updateDisplays = 5;
Engine.Counters.updateDisplays = 4;
}
if (Engine.Counters.serverGrowth <= 0) {
@ -706,6 +724,12 @@ var Engine = {
return false;
});
Engine.Clickables.hacknetNodesMainMenuButton = document.getElementById("hacknet-nodes-menu-link");
Engine.Clickables.hacknetNodesMainMenuButton.addEventListener("click", function() {
Engine.loadHacknetNodesContent();
return false;
});
Engine.Clickables.worldMainMenuButton = document.getElementById("world-menu-link");
Engine.Clickables.worldMainMenuButton.addEventListener("click", function() {
Engine.loadWorldContent();
@ -811,6 +835,9 @@ var Engine = {
Engine.Display.activeScriptsContent = document.getElementById("active-scripts-container");
Engine.Display.activeScriptsContent.style.visibility = "hidden";
Engine.Display.hacknetNodesContent = document.getElementById("hacknet-nodes-container");
Engine.Display.hacknetNodesContent.style.visibility = "hidden";
Engine.Display.worldContent = document.getElementById("world-container");
Engine.Display.worldContent.style.visibility = "hidden";
@ -881,6 +908,9 @@ var Engine = {
Player.work(numCyclesOffline);
}
}
//Hacknet Nodes
processAllHacknetNodeEarnings(numCyclesOffline);
} else {
//No save found, start new game
console.log("Initializing new game");