Finished BETA version of Missions. All other changes for v0.29.1

This commit is contained in:
danielyxie 2017-09-27 10:13:42 -05:00
parent c84417607f
commit 22a5d3b3cc
9 changed files with 1989 additions and 1795 deletions

@ -34,7 +34,6 @@
background-color:blue;
}
.hack-mission-player-node:hover,
.hack-mission-player-node-active {
border: 2px solid white;
color: #6666ff;

3466
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

@ -6,6 +6,7 @@ import {getServer} from "./Server.js";
import {dialogBoxCreate} from "../utils/DialogBox.js";
import {printArray} from "../utils/HelperFunctions.js";
import {logBoxCreate} from "../utils/LogBox.js";
import numeral from "../utils/numeral.min.js";
import {formatNumber} from "../utils/StringHelperFunctions.js";
@ -183,10 +184,10 @@ function updateActiveScriptsItems() {
total += updateActiveScriptsItemContent(workerScripts[i]);
}
document.getElementById("active-scripts-total-prod").innerHTML =
"Total online production of Active Scripts: $" + formatNumber(total, 2) + " / second<br>" +
"Total online production since last Augmentation installation: $" +
formatNumber(Player.scriptProdSinceLastAug, 2) + " ($" +
formatNumber(Player.scriptProdSinceLastAug / Player.playtimeSinceLastAug, 2) + " / second)";
"Total online production of Active Scripts: " + numeral(total).format('$0.000a') + " / sec<br>" +
"Total online production since last Aug installation: " +
numeral(Player.scriptProdSinceLastAug).format('$0.000a') + " (" +
numeral(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000)).format('$0.000a') + " / sec)";
return total;
}

@ -124,31 +124,30 @@ let CONSTANTS = {
//Hacking Missions
HackingMissionRepToDiffConversion: 5000, //Faction rep is divided by this to get mission difficulty
HackingMissionRepToRewardConversion: 20, //Faction rep divided byt his to get mission rep reward
HackingMissionRepToRewardConversion: 12, //Faction rep divided byt his to get mission rep reward
HackingMissionSpamTimeIncrease: 20000, //How much time limit increase is gained when conquering a Spam Node (ms)
HackingMissionTransferAttackIncrease: 1.05, //Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node
HackingMissionHowToPlay: "Hacking missions are a minigame that, if won, will reward you with faction reputation.<br><br>" +
"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 " +
"to capture all of the enemy's database nodes, which are the parallelogram-shaped ones, 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> " +
"Attack - Targets an enemy Node and lowers its HP. The effectiveness is determined by the Node's Attack, the Player's " +
"Attack - Targets an enemy Node and lowers its HP. The effectiveness is determined by the owner's Attack, the Player's " +
"hacking level, and the enemy's defense.<br>" +
"Scan - Targets an enemy Node and lowers its Defense. The effectiveness is determined by the Player's hacking level and the " +
"Scan - Targets an enemy Node and lowers its Defense. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the " +
"enemy's defense.<br>" +
"Weaken - Targets an enemy Node and lowers its Attack. The effectiveness is determined by the Player's hacking level and the enemy's " +
"Weaken - Targets an enemy Node and lowers its Attack. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the enemy's " +
"defense.<br>" +
"Fortify - Raises the Node's Defense. The effectiveness is determined by your hacking level.<br><br>" +
"To capture a Node, you must lower its HP down to 0. " +
"A Node's 'Attack' stats affects its effectiveness when attacking other Nodes. A Node's 'Defense' helps protect " +
"against the actions of enemy Nodes. One important thing to note is that, when defending, your total 'Defense' " +
"(sum of the Defense of all of your Nodes) is what's taken into account when determining the effect of offensive actions. " +
"However, when attacking, only the 'Attack' of the Node being used to attack is taken into account.<br><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>" +
"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." +
"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<br>" +
"CPU Core - These are your main Nodes that are used to perform actions. Capable of performing every action<br>" +
"Firewall - Nodes with high defense. These Nodes cannot perform any actions<br>" +
"Database - A special type of Node. The player's objective is to conquer all of the enemy's Database Nodes within " +
"the time limit. These Nodes cannot perform any actions<br>" +
@ -157,10 +156,16 @@ let CONSTANTS = {
"Transfer - Conquering one of these nodes will increase the Attack of all of your CPU Cores by a small fixed percentage. " +
"These Nodes are capable of performing every action except the 'Attack' action<br>" +
"Shield - Nodes with high defense. These Nodes cannot perform any actions<br><br>" +
"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>" +
"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 only target " +
"Nodes that are adjacent to one of your Nodes (immediately above, below, or to the side. NOT diagonal). Furthermore, only CPU Cores and Transfer Nodes " +
"can target, since they are the only ones that can perform actions",
"can target, since they are the only ones that can perform actions. To remove a target, you can simply click on the line that represents " +
"the connection between one of your Nodes and its target. Alternatively, you can select the 'source' Node and click the 'Drop Connection' button, " +
"or press 'd',",
//Gang constants
@ -418,6 +423,9 @@ let CONSTANTS = {
"args.length<br><br>" +
"Note that none of the other functions that typically work with arrays, such as remove(), insert(), clear(), etc., will work on the " +
"args array.<br><br>" +
"<u><h1>Javascript Modules</h1></u><br>" +
"Netscript supports the following Javascript Modules:<br><br>" +
"Math<br>Date (static functions only)<br><br>" +
"<u><h1> Functions </h1></u><br>" +
"You can NOT define you own functions in Netscript (yet), but there are several built in functions that " +
"you may use: <br><br> " +
@ -649,8 +657,9 @@ let CONSTANTS = {
"on the server specified by the hostname/ip. The argument must be a string with the hostname/ip of the target server.<br><br>" +
"<i>getScriptIncome([scriptname], [hostname/ip], [args...])</i><br>" +
"Returns the amount of income the specified script generates while online (when the game is open, does not apply for " +
"offline income). This function can also return the total income of all of your active scripts by running the function " +
"with no arguments.<br><br>" +
"offline income). This function can also be called with no arguments. If called with no arguments, then this function " +
"will return an array of two values. The first value is the total income ($/sec) of all of your active scripts (currently running). " +
"The second value is the total income ($/sec) from scripts since you last installed Augmentations (or destroyed a BitNode).<br><br>" +
"Remember that a script is uniquely identified by both its name and its arguments. So for example if you ran a script " +
"with the arguments 'foodnstuff' and '5' then in order to use this function to get that script's income you must " +
"specify those arguments in this function call.<br><br>" +
@ -669,6 +678,8 @@ let CONSTANTS = {
"The second argument must be a string with the hostname/IP of the target server. If the first argument is specified " +
"then the second argument must be specified as well. Any additional arguments passed to the function will specify " +
"the arguments passed into the target script.<br><br>" +
"<i>getTimeSinceLastAug()</i><br>" +
"Returns the amount of time in milliseconds that have passed since you last installed Augmentations (or destroyed a BitNode).<br><br>" +
"<u><h1>Hacknet Nodes API</h1></u><br>" +
"Netscript provides the following API for accessing and upgrading your Hacknet Nodes through scripts. This API does NOT work offline.<br><br>" +
"<i>hacknetnodes</i><br> A special variable. This is an array that maps to the Player's Hacknet Nodes. The Hacknet Nodes are accessed through " +
@ -998,28 +1009,24 @@ let CONSTANTS = {
LatestUpdate:
"v0.29.1<br>" +
"-New gameplay feature that is currently in BETA: Hacking Missions. Hacking Missions is an active gameplay mechanic (its a minigame) " +
"that is meant to be used to earn faction reputation. However, since this is currently in beta, hacking missions will NOT grant reputation " +
"for the time being, since the feature likely has many bugs, balance problems, and other issues. If you have any feedback " +
"regarding the new feature, feel free to let me know<br>" +
"-CHANGED THE RETURN VALUE OF getScriptIncome() WHEN RAN WITH NO ARGUMENTS. It will now return an array of " +
"two values rather than a single value. This may break your scripts, so make sure to update them!<br>" +
"-Added continue statement for for/while loops<br>" +
"-Added getServerMinSecurityLevel() Netscript function<br>" +
"-Added getServerMinSecurityLevel(), getPurchasedServers(), and getTimeSinceLastAug() Netscript functions<br>" +
"-Netscript scp() function can now take an array as the first argument, and will try to copy " +
"every file specified in the array (it will just call scp() normally for every element in the array). " +
"If an array is passed in, then the scp() function returns true if at least one element from the array is successfully copied<br>" +
"-Added Javascript's Date module to Netscript. Since 'new' is not supported in Netscript yet, only the Date module's " +
"static methods will work (now(), UTC(), parse(), etc.).<br>" +
"-Failing a crime now gives half the experience it did before<br>" +
"-The repeated 'Find The-Cave' message after installing The Red Pill Augmentation now only happens " +
"if you've never destroyed a BitNode before<br>" +
"-fileExists() function now works on literature files<br><br>" +
"v0.29.0<br>" +
"-Added BitNode-5: Artificial Intelligence<br>" +
"-Added getIp(), getIntelligence(), getHackingMultipliers(), and getBitNodeMultipliers() Netscript functions (requires Source-File 5)<br>" +
"-Updated scan() Netscript function so that you can choose to have it print IPs rather than hostnames<br>" +
"-Refactored scp() Netscript function so that it takes an optional 'source server' argument<br>" +
"-For Infiltration, decreased the percentage by which the security level increases by " +
"about 10% for every location<br>" +
"-Using :w in the script editor's Vim keybinding mode should now save and quit to Terminal<br>" +
"-Some minor optimizations that should reduce the size of the save file<br>" +
"-scan-analyze Terminal command will no longer show your purchased servers, unless you pass a '-a' flag into the command<br>" +
"-After installing the Red Pill augmentation from Daedalus, the message telling you to find 'The-Cave' " +
"will now repeatedly pop up regardless of whether or not you have messages suppressed<br>" +
"-Various bugfixes",
"-The forced repeated 'Find The-Cave' message after installing The Red Pill Augmentation now only happens " +
"if you've never destroyed a BitNode before, and will only popup every 15 minutes. If you have already destroyed a BitNode, " +
"the message will not pop up if you have messages suppressed (if you don't have messages suppressed it WILL still repeatedly popup)<br>" +
"-fileExists() function now works on literature files<br><br>",
}
export {CONSTANTS};

@ -1,7 +1,9 @@
import {Augmentations, Augmentation,
AugmentationNames} from "./Augmentations.js";
import {Programs} from "./CreateProgram.js";
import {inMission} from "./Missions.js";
import {Player} from "./Player.js";
import {redPillFlag} from "./RedPill.js";
import {GetServerByHostname} from "./Server.js";
import {Settings} from "./Settings.js";
import {dialogBoxCreate, dialogBoxOpened} from "../utils/DialogBox.js";
@ -75,10 +77,15 @@ function checkForMessagesToSend() {
redpillOwned = true;
}
if (redpill && redpillOwned && Player.sourceFiles.length === 0) {
if (redpill && redpillOwned && Player.sourceFiles.length === 0 && !redPillFlag && !inMission) {
if (!dialogBoxOpened) {
sendMessage(redpill, true);
}
} else if (redpill && redpillOwned) {
//If player has already destroyed a BitNode, message is not forced
if (!redPillFlag && !inMission && !dialogBoxOpened) {
sendMessage(redpill);
}
} else if (jumper0 && !jumper0.recvd && Player.hacking_skill >= 25) {
sendMessage(jumper0);
Player.getHomeComputer().programs.push(Programs.Flight);
@ -96,8 +103,6 @@ function checkForMessagesToSend() {
sendMessage(jumper4);
} else if (bitrunnersTest && !bitrunnersTest.recvd && Player.hacking_skill >= 500) {
sendMessage(bitrunnersTest);
} else if (redpill && redpillOwned) {
sendMessage(redpill);
}
}

@ -156,15 +156,17 @@ function HackingMission(rep, fac) {
this.faction = fac;
this.started = false;
this.time = 120000; //2 minutes, milliseconds
this.time = 180000; //2 minutes, milliseconds
this.playerCores = [];
this.playerNodes = []; //Non-core nodes
this.playerAtk = 0;
this.playerDef = 0;
this.enemyCores = [];
this.enemyDatabases = [];
this.enemyNodes = []; //Non-core nodes
this.enemyAtk = 0;
this.enemyDef = 0;
this.miscNodes = [];
@ -190,7 +192,7 @@ function HackingMission(rep, fac) {
//difficulty capped at 16
this.difficulty = Math.min(16, Math.round(rep / CONSTANTS.HackingMissionRepToDiffConversion) + 1);
console.log("difficulty: " + this.difficulty);
this.reward = 200 + (rep / CONSTANTS.HackingMissionRepToRewardConversion);
this.reward = 250 + (rep / CONSTANTS.HackingMissionRepToRewardConversion);
}
HackingMission.prototype.init = function() {
@ -213,7 +215,7 @@ HackingMission.prototype.init = function() {
//Randomly generate enemy nodes (CPU and Firewall) based on difficulty
var numNodes = getRandomInt(this.difficulty, this.difficulty + 1);
var numFirewalls = getRandomInt(this.difficulty, this.difficulty + 2);
var numFirewalls = getRandomInt(this.difficulty, this.difficulty + 1);
var numDatabases = getRandomInt(this.difficulty, this.difficulty + 1);
var totalNodes = numNodes + numFirewalls + numDatabases;
var xlimit = 7 - Math.floor(totalNodes / 8);
@ -254,6 +256,7 @@ HackingMission.prototype.init = function() {
this.enemyDatabases.push(node);
}
this.calculateDefenses();
this.calculateAttacks();
this.createMap();
}
@ -297,11 +300,23 @@ HackingMission.prototype.createPageDom = function() {
startBtn.style.display = "inline-block";
startBtn.addEventListener("click", ()=>{
this.start();
return false;
});
var forfeitMission = document.createElement("a");
forfeitMission.innerHTML = "Forfeit Mission (Exit)";
forfeitMission.classList.add("a-link-button");
forfeitMission.classList.add("hack-mission-header-element");
forfeitMission.style.display = "inline-block";
forfeitMission.addEventListener("click", ()=> {
this.finishMission(false);
return false;
});
var timer = document.createElement("p");
timer.setAttribute("id", "hacking-mission-timer");
timer.style.display = "inline-block";
timer.style.margin = "6px";
//Create Action Buttons (Attack/Scan/Weaken/ etc...)
var actionsContainer = document.createElement("span");
@ -353,18 +368,18 @@ HackingMission.prototype.createPageDom = function() {
this.actionButtons[5].appendChild(dropconnTooltip);
//Player/enemy defense displays will be in action container
var playerDefense = document.createElement("p");
var enemyDefense = document.createElement("p");
playerDefense.style.display = "inline-block";
enemyDefense.style.display = "inline-block";
playerDefense.style.color = "blue";
enemyDefense.style.color = "red";
playerDefense.style.margin = "4px";
enemyDefense.style.margin = "4px";
playerDefense.setAttribute("id", "hacking-mission-player-def");
enemyDefense.setAttribute("id", "hacking-mission-enemy-def");
actionsContainer.appendChild(playerDefense);
actionsContainer.appendChild(enemyDefense);
var playerStats = document.createElement("p");
var enemyStats = document.createElement("p");
playerStats.style.display = "inline-block";
enemyStats.style.display = "inline-block";
playerStats.style.color = "#00ccff";
enemyStats.style.color = "red";
playerStats.style.margin = "4px";
enemyStats.style.margin = "4px";
playerStats.setAttribute("id", "hacking-mission-player-stats");
enemyStats.setAttribute("id", "hacking-mission-enemy-stats");
actionsContainer.appendChild(playerStats);
actionsContainer.appendChild(enemyStats);
//Set Action Button event listeners
this.actionButtons[0].addEventListener("click", ()=>{
@ -434,6 +449,7 @@ HackingMission.prototype.createPageDom = function() {
container.appendChild(inGameGuideBtn);
container.appendChild(wikiGuideBtn);
container.appendChild(startBtn);
container.appendChild(forfeitMission);
container.appendChild(timer);
container.appendChild(actionsContainer);
container.appendChild(timeDisplay);
@ -485,7 +501,34 @@ HackingMission.prototype.setActionButton = function(i, active=true) {
}
//Should only be used at the start
HackingMission.prototype.calculateAttacks = function() {
var total = 0;
for (var i = 0; i < this.playerCores.length; ++i) {
total += this.playerCores[i].atk;
}
for (var i = 0; i < this.playerNodes.length; ++i) {
total += this.playerNodes[i].atk;
}
this.playerAtk = total;
document.getElementById("hacking-mission-player-stats").innerHTML =
"Player Attack: " + formatNumber(this.playerAtk, 1) + "<br>" +
"Player Defense: " + formatNumber(this.playerDef, 1);
total = 0;
for (var i = 0; i < this.enemyCores.length; ++i) {
total += this.enemyCores[i].atk;
}
for (var i = 0; i < this.enemyDatabases.length; ++i) {
total += this.enemyDatabases[i].atk;
}
for (var i = 0; i < this.enemyNodes.length; ++i) {
total += this.enemyNodes[i].atk;
}
this.enemyAtk = total;
document.getElementById("hacking-mission-enemy-stats").innerHTML =
"Enemy Attack: " + formatNumber(this.enemyAtk, 1) + "<br>" +
"Enemy Defense: " + formatNumber(this.enemyDef, 1);
}
HackingMission.prototype.calculateDefenses = function() {
var total = 0;
for (var i = 0; i < this.playerCores.length; ++i) {
@ -495,7 +538,8 @@ HackingMission.prototype.calculateDefenses = function() {
total += this.playerNodes[i].def;
}
this.playerDef = total;
document.getElementById("hacking-mission-player-def").innerText =
document.getElementById("hacking-mission-player-stats").innerHTML =
"Player Attack: " + formatNumber(this.playerAtk, 1) + "<br>" +
"Player Defense: " + formatNumber(this.playerDef, 1);
total = 0;
for (var i = 0; i < this.enemyCores.length; ++i) {
@ -508,7 +552,8 @@ HackingMission.prototype.calculateDefenses = function() {
total += this.enemyNodes[i].def;
}
this.enemyDef = total;
document.getElementById("hacking-mission-enemy-def").innerText =
document.getElementById("hacking-mission-enemy-stats").innerHTML =
"Enemy Attack: " + formatNumber(this.enemyAtk, 1) + "<br>" +
"Enemy Defense: " + formatNumber(this.enemyDef, 1);
}
@ -563,16 +608,16 @@ HackingMission.prototype.createMap = function() {
case 0: //Spam
var stats = {
atk: 0,
def: randMult * getRandomInt(30, 40),
hp: randMult * getRandomInt(70, 90)
def: randMult * getRandomInt(35, 55),
hp: randMult * getRandomInt(125, 150)
}
node = new Node(NodeTypes.Spam, stats);
break;
case 1: //Transfer
var stats = {
atk: 0,
def: randMult * getRandomInt(50, 70),
hp: randMult * getRandomInt(80, 95)
def: randMult * getRandomInt(45, 65),
hp: randMult * getRandomInt(150, 175)
}
node = new Node(NodeTypes.Transfer, stats);
break;
@ -580,8 +625,8 @@ HackingMission.prototype.createMap = function() {
default:
var stats = {
atk: 0,
def: randMult * getRandomInt(90, 105),
hp: randMult * getRandomInt(130, 150)
def: randMult * getRandomInt(60, 80),
hp: randMult * getRandomInt(200, 250)
}
node = new Node(NodeTypes.Shield, stats);
break;
@ -779,11 +824,8 @@ HackingMission.prototype.start = function() {
this.started = true;
this.initJsPlumb();
var startBtn = clearEventListeners("hack-mission-start-btn");
startBtn.innerHTML = "Forfeit Mission";
startBtn.addEventListener("click", ()=>{
this.finishMission(false);
return false;
});
startBtn.classList.remove("a-link-button");
startBtn.classList.add("a-link-button-inactive");
}
HackingMission.prototype.initJsPlumb = function() {
@ -806,8 +848,8 @@ HackingMission.prototype.initJsPlumb = function() {
instance.makeSource(this.playerCores[i].el, {
deleteEndpointsOnEmpty:true,
maxConnections:1,
anchor:"Center",
connector:"Straight"
anchor:"Continuous",
connector:"Flowchart"
});
}
@ -815,29 +857,29 @@ HackingMission.prototype.initJsPlumb = function() {
for (var i = 0; i < this.enemyCores.length; ++i) {
instance.makeTarget(this.enemyCores[i].el, {
maxConnections:-1,
anchor:"Center",
connector:"Straight"
anchor:"Continuous",
connector:"Flowchart"
});
}
for (var i = 0; i < this.enemyDatabases.length; ++i) {
instance.makeTarget(this.enemyDatabases[i].el, {
maxConnections:-1,
anchor:"Center",
connector:["Straight"]
anchor:"Continuous",
connector:["Flowchart"]
});
}
for (var i = 0; i < this.enemyNodes.length; ++i) {
instance.makeTarget(this.enemyNodes[i].el, {
maxConnections:-1,
anchor:"Center",
connector:"Straight"
anchor:"Continuous",
connector:"Flowchart"
});
}
for (var i = 0; i < this.miscNodes.length; ++i) {
instance.makeTarget(this.miscNodes[i].el, {
maxConnections:-1,
anchor:"Center",
connector:"Straight"
anchor:"Continuous",
connector:"Flowchart"
});
}
@ -888,45 +930,60 @@ HackingMission.prototype.dropAllConnectionsToNode = function(node) {
}
}
var storedCycles = 0;
HackingMission.prototype.process = function(numCycles=1) {
if (!this.started) {return;}
storedCycles += numCycles;
if (storedCycles < 3) {return;} //Only process every 2 cycles minimum
var res = false;
//Process actions of all player nodes
this.playerCores.forEach((node)=>{
res |= this.processNode(node, numCycles);
res |= this.processNode(node, storedCycles);
});
this.playerNodes.forEach((node)=>{
if (node.type === NodeTypes.Transfer) {
res |= this.processNode(node, numCycles);
res |= this.processNode(node, storedCycles);
}
});
//Process actions of all enemy nodes
this.enemyCores.forEach((node)=>{
res |= this.processNode(node, numCycles);
res |= this.processNode(node, storedCycles);
});
this.enemyNodes.forEach((node)=>{
if (node.type === NodeTypes.Transfer) {
res |= this.processNode(node, numCycles);
res |= this.processNode(node, storedCycles);
}
});
if (res) {this.calculateDefenses();}
if (res) {
this.calculateAttacks();
this.calculateDefenses();
}
if (this.enemyDatabases.length === 0) {
this.finishMission(true);
return;
}
//Defense of every misc Node increase by 1 per second
this.miscNodes.forEach((node)=>{
node.def += (0.1 * storedCycles);
this.updateNodeDomElement(node);
});
//Update timer and check if player lost
this.time -= (numCycles * Engine._idleSpeed);
this.time -= (storedCycles * Engine._idleSpeed);
if (this.time <= 0) {
this.finishMission(false);
return;
}
this.updateTimer();
storedCycles = 0;
}
//Returns a bool representing whether defenses need to be re-calculated
@ -935,48 +992,52 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
return;
}
var targetNode = null, def;
var targetNode = null, def, atk;
if (nodeObj.conn) {
targetNode = this.getNodeFromElement(nodeObj.conn.target);
if (targetNode.plyrCtrl) {
def = this.playerDef;
atk = this.enemyAtk;
} else if (targetNode.enmyCtrl) {
def = this.enemyDef;
atk = this.playerAtk;
} else { //Misc Node
def = targetNode.def;
nodeObj.plyrCtrl ? atk = this.playerAtk : atk = this.enemyAtk;
}
}
//Calculations are per second, so divide everything by 5
var calcDefenses = false;
var calcStats = false;
switch(nodeObj.action) {
case NodeActions.Attack:
if (nodeObj.conn === null) {break;}
var dmg = this.calculateAttackDamage(nodeObj.atk, def, Player.hacking_skill);
var dmg = this.calculateAttackDamage(atk, def, Player.hacking_skill);
targetNode.hp -= (dmg/5 * numCycles);
break;
case NodeActions.Scan:
if (nodeObj.conn === null) {break;}
var eff = this.calculateScanEffect(nodeObj.atk, def, Player.hacking_skill);
var eff = this.calculateScanEffect(atk, def, Player.hacking_skill);
targetNode.def -= (eff/5 * numCycles);
calcDefenses = true;
calcStats = true;
break;
case NodeActions.Weaken:
if (nodeObj.conn === null) {break;}
var eff = this.calculateWeakenEffect(nodeObj.atk, def, Player.hacking_skill);
var eff = this.calculateWeakenEffect(atk, def, Player.hacking_skill);
targetNode.atk -= (eff/5 * numCycles);
calcStats = true;
break;
case NodeActions.Fortify:
var eff = this.calculateFortifyEffect(Player.hacking_skill);
nodeObj.def += (eff/5 * numCycles);
calcDefenses = true;
calcStats = true;
break;
case NodeActions.Overflow:
var eff = this.calculateOverflowEffect(Player.hacking_skill);
if (nodeObj.def < eff) {break;}
nodeObj.def -= (eff/5 * numCycles);
nodeObj.atk += (eff/5 * numCycles);
calcDefenses = true;
calcStats = true;
break;
default:
console.log("ERR: Invalid Node Action: " + nodeObj.action);
@ -1014,20 +1075,20 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
this.jsplumbinstance.makeSource(targetNode.el, {
deleteEndpointsOnEmpty:true,
maxConnections:1,
anchor:"Center",
connector:"Straight"
anchor:"Continuous",
connector:"Flowchart"
});
} else {
targetNode.setControlledByEnemy();
this.jsplumbinstance.unmakeSource(targetNode.el);
this.jsplumbinstance.makeTarget(targetNode.el, {
maxConnections:-1,
anchor:"Center",
connector:["Straight"]
anchor:"Continuous",
connector:["Flowchart"]
});
}
calcDefenses = true;
calcStats = true;
//Helper function to swap nodes between the respective enemyNodes/playerNodes arrays
function swapNodes(orig, dest, targetNode) {
@ -1105,24 +1166,24 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
}
this.updateNodeDomElement(nodeObj);
if (targetNode) {this.updateNodeDomElement(targetNode);}
return calcDefenses;
return calcStats;
}
var hackEffWeightSelf = 100; //Weight for Node actions on self
var hackEffWeightTarget = 15; //Weight for Node Actions against Target
var hackEffWeightAttack = 100; //Weight for Attack action
var hackEffWeightSelf = 150; //Weight for Node actions on self
var hackEffWeightTarget = 25; //Weight for Node Actions against Target
var hackEffWeightAttack = 110; //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, 0.1);
return Math.max(atk + (hacking / hackEffWeightAttack) - def, 1);
}
HackingMission.prototype.calculateScanEffect = function(atk, def, hacking=0) {
return Math.max((atk/2) + hacking / hackEffWeightTarget - def, 0.1);
return Math.max((atk/2) + hacking / hackEffWeightTarget - def, 1);
}
HackingMission.prototype.calculateWeakenEffect = function(atk, def, hacking=0) {
return Math.max((atk/2) + hacking / hackEffWeightTarget - def, 0.1);
return Math.max((atk/2) + hacking / hackEffWeightTarget - def, 1);
}
HackingMission.prototype.calculateFortifyEffect = function(hacking=0) {
@ -1151,9 +1212,11 @@ HackingMission.prototype.finishMission = function(win) {
currMission = null;
if (win) {
dialogBoxCreate("Mission won!");
dialogBoxCreate("Mission won! This feature is currently in " +
"beta so you did not earn anything, but normally you would have won " +
this.reward + " reputation with " + this.faction.name);
} else {
dialogBoxCreate("Mission lost!");
dialogBoxCreate("Mission lost/forfeited!");
}
//Clear mission container

@ -469,6 +469,16 @@ function NetscriptFunctions(workerScript) {
if (arguments.length !== 2 && arguments.length !== 3) {
throw makeRuntimeRejectMsg(workerScript, "Error: scp() call has incorrect number of arguments. Takes 2 or 3 arguments");
}
if (scriptname && scriptname.constructor === Array) {
//Recursively call scp on all elements of array
var res = false;
scriptname.forEach(function(script) {
if (NetscriptFunctions(workerScript).scp(script, ip1, ip2)) {
res = true;
};
});
return res;
}
if (!scriptname.endsWith(".lit") && !scriptname.endsWith(".script")) {
throw makeRuntimeRejectMsg(workerScript, "Error: scp() only works for .script and .lit files");
}
@ -1124,7 +1134,10 @@ function NetscriptFunctions(workerScript) {
getScriptIncome : function(scriptname, ip) {
if (arguments.length === 0) {
//Get total script income
return updateActiveScriptsItems();
var res = [];
res.push(updateActiveScriptsItems());
res.push(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
return res;
} else {
//Get income for a particular script
var server = getServer(ip);
@ -1170,6 +1183,9 @@ function NetscriptFunctions(workerScript) {
return runningScriptObj.onlineExpGained / runningScriptObj.onlineRunningTime;
}
},
getTimeSinceLastAug : function() {
return Player.playtimeSinceLastAug;
},
/* Singularity Functions */
universityCourse(universityName, className) {

@ -275,6 +275,7 @@ PlayerObject.prototype.prestigeAugmentation = function() {
this.lastUpdate = new Date().getTime();
this.playtimeSinceLastAug = 0;
this.scriptProdSinceLastAug = 0;
this.hacknetNodes.length = 0;
this.totalHacknetNodeProduction = 0;
@ -365,6 +366,7 @@ PlayerObject.prototype.prestigeSourceFile = function() {
this.hasTixApiAccess = false;
this.playtimeSinceLastAug = 0;
this.scriptProdSinceLastAug = 0;
}
PlayerObject.prototype.getCurrentServer = function() {

@ -840,6 +840,7 @@ let Engine = {
Player.gang.process(numCycles);
}
//Mission
if (inMission && currMission) {
currMission.process(numCycles);
}
@ -967,7 +968,7 @@ let Engine = {
if (Engine.Counters.messages <= 0) {
checkForMessagesToSend();
if (Augmentations[AugmentationNames.TheRedPill].owned) {
Engine.Counters.messages = 600; //2 minutes for Red pill message
Engine.Counters.messages = 4500; //15 minutes for Red pill message
} else {
Engine.Counters.messages = 150;
}