Refactored Netscript Hacknet Node library. Now an array called hacknetnodes[i] can be used to access hacknet nodes, and they can be upgraded with functions

This commit is contained in:
Daniel Xie 2017-05-25 07:18:34 -05:00
parent 6316cbae23
commit 8cd9e8954d
8 changed files with 196 additions and 69 deletions

@ -52,8 +52,11 @@ CONSTANTS = {
ScriptGetHackingLevelRamCost: 0.1, ScriptGetHackingLevelRamCost: 0.1,
ScriptGetServerMoneyRamCost: 0.1, ScriptGetServerMoneyRamCost: 0.1,
ScriptOperatorRamCost: 0.01, ScriptOperatorRamCost: 0.01,
ScriptPurchaseHacknetRamCost: 1.0, ScriptPurchaseHacknetRamCost: 1.5,
ScriptUpgradeHacknetRamCost: 1.0, ScriptHacknetNodesRamCost: 1.0, //Base cost for accessing hacknet nodes array
ScriptHNUpgLevelRamCost: 0.4,
ScriptHNUpgRamRamCost: 0.6,
ScriptHNUpgCoreRamCost: 0.8,
//Server growth rate //Server growth rate
ServerGrowthRate: 1.00075, ServerGrowthRate: 1.00075,

@ -73,15 +73,16 @@ HacknetNode.prototype.calculateLevelUpgradeCost = function(levels=1) {
HacknetNode.prototype.purchaseLevelUpgrade = function(levels=1) { HacknetNode.prototype.purchaseLevelUpgrade = function(levels=1) {
var cost = this.calculateLevelUpgradeCost(levels); var cost = this.calculateLevelUpgradeCost(levels);
if (isNaN(cost)) {throw new Error("Cost is NaN"); return;} if (isNaN(cost)) {return false;}
if (cost > Player.money) {return;} if (cost > Player.money) {return false;}
Player.loseMoney(cost); Player.loseMoney(cost);
if (this.level + levels >= CONSTANTS.HacknetNodeMaxLevel) { if (this.level + levels >= CONSTANTS.HacknetNodeMaxLevel) {
this.level = CONSTANTS.HacknetNodeMaxLevel; this.level = CONSTANTS.HacknetNodeMaxLevel;
return; return false;
} }
this.level += levels; this.level += levels;
this.updateMoneyGainRate(); this.updateMoneyGainRate();
return true;
} }
HacknetNode.prototype.calculateRamUpgradeCost = function() { HacknetNode.prototype.calculateRamUpgradeCost = function() {
@ -96,12 +97,13 @@ HacknetNode.prototype.calculateRamUpgradeCost = function() {
HacknetNode.prototype.purchaseRamUpgrade = function() { HacknetNode.prototype.purchaseRamUpgrade = function() {
var cost = this.calculateRamUpgradeCost(); var cost = this.calculateRamUpgradeCost();
if (isNaN(cost)) {throw new Error("Cost is NaN"); return;} if (isNaN(cost)) {return false;}
if (cost > Player.money) {return;} if (cost > Player.money) {return false;}
Player.loseMoney(cost); Player.loseMoney(cost);
if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {return;} if (this.ram >= CONSTANTS.HacknetNodeMaxRam) {return false;}
this.ram *= 2; //Ram is always doubled this.ram *= 2; //Ram is always doubled
this.updateMoneyGainRate(); this.updateMoneyGainRate();
return true;
} }
HacknetNode.prototype.calculateCoreUpgradeCost = function() { HacknetNode.prototype.calculateCoreUpgradeCost = function() {
@ -112,12 +114,13 @@ HacknetNode.prototype.calculateCoreUpgradeCost = function() {
HacknetNode.prototype.purchaseCoreUpgrade = function() { HacknetNode.prototype.purchaseCoreUpgrade = function() {
var cost = this.calculateCoreUpgradeCost(); var cost = this.calculateCoreUpgradeCost();
if (isNaN(cost)) {throw new Error("Cost is NaN"); return;} if (isNaN(cost)) {return false;}
if (cost > Player.money) {return;} if (cost > Player.money) {return false;}
Player.loseMoney(cost); Player.loseMoney(cost);
if (this.numCores >= CONSTANTS.HacknetNodeMaxCores) {return;} if (this.numCores >= CONSTANTS.HacknetNodeMaxCores) {return false;}
++this.numCores; ++this.numCores;
this.updateMoneyGainRate(); this.updateMoneyGainRate();
return true;
} }
/* Saving and loading HackNets */ /* Saving and loading HackNets */

@ -23,6 +23,17 @@ function evaluate(exp, workerScript) {
case "var": case "var":
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (env.stopFlag) {reject(workerScript);} if (env.stopFlag) {reject(workerScript);}
if (exp.value == "hacknetnodes") {
setTimeout(function() {
var pEvaluateHacknetNode = evaluateHacknetNode(exp, workerScript);
pEvaluateHacknetNode.then(function(res) {
resolve(res);
}, function(e) {
reject(e);
});
}, CONSTANTS.CodeInstructionRunTime);
return;
}
try { try {
resolve(env.get(exp.value)); resolve(env.get(exp.value));
} catch (e) { } catch (e) {
@ -747,7 +758,7 @@ function evaluate(exp, workerScript) {
} }
if (cost > Player.money) { if (cost > Player.money) {
workerScript.scriptRef.log("Could not afford to purchase new Hacknet Node"); workerScript.scriptRef.log("Could not afford to purchase new Hacknet Node");
resolve(""); resolve(false);
return; return;
} }
@ -761,62 +772,15 @@ function evaluate(exp, workerScript) {
Player.hacknetNodes.push(node); Player.hacknetNodes.push(node);
displayHacknetNodesContent(); displayHacknetNodesContent();
workerScript.scriptRef.log("Purchased new Hacknet Node with name: " + name); workerScript.scriptRef.log("Purchased new Hacknet Node with name: " + name);
resolve(name); resolve(numOwned);
}, CONSTANTS.CodeInstructionRunTime); }, CONSTANTS.CodeInstructionRunTime);
} else if (exp.func.value == "upgradeHacknetNode") { }
if (exp.args.length != 1) {
reject("|"+workerScript.serverIp+"|"+workerScript.name+"|upgradeHacknetNode() call has incorrect number of arguments. Takes 1 argument");
return;
}
var namePromise = evaluate(exp.args[0], workerScript);
namePromise.then(function(name) {
var node = getHacknetNode(name);
if (node == null) {
reject("|"+workerScript.serverIp+"|"+workerScript.name+"|Invalid Hacknet Node name passed into upgradeHacknetNode()");
return;
}
var cost = node.calculateLevelUpgradeCost(1);
if (isNaN(cost)) {
reject("|"+workerScript.serverIp+"|"+workerScript.name+"|Could not calculate cost in upgradeHacknetNode(). This is a bug please report to game dev");
return;
}
if (cost > Player.money) {
workerScript.scriptRef.log("Could not afford to upgrade Hacknet Node: " + name);
resolve(false);
return;
}
if (node.level >= CONSTANTS.HacknetNodeMaxLevel) {
workerScript.scriptRef.log("Hacknet Node " + name + " already at max level");
node.level = CONSTANTS.HacknetNodeMaxLevel;
resolve(false);
return;
}
Player.loseMoney(cost);
node.level += 1;
node.updateMoneyGainRate();
workerScript.scriptRef.log("Hacknet node " + name + " upgraded to level " + node.level + "!");
resolve(true);
}, function(e) {
reject(e);
});
} else if (exp.func.value == "getNumHacknetNodes") {
if (exp.args.length != 0) {
reject("|"+workerScript.serverIp+"|"+workerScript.name+"|getNumHacknetNodes() call has incorrect number of arguments. Takes 0 arguments");
return;
}
setTimeout(function() {
if (env.stopFlag) {reject(workerScript);}
workerScript.scriptRef.log("getNumHacknetNodes() returned " + Player.hacknetNodes.length);
resolve(Player.hacknetNodes.length);
}, CONSTANTS.CodeInstructionRunTime);
}
}, CONSTANTS.CodeInstructionRunTime); }, CONSTANTS.CodeInstructionRunTime);
}); });
reject("|" + workerScript.serverIp + "|" + workerScript.name + "|Unrecognized function call"); reject("|" + workerScript.serverIp + "|" + workerScript.name + "|Unrecognized function call");
break; break;
default: default:
reject("|" + workerScript.serverIp + "|" + workerScript.name + "|Can't evaluate type " + exp.type); break;
} }
} }
@ -980,6 +944,109 @@ function evaluateWhile(exp, workerScript) {
}); });
} }
function evaluateHacknetNode(exp, workerScript) {
var env = workerScript.env;
return new Promise(function(resolve, reject) {
setTimeout(function() {
if (exp.index == null) {
if ((exp.op.type == "call" && exp.op.value == "length") ||
(exp.op.type == "var" && exp.op.value == "length")) {
resolve(Player.hacknetNodes.length);
workerScript.scriptRef.log("hacknetnodes.length returned " + Player.hacknetNodes.length);
return;
} else {
workerScript.scriptRef.log("Invalid/null index for hacknetnodes");
reject(makeRuntimeRejectMsg(workerScript, "Invalid/null index. hacknetnodes array must be accessed with an index"));
return;
}
}
var indexPromise = evaluate(exp.index.value, workerScript);
indexPromise.then(function(index) {
if (isNaN(index) || index >= Player.hacknetNodes.length || index < 0) {
workerScript.scriptRef.log("Invalid index value for hacknetnodes[]");
reject(makeRuntimeRejectMsg(workerScript, "Invalid index value for hacknetnodes[]."));
return;
}
var nodeObj = Player.hacknetNodes[index];
if (exp.op == null) {
reject(makeRuntimeRejectMsg(workerScript, "No operator or property called for hacknetnodes. Usage: hacknetnodes[i].property/operator"));
return;
} else if (exp.op.type == "var") {
//Get properties: level, ram, cores
switch(exp.op.value) {
case "level":
resolve(nodeObj.level);
break;
case "ram":
resolve(nodeObj.ram);
break;
case "cores":
resolve(nodeObj.numCores);
break;
default:
reject(makeRuntimeRejectMsg(workerScript, "Unrecognized property for Hacknet Node. Valid properties: ram, cores, level"));
break;
}
} else if (exp.op.type == "call") {
switch(exp.op.func.value) {
case "upgradeLevel":
if (exp.op.args.length == 1) {
var argPromise = evaluate(exp.op.args[0], workerScript);
argPromise.then(function(arg) {
if (isNaN(arg)) {
reject(makeRuntimeRejectMsg(workerScript, "Argument passed into upgradeLevel() is not numeric"));
return;
}
var res = nodeObj.purchaseLevelUpgrade(arg);
if (res) {
workerScript.scriptRef.log("Upgraded " + nodeObj.name + arg + " times to level " + nodeObj.level);
}
resolve(res);
}, function(e) {
reject(e);
});
} else {
var res = nodeObj.purchaseLevelUpgrade(1);
if (res) {
workerScript.scriptRef.log("Upgraded " + nodeObj.name + " once to level " + nodeObj.level);
}
resolve(res);
}
break;
case "upgradeRam":
var res = nodeObj.purchaseRamUpgrade();
if (res) {
workerScript.scriptRef.log("Upgraded " + nodeObj.name + "'s RAM to " + nodeObj.ram + "GB");
}
resolve(res);
break;
case "upgradeCore":
var res = nodeObj.purchaseCoreUpgrade();
if (res) {
workerScript.scriptRef.log("Upgraded " + nodeObj.name + "'s number of cores to " + nodeObj.numCores);
}
resolve(res);
break;
default:
reject(makeRuntimeRejectMsg(workerScript, "Unrecognized function/operator for hacknet node. Valid functions: upgradeLevel(n), upgradeRam(), upgradeCore()"));
break;
}
} else {
reject(makeRuntimeRejectMsg(workerScript, "Unrecognized operation for hacknet node"));
return;
}
}, function(e) {
reject(e);
});
}, CONSTANTS.CodeInstructionRunTime);
}, function(e) {
reject(e);
});
}
function evaluateProg(exp, workerScript, index) { function evaluateProg(exp, workerScript, index) {
var env = workerScript.env; var env = workerScript.env;
@ -1016,6 +1083,10 @@ function evaluateProg(exp, workerScript, index) {
}); });
} }
function makeRuntimeRejectMsg(workerScript, msg) {
return "|"+workerScript.serverIp+"|"+workerScript.name+"|" + msg;
}
function apply_op(op, a, b) { function apply_op(op, a, b) {
function num(x) { function num(x) {
if (typeof x != "number") if (typeof x != "number")

@ -187,8 +187,48 @@ function Parser(input) {
cond: cond, cond: cond,
code: code code: code
} }
} }
/* hacknetnodes[i].operator
*/
function parse_hacknetnodes() {
var index = null;
if (is_punc("[")) {
index = parse_expression();
if (index.type != "index") {
console.log("Failed here");
unexpected();
}
}
if (is_punc(".")) {
checkPuncAndSkip(".");
var op = maybe_call(function() {
var tok = input.next();
return tok;
});
return {
type: "var",
value: "hacknetnodes",
index: index,
op: op,
}
}
console.log("Failed here");
unexpected();
}
function parse_arrayindex() {
var index = delimited("[", "]", ";", parse_expression);
var val = 0;
if (index.length == 1 && (index[0].type == "num" || index[0].type == "var")) {
val = index[0];
} else {
console.log("WARNING: Extra indices passed in")
}
return { type: "index", value: val };
}
function parse_bool() { function parse_bool() {
return { return {
@ -211,13 +251,16 @@ function Parser(input) {
return exp; return exp;
} }
if (is_punc("{")) return parse_prog(); if (is_punc("{")) return parse_prog();
if (is_punc("[")) return parse_arrayindex();
if (is_kw("if")) return parse_if(); if (is_kw("if")) return parse_if();
if (is_kw("for")) return parse_for(); if (is_kw("for")) return parse_for();
if (is_kw("while")) return parse_while(); if (is_kw("while")) return parse_while();
//if (is_kw("hacknetnodes")) return parse_hacknetnodes();
//Note, let for loops be function calls (call node types) //Note, let for loops be function calls (call node types)
if (is_kw("true") || is_kw("false")) return parse_bool(); if (is_kw("true") || is_kw("false")) return parse_bool();
var tok = input.next(); var tok = input.next();
if (tok.type == "var" && tok.value == "hacknetnodes") return parse_hacknetnodes();
if (tok.type == "var" || tok.type == "num" || tok.type == "str") if (tok.type == "var" || tok.type == "num" || tok.type == "str")
return tok; return tok;
unexpected(); unexpected();

@ -6,7 +6,7 @@
* {type: "punc", value: "(" } // punctuation: parens, comma, semicolon etc. * {type: "punc", value: "(" } // punctuation: parens, comma, semicolon etc.
* {type: "num", value: 5 } // numbers (including floats) * {type: "num", value: 5 } // numbers (including floats)
* {type: "str", value: "Hello World!" } // strings * {type: "str", value: "Hello World!" } // strings
* {type: "kw", value: "for/if/" } // keywords, see defs below * {type: "kw", value: "for/if/..." } // keywords, see defs below
* {type: "var", value: "a" } // identifiers/variables * {type: "var", value: "a" } // identifiers/variables
* {type: "op", value: "!=" } // operator characters * {type: "op", value: "!=" } // operator characters
* {type: "bool", value: "true" } // Booleans * {type: "bool", value: "true" } // Booleans
@ -46,7 +46,7 @@ function Tokenizer(input) {
} }
function is_punc(ch) { function is_punc(ch) {
return ",;(){}[]".indexOf(ch) >= 0; return ",;(){}[].".indexOf(ch) >= 0;
} }
function is_whitespace(ch) { function is_whitespace(ch) {

@ -31,7 +31,7 @@ function runScriptsLoop() {
if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) { if (workerScripts[i].running == false && workerScripts[i].env.stopFlag == false) {
try { try {
var ast = Parser(Tokenizer(InputStream(workerScripts[i].code))); var ast = Parser(Tokenizer(InputStream(workerScripts[i].code)));
//console.log(ast); console.log(ast);
} catch (e) { } catch (e) {
dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":", e, "", ""); dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":", e, "", "");
workerScripts[i].env.stopFlag = true; workerScripts[i].env.stopFlag = true;

@ -185,7 +185,10 @@ Script.prototype.updateRamUsage = function() {
var getServerMoneyAvailableCount = numOccurrences(codeCopy, "getServerMoneyAvailable("); var getServerMoneyAvailableCount = numOccurrences(codeCopy, "getServerMoneyAvailable(");
var numOperators = numNetscriptOperators(codeCopy); var numOperators = numNetscriptOperators(codeCopy);
var purchaseHacknetCount = numOccurrences(codeCopy, "purchaseHacknetNode("); var purchaseHacknetCount = numOccurrences(codeCopy, "purchaseHacknetNode(");
var upgradeHacknetCount = numOccurrences(codeCopy, "upgradeHacknetNode("); var hacknetnodesArrayCount = numOccurrences(codeCopy, "hacknetnodes[");
var hnUpgLevelCount = numOccurrences(codeCopy, ".upgradeLevel(");
var hnUpgRamCount = numOccurrences(codeCopy, ".upgradeRam()");
var hnUpgCoreCount = numOccurrences(codeCopy, ".upgradeCore()");
this.ramUsage = baseRam + this.ramUsage = baseRam +
((whileCount * CONSTANTS.ScriptWhileRamCost) + ((whileCount * CONSTANTS.ScriptWhileRamCost) +
@ -204,7 +207,10 @@ Script.prototype.updateRamUsage = function() {
(getServerMoneyAvailableCount * CONSTANTS.ScriptGetServerMoneyRamCost) + (getServerMoneyAvailableCount * CONSTANTS.ScriptGetServerMoneyRamCost) +
(numOperators * CONSTANTS.ScriptOperatorRamCost) + (numOperators * CONSTANTS.ScriptOperatorRamCost) +
(purchaseHacknetCount * CONSTANTS.ScriptPurchaseHacknetRamCost) + (purchaseHacknetCount * CONSTANTS.ScriptPurchaseHacknetRamCost) +
(upgradeHacknetCount * CONSTANTS.ScriptUpgradeHacknetRamCost)); (hacknetnodesArrayCount * CONSTANTS.ScriptHacknetNodesRamCost) +
(hnUpgLevelCount * CONSTANTS.ScriptHNUpgLevelRamCost) +
(hnUpgRamCount * CONSTANTS.ScriptHNUpgRamRamCost) +
(hnUpgCoreCount * CONSTANTS.ScriptHNUpgCoreRamCost));
console.log("ram usage: " + this.ramUsage); console.log("ram usage: " + this.ramUsage);
if (isNaN(this.ramUsage)) { if (isNaN(this.ramUsage)) {
dialogBoxCreate("ERROR in calculating ram usage. This is a bug, please report to game develoepr"); dialogBoxCreate("ERROR in calculating ram usage. This is a bug, please report to game develoepr");

@ -1080,6 +1080,7 @@ var Engine = {
//DEBUG //DEBUG
document.getElementById("debug-delete-scripts-link").addEventListener("click", function() { document.getElementById("debug-delete-scripts-link").addEventListener("click", function() {
console.log("Deleting running scripts on home computer");
Player.getHomeComputer().runningScripts = []; Player.getHomeComputer().runningScripts = [];
return false; return false;
}); });