Merge pull request #823 from danielyxie/dev

v0.50.2
This commit is contained in:
hydroflame 2021-03-25 21:16:22 -04:00 committed by GitHub
commit 93f8785ec6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 312 additions and 144 deletions

File diff suppressed because one or more lines are too long

18
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -66,7 +66,7 @@ documentation_title = '{0} Documentation'.format(project)
# The short X.Y version. # The short X.Y version.
version = '0.50' version = '0.50'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.50.1' release = '0.50.2'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

@ -24,6 +24,8 @@ level 3, then you will be able to access all of the Singularity Functions.
travelToCity() <singularityfunctions/travelToCity> travelToCity() <singularityfunctions/travelToCity>
purchaseTor() <singularityfunctions/purchaseTor> purchaseTor() <singularityfunctions/purchaseTor>
purchaseProgram() <singularityfunctions/purchaseProgram> purchaseProgram() <singularityfunctions/purchaseProgram>
connect() <singularityfunctions/connect>
manualHack() <singularityfunctions/manualHack>
getStats() <singularityfunctions/getStats> getStats() <singularityfunctions/getStats>
getCharacterInformation() <singularityfunctions/getCharacterInformation> getCharacterInformation() <singularityfunctions/getCharacterInformation>
isBusy() <singularityfunctions/isBusy> isBusy() <singularityfunctions/isBusy>

@ -0,0 +1,20 @@
connect() Netscript Function
============================
.. js:function:: connect(hostname)
:RAM cost: 2 GB
:param string hostname: hostname of the server to connect.
:returns: ``true`` if the connection was a success.
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.
This function will connect you to the specified server if it's directly connected to the current server.
You can also pass in 'home' to return to your home server from anywhere.
Examples:
.. code-block:: javascript
connect("joesguns");
connect("CSEC");

@ -0,0 +1,24 @@
manualHack() Netscript Function
===============================
.. js:function:: manualHack()
:RAM cost: 2 GB
:returns: The amount of money stolen if the hack is successful, and zero otherwise
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.
This function will perform a manual hack on the server you are currently connected to.
This is typically required to join factions.
Examples:
.. code-block:: javascript
connect("CSEC");
manualHack();
.. warning::
For NS2 users:
This function is async.

@ -31,6 +31,8 @@ Alt + f Switch to 'Factions' page
Alt + a Switch to 'Augmentations' page Alt + a Switch to 'Augmentations' page
Alt + u Switch to 'Tutorial' page Alt + u Switch to 'Tutorial' page
Alt + o Switch to 'Options' page Alt + o Switch to 'Options' page
Alt + g Switch to 'Gang' page
Alt + b Switch to 'Bladeburner' page
========== =========================================================================== ========== ===========================================================================
Script Editor Script Editor

@ -274,6 +274,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.InfiltrationMoney = 3; BitNodeMultipliers.InfiltrationMoney = 3;
BitNodeMultipliers.FactionWorkRepGain = 0.5; BitNodeMultipliers.FactionWorkRepGain = 0.5;
BitNodeMultipliers.FactionPassiveRepGain = 0; BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.GangKarmaRequirement = 0;
break; break;
case 3: // Corporatocracy case 3: // Corporatocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.8; BitNodeMultipliers.HackingLevelMultiplier = 0.8;
@ -289,6 +290,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.HacknetNodeMoney = 0.25; BitNodeMultipliers.HacknetNodeMoney = 0.25;
BitNodeMultipliers.HomeComputerRamCost = 1.5; BitNodeMultipliers.HomeComputerRamCost = 1.5;
BitNodeMultipliers.PurchasedServerCost = 2; BitNodeMultipliers.PurchasedServerCost = 2;
BitNodeMultipliers.GangKarmaRequirement = 3;
break; break;
case 4: // The Singularity case 4: // The Singularity
BitNodeMultipliers.ServerMaxMoney = 0.15; BitNodeMultipliers.ServerMaxMoney = 0.15;
@ -331,6 +333,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.FactionPassiveRepGain = 0; BitNodeMultipliers.FactionPassiveRepGain = 0;
BitNodeMultipliers.HackExpGain = 0.25; BitNodeMultipliers.HackExpGain = 0.25;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
break; break;
case 7: // Bladeburner 2079 case 7: // Bladeburner 2079
BitNodeMultipliers.BladeburnerRank = 0.6; BitNodeMultipliers.BladeburnerRank = 0.6;
@ -351,6 +354,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.FourSigmaMarketDataCost = 2; BitNodeMultipliers.FourSigmaMarketDataCost = 2;
BitNodeMultipliers.FourSigmaMarketDataApiCost = 2; BitNodeMultipliers.FourSigmaMarketDataApiCost = 2;
BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed BitNodeMultipliers.DaedalusAugsRequirement = 1.166; // Results in 35 Augs needed
BitNodeMultipliers.GangKarmaRequirement = 5;
break; break;
case 8: // Ghost of Wall Street case 8: // Ghost of Wall Street
BitNodeMultipliers.ScriptHackMoney = 0.3; BitNodeMultipliers.ScriptHackMoney = 0.3;
@ -363,6 +367,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.RepToDonateToFaction = 0; BitNodeMultipliers.RepToDonateToFaction = 0;
BitNodeMultipliers.CorporationValuation = 0; BitNodeMultipliers.CorporationValuation = 0;
BitNodeMultipliers.CodingContractMoney = 0; BitNodeMultipliers.CodingContractMoney = 0;
BitNodeMultipliers.GangKarmaRequirement = 10;
break; break;
case 9: // Hacktocracy case 9: // Hacktocracy
BitNodeMultipliers.HackingLevelMultiplier = 0.4; BitNodeMultipliers.HackingLevelMultiplier = 0.4;
@ -384,6 +389,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.FourSigmaMarketDataApiCost = 4; BitNodeMultipliers.FourSigmaMarketDataApiCost = 4;
BitNodeMultipliers.BladeburnerRank = 0.9; BitNodeMultipliers.BladeburnerRank = 0.9;
BitNodeMultipliers.BladeburnerSkillCost = 1.2; BitNodeMultipliers.BladeburnerSkillCost = 1.2;
BitNodeMultipliers.GangKarmaRequirement = 3;
break; break;
case 10: // Digital Carbon case 10: // Digital Carbon
BitNodeMultipliers.HackingLevelMultiplier = 0.2; BitNodeMultipliers.HackingLevelMultiplier = 0.2;
@ -407,6 +413,7 @@ export function initBitNodeMultipliers(p: IPlayer) {
BitNodeMultipliers.PurchasedServerLimit = 0.6; BitNodeMultipliers.PurchasedServerLimit = 0.6;
BitNodeMultipliers.PurchasedServerMaxRam = 0.5; BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
BitNodeMultipliers.BladeburnerRank = 0.8; BitNodeMultipliers.BladeburnerRank = 0.8;
BitNodeMultipliers.GangKarmaRequirement = 3;
break; break;
case 11: //The Big Crash case 11: //The Big Crash
BitNodeMultipliers.HackingLevelMultiplier = 0.5; BitNodeMultipliers.HackingLevelMultiplier = 0.5;

@ -109,6 +109,11 @@ interface IBitNodeMultipliers {
*/ */
FourSigmaMarketDataCost: number; FourSigmaMarketDataCost: number;
/**
* Influences how much negative karma is required to create a gang in this bitnode.
*/
GangKarmaRequirement: number;
/** /**
* Influences the experienced gained when hacking a server. * Influences the experienced gained when hacking a server.
*/ */
@ -268,4 +273,5 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
BladeburnerSkillCost: 1, BladeburnerSkillCost: 1,
DaedalusAugsRequirement: 1, DaedalusAugsRequirement: 1,
GangKarmaRequirement: 1,
}; };

@ -6,7 +6,7 @@
import { IMap } from "./types"; import { IMap } from "./types";
export let CONSTANTS: IMap<any> = { export let CONSTANTS: IMap<any> = {
Version: "0.50.1", Version: "0.50.2",
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience /** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then * and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@ -228,21 +228,18 @@ export let CONSTANTS: IMap<any> = {
LatestUpdate: LatestUpdate:
` `
v0.50.1 - 2021-03-22 (hydroflame) v0.50.2 - 2021-03-25 Everyone asked for this one. (hydroflame)
------- -------
BitNodeMultipliers
* 'GangKarmaRequirements': a new multipler that influences how much karma is required to make a gang different bitnodes.
Netscript Netscript
* getTaskStats works * 'connect': a new singularity function that connects you to a server. (like the terminal command)
* 'manualHack': a new singularity function that performs a manual hack on the players current server.
Source-File -1 * ns2 stack trace works on Firefox now.
* Added a new Exploit
Factions
* Augmentations offered by a Faction but already bought are in a separate list at the bottom of the page.
Bug fixed
* Fixed a bug where completing a maxed non-repeatable BitNode would make its color on the BitVerse like level 1.
Misc. Misc.
* Minor spacing in stats tables. * New shortcut, Alt + b, brings you to bladeburner
* New shortcut, Alt + g, brings you to gang
` `
} }

@ -1516,7 +1516,6 @@ HackingMission.prototype.finishMission = function(win) {
var gain = this.reward * Player.faction_rep_mult * favorMult; var gain = this.reward * Player.faction_rep_mult * favorMult;
dialogBoxCreate("Mission won! You earned " + dialogBoxCreate("Mission won! You earned " +
numeralWrapper.format(gain, '0.000a') + " reputation with " + this.faction.name); numeralWrapper.format(gain, '0.000a') + " reputation with " + this.faction.name);
console.log(`diff ${this.difficulty}`);
Player.gainIntelligenceExp(this.difficulty * CONSTANTS.IntelligenceHackingMissionBaseExpGain); Player.gainIntelligenceExp(this.difficulty * CONSTANTS.IntelligenceHackingMissionBaseExpGain);
this.faction.playerReputation += gain; this.faction.playerReputation += gain;
} else { } else {

@ -176,6 +176,8 @@ export const RamCosts: IMap<any> = {
travelToCity: () => RamCostConstants.ScriptSingularityFn1RamCost, travelToCity: () => RamCostConstants.ScriptSingularityFn1RamCost,
purchaseTor: () => RamCostConstants.ScriptSingularityFn1RamCost, purchaseTor: () => RamCostConstants.ScriptSingularityFn1RamCost,
purchaseProgram: () => RamCostConstants.ScriptSingularityFn1RamCost, purchaseProgram: () => RamCostConstants.ScriptSingularityFn1RamCost,
connect: () => RamCostConstants.ScriptSingularityFn1RamCost,
manualHack: () => RamCostConstants.ScriptSingularityFn1RamCost,
getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, getStats: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, getCharacterInformation: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,
isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4, isBusy: () => RamCostConstants.ScriptSingularityFn1RamCost / 4,

@ -59,6 +59,7 @@ import {
import { HacknetServer, MaxNumberHacknetServers } from "./Hacknet/HacknetServer"; import { HacknetServer, MaxNumberHacknetServers } from "./Hacknet/HacknetServer";
import { CityName } from "./Locations/data/CityNames"; import { CityName } from "./Locations/data/CityNames";
import { LocationName } from "./Locations/data/LocationNames"; import { LocationName } from "./Locations/data/LocationNames";
import { Terminal } from "./Terminal";
import { Message } from "./Message/Message"; import { Message } from "./Message/Message";
import { Messages } from "./Message/MessageHelpers"; import { Messages } from "./Message/MessageHelpers";
@ -405,16 +406,42 @@ function NetscriptFunctions(workerScript) {
} }
if(!filename) continue if(!filename) continue
const lineRe = /.*:(\d+):\d+.*/; function parseChromeStackline(line) {
const lineMatch = stackline.match(lineRe); const lineRe = /.*:(\d+):\d+.*/;
const funcRe = /.*at (.+) \(.*/;
const lineMatch = line.match(lineRe);
const funcMatch = line.match(funcRe);
if(lineMatch && funcMatch) {
let func = funcMatch[1];
return {line: lineMatch[1], func: func};
}
return null;
}
let call = {line: "-1", func: "unknown"};;
let chromeCall = parseChromeStackline(stackline);
if (chromeCall) {
call = chromeCall;
}
const funcRe = /.*at (.+) \(.*/; function parseFirefoxStackline(line) {
const funcMatch = stackline.match(funcRe); const lineRe = /.*:(\d+):\d+$/;
let func = funcMatch[1]; const lineMatch = line.match(lineRe);
if(func.includes('.')) func = func.split('.')[1];
userstack.push(`${filename}:L${lineMatch[1]}@${func}`); const lio = line.lastIndexOf("@");
if(lineMatch && lio !== -1) {
return {line: lineMatch[1], func: line.slice(0, lio)};
}
return null;
}
let firefoxCall = parseFirefoxStackline(stackline);
if (firefoxCall) {
call = firefoxCall;
}
userstack.push(`${filename}:L${call.line}@${call.func}`);
} }
workerScript.log(caller, msg); workerScript.log(caller, msg);
@ -537,29 +564,97 @@ function NetscriptFunctions(workerScript) {
const runAfterReset = function(cbScript=null) { const runAfterReset = function(cbScript=null) {
//Run a script after reset //Run a script after reset
console.log(cbScript);
if (cbScript && isString(cbScript)) { if (cbScript && isString(cbScript)) {
console.log('here');
const home = Player.getHomeComputer(); const home = Player.getHomeComputer();
for (const script of home.scripts) { for (const script of home.scripts) {
console.log('here 2'+script);
if (script.filename === cbScript) { if (script.filename === cbScript) {
console.log('here 3');
const ramUsage = script.ramUsage; const ramUsage = script.ramUsage;
const ramAvailable = home.maxRam - home.ramUsed; const ramAvailable = home.maxRam - home.ramUsed;
if (ramUsage > ramAvailable) { if (ramUsage > ramAvailable) {
console.log('here 4');
return; // Not enough RAM return; // Not enough RAM
} }
const runningScriptObj = new RunningScript(script, []); // No args const runningScriptObj = new RunningScript(script, []); // No args
runningScriptObj.threads = 1; // Only 1 thread runningScriptObj.threads = 1; // Only 1 thread
console.log('running!');
startWorkerScript(runningScriptObj, home); startWorkerScript(runningScriptObj, home);
} }
} }
} }
} }
const hack = function(ip, manual, { threads: requestedThreads, stock } = {}) {
if (ip === undefined) {
throw makeRuntimeErrorMsg("hack", "Takes 1 argument.");
}
const threads = resolveNetscriptRequestedThreads(workerScript, "hack", requestedThreads);
const server = getServer(ip);
if (server == null) {
throw makeRuntimeErrorMsg("hack", `Invalid IP/hostname: ${ip}.`);
}
// Calculate the hacking time
var hackingTime = calculateHackingTime(server); // This is in seconds
// No root access or skill level too low
const canHack = netscriptCanHack(server, Player);
if (!canHack.res) {
throw makeRuntimeErrorMsg('hack', canHack.msg);
}
workerScript.log("hack", `Executing ${ip} in ${hackingTime.toFixed(3)} seconds (t=${threads})`);
return netscriptDelay(hackingTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
var hackChance = calculateHackingChance(server);
var rand = Math.random();
var expGainedOnSuccess = calculateHackingExpGain(server) * threads;
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { // Success!
const percentHacked = calculatePercentMoneyHacked(server);
let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax));
if (isNaN(maxThreadNeeded)) {
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value
maxThreadNeeded = 1e6;
}
let moneyDrained = Math.floor(server.moneyAvailable * percentHacked) * threads;
// Over-the-top safety checks
if (moneyDrained <= 0) {
moneyDrained = 0;
expGainedOnSuccess = expGainedOnFailure;
}
if (moneyDrained > server.moneyAvailable) {moneyDrained = server.moneyAvailable;}
server.moneyAvailable -= moneyDrained;
if (server.moneyAvailable < 0) {server.moneyAvailable = 0;}
const moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain;
Player.gainMoney(moneyGained);
workerScript.scriptRef.onlineMoneyMade += moneyGained;
Player.scriptProdSinceLastAug += moneyGained;
Player.recordMoneySource(moneyGained, "hacking");
workerScript.scriptRef.recordHack(server.ip, moneyGained, threads);
Player.gainHackingExp(expGainedOnSuccess);
workerScript.scriptRef.onlineExpGained += expGainedOnSuccess;
workerScript.log("hack", `Successfully hacked '${server.hostname}' for ${numeralWrapper.format(moneyGained, '$0.000a')} and ${numeralWrapper.format(expGainedOnSuccess, '0.000a')} exp (t=${threads})`);
server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded));
if (stock) {
influenceStockThroughServerHack(server, moneyGained);
}
if(manual) {
server.manuallyHacked = true;
}
return Promise.resolve(moneyGained);
} else {
// Player only gains 25% exp for failure?
Player.gainHackingExp(expGainedOnFailure);
workerScript.scriptRef.onlineExpGained += expGainedOnFailure;
workerScript.log("hack", `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.format(expGainedOnFailure, '0.000a')} exp (t=${threads})`);
return Promise.resolve(0);
}
});
}
return { return {
hacknet : { hacknet : {
numNodes : function() { numNodes : function() {
@ -676,74 +771,7 @@ function NetscriptFunctions(workerScript) {
}, },
hack : function(ip, { threads: requestedThreads, stock } = {}){ hack : function(ip, { threads: requestedThreads, stock } = {}){
updateDynamicRam("hack", getRamCost("hack")); updateDynamicRam("hack", getRamCost("hack"));
if (ip === undefined) { return hack(ip, false, {threads: requestedThreads, stock: stock});
throw makeRuntimeErrorMsg("hack", "Takes 1 argument.");
}
const threads = resolveNetscriptRequestedThreads(workerScript, "hack", requestedThreads);
const server = getServer(ip);
if (server == null) {
throw makeRuntimeErrorMsg("hack", `Invalid IP/hostname: ${ip}.`);
}
// Calculate the hacking time
var hackingTime = calculateHackingTime(server); // This is in seconds
// No root access or skill level too low
const canHack = netscriptCanHack(server, Player);
if (!canHack.res) {
throw makeRuntimeErrorMsg('hack', canHack.msg);
}
workerScript.log("hack", `Executing ${ip} in ${hackingTime.toFixed(3)} seconds (t=${threads})`);
return netscriptDelay(hackingTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
var hackChance = calculateHackingChance(server);
var rand = Math.random();
var expGainedOnSuccess = calculateHackingExpGain(server) * threads;
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { // Success!
const percentHacked = calculatePercentMoneyHacked(server);
let maxThreadNeeded = Math.ceil(1/percentHacked*(server.moneyAvailable/server.moneyMax));
if (isNaN(maxThreadNeeded)) {
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value
maxThreadNeeded = 1e6;
}
let moneyDrained = Math.floor(server.moneyAvailable * percentHacked) * threads;
// Over-the-top safety checks
if (moneyDrained <= 0) {
moneyDrained = 0;
expGainedOnSuccess = expGainedOnFailure;
}
if (moneyDrained > server.moneyAvailable) {moneyDrained = server.moneyAvailable;}
server.moneyAvailable -= moneyDrained;
if (server.moneyAvailable < 0) {server.moneyAvailable = 0;}
const moneyGained = moneyDrained * BitNodeMultipliers.ScriptHackMoneyGain;
Player.gainMoney(moneyGained);
workerScript.scriptRef.onlineMoneyMade += moneyGained;
Player.scriptProdSinceLastAug += moneyGained;
Player.recordMoneySource(moneyGained, "hacking");
workerScript.scriptRef.recordHack(server.ip, moneyGained, threads);
Player.gainHackingExp(expGainedOnSuccess);
workerScript.scriptRef.onlineExpGained += expGainedOnSuccess;
workerScript.log("hack", `Successfully hacked '${server.hostname}' for ${numeralWrapper.format(moneyGained, '$0.000a')} and ${numeralWrapper.format(expGainedOnSuccess, '0.000a')} exp (t=${threads})`);
server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded));
if (stock) {
influenceStockThroughServerHack(server, moneyGained);
}
return Promise.resolve(moneyGained);
} else {
// Player only gains 25% exp for failure?
Player.gainHackingExp(expGainedOnFailure);
workerScript.scriptRef.onlineExpGained += expGainedOnFailure;
workerScript.log("hack", `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.format(expGainedOnFailure, '0.000a')} exp (t=${threads})`);
return Promise.resolve(0);
}
});
}, },
hackAnalyzeThreads : function(ip, hackAmount) { hackAnalyzeThreads : function(ip, hackAmount) {
updateDynamicRam("hackAnalyzeThreads", getRamCost("hackAnalyzeThreads")); updateDynamicRam("hackAnalyzeThreads", getRamCost("hackAnalyzeThreads"));
@ -1686,8 +1714,6 @@ function NetscriptFunctions(workerScript) {
checkTixApiAccess("buyStock"); checkTixApiAccess("buyStock");
const stock = getStockFromSymbol(symbol, "buyStock"); const stock = getStockFromSymbol(symbol, "buyStock");
const res = buyStock(stock, shares, workerScript, { rerenderFn: displayStockMarketContent }); const res = buyStock(stock, shares, workerScript, { rerenderFn: displayStockMarketContent });
console.log(stock);
console.log(res);
return res ? stock.price : 0; return res ? stock.price : 0;
}, },
sellStock: function(symbol, shares) { sellStock: function(symbol, shares) {
@ -2684,6 +2710,47 @@ function NetscriptFunctions(workerScript) {
workerScript.log("purchaseProgram", `You have purchased the '${item.program}' program. The new program can be found on your home computer.`); workerScript.log("purchaseProgram", `You have purchased the '${item.program}' program. The new program can be found on your home computer.`);
return true; return true;
}, },
connect: function(hostname) {
if (!hostname) {
throw makeRuntimeErrorMsg("connect", `Invalid hostname: '${hostname}'`);
}
let target = getServer(hostname);
if (target == null) {
throw makeRuntimeErrorMsg("connect", `Invalid hostname: '${hostname}'`);
return;
}
if(hostname === 'home') {
Player.getCurrentServer().isConnectedTo = false;
Player.currentServer = Player.getHomeComputer().ip;
Player.getCurrentServer().isConnectedTo = true;
Terminal.currDir = "/";
Terminal.resetTerminalInput(true);
return true;
}
const server = Player.getCurrentServer();
for (let i = 0; i < server.serversOnNetwork.length; i++) {
const other = getServerOnNetwork(server, i);
if (other.ip == hostname || other.hostname == hostname) {
Player.getCurrentServer().isConnectedTo = false;
Player.currentServer = target.ip;
Player.getCurrentServer().isConnectedTo = true;
Terminal.currDir = "/";
Terminal.resetTerminalInput(true);
return true;
}
}
return false;
},
manualHack: function() {
updateDynamicRam("manualHack", getRamCost("manualHack"));
checkSingularityAccess("manualHack", 1);
const server = Player.getCurrentServer();
return hack(server.hostname, true);
},
getStats: function() { getStats: function() {
updateDynamicRam("getStats", getRamCost("getStats")); updateDynamicRam("getStats", getRamCost("getStats"));
checkSingularityAccess("getStats", 1); checkSingularityAccess("getStats", 1);

@ -163,7 +163,7 @@ function startNetscript1Script(workerScript) {
if (typeof entry === "function") { if (typeof entry === "function") {
//Async functions need to be wrapped. See JS-Interpreter documentation //Async functions need to be wrapped. See JS-Interpreter documentation
if (name === "hack" || name === "grow" || name === "weaken" || name === "sleep" || if (name === "hack" || name === "grow" || name === "weaken" || name === "sleep" ||
name === "prompt") { name === "prompt" || name === "manualHack") {
let tempWrapper = function() { let tempWrapper = function() {
let fnArgs = []; let fnArgs = [];

@ -1,16 +1,16 @@
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Gang } from "../../Gang"; import { Gang } from "../../Gang";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
// Amount of negative karma needed to manage a gang in BitNodes other than 2 // Amount of negative karma needed to manage a gang in BitNodes other than 2
const GangKarmaRequirement = -54000; const GangKarmaRequirement = -54000;
export function canAccessGang() { export function canAccessGang() {
if (this.bitNodeN === 2) { return true; } if (this.bitNodeN === 2) { return true; }
if (SourceFileFlags[2] <= 0) { return false; } if (SourceFileFlags[2] <= 0) { return false; }
return (this.karma <= GangKarmaRequirement); return (this.karma <= BitNodeMultipliers.GangKarmaRequirement*GangKarmaRequirement);
} }
export function getGangFaction() { export function getGangFaction() {

@ -113,6 +113,10 @@ function isNumber(str) {
return !isNaN(str) && !isNaN(parseFloat(str)); return !isNaN(str) && !isNaN(parseFloat(str));
} }
function getTerminalInput() {
return document.getElementById("terminal-input-text-box").value;
}
// Defines key commands in terminal // Defines key commands in terminal
$(document).keydown(function(event) { $(document).keydown(function(event) {
// Terminal // Terminal
@ -122,7 +126,7 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.ENTER) { if (event.keyCode === KEY.ENTER) {
event.preventDefault(); // Prevent newline from being entered in Script Editor event.preventDefault(); // Prevent newline from being entered in Script Editor
const command = terminalInput.value; const command = getTerminalInput();
const dir = Terminal.currDir; const dir = Terminal.currDir;
post( post(
"<span class='prompt'>[" + "<span class='prompt'>[" +
@ -344,22 +348,36 @@ let Terminal = {
// Excludes the trailing forward slash // Excludes the trailing forward slash
currDir: "/", currDir: "/",
resetTerminalInput: function() { resetTerminalInput: function(keepInput=false) {
let input = "";
if(keepInput) {
input = getTerminalInput();
}
const dir = Terminal.currDir; const dir = Terminal.currDir;
if (FconfSettings.WRAP_INPUT) { if (FconfSettings.WRAP_INPUT) {
document.getElementById("terminal-input-td").innerHTML = document.getElementById("terminal-input-td").innerHTML =
`<div id='terminal-input-header' class='prompt'>[${Player.getCurrentServer().hostname} ~${dir}]$ </div>` + `<div id='terminal-input-header' class='prompt'>[${Player.getCurrentServer().hostname} ~${dir}]$ </div>` +
'<textarea type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>'; `<textarea type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" value=\"${input}\"/>`;
// Auto re-size the line element as it wraps // Auto re-size the line element as it wraps
autosize(document.getElementById("terminal-input-text-box")); autosize(document.getElementById("terminal-input-text-box"));
} else { } else {
document.getElementById("terminal-input-td").innerHTML = document.getElementById("terminal-input-td").innerHTML =
`<div id='terminal-input-header' class='prompt'>[${Player.getCurrentServer().hostname} ~${dir}]$ </div>` + `<div id='terminal-input-header' class='prompt'>[${Player.getCurrentServer().hostname} ~${dir}]$ </div>` +
`<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>`; `<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" value=\"${input}\"/>`;
} }
var hdr = document.getElementById("terminal-input-header"); const hdr = document.getElementById("terminal-input-header");
hdr.style.display = "inline"; hdr.style.display = "inline";
const terminalInput = document.getElementById("terminal-input-text-box");
if (typeof terminalInput.selectionStart == "number") {
terminalInput.selectionStart = terminalInput.selectionEnd = terminalInput.value.length;
} else if (typeof terminalInput.createTextRange != "undefined") {
terminalInput.focus();
var range = el.createTextRange();
range.collapse(false);
range.select();
}
}, },
modifyInput: function(mod) { modifyInput: function(mod) {

@ -134,7 +134,7 @@ $(document).keydown(function(e) {
} catch(e) {} } catch(e) {}
if (!Player.isWorking && !redPillFlag && !inMission && !cinematicTextFlag) { if (!Player.isWorking && !redPillFlag && !inMission && !cinematicTextFlag) {
if (e.keyCode == 84 && e.altKey) { if (e.keyCode == KEY.T && e.altKey) {
e.preventDefault(); e.preventDefault();
Engine.loadTerminalContent(); Engine.loadTerminalContent();
} else if (e.keyCode === KEY.C && e.altKey) { } else if (e.keyCode === KEY.C && e.altKey) {
@ -174,6 +174,12 @@ $(document).keydown(function(e) {
} else if (e.keyCode === KEY.U && e.altKey) { } else if (e.keyCode === KEY.U && e.altKey) {
e.preventDefault(); e.preventDefault();
Engine.loadTutorialContent(); Engine.loadTutorialContent();
} else if (e.keyCode === KEY.B && e.altKey) {
e.preventDefault();
Engine.loadBladeburnerContent();
} else if (e.keyCode === KEY.G && e.altKey) {
e.preventDefault();
Engine.loadGangContent();
} }
} }

@ -4,30 +4,48 @@ import { IMap } from "../../src/types";
* Keyboard key codes * Keyboard key codes
*/ */
export const KEY: IMap<number> = { export const KEY: IMap<number> = {
A: 65, CTRL: 17,
B: 66, DOWNARROW: 40,
C: 67, ENTER: 13,
CTRL: 17, ESC: 27,
D: 68, TAB: 9,
DOWNARROW: 40, UPARROW: 38,
E: 69,
ENTER: 13, "0": 48,
ESC: 27, "1": 49,
F: 70, "2": 50,
H: 72, "3": 51,
J: 74, "4": 52,
K: 75, "5": 53,
L: 76, "6": 54,
M: 77, "7": 55,
N: 78, "8": 56,
O: 79, "9": 57,
P: 80,
R: 82, A: 65,
S: 83, B: 66,
TAB: 9, C: 67,
U: 85, D: 68,
UPARROW: 38, E: 69,
W: 87, F: 70,
"1": 49, G: 71,
"2": 50, H: 72,
I: 73,
J: 74,
K: 75,
L: 76,
M: 77,
N: 78,
O: 79,
P: 80,
Q: 81,
R: 82,
S: 83,
T: 84,
U: 85,
V: 86,
W: 87,
X: 88,
Y: 89,
Z: 90,
}; };