Compare commits

...

7 Commits

Author SHA1 Message Date
danielyxie
7417fb6ef8 v0.46.2 2019-04-14 02:08:10 -07:00
danielyxie
98a04e4932 Fix Terminal wget bug. Issue #593 2019-04-13 00:33:30 -07:00
danielyxie
8d33c5b571 Gang bug fixes. Issues #574 and #575 2019-04-13 00:26:49 -07:00
danielyxie
221b81d802 Fixed comment styling for all top-level src files 2019-04-12 20:47:17 -07:00
danielyxie
df89cc5002 Improved module import styling for all top-level src files 2019-04-12 20:47:17 -07:00
danielyxie
3b6b37f8a6 Merge pull request #594 from danielyxie/dev
v0.46.1
2019-04-12 16:53:19 -07:00
danielyxie
cf2acb8844 Updated documentation changelog for v0.46.1 2019-04-12 16:54:11 -07:00
62 changed files with 3308 additions and 2534 deletions

View File

@@ -75,7 +75,7 @@
margin: 4px;
padding: 2px;
resize: none;
width: 50%;
width: 60%;
}
#script-editor-status {

File diff suppressed because one or more lines are too long

2
dist/engine.css vendored
View File

@@ -893,7 +893,7 @@ button {
margin: 4px;
padding: 2px;
resize: none;
width: 50%; }
width: 60%; }
#script-editor-status {
float: left;

114
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,31 @@
Changelog
=========
v0.46.2 - 4/14/2019
-------------------
* Source-File 2 now allows you to form gangs in other BitNodes when your karma reaches a very large negative value
** (Karma is a hidden stat and is lowered by committing crimes)
* Gang changes:
** Bug Fix: Gangs can no longer clash with themselve
** Bug Fix: Winning against another gang should properly reduce their power
* Bug Fix: Terminal 'wget' command now works properly
* Bug Fix: Hacknet Server Hash upgrades now properly reset upon installing Augs/switching BitNodes
* Bug Fix: Fixed button for creating Corporations
v0.46.1 - 4/12/2019
-------------------
* Added a very rudimentary directory system to the Terminal
* Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
* 'Generate Coding Contract' hash upgrade is now more expensive
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer
* The cost of selling hashes for money no longer increases each time
* Selling hashes for money now costs 4 hashes (in exchange for $1m)
* Bug Fix: Hacknet Node earnings should work properly when game is inactive/offline
* Bug Fix: Duplicate Sleeve augmentations are now properly reset when switching to a new BitNode
v0.46.0 - 4/3/2019
------------------
* Added BitNode-9: Hacktocracy

View File

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

View File

@@ -8,6 +8,7 @@ recruitMember() Netscript Function
Attempt to recruit a new gang member.
Possible reasons for failure:
* Cannot currently recruit a new member
* There already exists a member with the specified name

View File

@@ -3,7 +3,7 @@ hashCost() Netscript Function
.. warning:: This page contains spoilers for the game
.. js:function:: hashCost(upgName, upgTarget)
.. js:function:: hashCost(upgName)
:param string upgName: Name of upgrade to get the cost of. Must be an exact match

View File

@@ -115,7 +115,7 @@
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/>
<input id="script-editor-filename" type="text" maxlength="100" tabindex="1"/>
</div>
<div id="ace-editor"></div>
@@ -221,8 +221,6 @@
<!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container"></div>
<div id="faction-augmentations-container" class="generic-menupage-container"></div>
<!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container"></div>

View File

@@ -114,5 +114,5 @@
"watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development"
},
"version": "0.45.0"
"version": "0.46.2"
}

View File

@@ -1,35 +1,38 @@
import {workerScripts,
killWorkerScript} from "./NetscriptWorker";
import { Player } from "./Player";
import { getServer } from "./Server/ServerHelpers";
import {numeralWrapper} from "./ui/numeralFormat";
import {dialogBoxCreate} from "../utils/DialogBox";
import {createAccordionElement} from "../utils/uiHelpers/createAccordionElement";
import {arrayToString} from "../utils/helpers/arrayToString";
import {createElement} from "../utils/uiHelpers/createElement";
import {createProgressBarText} from "../utils/helpers/createProgressBarText";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {getElementById} from "../utils/uiHelpers/getElementById";
import {logBoxCreate} from "../utils/LogBox";
import {formatNumber,
convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {removeElement} from "../utils/uiHelpers/removeElement";
import {roundToTwo} from "../utils/helpers/roundToTwo";
import {Page, routing} from "./ui/navigationTracking";
// TODO - Convert this to React
import { workerScripts, killWorkerScript } from "./NetscriptWorker";
import { Player } from "./Player";
import { getServer } from "./Server/ServerHelpers";
/* {
* serverName: {
* header: Server Header Element
* panel: Server Panel List (ul) element
* scripts: {
* script id: Ref to Script information
* }
* }
* ...
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
import { dialogBoxCreate } from "../utils/DialogBox";
import { logBoxCreate } from "../utils/LogBox";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { arrayToString } from "../utils/helpers/arrayToString";
import { createProgressBarText } from "../utils/helpers/createProgressBarText";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { roundToTwo } from "../utils/helpers/roundToTwo";
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
import { createElement } from "../utils/uiHelpers/createElement";
import { getElementById } from "../utils/uiHelpers/getElementById";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../utils/uiHelpers/removeElement";
/**
* {
* serverName: {
* header: Server Header Element
* panel: Server Panel List (ul) element
* scripts: {
* script id: Ref to Script information
* }
* }
* ...
*/
let ActiveScriptsUI = {};
let ActiveScriptsTasks = []; //Sequentially schedule the creation/deletion of UI elements
const ActiveScriptsUI = {};
const ActiveScriptsTasks = []; // Sequentially schedule the creation/deletion of UI elements
const getHeaderHtml = (server) => {
// TODO: calculate the longest hostname length rather than hard coding it
@@ -48,7 +51,7 @@ const updateHeaderHtml = (server) => {
return;
}
// convert it to a string, as that's how it's stored it will come out of the data attributes
// Convert it to a string, as that's how it's stored it will come out of the data attributes
const ramPercentage = '' + roundToTwo(server.ramUsed / server.maxRam);
if (accordion.header.dataset.ramPercentage !== ramPercentage) {
accordion.header.dataset.ramPercentage = ramPercentage;
@@ -81,16 +84,18 @@ function createActiveScriptsServerPanel(server) {
header: hdr,
panel: panel,
panelList: panelScriptList,
scripts: {}, //Holds references to li elements for each active script
scriptHdrs: {}, //Holds references to header elements for each active script
scriptStats: {} //Holds references to the p elements containing text for each active script
scripts: {}, // Holds references to li elements for each active script
scriptHdrs: {}, // Holds references to header elements for each active script
scriptStats: {}, // Holds references to the p elements containing text for each active script
};
return li;
}
//Deletes the info for a particular server (Dropdown header + Panel with all info)
//in the Active Scripts page if it exists
/**
* Deletes the info for a particular server (Dropdown header + Panel with all info)
* in the Active Scripts page if it exists
*/
function deleteActiveScriptsServerPanel(server) {
let hostname = server.hostname;
if (ActiveScriptsUI[hostname] == null) {
@@ -98,9 +103,9 @@ function deleteActiveScriptsServerPanel(server) {
return;
}
//Make sure it's empty
// Make sure it's empty
if (Object.keys(ActiveScriptsUI[hostname].scripts).length > 0) {
console.log("WARNING: Tried to delete Active Scripts Server panel that still has scripts. Aborting");
console.warn("Tried to delete Active Scripts Server panel that still has scripts. Aborting");
return;
}
@@ -112,7 +117,7 @@ function deleteActiveScriptsServerPanel(server) {
function addActiveScriptsItem(workerscript) {
var server = getServer(workerscript.serverIp);
if (server == null) {
console.log("ERROR: Invalid server IP for workerscript in addActiveScriptsItem()");
console.warn("Invalid server IP for workerscript in addActiveScriptsItem()");
return;
}
let hostname = server.hostname;
@@ -122,7 +127,7 @@ function addActiveScriptsItem(workerscript) {
createActiveScriptsServerPanel(server);
}
//Create the unique identifier (key) for this script
// Create the unique identifier (key) for this script
var itemNameArray = ["active", "scripts", hostname, workerscript.name];
for (var i = 0; i < workerscript.args.length; ++i) {
itemNameArray.push(String(workerscript.args[i]));
@@ -139,8 +144,10 @@ function addActiveScriptsItem(workerscript) {
panel.classList.remove("accordion-panel");
panel.classList.add("active-scripts-script-panel");
//Handle the constant elements on the panel that don't change after creation
//Threads, args, kill/log button
/**
* Handle the constant elements on the panel that don't change after creation:
* Threads, args, kill/log button
*/
panel.appendChild(createElement("p", {
innerHTML: "Threads: " + workerscript.scriptRef.threads + "<br>" +
"Args: " + arrayToString(workerscript.args)
@@ -173,7 +180,7 @@ function addActiveScriptsItem(workerscript) {
}
}));
//Append element to list
// Append element to list
ActiveScriptsUI[hostname]["panelList"].appendChild(li);
ActiveScriptsUI[hostname].scripts[itemName] = li;
ActiveScriptsUI[hostname].scriptHdrs[itemName] = hdr;
@@ -218,11 +225,13 @@ function deleteActiveScriptsItem(workerscript) {
}.bind(null, workerscript));
}
//Update the ActiveScriptsItems array
function updateActiveScriptsItems(maxTasks=150) {
//Run tasks that need to be done sequentially (adding items, creating/deleting server panels)
//We'll limit this to 150 at a time in case someone decides to start a bunch of scripts all at once...
let numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
/**
* Run tasks that need to be done sequentially (adding items, creating/deleting server panels)
* We'll limit this to 150 at a time for performance (in case someone decides to start a
* bunch of scripts all at once...)
*/
const numTasks = Math.min(maxTasks, ActiveScriptsTasks.length);
for (let i = 0; i < numTasks; ++i) {
let task = ActiveScriptsTasks.shift();
try {
@@ -233,8 +242,8 @@ function updateActiveScriptsItems(maxTasks=150) {
}
}
if (!routing.isOn(Page.ActiveScripts)) {return;}
var total = 0;
if (!routing.isOn(Page.ActiveScripts)) { return; }
let total = 0;
for (var i = 0; i < workerScripts.length; ++i) {
try {
total += updateActiveScriptsItemContent(workerScripts[i]);
@@ -246,10 +255,10 @@ function updateActiveScriptsItems(maxTasks=150) {
getElementById("active-scripts-total-production-active").innerText = numeralWrapper.formatMoney(total);
getElementById("active-scripts-total-prod-aug-total").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug);
getElementById("active-scripts-total-prod-aug-avg").innerText = numeralWrapper.formatMoney(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
return total;
}
//Updates the content of the given item in the Active Scripts list
function updateActiveScriptsItemContent(workerscript) {
var server = getServer(workerscript.serverIp);
if (server == null) {
@@ -258,7 +267,7 @@ function updateActiveScriptsItemContent(workerscript) {
}
let hostname = server.hostname;
if (ActiveScriptsUI[hostname] == null) {
return; //Hasn't been created yet. We'll skip it
return; // Hasn't been created yet. We'll skip it
}
updateHeaderHtml(server);
@@ -270,11 +279,11 @@ function updateActiveScriptsItemContent(workerscript) {
var itemName = itemNameArray.join("-");
if (ActiveScriptsUI[hostname].scriptStats[itemName] == null) {
return; //Hasn't been fully added yet. We'll skip it
return; // Hasn't been fully added yet. We'll skip it
}
var item = ActiveScriptsUI[hostname].scriptStats[itemName];
//Update the text if necessary. This fn returns the online $/s production
// Update the text if necessary. This fn returns the online $/s production
return updateActiveScriptsText(workerscript, item, itemName);
}
@@ -293,7 +302,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
updateHeaderHtml(server);
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
//Only update if the item is visible
// Only update if the item is visible
if (ActiveScriptsUI[hostname].header.classList.contains("active") === false) {return onlineMps;}
if (ActiveScriptsUI[hostname].scriptHdrs[itemName].classList.contains("active") === false) {return onlineMps;}
@@ -302,7 +311,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
var onlineTime = "Online Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.onlineRunningTime * 1e3);
var offlineTime = "Offline Time: " + convertTimeMsToTimeElapsedString(workerscript.scriptRef.offlineRunningTime * 1e3);
//Online
// Online
var onlineTotalMoneyMade = "Total online production: " + numeralWrapper.formatMoney(workerscript.scriptRef.onlineMoneyMade);
var onlineTotalExpEarned = (Array(26).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.onlineExpGained) + " hacking exp").replace( / /g, "&nbsp;");
@@ -310,7 +319,7 @@ function updateActiveScriptsText(workerscript, item, itemName) {
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
var onlineEpsText = (Array(25).join(" ") + numeralWrapper.formatBigNumber(onlineEps) + " hacking exp / second").replace( / /g, "&nbsp;");
//Offline
// Offline
var offlineTotalMoneyMade = "Total offline production: " + numeralWrapper.formatMoney(workerscript.scriptRef.offlineMoneyMade);
var offlineTotalExpEarned = (Array(27).join(" ") + numeralWrapper.formatBigNumber(workerscript.scriptRef.offlineExpGained) + " hacking exp").replace( / /g, "&nbsp;");

View File

@@ -1,5 +1,5 @@
import { IMap } from "./types";
import { post } from "./ui/postToTerminal";
import { IMap } from "./types";
import { post } from "./ui/postToTerminal";
export let Aliases: IMap<string> = {};
export let GlobalAliases: IMap<string> = {};
@@ -20,7 +20,7 @@ export function loadGlobalAliases(saveString: string): void {
}
}
//Print all aliases to terminal
// Prints all aliases to terminal
export function printAliases(): void {
for (var name in Aliases) {
if (Aliases.hasOwnProperty(name)) {
@@ -34,7 +34,7 @@ export function printAliases(): void {
}
}
//True if successful, false otherwise
// Returns true if successful, false otherwise
export function parseAliasDeclaration(dec: string, global: boolean=false) {
var re = /^([_|\w|!|%|,|@]+)="(.+)"$/;
var matches = dec.match(re);
@@ -90,8 +90,10 @@ export function removeAlias(name: string): boolean {
return false;
}
//Returns the original string with any aliases substituted in
//Aliases only applied to "whole words", one level deep
/**
* Returns the original string with any aliases substituted in.
* Aliases are only applied to "whole words", one level deep
*/
export function substituteAliases(origCommand: string): string {
const commandArray = origCommand.split(" ");
if (commandArray.length > 0){

View File

@@ -10,6 +10,7 @@ import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviv
interface IConstructorParams {
info: string;
isSpecial?: boolean;
moneyCost: number;
name: string;
prereqs?: string[];
@@ -62,6 +63,9 @@ export class Augmentation {
// Description of what this Aug is and what it does
info: string = "";
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
isSpecial: boolean = false;
// Augmentation level - for repeatable Augs like NeuroFlux Governor
level: number = 0;
@@ -90,6 +94,10 @@ export class Augmentation {
this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
this.startingCost = this.baseCost;
if (params.isSpecial) {
this.isSpecial = true;
}
this.level = 0;
// Set multipliers

View File

@@ -1677,19 +1677,6 @@ function initAugmentations() {
}
AddToAugmentations(SNA);
//For BitNode-2, add all Augmentations to crime/evil factions.
//Do this before adding special Augmentations that become available in later BitNodes
if (Player.bitNodeN === 2) {
console.log("Adding all augmentations to crime factions for Bit node 2");
Factions["Slum Snakes"].addAllAugmentations(Augmentations);
Factions["Tetrads"].addAllAugmentations(Augmentations);
Factions["The Syndicate"].addAllAugmentations(Augmentations);
Factions["The Dark Army"].addAllAugmentations(Augmentations);
Factions["Speakers for the Dead"].addAllAugmentations(Augmentations);
Factions["NiteSec"].addAllAugmentations(Augmentations);
Factions["The Black Hand"].addAllAugmentations(Augmentations);
}
//Special Bladeburner Augmentations
var BladeburnersFactionName = "Bladeburners";
if (factionExists(BladeburnersFactionName)) {
@@ -1708,6 +1695,7 @@ function initAugmentations() {
"Increases the player's dexterity by 5%.",
bladeburner_success_chance_mult: 1.03,
dexterity_mult: 1.05,
isSpecial: true,
});
EsperEyewear.addToFactions([BladeburnersFactionName]);
resetAugmentation(EsperEyewear);
@@ -1725,6 +1713,7 @@ function initAugmentations() {
bladeburner_success_chance_mult: 1.03,
bladeburner_analysis_mult: 1.05,
bladeburner_stamina_gain_mult: 1.02,
isSpecial: true,
});
EMS4Recombination.addToFactions([BladeburnersFactionName]);
resetAugmentation(EMS4Recombination);
@@ -1743,6 +1732,7 @@ function initAugmentations() {
strength_mult: 1.05,
dexterity_mult: 1.05,
bladeburner_success_chance_mult: 1.04,
isSpecial: true,
});
OrionShoulder.addToFactions([BladeburnersFactionName]);
resetAugmentation(OrionShoulder);
@@ -1758,6 +1748,7 @@ function initAugmentations() {
"This augmentation:<br>" +
"Increases the player's success chance in Bladeburner contracts/operations by 6%.",
bladeburner_success_chance_mult: 1.06,
isSpecial: true,
});
HyperionV1.addToFactions([BladeburnersFactionName]);
resetAugmentation(HyperionV1);
@@ -1772,6 +1763,7 @@ function initAugmentations() {
"Increases the player's success chance in Bladeburner contracts/operations by 8%.",
prereqs:[AugmentationNames.HyperionV1],
bladeburner_success_chance_mult: 1.08,
isSpecial: true,
});
HyperionV2.addToFactions([BladeburnersFactionName]);
resetAugmentation(HyperionV2);
@@ -1790,6 +1782,7 @@ function initAugmentations() {
dexterity_mult: 1.07,
agility_mult: 1.07,
bladeburner_stamina_gain_mult: 1.05,
isSpecial: true,
});
GolemSerum.addToFactions([BladeburnersFactionName]);
resetAugmentation(GolemSerum);
@@ -1805,6 +1798,7 @@ function initAugmentations() {
dexterity_exp_mult: 1.1,
bladeburner_analysis_mult: 1.1,
bladeburner_success_chance_mult: 1.04,
isSpecial: true,
});
VangelisVirus.addToFactions([BladeburnersFactionName]);
resetAugmentation(VangelisVirus);
@@ -1824,6 +1818,7 @@ function initAugmentations() {
dexterity_exp_mult: 1.1,
bladeburner_analysis_mult: 1.15,
bladeburner_success_chance_mult: 1.05,
isSpecial: true,
});
VangelisVirus3.addToFactions([BladeburnersFactionName]);
resetAugmentation(VangelisVirus3);
@@ -1842,6 +1837,7 @@ function initAugmentations() {
dexterity_exp_mult: 1.05,
agility_exp_mult: 1.05,
bladeburner_max_stamina_mult: 1.1,
isSpecial: true,
});
INTERLINKED.addToFactions([BladeburnersFactionName]);
resetAugmentation(INTERLINKED);
@@ -1859,6 +1855,7 @@ function initAugmentations() {
agility_mult: 1.05,
bladeburner_max_stamina_mult: 1.05,
bladeburner_stamina_gain_mult: 1.05,
isSpecial: true,
});
BladeRunner.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeRunner);
@@ -1879,6 +1876,7 @@ function initAugmentations() {
agility_mult: 1.04,
bladeburner_stamina_gain_mult: 1.02,
bladeburner_success_chance_mult: 1.03,
isSpecial: true,
});
BladeArmor.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmor);
@@ -1895,6 +1893,7 @@ function initAugmentations() {
bladeburner_success_chance_mult: 1.05,
bladeburner_stamina_gain_mult: 1.02,
bladeburner_max_stamina_mult: 1.05,
isSpecial: true,
});
BladeArmorPowerCells.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorPowerCells);
@@ -1909,6 +1908,7 @@ function initAugmentations() {
prereqs:[AugmentationNames.BladeArmor],
defense_mult: 1.05,
bladeburner_success_chance_mult: 1.06,
isSpecial: true,
});
BladeArmorEnergyShielding.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorEnergyShielding);
@@ -1922,6 +1922,7 @@ function initAugmentations() {
"Increases the player's success chance in Bladeburner contracts/operations by 8%.",
prereqs:[AugmentationNames.BladeArmor],
bladeburner_success_chance_mult: 1.08,
isSpecial: true,
});
BladeArmorUnibeam.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorUnibeam);
@@ -1936,6 +1937,7 @@ function initAugmentations() {
"Increases the player's success chance in Bladeburner contracts/operations by 10%.",
prereqs:[AugmentationNames.BladeArmorUnibeam],
bladeburner_success_chance_mult: 1.1,
isSpecial: true,
});
BladeArmorOmnibeam.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorOmnibeam);
@@ -1951,6 +1953,7 @@ function initAugmentations() {
prereqs:[AugmentationNames.BladeArmor],
bladeburner_analysis_mult: 1.15,
bladeburner_success_chance_mult: 1.02,
isSpecial: true,
});
BladeArmorIPU.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladeArmorIPU);
@@ -1963,7 +1966,8 @@ function initAugmentations() {
"extremely large radius. These specially-modified holograms were specially " +
"weaponized by Bladeburner units to be used against Synthoids.<br><br>" +
"This augmentation allows you to perform Bladeburner actions and other " +
"actions (such as working, commiting crimes, etc.) at the same time."
"actions (such as working, commiting crimes, etc.) at the same time.",
isSpecial: true,
});
BladesSimulacrum.addToFactions([BladeburnersFactionName]);
resetAugmentation(BladesSimulacrum);

View File

@@ -57,8 +57,9 @@ export function initBitNodes() {
"For every Faction NOT listed above, reputation gains are halved<br>" +
"You will no longer gain passive reputation with Factions<br><br>" +
"Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will " +
"upgrade its level up to a maximum of 3. This Source-File increases the player's crime success rate, " +
"crime money, and charisma multipliers by:<br><br>" +
"upgrade its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes " +
"once your karma decreases to a certain value. " +
"It also increases the player's crime success rate, crime money, and charisma multipliers by:<br><br>" +
"Level 1: 24%<br>" +
"Level 2: 36%<br>" +
"Level 3: 42%");

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,35 @@
import {Engine} from "./engine";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {createElement} from "../utils/uiHelpers/createElement";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import {isString} from "../utils/helpers/isString";
import { Engine } from "./engine";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
var cinematicTextFlag = false;
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { createElement } from "../utils/uiHelpers/createElement";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { isString } from "../utils/helpers/isString";
//Lines must be an array of strings
function writeCinematicText(lines) {
export let cinematicTextFlag = false;
/**
* Print a message using a hacking-style "typing" effect.
* Note that this clears the UI so that the text from this is the only thing visible.
*
* @param lines {string[]} Array of strings to print, where each element is a separate line
*/
export function writeCinematicText(lines) {
cinematicTextFlag = true;
if (lines.constructor !== Array) {
throw new Error("Invalid non-array argument passed into writeCinematicText()");
}
//We'll reuse the 'Red Pill' content
// Reuse the 'Red Pill' content
Engine.loadCinematicTextContent();
var container = document.getElementById("cinematic-text-container");
const container = document.getElementById("cinematic-text-container");
container.style.width = "75%";
if (container == null) {throw new Error("Could not find cinematic-text-container for writeCinematicText()");}
removeChildrenFromElement(container);
for (var i = 0; i < lines.length; ++i) {
for (let i = 0; i < lines.length; ++i) {
if (!isString(lines[i])) {
throw new Error("Invalid non-string element in 'lines' argument. writeCinematicText() failed");
}
@@ -45,11 +51,11 @@ function writeCinematicTextRecurse(lines, lineNumber=0) {
function writeCinematicTextLine(line) {
return new Promise(function(resolve, reject) {
var container = document.getElementById("cinematic-text-container");
var pElem = document.createElement("p");
const container = document.getElementById("cinematic-text-container");
const pElem = document.createElement("p");
container.appendChild(pElem);
var promise = writeCinematicTextLetter(pElem, line, 0);
const promise = writeCinematicTextLetter(pElem, line, 0);
promise.then(function(res) {
resolve(res);
}, function(e) {
@@ -61,14 +67,15 @@ function writeCinematicTextLine(line) {
function writeCinematicTextLetter(pElem, line, i=0) {
return new Promise(function(resolve, reject) {
setTimeoutRef(function() {
const textToShow = line.substring(0, i);
if (i >= line.length) {
var textToShow = line.substring(0, i);
pElem.innerHTML = textToShow;
return resolve(true);
}
var textToShow = line.substring(0, i);
pElem.innerHTML = textToShow + "<span class='typed-cursor'> &#9608; </span>";
var promise = writeCinematicTextLetter(pElem, line, i+1);
const promise = writeCinematicTextLetter(pElem, line, i+1);
promise.then(function(res) {
resolve(res);
}, function(e) {
@@ -96,5 +103,3 @@ function cinematicTextEnd() {
}));
});
}
export {cinematicTextFlag, writeCinematicText};

View File

@@ -1,12 +1,15 @@
import { CodingContract,
CodingContractRewardType,
CodingContractTypes } from "./CodingContracts";
import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { AllServers } from "./Server/AllServers";
import { GetServerByHostname } from "./Server/ServerHelpers";
import {
CodingContract,
CodingContractRewardType,
CodingContractTypes
} from "./CodingContracts";
import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { AllServers } from "./Server/AllServers";
import { GetServerByHostname } from "./Server/ServerHelpers";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { getRandomInt } from "../utils/helpers/getRandomInt";
export function generateRandomContract() {
// First select a random problem type
@@ -127,14 +130,15 @@ function getRandomReward() {
});
switch (reward.type) {
case CodingContractRewardType.FactionReputation:
case CodingContractRewardType.FactionReputation: {
// Get a random faction that player is a part of. That
// faction must allow hacking contracts
var numFactions = factionsThatAllowHacking.length;
var randFaction = factionsThatAllowHacking[getRandomInt(0, numFactions - 1)];
reward.name = randFaction;
break;
case CodingContractRewardType.CompanyReputation:
}
case CodingContractRewardType.CompanyReputation: {
const allJobs = Object.keys(Player.jobs);
if (allJobs.length > 0) {
reward.name = allJobs[getRandomInt(0, allJobs.length - 1)];
@@ -142,6 +146,7 @@ function getRandomReward() {
reward.type = CodingContractRewardType.Money;
}
break;
}
default:
break;
}

View File

@@ -1,11 +1,17 @@
import { codingContractTypesMetadata,
DescriptionFunc,
GeneratorFunc,
SolverFunc } from "./data/codingcontracttypes";
import {
codingContractTypesMetadata,
DescriptionFunc,
GeneratorFunc,
SolverFunc
} from "./data/codingcontracttypes";
import { IMap } from "./types";
import { IMap } from "./types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import {
Generic_fromJSON,
Generic_toJSON,
Reviver
} from "../utils/JSONReviver";
import { KEY } from "../utils/helpers/keyCodes";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
@@ -13,6 +19,7 @@ import { removeElementById } from "../utils/uiHelpers/removeElementById";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
/* Represents different types of problems that a Coding Contract can have */

View File

@@ -3,98 +3,98 @@
*
* Constants for specific mechanics or features will NOT be here.
*/
import {IMap} from "./types";
import { IMap } from "./types";
export let CONSTANTS: IMap<any> = {
Version: "0.46.1",
Version: "0.46.2",
//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
//the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
/** 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
* the player will have this level assuming no multipliers. Multipliers can cause skills to go above this.
*/
MaxSkillLevel: 975,
//Milliseconds per game cycle
// Milliseconds per game cycle
MilliPerCycle: 200,
//How much reputation is needed to join a megacorporation's faction
// How much reputation is needed to join a megacorporation's faction
CorpFactionRepRequirement: 200e3,
/* Base costs */
// Base RAM costs
BaseCostFor1GBOfRamHome: 32000,
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
// Cost to travel to another city
TravelCost: 200e3,
/* Faction and Company favor */
BaseFavorToDonate: 150,
DonateMoneyToRepDivisor: 1e6,
// Faction and Company favor-related things
BaseFavorToDonate: 150,
DonateMoneyToRepDivisor: 1e6,
FactionReputationToFavorBase: 500,
FactionReputationToFavorMult: 1.02,
CompanyReputationToFavorBase: 500,
CompanyReputationToFavorMult: 1.02,
/* Augmentation */
//NeuroFlux Governor cost multiplier as you level up
// NeuroFlux Governor Augmentation cost multiplier
NeuroFluxGovernorLevelMult: 1.14,
/* Netscript Constants */
//RAM Costs for different commands
ScriptBaseRamCost: 1.6,
ScriptDomRamCost: 25,
ScriptWhileRamCost: 0,
ScriptForRamCost: 0,
ScriptIfRamCost: 0,
ScriptHackRamCost: 0.1,
ScriptHackAnalyzeRamCost: 1,
ScriptGrowRamCost: 0.15,
ScriptGrowthAnalyzeRamCost: 1,
ScriptWeakenRamCost: 0.15,
ScriptScanRamCost: 0.2,
ScriptPortProgramRamCost: 0.05,
ScriptRunRamCost: 1.0,
ScriptExecRamCost: 1.3,
ScriptSpawnRamCost: 2.0,
ScriptScpRamCost: 0.6,
ScriptKillRamCost: 0.5, //Kill and killall
ScriptHasRootAccessRamCost: 0.05,
ScriptGetHostnameRamCost: 0.05, //getHostname() and getIp()
ScriptGetHackingLevelRamCost: 0.05, //getHackingLevel()
ScriptGetMultipliersRamCost: 4.0, //getHackingMultipliers() and getBitNodeMultipliers()
ScriptGetServerRamCost: 0.1,
ScriptFileExistsRamCost: 0.1,
ScriptIsRunningRamCost: 0.1,
ScriptHacknetNodesRamCost: 4.0, //Base cost for accessing Hacknet Node API
ScriptHNUpgLevelRamCost: 0.4,
ScriptHNUpgRamRamCost: 0.6,
ScriptHNUpgCoreRamCost: 0.8,
ScriptGetStockRamCost: 2.0,
ScriptBuySellStockRamCost: 2.5,
// RAM Costs for Netscript functions
ScriptBaseRamCost: 1.6,
ScriptDomRamCost: 25,
ScriptWhileRamCost: 0,
ScriptForRamCost: 0,
ScriptIfRamCost: 0,
ScriptHackRamCost: 0.1,
ScriptHackAnalyzeRamCost: 1,
ScriptGrowRamCost: 0.15,
ScriptGrowthAnalyzeRamCost: 1,
ScriptWeakenRamCost: 0.15,
ScriptScanRamCost: 0.2,
ScriptPortProgramRamCost: 0.05,
ScriptRunRamCost: 1.0,
ScriptExecRamCost: 1.3,
ScriptSpawnRamCost: 2.0,
ScriptScpRamCost: 0.6,
ScriptKillRamCost: 0.5,
ScriptHasRootAccessRamCost: 0.05,
ScriptGetHostnameRamCost: 0.05,
ScriptGetHackingLevelRamCost: 0.05,
ScriptGetMultipliersRamCost: 4.0,
ScriptGetServerRamCost: 0.1,
ScriptFileExistsRamCost: 0.1,
ScriptIsRunningRamCost: 0.1,
ScriptHacknetNodesRamCost: 4.0,
ScriptHNUpgLevelRamCost: 0.4,
ScriptHNUpgRamRamCost: 0.6,
ScriptHNUpgCoreRamCost: 0.8,
ScriptGetStockRamCost: 2.0,
ScriptBuySellStockRamCost: 2.5,
ScriptGetPurchaseServerRamCost: 0.25,
ScriptPurchaseServerRamCost: 2.25,
ScriptGetPurchasedServerLimit: 0.05,
ScriptPurchaseServerRamCost: 2.25,
ScriptGetPurchasedServerLimit: 0.05,
ScriptGetPurchasedServerMaxRam: 0.05,
ScriptRoundRamCost: 0.05,
ScriptReadWriteRamCost: 1.0,
ScriptArbScriptRamCost: 1.0, // Functions that apply to all scripts regardless of args
ScriptGetScriptRamCost: 0.1,
ScriptGetHackTimeRamCost: 0.05,
ScriptGetFavorToDonate: 0.10,
ScriptCodingContractBaseRamCost:10,
ScriptSleeveBaseRamCost: 4,
ScriptRoundRamCost: 0.05,
ScriptReadWriteRamCost: 1.0,
ScriptArbScriptRamCost: 1.0,
ScriptGetScriptRamCost: 0.1,
ScriptGetHackTimeRamCost: 0.05,
ScriptGetFavorToDonate: 0.10,
ScriptCodingContractBaseRamCost: 10,
ScriptSleeveBaseRamCost: 4,
ScriptSingularityFn1RamCost: 1,
ScriptSingularityFn2RamCost: 2,
ScriptSingularityFn3RamCost: 3,
ScriptSingularityFn1RamCost: 1,
ScriptSingularityFn2RamCost: 2,
ScriptSingularityFn3RamCost: 3,
ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4
ScriptSingularityFnRamMult: 2, // Multiplier for RAM cost outside of BN-4
ScriptGangApiBaseRamCost: 4,
ScriptGangApiBaseRamCost: 4,
ScriptBladeburnerApiBaseRamCost: 4,
ScriptBladeburnerApiBaseRamCost: 4,
NumNetscriptPorts: 20,
NumNetscriptPorts: 20,
//Server constants
// Server-related constants
HomeComputerMaxRam: 1073741824, // 2 ^ 30
ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate
ServerMaxGrowthRate: 1.0035, // Maximum possible growth rate (max rate accounting for server security)
@@ -102,49 +102,50 @@ export let CONSTANTS: IMap<any> = {
ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened
PurchasedServerLimit: 25,
PurchasedServerMaxRam: 1048576, //2^20
PurchasedServerMaxRam: 1048576, // 2^20
//Augmentation Constants
AugmentationCostMultiplier: 5, //Used for balancing costs without having to readjust every Augmentation cost
AugmentationRepMultiplier: 2.5, //Used for balancing rep cost without having to readjust every value
MultipleAugMultiplier: 1.9,
// Augmentation Constants
AugmentationCostMultiplier: 5, // Used for balancing costs without having to readjust every Augmentation cost
AugmentationRepMultiplier: 2.5, // Used for balancing rep cost without having to readjust every value
MultipleAugMultiplier: 1.9,
//How much a TOR router costs
TorRouterCost: 200000,
// TOR Router
TorRouterCost: 200e3,
//Infiltration constants
// Infiltration
InfiltrationBribeBaseAmount: 100e3, //Amount per clearance level
InfiltrationMoneyValue: 5e3, //Convert "secret" value to money
InfiltrationMoneyValue: 5e3, //Convert "secret" value to money
InfiltrationRepValue: 1.4, //Convert "secret" value to faction reputation
InfiltrationExpPow: 0.8,
//Stock market constants
WSEAccountCost: 200e6,
TIXAPICost: 5e9,
MarketData4SCost: 1e9,
// Stock market
WSEAccountCost: 200e6,
TIXAPICost: 5e9,
MarketData4SCost: 1e9,
MarketDataTixApi4SCost: 25e9,
StockMarketCommission: 100e3,
StockMarketCommission: 100e3,
//Hospital/Health
// Hospital/Health
HospitalCostPerHp: 100e3,
//Intelligence-related constants
IntelligenceCrimeWeight: 0.05, //Weight for how much int affects crime success rates
IntelligenceInfiltrationWeight: 0.1, //Weight for how much int affects infiltration success rates
// Intelligence-related constants
IntelligenceCrimeWeight: 0.05, // Weight for how much int affects crime success rates
IntelligenceInfiltrationWeight: 0.1, // Weight for how much int affects infiltration success rates
IntelligenceCrimeBaseExpGain: 0.001,
IntelligenceProgramBaseExpGain: 500, //Program required hack level divided by this to determine int exp gain
IntelligenceTerminalHackBaseExpGain: 200, //Hacking exp divided by this to determine int exp gain
IntelligenceProgramBaseExpGain: 500, // Program required hack level divided by this to determine int exp gain
IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain
IntelligenceSingFnBaseExpGain: 0.002,
IntelligenceClassBaseExpGain: 0.000001,
IntelligenceHackingMissionBaseExpGain: 0.03, //Hacking Mission difficulty multiplied by this to get exp gain
IntelligenceHackingMissionBaseExpGain: 0.03, // Hacking Mission difficulty multiplied by this to get exp gain
//Hacking Missions
HackingMissionRepToDiffConversion: 10000, //Faction rep is divided by this to get mission difficulty
HackingMissionRepToRewardConversion: 7, //Faction rep divided byt his to get mission rep reward
HackingMissionSpamTimeIncrease: 25000, //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
HackingMissionMiscDefenseIncrease: 1.05, //The amount by which every misc node's defense is multiplied when one is conquered
HackingMissionDifficultyToHacking: 135, //Difficulty is multiplied by this to determine enemy's "hacking" level (to determine effects of scan/attack, etc)
// Hacking Missions
// TODO Move this into Hacking Mission implementation
HackingMissionRepToDiffConversion: 10000, // Faction rep is divided by this to get mission difficulty
HackingMissionRepToRewardConversion: 7, // Faction rep divided byt his to get mission rep reward
HackingMissionSpamTimeIncrease: 25000, // 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
HackingMissionMiscDefenseIncrease: 1.05, // The amount by which every misc node's defense is multiplied when one is conquered
HackingMissionDifficultyToHacking: 135, // Difficulty is multiplied by this to determine enemy's "hacking" level (to determine effects of scan/attack, etc)
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 " +
@@ -192,7 +193,7 @@ export let CONSTANTS: IMap<any> = {
"-Miscellaneous Nodes slowly raise their defense over time<br><br>" +
"-Nodes slowly regenerate health over time.",
/* Time Constants */
// Time-related constants
MillisecondsPer20Hours: 72000000,
GameCyclesPer20Hours: 72000000 / 200,
@@ -220,7 +221,7 @@ export let CONSTANTS: IMap<any> = {
MillisecondsPerFiveMinutes: 300000,
GameCyclesPerFiveMinutes: 300000 / 200,
/* Player Work / Action related Constants */
// Player Work & Action
FactionWorkHacking: "Faction Hacking Work",
FactionWorkField: "Faction Field Work",
FactionWorkSecurity: "Faction Security Work",
@@ -263,20 +264,31 @@ export let CONSTANTS: IMap<any> = {
CrimeAssassination: "assassinate a high-profile target",
CrimeHeist: "pull off the ultimate heist",
/* Coding Contract Constants */
CodingContractBaseFactionRepGain: 2500,
CodingContractBaseCompanyRepGain: 4000,
CodingContractBaseMoneyGain: 75e6,
// Coding Contract
// TODO Move this into Coding contract impelmentation?
CodingContractBaseFactionRepGain: 2500,
CodingContractBaseCompanyRepGain: 4000,
CodingContractBaseMoneyGain: 75e6,
// BitNode/Source-File related stuff
TotalNumBitNodes: 24,
LatestUpdate:
`
v0.46.2
* Source-File 2 now allows you to form gangs in other BitNodes when your karma reaches a very large negative value
** (Karma is a hidden stat and is lowered by committing crimes)
* Gang changes:
** Bug Fix: Gangs can no longer clash with themselve
** Bug Fix: Winning against another gang should properly reduce their power
* Bug Fix: Terminal 'wget' command now works properly
* Bug Fix: Hacknet Server Hash upgrades now properly reset upon installing Augs/switching BitNodes
* Bug Fix: Fixed button for creating Corporations
v0.46.1
* Added a very rudimentary directory system to the Terminal
** Details here: https://bitburner.readthedocs.io/en/latest/basicgameplay/terminal.html#filesystem-directories
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
* 'Generate Coding Contract' hash upgrade is now more expensive
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer

View File

@@ -1,38 +1,44 @@
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { CodingContractTypes } from "./CodingContracts";
import { generateContract,
generateRandomContract,
generateRandomContractOnHome } from "./CodingContractGenerator";
import { Companies } from "./Company/Companies";
import { Company } from "./Company/Company";
import { Programs } from "./Programs/Programs";
import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { AllServers } from "./Server/AllServers";
import { GetServerByHostname } from "./Server/ServerHelpers";
import { hackWorldDaemon } from "./RedPill";
import { StockMarket,
SymbolToStockMap } from "./StockMarket/StockMarket";
import { Stock } from "./StockMarket/Stock";
import { Terminal } from "./Terminal";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { CodingContractTypes } from "./CodingContracts";
import {
generateContract,
generateRandomContract,
generateRandomContractOnHome
} from "./CodingContractGenerator";
import { Companies } from "./Company/Companies";
import { Company } from "./Company/Company";
import { Programs } from "./Programs/Programs";
import { Factions } from "./Faction/Factions";
import { Player } from "./Player";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { AllServers } from "./Server/AllServers";
import { GetServerByHostname } from "./Server/ServerHelpers";
import { hackWorldDaemon } from "./RedPill";
import { StockMarket, SymbolToStockMap } from "./StockMarket/StockMarket";
import { Stock } from "./StockMarket/Stock";
import { Terminal } from "./Terminal";
import { numeralWrapper } from "./ui/numeralFormat";
import { numeralWrapper } from "./ui/numeralFormat";
import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { createElement } from "../utils/uiHelpers/createElement";
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
import { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { createElement } from "../utils/uiHelpers/createElement";
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
import { getSelectText } from "../utils/uiHelpers/getSelectData";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import React from "react";
import ReactDOM from "react-dom";
import ReactDOM from "react-dom";
const Component = React.Component;
const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12];
// Update as additional BitNodes get implemented
const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
// Some dev menu buttons just add a lot of something for convenience
const tonsPP = 1e27;
const tonsP = 1e12;
class ValueAdjusterComponent extends Component {
constructor(props) {
@@ -41,7 +47,7 @@ class ValueAdjusterComponent extends Component {
this.setValue = this.setValue.bind(this);
}
setValue(event) {
this.setState({ value: event.target.value });
this.setState({ value: parseFloat(event.target.value) });
}
render() {
const { title, add, subtract, reset } = this.props;
@@ -124,7 +130,6 @@ class DevMenuComponent extends Component {
this.setState({ codingcontract: event.target.value });
}
addMoney(n) {
return function() {
Player.gainMoney(n);
@@ -186,14 +191,20 @@ class DevMenuComponent extends Component {
}
}
modifyKarma(modifier) {
return function(amt) {
Player.karma += (amt * modifier);
}
}
tonsOfExp() {
Player.gainHackingExp(1e27);
Player.gainStrengthExp(1e27);
Player.gainDefenseExp(1e27);
Player.gainDexterityExp(1e27);
Player.gainAgilityExp(1e27);
Player.gainCharismaExp(1e27);
Player.gainIntelligenceExp(1e27);
Player.gainHackingExp(tonsPP);
Player.gainStrengthExp(tonsPP);
Player.gainDefenseExp(tonsPP);
Player.gainDexterityExp(tonsPP);
Player.gainAgilityExp(tonsPP);
Player.gainCharismaExp(tonsPP);
Player.gainIntelligenceExp(tonsPP);
Player.updateSkillLevels();
}
@@ -237,6 +248,12 @@ class DevMenuComponent extends Component {
}
}
resetKarma() {
return function() {
Player.karma = 0;
}
}
enableIntelligence() {
if(Player.intelligence === 0) {
Player.intelligence = 1;
@@ -296,7 +313,7 @@ class DevMenuComponent extends Component {
tonsOfRep() {
for (const i in Factions) {
Factions[i].playerReputation = 1e27;
Factions[i].playerReputation = tonsPP;
}
}
@@ -308,7 +325,7 @@ class DevMenuComponent extends Component {
tonsOfFactionFavor() {
for (const i in Factions) {
Factions[i].favor = 1e27;
Factions[i].favor = tonsPP;
}
}
@@ -454,7 +471,7 @@ class DevMenuComponent extends Component {
tonsOfRepCompanies() {
for (const c in Companies) {
Companies[c].playerReputation = 1e12;
Companies[c].playerReputation = tonsP;
}
}
@@ -466,7 +483,7 @@ class DevMenuComponent extends Component {
tonsOfFavorCompanies() {
for (const c in Companies) {
Companies[c].favor = 1e12;
Companies[c].favor = tonsP;
}
}
@@ -491,7 +508,7 @@ class DevMenuComponent extends Component {
addTonsBladeburnerRank() {
if (!!Player.bladeburner) {
Player.bladeburner.changeRank(1e12);
Player.bladeburner.changeRank(tonsP);
}
}
@@ -511,13 +528,13 @@ class DevMenuComponent extends Component {
addTonsBladeburnerCycles() {
if (!!Player.bladeburner) {
Player.bladeburner.storedCycles += 1e12;
Player.bladeburner.storedCycles += tonsP;
}
}
addTonsGangCycles() {
if (!!Player.gang) {
Player.gang.storedCycles = 1e12;
Player.gang.storedCycles = tonsP;
}
}
@@ -537,7 +554,7 @@ class DevMenuComponent extends Component {
addTonsCorporationCycles() {
if (!!Player.corporation) {
Player.corporation.storedCycles = 1e12;
Player.corporation.storedCycles = tonsP;
}
}
@@ -644,16 +661,16 @@ class DevMenuComponent extends Component {
let sourceFiles = [];
validSFN.forEach( i => sourceFiles.push(
<tr key={'sf-'+i}>
<td><span className="text">SF-{i}:</span></td>
<td>
<button className="std-button touch-right" onClick={this.setSF(i, 0)}>0</button>
<button className="std-button touch-sides" onClick={this.setSF(i, 1)}>1</button>
<button className="std-button touch-sides" onClick={this.setSF(i, 2)}>2</button>
<button className="std-button touch-left" onClick={this.setSF(i, 3)}>3</button>
</td>
</tr>
));
<tr key={'sf-'+i}>
<td><span className="text">SF-{i}:</span></td>
<td>
<button className="std-button touch-right" onClick={this.setSF(i, 0)}>0</button>
<button className="std-button touch-sides" onClick={this.setSF(i, 1)}>1</button>
<button className="std-button touch-sides" onClick={this.setSF(i, 2)}>2</button>
<button className="std-button touch-left" onClick={this.setSF(i, 3)}>3</button>
</td>
</tr>
));
@@ -713,11 +730,11 @@ class DevMenuComponent extends Component {
<span className="text text-center">Hacking:</span>
</td>
<td>
<ValueAdjusterComponent
title="hacking exp"
add={this.modifyExp('hacking', 1)}
subtract={this.modifyExp('hacking', -1)}
reset={this.resetExperience('hacking')}
<ValueAdjusterComponent
title="hacking exp"
add={this.modifyExp('hacking', 1)}
subtract={this.modifyExp('hacking', -1)}
reset={this.resetExperience('hacking')}
/>
</td>
</tr>
@@ -726,7 +743,7 @@ class DevMenuComponent extends Component {
<span className="text text-center">Strength:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="strength exp"
add={this.modifyExp('strength', 1)}
subtract={this.modifyExp('strength', -1)}
@@ -739,7 +756,7 @@ class DevMenuComponent extends Component {
<span className="text text-center">Defense:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="defense exp"
add={this.modifyExp('defense', 1)}
subtract={this.modifyExp('defense', -1)}
@@ -752,7 +769,7 @@ class DevMenuComponent extends Component {
<span className="text text-center">Dexterity:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="dexterity exp"
add={this.modifyExp('dexterity', 1)}
subtract={this.modifyExp('dexterity', -1)}
@@ -765,7 +782,7 @@ class DevMenuComponent extends Component {
<span className="text text-center">Agility:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="agility exp"
add={this.modifyExp('agility', 1)}
subtract={this.modifyExp('agility', -1)}
@@ -778,7 +795,7 @@ class DevMenuComponent extends Component {
<span className="text text-center">Charisma:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="charisma exp"
add={this.modifyExp('charisma', 1)}
subtract={this.modifyExp('charisma', -1)}
@@ -791,7 +808,7 @@ class DevMenuComponent extends Component {
<span className="text text-center">Intelligence:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="intelligence exp"
add={this.modifyExp('intelligence', 1)}
subtract={this.modifyExp('intelligence', -1)}
@@ -805,6 +822,19 @@ class DevMenuComponent extends Component {
<button className="std-button" onClick={this.disableIntelligence}>Disable</button>
</td>
</tr>
<tr>
<td>
<span className="text text-center">Karma:</span>
</td>
<td>
<ValueAdjusterComponent
title="karma"
add={this.modifyKarma(1)}
subtract={this.modifyKarma(-1)}
reset={this.resetKarma()}
/>
</td>
</tr>
</tbody>
</table>
</div>
@@ -832,7 +862,7 @@ class DevMenuComponent extends Component {
<span className="text">Reputation:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="reputation"
add={this.modifyFactionRep(1)}
subtract={this.modifyFactionRep(-1)}
@@ -845,7 +875,7 @@ class DevMenuComponent extends Component {
<span className="text">Favor:</span>
</td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="favor"
add={this.modifyFactionFavor(1)}
subtract={this.modifyFactionFavor(-1)}
@@ -979,7 +1009,7 @@ class DevMenuComponent extends Component {
<tr>
<td><span className="text">Reputation:</span></td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="reputation"
add={this.modifyCompanyRep(1)}
subtract={this.modifyCompanyRep(-1)}
@@ -990,7 +1020,7 @@ class DevMenuComponent extends Component {
<tr>
<td><span className="text">Favor:</span></td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="favor"
add={this.modifyCompanyFavor(1)}
subtract={this.modifyCompanyFavor(-1)}
@@ -1028,7 +1058,7 @@ class DevMenuComponent extends Component {
<td><span className="text">Rank:</span></td>
<td><button className="std-button" onClick={this.addTonsBladeburnerRank}>Tons</button></td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="rank"
add={this.modifyBladeburnerRank(1)}
subtract={this.modifyBladeburnerRank(-1)}
@@ -1040,7 +1070,7 @@ class DevMenuComponent extends Component {
<td><span className="text">Cycles:</span></td>
<td><button className="std-button" onClick={this.addTonsBladeburnerCycles}>Tons</button></td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="cycles"
add={this.modifyBladeburnerCycles(1)}
subtract={this.modifyBladeburnerCycles(-1)}
@@ -1064,7 +1094,7 @@ class DevMenuComponent extends Component {
<td><span className="text">Cycles:</span></td>
<td><button className="std-button" onClick={this.addTonsGangCycles}>Tons</button></td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="cycles"
add={this.modifyGangCycles(1)}
subtract={this.modifyGangCycles(-1)}
@@ -1088,7 +1118,7 @@ class DevMenuComponent extends Component {
<td><span className="text">Cycles:</span></td>
<td><button className="std-button" onClick={this.addTonsCorporationCycles}>Tons</button></td>
<td>
<ValueAdjusterComponent
<ValueAdjusterComponent
title="cycles"
add={this.modifyCorporationCycles(1)}
subtract={this.modifyCorporationCycles(-1)}
@@ -1121,7 +1151,7 @@ class DevMenuComponent extends Component {
{contractTypes}
</select>
<button className="std-button" onClick={this.specificContract}>Generate Specified Contract Type on Home Comp</button>
</td>
</tr>
</tbody>
@@ -1185,7 +1215,6 @@ class DevMenuComponent extends Component {
}
}
const devMenuContainerId = "dev-menu-container";
export function createDevMenu() {
@@ -1199,11 +1228,11 @@ export function createDevMenu() {
id: devMenuContainerId,
});
const entireGameContainer = document.getElementById("entire-game-container");
if (entireGameContainer == null) {
throw new Error("Could not find entire-game-container DOM element");
}
entireGameContainer.appendChild(devMenuContainer);
const entireGameContainer = document.getElementById("entire-game-container");
if (entireGameContainer == null) {
throw new Error("Could not find entire-game-container DOM element");
}
entireGameContainer.appendChild(devMenuContainer);
ReactDOM.render(<DevMenuComponent />, devMenuContainer);
}

View File

@@ -97,16 +97,6 @@ export class Faction {
return [favorGain, rep];
}
//Adds all Augmentations to this faction.
addAllAugmentations(augs: object): void {
this.augmentations.length = 0;
for (const name in augs) {
if (augs.hasOwnProperty(name)) {
this.augmentations.push(name);
}
}
}
/**
* Serialize the current object to a JSON save state.
*/

7
src/Faction/FactionHelpers.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
import { Augmentation } from "../Augmentation/Augmentation";
import { Faction } from "../Faction/Faction";
export declare function getNextNeurofluxLevel(): number;
export declare function hasAugmentationPrereqs(aug: Augmentation): boolean;
export declare function purchaseAugmentationBoxCreate(aug: Augmentation, fac: Faction): void;
export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void;

View File

@@ -1,694 +0,0 @@
import { Augmentations } from "../Augmentation/Augmentations";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants";
import { Engine } from "../engine";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { FactionInfos } from "./FactionInfo";
import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player";
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { createSleevePurchasesFromCovenantPopup } from "../PersonObjects/Sleeve/SleeveCovenantPurchases";
import {Page, routing} from "../ui/navigationTracking";
import {numeralWrapper} from "../ui/numeralFormat";
import {dialogBoxCreate} from "../../utils/DialogBox";
import {factionInvitationBoxCreate} from "../../utils/FactionInvitationBox";
import {removeChildrenFromElement} from "../../utils/uiHelpers/removeChildrenFromElement";
import {createElement} from "../../utils/uiHelpers/createElement";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../../utils/JSONReviver";
import {formatNumber} from "../../utils/StringHelperFunctions";
import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../../utils/YesNoBox";
function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) {
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
} else {
factionInvitationBoxCreate(faction);
}
}
function joinFaction(faction) {
faction.isMember = true;
Player.factions.push(faction.name);
const factionInfo = faction.getInfo();
//Determine what factions you are banned from now that you have joined this faction
for(const i in factionInfo.enemies) {
const enemy = factionInfo.enemies[i];
if (Factions[enemy] instanceof Faction) {
Factions[enemy].isBanned = true;
}
}
}
//Displays the HTML content for a specific faction
function displayFactionContent(factionName) {
var faction = Factions[factionName];
if (faction == null) {
throw new Error("Invalid factionName passed into displayFactionContent: " + factionName);
}
if (!faction.isMember) {
throw new Error("Not a member of this faction, cannot display faction information");
}
var factionInfo = faction.getInfo();
removeChildrenFromElement(Engine.Display.factionContent);
var elements = [];
//Header and faction info
elements.push(createElement("h1", {
innerText:factionName
}));
elements.push(createElement("pre", {
innerHTML:"<i>" + factionInfo.infoText + "</i>"
}));
elements.push(createElement("p", {
innerText:"---------------",
}));
//Faction reputation and favor
var favorGain = faction.getFavorGain();
if (favorGain.length != 2) {favorGain = 0;}
favorGain = favorGain[0];
elements.push(createElement("p", {
innerText: "Reputation: " + formatNumber(faction.playerReputation, 4),
tooltip:"You will earn " + formatNumber(favorGain, 0) +
" faction favor upon resetting after installing an Augmentation"
}))
elements.push(createElement("p", {
innerText:"---------------",
}));
elements.push(createElement("p", {
innerText:"Faction Favor: " + formatNumber(faction.favor, 0),
tooltip:"Faction favor increases the rate at which " +
"you earn reputation for this faction by 1% per favor. Faction favor " +
"is gained whenever you reset after installing an Augmentation. The amount of " +
"favor you gain depends on how much reputation you have with the faction"
}));
elements.push(createElement("p", {
innerText:"---------------",
}));
//Faction Work Description Text
elements.push(createElement("pre", {
id:"faction-work-description-text",
innerText:"Perform work/carry out assignments for your faction to help further its cause! By doing so " +
"you will earn reputation for your faction. You will also gain reputation passively over time, " +
"although at a very slow rate. Earning reputation will allow you to purchase Augmentations " +
"through this faction, which are powerful upgrades that enhance your abilities. Note that you cannot " +
"use your terminal or create scripts when you are performing a task!"
}));
elements.push(createElement("br"));
//Hacking Mission Option
var hackMissionDiv = createElement("div", { class:"faction-work-div" });
var hackMissionDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
hackMissionDiv.appendChild(hackMissionDivWrapper);
hackMissionDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Hacking Mission",
clickListener:()=>{
Engine.loadMissionContent();
var mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true
mission.init();
return false;
}
}));
hackMissionDivWrapper.appendChild(createElement("p", {
innerText:"Attempt a hacking mission for your faction. " +
"A mission is a mini game that, if won, earns you " +
"significant reputation with this faction. (Recommended hacking level: 200+)"
}));
elements.push(hackMissionDiv);
//Hacking Contracts Option
var hackDiv = createElement("div", { class:"faction-work-div", });
var hackDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
hackDiv.appendChild(hackDivWrapper);
hackDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Hacking Contracts",
clickListener:()=>{
Player.startFactionHackWork(faction);
return false;
}
}));
hackDivWrapper.appendChild(createElement("p", {
innerText:"Complete hacking contracts for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your hacking skill. " +
"You will gain hacking exp."
}));
elements.push(hackDiv);
//Field Work Option
var fieldWorkDiv = createElement("div", { class:"faction-work-div" });
var fieldWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
fieldWorkDiv.appendChild(fieldWorkDivWrapper);
fieldWorkDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Field Work",
clickListener:()=>{
Player.startFactionFieldWork(faction);
return false;
}
}));
fieldWorkDivWrapper.appendChild(createElement("p", {
innerText:"Carry out field missions for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on all of your stats. " +
"You will gain exp for all stats."
}));
elements.push(fieldWorkDiv);
//Security Work Option
var securityWorkDiv = createElement("div", { class:"faction-work-div" });
var securityWorkDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
securityWorkDiv.appendChild(securityWorkDivWrapper);
securityWorkDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Security Work",
clickListener:()=>{
Player.startFactionSecurityWork(faction);
return false;
}
}));
securityWorkDivWrapper.appendChild(createElement("p", {
innerText:"Serve in a security detail for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your combat stats. " +
"You will gain exp for all combat stats."
}));
elements.push(securityWorkDiv);
//Donate for reputation
var donateDiv = createElement("div", { class:"faction-work-div" });
var donateDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
donateDiv.appendChild(donateDivWrapper);
var donateRepGain = createElement("p", {
innerText:"This donation will result in 0.000 reputation gain"
});
var donateAmountInput = createElement("input", {
class: "text-input", placeholder:"Donation amount",
inputListener:()=>{
let amt = 0;
if(donateAmountInput.value !== "") {
amt = parseFloat(donateAmountInput.value);
}
if (isNaN(amt)) {
donateRepGain.innerText = "Invalid donate amount entered!";
} else {
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
donateRepGain.innerText = "This donation will result in " +
formatNumber(repGain, 3) + " reputation gain";
}
},
});
donateDivWrapper.appendChild(createElement("a", {
class:"std-button", innerText:"Donate Money",
clickListener:()=>{
var amt = parseFloat(donateAmountInput.value);
if (isNaN(amt) || amt < 0) {
dialogBoxCreate("Invalid amount entered!");
} else if (Player.money.lt(amt)) {
dialogBoxCreate("You cannot afford to donate this much money!");
} else {
Player.loseMoney(amt);
var repGain = amt / CONSTANTS.DonateMoneyToRepDivisor * Player.faction_rep_mult;
faction.playerReputation += repGain;
dialogBoxCreate("You just donated " + numeralWrapper.format(amt, "$0.000a") + " to " +
faction.name + " to gain " + formatNumber(repGain, 3) + " reputation");
displayFactionContent(factionName);
}
}
}));
donateDivWrapper.appendChild(donateAmountInput);
donateDivWrapper.appendChild(donateRepGain);
elements.push(donateDiv);
//Purchase Augmentations
const purchaseAugmentationsDiv = createElement("div", { class: "faction-work-div", display: "inline" });
const purchaseAugmentationsDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
purchaseAugmentationsDiv.appendChild(purchaseAugmentationsDivWrapper);
purchaseAugmentationsDivWrapper.appendChild(createElement("a", {
class:"std-button",
innerText:"Purchase Augmentations",
margin: "5px",
clickListener:()=>{
Engine.hideAllContent();
Engine.Display.factionAugmentationsContent.style.display = "block";
displayFactionAugmentations(factionName);
return false;
}
}));
purchaseAugmentationsDivWrapper.appendChild(createElement("pre", {
innerHTML: "<br>As your reputation with this faction rises, you will " +
"unlock Augmentations, which you can purchase to enhance " +
"your abilities.<br><br>"
}));
elements.push(purchaseAugmentationsDiv);
//Gang (BitNode-2)
if (Player.bitNodeN == 2 && (factionName == "Slum Snakes" || factionName == "Tetrads" ||
factionName == "The Syndicate" || factionName == "The Dark Army" || factionName == "Speakers for the Dead" ||
factionName == "NiteSec" || factionName == "The Black Hand")) {
//Set everything else to invisible
hackMissionDiv.style.display = "none";
hackDiv.style.display = "none";
fieldWorkDiv.style.display = "none";
securityWorkDiv.style.display = "none";
donateDiv.style.display = "none";
//Create the 'Manage Gang' button
var gangDiv = createElement("div", {
id:"faction-gang-div", class:"faction-work-div", display:"inline"
});
var gangDivWrapper = createElement("div", {class:"faction-work-div-wrapper"});
gangDiv.appendChild(gangDivWrapper);
gangDivWrapper.appendChild(createElement("a", {
class:"a-link-button", innerText:"Manage Gang",
clickListener: () => {
if (!Player.inGang()) {
// Determine whether this is a hacking gang
let hacking = false;
if (factionName === "NiteSec" || factionName === "The Black Hand") { hacking = true; }
// Configure Yes/No buttons for the pop-up
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Create Gang";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", () => {
Player.startGang(factionName, hacking);
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
Engine.loadGangContent();
yesNoBoxClose();
});
noBtn.addEventListener("click", () => {
yesNoBoxClose();
});
// Pop-up text
let gangTypeText = "";
if (hacking) {
gangTypeText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.<br><br>";
} else {
gangTypeText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
}
yesNoBoxCreate(`Would you like to create a new Gang with ${factionName}?<br><br>` +
"Note that this will prevent you from creating a Gang with any other Faction until " +
"this BitNode is destroyed.<br><br>" +
gangTypeText +
"Other than hacking vs combat, there are NO differences between the Factions you can " +
"create a Gang with, and each of these Factions have all Augmentations available.");
} else {
Engine.loadGangContent();
}
}
}));
gangDivWrapper.appendChild(createElement("p", {
innerText:"Create and manage a gang for this Faction. " +
"Gangs will earn you money and faction reputation."
}));
//Manage Gang button goes before Faction work stuff
elements.splice(7, 1, gangDiv);
if (Player.inGang() && Player.gang.facName != factionName) {
//If the player has a gang but its not for this faction
gangDiv.style.display = "none";
}
//Display all elements
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionContent.appendChild(elements[i]);
}
return;
}
// Purchase Sleeves from Covenant
if (factionName === "The Covenant" && Player.bitNodeN >= 10 && SourceFileFlags[10]) {
const covenantPurchaseSleevesDiv = createElement("div", { class: "faction-work-div", display: "inline" });
const covenantPurchaseSleevesDivWrapper = createElement("div", { class: "faction-work-div-wrapper" });
covenantPurchaseSleevesDiv.appendChild(covenantPurchaseSleevesDivWrapper);
covenantPurchaseSleevesDivWrapper.appendChild(createElement("a", {
class: "std-button",
innerText: "Purchase Duplicate Sleeves",
clickListener: () => {
createSleevePurchasesFromCovenantPopup(Player);
}
}));
covenantPurchaseSleevesDivWrapper.appendChild(createElement("p", {
innerText: "Purchase Duplicate Sleeves. These are permanent! You can purchase up to 5 total.",
}));
elements.push(covenantPurchaseSleevesDiv);
}
// Determine if actions should be possible
donateDiv.style.display = faction.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction) ? "inline" : "none";
hackMissionDiv.style.display = factionInfo.offerHackingMission ? "inline": "none";
hackDiv.style.display = factionInfo.offerHackingWork ? "inline" : "none";
fieldWorkDiv.style.display = factionInfo.offerFieldWork ? "inline" : "none";
securityWorkDiv.style.display = factionInfo.offerSecurityWork ? "inline" : "none";
//Display all elements
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionContent.appendChild(elements[i]);
}
}
function displayFactionAugmentations(factionName) {
var faction = Factions[factionName];
if (faction == null) {
throw new Error("Could not find faction " + factionName + " in displayFactionAugmentations");
}
removeChildrenFromElement(Engine.Display.factionAugmentationsContent);
var elements = [];
//Back button
elements.push(createElement("a", {
innerText:"Back", class:"a-link-button",
clickListener:()=>{
Engine.loadFactionContent();
displayFactionContent(factionName);
return false;
}
}));
//Header text
elements.push(createElement("h1", {innerText:"Faction Augmentations"}));
elements.push(createElement("p", {
id:"faction-augmentations-page-desc",
innerHTML:"Lists all Augmentations that are available to purchase from " + factionName + "<br><br>" +
"Augmentations are powerful upgrades that will enhance your abilities."
}));
elements.push(createElement("br"));
elements.push(createElement("br"));
//Augmentations List
var augmentationsList = createElement("ul");
//Sort buttons
const sortByCostBtn = createElement("a", {
innerText:"Sort by Cost", class:"a-link-button",
clickListener:()=>{
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Cost;
var augs = faction.augmentations.slice();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseCost - aug2.baseCost;
});
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
}
});
const sortByRepBtn = createElement("a", {
innerText:"Sort by Reputation", class:"a-link-button",
clickListener:()=>{
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Reputation;
var augs = faction.augmentations.slice();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseRepRequirement - aug2.baseRepRequirement;
});
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, augs, faction);
}
});
const defaultSortBtn = createElement("a", {
innerText:"Sort by Default Order", class:"a-link-button",
clickListener:()=>{
Settings.PurchaseAugmentationsOrder = PurchaseAugmentationsOrderSetting.Default;
removeChildrenFromElement(augmentationsList);
createFactionAugmentationDisplayElements(augmentationsList, faction.augmentations, faction);
}
});
elements.push(sortByCostBtn);
elements.push(sortByRepBtn);
elements.push(defaultSortBtn);
switch(Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost:
sortByCostBtn.click();
break;
case PurchaseAugmentationsOrderSetting.Reputation:
sortByRepBtn.click();
break;
default:
defaultSortBtn.click();
break;
}
elements.push(augmentationsList);
for (var i = 0; i < elements.length; ++i) {
Engine.Display.factionAugmentationsContent.appendChild(elements[i]);
}
}
//Takes in an array of Augmentation Names, constructs DOM elements
//to list them on the faction page, and appends them to the given
//DOM element
// @augmentationsList DOM List to append Aug DOM elements to
// @augs Array of Aug names
// @faction Faction for which to display Augmentations
function createFactionAugmentationDisplayElements(augmentationsList, augs, faction) {
const factionInfo = faction.getInfo();
for (var i = 0; i < augs.length; ++i) {
(function () {
var aug = Augmentations[augs[i]];
if (aug == null) {
throw new Error("Invalid Augmentation when trying to create Augmentation display Elements");
}
var owned = false;
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name == aug.name) {
owned = true;
break;
}
}
for (var j = 0; j < Player.augmentations.length; ++j) {
if (Player.augmentations[j].name == aug.name) {
owned = true;
break;
}
}
var item = createElement("li");
var span = createElement("span", { display:"inline-block", margin: "4px", padding: "4px" });
var aDiv = createElement("div", {tooltip:aug.info});
var aElem = createElement("a", {
innerText:aug.name, display:"inline",
clickListener:()=>{
if (!Settings.SuppressBuyAugmentationConfirmation) {
purchaseAugmentationBoxCreate(aug, faction);
} else {
purchaseAugmentation(aug, faction);
}
return false;
}
});
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
aElem.innerText += " - Level " + (getNextNeurofluxLevel());
}
var pElem = createElement("p", {
display:"inline",
})
var req = aug.baseRepRequirement * factionInfo.augmentationRepRequirementMult;
var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "LOCKED (Requires " + aug.prereqs.join(",") + " as prerequisite(s))";
pElem.style.color = "red";
} else if (aug.name != AugmentationNames.NeuroFluxGovernor && (aug.owned || owned)) {
aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "ALREADY OWNED";
} else if (faction.playerReputation >= req) {
aElem.setAttribute("class", "a-link-button");
pElem.innerHTML = "UNLOCKED - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
} else {
aElem.setAttribute("class", "a-link-button-inactive");
pElem.innerHTML = "LOCKED (Requires " + formatNumber(req, 1) + " faction reputation) - " + numeralWrapper.format(aug.baseCost * factionInfo.augmentationPriceMult, "$0.000a");
pElem.style.color = "red";
}
aDiv.appendChild(aElem);
span.appendChild(aDiv);
span.appendChild(pElem);
item.appendChild(span);
augmentationsList.appendChild(item);
}()); //Immediate invocation closure
}
}
function purchaseAugmentationBoxCreate(aug, fac) {
const factionInfo = fac.getInfo();
var yesBtn = yesNoBoxGetYesButton(), noBtn = yesNoBoxGetNoButton();
yesBtn.innerHTML = "Purchase";
noBtn.innerHTML = "Cancel";
yesBtn.addEventListener("click", function() {
purchaseAugmentation(aug, fac);
});
noBtn.addEventListener("click", function() {
yesNoBoxClose();
});
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
aug.info + "<br><br>" +
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
}
//Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation
function hasAugmentationPrereqs(aug) {
var hasPrereqs = true;
if (aug.prereqs && aug.prereqs.length > 0) {
for (var i = 0; i < aug.prereqs.length; ++i) {
var prereqAug = Augmentations[aug.prereqs[i]];
if (prereqAug == null) {
console.log("ERROR: Invalid prereq Augmentation: " + aug.prereqs[i]);
continue;
}
if (prereqAug.owned === false) {
hasPrereqs = false;
//Check if the aug is purchased
for (var j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name === prereqAug.name) {
hasPrereqs = true;
break;
}
}
}
}
}
return hasPrereqs;
}
function purchaseAugmentation(aug, fac, sing=false) {
const factionInfo = fac.getInfo();
var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
"purchase this one.";
if (sing) {return txt;} else {dialogBoxCreate(txt);}
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
let txt = "You don't have enough money to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (fac.playerReputation < aug.baseRepRequirement) {
let txt = "You don't have enough faction reputation to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
if (Player.firstAugPurchased === false) {
Player.firstAugPurchased = true;
document.getElementById("augmentations-tab").style.display = "list-item";
document.getElementById("character-menu-header").click();
document.getElementById("character-menu-header").click();
}
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
queuedAugmentation.level = getNextNeurofluxLevel();
}
Player.queuedAugmentations.push(queuedAugmentation);
Player.loseMoney((aug.baseCost * factionInfo.augmentationPriceMult));
//If you just purchased Neuroflux Governor, recalculate the cost
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
var nextLevel = getNextNeurofluxLevel();
--nextLevel;
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
aug.baseRepRequirement = 500 * mult * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
aug.baseCost = 750e3 * mult * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
for (var i = 0; i < Player.queuedAugmentations.length-1; ++i) {
aug.baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
for (var name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
if (sing) {
return "You purchased " + aug.name;
} else {
if(!Settings.SuppressBuyAugmentationConfirmation){
dialogBoxCreate("You purchased " + aug.name + ". It's enhancements will not take " +
"effect until they are installed. To install your augmentations, go to the " +
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
"augmentations will now be more expensive.");
}
}
displayFactionAugmentations(fac.name);
} else {
dialogBoxCreate("Hmm, something went wrong when trying to purchase an Augmentation. " +
"Please report this to the game developer with an explanation of how to " +
"reproduce this.");
}
yesNoBoxClose();
}
function getNextNeurofluxLevel() {
// Get current Neuroflux level based on Player's augmentations
let currLevel = 0;
for (var i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
}
}
// Account for purchased but uninstalled Augmentations
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel;
}
}
return currLevel + 1;
}
function processPassiveFactionRepGain(numCycles) {
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult;
for (var name in Factions) {
if (Factions.hasOwnProperty(name)) {
var faction = Factions[name];
//TODO Get hard value of 1 rep per "rep gain cycle"" for now..
//maybe later make this based on
//a player's 'status' like how powerful they are and how much money they have
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);}
}
}
}
export {getNextNeurofluxLevel, inviteToFaction,
joinFaction, displayFactionContent, processPassiveFactionRepGain,
purchaseAugmentation};

View File

@@ -0,0 +1,240 @@
import React from "react";
import ReactDOM from "react-dom";
import { FactionRoot } from "./ui/Root";
import { Augmentations } from "../Augmentation/Augmentations";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants";
import { Engine } from "../engine";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player";
import { Settings } from "../Settings/Settings";
import { Page, routing } from "../ui/navigationTracking";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { factionInvitationBoxCreate } from "../../utils/FactionInvitationBox";
import {
Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../../utils/JSONReviver";
import { formatNumber } from "../../utils/StringHelperFunctions";
import {
yesNoBoxCreate,
yesNoBoxGetYesButton,
yesNoBoxGetNoButton,
yesNoBoxClose
} from "../../utils/YesNoBox";
export function inviteToFaction(faction) {
if (Settings.SuppressFactionInvites) {
faction.alreadyInvited = true;
Player.factionInvitations.push(faction.name);
if (routing.isOn(Page.Factions)) {
Engine.loadFactionsContent();
}
} else {
factionInvitationBoxCreate(faction);
}
}
export function joinFaction(faction) {
faction.isMember = true;
Player.factions.push(faction.name);
const factionInfo = faction.getInfo();
//Determine what factions you are banned from now that you have joined this faction
for(const i in factionInfo.enemies) {
const enemy = factionInfo.enemies[i];
if (Factions[enemy] instanceof Faction) {
Factions[enemy].isBanned = true;
}
}
}
export function startHackingMission(faction) {
const mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true
mission.init();
}
//Displays the HTML content for a specific faction
export function displayFactionContent(factionName, initiallyOnAugmentationsPage=false) {
const faction = Factions[factionName];
if (faction == null) {
throw new Error(`Invalid factionName passed into displayFactionContent(): ${factionName}`);
}
if (!faction.isMember) {
throw new Error(`Not a member of this faction. Cannot display faction information`);
}
ReactDOM.render(
<FactionRoot
engine={Engine}
initiallyOnAugmentationsPage={initiallyOnAugmentationsPage}
faction={faction}
p={Player}
startHackingMissionFn={startHackingMission}
/>,
Engine.Display.factionContent
)
}
export function purchaseAugmentationBoxCreate(aug, fac) {
const factionInfo = fac.getInfo();
const yesBtn = yesNoBoxGetYesButton();
yesBtn.innerHTML = "Purchase";
yesBtn.addEventListener("click", function() {
purchaseAugmentation(aug, fac);
});
const noBtn = yesNoBoxGetNoButton();
noBtn.innerHTML = "Cancel";
noBtn.addEventListener("click", function() {
yesNoBoxClose();
});
yesNoBoxCreate("<h2>" + aug.name + "</h2><br>" +
aug.info + "<br><br>" +
"<br>Would you like to purchase the " + aug.name + " Augmentation for $" +
formatNumber(aug.baseCost * factionInfo.augmentationPriceMult, 2) + "?");
}
//Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation
export function hasAugmentationPrereqs(aug) {
let hasPrereqs = true;
if (aug.prereqs && aug.prereqs.length > 0) {
for (let i = 0; i < aug.prereqs.length; ++i) {
const prereqAug = Augmentations[aug.prereqs[i]];
if (prereqAug == null) {
console.error(`Invalid prereq Augmentation ${aug.prereqs[i]}`);
continue;
}
if (prereqAug.owned === false) {
hasPrereqs = false;
// Check if the aug is purchased
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name === prereqAug.name) {
hasPrereqs = true;
break;
}
}
}
}
}
return hasPrereqs;
}
export function purchaseAugmentation(aug, fac, sing=false) {
const factionInfo = fac.getInfo();
var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
var txt = "You must first purchase or install " + aug.prereqs.join(",") + " before you can " +
"purchase this one.";
if (sing) {return txt;} else {dialogBoxCreate(txt);}
} else if (aug.baseCost !== 0 && Player.money.lt(aug.baseCost * factionInfo.augmentationPriceMult)) {
let txt = "You don't have enough money to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (fac.playerReputation < aug.baseRepRequirement) {
let txt = "You don't have enough faction reputation to purchase " + aug.name;
if (sing) {return txt;}
dialogBoxCreate(txt);
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) {
if (Player.firstAugPurchased === false) {
Player.firstAugPurchased = true;
document.getElementById("augmentations-tab").style.display = "list-item";
document.getElementById("character-menu-header").click();
document.getElementById("character-menu-header").click();
}
var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
queuedAugmentation.level = getNextNeurofluxLevel();
}
Player.queuedAugmentations.push(queuedAugmentation);
Player.loseMoney((aug.baseCost * factionInfo.augmentationPriceMult));
// If you just purchased Neuroflux Governor, recalculate the cost
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
var nextLevel = getNextNeurofluxLevel();
--nextLevel;
var mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
aug.baseRepRequirement = 500 * mult * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
aug.baseCost = 750e3 * mult * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
for (var i = 0; i < Player.queuedAugmentations.length-1; ++i) {
aug.baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
for (var name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier;
}
}
if (sing) {
return "You purchased " + aug.name;
} else {
if(!Settings.SuppressBuyAugmentationConfirmation){
dialogBoxCreate("You purchased " + aug.name + ". It's enhancements will not take " +
"effect until they are installed. To install your augmentations, go to the " +
"'Augmentations' tab on the left-hand navigation menu. Purchasing additional " +
"augmentations will now be more expensive.");
}
}
// Force a rerender of the Augmentations page
displayFactionContent(fac.name, true);
} else {
dialogBoxCreate("Hmm, something went wrong when trying to purchase an Augmentation. " +
"Please report this to the game developer with an explanation of how to " +
"reproduce this.");
}
yesNoBoxClose();
}
export function getNextNeurofluxLevel() {
// Get current Neuroflux level based on Player's augmentations
let currLevel = 0;
for (var i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
}
}
// Account for purchased but uninstalled Augmentations
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel;
}
}
return currLevel + 1;
}
export function processPassiveFactionRepGain(numCycles) {
var numTimesGain = (numCycles / 600) * Player.faction_rep_mult;
for (var name in Factions) {
if (Factions.hasOwnProperty(name)) {
var faction = Factions[name];
//TODO Get hard value of 1 rep per "rep gain cycle"" for now..
//maybe later make this based on
//a player's 'status' like how powerful they are and how much money they have
if (faction.isMember) {faction.playerReputation += (numTimesGain * BitNodeMultipliers.FactionPassiveRepGain);}
}
}
}

View File

@@ -0,0 +1,163 @@
/**
* Root React Component for displaying a faction's "Purchase Augmentations" page
*/
import * as React from "react";
import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
faction: Faction;
p: IPlayer;
routeToMainPage: () => void;
}
type IState = {
rerenderFlag: boolean;
sortOrder: PurchaseAugmentationsOrderSetting;
}
const infoStyleMarkup = {
width: "70%",
}
export class AugmentationsPage extends React.Component<IProps, IState> {
// Flag for whether the player has a gang with this faction
isPlayersGang: boolean;
constructor(props: IProps) {
super(props);
this.isPlayersGang = props.p.inGang() && (props.p.getGangName() === props.faction.name);
this.state = {
rerenderFlag: false,
sortOrder: PurchaseAugmentationsOrderSetting.Default,
}
this.rerender = this.rerender.bind(this);
}
getAugs() {
if (this.isPlayersGang) {
const augs: string[] = [];
for (const augName in Augmentations) {
const aug = Augmentations[augName];
if (!aug.isSpecial) {
augs.push(augName);
}
}
return augs;
} else {
return this.props.faction.augmentations.slice();
}
}
getAugsSorted() {
switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: {
return this.getAugsSortedByCost();
}
case PurchaseAugmentationsOrderSetting.Reputation: {
return this.getAugsSortedByReputation();
}
default:
return this.getAugsSortedByDefault();
}
}
getAugsSortedByCost() {
const augs = this.getAugs();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseCost - aug2.baseCost;
});
return augs;
}
getAugsSortedByReputation() {
const augs = this.getAugs();
augs.sort((augName1, augName2)=>{
var aug1 = Augmentations[augName1], aug2 = Augmentations[augName2];
if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names");
}
return aug1.baseRepRequirement - aug2.baseRepRequirement;
});
return augs;
}
getAugsSortedByDefault() {
return this.getAugs();
}
switchSortOrder(newOrder: PurchaseAugmentationsOrderSetting) {
Settings.PurchaseAugmentationsOrder = newOrder;
this.rerender();
}
rerender() {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
}
});
}
render() {
const augs = this.getAugsSorted();
const augList = augs.map((aug) => {
return (
<PurchaseableAugmentation
augName={aug}
faction={this.props.faction}
key={aug}
p={this.props.p}
rerender={this.rerender}
/>
)
});
return (
<div>
<StdButton
onClick={this.props.routeToMainPage}
text={"Back"}
/>
<h1>Faction Augmentations</h1>
<p style={infoStyleMarkup}>
These are all of the Augmentations that are available to purchase
from {this.props.faction.name}. Augmentations are powerful upgrades
that will enhance your abilities.
</p>
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}
text={"Sort by Cost"}
/>
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}
text={"Sort by Reputation"}
/>
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}
text={"Sort by Default Order"}
/>
<br />
{augList}
</div>
)
}
}

View File

@@ -0,0 +1,101 @@
/**
* React component for a donate option on the Faction UI
*/
import * as React from "react";
import { CONSTANTS } from "../../Constants";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat";
import { StdButton } from "../../ui/React/StdButton";
import { dialogBoxCreate } from "../../../utils/DialogBox";
type IProps = {
faction: Faction;
p: IPlayer;
rerender: () => void;
}
type IState = {
donateAmt: number;
statusTxt: string;
}
const inputStyleMarkup = {
margin: "5px",
}
export class DonateOption extends React.Component<IProps, IState> {
// Style markup for block elements. Stored as property
blockStyle: object = { display: "block" };
constructor(props: IProps) {
super(props);
this.state = {
donateAmt: 0,
statusTxt: "",
}
this.calculateRepGain = this.calculateRepGain.bind(this);
this.donate = this.donate.bind(this);
this.handleChange = this.handleChange.bind(this);
}
// Returns rep gain for the current donation amount
calculateRepGain(): number {
return this.state.donateAmt / CONSTANTS.DonateMoneyToRepDivisor * this.props.p.faction_rep_mult;
}
donate(): void {
const fac = this.props.faction;
const amt = this.state.donateAmt;
if (isNaN(amt) || amt <= 0) {
dialogBoxCreate(`Invalid amount entered!`);
} else if (!this.props.p.canAfford(amt)) {
dialogBoxCreate(`You cannot afford to donate this much money!`);
} else {
this.props.p.loseMoney(amt);
const repGain = this.calculateRepGain();
this.props.faction.playerReputation += repGain;
dialogBoxCreate(`You just donated ${numeralWrapper.formatMoney(amt)} to ${fac.name} to gain ` +
`${numeralWrapper.format(repGain, "0,0.000")} reputation`);
this.props.rerender();
}
}
handleChange(e: React.ChangeEvent<HTMLInputElement>): void {
const amt = parseFloat(e.target.value);
if (isNaN(amt)) {
this.setState({
donateAmt: 0,
statusTxt: "Invalid donate amount entered!",
});
} else {
const repGain = this.calculateRepGain();
this.setState({
donateAmt: amt,
statusTxt: `This donation will result in ${numeralWrapper.format(repGain, "0,0.000")} reputation gain`,
})
}
}
render() {
return (
<div className={"faction-work-div"}>
<div className={"faction-work-div-wrapper"}>
<input onChange={this.handleChange} placeholder={"Donation amount"} style={inputStyleMarkup} />
<StdButton
onClick={this.donate}
text={"Donate Money"}
/>
<p style={this.blockStyle}>{this.state.statusTxt}</p>
</div>
</div>
)
}
}

72
src/Faction/ui/Info.tsx Normal file
View File

@@ -0,0 +1,72 @@
/**
* React component for general information about the faction. This includes the
* factions "motto", reputation, favor, and gameplay instructions
*/
import * as React from "react";
import { Faction } from "../../Faction/Faction";
import { FactionInfo } from "../../Faction/FactionInfo";
import { numeralWrapper } from "../../ui/numeralFormat";
import { AutoupdatingParagraph } from "../../ui/React/AutoupdatingParagraph";
import { ParagraphWithTooltip } from "../../ui/React/ParagraphWithTooltip";
type IProps = {
faction: Faction;
factionInfo: FactionInfo;
}
type IInnerHTMLMarkup = {
__html: string;
}
const blockStyleMarkup = {
display: "block",
}
const infoStyleMarkup = {
display: "block",
width: "70%",
}
export class Info extends React.Component<IProps, any> {
render() {
const formattedRep = numeralWrapper.format(this.props.faction.playerReputation, "0.000a");
const favorGain = this.props.faction.getFavorGain()[0];
const favorTooltip = "Faction favor increases the rate at which you earn reputation for " +
"this faction by 1% per favor. Faction favor is gained whenever you " +
"reset after installing an Augmentation. The amount of " +
"favor you gain depends on how much reputation you have with the faction"
const infoText: IInnerHTMLMarkup = {
__html: this.props.factionInfo.infoText,
}
return (
<div>
<i className={"text"} style={infoStyleMarkup} dangerouslySetInnerHTML={infoText}></i>
<p style={blockStyleMarkup}>-------------------------</p>
<AutoupdatingParagraph
intervalTime={5e3}
text={`Reputation: ${formattedRep}`}
tooltip={`You will earn ${numeralWrapper.format(favorGain, "0,0")} faction favor upon resetting after installing an Augmentation`}
/>
<p style={blockStyleMarkup}>-------------------------</p>
<ParagraphWithTooltip
text={`Faction Favor: ${numeralWrapper.format(this.props.faction.favor, "0,0")}`}
tooltip={favorTooltip}
/>
<p style={blockStyleMarkup}>-------------------------</p>
<p style={infoStyleMarkup}>
Perform work/carry out assignments for your faction to help further its cause!
By doing so you will earn reputation for your faction. You will also gain
reputation passively over time, although at a very slow rate. Earning
reputation will allow you to purchase Augmentations through this faction, which
are powerful upgrades that enhance your abilities. Note that you cannot use your
terminal or create scripts when you are performing a task!
</p>
</div>
)
}
}

30
src/Faction/ui/Option.tsx Normal file
View File

@@ -0,0 +1,30 @@
/**
* React component for a selectable option on the Faction UI. These
* options including working for the faction, hacking missions, purchasing
* augmentations, etc.
*/
import * as React from "react";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
buttonText: string;
infoText: string;
onClick: (e: React.MouseEvent<HTMLElement>) => void;
}
export class Option extends React.Component<IProps, any> {
render() {
return (
<div className={"faction-work-div"}>
<div className={"faction-work-div-wrapper"}>
<StdButton
onClick={this.props.onClick}
text={this.props.buttonText}
/>
<p>{this.props.infoText}</p>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,151 @@
/**
* React component for displaying a single augmentation for purchase through
* the faction UI
*/
import * as React from "react";
import {
getNextNeurofluxLevel,
hasAugmentationPrereqs,
purchaseAugmentation,
purchaseAugmentationBoxCreate,
} from "../FactionHelpers";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat";
import { IMap } from "../../types";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
augName: string;
faction: Faction;
p: IPlayer;
rerender: () => void;
}
const spanStyleMarkup = {
margin: "4px",
padding: "4px",
}
const inlineStyleMarkup = {
display: "inline-block",
}
export class PurchaseableAugmentation extends React.Component<IProps, any> {
aug: Augmentation | null;
constructor(props: IProps) {
super(props);
this.aug = Augmentations[this.props.augName];
this.handleClick = this.handleClick.bind(this);
}
getMoneyCost(): number {
return this.aug!.baseCost * this.props.faction.getInfo().augmentationPriceMult;
}
getRepCost(): number {
return this.aug!.baseRepRequirement * this.props.faction.getInfo().augmentationRepRequirementMult;
}
handleClick() {
if (!Settings.SuppressBuyAugmentationConfirmation) {
purchaseAugmentationBoxCreate(this.aug!, this.props.faction);
} else {
purchaseAugmentation(this.aug!, this.props.faction);
}
}
// Whether the player has the prerequisite Augmentations
hasPrereqs(): boolean {
return hasAugmentationPrereqs(this.aug!);
}
// Whether the player has enough rep for this Augmentation
hasReputation(): boolean {
return this.props.faction.playerReputation >= this.getRepCost();
}
// Whether the player has this augmentations (purchased OR installed)
owned(): boolean {
let owned = false;
for (const queuedAug of this.props.p.queuedAugmentations) {
if (queuedAug.name === this.props.augName) {
owned = true;
break;
}
}
for (const installedAug of this.props.p.augmentations) {
if (installedAug.name === this.props.augName) {
owned = true;
break;
}
}
return owned;
}
render() {
if (this.aug == null) {
console.error(`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${this.props.augName}`);
return null;
}
const moneyCost = this.getMoneyCost();
const repCost = this.getRepCost();
// Determine UI properties
let disabled: boolean = false;
let statusTxt: string = "";
let color: string = "";
if (!this.hasPrereqs()) {
disabled = true;
statusTxt = `LOCKED (Requires ${this.aug.prereqs.join(",")} as prerequisite(s))`;
color = "red";
} else if (this.aug.name !== AugmentationNames.NeuroFluxGovernor && (this.aug.owned || this.owned())) {
disabled = true;
statusTxt = "ALREADY OWNED";
} else if (this.hasReputation()) {
statusTxt = `UNLOCKED - ${numeralWrapper.formatMoney(moneyCost)}`;
} else {
disabled = true;
statusTxt = `LOCKED (Requires ${numeralWrapper.format(repCost, "0,0.0")} faction reputation - ${numeralWrapper.formatMoney(moneyCost)})`;
color = "red";
}
const txtStyle: IMap<string> = {
display: "inline-block",
}
if (color !== "") { txtStyle.color = color; }
// Determine button txt
let btnTxt = this.aug.name;
if (this.aug.name === AugmentationNames.NeuroFluxGovernor) {
btnTxt += ` - Level ${getNextNeurofluxLevel()}`;
}
return (
<li>
<span style={spanStyleMarkup}>
<StdButton
disabled={disabled}
onClick={this.handleClick}
style={inlineStyleMarkup}
text={btnTxt}
/>
<p style={txtStyle}>{statusTxt}</p>
</span>
</li>
)
}
}

298
src/Faction/ui/Root.tsx Normal file
View File

@@ -0,0 +1,298 @@
/**
* Root React Component for displaying a Faction's UI.
* This is the component for displaying a single faction's UI, not the list of all
* accessible factions
*/
import * as React from "react";
import { AugmentationsPage } from "./AugmentationsPage";
import { DonateOption } from "./DonateOption";
import { Info } from "./Info";
import { Option } from "./Option";
import { CONSTANTS } from "../../Constants";
import { IEngine } from "../../IEngine";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import {
yesNoBoxClose,
yesNoBoxCreate,
yesNoBoxGetNoButton,
yesNoBoxGetYesButton,
} from "../../../utils/YesNoBox";
type IProps = {
engine: IEngine;
initiallyOnAugmentationsPage?: boolean;
faction: Faction;
p: IPlayer;
startHackingMissionFn: (faction: Faction) => void;
}
type IState = {
rerenderFlag: boolean;
purchasingAugs: boolean;
}
// Info text for all options on the UI
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " +
"faction reputation";
const hackingMissionInfo = "Attempt a hacking mission for your faction. " +
"A mission is a mini game that, if won, earns you " +
"significant reputation with this faction. (Recommended hacking level: 200+)";
const hackingContractsInfo = "Complete hacking contracts for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your hacking skill. " +
"You will gain hacking exp.";
const fieldWorkInfo = "Carry out field missions for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on all of your stats. " +
"You will gain exp for all stats.";
const securityWorkInfo = "Serve in a security detail for your faction. " +
"Your effectiveness, which determines how much " +
"reputation you gain for this faction, is based on your combat stats. " +
"You will gain exp for all combat stats.";
const augmentationsInfo = "As your reputation with this faction rises, you will " +
"unlock Augmentations, which you can purchase to enhance " +
"your abilities.";
const sleevePurchasesInfo = "Purchase Duplicate Sleeves and upgrades. These are permanent!";
const GangNames = [
"Slum Snakes",
"Tetrads",
"The Syndicate",
"The Dark Army",
"Speakers for the Dead",
"NiteSec",
"The Black Hand"
];
export class FactionRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
purchasingAugs: props.initiallyOnAugmentationsPage ? props.initiallyOnAugmentationsPage : false,
}
this.manageGang = this.manageGang.bind(this);
this.rerender = this.rerender.bind(this);
this.routeToMain = this.routeToMain.bind(this);
this.routeToPurchaseAugs = this.routeToPurchaseAugs.bind(this);
this.sleevePurchases = this.sleevePurchases.bind(this);
this.startFieldWork = this.startFieldWork.bind(this);
this.startHackingContracts = this.startHackingContracts.bind(this);
this.startHackingMission = this.startHackingMission.bind(this);
this.startSecurityWork = this.startSecurityWork.bind(this);
}
manageGang() {
// If player already has a gang, just go to the gang UI
if (this.props.p.inGang()) {
return this.props.engine.loadGangContent();
}
// Otherwise, we have to create the gang
const facName = this.props.faction.name;
let isHacking = false;
if (facName === "NiteSec" || facName === "The Black Hand") {
isHacking = true;
}
// A Yes/No popup box will allow the player to confirm gang creation
const yesBtn = yesNoBoxGetYesButton();
const noBtn = yesNoBoxGetNoButton();
if (yesBtn == null || noBtn == null) { return; }
yesBtn.innerHTML = "Create Gang";
yesBtn.addEventListener("click", () => {
this.props.p.startGang(facName, isHacking);
const worldMenuHeader = document.getElementById("world-menu-header");
if (worldMenuHeader instanceof HTMLElement) {
worldMenuHeader.click(); worldMenuHeader.click();
}
this.props.engine.loadGangContent();
yesNoBoxClose();
});
noBtn.innerHTML = "Cancel";
noBtn.addEventListener("click", () => {
yesNoBoxClose();
});
// Pop-up text
let gangTypeText = "";
if (isHacking) {
gangTypeText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.<br><br>";
} else {
gangTypeText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.<br><br>";
}
yesNoBoxCreate(`Would you like to create a new Gang with ${facName}?<br><br>` +
"Note that this will prevent you from creating a Gang with any other Faction until " +
"this BitNode is destroyed. It also resets your reputation with this faction.<br><br>" +
gangTypeText +
"Other than hacking vs combat, there are NO differences between the Factions you can " +
"create a Gang with, and each of these Factions have all Augmentations available.");
}
rerender() {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
}
});
}
// Route to the main faction page
routeToMain() {
this.setState({ purchasingAugs: false });
}
// Route to the purchase augmentation UI for this faction
routeToPurchaseAugs() {
this.setState({ purchasingAugs: true });
}
sleevePurchases() {
createSleevePurchasesFromCovenantPopup(this.props.p);
}
startFieldWork() {
this.props.p.startFactionFieldWork(this.props.faction);
}
startHackingContracts() {
this.props.p.startFactionHackWork(this.props.faction);
}
startHackingMission() {
const fac = this.props.faction;
this.props.engine.loadMissionContent();
this.props.startHackingMissionFn(fac);
}
startSecurityWork() {
this.props.p.startFactionSecurityWork(this.props.faction);
}
render() {
return this.state.purchasingAugs ? this.renderAugmentationsPage() : this.renderMainPage();
}
renderMainPage() {
const p = this.props.p;
const faction = this.props.faction;
const factionInfo = faction.getInfo();
// We have a special flag for whether the player this faction is the player's
// gang faction because if the player has a gang, they cannot do any other action
const isPlayersGang = p.inGang() && (p.getGangName() === faction.name);
// Flags for whether special options (gang, sleeve purchases, donate, etc.)
// should be shown
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
const canDonate = faction.favor >= favorToDonate;
const canPurchaseSleeves = (faction.name === "The Covenant" && p.bitNodeN >= 10 && SourceFileFlags[10]);
let canAccessGang = (p.canAccessGang() && GangNames.includes(faction.name));
if (p.inGang() && (p.getGangName() !== faction.name)) {
canAccessGang = false;
}
return (
<div>
<h1>{faction.name}</h1>
<Info
faction={faction}
factionInfo={factionInfo}
/>
{
canAccessGang &&
<Option
buttonText={"Manage Gang"}
infoText={gangInfo}
onClick={this.manageGang}
/>
}
{
(!isPlayersGang && factionInfo.offerHackingMission) &&
<Option
buttonText={"Hacking Mission"}
infoText={hackingMissionInfo}
onClick={this.startHackingMission}
/>
}
{
(!isPlayersGang && factionInfo.offerHackingWork) &&
<Option
buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo}
onClick={this.startHackingContracts}
/>
}
{
(!isPlayersGang && factionInfo.offerFieldWork) &&
<Option
buttonText={"Field Work"}
infoText={fieldWorkInfo}
onClick={this.startFieldWork}
/>
}
{
(!isPlayersGang && factionInfo.offerSecurityWork) &&
<Option
buttonText={"Security Work"}
infoText={securityWorkInfo}
onClick={this.startSecurityWork}
/>
}
{
(!isPlayersGang && canDonate) &&
<DonateOption
faction={this.props.faction}
p={this.props.p}
rerender={this.rerender}
/>
}
<Option
buttonText={"Purchase Augmentations"}
infoText={augmentationsInfo}
onClick={this.routeToPurchaseAugs}
/>
{
canPurchaseSleeves &&
<Option
buttonText={"Purchase Duplicate Sleeves"}
infoText={sleevePurchasesInfo}
onClick={this.sleevePurchases}
/>
}
</div>
)
}
renderAugmentationsPage() {
return (
<div>
<AugmentationsPage
faction={this.props.faction}
p={this.props.p}
routeToMainPage={this.routeToMain}
/>
</div>
)
}
}

View File

@@ -1,31 +1,39 @@
/*
Also add police clashes
balance point to keep them from running out of control
/**
* TODO
* Add police clashes
* balance point to keep them from running out of control
*/
import { gangMemberTasksMetadata } from "./data/gangmembertasks";
import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades";
import { gangMemberTasksMetadata } from "./data/gangmembertasks";
import { gangMemberUpgradesMetadata } from "./data/gangmemberupgrades";
import { Engine } from "./engine";
import { Faction } from "./Faction/Faction";
import { Factions } from "./Faction/Factions";
import { displayFactionContent } from "./Faction/FactionHelpers";
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
import { dialogBoxCreate } from "../utils/DialogBox";
import {
Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../utils/JSONReviver";
import { formatNumber } from "../utils/StringHelperFunctions";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { KEY } from "../utils/helpers/keyCodes";
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { Engine } from "./engine";
import { Faction } from "./Faction/Faction";
import { Factions } from "./Faction/Factions";
import { displayFactionContent } from "./Faction/FactionHelpers";
import { numeralWrapper } from "./ui/numeralFormat";
import { dialogBoxCreate } from "../utils/DialogBox";
import { Reviver, Generic_toJSON,
Generic_fromJSON } from "../utils/JSONReviver";
import { KEY } from "../utils/helpers/keyCodes";
import { createAccordionElement } from "../utils/uiHelpers/createAccordionElement";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { Page,
routing } from "./ui/navigationTracking";
import { formatNumber } from "../utils/StringHelperFunctions";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
// Constants
const GangRespectToReputationRatio = 5; // Respect is divided by this to get rep gain
@@ -50,7 +58,7 @@ $(document).keydown(function(event) {
}
});
//Delete upgrade box when clicking outside
// Delete upgrade box when clicking outside
$(document).mousedown(function(event) {
var boxId = "gang-member-upgrade-popup-box";
var contentId = "gang-member-upgrade-popup-box-content";
@@ -66,8 +74,16 @@ $(document).mousedown(function(event) {
}
});
let GangNames = ["Slum Snakes", "Tetrads", "The Syndicate", "The Dark Army", "Speakers for the Dead",
"NiteSec", "The Black Hand"];
const GangNames = [
"Slum Snakes",
"Tetrads",
"The Syndicate",
"The Dark Army",
"Speakers for the Dead",
"NiteSec",
"The Black Hand"
];
export let AllGangs = {
"Slum Snakes" : {
power: 1,
@@ -137,12 +153,12 @@ export function loadAllGangs(saveString) {
}
/**
* @param facName - Name of corresponding faction
* @param hacking - Boolean indicating whether or not its a hacking gang
* @param facName {string} Name of corresponding faction
* @param hacking {bollean} Whether or not its a hacking gang
*/
export function Gang(facName, hacking=false) {
this.facName = facName;
this.members = []; //Array of GangMembers
this.members = [];
this.wanted = 1;
this.respect = 1;
@@ -197,7 +213,7 @@ Gang.prototype.process = function(numCycles=1, player) {
}
Gang.prototype.processGains = function(numCycles=1, player) {
//Get gains per cycle
// Get gains per cycle
var moneyGains = 0, respectGains = 0, wantedLevelGains = 0;
for (var i = 0; i < this.members.length; ++i) {
respectGains += (this.members[i].calculateRespectGain(this));
@@ -291,9 +307,9 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
}
// Then process territory
for (var i = 0; i < GangNames.length; ++i) {
for (let i = 0; i < GangNames.length; ++i) {
const others = GangNames.filter((e) => {
return e !== i;
return e !== GangNames[i];
});
const other = getRandomInt(0, others.length - 1);
@@ -318,6 +334,7 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
AllGangs[otherGang].territory -= 0.0001;
if (thisGang === gangName) {
this.clash(true); // Player won
AllGangs[otherGang].power *= (1 / 1.01);
} else if (otherGang === gangName) {
this.clash(false); // Player lost
} else {
@@ -333,6 +350,7 @@ Gang.prototype.processTerritoryAndPowerGains = function(numCycles=1) {
this.clash(false); // Player lost
} else if (otherGang === gangName) {
this.clash(true); // Player won
AllGangs[thisGang].power *= (1 / 1.01);
} else {
AllGangs[thisGang].power *= (1 / 1.01);
}
@@ -562,10 +580,9 @@ Gang.fromJSON = function(value) {
Reviver.constructors.Gang = Gang;
/*** Gang Member object ***/
function GangMember(name) {
this.name = name;
this.task = "Unassigned"; //GangMemberTask object
this.task = "Unassigned";
this.earnedRespect = 0;
@@ -597,11 +614,11 @@ function GangMember(name) {
this.agi_asc_mult = 1;
this.cha_asc_mult = 1;
this.upgrades = []; //Names of upgrades
this.augmentations = []; //Names only
this.upgrades = []; // Names of upgrades
this.augmentations = []; // Names of augmentations only
}
//Same formula for Player
// Same skill calculation formula as Player
GangMember.prototype.calculateSkill = function(exp, mult=1) {
return Math.max(Math.floor(mult * (32 * Math.log(exp + 534.5) - 200)), 1);
}
@@ -645,7 +662,7 @@ GangMember.prototype.getTask = function() {
return GangMemberTasks["Unassigned"];
}
//Gains are per cycle
// Gains are per cycle
GangMember.prototype.calculateRespectGain = function(gang) {
const task = this.getTask();
if (task == null || !(task instanceof GangMemberTask) || task.baseRespect === 0) {return 0;}
@@ -770,9 +787,11 @@ GangMember.prototype.ascend = function() {
// Returns the multipliers that would be gained from ascension
GangMember.prototype.getAscensionResults = function() {
// Calculate ascension bonus to stat multipliers.
// This is based on the current number of multipliers from Non-Augmentation upgrades
// + Ascension Bonus = N% of current bonus from Augmentations
/**
* Calculate ascension bonus to stat multipliers.
* This is based on the current number of multipliers from Non-Augmentation upgrades
* + Ascension Bonus = N% of current bonus from Augmentations
*/
let hack = 1;
let str = 1;
let def = 1;
@@ -837,7 +856,7 @@ GangMember.fromJSON = function(value) {
Reviver.constructors.GangMember = GangMember;
//Defines tasks that Gang Members can work on
// Defines tasks that Gang Members can work on
function GangMemberTask(name="", desc="", isHacking=false, isCombat=false,
params={baseRespect: 0, baseWanted: 0, baseMoney: 0,
hackWeight: 0, strWeight: 0, defWeight: 0,
@@ -935,7 +954,7 @@ GangMemberUpgrade.prototype.createDescription = function() {
this.desc = lines.join("<br>");
}
//Passes in a GangMember object
// Passes in a GangMember object
GangMemberUpgrade.prototype.apply = function(member) {
if (this.mults.str != null) { member.str_mult *= this.mults.str; }
if (this.mults.def != null) { member.def_mult *= this.mults.def; }
@@ -971,7 +990,7 @@ gangMemberUpgradesMetadata.forEach((e) => {
Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
const boxId = "gang-member-upgrade-popup-box";
if (UIElems.gangMemberUpgradeBoxOpened) {
//Already opened, refreshing
// Already opened, refreshing
if (UIElems.gangMemberUpgradeBoxElements == null || UIElems.gangMemberUpgradeBox == null || UIElems.gangMemberUpgradeBoxContent == null) {
console.error("Refreshing Gang member upgrade box throws error because required elements are null");
return;
@@ -991,7 +1010,7 @@ Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
}
}
} else {
//New popup
// New popup
UIElems.gangMemberUpgradeBoxFilter = createElement("input", {
type:"text", placeholder:"Filter gang members",
value:initialFilter,
@@ -1023,7 +1042,7 @@ Gang.prototype.createGangMemberUpgradeBox = function(player, initialFilter="") {
}
}
//Create upgrade panels for each individual Gang Member
// Create upgrade panels for each individual Gang Member
GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
var container = createElement("div", {
border:"1px solid white",
@@ -1045,7 +1064,7 @@ GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
"Cha: " + this.cha + " (x" + formatNumber(this.cha_mult * this.cha_asc_mult, 2) + ")\n",
});
//Already purchased upgrades
// Already purchased upgrades
const ownedUpgradesElements = [];
function pushOwnedUpgrade(upgName) {
const upg = GangMemberUpgrades[upgName];
@@ -1071,7 +1090,7 @@ GangMember.prototype.createGangMemberUpgradePanel = function(gangObj, player) {
container.appendChild(ownedUpgrades);
container.appendChild(createElement("br", {}));
//Upgrade buttons. Only show upgrades that can be afforded
// Upgrade buttons. Only show upgrades that can be afforded
const weaponUpgrades = [];
const armorUpgrades = [];
const vehicleUpgrades = [];
@@ -1203,18 +1222,18 @@ Gang.prototype.displayGangContent = function(player) {
if (!UIElems.gangContentCreated || UIElems.gangContainer == null) {
UIElems.gangContentCreated = true;
//Create gang container
// Create gang container
UIElems.gangContainer = createElement("div", {
id:"gang-container", class:"generic-menupage-container",
});
//Get variables
// Get variables
var facName = this.facName,
members = this.members,
wanted = this.wanted,
respect = this.respect;
//Back button
// Back button
UIElems.gangContainer.appendChild(createElement("a", {
class:"a-link-button", display:"inline-block", innerText:"Back",
clickListener:()=>{
@@ -1224,7 +1243,7 @@ Gang.prototype.displayGangContent = function(player) {
}
}));
//Buttons to switch between panels
// Buttons to switch between panels
UIElems.managementButton = createElement("a", {
id:"gang-management-subpage-button", class:"a-link-button-inactive",
display:"inline-block", innerHTML: "Gang Management (Alt+1)",
@@ -1256,7 +1275,7 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangContainer.appendChild(UIElems.managementButton);
UIElems.gangContainer.appendChild(UIElems.territoryButton);
//Subpage for managing gang members
// Subpage for managing gang members
UIElems.gangManagementSubpage = createElement("div", {
display:"block", id:"gang-management-subpage",
});
@@ -1352,7 +1371,7 @@ Gang.prototype.displayGangContent = function(player) {
});
UIElems.gangManagementSubpage.appendChild(UIElems.gangRecruitRequirementText);
//Gang Member List management buttons (Expand/Collapse All, select a single member)
// Gang Member List management buttons (Expand/Collapse All, select a single member)
UIElems.gangManagementSubpage.appendChild(createElement("br", {}));
UIElems.gangExpandAllButton = createElement("a", {
class:"a-link-button", display:"inline-block",
@@ -1400,17 +1419,17 @@ Gang.prototype.displayGangContent = function(player) {
UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberFilter);
UIElems.gangManagementSubpage.appendChild(UIElems.gangManageEquipmentButton);
//Gang Member list
// Gang Member list
UIElems.gangMemberList = createElement("ul", {id:"gang-member-list"});
this.displayGangMemberList();
UIElems.gangManagementSubpage.appendChild(UIElems.gangMemberList);
//Subpage for seeing gang territory information
// Subpage for seeing gang territory information
UIElems.gangTerritorySubpage = createElement("div", {
id:"gang-territory-subpage", display:"none"
});
//Info text for territory page
// Info text for territory page
UIElems.gangTerritoryDescText = createElement("p", {
width:"70%",
innerHTML:
@@ -1575,7 +1594,7 @@ Gang.prototype.updateGangContent = function() {
}
}
} else {
//Update information for overall gang
// Update information for overall gang
if (UIElems.gangInfo instanceof Element) {
var faction = Factions[this.facName];
var rep;
@@ -1585,7 +1604,7 @@ Gang.prototype.updateGangContent = function() {
rep = faction.playerReputation;
}
removeChildrenFromElement(UIElems.gangInfo);
UIElems.gangInfo.appendChild(createElement("p", { // Respect
UIElems.gangInfo.appendChild(createElement("p", { // Respect
display: "inline-block",
innerText: "Respect: " + formatNumber(this.respect, 6) +
" (" + formatNumber(5*this.respectGainRate, 6) + " / sec)",
@@ -1596,7 +1615,7 @@ Gang.prototype.updateGangContent = function() {
}));
UIElems.gangInfo.appendChild(createElement("br"));
UIElems.gangInfo.appendChild(createElement("p", { // Wanted level
UIElems.gangInfo.appendChild(createElement("p", { // Wanted level
display: "inline-block",
innerText: "Wanted Level: " + formatNumber(this.wanted, 6) +
" (" + formatNumber(5*this.wantedGainRate, 6) + " / sec)",
@@ -1608,20 +1627,20 @@ Gang.prototype.updateGangContent = function() {
var wantedPenalty = this.getWantedPenalty();
wantedPenalty = (1 - wantedPenalty) * 100;
UIElems.gangInfo.appendChild(createElement("p", { // Wanted Level multiplier
UIElems.gangInfo.appendChild(createElement("p", { // Wanted Level multiplier
display: "inline-block",
innerText: `Wanted Level Penalty: -${formatNumber(wantedPenalty, 2)}%`,
tooltip: "Penalty for respect and money gain rates due to Wanted Level"
}));
UIElems.gangInfo.appendChild(createElement("br"));
UIElems.gangInfo.appendChild(createElement("p", { // Money gain rate
UIElems.gangInfo.appendChild(createElement("p", { // Money gain rate
display: "inline-block",
innerText: `Money gain rate: ${numeralWrapper.format(5 * this.moneyGainRate, "$0.000a")} / sec`,
}));
UIElems.gangInfo.appendChild(createElement("br"));
//Fix some rounding issues graphically
// Fix some rounding issues graphically
var territoryMult = AllGangs[this.facName].territory * 100;
let displayNumber;
if (territoryMult <= 0) {
@@ -1656,7 +1675,7 @@ Gang.prototype.updateGangContent = function() {
console.error("gang-info DOM element DNE");
}
//Toggle the 'Recruit member button' if valid
// Toggle the 'Recruit member button' if valid
const numMembers = this.members.length;
const respectCost = this.getRespectNeededToRecruitMember();
@@ -1674,14 +1693,14 @@ Gang.prototype.updateGangContent = function() {
UIElems.gangRecruitRequirementText.innerHTML = `${formatNumber(respectCost, 2)} respect needed to recruit next member`;
}
//Update information for each gang member
// Update information for each gang member
for (let i = 0; i < this.members.length; ++i) {
this.updateGangMemberDisplayElement(this.members[i]);
}
}
}
//Takes in a GangMember object
// Takes in a GangMember object
Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
if (!UIElems.gangContentCreated) { return; }
const name = memberObj.name;
@@ -1822,7 +1841,7 @@ Gang.prototype.createGangMemberDisplayElement = function(memberObj) {
taskDiv.appendChild(taskSelector);
taskDiv.appendChild(gainInfo);
//Panel for Description of task
// Panel for Description of task
var taskDescDiv = createElement("div", {
class:"gang-member-info-div",
id: name + "gang-member-task-desc",

View File

@@ -3,8 +3,13 @@
* to TypeScript at the moment
*/
export interface IEngine {
hideAllContent: () => void;
loadBladeburnerContent: () => void;
loadFactionContent: () => void;
loadFactionsContent: () => void;
loadGangContent: () => void;
loadInfiltrationContent: () => void;
loadMissionContent: () => void;
loadResleevingContent: () => void;
loadStockMarketContent: () => void;
}

View File

@@ -1,30 +1,12 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { Player } from "./Player";
import { dialogBoxCreate } from "../utils/DialogBox";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { infiltrationBoxCreate } from "../utils/InfiltrationBox";
import { formatNumber } from "../utils/StringHelperFunctions";
/* Infiltration.js
*
* Kill
* Knockout (nonlethal)
* Stealth Knockout (nonlethal)
* Assassinate
*
* Hack Security
* Destroy Security
* Sneak past Security
*
* Pick the locked door
*
* Bribe security
*
* Escape
*/
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { Player } from "./Player";
import { dialogBoxCreate } from "../utils/DialogBox";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { infiltrationBoxCreate } from "../utils/InfiltrationBox";
import { formatNumber } from "../utils/StringHelperFunctions";
let InfiltrationScenarios = {
Guards: "You see an armed security guard patrolling the area.",
@@ -35,21 +17,21 @@ let InfiltrationScenarios = {
}
function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff) {
this.companyName = companyName;
this.clearanceLevel = 0;
this.maxClearanceLevel = maxClearance;
this.securityLevel = startLevel;
this.difficulty = diff; //Affects how much security level increases. Represents a percentage
this.baseValue = val; //Base value of company secrets
this.secretsStolen = []; //Numbers representing value of stolen secrets
this.companyName = companyName;
this.clearanceLevel = 0;
this.maxClearanceLevel = maxClearance;
this.securityLevel = startLevel;
this.difficulty = diff; // Affects how much security level increases. Represents a percentage
this.baseValue = val; // Base value of company secrets
this.secretsStolen = []; // Numbers representing value of stolen secrets
this.hackingExpGained = 0;
this.strExpGained = 0;
this.defExpGained = 0;
this.dexExpGained = 0;
this.agiExpGained = 0;
this.chaExpGained = 0;
this.intExpGained = 0;
this.hackingExpGained = 0;
this.strExpGained = 0;
this.defExpGained = 0;
this.dexExpGained = 0;
this.agiExpGained = 0;
this.chaExpGained = 0;
this.intExpGained = 0;
}
InfiltrationInstance.prototype.expMultiplier = function() {
@@ -124,7 +106,7 @@ InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
InfiltrationInstance.prototype.calcGainedIntelligenceExp = function() {
if(!this.intExpGained || isNaN(this.intExpGained)) return 0;
return Math.pow(this.intExpGained*this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
return Math.pow(this.intExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
}
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
@@ -177,7 +159,7 @@ function nextInfiltrationLevel(inst) {
bribeButton.style.display = "none";
escapeButton.style.display = "none";
var rand = getRandomInt(0, 5); //This needs to change if more scenarios are added
var rand = getRandomInt(0, 5); // This needs to change if more scenarios are added
var scenario = null;
switch (rand) {
case 1:
@@ -460,7 +442,7 @@ function nextInfiltrationLevel(inst) {
function endInfiltrationLevel(inst) {
//Check if you gained any secrets
// Check if you gained any secrets
if (inst.clearanceLevel % 5 == 0) {
var baseSecretValue = inst.baseValue * inst.clearanceLevel / 2;
var secretValue = baseSecretValue * Player.faction_rep_mult *
@@ -474,12 +456,12 @@ function endInfiltrationLevel(inst) {
"could be given to factions for reputation (<span class='light-yellow'>" + formatNumber(secretValue, 3) + " rep</span>)");
}
//Increase security level based on difficulty
// Increase security level based on difficulty
inst.securityLevel *= (1 + (inst.difficulty / 100));
writeInfiltrationStatusText("You move on to the facility's next clearance level. This " +
"clearance level has " + inst.difficulty + "% higher security");
//If this is max level, force endInfiltration
// If this is max level, force endInfiltration
if (inst.clearanceLevel >= inst.maxClearanceLevel) {
endInfiltration(inst, true);
} else {
@@ -647,8 +629,8 @@ function updateInfiltrationButtons(inst, scenario) {
let intWgt = CONSTANTS.IntelligenceInfiltrationWeight;
//Kill
//Success: 5%, Failure 10%, -Karma
// Kill
// Success: 5%, Failure 10%, -Karma
function attemptInfiltrationKill(inst) {
var chance = getInfiltrationKillChance(inst);
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
@@ -673,8 +655,8 @@ function getInfiltrationKillChance(inst) {
}
//Knockout
//Success: 3%, Failure: 10%
// Knockout
// Success: 3%, Failure: 10%
function attemptInfiltrationKnockout(inst) {
var chance = getInfiltrationKnockoutChance(inst);
inst.gainStrengthExp(inst.securityLevel / 70) * Player.strength_exp_mult;
@@ -698,8 +680,8 @@ function getInfiltrationKnockoutChance(inst) {
Player.agility) / (1.7 * lvl));
}
//Stealth knockout
//Success: 0%, Failure: 10%
// Stealth knockout
// Success: 0%, Failure: 10%
function attemptInfiltrationStealthKnockout(inst) {
var chance = getInfiltrationStealthKnockoutChance(inst);
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
@@ -722,8 +704,8 @@ function getInfiltrationStealthKnockoutChance(inst) {
intWgt * Player.intelligence) / (3 * lvl));
}
//Assassination
//Success: 0%, Failure: 5%, -Karma
// Assassination
// Success: 0%, Failure: 5%, -Karma
function attemptInfiltrationAssassinate(inst) {
var chance = getInfiltrationAssassinateChance(inst);
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
@@ -746,8 +728,8 @@ function getInfiltrationAssassinateChance(inst) {
}
//Destroy security
//Success: 5%, Failure: 10%
// Destroy security
// Success: 5%, Failure: 10%
function attemptInfiltrationDestroySecurity(inst) {
var chance = getInfiltrationDestroySecurityChance(inst);
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
@@ -773,8 +755,8 @@ function getInfiltrationDestroySecurityChance(inst) {
}
//Hack security
//Success: 3%, Failure: 5%
// Hack security
// Success: 3%, Failure: 5%
function attemptInfiltrationHack(inst) {
var chance = getInfiltrationHackChance(inst);
inst.gainHackingExp(inst.securityLevel / 30) * Player.hacking_exp_mult;
@@ -796,8 +778,8 @@ function getInfiltrationHackChance(inst) {
(intWgt * Player.intelligence)) / lvl);
}
//Sneak past security
//Success: 0%, Failure: 8%
// Sneak past security
// Success: 0%, Failure: 8%
function attemptInfiltrationSneak(inst) {
var chance = getInfiltrationSneakChance(inst);
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;
@@ -817,8 +799,8 @@ function getInfiltrationSneakChance(inst) {
intWgt * Player.intelligence) / (2 * lvl));
}
//Pick locked door
//Success: 1%, Failure: 3%
// Pick locked door
// Success: 1%, Failure: 3%
function attemptInfiltrationPickLockedDoor(inst) {
var chance = getInfiltrationPickLockedDoorChance(inst);
inst.gainDexterityExp(inst.securityLevel / 25) * Player.dexterity_exp_mult;
@@ -838,8 +820,8 @@ function getInfiltrationPickLockedDoorChance(inst) {
intWgt * Player.intelligence) / lvl);
}
//Bribe
//Success: 0%, Failure: 15%,
// Bribe
// Success: 0%, Failure: 15%,
function attemptInfiltrationBribe(inst) {
var chance = getInfiltrationBribeChance(inst);
inst.gainCharismaExp(inst.securityLevel / 8) * Player.charisma_exp_mult;
@@ -857,8 +839,8 @@ function getInfiltrationBribeChance(inst) {
(Player.charisma) / lvl);
}
//Escape
//Failure: 5%
// Escape
// Failure: 5%
function attemptInfiltrationEscape(inst) {
var chance = getInfiltrationEscapeChance(inst);
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;

View File

@@ -1,34 +1,37 @@
import { Engine } from "./engine";
import { Player } from "./Player";
import { Settings } from "./Settings/Settings";
import { initializeMainMenuLinks } from "./ui/MainMenu/Links";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { Engine } from "./engine";
import { Player } from "./Player";
import { Settings } from "./Settings/Settings";
//Ordered array of keys to Interactive Tutorial Steps
import { initializeMainMenuLinks } from "./ui/MainMenu/Links";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
// Ordered array of keys to Interactive Tutorial Steps
const orderedITutorialSteps = [
"Start",
"GoToCharacterPage", //Click on 'Stats' page
"CharacterPage", //Introduction to 'Stats' page
"CharacterGoToTerminalPage", //Go back to Terminal
"TerminalIntro", //Introduction to Terminal
"TerminalHelp", //Using 'help' Terminal command
"TerminalLs", //Using 'ls' Terminal command
"TerminalScan", //Using 'scan' Terminal command
"TerminalScanAnalyze1", //Using 'scan-analyze' Terminal command
"TerminalScanAnalyze2", //Using 'scan-analyze 3' Terminal command
"TerminalConnect", //Connecting to foodnstuff
"TerminalAnalyze", //Analyzing foodnstuff
"TerminalNuke", //NUKE foodnstuff
"TerminalManualHack", //Hack foodnstuff
"TerminalHackingMechanics", //Explanation of hacking mechanics
"TerminalCreateScript", //Create a script using 'nano'
"TerminalTypeScript", //Script Editor page - Type script and then save & close
"TerminalFree", //Using 'Free' Terminal command
"TerminalRunScript", //Running script using 'run' Terminal command
"GoToCharacterPage", // Click on 'Stats' page
"CharacterPage", // Introduction to 'Stats' page
"CharacterGoToTerminalPage", // Go back to Terminal
"TerminalIntro", // Introduction to Terminal
"TerminalHelp", // Using 'help' Terminal command
"TerminalLs", // Using 'ls' Terminal command
"TerminalScan", // Using 'scan' Terminal command
"TerminalScanAnalyze1", // Using 'scan-analyze' Terminal command
"TerminalScanAnalyze2", // Using 'scan-analyze 3' Terminal command
"TerminalConnect", // Connecting to foodnstuff
"TerminalAnalyze", // Analyzing foodnstuff
"TerminalNuke", // NUKE foodnstuff
"TerminalManualHack", // Hack foodnstuff
"TerminalHackingMechanics", // Explanation of hacking mechanics
"TerminalCreateScript", // Create a script using 'nano'
"TerminalTypeScript", // Script Editor page - Type script and then save & close
"TerminalFree", // Using 'Free' Terminal command
"TerminalRunScript", // Running script using 'run' Terminal command
"TerminalGoToActiveScriptsPage",
"ActiveScriptsPage",
"ActiveScriptsToTerminal",
@@ -41,22 +44,22 @@ const orderedITutorialSteps = [
"End"
]
//Create an 'enum' for the Steps
// Create an 'enum' for the Steps
const iTutorialSteps = {};
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
iTutorialSteps[orderedITutorialSteps[i]] = i;
}
var ITutorial = {
currStep: 0, //iTutorialSteps.Start
currStep: 0, // iTutorialSteps.Start
isRunning: false,
//Keeps track of whether each step has been done
// Keeps track of whether each step has been done
stepIsDone: {},
}
function iTutorialStart() {
//Initialize Interactive Tutorial state by settings 'done' for each state to false
// Initialize Interactive Tutorial state by settings 'done' for each state to false
ITutorial.stepIsDone = {};
for (let i = 0; i < orderedITutorialSteps.length; ++i) {
ITutorial.stepIsDone[i] = false;
@@ -64,7 +67,7 @@ function iTutorialStart() {
Engine.loadTerminalContent();
//Don't autosave during this interactive tutorial
// Don't autosave during this interactive tutorial
Engine.Counters.autoSaveCounter = Infinity;
console.log("Interactive Tutorial started");
ITutorial.currStep = 0;
@@ -72,21 +75,21 @@ function iTutorialStart() {
document.getElementById("interactive-tutorial-container").style.display = "block";
//Exit tutorial button
// Exit tutorial button
var exitButton = clearEventListeners("interactive-tutorial-exit");
exitButton.addEventListener("click", function() {
iTutorialEnd();
return false;
});
//Back button
// Back button
var backButton = clearEventListeners("interactive-tutorial-back");
backButton.addEventListener("click", function() {
iTutorialPrevStep();
return false;
});
//Next button
// Next button
var nextButton = clearEventListeners("interactive-tutorial-next");
nextButton.addEventListener("click", function() {
iTutorialNextStep();
@@ -99,7 +102,7 @@ function iTutorialStart() {
function iTutorialEvaluateStep() {
if (!ITutorial.isRunning) {console.log("Interactive Tutorial not running"); return;}
//Disable and clear main menu
// Disable and clear main menu
var terminalMainMenu = clearEventListeners("terminal-menu-link");
var statsMainMenu = clearEventListeners("stats-menu-link");
var activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
@@ -113,7 +116,7 @@ function iTutorialEvaluateStep() {
cityMainMenu.removeAttribute("class");
tutorialMainMenu.removeAttribute("class");
//Interactive Tutorial Next button
// Interactive Tutorial Next button
var nextBtn = document.getElementById("interactive-tutorial-next");
switch(ITutorial.currStep) {
@@ -131,7 +134,7 @@ function iTutorialEvaluateStep() {
"the main navigation menu (left-hand side of the screen)");
nextBtn.style.display = "none";
//Flash 'Stats' menu and set its tutorial click handler
// Flash 'Stats' menu and set its tutorial click handler
statsMainMenu.setAttribute("class", "flashing-button");
statsMainMenu.addEventListener("click", function() {
Engine.loadCharacterContent();
@@ -151,7 +154,7 @@ function iTutorialEvaluateStep() {
"main navigation menu.");
nextBtn.style.display = "none";
//Flash 'Terminal' menu and set its tutorial click handler
// Flash 'Terminal' menu and set its tutorial click handler
terminalMainMenu.setAttribute("class", "flashing-button");
terminalMainMenu.addEventListener("click", function() {
Engine.loadTerminalContent();
@@ -169,13 +172,13 @@ function iTutorialEvaluateStep() {
Engine.loadTerminalContent();
iTutorialSetText("Let's try it out. Start by entering the 'help' command into the Terminal " +
"(Don't forget to press Enter after typing the command)");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalLs:
Engine.loadTerminalContent();
iTutorialSetText("The 'help' command displays a list of all available Terminal commands, how to use them, " +
"and a description of what they do. <br><br>Let's try another command. Enter the 'ls' command");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalScan:
Engine.loadTerminalContent();
@@ -184,7 +187,7 @@ function iTutorialEvaluateStep() {
"We'll get to what this does later. <br><br>Using your home computer's terminal, you can connect " +
"to other machines throughout the world. Let's do that now by first entering " +
"the 'scan' command.");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalScanAnalyze1:
Engine.loadTerminalContent();
@@ -194,7 +197,7 @@ function iTutorialEvaluateStep() {
"That's great and all, but there's so many servers. Which one should you go to? " +
"The 'scan-analyze' command gives some more detailed information about servers on the " +
"network. Try it now");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalScanAnalyze2:
Engine.loadTerminalContent();
@@ -202,7 +205,7 @@ function iTutorialEvaluateStep() {
"information about each server that you can connect to (servers that are a distance of " +
"one node away). <br><br> It is also possible to run 'scan-analyze' with " +
"a higher depth. Let's try a depth of two with the following command: 'scan-analyze 2'.")
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalConnect:
Engine.loadTerminalContent();
@@ -212,7 +215,7 @@ function iTutorialEvaluateStep() {
"the ip or the hostname, but dont use both.<br><br>" +
"From the results of the 'scan-analyze' command, we can see that the 'foodnstuff' server is " +
"only one node away. Let's connect so it now using: 'connect foodnstuff'");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalAnalyze:
Engine.loadTerminalContent();
@@ -221,7 +224,7 @@ function iTutorialEvaluateStep() {
"on servers and computers. Using your hacking abilities, you can hack servers " +
"to steal money and gain experience. <br><br> " +
"Before you try to hack a server, you should run diagnostics using the 'analyze' command");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalNuke:
Engine.loadTerminalContent();
@@ -233,13 +236,13 @@ function iTutorialEvaluateStep() {
"open ports.<br><br> The 'analyze' results shows that there do not need to be any open ports " +
"on this machine for the NUKE virus to work, so go ahead and run the virus using the " +
"'run NUKE.exe' command.");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalManualHack:
Engine.loadTerminalContent();
iTutorialSetText("You now have root access! You can hack the server using the 'hack' command. " +
"Try doing that now.");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalHackingMechanics:
Engine.loadTerminalContent();
@@ -262,7 +265,7 @@ function iTutorialEvaluateStep() {
"command. Scripts must end with the '.script' extension. Let's make a script now by " +
"entering 'nano foodnstuff.script' after the hack command finishes running (Sidenote: Pressing ctrl + c" +
" will end a command like hack early)");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalTypeScript:
Engine.loadScriptEditorContent("foodnstuff.script", "");
@@ -278,7 +281,7 @@ function iTutorialEvaluateStep() {
"For anyone with basic programming experience, this code should be straightforward. " +
"This script will continuously hack the 'foodnstuff' server.<br><br>" +
"To save and close the script editor, press the button in the bottom left, or press ctrl + b.");
nextBtn.style.display = "none"; //next step triggered in saveAndCloseScriptEditor() (Script.js)
nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js)
break;
case iTutorialSteps.TerminalFree:
Engine.loadTerminalContent();
@@ -286,13 +289,13 @@ function iTutorialEvaluateStep() {
"run on any machine which you have root access to. Different servers have different " +
"amounts of RAM. You can also purchase more RAM for your home server.<br><br>To check how much " +
"RAM is available on this machine, enter the 'free' command.");
nextBtn.style.display = "none"; //next step triggered by terminal commmand
nextBtn.style.display = "none"; // next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalRunScript:
Engine.loadTerminalContent();
iTutorialSetText("We have 16GB of free RAM on this machine, which is enough to run our " +
"script. Let's run our script using 'run foodnstuff.script'.");
nextBtn.style.display = "none"; //next step triggered by terminal commmand
nextBtn.style.display = "none"; // next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
Engine.loadTerminalContent();
@@ -306,7 +309,7 @@ function iTutorialEvaluateStep() {
"'Active Scripts' link in the main navigation menu.");
nextBtn.style.display = "none";
//Flash 'Active Scripts' menu and set its tutorial click handler
// Flash 'Active Scripts' menu and set its tutorial click handler
activeScriptsMainMenu.setAttribute("class", "flashing-button");
activeScriptsMainMenu.addEventListener("click", function() {
Engine.loadActiveScriptsContent();
@@ -322,7 +325,7 @@ function iTutorialEvaluateStep() {
"link.");
nextBtn.style.display = "none";
//Flash 'Terminal' button and set its tutorial click handler
// Flash 'Terminal' button and set its tutorial click handler
terminalMainMenu.setAttribute("class", "flashing-button");
terminalMainMenu.addEventListener("click", function() {
Engine.loadTerminalContent();
@@ -335,7 +338,7 @@ function iTutorialEvaluateStep() {
iTutorialSetText("One last thing about scripts, each active script contains logs that detail " +
"what it's doing. We can check these logs using the 'tail' command. Do that " +
"now for the script we just ran by typing 'tail foodnstuff.script'");
nextBtn.style.display = "none"; //next step triggered by terminal command
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalTailScript:
Engine.loadTerminalContent();
@@ -356,7 +359,7 @@ function iTutorialEvaluateStep() {
"the 'Hacknet Nodes' page through the main navigation menu now.");
nextBtn.style.display = "none";
//Flash 'Hacknet' menu and set its tutorial click handler
// Flash 'Hacknet' menu and set its tutorial click handler
hacknetMainMenu.setAttribute("class", "flashing-button");
hacknetMainMenu.addEventListener("click", function() {
Engine.loadHacknetNodesContent();
@@ -368,7 +371,7 @@ function iTutorialEvaluateStep() {
Engine.loadHacknetNodesContent();
iTutorialSetText("From this page you can purchase new Hacknet Nodes and upgrade your " +
"existing ones. Let's purchase a new one now.");
nextBtn.style.display = "none"; //Next step triggered by purchaseHacknet() (HacknetNode.js)
nextBtn.style.display = "none"; // Next step triggered by purchaseHacknet() (HacknetNode.js)
break;
case iTutorialSteps.HacknetNodesGoToWorldPage:
Engine.loadHacknetNodesContent();
@@ -379,7 +382,7 @@ function iTutorialEvaluateStep() {
"Let's go to the 'City' page through the main navigation menu.");
nextBtn.style.display = "none";
//Flash 'City' menu and set its tutorial click handler
// Flash 'City' menu and set its tutorial click handler
cityMainMenu.setAttribute("class", "flashing-button");
cityMainMenu.addEventListener("click", function() {
Engine.loadLocationContent();
@@ -396,7 +399,7 @@ function iTutorialEvaluateStep() {
"Lastly, click on the 'Tutorial' link in the main navigation menu.");
nextBtn.style.display = "none";
//Flash 'Tutorial' menu and set its tutorial click handler
// Flash 'Tutorial' menu and set its tutorial click handler
tutorialMainMenu.setAttribute("class", "flashing-button");
tutorialMainMenu.addEventListener("click", function() {
Engine.loadTutorialContent();
@@ -425,9 +428,9 @@ function iTutorialEvaluateStep() {
}
}
//Go to the next step and evaluate it
// Go to the next step and evaluate it
function iTutorialNextStep() {
//Special behavior for certain steps
// Special behavior for certain steps
if (ITutorial.currStep === iTutorialSteps.GoToCharacterPage) {
document.getElementById("stats-menu-link").removeAttribute("class");
}
@@ -457,7 +460,7 @@ function iTutorialNextStep() {
iTutorialEvaluateStep();
}
//Go to previous step and evaluate
// Go to previous step and evaluate
function iTutorialPrevStep() {
if (ITutorial.currStep > iTutorialSteps.Start) {
ITutorial.currStep -= 1;
@@ -466,7 +469,7 @@ function iTutorialPrevStep() {
}
function iTutorialEnd() {
//Re-enable auto save
// Re-enable auto save
if (Settings.AutosaveInterval === 0) {
Engine.Counters.autoSaveCounter = Infinity;
} else {
@@ -491,7 +494,7 @@ function iTutorialEnd() {
ITutorial.isRunning = false;
document.getElementById("interactive-tutorial-container").style.display = "none";
//Create a popup with final introductory stuff
// Create a popup with final introductory stuff
var popupId = "interactive-tutorial-ending-popup";
var txt = createElement("p", {
innerHTML:

View File

@@ -1,8 +1,9 @@
import {dialogBoxCreate} from "../utils/DialogBox";
/* Literature.js
* Lore / world building literature that can be found on servers
/**
* Lore / world building literature files that can be found on servers.
* These files can be read by the player
*/
import { dialogBoxCreate } from "../utils/DialogBox";
function Literature(title, filename, txt) {
this.title = title;
this.fn = filename;
@@ -10,10 +11,9 @@ function Literature(title, filename, txt) {
}
function showLiterature(fn) {
var litObj = Literatures[fn];
if (litObj == null) {return;}
var txt = "<i>" + litObj.title + "</i><br><br>" +
litObj.txt;
const litObj = Literatures[fn];
if (litObj == null) { return; }
const txt = `<i>${litObj.title}</i><br><br>${litObj.txt}`;
dialogBoxCreate(txt);
}
@@ -430,7 +430,10 @@ function initLiterature() {
fn = "the-secret-war.lit";
txt = ""
Literatures[fn] = new Literature(title, fn, txt);
}
export {Literatures, initLiterature, showLiterature};
export {
Literatures,
initLiterature,
showLiterature
};

View File

@@ -108,7 +108,7 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer) {
* Create a popup that lets the player start a Corporation
*/
export function createStartCorporationPopup(p: IPlayer) {
if (!p.canAccessCorporation() || p.hasCorporation) { return; }
if (!p.canAccessCorporation() || p.hasCorporation()) { return; }
const popupId = "create-corporation-popup";
const txt = createElement("p", {

View File

@@ -1,16 +1,21 @@
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player";
import { dialogBoxCreate } from "../utils/DialogBox";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { addOffset } from "../utils/helpers/addOffset";
import { formatNumber } from "../utils/StringHelperFunctions";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { isString } from "../utils/helpers/isString";
import jsplumb from 'jsplumb'
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player";
let inMission = false; //Flag to denote whether a mission is running
import { dialogBoxCreate } from "../utils/DialogBox";
import { formatNumber } from "../utils/StringHelperFunctions";
import { addOffset } from "../utils/helpers/addOffset";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { isString } from "../utils/helpers/isString";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import jsplumb from "jsplumb";
let inMission = false; // Flag to denote whether a mission is running
let currMission = null;
function setInMission(bool, mission) {
inMission = bool;
@@ -21,26 +26,26 @@ function setInMission(bool, mission) {
}
}
//Keyboard shortcuts
// Keyboard shortcuts
$(document).keydown(function(e) {
if (inMission && currMission && currMission.selectedNode.length != 0) {
switch (e.keyCode) {
case 65: //a for Attack
case 65: // a for Attack
currMission.actionButtons[0].click();
break;
case 83: //s for Scan
case 83: // s for Scan
currMission.actionButtons[1].click();
break;
case 87: //w for Weaken
case 87: // w for Weaken
currMission.actionButtons[2].click();
break;
case 70: //f for Fortify
case 70: // f for Fortify
currMission.actionButtons[3].click();
break;
case 82: //r for Overflow
case 82: // r for Overflow
currMission.actionButtons[4].click();
break;
case 68: //d for Detach connection
case 68: // d for Detach connection
currMission.actionButtons[5].click();
break;
default:
@@ -50,20 +55,20 @@ $(document).keydown(function(e) {
});
let NodeTypes = {
Core: "CPU Core Node", //All actions available
Firewall: "Firewall Node", //No actions available
Database: "Database Node", //No actions available
Spam: "Spam Node", //No actions Available
Transfer: "Transfer Node", //Can Weaken, Scan, Fortify and Overflow
Shield: "Shield Node" //Can Fortify
Core: "CPU Core Node", // All actions available
Firewall: "Firewall Node", // No actions available
Database: "Database Node", // No actions available
Spam: "Spam Node", // No actions Available
Transfer: "Transfer Node", // Can Weaken, Scan, Fortify and Overflow
Shield: "Shield Node" // Can Fortify
}
let NodeActions = {
Attack: "Attacking", //Damaged based on attack stat + hacking level + opp def
Scan: "Scanning", //-Def for target, affected by attack and hacking level
Weaken: "Weakening", //-Attack for target, affected by attack and hacking level
Fortify: "Fortifying", //+Defense for Node, affected by hacking level
Overflow: "Overflowing", //+Attack but -Defense for Node, affected by hacking level
Attack: "Attacking", // Damaged based on attack stat + hacking level + opp def
Scan: "Scanning", // -Def for target, affected by attack and hacking level
Weaken: "Weakening", // -Attack for target, affected by attack and hacking level
Fortify: "Fortifying", // +Defense for Node, affected by hacking level
Overflow: "Overflowing", // +Attack but -Defense for Node, affected by hacking level
}
function Node(type, stats) {
@@ -74,14 +79,15 @@ function Node(type, stats) {
this.maxhp = this.hp;
this.plyrCtrl = false;
this.enmyCtrl = false;
this.pos = [0, 0]; //x, y
this.el = null; //Holds the Node's DOM element
this.pos = [0, 0]; // x, y
this.el = null; // Holds the Node's DOM element
this.action = null;
this.targetedCount = 0; //Count of how many connections this node is the target of
this.targetedCount = 0; // Count of how many connections this node is the target of
//Holds the JsPlumb Connection object for this Node,
//where this Node is the Source (since each Node
//can only have 1 outgoing Connection)
/**
* Holds the JsPlumb Connection object for this Node, where this Node is the Source (since each Node
* can only have 1 outgoing Connection)
*/
this.conn = null;
}
@@ -107,12 +113,12 @@ Node.prototype.setControlledByEnemy = function() {
}
}
//Sets this node to be the active node
// Sets this node to be the active node
Node.prototype.select = function(actionButtons) {
if (this.enmyCtrl) {return;}
this.el.classList.add("hack-mission-player-node-active");
//Make all buttons inactive
// Make all buttons inactive
for (var i = 0; i < actionButtons.length; ++i) {
actionButtons[i].classList.remove("a-link-button");
actionButtons[i].classList.add("a-link-button-inactive");
@@ -120,7 +126,7 @@ Node.prototype.select = function(actionButtons) {
switch(this.type) {
case NodeTypes.Core:
//All buttons active
// All buttons active
for (var i = 0; i < actionButtons.length; ++i) {
actionButtons[i].classList.remove("a-link-button-inactive");
actionButtons[i].classList.add("a-link-button");
@@ -165,31 +171,33 @@ Node.prototype.untarget = function() {
--this.targetedCount;
}
//Hacking mission instance
//Takes in the reputation of the Faction for which the mission is
//being conducted
/**
* Hacking mission instance
* @param rep {number} How much reputation the player has for the faction
* @param fac {Faction} Faction for which this mission is being conducted
*/
function HackingMission(rep, fac) {
this.faction = fac;
this.started = false;
this.time = 180000; //5 minutes to start, milliseconds
this.time = 180000; // 5 minutes to start, milliseconds
this.playerCores = [];
this.playerNodes = []; //Non-core nodes
this.playerNodes = []; // Non-core nodes
this.playerAtk = 0;
this.playerDef = 0;
this.enemyCores = [];
this.enemyDatabases = [];
this.enemyNodes = []; //Non-core nodes
this.enemyNodes = []; // Non-core nodes
this.enemyAtk = 0;
this.enemyDef = 0;
this.miscNodes = [];
this.selectedNode = []; //Which of the player's nodes are currently selected
this.selectedNode = []; // Which of the player's nodes are currently selected
this.actionButtons = []; //DOM buttons for actions
this.actionButtons = []; // DOM buttons for actions
this.availablePositions = [];
for (var r = 0; r < 8; ++r) {
@@ -211,10 +219,10 @@ function HackingMission(rep, fac) {
}
HackingMission.prototype.init = function() {
//Create Header DOM
// Create Header DOM
this.createPageDom();
//Create player starting nodes
// Create player starting nodes
var home = Player.getHomeComputer()
for (var i = 0; i < home.cpuCores; ++i) {
var stats = {
@@ -228,7 +236,7 @@ HackingMission.prototype.init = function() {
this.removeAvailablePosition(i, 0);
}
//Randomly generate enemy nodes (CPU and Firewall) based on difficulty
// Randomly generate enemy nodes (CPU and Firewall) based on difficulty
var numNodes = Math.min(8, Math.max(1, Math.round(this.difficulty / 4)));
var numFirewalls = Math.min(20,
getRandomInt(Math.round(this.difficulty/3), Math.round(this.difficulty/3) + 1));
@@ -304,10 +312,10 @@ HackingMission.prototype.createPageDom = function() {
wikiGuideBtn.style.display = "inline-block";
wikiGuideBtn.classList.add("hack-mission-header-element");
wikiGuideBtn.target = "_blank";
//TODO Add link to wiki page wikiGuideBtn.href =
// TODO Add link to wiki page wikiGuideBtn.href =
//Start button will get replaced with forfeit when game is started
// Start button will get replaced with forfeit when game is started
var startBtn = document.createElement("a");
startBtn.innerHTML = "Start";
startBtn.setAttribute("id", "hack-mission-start-btn");
@@ -334,15 +342,15 @@ HackingMission.prototype.createPageDom = function() {
timer.style.display = "inline-block";
timer.style.margin = "6px";
//Create Action Buttons (Attack/Scan/Weaken/ etc...)
// Create Action Buttons (Attack/Scan/Weaken/ etc...)
var actionsContainer = document.createElement("span");
actionsContainer.style.display = "block";
actionsContainer.classList.add("hack-mission-action-buttons-container");
for (var i = 0; i < 6; ++i) {
this.actionButtons.push(document.createElement("a"));
this.actionButtons[i].style.display = "inline-block";
this.actionButtons[i].classList.add("a-link-button-inactive"); //Disabled at start
this.actionButtons[i].classList.add("tooltip"); //Disabled at start
this.actionButtons[i].classList.add("a-link-button-inactive"); // Disabled at start
this.actionButtons[i].classList.add("tooltip"); // Disabled at start
this.actionButtons[i].classList.add("hack-mission-header-element");
actionsContainer.appendChild(this.actionButtons[i]);
}
@@ -383,7 +391,7 @@ HackingMission.prototype.createPageDom = function() {
"also be done by simply clicking the white connection line.";
this.actionButtons[5].appendChild(dropconnTooltip);
//Player/enemy defense displays will be in action container
// Player/enemy defense displays will be in action container
var playerStats = document.createElement("p");
var enemyStats = document.createElement("p");
playerStats.style.display = "inline-block";
@@ -397,7 +405,7 @@ HackingMission.prototype.createPageDom = function() {
actionsContainer.appendChild(playerStats);
actionsContainer.appendChild(enemyStats);
//Set Action Button event listeners
// Set Action Button event listeners
this.actionButtons[0].addEventListener("click", ()=>{
if (!(this.selectedNode.length > 0)) {
console.log("ERR: Pressing Action button without selected node");
@@ -405,7 +413,7 @@ HackingMission.prototype.createPageDom = function() {
}
if (this.selectedNode[0].type !== NodeTypes.Core) {return;}
this.setActionButtonsActive(this.selectedNode[0].type);
this.setActionButton(NodeActions.Attack, false); //Set attack button inactive
this.setActionButton(NodeActions.Attack, false); // Set attack button inactive
this.selectedNode.forEach(function(node){
node.action = NodeActions.Attack;
});
@@ -416,10 +424,10 @@ HackingMission.prototype.createPageDom = function() {
console.log("ERR: Pressing Action button without selected node");
return;
}
var nodeType = this.selectedNode[0].type; //In a multiselect, every Node will have the same type
var nodeType = this.selectedNode[0].type; // In a multiselect, every Node will have the same type
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
this.setActionButtonsActive(nodeType);
this.setActionButton(NodeActions.Scan, false); //Set scan button inactive
this.setActionButton(NodeActions.Scan, false); // Set scan button inactive
this.selectedNode.forEach(function(node){
node.action = NodeActions.Scan;
});
@@ -430,10 +438,10 @@ HackingMission.prototype.createPageDom = function() {
console.log("ERR: Pressing Action button without selected node");
return;
}
var nodeType = this.selectedNode[0].type; //In a multiselect, every Node will have the same type
var nodeType = this.selectedNode[0].type; // In a multiselect, every Node will have the same type
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
this.setActionButtonsActive(nodeType);
this.setActionButton(NodeActions.Weaken, false); //Set Weaken button inactive
this.setActionButton(NodeActions.Weaken, false); // Set Weaken button inactive
this.selectedNode.forEach(function(node){
node.action = NodeActions.Weaken;
});
@@ -445,7 +453,7 @@ HackingMission.prototype.createPageDom = function() {
return;
}
this.setActionButtonsActive(this.selectedNode[0].type);
this.setActionButton(NodeActions.Fortify, false); //Set Fortify button inactive
this.setActionButton(NodeActions.Fortify, false); // Set Fortify button inactive
this.selectedNode.forEach(function(node){
node.action = NodeActions.Fortify;
});
@@ -459,7 +467,7 @@ HackingMission.prototype.createPageDom = function() {
var nodeType = this.selectedNode[0].type;
if (nodeType !== NodeTypes.Core && nodeType !== NodeTypes.Transfer) {return;}
this.setActionButtonsActive(nodeType);
this.setActionButton(NodeActions.Overflow, false); //Set Overflow button inactive
this.setActionButton(NodeActions.Overflow, false); // Set Overflow button inactive
this.selectedNode.forEach(function(node){
node.action = NodeActions.Overflow;
});
@@ -477,11 +485,7 @@ HackingMission.prototype.createPageDom = function() {
}
node.action = NodeActions.Fortify;
});
// if (this.selectedNode.conn) {
// var endpoints = this.selectedNode.conn.endpoints;
// endpoints[0].detachFrom(endpoints[1]);
// }
})
});
var timeDisplay = document.createElement("p");
@@ -508,8 +512,10 @@ HackingMission.prototype.setActionButtonsActive = function(nodeType=null) {
this.actionButtons[i].classList.remove("a-link-button-inactive");
}
//For Transfer, FireWall and Shield Nodes, certain buttons should always be disabled
//0 = Attack, 1 = Scan, 2 = Weaken, 3 = Fortify, 4 = overflow, 5 = Drop conn
/**
* For Transfer, FireWall and Shield Nodes, certain buttons should always be disabled
* 0 = Attack, 1 = Scan, 2 = Weaken, 3 = Fortify, 4 = overflow, 5 = Drop conn
*/
if (nodeType) {
switch (nodeType) {
case NodeTypes.Firewall:
@@ -535,7 +541,7 @@ HackingMission.prototype.setActionButtonsActive = function(nodeType=null) {
}
}
//True for active, false for inactive
// True for active, false for inactive
HackingMission.prototype.setActionButton = function(i, active=true) {
if (isString(i)) {
switch (i) {
@@ -652,7 +658,7 @@ HackingMission.prototype.setNodePosition = function(nodeObj, x, y) {
HackingMission.prototype.setNodeRandomPosition = function(nodeObj, xlimit=0) {
var i = getRandomInt(0, this.availablePositions.length - 1);
if (this.availablePositions[i][1] < xlimit) {
//Recurse if not within limit
// Recurse if not within limit
return this.setNodeRandomPosition(nodeObj, xlimit);
}
var pos = this.availablePositions.splice(i, 1);
@@ -661,15 +667,15 @@ HackingMission.prototype.setNodeRandomPosition = function(nodeObj, xlimit=0) {
}
HackingMission.prototype.createMap = function() {
//Use a grid
// Use a grid
var map = document.createElement("div");
map.classList.add("hack-mission-grid");
map.setAttribute("id", "hacking-mission-map");
document.getElementById("mission-container").appendChild(map);
//Create random Nodes for every space in the map that
//hasn't been filled yet. The stats of each Node will be based on
//the player/enemy attack
// Create random Nodes for every space in the map that
// hasn't been filled yet. The stats of each Node will be based on
// the player/enemy attack
var averageAttack = (this.playerAtk + this.enemyAtk) / 2;
for (var x = 0; x < 8; ++x) {
for (var y = 0; y < 8; ++y) {
@@ -677,7 +683,7 @@ HackingMission.prototype.createMap = function() {
var node, type = getRandomInt(0, 2);
var randMult = addOffset(0.85 + (this.difficulty / 2), 15);
switch (type) {
case 0: //Spam
case 0: // Spam
var stats = {
atk: 0,
def: averageAttack * 1.1 + getRandomInt(15, 45),
@@ -685,7 +691,7 @@ HackingMission.prototype.createMap = function() {
}
node = new Node(NodeTypes.Spam, stats);
break;
case 1: //Transfer
case 1: // Transfer
var stats = {
atk: 0,
def: averageAttack * 1.1 + getRandomInt(15, 45),
@@ -693,7 +699,7 @@ HackingMission.prototype.createMap = function() {
}
node = new Node(NodeTypes.Transfer, stats);
break;
case 2: //Shield
case 2: // Shield
default:
var stats = {
atk: 0,
@@ -710,14 +716,14 @@ HackingMission.prototype.createMap = function() {
}
}
//Create DOM elements in order
// Create DOM elements in order
for (var r = 0; r < 8; ++r) {
for (var c = 0; c < 8; ++c) {
this.createNodeDomElement(this.map[r][c]);
}
}
//Configure all Player CPUS
// Configure all Player CPUS
for (var i = 0; i < this.playerCores.length; ++i) {
console.log("Configuring Player Node: " + this.playerCores[i].el.id);
this.configurePlayerNodeElement(this.playerCores[i].el);
@@ -728,12 +734,12 @@ HackingMission.prototype.createNodeDomElement = function(nodeObj) {
var nodeDiv = document.createElement("a"), txtEl = document.createElement('p');
nodeObj.el = nodeDiv;
//Set the node element's id based on its coordinates
// Set the node element's id based on its coordinates
var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
nodeDiv.setAttribute("id", id);
txtEl.setAttribute("id", id + "-txt");
//Set node classes for owner
// Set node classes for owner
nodeDiv.classList.add("hack-mission-node");
if (nodeObj.plyrCtrl) {
nodeDiv.classList.add("hack-mission-player-node");
@@ -741,7 +747,7 @@ HackingMission.prototype.createNodeDomElement = function(nodeObj) {
nodeDiv.classList.add("hack-mission-enemy-node");
}
//Set node classes based on type
// Set node classes based on type
var txt;
switch (nodeObj.type) {
case NodeTypes.Core:
@@ -794,7 +800,7 @@ HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
var id = "hacking-mission-node-" + nodeObj.pos[0] + "-" + nodeObj.pos[1];
var nodeDiv = document.getElementById(id), txtEl = document.getElementById(id + "-txt");
//Set node classes based on type
// Set node classes based on type
var txt;
switch (nodeObj.type) {
case NodeTypes.Core:
@@ -832,9 +838,11 @@ HackingMission.prototype.updateNodeDomElement = function(nodeObj) {
txtEl.innerHTML = txt;
}
//Gets a Node DOM element's corresponding Node object using its
//element id. Function accepts either the DOM element object or the ID as
//an argument
/**
* Gets a Node DOM element's corresponding Node object using its
* element id. Function accepts either the DOM element object or the ID as
* an argument
*/
HackingMission.prototype.getNodeFromElement = function(el) {
var id;
if (isString(el)) {
@@ -897,14 +905,16 @@ function clearAllSelectedNodes(hackMissionInst) {
}
}
//Configures a DOM element representing a player-owned node to
//be selectable and actionable
//Note: Does NOT change its css class. This is handled by Node.setControlledBy...
/**
* Configures a DOM element representing a player-owned node to
* be selectable and actionable.
* Note: Does NOT change its css class. This is handled by Node.setControlledBy...
*/
HackingMission.prototype.configurePlayerNodeElement = function(el) {
var nodeObj = this.getNodeFromElement(el);
if (nodeObj == null) {console.log("Error getting Node object");}
//Add event listener
// Add event listener
var self = this;
function selectNodeWrapper() {
selectNode(self, el);
@@ -922,10 +932,12 @@ HackingMission.prototype.configurePlayerNodeElement = function(el) {
}
}
//Configures a DOM element representing an enemy-node by removing
//any event listeners
/**
* Configures a DOM element representing an enemy-node by removing
* any event listeners
*/
HackingMission.prototype.configureEnemyNodeElement = function(el) {
//Deselect node if it was the selected node
// Deselect node if it was the selected node
var nodeObj = this.getNodeFromElement(el);
for (var i = 0; i < this.selectedNode.length; ++i) {
if (this.selectedNode[i] == nodeObj) {
@@ -936,8 +948,10 @@ HackingMission.prototype.configureEnemyNodeElement = function(el) {
}
}
//Returns bool indicating whether a node is reachable by player
//by checking if any of the adjacent nodes are owned by the player
/**
* Returns bool indicating whether a node is reachable by player
* by checking if any of the adjacent nodes are owned by the player
*/
HackingMission.prototype.nodeReachable = function(node) {
var x = node.pos[0], y = node.pos[1];
if (x > 0 && this.map[x-1][y].plyrCtrl) {return true;}
@@ -980,7 +994,7 @@ HackingMission.prototype.initJsPlumb = function() {
this.jsplumbinstance = instance;
//All player cores are sources
// All player cores are sources
for (var i = 0; i < this.playerCores.length; ++i) {
instance.makeSource(this.playerCores[i].el, {
deleteEndpointsOnEmpty:true,
@@ -990,7 +1004,7 @@ HackingMission.prototype.initJsPlumb = function() {
});
}
//Everything else is a target
// Everything else is a target
for (var i = 0; i < this.enemyCores.length; ++i) {
instance.makeTarget(this.enemyCores[i].el, {
maxConnections:-1,
@@ -1020,7 +1034,7 @@ HackingMission.prototype.initJsPlumb = function() {
});
}
//Clicking a connection drops it
// Clicking a connection drops it
instance.bind("click", (conn, originalEvent) => {
// Cannot drop enemy's connections
const sourceNode = this.getNodeFromElement(conn.source);
@@ -1030,15 +1044,15 @@ HackingMission.prototype.initJsPlumb = function() {
endpoints[0].detachFrom(endpoints[1]);
});
//Connection events
// Connection events
instance.bind("connection", (info) => {
var targetNode = this.getNodeFromElement(info.target);
//Do not detach for enemy nodes
// Do not detach for enemy nodes
var thisNode = this.getNodeFromElement(info.source);
if (thisNode.enmyCtrl) {return;}
//If the node is not reachable, drop the connection
// If the node is not reachable, drop the connection
if (!this.nodeReachable(targetNode)) {
info.sourceEndpoint.detachFrom(info.targetEndpoint);
return;
@@ -1050,7 +1064,7 @@ HackingMission.prototype.initJsPlumb = function() {
++targetNode.targetedCount;
});
//Detach Connection events
// Detach Connection events
instance.bind("connectionDetached", (info, originalEvent)=>{
var sourceNode = this.getNodeFromElement(info.source);
sourceNode.conn = null;
@@ -1060,7 +1074,7 @@ HackingMission.prototype.initJsPlumb = function() {
}
//Drops all connections where the specified node is the source
// Drops all connections where the specified node is the source
HackingMission.prototype.dropAllConnectionsFromNode = function(node) {
var allConns = this.jsplumbinstance.getAllConnections();
for (var i = allConns.length-1; i >= 0; --i) {
@@ -1070,7 +1084,7 @@ HackingMission.prototype.dropAllConnectionsFromNode = function(node) {
}
}
//Drops all connections where the specified node is the target
// Drops all connections where the specified node is the target
HackingMission.prototype.dropAllConnectionsToNode = function(node) {
var allConns = this.jsplumbinstance.getAllConnections();
for (var i = allConns.length-1; i >= 0; --i) {
@@ -1085,10 +1099,10 @@ var storedCycles = 0;
HackingMission.prototype.process = function(numCycles=1) {
if (!this.started) {return;}
storedCycles += numCycles;
if (storedCycles < 2) {return;} //Only process every 3 cycles minimum
if (storedCycles < 2) {return;} // Only process every 3 cycles minimum
var res = false;
//Process actions of all player nodes
// Process actions of all player nodes
this.playerCores.forEach((node)=>{
res |= this.processNode(node, storedCycles);
});
@@ -1101,7 +1115,7 @@ HackingMission.prototype.process = function(numCycles=1) {
}
});
//Process actions of all enemy nodes
// Process actions of all enemy nodes
this.enemyCores.forEach((node)=>{
this.enemyAISelectAction(node);
res |= this.processNode(node, storedCycles);
@@ -1116,7 +1130,7 @@ HackingMission.prototype.process = function(numCycles=1) {
}
});
//The hp of enemy databases increases slowly
// The hp of enemy databases increases slowly
this.enemyDatabases.forEach((node)=>{
node.maxhp += (0.1 * storedCycles);
node.hp += (0.1 * storedCycles);
@@ -1127,19 +1141,19 @@ HackingMission.prototype.process = function(numCycles=1) {
this.calculateDefenses();
}
//Win if all enemy databases are conquered
// Win if all enemy databases are conquered
if (this.enemyDatabases.length === 0) {
this.finishMission(true);
return;
}
//Lose if all your cores are gone
// Lose if all your cores are gone
if (this.playerCores.length === 0) {
this.finishMission(false);
return;
}
//Defense/hp of misc nodes increases slowly over time
// Defense/hp of misc nodes increases slowly over time
this.miscNodes.forEach((node)=>{
node.def += (0.1 * storedCycles);
node.maxhp += (0.05 * storedCycles);
@@ -1148,7 +1162,7 @@ HackingMission.prototype.process = function(numCycles=1) {
this.updateNodeDomElement(node);
});
//Update timer and check if player lost
// Update timer and check if player lost
this.time -= (storedCycles * Engine._idleSpeed);
if (this.time <= 0) {
this.finishMission(false);
@@ -1159,7 +1173,7 @@ HackingMission.prototype.process = function(numCycles=1) {
storedCycles = 0;
}
//Returns a bool representing whether defenses need to be re-calculated
// Returns a bool representing whether defenses need to be re-calculated
HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
if (nodeObj.action == null) {
return;
@@ -1174,21 +1188,21 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
}
if (targetNode == null) {
//Player is in the middle of dragging the connection,
//so the target node is null. Do nothing here
// Player is in the middle of dragging the connection,
// so the target node is null. Do nothing here
} else if (targetNode.plyrCtrl) {
def = this.playerDef;
atk = this.enemyAtk;
} else if (targetNode.enmyCtrl) {
def = this.enemyDef;
atk = this.playerAtk;
} else { //Misc Node
} else { // Misc Node
def = targetNode.def;
nodeObj.plyrCtrl ? atk = this.playerAtk : atk = this.enemyAtk;
}
}
//Calculations are per second, so divide everything by 5
// Calculations are per second, so divide everything by 5
var calcStats = false, plyr = nodeObj.plyrCtrl;
var enmyHacking = this.difficulty * CONSTANTS.HackingMissionDifficultyToHacking;
switch(nodeObj.action) {
@@ -1229,13 +1243,13 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
break;
}
//Stats can't go below 0
// Stats can't go below 0
if (nodeObj.atk < 0) {nodeObj.atk = 0;}
if (nodeObj.def < 0) {nodeObj.def = 0;}
if (targetNode && targetNode.atk < 0) {targetNode.atk = 0;}
if (targetNode && targetNode.def < 0) {targetNode.def = 0;}
//Conquering a node
// Conquering a node
if (targetNode && targetNode.hp <= 0) {
var conqueredByPlayer = nodeObj.plyrCtrl;
targetNode.hp = targetNode.maxhp;
@@ -1245,18 +1259,18 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
targetNode.deselect(this.actionButtons);
}
//The conquered node has its stats reduced
// The conquered node has its stats reduced
targetNode.atk /= 2;
targetNode.def /= 3.5;
//Flag for whether the target node was a misc node
// Flag for whether the target node was a misc node
var isMiscNode = !targetNode.plyrCtrl && !targetNode.enmyCtrl;
//Remove all connections from Node
// Remove all connections from Node
this.dropAllConnectionsToNode(targetNode);
this.dropAllConnectionsFromNode(targetNode);
//Changes the css class and turn the node into a JsPlumb Source/Target
// Changes the css class and turn the node into a JsPlumb Source/Target
if (conqueredByPlayer) {
targetNode.setControlledByPlayer();
this.jsplumbinstance.unmakeTarget(targetNode.el);
@@ -1268,7 +1282,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
});
} else {
targetNode.setControlledByEnemy();
nodeObj.conn = null; //Clear connection
nodeObj.conn = null; // Clear connection
this.jsplumbinstance.unmakeSource(targetNode.el);
this.jsplumbinstance.makeTarget(targetNode.el, {
maxConnections:-1,
@@ -1279,7 +1293,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
calcStats = true;
//Helper function to swap nodes between the respective enemyNodes/playerNodes arrays
// Helper function to swap nodes between the respective enemyNodes/playerNodes arrays
function swapNodes(orig, dest, targetNode) {
for (var i = 0; i < orig.length; ++i) {
if (orig[i] == targetNode) {
@@ -1319,7 +1333,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
case NodeTypes.Spam:
if (conqueredByPlayer) {
swapNodes(isMiscNode ? this.miscNodes : this.enemyNodes, this.playerNodes, targetNode);
//Conquering spam node increases time limit
// Conquering spam node increases time limit
this.time += CONSTANTS.HackingMissionSpamTimeIncrease;
} else {
swapNodes(isMiscNode ? this.miscNodes : this.playerNodes, this.enemyNodes, targetNode);
@@ -1327,7 +1341,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
break;
case NodeTypes.Transfer:
//Conquering a Transfer node increases the attack of all cores by some percentages
// Conquering a Transfer node increases the attack of all cores by some percentages
if (conqueredByPlayer) {
swapNodes(isMiscNode ? this.miscNodes : this.enemyNodes, this.playerNodes, targetNode);
this.playerCores.forEach(function(node) {
@@ -1353,7 +1367,7 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
break;
}
//If a misc node was conquered, the defense for all misc nodes increases by some fixed amount
// If a misc node was conquered, the defense for all misc nodes increases by some fixed amount
if (isMiscNode) { //&& conqueredByPlayer) {
this.miscNodes.forEach((node)=>{
if (node.targetedCount === 0) {
@@ -1363,23 +1377,25 @@ HackingMission.prototype.processNode = function(nodeObj, numCycles=1) {
}
}
//Update node DOMs
// Update node DOMs
this.updateNodeDomElement(nodeObj);
if (targetNode) {this.updateNodeDomElement(targetNode);}
return calcStats;
}
//Enemy "AI" for CPU Core and Transfer Nodes
// Enemy "AI" for CPU Core and Transfer Nodes
HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
if (nodeObj == null) {return;}
switch(nodeObj.type) {
case NodeTypes.Core:
//Select a single RANDOM target from miscNodes and player's Nodes
//If it is reachable, it will target it. If not, no target will
//be selected for now, and the next time process() gets called this will repeat
/**
* Select a single RANDOM target from miscNodes and player's Nodes
* If it is reachable, it will target it. If not, no target will
* be selected for now, and the next time process() gets called this will repeat
*/
if (nodeObj.conn == null) {
if (this.miscNodes.length === 0) {
//Randomly pick a player node and attack it if its reachable
// Randomly pick a player node and attack it if its reachable
var rand = getRandomInt(0, this.playerNodes.length-1);
var node;
if (this.playerNodes.length === 0) {
@@ -1388,23 +1404,23 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
node = this.playerNodes[rand];
}
if (this.nodeReachableByEnemy(node)) {
//Create connection
// Create connection
nodeObj.conn = this.jsplumbinstance.connect({
source:nodeObj.el,
target:node.el
});
++node.targetedCount;
} else {
//Randomly pick a player core and attack it if its reachable
// Randomly pick a player core and attack it if its reachable
rand = getRandomInt(0, this.playerCores.length-1);
if (this.playerCores.length === 0) {
return; //No Misc Nodes, no player Nodes, no Player cores. Player lost
return; // No Misc Nodes, no player Nodes, no Player cores. Player lost
} else {
node = this.playerCores[rand];
}
if (this.nodeReachableByEnemy(node)) {
//Create connection
// Create connection
nodeObj.conn = this.jsplumbinstance.connect({
source:nodeObj.el,
target:node.el
@@ -1413,7 +1429,7 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
}
}
} else {
//Randomly pick a misc node and attack it if its reachable
// Randomly pick a misc node and attack it if its reachable
var rand = getRandomInt(0, this.miscNodes.length-1);
var node = this.miscNodes[rand];
if (this.nodeReachableByEnemy(node)) {
@@ -1425,10 +1441,10 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
}
}
//If no connection was made, set the Core to Fortify
// If no connection was made, set the Core to Fortify
nodeObj.action = NodeActions.Fortify;
} else {
//If this node has a selected target
// If this node has a selected target
var targetNode;
if (nodeObj.conn.target) {
targetNode = this.getNodeFromElement(nodeObj.conn.target);
@@ -1453,7 +1469,7 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
}
break;
case NodeTypes.Transfer:
//Switch between fortifying and overflowing as necessary
// Switch between fortifying and overflowing as necessary
if (nodeObj.def < 125) {
nodeObj.action = NodeActions.Fortify;
} else {
@@ -1469,11 +1485,11 @@ HackingMission.prototype.enemyAISelectAction = function(nodeObj) {
}
}
var hackEffWeightSelf = 130; //Weight for Node actions on self
var hackEffWeightTarget = 25; //Weight for Node Actions against Target
var hackEffWeightAttack = 80; //Weight for Attack action
var hackEffWeightSelf = 130; // Weight for Node actions on self
var hackEffWeightTarget = 25; // Weight for Node Actions against Target
var hackEffWeightAttack = 80; // Weight for Attack action
//Returns damage per cycle based on stats
// Returns damage per cycle based on stats
HackingMission.prototype.calculateAttackDamage = function(atk, def, hacking = 0) {
return Math.max(0.55 * (atk + (hacking / hackEffWeightAttack) - def), 1);
}
@@ -1494,11 +1510,11 @@ HackingMission.prototype.calculateOverflowEffect = function(hacking=0) {
return 0.95 * hacking / hackEffWeightSelf;
}
//Updates timer display
// Updates timer display
HackingMission.prototype.updateTimer = function() {
var timer = document.getElementById("hacking-mission-timer");
//Convert time remaining to a string of the form mm:ss
// Convert time remaining to a string of the form mm:ss
var seconds = Math.round(this.time / 1000);
var minutes = Math.trunc(seconds / 60);
seconds %= 60;
@@ -1506,7 +1522,7 @@ HackingMission.prototype.updateTimer = function() {
timer.innerText = "Time left: " + str;
}
//The 'win' argument is a bool for whether or not the player won
// The 'win' argument is a bool for whether or not the player won
HackingMission.prototype.finishMission = function(win) {
inMission = false;
currMission = null;
@@ -1525,13 +1541,13 @@ HackingMission.prototype.finishMission = function(win) {
dialogBoxCreate("Mission lost/forfeited! You did not gain any faction reputation.");
}
//Clear mission container
// Clear mission container
var container = document.getElementById("mission-container");
while(container.firstChild) {
container.removeChild(container.firstChild);
}
//Return to Faction page
// Return to Faction page
document.getElementById("mainmenu-container").style.visibility = "visible";
document.getElementById("character-overview-wrapper").style.visibility = "visible";
Engine.loadFactionContent();

View File

@@ -1,6 +1,6 @@
import {Player} from "./Player";
import {Bladeburner} from "./Bladeburner";
import {makeRuntimeRejectMsg} from "./NetscriptEvaluator";
import { Player } from "./Player";
import { Bladeburner } from "./Bladeburner";
import { makeRuntimeRejectMsg } from "./NetscriptEvaluator";
function unknownBladeburnerActionErrorMessage(functionName, actionType, actionName) {
return `ERROR: bladeburner.${functionName}() failed due to an invalid action specified. ` +

View File

@@ -1,5 +1,5 @@
import {NetscriptFunctions} from "./NetscriptFunctions";
import {NetscriptPort} from "./NetscriptPort";
import { NetscriptFunctions } from "./NetscriptFunctions";
import { NetscriptPort } from "./NetscriptPort";
/* Environment
* NetScript program environment

View File

@@ -1,21 +1,21 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { CONSTANTS } from "./Constants";
import { Player } from "./Player";
import { Environment } from "./NetscriptEnvironment";
import { WorkerScript, addWorkerScript} from "./NetscriptWorker";
import { Server } from "./Server/Server";
import { getServer } from "./Server/ServerHelpers";
import { Settings } from "./Settings/Settings";
import { RunningScript } from "./Script/RunningScript";
import { Script } from "./Script/Script";
import { findRunningScript } from "./Script/ScriptHelpers";
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { CONSTANTS } from "./Constants";
import { Player } from "./Player";
import { Environment } from "./NetscriptEnvironment";
import { WorkerScript, addWorkerScript } from "./NetscriptWorker";
import { Server } from "./Server/Server";
import { getServer } from "./Server/ServerHelpers";
import { Settings } from "./Settings/Settings";
import { RunningScript } from "./Script/RunningScript";
import { Script } from "./Script/Script";
import { findRunningScript } from "./Script/ScriptHelpers";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { parse, Node } from "../utils/acorn";
import {parse, Node} from "../utils/acorn";
import {arrayToString} from "../utils/helpers/arrayToString";
import {isValidIPAddress} from "../utils/helpers/isValidIPAddress";
import {isString} from "../utils/helpers/isString";
import { arrayToString } from "../utils/helpers/arrayToString";
import { isValidIPAddress } from "../utils/helpers/isValidIPAddress";
import { isString } from "../utils/helpers/isString";
export function evaluateImport(exp, workerScript, checkingRam=false) {
//When its checking RAM, it exports an array of nodes for each imported function

View File

@@ -1,117 +1,149 @@
var sprintf = require('sprintf-js').sprintf,
vsprintf = require('sprintf-js').vsprintf
const sprintf = require("sprintf-js").sprintf;
const vsprintf = require("sprintf-js").vsprintf;
import {updateActiveScriptsItems} from "./ActiveScriptsUI";
import { Augmentation } from "./Augmentation/Augmentation";
import { Augmentations } from "./Augmentation/Augmentations";
import { augmentationExists,
installAugmentations } from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { findCrime } from "./Crime/CrimeHelpers";
import {Bladeburner} from "./Bladeburner";
import {Company} from "./Company/Company";
import {Companies, companyExists} from "./Company/Companies";
import {CompanyPosition} from "./Company/CompanyPosition";
import {CompanyPositions} from "./Company/CompanyPositions";
import {CONSTANTS} from "./Constants";
import { DarkWebItems } from "./DarkWeb/DarkWebItems";
import {calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime} from "./Hacking";
import {AllGangs} from "./Gang";
import { Faction } from "./Faction/Faction";
import { Factions,
factionExists } from "./Faction/Factions";
import { joinFaction,
purchaseAugmentation } from "./Faction/FactionHelpers";
import { FactionWorkType } from "./Faction/FactionWorkTypeEnum";
import { netscriptCanGrow,
netscriptCanHack,
netscriptCanWeaken } from "./Hacking/netscriptCanHack";
import { updateActiveScriptsItems } from "./ActiveScriptsUI";
import { Augmentation } from "./Augmentation/Augmentation";
import { Augmentations } from "./Augmentation/Augmentations";
import {
augmentationExists,
installAugmentations
} from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { findCrime } from "./Crime/CrimeHelpers";
import { Bladeburner } from "./Bladeburner";
import { Company } from "./Company/Company";
import { Companies, companyExists } from "./Company/Companies";
import { CompanyPosition } from "./Company/CompanyPosition";
import { CompanyPositions } from "./Company/CompanyPositions";
import { CONSTANTS } from "./Constants";
import { DarkWebItems } from "./DarkWeb/DarkWebItems";
import {
calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime
} from "./Hacking";
import { AllGangs } from "./Gang";
import { Faction } from "./Faction/Faction";
import { Factions, factionExists } from "./Faction/Factions";
import { joinFaction, purchaseAugmentation } from "./Faction/FactionHelpers";
import { FactionWorkType } from "./Faction/FactionWorkTypeEnum";
import {
netscriptCanGrow,
netscriptCanHack,
netscriptCanWeaken
} from "./Hacking/netscriptCanHack";
import { getCostOfNextHacknetNode,
getCostOfNextHacknetServer,
purchaseHacknet,
hasHacknetServers,
purchaseHashUpgrade } from "./Hacknet/HacknetHelpers";
import { CityName } from "./Locations/data/CityNames";
import { LocationName } from "./Locations/data/LocationNames";
import {
getCostOfNextHacknetNode,
getCostOfNextHacknetServer,
purchaseHacknet,
hasHacknetServers,
purchaseHashUpgrade
} from "./Hacknet/HacknetHelpers";
import { CityName } from "./Locations/data/CityNames";
import { LocationName } from "./Locations/data/LocationNames";
import { HacknetServer } from "./Hacknet/HacknetServer";
import { Message } from "./Message/Message";
import { Messages } from "./Message/MessageHelpers";
import {inMission} from "./Missions";
import {Player} from "./Player";
import { Programs } from "./Programs/Programs";
import { Script } from "./Script/Script";
import { findRunningScript } from "./Script/ScriptHelpers";
import { isScriptFilename } from "./Script/ScriptHelpersTS";
import { AllServers,
AddToAllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { GetServerByHostname,
getServer,
getServerOnNetwork,
numCycleForGrowth,
processSingleServerGrowth } from "./Server/ServerHelpers";
import { getPurchaseServerCost,
getPurchaseServerLimit,
getPurchaseServerMaxRam } from "./Server/ServerPurchases";
import {Settings} from "./Settings/Settings";
import {SpecialServerIps} from "./Server/SpecialServerIps";
import {Stock} from "./StockMarket/Stock";
import {StockMarket, StockSymbols, SymbolToStockMap,
initStockMarket, initSymbolToStockMap, buyStock,
sellStock, updateStockPlayerPosition,
shortStock, sellShort, OrderTypes,
PositionTypes, placeOrder, cancelOrder} from "./StockMarket/StockMarket";
import { getStockmarket4SDataCost,
getStockMarket4STixApiCost } from "./StockMarket/StockMarketCosts";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags"
import {TextFile, getTextFile, createTextFile} from "./TextFile";
import { HacknetServer } from "./Hacknet/HacknetServer";
import { Message } from "./Message/Message";
import { Messages } from "./Message/MessageHelpers";
import { inMission } from "./Missions";
import { Player } from "./Player";
import { Programs } from "./Programs/Programs";
import { Script } from "./Script/Script";
import { findRunningScript } from "./Script/ScriptHelpers";
import { isScriptFilename } from "./Script/ScriptHelpersTS";
import { AllServers, AddToAllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import {
GetServerByHostname,
getServer,
getServerOnNetwork,
numCycleForGrowth,
processSingleServerGrowth
} from "./Server/ServerHelpers";
import {
getPurchaseServerCost,
getPurchaseServerLimit,
getPurchaseServerMaxRam
} from "./Server/ServerPurchases";
import { Settings } from "./Settings/Settings";
import { SpecialServerIps } from "./Server/SpecialServerIps";
import { Stock } from "./StockMarket/Stock";
import {
StockMarket,
StockSymbols,
SymbolToStockMap,
initStockMarket,
initSymbolToStockMap,
buyStock,
sellStock,
updateStockPlayerPosition,
shortStock,
sellShort,
OrderTypes,
PositionTypes,
placeOrder,
cancelOrder
} from "./StockMarket/StockMarket";
import {
getStockmarket4SDataCost,
getStockMarket4STixApiCost
} from "./StockMarket/StockMarketCosts";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { TextFile, getTextFile, createTextFile } from "./TextFile";
import {unknownBladeburnerActionErrorMessage,
unknownBladeburnerExceptionMessage,
checkBladeburnerAccess} from "./NetscriptBladeburner";
import * as nsGang from "./NetscriptGang";
import {WorkerScript, workerScripts,
killWorkerScript, NetscriptPorts} from "./NetscriptWorker";
import {makeRuntimeRejectMsg, netscriptDelay,
runScriptFromScript} from "./NetscriptEvaluator";
import {NetscriptPort} from "./NetscriptPort";
import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { findSleevePurchasableAugs } from "./PersonObjects/Sleeve/Sleeve";
import {
unknownBladeburnerActionErrorMessage,
unknownBladeburnerExceptionMessage,
checkBladeburnerAccess
} from "./NetscriptBladeburner";
import * as nsGang from "./NetscriptGang";
import {
WorkerScript,
workerScripts,
killWorkerScript,
NetscriptPorts
} from "./NetscriptWorker";
import {
makeRuntimeRejectMsg,
netscriptDelay,
runScriptFromScript
} from "./NetscriptEvaluator";
import { NetscriptPort } from "./NetscriptPort";
import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { findSleevePurchasableAugs } from "./PersonObjects/Sleeve/Sleeve";
import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {post} from "./ui/postToTerminal";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { is2DArray } from "./utils/helpers/is2DArray";
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
import { post } from "./ui/postToTerminal";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { is2DArray } from "./utils/helpers/is2DArray";
import {dialogBoxCreate} from "../utils/DialogBox";
import {isPowerOfTwo} from "../utils/helpers/isPowerOfTwo";
import {arrayToString} from "../utils/helpers/arrayToString";
import {createRandomIp} from "../utils/IPAddress";
import {formatNumber, isHTML} from "../utils/StringHelperFunctions";
import {isString} from "../utils/helpers/isString";
import { dialogBoxCreate } from "../utils/DialogBox";
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo";
import { arrayToString } from "../utils/helpers/arrayToString";
import { createRandomIp } from "../utils/IPAddress";
import { formatNumber, isHTML } from "../utils/StringHelperFunctions";
import { isString } from "../utils/helpers/isString";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
var hasCorporationSF = false, //Source-File 3
hasSingularitySF = false, //Source-File 4
hasAISF = false, //Source-File 5
hasBladeburnerSF = false, //Source-File 6
hasBladeburner2079SF = false, //Source-File 7
hasWallStreetSF = false, //Source-File 8
hasBn11SF = false; //Source-File 11
let hasCorporationSF = false; // Source-File 3
let hasSingularitySF = false; // Source-File 4
let hasAISF = false; // Source-File 5
let hasBladeburnerSF = false; // Source-File 6
let hasBladeburner2079SF = false; // Source-File 7
let hasWallStreetSF = false; // Source-File 8
let hasBn11SF = false; // Source-File 11
var singularitySFLvl=1, wallStreetSFLvl=1;
let singularitySFLvl = 1;
let wallStreetSFLvl = 1;
var possibleLogs = {
ALL: true,
@@ -184,7 +216,7 @@ var possibleLogs = {
setTerritoryWarfare: true,
}
//Used to check and set flags for every Source File, despite the name of the function
// Used to check and set flags for every Source File, despite the name of the function
function initSingularitySFFlags() {
for (var i = 0; i < Player.sourceFiles.length; ++i) {
if (Player.sourceFiles[i].n === 3) {hasCorporationSF = true;}
@@ -423,10 +455,10 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "hack() error. Invalid IP or hostname passed in: " + ip + ". Stopping...");
}
//Calculate the hacking time
var hackingTime = calculateHackingTime(server); //This is in seconds
// Calculate the hacking time
var hackingTime = calculateHackingTime(server); // This is in seconds
//No root access or skill level too low
// No root access or skill level too low
const canHack = netscriptCanHack(server, Player);
if (!canHack.res) {
workerScript.scriptRef.log(`ERROR: ${canHack.msg}`);
@@ -443,18 +475,17 @@ function NetscriptFunctions(workerScript) {
var rand = Math.random();
var expGainedOnSuccess = calculateHackingExpGain(server) * threads;
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { //Success!
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
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value
maxThreadNeeded = 1e6;
}
let moneyGained = Math.floor(server.moneyAvailable * percentHacked) * threads;
//Over-the-top safety checks
// Over-the-top safety checks
if (moneyGained <= 0) {
moneyGained = 0;
expGainedOnSuccess = expGainedOnFailure;
@@ -476,7 +507,7 @@ function NetscriptFunctions(workerScript) {
server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded));
return Promise.resolve(moneyGained);
} else {
//Player only gains 25% exp for failure?
// Player only gains 25% exp for failure?
Player.gainHackingExp(expGainedOnFailure);
workerScript.scriptRef.onlineExpGained += expGainedOnFailure;
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.hack == null) {
@@ -554,7 +585,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Cannot grow(). Invalid IP or hostname passed in: " + ip);
}
//No root access or skill level too low
// No root access or skill level too low
const canHack = netscriptCanGrow(server);
if (!canHack.res) {
workerScript.scriptRef.log(`ERROR: ${canHack.msg}`);
@@ -568,7 +599,7 @@ function NetscriptFunctions(workerScript) {
return netscriptDelay(growTime * 1000, workerScript).then(function() {
if (workerScript.env.stopFlag) {return Promise.reject(workerScript);}
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
server.moneyAvailable += (1 * threads); //It can be grown even if it has no money
server.moneyAvailable += (1 * threads); // It can be grown even if it has no money
var growthPercentage = processSingleServerGrowth(server, 450 * threads, Player);
const moneyAfter = server.moneyAvailable;
workerScript.scriptRef.recordGrow(server.ip, threads);
@@ -616,7 +647,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Cannot weaken(). Invalid IP or hostname passed in: " + ip);
}
//No root access or skill level too low
// No root access or skill level too low
const canHack = netscriptCanWeaken(server);
if (!canHack.res) {
workerScript.scriptRef.log(`ERROR: ${canHack.msg}`);
@@ -1043,7 +1074,7 @@ function NetscriptFunctions(workerScript) {
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
// Recursively call scp on all elements of array
var res = false;
scriptname.forEach(function(script) {
if (NetscriptFunctions(workerScript).scp(script, ip1, ip2)) {
@@ -1089,7 +1120,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: scp() call has incorrect number of arguments. Takes 2 or 3 arguments");
}
//Scp for lit files
// Scp for lit files
if (scriptname.endsWith(".lit")) {
var found = false;
for (var i = 0; i < currServ.messages.length; ++i) {
@@ -1109,7 +1140,7 @@ function NetscriptFunctions(workerScript) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.scp == null) {
workerScript.scriptRef.log(scriptname + " copied over to " + destServer.hostname);
}
return true; //Already exists
return true; // Already exists
}
}
destServer.messages.push(scriptname);
@@ -1119,7 +1150,7 @@ function NetscriptFunctions(workerScript) {
return true;
}
//Scp for text files
// Scp for text files
if (scriptname.endsWith(".txt")) {
var found = false, txtFile;
for (var i = 0; i < currServ.textFiles.length; ++i) {
@@ -1137,7 +1168,7 @@ function NetscriptFunctions(workerScript) {
for (var i = 0; i < destServer.textFiles.length; ++i) {
if (destServer.textFiles[i].fn === scriptname) {
//Overwrite
// Overwrite
destServer.textFiles[i].text = txtFile.text;
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.scp == null) {
workerScript.scriptRef.log(scriptname + " copied over to " + destServer.hostname);
@@ -1153,7 +1184,7 @@ function NetscriptFunctions(workerScript) {
return true;
}
//Scp for script files
// Scp for script files
var sourceScript = null;
for (var i = 0; i < currServ.scripts.length; ++i) {
if (scriptname == currServ.scripts[i].filename) {
@@ -1166,7 +1197,7 @@ function NetscriptFunctions(workerScript) {
return false;
}
//Overwrite script if it already exists
// Overwrite script if it already exists
for (var i = 0; i < destServer.scripts.length; ++i) {
if (scriptname == destServer.scripts[i].filename) {
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.scp == null) {
@@ -1181,7 +1212,7 @@ function NetscriptFunctions(workerScript) {
}
}
//Create new script if it does not already exist
// Create new script if it does not already exist
var newScript = new Script();
newScript.filename = scriptname;
newScript.code = sourceScript.code;
@@ -1207,7 +1238,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "ls() failed. Invalid IP or hostname passed in: " + ip);
}
//Get the grep filter, if one exists
// Get the grep filter, if one exists
var filter = false;
if (arguments.length >= 2) {
filter = grep.toString();
@@ -1271,7 +1302,7 @@ function NetscriptFunctions(workerScript) {
}
}
//Sort the files alphabetically then print each
// Sort the files alphabetically then print each
allFiles.sort();
return allFiles;
},
@@ -1676,7 +1707,7 @@ function NetscriptFunctions(workerScript) {
var gains = stock.price * shares - CONSTANTS.StockMarketCommission;
Player.gainMoney(gains);
//Calculate net profit and add to script stats
// Calculate net profit and add to script stats
var netProfit = ((stock.price - stock.playerAvgPx) * shares) - CONSTANTS.StockMarketCommission;
if (isNaN(netProfit)) {netProfit = 0;}
workerScript.scriptRef.onlineMoneyMade += netProfit;
@@ -1876,7 +1907,7 @@ function NetscriptFunctions(workerScript) {
if (stock == null) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into getStockVolatility()");
}
return stock.mv / 100; //Convert from percentage to decimal
return stock.mv / 100; // Convert from percentage to decimal
},
getStockForecast : function(symbol) {
if (workerScript.checkingRam) {
@@ -1892,7 +1923,7 @@ function NetscriptFunctions(workerScript) {
}
var forecast = 50;
stock.b ? forecast += stock.otlkMag : forecast -= stock.otlkMag;
return forecast / 100; //Convert from percentage to decimal
return forecast / 100; // Convert from percentage to decimal
},
purchase4SMarketData : function() {
if (workerScript.checkingRam) {
@@ -2055,25 +2086,25 @@ function NetscriptFunctions(workerScript) {
var ip = server.ip;
//Can't delete server you're currently connected to
// Can't delete server you're currently connected to
if (server.isConnectedTo) {
workerScript.scriptRef.log("ERROR: deleteServer() failed because you are currently connected to the server you are trying to delete");
return false;
}
//A server cannot delete itself
// A server cannot delete itself
if (ip === workerScript.serverIp) {
workerScript.scriptRef.log("ERROR: Cannot call deleteServer() on self. deleteServer() failed");
return false;
}
//Delete all scripts running on server
// Delete all scripts running on server
if (server.runningScripts.length > 0) {
workerScript.scriptRef.log("ERROR: Cannot delete server " + server.hostname + " because it still has scripts running.");
return false;
}
//Delete from player's purchasedServers array
// Delete from player's purchasedServers array
var found = false;
for (var i = 0; i < Player.purchasedServers.length; ++i) {
if (ip == Player.purchasedServers[i]) {
@@ -2089,10 +2120,10 @@ function NetscriptFunctions(workerScript) {
return false;
}
//Delete from all servers
// Delete from all servers
delete AllServers[ip];
//Delete from home computer
// Delete from home computer
found = false;
var homeComputer = Player.getHomeComputer();
for (var i = 0; i < homeComputer.serversOnNetwork.length; ++i) {
@@ -2104,7 +2135,7 @@ function NetscriptFunctions(workerScript) {
return true;
}
}
//Wasn't found on home computer
// Wasn't found on home computer
workerScript.scriptRef.log("ERROR: Could not find server " + server.hostname +
"as a purchased server. This is likely a bug please contact game dev");
return false;
@@ -2133,8 +2164,8 @@ function NetscriptFunctions(workerScript) {
return updateStaticRam("write", CONSTANTS.ScriptReadWriteRamCost);
}
updateDynamicRam("write", CONSTANTS.ScriptReadWriteRamCost);
if (!isNaN(port)) { //Write to port
//Port 1-10
if (!isNaN(port)) { // Write to port
// Port 1-10
port = Math.round(port);
if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Trying to write to invalid port: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid.");
@@ -2144,17 +2175,17 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "Could not find port: " + port + ". This is a bug contact the game developer");
}
return port.write(data);
} else if (isString(port)) { //Write to script or text file
} else if (isString(port)) { // Write to script or text file
var fn = port;
var server = workerScript.getServer();
if (server == null) {
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in write(). This is a bug please contact game dev");
}
if (isScriptFilename(fn)) {
//Write to script
// Write to script
let script = workerScript.getScriptOnServer(fn);
if (script == null) {
//Create a new script
// Create a new script
script = new Script(fn, data, server.ip);
server.scripts.push(script);
return true;
@@ -2162,7 +2193,7 @@ function NetscriptFunctions(workerScript) {
mode === "w" ? script.code = data : script.code += data;
script.updateRamUsage();
} else {
//Write to text file
// Write to text file
let txtFile = getTextFile(fn, server);
if (txtFile == null) {
txtFile = createTextFile(fn, data, server);
@@ -2203,8 +2234,8 @@ function NetscriptFunctions(workerScript) {
return updateStaticRam("read", CONSTANTS.ScriptReadWriteRamCost);
}
updateDynamicRam("read", CONSTANTS.ScriptReadWriteRamCost);
if (!isNaN(port)) { //Read from port
//Port 1-10
if (!isNaN(port)) { // Read from port
// Port 1-10
port = Math.round(port);
if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Trying to read from invalid port: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid.");
@@ -2214,21 +2245,21 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Could not find port: " + port + ". This is a bug contact the game developer");
}
return port.read();
} else if (isString(port)) { //Read from script or text file
} else if (isString(port)) { // Read from script or text file
let fn = port;
let server = getServer(workerScript.serverIp);
if (server == null) {
throw makeRuntimeRejectMsg(workerScript, "Error getting Server for this script in read(). This is a bug please contact game dev");
}
if (isScriptFilename(fn)) {
//Read from script
// Read from script
let script = workerScript.getScriptOnServer(fn);
if (script == null) {
return "";
}
return script.code;
} else {
//Read from text file
// Read from text file
let txtFile = getTextFile(fn, server);
if (txtFile !== null) {
return txtFile.text;
@@ -2263,7 +2294,7 @@ function NetscriptFunctions(workerScript) {
return updateStaticRam("clear", CONSTANTS.ScriptReadWriteRamCost);
}
updateDynamicRam("clear", CONSTANTS.ScriptReadWriteRamCost);
if (!isNaN(port)) { //Clear port
if (!isNaN(port)) { // Clear port
port = Math.round(port);
if (port < 1 || port > CONSTANTS.NumNetscriptPorts) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Trying to clear invalid port: " + port + ". Only ports 1-" + CONSTANTS.NumNetscriptPorts + " are valid");
@@ -2273,7 +2304,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeRejectMsg(workerScript, "ERROR: Could not find port: " + port + ". This is a bug contact the game developer");
}
return port.clear();
} else if (isString(port)) { //Clear text file
} else if (isString(port)) { // Clear text file
var fn = port;
var server = getServer(workerScript.serverIp);
if (server == null) {
@@ -2391,7 +2422,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getHackTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "getHackTime() failed. Invalid IP or hostname passed in: " + ip);
}
return calculateHackingTime(server, hack, int); //Returns seconds
return calculateHackingTime(server, hack, int); // Returns seconds
},
getGrowTime : function(ip, hack, int) {
if (workerScript.checkingRam) {
@@ -2403,7 +2434,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getGrowTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "getGrowTime() failed. Invalid IP or hostname passed in: " + ip);
}
return calculateGrowTime(server, hack, int); //Returns seconds
return calculateGrowTime(server, hack, int); // Returns seconds
},
getWeakenTime : function(ip, hack, int) {
if (workerScript.checkingRam) {
@@ -2415,7 +2446,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("getWeakenTime() failed. Invalid IP or hostname passed in: " + ip);
throw makeRuntimeRejectMsg(workerScript, "getWeakenTime() failed. Invalid IP or hostname passed in: " + ip);
}
return calculateWeakenTime(server, hack, int); //Returns seconds
return calculateWeakenTime(server, hack, int); // Returns seconds
},
getScriptIncome : function(scriptname, ip) {
if (workerScript.checkingRam) {
@@ -2423,13 +2454,13 @@ function NetscriptFunctions(workerScript) {
}
updateDynamicRam("getScriptIncome", CONSTANTS.ScriptGetScriptRamCost);
if (arguments.length === 0) {
//Get total script income
// Get total script income
var res = [];
res.push(updateActiveScriptsItems());
res.push(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug/1000));
return res;
} else {
//Get income for a particular script
// Get income for a particular script
var server = getServer(ip);
if (server == null) {
workerScript.scriptRef.log("getScriptIncome() failed. Invalid IP or hostnamed passed in: " + ip);
@@ -2459,7 +2490,7 @@ function NetscriptFunctions(workerScript) {
}
return total;
} else {
//Get income for a particular script
// Get income for a particular script
var server = getServer(ip);
if (server == null) {
workerScript.scriptRef.log("getScriptExpGain() failed. Invalid IP or hostnamed passed in: " + ip);
@@ -3174,7 +3205,7 @@ function NetscriptFunctions(workerScript) {
workerScript.scriptRef.log("ERROR: Invalid job passed into applyToCompany: " + field + ". applyToCompany() failed");
return false;
}
//The Player object's applyForJob function can return string with special error messages
// The Player object's applyForJob function can return string with special error messages
if (isString(res)) {
workerScript.scriptRef.log(res);
return false;
@@ -3266,7 +3297,7 @@ function NetscriptFunctions(workerScript) {
return false;
}
}
//Make a copy of Player.factionInvitations
// Make a copy of Player.factionInvitations
return Player.factionInvitations.slice();
},
joinFaction : function(name) {
@@ -3295,7 +3326,7 @@ function NetscriptFunctions(workerScript) {
var fac = Factions[name];
joinFaction(fac);
//Update Faction Invitation list to account for joined + banned factions
// Update Faction Invitation list to account for joined + banned factions
for (var i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == name || Factions[Player.factionInvitations[i]].isBanned) {
Player.factionInvitations.splice(i, 1);
@@ -3351,7 +3382,7 @@ function NetscriptFunctions(workerScript) {
}
var fac = Factions[name];
//Arrays listing factions that allow each time of work
// Arrays listing factions that allow each time of work
var hackAvailable = ["Illuminati", "Daedalus", "The Covenant", "ECorp", "MegaCorp",
"Bachman & Associates", "Blade Industries", "NWO", "Clarke Incorporated",
"OmniTek Incorporated", "Four Sigma", "KuaiGong International",
@@ -4663,7 +4694,7 @@ function NetscriptFunctions(workerScript) {
updateDynamicRam("joinBladeburnerDivision", CONSTANTS.ScriptBladeburnerApiBaseRamCost);
if ((Player.bitNodeN === 7 || hasBladeburner2079SF)) {
if (Player.bladeburner instanceof Bladeburner) {
return true; //Already member
return true; // Already member
} else if (Player.strength >= 100 && Player.defense >= 100 &&
Player.dexterity >= 100 && Player.agility >= 100) {
Player.bladeburner = new Bladeburner({new:true});
@@ -5125,9 +5156,17 @@ function NetscriptFunctions(workerScript) {
return Player.sleeves[sleeveNumber].tryBuyAugmentation(Player, aug);
}
} // End sleeve
} //End return
} //End NetscriptFunction()
}, // End sleeve
heart: {
// Easter egg function
break : function() {
if (workerScript.checkingRam) { return 0; }
return Player.karma;
}
}
} // End return
} // End NetscriptFunction()
export {NetscriptFunctions, initSingularitySFFlags, hasSingularitySF, hasBn11SF,
hasWallStreetSF, wallStreetSFLvl, hasCorporationSF, hasAISF, hasBladeburnerSF};

View File

@@ -1,6 +1,6 @@
import {Player} from "./Player";
import {Gang} from "./Gang";
import {makeRuntimeRejectMsg} from "./NetscriptEvaluator";
import { Player } from "./Player";
import { Gang } from "./Gang";
import { makeRuntimeRejectMsg } from "./NetscriptEvaluator";
export function unknownGangApiExceptionMessage(functionName, err) {
return `gang.${functionName}() failed with exception: ` + err;

View File

@@ -1,4 +1,4 @@
import {makeRuntimeRejectMsg} from "./NetscriptEvaluator";
import { makeRuntimeRejectMsg } from "./NetscriptEvaluator";
// Makes a blob that contains the code of a given script.
export function makeScriptBlob(code) {

View File

@@ -1,4 +1,4 @@
import {Settings} from "./Settings/Settings";
import { Settings } from "./Settings/Settings";
function NetscriptPort() {
this.data = [];

View File

@@ -1,28 +1,34 @@
import {addActiveScriptsItem,
deleteActiveScriptsItem,
updateActiveScriptsItems} from "./ActiveScriptsUI";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import {Interpreter} from "./JSInterpreter";
import {Environment} from "./NetscriptEnvironment";
import {evaluate, isScriptErrorMessage,
makeRuntimeRejectMsg,
killNetscriptDelay} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions";
import {executeJSScript} from "./NetscriptJSEvaluator";
import {NetscriptPort} from "./NetscriptPort";
import { AllServers } from "./Server/AllServers";
import {Settings} from "./Settings/Settings";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import {
addActiveScriptsItem,
deleteActiveScriptsItem,
updateActiveScriptsItems
} from "./ActiveScriptsUI";
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { Interpreter } from "./JSInterpreter";
import { Environment } from "./NetscriptEnvironment";
import {
evaluate,
isScriptErrorMessage,
makeRuntimeRejectMsg,
killNetscriptDelay
} from "./NetscriptEvaluator";
import { NetscriptFunctions } from "./NetscriptFunctions";
import { executeJSScript } from "./NetscriptJSEvaluator";
import { NetscriptPort } from "./NetscriptPort";
import { AllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import {generate} from 'escodegen';
import { generate } from "escodegen";
import { parse, Node } from "../utils/acorn";
import { dialogBoxCreate } from "../utils/DialogBox";
import { compareArrays } from "../utils/helpers/compareArrays";
import { arrayToString } from "../utils/helpers/arrayToString";
import { roundToTwo } from "../utils/helpers/roundToTwo";
import { isString } from "../utils/StringHelperFunctions";
import {parse, Node} from "../utils/acorn";
import {dialogBoxCreate} from "../utils/DialogBox";
import {compareArrays} from "../utils/helpers/compareArrays";
import {arrayToString} from "../utils/helpers/arrayToString";
import {roundToTwo} from "../utils/helpers/roundToTwo";
import {isString} from "../utils/StringHelperFunctions";
const walk = require("acorn/dist/walk");

View File

@@ -12,6 +12,7 @@ import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugment
import { Company } from "../Company/Company";
import { CompanyPosition } from "../Company/CompanyPosition";
import { CityName } from "../Locations/data/CityNames";
import { Faction } from "../Faction/Faction";
import { HashManager } from "../Hacknet/HashManager";
import { HacknetNode } from "../Hacknet/HacknetNode";
import { LocationName } from "../Locations/data/LocationNames";
@@ -114,6 +115,7 @@ export interface IPlayer {
applyForWaiterJob(sing?: boolean): boolean | void;
canAccessBladeburner(): boolean;
canAccessCorporation(): boolean;
canAccessGang(): boolean;
canAccessResleeving(): boolean;
canAfford(cost: number): boolean;
gainHackingExp(exp: number): void;
@@ -124,6 +126,7 @@ export interface IPlayer {
gainCharismaExp(exp: number): void;
gainMoney(money: number): void;
getCurrentServer(): Server;
getGangName(): string;
getHomeComputer(): Server;
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition;
getUpgradeHomeRamCost(): number;
@@ -151,6 +154,10 @@ export interface IPlayer {
money: number,
time: number,
singParams: any): void;
startFactionFieldWork(faction: Faction): void;
startFactionHackWork(faction: Faction): void;
startFactionSecurityWork(faction: Faction): void;
startGang(facName: string, isHacking: boolean): void;
startWork(companyName: string): void;
startWorkPartTime(companyName: string): void;
travel(to: CityName): boolean;

View File

@@ -1,17 +1,20 @@
import * as generalMethods from "./PlayerObjectGeneralMethods";
import * as serverMethods from "./PlayerObjectServerMethods";
import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods";
import * as corporationMethods from "./PlayerObjectCorporationMethods";
import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods";
import * as corporationMethods from "./PlayerObjectCorporationMethods";
import * as gangMethods from "./PlayerObjectGangMethods";
import * as generalMethods from "./PlayerObjectGeneralMethods";
import * as serverMethods from "./PlayerObjectServerMethods";
import { HashManager } from "../../Hacknet/HashManager";
import { CityName } from "../../Locations/data/CityNames";
import { HashManager } from "../../Hacknet/HashManager";
import { CityName } from "../../Locations/data/CityNames";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { Reviver,
Generic_toJSON,
Generic_fromJSON } from "../../../utils/JSONReviver";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import {
Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../../../utils/JSONReviver";
import Decimal from "decimal.js";
import Decimal from "decimal.js";
export function PlayerObject() {
//Skills and stats
@@ -199,7 +202,15 @@ export function PlayerObject() {
this.scriptProdSinceLastAug = 0;
};
Object.assign(PlayerObject.prototype, generalMethods, serverMethods, bladeburnerMethods, corporationMethods);
// Apply player methods to the prototype using Object.assign()
Object.assign(
PlayerObject.prototype,
generalMethods,
serverMethods,
bladeburnerMethods,
corporationMethods,
gangMethods
);
PlayerObject.prototype.toJSON = function() {
return Generic_toJSON("PlayerObject", this);

View File

@@ -0,0 +1,34 @@
import { Factions } from "../../Faction/Factions";
import { Gang } from "../../Gang";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
// Amount of negative karma needed to manage a gang in BitNodes other than 2
const GangKarmaRequirement = -54000;
export function canAccessGang() {
if (this.bitNodeN === 2) { return true; }
if (SourceFileFlags[2] <= 0) { return false; }
return (this.karma <= GangKarmaRequirement);
}
export function getGangName() {
return this.gang.facName;
}
export function inGang() {
if (this.gang == null || this.gang == undefined) { return false; }
return (this.gang instanceof Gang);
}
export function startGang(factionName, hacking) {
this.gang = new Gang(factionName, hacking);
const fac = Factions[factionName];
if (fac == null) {
throw new Error(`Invalid faction name when creating gang: ${factionName}`);
}
fac.playerReputation = 0;
}

View File

@@ -20,7 +20,7 @@ import {Engine} from "../../engine";
import { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions";
import { displayFactionContent } from "../../Faction/FactionHelpers";
import {Gang, resetGangs} from "../../Gang";
import { resetGangs } from "../../Gang";
import { hasHacknetServers } from "../../Hacknet/HacknetHelpers";
import { HashManager } from "../../Hacknet/HashManager";
import { Cities } from "../../Locations/Cities";
@@ -150,8 +150,9 @@ export function prestigeAugmentation() {
this.moneySourceA.reset();
this.hacknetNodes.length = 0;
this.hashManager.prestige(this);
//Re-calculate skills and reset HP
// Re-calculate skills and reset HP
this.updateSkillLevels();
this.hp = this.max_hp;
}
@@ -239,18 +240,19 @@ export function prestigeSourceFile() {
this.lastUpdate = new Date().getTime();
this.hacknetNodes.length = 0;
this.hashManager.prestige(this);
//Gang
// Gang
this.gang = null;
resetGangs();
//Reset Stock market
// Reset Stock market
this.hasWseAccount = false;
this.hasTixApiAccess = false;
this.has4SData = false;
this.has4SDataTixApi = false;
//BitNode 3: Corporatocracy
// BitNode 3: Corporatocracy
this.corporation = 0;
// Statistics trackers
@@ -2174,18 +2176,6 @@ export function checkForFactionInvitations() {
return invitedFactions;
}
/*************** Gang ****************/
//Returns true if Player is in a gang and false otherwise
export function inGang() {
if (this.gang == null || this.gang == undefined) {return false;}
return (this.gang instanceof Gang);
}
export function startGang(factionName, hacking) {
this.gang = new Gang(factionName, hacking);
}
/************* BitNodes **************/
export function setBitNodeNumber(n) {
this.bitNodeN = n;

View File

@@ -10,7 +10,7 @@ export let Player = new PlayerObject();
export function loadPlayer(saveString) {
Player = JSON.parse(saveString, Reviver);
//Parse Decimal.js objects
// Parse Decimal.js objects
Player.money = new Decimal(Player.money);
if (Player.corporation instanceof Corporation) {

View File

@@ -1,57 +1,70 @@
import {deleteActiveScriptsItem} from "./ActiveScriptsUI";
import { Augmentations } from "./Augmentation/Augmentations";
import { augmentationExists,
initAugmentations } from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode";
import {Bladeburner} from "./Bladeburner";
import {writeCinematicText} from "./CinematicText";
import {Companies, initCompanies} from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/IndustryData";
import { Programs } from "./Programs/Programs";
import {Engine} from "./engine";
import { Faction } from "./Faction/Faction";
import { Factions,
initFactions } from "./Faction/Factions";
import { joinFaction } from "./Faction/FactionHelpers";
import {deleteGangDisplayContent} from "./Gang";
import { Message } from "./Message/Message";
import { initMessages,
Messages } from "./Message/MessageHelpers";
import {initSingularitySFFlags, hasWallStreetSF}from "./NetscriptFunctions";
import {WorkerScript, workerScripts,
prestigeWorkerScripts} from "./NetscriptWorker";
import {Player} from "./Player";
import { deleteActiveScriptsItem } from "./ActiveScriptsUI";
import { Augmentations } from "./Augmentation/Augmentations";
import {
augmentationExists,
initAugmentations
} from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner";
import { writeCinematicText } from "./CinematicText";
import { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/IndustryData";
import { Programs } from "./Programs/Programs";
import { Engine } from "./engine";
import { Faction } from "./Faction/Faction";
import { Factions, initFactions } from "./Faction/Factions";
import { joinFaction } from "./Faction/FactionHelpers";
import { deleteGangDisplayContent } from "./Gang";
import { Message } from "./Message/Message";
import { initMessages, Messages } from "./Message/MessageHelpers";
import { initSingularitySFFlags, hasWallStreetSF } from "./NetscriptFunctions";
import {
WorkerScript,
workerScripts,
prestigeWorkerScripts
} from "./NetscriptWorker";
import { Player } from "./Player";
import { AllServers,
AddToAllServers,
initForeignServers,
prestigeAllServers } from "./Server/AllServers";
import { Server } from "./Server/Server"
import { prestigeHomeComputer } from "./Server/ServerHelpers";
import { SourceFileFlags,
updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { SpecialServerIps,
SpecialServerIpsMap,
prestigeSpecialServerIps,
SpecialServerNames} from "./Server/SpecialServerIps";
import {initStockMarket, initSymbolToStockMap,
stockMarketContentCreated,
setStockMarketContentCreated} from "./StockMarket/StockMarket";
import {Terminal, postNetburnerText} from "./Terminal";
import {
AllServers,
AddToAllServers,
initForeignServers,
prestigeAllServers
} from "./Server/AllServers";
import { Server } from "./Server/Server";
import { prestigeHomeComputer } from "./Server/ServerHelpers";
import {
SourceFileFlags,
updateSourceFileFlags
} from "./SourceFile/SourceFileFlags";
import {
SpecialServerIps,
SpecialServerIpsMap,
prestigeSpecialServerIps,
SpecialServerNames
} from "./Server/SpecialServerIps";
import {
initStockMarket,
initSymbolToStockMap,
stockMarketContentCreated,
setStockMarketContentCreated
} from "./StockMarket/StockMarket";
import { Terminal, postNetburnerText } from "./Terminal";
import {Page, routing} from "./ui/navigationTracking";
import { Page, routing } from "./ui/navigationTracking";
import Decimal from "decimal.js";
import {dialogBoxCreate} from "../utils/DialogBox";
import {removeElementById} from "../utils/uiHelpers/removeElementById";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
let BitNode8StartingMoney = 250e6;
import Decimal from "decimal.js";
//Prestige by purchasing augmentation
const BitNode8StartingMoney = 250e6;
// Prestige by purchasing augmentation
function prestigeAugmentation() {
// Set Navigation to Terminal screen, for any logic that depends on it
routing.navigateTo(Page.Terminal);
@@ -68,17 +81,17 @@ function prestigeAugmentation() {
$("#terminal tr:not(:last)").remove();
postNetburnerText();
//Delete all Worker Scripts objects
// Delete all Worker Scripts objects
prestigeWorkerScripts();
var homeComp = Player.getHomeComputer();
//Delete all servers except home computer
// Delete all servers except home computer
prestigeAllServers();
//Delete Special Server IPs
prestigeSpecialServerIps(); //Must be done before initForeignServers()
// Delete Special Server IPs
prestigeSpecialServerIps(); // Must be done before initForeignServers()
//Reset home computer (only the programs) and add to AllServers
// Reset home computer (only the programs) and add to AllServers
AddToAllServers(homeComp);
prestigeHomeComputer(homeComp);
@@ -93,59 +106,59 @@ function prestigeAugmentation() {
homeComp.programs.push(Programs.BruteSSHProgram.name);
}
//Re-create foreign servers
// Re-create foreign servers
initForeignServers(Player.getHomeComputer());
//Gain favor for Companies
// Gain favor for Companies
for (var member in Companies) {
if (Companies.hasOwnProperty(member)) {
Companies[member].gainFavor();
}
}
//Gain favor for factions
// Gain favor for factions
for (var member in Factions) {
if (Factions.hasOwnProperty(member)) {
Factions[member].gainFavor();
}
}
//Stop a Terminal action if there is onerror
// Stop a Terminal action if there is onerror
if (Engine._actionInProgress) {
Engine._actionInProgress = false;
Terminal.finishAction(true);
}
//Re-initialize things - This will update any changes
initFactions(); //Factions must be initialized before augmentations
initAugmentations(); //Calls reapplyAllAugmentations() and resets Player multipliers
// Re-initialize things - This will update any changes
initFactions(); // Factions must be initialized before augmentations
initAugmentations(); // Calls reapplyAllAugmentations() and resets Player multipliers
Player.reapplyAllSourceFiles();
initCompanies();
//Messages
// Messages
initMessages();
//Gang, in BitNode 2
if (Player.bitNodeN == 2 && Player.inGang()) {
var faction = Factions[Player.gang.facName];
// Gang
if (Player.inGang()) {
const faction = Factions[Player.gang.facName];
if (faction instanceof Faction) {
joinFaction(faction);
}
}
//Cancel Bladeburner action
// Cancel Bladeburner action
if (Player.bladeburner instanceof Bladeburner) {
Player.bladeburner.prestige();
}
//BitNode 8: Ghost of Wall Street
// BitNode 8: Ghost of Wall Street
if (Player.bitNodeN === 8) {Player.money = new Decimal(BitNode8StartingMoney);}
if (Player.bitNodeN === 8 || hasWallStreetSF) {
Player.hasWseAccount = true;
Player.hasTixApiAccess = true;
}
//Reset Stock market
// Reset Stock market
if (Player.hasWseAccount) {
initStockMarket();
initSymbolToStockMap();
@@ -156,13 +169,13 @@ function prestigeAugmentation() {
stockMarketList.removeChild(stockMarketList.firstChild);
}
var watchlist = document.getElementById("stock-market-watchlist-filter");
watchlist.value = ""; //Reset watchlist filter
watchlist.value = ""; // Reset watchlist filter
// Refresh Main Menu (the 'World' menu, specifically)
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
//Red Pill
// Red Pill
if (augmentationExists(AugmentationNames.TheRedPill) &&
Augmentations[AugmentationNames.TheRedPill].owned) {
var WorldDaemon = AllServers[SpecialServerIps[SpecialServerNames.WorldDaemon]];
@@ -175,27 +188,27 @@ function prestigeAugmentation() {
}
//Prestige by destroying Bit Node and gaining a Source File
// Prestige by destroying Bit Node and gaining a Source File
function prestigeSourceFile() {
initBitNodeMultipliers(Player);
updateSourceFileFlags(Player);
Player.prestigeSourceFile();
prestigeWorkerScripts(); //Delete all Worker Scripts objects
prestigeWorkerScripts(); // Delete all Worker Scripts objects
var homeComp = Player.getHomeComputer();
//Delete all servers except home computer
prestigeAllServers(); //Must be done before initForeignServers()
// Delete all servers except home computer
prestigeAllServers(); // Must be done before initForeignServers()
//Delete Special Server IPs
// Delete Special Server IPs
prestigeSpecialServerIps();
//Reset home computer (only the programs) and add to AllServers
// Reset home computer (only the programs) and add to AllServers
AddToAllServers(homeComp);
prestigeHomeComputer(homeComp);
//Re-create foreign servers
// Re-create foreign servers
initForeignServers(Player.getHomeComputer());
if (SourceFileFlags[9] >= 2) {
@@ -240,11 +253,11 @@ function prestigeSourceFile() {
Player.reapplyAllSourceFiles();
initCompanies();
//Clear terminal
// Clear terminal
$("#terminal tr:not(:last)").remove();
postNetburnerText();
//Messages
// Messages
initMessages();
var mainMenu = document.getElementById("mainmenu-container");
@@ -252,17 +265,17 @@ function prestigeSourceFile() {
Terminal.resetTerminalInput();
Engine.loadTerminalContent();
//Reinitialize Bit Node flags
// Reinitialize Bit Node flags
initSingularitySFFlags();
//BitNode 3: Corporatocracy
// BitNode 3: Corporatocracy
if (Player.bitNodeN === 3) {
homeComp.messages.push("corporation-management-handbook.lit");
dialogBoxCreate("You received a copy of the Corporation Management Handbook on your home computer. " +
"Read it if you need help getting started with Corporations!");
}
//BitNode 6: Bladeburner
// BitNode 6: Bladeburner
if (Player.bitNodeN === 6) {
var cinematicText = ["In the middle of the 21st century, OmniTek Incorporated advanced robot evolution " +
"with their Synthoids (synthetic androids), a being virtually identical to a human.",
@@ -306,7 +319,7 @@ function prestigeSourceFile() {
}
//BitNode 8: Ghost of Wall Street
// BitNode 8: Ghost of Wall Street
if (Player.bitNodeN === 8) {Player.money = new Decimal(BitNode8StartingMoney);}
if (Player.bitNodeN === 8 || hasWallStreetSF) {
Player.hasWseAccount = true;
@@ -350,7 +363,7 @@ function prestigeSourceFile() {
document.getElementById("world-menu-header").click();
document.getElementById("world-menu-header").click();
//Gain int exp
// Gain int exp
Player.gainIntelligenceExp(5);
}

View File

@@ -1,23 +1,29 @@
import { BitNodes } from "./BitNode/BitNode";
import { Engine } from "./engine";
import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige";
import { SourceFiles,
SourceFile } from "./SourceFile";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { Terminal } from "./Terminal";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
/**
* Implementation for what happens when you destroy a BitNode
*/
import { BitNodes } from "./BitNode/BitNode";
import { Engine } from "./engine";
import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige";
import { SourceFiles, SourceFile } from "./SourceFile";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { Terminal } from "./Terminal";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {dialogBoxCreate} from "../utils/DialogBox";
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
import {yesNoBoxCreate, yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox";
import { dialogBoxCreate } from "../utils/DialogBox";
import {
yesNoBoxCreate,
yesNoBoxGetYesButton,
yesNoBoxGetNoButton,
yesNoBoxClose
} from "../utils/YesNoBox";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
/* RedPill.js
* Implements what happens when you have Red Pill augmentation and then hack the world daemon */
//Returns promise
// Returns promise
function writeRedPillLine(line) {
return new Promise(function(resolve, reject) {
var container = document.getElementById("red-pill-content");
@@ -96,8 +102,6 @@ function hackWorldDaemon(currentNodeNumber, flume=false) {
});
}
//The bitNode name passed in will have a hyphen between number (e.g. BitNode-1)
//This needs to be removed
function giveSourceFile(bitNodeNumber) {
var sourceFileKey = "SourceFile"+ bitNodeNumber.toString();
var sourceFile = SourceFiles[sourceFileKey];
@@ -106,7 +110,7 @@ function giveSourceFile(bitNodeNumber) {
return;
}
//Check if player already has this source file
// Check if player already has this source file
var alreadyOwned = false;
var ownedSourceFile = null;
for (var i = 0; i < Player.sourceFiles.length; ++i) {
@@ -129,7 +133,7 @@ function giveSourceFile(bitNodeNumber) {
} else {
var playerSrcFile = new PlayerOwnedSourceFile(bitNodeNumber, 1);
Player.sourceFiles.push(playerSrcFile);
if (bitNodeNumber === 5) { //Artificial Intelligence
if (bitNodeNumber === 5) { // Artificial Intelligence
Player.intelligence = 1;
}
dialogBoxCreate("You received a Source-File for destroying a Bit Node!<br><br>" +
@@ -138,11 +142,11 @@ function giveSourceFile(bitNodeNumber) {
}
function loadBitVerse(destroyedBitNodeNum, flume=false) {
//Clear the screen
// Clear the screen
var container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
//Create the Bit Verse
// Create the Bit Verse
var bitVerseImage = document.createElement("pre");
var bitNodes = [];
for (var i = 1; i <= 12; ++i) {
@@ -207,7 +211,7 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
container.appendChild(bitVerseImage);
//Bit node event listeners
// Bit node event listeners
for (var i = 1; i <= 12; ++i) {
(function(i) {
var elemId = "bitnode-" + i.toString();
@@ -229,10 +233,10 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
dialogBoxCreate("Not yet implemented! Coming soon!")
});
}
}(i)); //Immediate invocation closure
}(i)); // Immediate invocation closure
}
//Create lore text
// Create lore text
return writeRedPillLine("Many decades ago, a humanoid extraterrestial species which we call the Enders descended on the Earth...violently").then(function() {
return writeRedPillLine("Our species fought back, but it was futile. The Enders had technology far beyond our own...");
}).then(function() {
@@ -281,7 +285,7 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
}
//Returns string with DOM element for Bit Node
// Returns string with DOM element for Bit Node
function createBitNode(n) {
var bitNodeStr = "BitNode" + n.toString();
var bitNode = BitNodes[bitNodeStr];
@@ -300,19 +304,19 @@ function createBitNodeYesNoEventListeners(newBitNode, destroyedBitNode, flume=fa
if (!flume) {
giveSourceFile(destroyedBitNode);
} else {
//If player used flume, subtract 5 int exp. The prestigeSourceFile()
//function below grants 5 int exp, so this allows sets net gain to 0
// If player used flume, subtract 5 int exp. The prestigeSourceFile()
// function below grants 5 int exp, so this allows sets net gain to 0
Player.gainIntelligenceExp(-5);
}
redPillFlag = false;
var container = document.getElementById("red-pill-content");
removeChildrenFromElement(container);
//Set new Bit Node
// Set new Bit Node
Player.bitNodeN = newBitNode;
console.log("Entering Bit Node " + Player.bitNodeN);
//Reenable terminal
// Reenable terminal
$("#hack-progress-bar").attr('id', "old-hack-progress-bar");
$("#hack-progress").attr('id', "old-hack-progress");
document.getElementById("terminal-input-td").innerHTML = '$ <input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1"/>';

View File

@@ -1,41 +1,52 @@
import {loadAliases, loadGlobalAliases,
Aliases, GlobalAliases} from "./Alias";
import {Companies, loadCompanies} from "./Company/Companies";
import {CompanyPosition} from "./Company/CompanyPosition";
import {CONSTANTS} from "./Constants";
import {Engine} from "./engine";
import { Factions,
loadFactions } from "./Faction/Factions";
import { processPassiveFactionRepGain } from "./Faction/FactionHelpers";
import { loadFconf } from "./Fconf/Fconf";
import { FconfSettings } from "./Fconf/FconfSettings";
import {loadAllGangs, AllGangs} from "./Gang";
import { hasHacknetServers,
processHacknetEarnings } from "./Hacknet/HacknetHelpers";
import {
loadAliases,
loadGlobalAliases,
Aliases,
GlobalAliases
} from "./Alias";
import { Companies, loadCompanies } from "./Company/Companies";
import { CompanyPosition } from "./Company/CompanyPosition";
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { Factions, loadFactions } from "./Faction/Factions";
import { processPassiveFactionRepGain } from "./Faction/FactionHelpers";
import { loadFconf } from "./Fconf/Fconf";
import { FconfSettings } from "./Fconf/FconfSettings";
import { loadAllGangs, AllGangs } from "./Gang";
import {
hasHacknetServers,
processHacknetEarnings
} from "./Hacknet/HacknetHelpers";
import { loadMessages, initMessages, Messages } from "./Message/MessageHelpers";
import {Player, loadPlayer} from "./Player";
import { loadAllRunningScripts } from "./Script/ScriptHelpers";
import { AllServers,
loadAllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
import { loadSpecialServerIps,
SpecialServerIps } from "./Server/SpecialServerIps";
import {loadStockMarket, StockMarket} from "./StockMarket/StockMarket";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { Player, loadPlayer } from "./Player";
import { loadAllRunningScripts } from "./Script/ScriptHelpers";
import { AllServers, loadAllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
import {
loadSpecialServerIps,
SpecialServerIps
} from "./Server/SpecialServerIps";
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
import {dialogBoxCreate} from "../utils/DialogBox";
import {gameOptionsBoxClose} from "../utils/GameOptions";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {Reviver, Generic_toJSON,
Generic_fromJSON} from "../utils/JSONReviver";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
import {createStatusText} from "./ui/createStatusText";
import {numeralWrapper} from "./ui/numeralFormat";
import {removeElementById} from "../utils/uiHelpers/removeElementById";
import { createStatusText } from "./ui/createStatusText";
import { numeralWrapper } from "./ui/numeralFormat";
import Decimal from "decimal.js";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { dialogBoxCreate } from "../utils/DialogBox";
import { gameOptionsBoxClose } from "../utils/GameOptions";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import {
Reviver,
Generic_toJSON,
Generic_fromJSON
} from "../utils/JSONReviver";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import Decimal from "decimal.js";
/* SaveObject.js
* Defines the object used to save/load games
@@ -61,7 +72,7 @@ function BitburnerSaveObject() {
BitburnerSaveObject.prototype.getSaveString = function() {
this.PlayerSave = JSON.stringify(Player);
//Delete all logs from all running scripts
// Delete all logs from all running scripts
var TempAllServers = JSON.parse(JSON.stringify(AllServers), Reviver);
for (var ip in TempAllServers) {
var server = TempAllServers[ip];
@@ -84,7 +95,7 @@ BitburnerSaveObject.prototype.getSaveString = function() {
this.SettingsSave = JSON.stringify(Settings);
this.FconfSettingsSave = JSON.stringify(FconfSettings);
this.VersionSave = JSON.stringify(CONSTANTS.Version);
if (Player.bitNodeN == 2 && Player.inGang()) {
if (Player.inGang()) {
this.AllGangsSave = JSON.stringify(AllGangs);
}
var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this))));
@@ -95,7 +106,7 @@ BitburnerSaveObject.prototype.getSaveString = function() {
BitburnerSaveObject.prototype.saveGame = function(db) {
var saveString = this.getSaveString();
//We'll save to both localstorage and indexedDb
// We'll save to both localstorage and indexedDb
var objectStore = db.transaction(["savestring"], "readwrite").objectStore("savestring");
var request = objectStore.put(saveString, "save");
@@ -104,12 +115,11 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
}
request.onsuccess = function(e) {
//console.log("Saved game to IndexedDB!");
// TODO anything here?
}
try {
window.localStorage.setItem("bitburnerSave", saveString);
//console.log("Saved game to LocalStorage!");
} catch(e) {
if (e.code == 22) {
createStatusText("Save failed for localStorage! Check console(F12)");
@@ -249,7 +259,7 @@ function loadGame(saveString) {
evaluateVersionCompatibility(ver);
if (window.location.href.toLowerCase().includes("bitburner-beta")) {
//Beta branch, always show changes
// Beta branch, always show changes
createBetaUpdateText();
} else if (ver != CONSTANTS.Version) {
createNewUpdateText();
@@ -260,7 +270,7 @@ function loadGame(saveString) {
} else {
createNewUpdateText();
}
if (Player.bitNodeN == 2 && Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) {
if (Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) {
try {
loadAllGangs(saveObj.AllGangsSave);
} catch(e) {
@@ -285,8 +295,8 @@ function loadImportedGame(saveObj, saveString) {
var tempAllGangs = null;
let tempCorporationResearchTrees = null;
//Check to see if the imported save file can be parsed. If any
//errors are caught it will fail
// Check to see if the imported save file can be parsed. If any
// errors are caught it will fail
try {
var decodedSaveString = decodeURIComponent(escape(atob(saveString)));
tempSaveObj = new BitburnerSaveObject();
@@ -294,7 +304,7 @@ function loadImportedGame(saveObj, saveString) {
tempPlayer = JSON.parse(tempSaveObj.PlayerSave, Reviver);
//Parse Decimal.js objects
// Parse Decimal.js objects
tempPlayer.money = new Decimal(tempPlayer.money);
tempAllServers = JSON.parse(tempSaveObj.AllServersSave, Reviver);
@@ -350,7 +360,7 @@ function loadImportedGame(saveObj, saveString) {
}
} else {
}
if (tempPlayer.bitNodeN == 2 && tempPlayer.inGang() && tempSaveObj.hasOwnProperty("AllGangsSave")) {
if (tempPlayer.inGang() && tempSaveObj.hasOwnProperty("AllGangsSave")) {
try {
loadAllGangs(tempSaveObj.AllGangsSave);
} catch(e) {
@@ -363,7 +373,7 @@ function loadImportedGame(saveObj, saveString) {
return false;
}
//Since the save file is valid, load everything for real
// Since the save file is valid, load everything for real
saveString = decodeURIComponent(escape(atob(saveString)));
saveObj = JSON.parse(saveString, Reviver);
@@ -439,7 +449,7 @@ function loadImportedGame(saveObj, saveString) {
} else {
createNewUpdateText();
}
if (Player.bitNodeN == 2 && Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) {
if (Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) {
try {
loadAllGangs(saveObj.AllGangsSave);
} catch(e) {
@@ -461,18 +471,18 @@ function loadImportedGame(saveObj, saveString) {
createPopup(popupId, [txt, gotitBtn]);
gameOptionsBoxClose();
//Re-start game
// Re-start game
console.log("Importing game");
Engine.setDisplayElements(); //Sets variables for important DOM elements
Engine.init(); //Initialize buttons, work, etc.
Engine.setDisplayElements(); // Sets variables for important DOM elements
Engine.init(); // Initialize buttons, work, etc.
//Calculate the number of cycles have elapsed while offline
// Calculate the number of cycles have elapsed while offline
Engine._lastUpdate = new Date().getTime();
var lastUpdate = Player.lastUpdate;
var numCyclesOffline = Math.floor((Engine._lastUpdate - lastUpdate) / Engine._idleSpeed);
/* Process offline progress */
var offlineProductionFromScripts = loadAllRunningScripts(); //This also takes care of offline production for those scripts
// Process offline progress
var offlineProductionFromScripts = loadAllRunningScripts(); // This also takes care of offline production for those scripts
if (Player.isWorking) {
console.log("work() called in load() for " + numCyclesOffline * Engine._idleSpeed + " milliseconds");
if (Player.workType == CONSTANTS.WorkTypeFaction) {
@@ -490,16 +500,16 @@ function loadImportedGame(saveObj, saveString) {
}
}
//Hacknet Nodes offline progress
// Hacknet Nodes offline progress
var offlineProductionFromHacknetNodes = processHacknetEarnings(numCyclesOffline);
const hacknetProdInfo = hasHacknetServers() ?
`${numeralWrapper.format(offlineProductionFromHacknetNodes, "0.000a")} hashes` :
`${numeralWrapper.formatMoney(offlineProductionFromHacknetNodes)}`;
//Passive faction rep gain offline
// Passive faction rep gain offline
processPassiveFactionRepGain(numCyclesOffline);
//Update total playtime
// Update total playtime
var time = numCyclesOffline * Engine._idleSpeed;
if (Player.totalPlaytime == null) {Player.totalPlaytime = 0;}
if (Player.playtimeSinceLastAug == null) {Player.playtimeSinceLastAug = 0;}
@@ -508,14 +518,14 @@ function loadImportedGame(saveObj, saveString) {
Player.playtimeSinceLastAug += time;
Player.playtimeSinceLastBitnode += time;
//Re-apply augmentations
// Re-apply augmentations
Player.reapplyAllAugmentations();
//Clear terminal
// Clear terminal
$("#terminal tr:not(:last)").remove();
Player.lastUpdate = Engine._lastUpdate;
Engine.start(); //Run main game loop and Scripts loop
Engine.start(); // Run main game loop and Scripts loop
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
dialogBoxCreate(`Offline for ${timeOfflineString}. While you were offline, your scripts ` +
"generated <span class='money-gold'>" +
@@ -536,7 +546,7 @@ BitburnerSaveObject.prototype.exportGame = function() {
this.StockMarketSave = JSON.stringify(StockMarket);
this.SettingsSave = JSON.stringify(Settings);
this.VersionSave = JSON.stringify(CONSTANTS.Version);
if (Player.bitNodeN == 2 && Player.inGang()) {
if (Player.inGang()) {
this.AllGangsSave = JSON.stringify(AllGangs);
}
@@ -571,12 +581,12 @@ BitburnerSaveObject.prototype.importGame = function() {
}
BitburnerSaveObject.prototype.deleteGame = function(db) {
//Delete from local storage
// Delete from local storage
if (window.localStorage.getItem("bitburnerSave")) {
window.localStorage.removeItem("bitburnerSave");
}
//Delete from indexedDB
// Delete from indexedDB
var request = db.transaction(["savestring"], "readwrite").objectStore("savestring").delete("save");
request.onsuccess = function(e) {
console.log("Successfully deleted save from indexedDb");
@@ -613,8 +623,6 @@ BitburnerSaveObject.fromJSON = function(value) {
Reviver.constructors.BitburnerSaveObject = BitburnerSaveObject;
//Import game
function openImportFileHandler(evt) {
var file = evt.target.files[0];
if (!file) {

View File

@@ -1,8 +1,7 @@
import { Player } from "./Player";
import { BitNodes } from "./BitNode/BitNode";
/* SourceFile.js */
//Each SourceFile corresponds to a BitNode with the same number
// Each SourceFile corresponds to a BitNode with the same number
function SourceFile(number, info="") {
var bitnodeKey = "BitNode" + number;
var bitnode = BitNodes[bitnodeKey];
@@ -25,8 +24,9 @@ function initSourceFiles() {
"Level 1: 16%<br>" +
"Level 2: 24%<br>" +
"Level 3: 28%");
SourceFiles["SourceFile2"] = new SourceFile(2, "This Source-File increases the player's crime success rate, crime money, and charisma " +
"multipliers by:<br><br>" +
SourceFiles["SourceFile2"] = new SourceFile(2, "This Source-File allows you to form gangs in other BitNodes " +
"once your karma decreases to a certain value. It also increases the player's " +
"crime success rate, crime money, and charisma multipliers by:<br><br>" +
"Level 1: 24%<br>" +
"Level 2: 36%<br>" +
"Level 3: 42%");
@@ -80,7 +80,7 @@ function initSourceFiles() {
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
}
//Takes in a PlayerOwnedSourceFile as the "srcFile" argument
// Takes in a PlayerOwnedSourceFile as the "srcFile" argument
function applySourceFile(srcFile) {
var srcFileKey = "SourceFile" + srcFile.n;
var sourceFileObject = SourceFiles[srcFileKey];

View File

@@ -8,77 +8,85 @@ import {
removeLeadingSlash,
removeTrailingSlash
} from "./Terminal/DirectoryHelpers";
import { determineAllPossibilitiesForTabCompletion } from "./Terminal/determineAllPossibilitiesForTabCompletion";
import { TerminalHelpText, HelpTexts } from "./Terminal/HelpText";
import { tabCompletion } from "./Terminal/tabCompletion";
import {
determineAllPossibilitiesForTabCompletion
} from "./Terminal/determineAllPossibilitiesForTabCompletion";
Aliases,
GlobalAliases,
parseAliasDeclaration,
printAliases,
removeAlias,
substituteAliases
} from "./Alias";
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import {
TerminalHelpText,
HelpTexts
} from "./Terminal/HelpText";
CodingContract,
CodingContractResult,
CodingContractRewardType
} from "./CodingContracts";
import { CONSTANTS } from "./Constants";
import { Programs } from "./Programs/Programs";
import {
tabCompletion
} from "./Terminal/tabCompletion";
import { Aliases,
GlobalAliases,
parseAliasDeclaration,
printAliases,
removeAlias,
substituteAliases } from "./Alias";
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import {CodingContract, CodingContractResult,
CodingContractRewardType} from "./CodingContracts";
import {CONSTANTS} from "./Constants";
import { Programs } from "./Programs/Programs";
import { executeDarkwebTerminalCommand,
checkIfConnectedToDarkweb } from "./DarkWeb/DarkWeb";
import { DarkWebItems } from "./DarkWeb/DarkWebItems";
import {Engine} from "./engine";
import { parseFconfSettings,
createFconf } from "./Fconf/Fconf";
import { FconfSettings } from "./Fconf/FconfSettings";
import {calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime} from "./Hacking";
import { HacknetServer } from "./Hacknet/HacknetServer";
import {iTutorialNextStep, iTutorialSteps,
ITutorial} from "./InteractiveTutorial";
import {showLiterature} from "./Literature";
import { Message } from "./Message/Message";
import { showMessage } from "./Message/MessageHelpers";
import {killWorkerScript, addWorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
import {hackWorldDaemon} from "./RedPill";
import { RunningScript } from "./Script/RunningScript";
import { findRunningScript } from "./Script/ScriptHelpers";
import { isScriptFilename } from "./Script/ScriptHelpersTS";
import { AllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { GetServerByHostname,
getServer,
getServerOnNetwork } from "./Server/ServerHelpers";
import {Settings} from "./Settings/Settings";
import { SpecialServerIps,
SpecialServerNames } from "./Server/SpecialServerIps";
import {getTextFile} from "./TextFile";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {KEY} from "../utils/helpers/keyCodes";
import {addOffset} from "../utils/helpers/addOffset";
import {isString} from "../utils/helpers/isString";
import {arrayToString} from "../utils/helpers/arrayToString";
import {getTimestamp} from "../utils/helpers/getTimestamp";
import {logBoxCreate} from "../utils/LogBox";
import {yesNoBoxCreate,
yesNoBoxGetYesButton,
yesNoBoxGetNoButton, yesNoBoxClose} from "../utils/YesNoBox";
executeDarkwebTerminalCommand,
checkIfConnectedToDarkweb
} from "./DarkWeb/DarkWeb";
import { DarkWebItems } from "./DarkWeb/DarkWebItems";
import { Engine } from "./engine";
import { parseFconfSettings, createFconf } from "./Fconf/Fconf";
import { FconfSettings } from "./Fconf/FconfSettings";
import {
calculateHackingChance,
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime
} from "./Hacking";
import { HacknetServer } from "./Hacknet/HacknetServer";
import {
iTutorialNextStep,
iTutorialSteps,
ITutorial
} from "./InteractiveTutorial";
import { showLiterature } from "./Literature";
import { Message } from "./Message/Message";
import { showMessage } from "./Message/MessageHelpers";
import { killWorkerScript, addWorkerScript } from "./NetscriptWorker";
import { Player } from "./Player";
import { hackWorldDaemon } from "./RedPill";
import { RunningScript } from "./Script/RunningScript";
import { findRunningScript } from "./Script/ScriptHelpers";
import { isScriptFilename } from "./Script/ScriptHelpersTS";
import { AllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import {
GetServerByHostname,
getServer,
getServerOnNetwork
} from "./Server/ServerHelpers";
import { Settings } from "./Settings/Settings";
import {
SpecialServerIps,
SpecialServerNames
} from "./Server/SpecialServerIps";
import { getTextFile } from "./TextFile";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
import { KEY } from "../utils/helpers/keyCodes";
import { addOffset } from "../utils/helpers/addOffset";
import { isString } from "../utils/helpers/isString";
import { arrayToString } from "../utils/helpers/arrayToString";
import { getTimestamp } from "../utils/helpers/getTimestamp";
import { logBoxCreate } from "../utils/LogBox";
import {
yesNoBoxCreate,
yesNoBoxGetYesButton,
yesNoBoxGetNoButton,
yesNoBoxClose
} from "../utils/YesNoBox";
import {
post,
postContent,
@@ -87,9 +95,10 @@ import {
hackProgressPost
} from "./ui/postToTerminal";
import autosize from 'autosize';
import * as JSZip from 'jszip';
import * as FileSaver from 'file-saver';
import autosize from "autosize";
import * as JSZip from "jszip";
import * as FileSaver from "file-saver";
function postNetburnerText() {
post("Bitburner v" + CONSTANTS.Version);
@@ -103,13 +112,13 @@ function isNumber(str) {
// Defines key commands in terminal
$(document).keydown(function(event) {
//Terminal
// Terminal
if (routing.isOn(Page.Terminal)) {
var terminalInput = document.getElementById("terminal-input-text-box");
if (terminalInput != null && !event.ctrlKey && !event.shiftKey && !Terminal.contractOpen) {terminalInput.focus();}
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 dir = Terminal.currDir;
post(
@@ -120,35 +129,35 @@ $(document).keydown(function(event) {
);
if (command.length > 0) {
Terminal.resetTerminalInput(); //Clear input first
Terminal.resetTerminalInput(); // Clear input first
Terminal.executeCommands(command);
}
}
if (event.keyCode === KEY.C && event.ctrlKey) {
if (Engine._actionInProgress) {
//Cancel action
// Cancel action
post("Cancelling...");
Engine._actionInProgress = false;
Terminal.finishAction(true);
} else if (FconfSettings.ENABLE_BASH_HOTKEYS) {
//Dont prevent default so it still copies
Terminal.resetTerminalInput(); //Clear Terminal
// Dont prevent default so it still copies
Terminal.resetTerminalInput(); // Clear Terminal
}
}
if (event.keyCode === KEY.L && event.ctrlKey) {
event.preventDefault();
Terminal.executeCommand("clear"); //Clear screen
Terminal.executeCommand("clear"); // Clear screen
}
//Ctrl p same as up arrow
//Ctrl n same as down arrow
// Ctrl p same as up arrow
// Ctrl n same as down arrow
if (event.keyCode === KEY.UPARROW ||
(FconfSettings.ENABLE_BASH_HOTKEYS && event.keyCode === KEY.P && event.ctrlKey)) {
if (FconfSettings.ENABLE_BASH_HOTKEYS) {event.preventDefault();}
//Cycle through past commands
// Cycle through past commands
if (terminalInput == null) {return;}
var i = Terminal.commandHistoryIndex;
var len = Terminal.commandHistory.length;
@@ -169,7 +178,7 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.DOWNARROW ||
(FconfSettings.ENABLE_BASH_HOTKEYS && event.keyCode === KEY.M && event.ctrlKey)) {
if (FconfSettings.ENABLE_BASH_HOTKEYS) {event.preventDefault();}
//Cycle through past commands
// Cycle through past commands
if (terminalInput == null) {return;}
var i = Terminal.commandHistoryIndex;
var len = Terminal.commandHistory.length;
@@ -179,7 +188,7 @@ $(document).keydown(function(event) {
Terminal.commandHistoryIndex = len;
}
//Latest command, put nothing
// Latest command, put nothing
if (i == len || i == len-1) {
Terminal.commandHistoryIndex = len;
terminalInput.value = "";
@@ -193,7 +202,7 @@ $(document).keydown(function(event) {
if (event.keyCode === KEY.TAB) {
event.preventDefault();
//Autocomplete
// Autocomplete
if (terminalInput == null) {return;}
var input = terminalInput.value;
if (input == "") {return;}
@@ -231,7 +240,7 @@ $(document).keydown(function(event) {
terminalInput.focus();
}
//Extra Bash Emulation Hotkeys, must be enabled through .fconf
// Extra Bash Emulation Hotkeys, must be enabled through .fconf
if (FconfSettings.ENABLE_BASH_HOTKEYS) {
if (event.keyCode === KEY.A && event.ctrlKey) {
event.preventDefault();
@@ -269,17 +278,16 @@ $(document).keydown(function(event) {
event.preventDefault();
}
//TODO AFTER THIS:
//alt + d deletes word after cursor
//^w deletes word before cursor
//^k clears line after cursor
//^u clears line before cursor
// TODO AFTER THIS:
// alt + d deletes word after cursor
// ^w deletes word before cursor
// ^k clears line after cursor
// ^u clears line before cursor
}
}
});
//Keep terminal in focus
// Keep terminal in focus
let terminalCtrlPressed = false, shiftKeyPressed = false;
$(document).ready(function() {
if (routing.isOn(Page.Terminal)) {
@@ -294,7 +302,7 @@ $(document).keydown(function(e) {
} else if (e.shiftKey) {
shiftKeyPressed = true;
} else if (terminalCtrlPressed || shiftKeyPressed || Terminal.contractOpen) {
//Don't focus
// Don't focus
} else {
var inputTextBox = document.getElementById("terminal-input-text-box");
if (inputTextBox != null) {inputTextBox.focus();}
@@ -340,7 +348,7 @@ let Terminal = {
`<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"/>';
//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"));
} else {
document.getElementById("terminal-input-td").innerHTML =
@@ -368,7 +376,7 @@ let Terminal = {
terminalInput.value = inputText.substr(0, start-1) + inputText.substr(start);
}
break;
case "deletewordbefore": //Delete rest of word before the cursor
case "deletewordbefore": // Delete rest of word before the cursor
for (var delStart = start-1; delStart > 0; --delStart) {
if (inputText.charAt(delStart) === " ") {
terminalInput.value = inputText.substr(0, delStart) + inputText.substr(start);
@@ -376,7 +384,7 @@ let Terminal = {
}
}
break;
case "deletewordafter": //Delete rest of word after the cursor
case "deletewordafter": // Delete rest of word after the cursor
for (var delStart = start+1; delStart <= text.length+1; ++delStart) {
if (inputText.charAt(delStart) === " ") {
terminalInput.value = inputText.substr(0, start) + inputText.substr(delStart);
@@ -384,9 +392,9 @@ let Terminal = {
}
}
break;
case "clearafter": //Deletes everything after cursor
case "clearafter": // Deletes everything after cursor
break;
case "clearbefore:": //Deleetes everything before cursor
case "clearbefore:": // Deleetes everything before cursor
break;
}
} catch(e) {
@@ -447,7 +455,7 @@ let Terminal = {
startHack: function() {
Terminal.hackFlag = true;
//Hacking through Terminal should be faster than hacking through a script
// Hacking through Terminal should be faster than hacking through a script
Terminal.actionTime = calculateHackingTime(Player.getCurrentServer()) / 4;
Terminal.startAction();
},
@@ -465,7 +473,7 @@ let Terminal = {
hackProgressPost("Time left:");
hackProgressBarPost("[");
//Disable terminal
// Disable terminal
document.getElementById("terminal-input-td").innerHTML = '<input type="text" class="terminal-input"/>';
$('input[class=terminal-input]').prop('disabled', true);
},
@@ -478,17 +486,17 @@ let Terminal = {
}
},
//Complete the hack/analyze command
// Complete the hack/analyze command
finishHack: function(cancelled = false) {
if (cancelled == false) {
var server = Player.getCurrentServer();
//Calculate whether hack was successful
// Calculate whether hack was successful
var hackChance = calculateHackingChance(server);
var rand = Math.random();
var expGainedOnSuccess = calculateHackingExpGain(server);
var expGainedOnFailure = (expGainedOnSuccess / 4);
if (rand < hackChance) { //Success!
if (rand < hackChance) { // Success!
if (SpecialServerIps[SpecialServerNames.WorldDaemon] &&
SpecialServerIps[SpecialServerNames.WorldDaemon] == server.ip) {
if (Player.bitNodeN == null) {
@@ -501,7 +509,7 @@ let Terminal = {
var moneyGained = calculatePercentMoneyHacked(server);
moneyGained = Math.floor(server.moneyAvailable * moneyGained);
if (moneyGained <= 0) {moneyGained = 0;} //Safety check
if (moneyGained <= 0) {moneyGained = 0;} // Safety check
server.moneyAvailable -= moneyGained;
Player.gainMoney(moneyGained);
@@ -512,14 +520,14 @@ let Terminal = {
server.fortify(CONSTANTS.ServerFortifyAmount);
post("Hack successful! Gained " + numeralWrapper.format(moneyGained, '($0,0.00)') + " and " + numeralWrapper.format(expGainedOnSuccess, '0.0000') + " hacking EXP");
} else { //Failure
//Player only gains 25% exp for failure? TODO Can change this later to balance
} else { // Failure
// Player only gains 25% exp for failure? TODO Can change this later to balance
Player.gainHackingExp(expGainedOnFailure)
post("Failed to hack " + server.hostname + ". Gained " + numeralWrapper.format(expGainedOnFailure, '0.0000') + " hacking EXP");
}
}
//Rename the progress bar so that the next hacks dont trigger it. Re-enable terminal
// Rename the progress bar so that the next hacks dont trigger it. Re-enable terminal
$("#hack-progress-bar").attr('id', "old-hack-progress-bar");
$("#hack-progress").attr('id', "old-hack-progress");
Terminal.resetTerminalInput();
@@ -587,7 +595,7 @@ let Terminal = {
executeCommands : function(commands) {
// Sanitize input
commands = commands.trim();
commands = commands.replace(/\s\s+/g, ' '); //Replace all extra whitespace in command with a single space
commands = commands.replace(/\s\s+/g, ' '); // Replace all extra whitespace in command with a single space
// Handle Terminal History - multiple commands should be saved as one
if (Terminal.commandHistory[Terminal.commandHistory.length-1] != commands) {
@@ -635,7 +643,7 @@ let Terminal = {
if (endQuote === command.length-1) {
start = i = endQuote+1;
} else {
start = i = endQuote+2; //Skip the space
start = i = endQuote+2; // Skip the space
}
continue;
}
@@ -654,7 +662,7 @@ let Terminal = {
if (endQuote === command.length-1) {
start = i = endQuote+1;
} else {
start = i = endQuote+2; //Skip the space
start = i = endQuote+2; // Skip the space
}
continue;
}
@@ -696,15 +704,15 @@ let Terminal = {
},
executeCommand : function(command) {
//Process any aliases
// Process any aliases
command = substituteAliases(command);
//Allow usage of ./
// Allow usage of ./
if (command.startsWith("./")) {
command = "run " + command.slice(2);
}
//Only split the first space
// Only split the first space
var commandArray = Terminal.parseCommandArguments(command);
if (commandArray.length == 0) { return; }
@@ -806,7 +814,7 @@ let Terminal = {
case iTutorialSteps.ActiveScriptsToTerminal:
if (commandArray.length == 2 &&
commandArray[0] == "tail" && commandArray[1] == "foodnstuff.script") {
//Check that the script exists on this machine
// Check that the script exists on this machine
var runningScript = findRunningScript("foodnstuff.script", [], Player.getCurrentServer());
if (runningScript == null) {
post("Error: No such script exists");
@@ -932,7 +940,7 @@ let Terminal = {
postError("Incorrect number of arguments. Usage: check [script] [arg1] [arg2]...");
} else {
const scriptName = Terminal.getFilepath(commandArray[1]);
//Can only tail script files
// Can only tail script files
if (!isScriptFilename(scriptName)) {
postError("tail can only be called on .script files (filename must end with .script)");
return;
@@ -968,7 +976,7 @@ let Terminal = {
postNetburnerText();
break;
case "connect": {
//Disconnect from current server in terminal and connect to new one
// Disconnect from current server in terminal and connect to new one
if (commandArray.length !== 2) {
postError("Incorrect usage of connect command. Usage: connect [ip/hostname]");
return;
@@ -994,7 +1002,7 @@ let Terminal = {
}
const fn = commandArray[1];
if (fn === "*" || fn === "*.script" || fn === "*.txt") {
//Download all scripts as a zip
// Download all scripts as a zip
var zip = new JSZip();
if (fn === "*" || fn === "*.script") {
for (var i = 0; i < s.scripts.length; ++i) {
@@ -1072,8 +1080,8 @@ let Terminal = {
postError("Incorrect usage of hack command. Usage: hack");
return;
}
//Hack the current PC (usually for money)
//You can't hack your home pc or servers you purchased
// Hack the current PC (usually for money)
// You can't hack your home pc or servers you purchased
if (s.purchasedByPlayer) {
postError("Cannot hack your own machines! You are currently connected to your home PC or one of your purchased servers");
} else if (s.hasAdminRights == false ) {
@@ -1253,7 +1261,7 @@ let Terminal = {
return;
}
//Check programs
// Check programs
let delTarget = Terminal.getFilepath(commandArray[1]);
const status = s.removeFile(delTarget);
@@ -1263,19 +1271,19 @@ let Terminal = {
break;
}
case "run":
//Run a program or a script
// Run a program or a script
if (commandArray.length < 2) {
postError("Incorrect number of arguments. Usage: run [program/script] [-t] [num threads] [arg1] [arg2]...");
} else {
var executableName = commandArray[1];
//Secret Music player!
// Secret Music player!
if (executableName === "musicplayer") {
post('<iframe src="https://open.spotify.com/embed/user/danielyxie/playlist/1ORnnL6YNvXOracUaUV2kh" width="300" height="380" frameborder="0" allowtransparency="true"></iframe>', false);
return;
}
//Check if its a script or just a program/executable
// Check if its a script or just a program/executable
if (isScriptFilename(executableName)) {
Terminal.runScript(commandArray);
} else if (executableName.endsWith(".cct")) {
@@ -1425,20 +1433,20 @@ let Terminal = {
post("Script Threads RAM Usage");
let currRunningScripts = s.runningScripts;
//Iterate through scripts on current server
// Iterate through scripts on current server
for (let i = 0; i < currRunningScripts.length; i++) {
let script = currRunningScripts[i];
//Calculate name padding
let numSpacesScript = 32 - script.filename.length; //26 -> width of name column
// Calculate name padding
let numSpacesScript = 32 - script.filename.length; // 26 -> width of name column
if (numSpacesScript < 0) {numSpacesScript = 0;}
let spacesScript = Array(numSpacesScript+1).join(" ");
//Calculate thread padding
let numSpacesThread = 16 - (script.threads + "").length; //16 -> width of thread column
// Calculate thread padding
let numSpacesThread = 16 - (script.threads + "").length; // 16 -> width of thread column
let spacesThread = Array(numSpacesThread+1).join(" ");
//Calculate and transform RAM usage
// Calculate and transform RAM usage
let ramUsage = numeralWrapper.format(script.getRamUsage() * script.threads, '0.00') + " GB";
var entry = [script.filename, spacesScript, script.threads, spacesThread, ramUsage];
@@ -1466,7 +1474,7 @@ let Terminal = {
}
let url = commandArray[1];
let target = comanndArray[2];
let target = commandArray[2];
if (!isScriptFilename(target) && !target.endsWith(".txt")) {
return post(`wget failed: Invalid target file. Target file must be script or text file`);
}
@@ -1506,7 +1514,7 @@ let Terminal = {
post("Connected to " + serv.hostname);
Terminal.currDir = "/";
if (Player.getCurrentServer().hostname == "darkweb") {
checkIfConnectedToDarkweb(); //Posts a 'help' message if connecting to dark web
checkIfConnectedToDarkweb(); // Posts a 'help' message if connecting to dark web
}
Terminal.resetTerminalInput();
},
@@ -1597,7 +1605,7 @@ let Terminal = {
prefix = null;
}
//Display all programs and scripts
// Display all programs and scripts
let allFiles = [];
let folders = [];
@@ -1733,18 +1741,18 @@ let Terminal = {
const currServ = Player.getCurrentServer();
post("Hostname IP Root Access");
for (let i = 0; i < currServ.serversOnNetwork.length; i++) {
//Add hostname
// Add hostname
let entry = getServerOnNetwork(currServ, i);
if (entry == null) { continue; }
entry = entry.hostname;
//Calculate padding and add IP
// Calculate padding and add IP
let numSpaces = 21 - entry.length;
let spaces = Array(numSpaces+1).join(" ");
entry += spaces;
entry += getServerOnNetwork(currServ, i).ip;
//Calculate padding and add root access info
// Calculate padding and add root access info
let hasRoot;
if (getServerOnNetwork(currServ, i).hasAdminRights) {
hasRoot = 'Y';
@@ -1760,7 +1768,7 @@ let Terminal = {
},
executeScanAnalyzeCommand: function(depth=1, all=false) {
//TODO Using array as stack for now, can make more efficient
// TODO Using array as stack for now, can make more efficient
post("~~~~~~~~~~ Beginning scan-analyze ~~~~~~~~~~");
post(" ");
@@ -1791,7 +1799,7 @@ let Terminal = {
stack.push(getServerOnNetwork(s, i));
depthQueue.push(d+1);
}
if (d == 0) {continue;} //Don't print current server
if (d == 0) {continue;} // Don't print current server
var titleDashes = Array((d-1) * 4 + 1).join("-");
if (Player.hasProgram(Programs.AutoLink.name)) {
post("<strong>" + titleDashes + "> <a class='scan-analyze-link'>" + s.hostname + "</a></strong>", false);
@@ -1800,7 +1808,6 @@ let Terminal = {
}
var dashes = titleDashes + "--";
//var dashes = Array(d * 2 + 1).join("-");
var c = "NO";
if (s.hasAdminRights) {c = "YES";}
post(`${dashes}Root Access: ${c}${!isHacknet ? ", Required hacking skill: " + s.requiredHackingSkill : ""}`);
@@ -1817,7 +1824,7 @@ let Terminal = {
if (Terminal.analyzeFlag || Terminal.hackFlag) {return;}
Terminal.connectToServer(hostname);
}
}());//Immediate invocation
}());// Immediate invocation
}
},
@@ -1841,7 +1848,7 @@ let Terminal = {
const ip = destServer.ip;
const currServ = Player.getCurrentServer();
//Scp for lit files
// Scp for lit files
if (scriptname.endsWith(".lit")) {
var found = false;
for (var i = 0; i < currServ.messages.length; ++i) {
@@ -1856,14 +1863,14 @@ let Terminal = {
for (var i = 0; i < destServer.messages.length; ++i) {
if (destServer.messages[i] === scriptname) {
post(scriptname + " copied over to " + destServer.hostname);
return; //Already exists
return; // Already exists
}
}
destServer.messages.push(scriptname);
return post(scriptname + " copied over to " + destServer.hostname);
}
//Scp for txt files
// Scp for txt files
if (scriptname.endsWith(".txt")) {
var found = false, txtFile;
for (var i = 0; i < currServ.textFiles.length; ++i) {
@@ -1919,8 +1926,8 @@ let Terminal = {
}
},
//First called when the "run [program]" command is called. Checks to see if you
//have the executable and, if you do, calls the executeProgram() function
// First called when the "run [program]" command is called. Checks to see if you
// have the executable and, if you do, calls the executeProgram() function
runProgram: function(commandArray) {
if (commandArray.length < 2) { return; }
@@ -1935,7 +1942,7 @@ let Terminal = {
post("ERROR: No such executable on home computer (Only programs that exist on your home computer can be run)");
},
//Contains the implementations of all possible programs
// Contains the implementations of all possible programs
executeProgram: function(commandArray) {
if (commandArray.length < 2) { return; }
@@ -2229,16 +2236,16 @@ let Terminal = {
}
//Check if this script is already running
// Check if this script is already running
if (findRunningScript(scriptName, args, server) != null) {
post("ERROR: This script is already running. Cannot run multiple instances");
return;
}
//Check if the script exists and if it does run it
// Check if the script exists and if it does run it
for (var i = 0; i < server.scripts.length; i++) {
if (server.scripts[i].filename === scriptName) {
//Check for admin rights and that there is enough RAM availble to run
// Check for admin rights and that there is enough RAM availble to run
var script = server.scripts[i];
var ramUsage = script.ramUsage * numThreads;
var ramAvailable = server.maxRam - server.ramUsed;
@@ -2251,7 +2258,7 @@ let Terminal = {
numThreads + " threads. Script requires " + ramUsage + "GB of RAM");
return;
} else {
//Able to run script
// Able to run script
post("Running script with " + numThreads + " thread(s) and args: " + arrayToString(args) + ".");
post("May take a few seconds to start up the process...");
var runningScriptObj = new RunningScript(script, args);

View File

@@ -1,6 +1,11 @@
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { dialogBoxCreate } from "../utils/DialogBox";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import {
Generic_fromJSON,
Generic_toJSON,
Reviver
} from "../utils/JSONReviver";
/**
* Represents a plain text file that is typically stored on a server.

View File

@@ -1,104 +1,129 @@
import {formatNumber,
convertTimeMsToTimeElapsedString,
replaceAt} from "../utils/StringHelperFunctions";
import {loxBoxCreate, logBoxUpdateText,
logBoxOpened} from "../utils/LogBox";
import {updateActiveScriptsItems} from "./ActiveScriptsUI";
import { Augmentations } from "./Augmentation/Augmentations";
import { installAugmentations,
initAugmentations,
displayAugmentationsContent,
PlayerOwnedAugmentation } from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import {
formatNumber,
convertTimeMsToTimeElapsedString,
replaceAt
} from "../utils/StringHelperFunctions";
import { loxBoxCreate, logBoxUpdateText, logBoxOpened } from "../utils/LogBox";
import { updateActiveScriptsItems } from "./ActiveScriptsUI";
import { Augmentations } from "./Augmentation/Augmentations";
import {
installAugmentations,
initAugmentations,
displayAugmentationsContent,
PlayerOwnedAugmentation
} from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import {BitNodes, initBitNodes,
initBitNodeMultipliers} from "./BitNode/BitNode";
import {Bladeburner} from "./Bladeburner";
import { CharacterOverviewComponent } from "./ui/React/CharacterOverview";
import {cinematicTextFlag} from "./CinematicText";
import {generateRandomContract} from "./CodingContractGenerator";
import {CompanyPositions} from "./Company/CompanyPositions";
import {initCompanies} from "./Company/Companies";
import { Corporation } from "./Corporation/Corporation";
import {CONSTANTS} from "./Constants";
import {createDevMenu, closeDevMenu} from "./DevMenu";
import { Factions, initFactions } from "./Faction/Factions";
import { displayFactionContent, joinFaction,
processPassiveFactionRepGain,
inviteToFaction } from "./Faction/FactionHelpers";
import { FconfSettings } from "./Fconf/FconfSettings";
import { hasHacknetServers,
renderHacknetNodesUI,
clearHacknetNodesUI,
processHacknetEarnings } from "./Hacknet/HacknetHelpers";
import {iTutorialStart} from "./InteractiveTutorial";
import {initLiterature} from "./Literature";
import { LocationName } from "./Locations/data/LocationNames";
import { LocationRoot } from "./Locations/ui/Root";
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import {inMission, currMission} from "./Missions";
import {initSingularitySFFlags,
hasSingularitySF, hasCorporationSF} from "./NetscriptFunctions";
import {updateOnlineScriptTimes,
runScriptsLoop} from "./NetscriptWorker";
import {Player} from "./Player";
import {prestigeAugmentation,
prestigeSourceFile} from "./Prestige";
import { Programs } from "./Programs/Programs";
import { displayCreateProgramContent,
getNumAvailableCreateProgram,
initCreateProgramButtons } from "./Programs/ProgramHelpers";
import {redPillFlag, hackWorldDaemon} from "./RedPill";
import {saveObject, loadGame} from "./SaveObject";
import { getCurrentEditor,
loadAllRunningScripts,
scriptEditorInit,
updateScriptEditorContent } from "./Script/ScriptHelpers";
import { AllServers,
initForeignServers } from "./Server/AllServers";
import {
BitNodes,
initBitNodes,
initBitNodeMultipliers
} from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner";
import { CharacterOverviewComponent } from "./ui/React/CharacterOverview";
import { cinematicTextFlag } from "./CinematicText";
import { generateRandomContract } from "./CodingContractGenerator";
import { CompanyPositions } from "./Company/CompanyPositions";
import { initCompanies } from "./Company/Companies";
import { Corporation } from "./Corporation/Corporation";
import { CONSTANTS } from "./Constants";
import { createDevMenu, closeDevMenu } from "./DevMenu";
import { Factions, initFactions } from "./Faction/Factions";
import {
displayFactionContent,
joinFaction,
processPassiveFactionRepGain,
inviteToFaction
} from "./Faction/FactionHelpers";
import { FconfSettings } from "./Fconf/FconfSettings";
import {
hasHacknetServers,
renderHacknetNodesUI,
clearHacknetNodesUI,
processHacknetEarnings
} from "./Hacknet/HacknetHelpers";
import { iTutorialStart } from "./InteractiveTutorial";
import { initLiterature } from "./Literature";
import { LocationName } from "./Locations/data/LocationNames";
import { LocationRoot } from "./Locations/ui/Root";
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import { inMission, currMission } from "./Missions";
import {
initSingularitySFFlags,
hasSingularitySF,
hasCorporationSF
} from "./NetscriptFunctions";
import { updateOnlineScriptTimes, runScriptsLoop } from "./NetscriptWorker";
import { Player } from "./Player";
import { prestigeAugmentation, prestigeSourceFile } from "./Prestige";
import { Programs } from "./Programs/Programs";
import {
displayCreateProgramContent,
getNumAvailableCreateProgram,
initCreateProgramButtons
} from "./Programs/ProgramHelpers";
import { redPillFlag, hackWorldDaemon } from "./RedPill";
import { saveObject, loadGame } from "./SaveObject";
import {
getCurrentEditor,
loadAllRunningScripts,
scriptEditorInit,
updateScriptEditorContent
} from "./Script/ScriptHelpers";
import { AllServers, initForeignServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import {Settings} from "./Settings/Settings";
import { initSourceFiles, SourceFiles } from "./SourceFile";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import {SpecialServerIps, initSpecialServerIps} from "./Server/SpecialServerIps";
import {StockMarket, StockSymbols,
SymbolToStockMap, initStockSymbols,
initSymbolToStockMap, stockMarketCycle,
processStockPrices,
displayStockMarketContent} from "./StockMarket/StockMarket";
import {Terminal, postNetburnerText} from "./Terminal";
import { Server } from "./Server/Server";
import { Settings } from "./Settings/Settings";
import { initSourceFiles, SourceFiles } from "./SourceFile";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import {
SpecialServerIps,
initSpecialServerIps
} from "./Server/SpecialServerIps";
import {
StockMarket,
StockSymbols,
SymbolToStockMap,
initStockSymbols,
initSymbolToStockMap,
stockMarketCycle,
processStockPrices,
displayStockMarketContent
} from "./StockMarket/StockMarket";
import { Terminal, postNetburnerText } from "./Terminal";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import { clearSleevesPage,
createSleevesPage,
updateSleevesPage } from "./PersonObjects/Sleeve/SleeveUI";
import { clearResleevesPage,
createResleevesPage } from "./PersonObjects/Resleeving/ResleevingUI";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import {
clearSleevesPage,
createSleevesPage,
updateSleevesPage
} from "./PersonObjects/Sleeve/SleeveUI";
import {
clearResleevesPage,
createResleevesPage
} from "./PersonObjects/Resleeving/ResleevingUI";
import { createStatusText } from "./ui/createStatusText";
import { displayCharacterInfo } from "./ui/displayCharacterInfo";
import {Page, routing} from "./ui/navigationTracking";
import {numeralWrapper} from "./ui/numeralFormat";
import {setSettingsLabels} from "./ui/setSettingsLabels";
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
import { initializeMainMenuLinks,
MainMenuLinks } from "./ui/MainMenu/Links";
import { createStatusText } from "./ui/createStatusText";
import { displayCharacterInfo } from "./ui/displayCharacterInfo";
import { Page, routing } from "./ui/navigationTracking";
import { numeralWrapper } from "./ui/numeralFormat";
import { setSettingsLabels } from "./ui/setSettingsLabels";
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
import { dialogBoxCreate } from "../utils/DialogBox";
import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { createElement } from "../utils/uiHelpers/createElement";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen";
import { KEY } from "../utils/helpers/keyCodes";
import { dialogBoxCreate} from "../utils/DialogBox";
import { gameOptionsBoxClose,
gameOptionsBoxOpen } from "../utils/GameOptions";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
import { createElement } from "../utils/uiHelpers/createElement";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen";
import {KEY} from "../utils/helpers/keyCodes";
import React from "react";
import ReactDOM from "react-dom";
import React from "react";
import ReactDOM from "react-dom";
// These should really be imported with the module that is presenting that UI, but because they very much depend on the
// cascade order, we'll pull them all in here.
@@ -130,7 +155,8 @@ import "../css/grid.min.css";
import "../css/dev-menu.css";
/* Shortcuts to navigate through the game
/**
* Shortcuts to navigate through the game
* Alt-t - Terminal
* Alt-c - Character
* Alt-e - Script editor
@@ -184,7 +210,7 @@ $(document).keydown(function(e) {
e.preventDefault();
Engine.loadCreateProgramContent();
} else if (e.keyCode === KEY.F && e.altKey) {
//Overriden by Fconf
// Overriden by Fconf
if (routing.isOn(Page.Terminal) && FconfSettings.ENABLE_BASH_HOTKEYS) {
return;
}
@@ -209,24 +235,25 @@ const Engine = {
version: "",
Debug: true,
//Clickable objects
// Clickable objects
Clickables: {
//Main menu buttons
// Main menu buttons
saveMainMenuButton: null,
deleteMainMenuButton: null,
},
//Display objects
// Display objects
// TODO-Refactor this into its own component
Display: {
//Progress bar
// Progress bar
progress: null,
//Display for status text (such as "Saved" or "Loaded")
// Display for status text (such as "Saved" or "Loaded")
statusText: null,
hacking_skill: null,
//Main menu content
// Main menu content
terminalContent: null,
characterContent: null,
scriptEditorContent: null,
@@ -235,7 +262,6 @@ const Engine = {
createProgramContent: null,
factionsContent: null,
factionContent: null,
factionAugmentationsContent: null,
augmentationsContent: null,
tutorialContent: null,
infiltrationContent: null,
@@ -246,15 +272,14 @@ const Engine = {
cinematicTextContent: null,
missionContent: null,
//Character info
// Character info
characterInfo: null,
},
//Time variables (milliseconds unix epoch time)
// Time variables (milliseconds unix epoch time)
_lastUpdate: new Date().getTime(),
_idleSpeed: 200, //Speed (in ms) at which the main loop is updated
_idleSpeed: 200, // Speed (in ms) at which the main loop is updated
/* Load content when a main menu button is clicked */
loadTerminalContent: function() {
Engine.hideAllContent();
Engine.Display.terminalContent.style.display = "block";
@@ -400,7 +425,6 @@ const Engine = {
loadWorkInProgressContent: function() {
Engine.hideAllContent();
var mainMenu = document.getElementById("mainmenu-container");
//mainMenu.style.visibility = "hidden";
mainMenu.style.visibility = "hidden";
Engine.Display.workInProgressContent.style.display = "block";
routing.navigateTo(Page.WorkInProgress);
@@ -505,8 +529,8 @@ const Engine = {
clearHacknetNodesUI();
Engine.Display.createProgramContent.style.display = "none";
Engine.Display.factionsContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.factionContent);
Engine.Display.factionContent.style.display = "none";
Engine.Display.factionAugmentationsContent.style.display = "none";
Engine.Display.augmentationsContent.style.display = "none";
Engine.Display.tutorialContent.style.display = "none";
Engine.Display.locationContent.style.display = "none";
@@ -577,15 +601,16 @@ const Engine = {
}
},
/* Display character info */
/// Display character info
updateCharacterInfo: function() {
displayCharacterInfo(Engine.Display.characterInfo, Player);
},
// TODO Refactor this into Faction implementation
displayFactionsInfo: function() {
removeChildrenFromElement(Engine.Display.factionsContent);
//Factions
// Factions
Engine.Display.factionsContent.appendChild(createElement("h1", {
innerText:"Factions"
}));
@@ -595,7 +620,7 @@ const Engine = {
var factionsList = createElement("ul");
Engine.Display.factionsContent.appendChild(createElement("br"));
//Add a button for each faction you are a member of
// Add a button for each faction you are a member of
for (var i = 0; i < Player.factions.length; ++i) {
(function () {
var factionName = Player.factions[i];
@@ -610,12 +635,12 @@ const Engine = {
}
}));
factionsList.appendChild(createElement("br"));
}()); //Immediate invocation
}()); // Immediate invocation
}
Engine.Display.factionsContent.appendChild(factionsList);
Engine.Display.factionsContent.appendChild(createElement("br"));
//Invited Factions
// Invited Factions
Engine.Display.factionsContent.appendChild(createElement("h1", {
innerText:"Outstanding Faction Invitations"
}));
@@ -627,7 +652,7 @@ const Engine = {
}));
var invitationsList = createElement("ul");
//Add a button to accept for each faction you have invitiations for
// Add a button to accept for each faction you have invitiations for
for (var i = 0; i < Player.factionInvitations.length; ++i) {
(function () {
var factionName = Player.factionInvitations[i];
@@ -660,18 +685,18 @@ const Engine = {
Engine.Display.factionsContent.appendChild(invitationsList);
},
/* Main Event Loop */
// Main Game Loop
idleTimer: function() {
//Get time difference
// Get time difference
var _thisUpdate = new Date().getTime();
var diff = _thisUpdate - Engine._lastUpdate;
var offset = diff % Engine._idleSpeed;
//Divide this by cycle time to determine how many cycles have elapsed since last update
// Divide this by cycle time to determine how many cycles have elapsed since last update
diff = Math.floor(diff / Engine._idleSpeed);
if (diff > 0) {
//Update the game engine by the calculated number of cycles
// Update the game engine by the calculated number of cycles
Engine._lastUpdate = _thisUpdate - offset;
Player.lastUpdate = _thisUpdate - offset;
Engine.updateGame(diff);
@@ -689,7 +714,7 @@ const Engine = {
Player.playtimeSinceLastAug += time;
Player.playtimeSinceLastBitnode += time;
//Start Manual hack
// Start Manual hack
if (Terminal.actionStarted === true) {
Engine._totalActionTime = Terminal.actionTime;
Engine._actionTimeLeft = Terminal.actionTime;
@@ -700,7 +725,7 @@ const Engine = {
Terminal.actionStarted = false;
}
//Working
// Working
if (Player.isWorking) {
if (Player.workType == CONSTANTS.WorkTypeFaction) {
Player.workForFaction(numCycles);
@@ -722,20 +747,19 @@ const Engine = {
processStockPrices(numCycles);
}
//Gang, if applicable
if (Player.bitNodeN == 2 && Player.inGang()) {
// Gang, if applicable
if (Player.inGang()) {
Player.gang.process(numCycles, Player);
}
//Mission
// Mission
if (inMission && currMission) {
currMission.process(numCycles);
}
//Corporation
// Corporation
if (Player.corporation instanceof Corporation) {
//Stores cycles in a "buffer". Processed separately using Engine Counters
//This is to avoid constant DOM redraws when Corporation is catching up
// Stores cycles in a "buffer". Processed separately using Engine Counters
Player.corporation.storeCycles(numCycles);
}
@@ -757,38 +781,41 @@ const Engine = {
}
}
//Counters
// Counters
Engine.decrementAllCounters(numCycles);
Engine.checkCounters();
//Manual hacks
// Manual hacks
if (Engine._actionInProgress == true) {
Engine.updateHackProgress(numCycles);
}
//Update the running time of all active scripts
// Update the running time of all active scripts
updateOnlineScriptTimes(numCycles);
//Hacknet Nodes
// Hacknet Nodes
processHacknetEarnings(numCycles);
},
//Counters for the main event loop. Represent the number of game cycles are required
//for something to happen.
/**
* Counters for the main event loop. Represent the number of game cycles that
* are required for something to happen. These counters are in game cycles,
* which is once every 200ms
*/
Counters: {
autoSaveCounter: 300, //Autosave every minute
updateSkillLevelsCounter: 10, //Only update skill levels every 2 seconds. Might improve performance
autoSaveCounter: 300,
updateSkillLevelsCounter: 10,
updateDisplays: 3,
updateDisplaysMed: 9,
updateDisplaysLong: 15,
updateActiveScriptsDisplay: 5,
createProgramNotifications: 10, //Checks whether any programs can be created and notifies
checkFactionInvitations: 100, //Check whether you qualify for any faction invitations
createProgramNotifications: 10,
checkFactionInvitations: 100,
passiveFactionGrowth: 600,
messages: 150,
sCr: 1500,
mechanicProcess: 5, //Processes certain mechanics (Corporation, Bladeburner)
contractGeneration: 3000 //Generate Coding Contracts
mechanicProcess: 5, // Processes certain mechanics (Corporation, Bladeburner)
contractGeneration: 3000, // Generate Coding Contracts
},
decrementAllCounters: function(numCycles = 1) {
@@ -799,8 +826,10 @@ const Engine = {
}
},
//Checks if any counters are 0 and if they are, executes whatever
//is necessary and then resets the counter
/**
* Checks if any counters are 0. If they are, executes whatever
* is necessary and then resets the counter
*/
checkCounters: function() {
if (Engine.Counters.autoSaveCounter <= 0) {
if (Settings.AutosaveInterval == null) {
@@ -820,7 +849,7 @@ const Engine = {
}
if (Engine.Counters.updateActiveScriptsDisplay <= 0) {
//Always update, but make the interval longer if the page isn't active
// Always update, but make the interval longer if the page isn't active
updateActiveScriptsItems();
if (routing.isOn(Page.ActiveScripts)) {
Engine.Counters.updateActiveScriptsDisplay = 5;
@@ -900,7 +929,7 @@ const Engine = {
if (Engine.Counters.messages <= 0) {
checkForMessagesToSend();
if (Augmentations[AugmentationNames.TheRedPill].owned) {
Engine.Counters.messages = 4500; //15 minutes for Red pill message
Engine.Counters.messages = 4500; // 15 minutes for Red pill message
} else {
Engine.Counters.messages = 150;
}
@@ -937,7 +966,8 @@ const Engine = {
}
},
/* Calculates the hack progress for a manual (non-scripted) hack and updates the progress bar/time accordingly */
// Calculates the hack progress for a manual (non-scripted) hack and updates the progress bar/time accordingly
// TODO Refactor this into Terminal module
_totalActionTime: 0,
_actionTimeLeft: 0,
_actionTimeStr: "Time left: ",
@@ -946,34 +976,36 @@ const Engine = {
_actionInProgress: false,
updateHackProgress: function(numCycles = 1) {
var timeElapsedMilli = numCycles * Engine._idleSpeed;
Engine._actionTimeLeft -= (timeElapsedMilli/ 1000); //Substract idle speed (ms)
Engine._actionTimeLeft -= (timeElapsedMilli/ 1000); // Substract idle speed (ms)
Engine._actionTimeLeft = Math.max(Engine._actionTimeLeft, 0);
//Calculate percent filled
// Calculate percent filled
var percent = Math.round((1 - Engine._actionTimeLeft / Engine._totalActionTime) * 100);
//Update progress bar
// Update progress bar
while (Engine._actionProgressBarCount * 2 <= percent) {
Engine._actionProgressStr = replaceAt(Engine._actionProgressStr, Engine._actionProgressBarCount, "|");
Engine._actionProgressBarCount += 1;
}
//Update hack time remaining
// Update hack time remaining
Engine._actionTimeStr = "Time left: " + Math.max(0, Math.round(Engine._actionTimeLeft)).toString() + "s";
document.getElementById("hack-progress").innerHTML = Engine._actionTimeStr;
//Dynamically update progress bar
// Dynamically update progress bar
document.getElementById("hack-progress-bar").innerHTML = Engine._actionProgressStr.replace( / /g, "&nbsp;" );
//Once percent is 100, the hack is completed
// Once percent is 100, the hack is completed
if (percent >= 100) {
Engine._actionInProgress = false;
Terminal.finishAction();
}
},
//Used when initializing a game
//elems should be an array of all DOM elements under the header
/**
* Collapses a main menu header. Used when initializing the game.
* @param elems {HTMLElement[]} Elements under header
*/
closeMainMenuHeader: function(elems) {
for (var i = 0; i < elems.length; ++i) {
elems[i].style.maxHeight = null;
@@ -982,8 +1014,10 @@ const Engine = {
}
},
//Used when initializing the game
//elems should be an array of all DOM elements under the header
/**
* Expands a main menu header. Used when initializing the game.
* @param elems {HTMLElement[]} Elements under header
*/
openMainMenuHeader: function(elems) {
for (var i = 0; i < elems.length; ++i) {
elems[i].style.maxHeight = elems[i].scrollHeight + "px";
@@ -991,10 +1025,12 @@ const Engine = {
}
},
//Used in game when clicking on a main menu header (NOT FOR INITIALIZATION)
//open is a boolean specifying whether its being opened or closed
//elems is an array of DOM elements for main menu tabs (li)
//links is an array of DOM elements for main menu links (a)
/**
* Used in game when clicking on a main menu header (NOT used for initialization)
* @param open {boolean} Whether header is being opened or closed
* @param elems {HTMLElement[]} li Elements under header
* @param links {HTMLElement[]} a elements under header
*/
toggleMainMenuHeader: function(open, elems, links) {
for (var i = 0; i < elems.length; ++i) {
if (open) {
@@ -1020,35 +1056,34 @@ const Engine = {
},
load: function(saveString) {
//Initialize main menu accordion panels to all start as "open"
var terminal = document.getElementById("terminal-tab");
var createScript = document.getElementById("create-script-tab");
var activeScripts = document.getElementById("active-scripts-tab");
var createProgram = document.getElementById("create-program-tab");
var stats = document.getElementById("stats-tab");
var factions = document.getElementById("factions-tab");
var augmentations = document.getElementById("augmentations-tab");
var hacknetnodes = document.getElementById("hacknet-nodes-tab");
var city = document.getElementById("city-tab");
var travel = document.getElementById("travel-tab");
var job = document.getElementById("job-tab");
var stockmarket = document.getElementById("stock-market-tab");
var bladeburner = document.getElementById("bladeburner-tab");
var corp = document.getElementById("corporation-tab");
var gang = document.getElementById("gang-tab");
var tutorial = document.getElementById("tutorial-tab");
var options = document.getElementById("options-tab");
var dev = document.getElementById("dev-tab");
// Initialize main menu accordion panels to all start as "open"
const terminal = document.getElementById("terminal-tab");
const createScript = document.getElementById("create-script-tab");
const activeScripts = document.getElementById("active-scripts-tab");
const createProgram = document.getElementById("create-program-tab");
const stats = document.getElementById("stats-tab");
const factions = document.getElementById("factions-tab");
const augmentations = document.getElementById("augmentations-tab");
const hacknetnodes = document.getElementById("hacknet-nodes-tab");
const city = document.getElementById("city-tab");
const travel = document.getElementById("travel-tab");
const job = document.getElementById("job-tab");
const stockmarket = document.getElementById("stock-market-tab");
const bladeburner = document.getElementById("bladeburner-tab");
const corp = document.getElementById("corporation-tab");
const gang = document.getElementById("gang-tab");
const tutorial = document.getElementById("tutorial-tab");
const options = document.getElementById("options-tab");
const dev = document.getElementById("dev-tab");
//Load game from save or create new game
// Load game from save or create new game
if (loadGame(saveString)) {
console.log("Loaded game from save");
initBitNodes();
initBitNodeMultipliers(Player);
initSourceFiles();
Engine.setDisplayElements(); //Sets variables for important DOM elements
Engine.init(); //Initialize buttons, work, etc.
initAugmentations(); //Also calls Player.reapplyAllAugmentations()
Engine.setDisplayElements(); // Sets variables for important DOM elements
Engine.init(); // Initialize buttons, work, etc.
initAugmentations(); // Also calls Player.reapplyAllAugmentations()
Player.reapplyAllSourceFiles();
initStockSymbols();
if (Player.hasWseAccount) {
@@ -1058,13 +1093,13 @@ const Engine = {
initSingularitySFFlags();
updateSourceFileFlags(Player);
//Calculate the number of cycles have elapsed while offline
// Calculate the number of cycles have elapsed while offline
Engine._lastUpdate = new Date().getTime();
var lastUpdate = Player.lastUpdate;
var numCyclesOffline = Math.floor((Engine._lastUpdate - lastUpdate) / Engine._idleSpeed);
/* Process offline progress */
var offlineProductionFromScripts = loadAllRunningScripts(); //This also takes care of offline production for those scripts
// Process offline progress
var offlineProductionFromScripts = loadAllRunningScripts(); // This also takes care of offline production for those scripts
if (Player.isWorking) {
console.log("work() called in load() for " + numCyclesOffline * Engine._idleSpeed + " milliseconds");
if (Player.workType == CONSTANTS.WorkTypeFaction) {
@@ -1082,13 +1117,13 @@ const Engine = {
}
}
//Hacknet Nodes offline progress
// Hacknet Nodes offline progress
var offlineProductionFromHacknetNodes = processHacknetEarnings(numCyclesOffline);
const hacknetProdInfo = hasHacknetServers() ?
`${numeralWrapper.format(offlineProductionFromHacknetNodes, "0.000a")} hashes` :
`${numeralWrapper.formatMoney(offlineProductionFromHacknetNodes)}`;
//Passive faction rep gain offline
// Passive faction rep gain offline
processPassiveFactionRepGain(numCyclesOffline);
// Stock Market offline progress
@@ -1097,7 +1132,7 @@ const Engine = {
}
// Gang progress for BitNode 2
if (Player.bitNodeN != null && Player.bitNodeN === 2 && Player.inGang()) {
if (Player.inGang()) {
Player.gang.process(numCyclesOffline, Player);
}
@@ -1125,7 +1160,7 @@ const Engine = {
}
}
//Update total playtime
// Update total playtime
var time = numCyclesOffline * Engine._idleSpeed;
if (Player.totalPlaytime == null) {Player.totalPlaytime = 0;}
if (Player.playtimeSinceLastAug == null) {Player.playtimeSinceLastAug = 0;}
@@ -1135,14 +1170,14 @@ const Engine = {
Player.playtimeSinceLastBitnode += time;
Player.lastUpdate = Engine._lastUpdate;
Engine.start(); //Run main game loop and Scripts loop
Engine.start(); // Run main game loop and Scripts loop
removeLoadingScreen();
const timeOfflineString = convertTimeMsToTimeElapsedString(time);
dialogBoxCreate(`Offline for ${timeOfflineString}. While you were offline, your scripts ` +
"generated <span class='money-gold'>" +
numeralWrapper.formatMoney(offlineProductionFromScripts) + "</span> " +
"and your Hacknet Nodes generated <span class='money-gold'>" + hacknetProdInfo + "</span>");
//Close main menu accordions for loaded game
// Close main menu accordions for loaded game
var visibleMenuTabs = [terminal, createScript, activeScripts, stats,
hacknetnodes, city, tutorial, options, dev];
if (Player.firstFacInvRecvd) {visibleMenuTabs.push(factions);}
@@ -1166,14 +1201,14 @@ const Engine = {
Engine.closeMainMenuHeader(visibleMenuTabs);
} else {
//No save found, start new game
// No save found, start new game
console.log("Initializing new game");
initBitNodes();
initBitNodeMultipliers(Player);
initSourceFiles();
initSpecialServerIps();
Engine.setDisplayElements(); //Sets variables for important DOM elements
Engine.start(); //Run main game loop and Scripts loop
Engine.setDisplayElements(); // Sets variables for important DOM elements
Engine.start(); // Run main game loop and Scripts loop
Player.init();
initForeignServers(Player.getHomeComputer());
initCompanies();
@@ -1184,18 +1219,17 @@ const Engine = {
initLiterature();
initSingularitySFFlags();
//Open main menu accordions for new game
//Main menu accordions
var hackingHdr = document.getElementById("hacking-menu-header");
// Open main menu accordions for new game
const hackingHdr = document.getElementById("hacking-menu-header");
hackingHdr.classList.toggle("opened");
var characterHdr = document.getElementById("character-menu-header");
const characterHdr = document.getElementById("character-menu-header");
characterHdr.classList.toggle("opened");
var worldHdr = document.getElementById("world-menu-header");
const worldHdr = document.getElementById("world-menu-header");
worldHdr.classList.toggle("opened");
var helpHdr = document.getElementById("help-menu-header");
const helpHdr = document.getElementById("help-menu-header");
helpHdr.classList.toggle("opened");
//Hide tabs that wont be revealed until later
// Hide tabs that wont be revealed until later
factions.style.display = "none";
augmentations.style.display = "none";
job.style.display = "none";
@@ -1213,18 +1247,18 @@ const Engine = {
tutorial, options]
);
//Start interactive tutorial
// Start interactive tutorial
iTutorialStart();
removeLoadingScreen();
}
//Initialize labels on game settings
// Initialize labels on game settings
setSettingsLabels();
scriptEditorInit();
Terminal.resetTerminalInput();
},
setDisplayElements: function() {
//Content elements
// Content elements
Engine.Display.terminalContent = document.getElementById("terminal-container");
routing.navigateTo(Page.Terminal);
@@ -1246,13 +1280,9 @@ const Engine = {
Engine.Display.factionsContent = document.getElementById("factions-container");
Engine.Display.factionsContent.style.display = "none";
Engine.Display.factionContent = document.getElementById("faction-container");
Engine.Display.factionContent.style.display = "none";
Engine.Display.factionAugmentationsContent = document.getElementById("faction-augmentations-container");
Engine.Display.factionAugmentationsContent.style.display = "none";
Engine.Display.augmentationsContent = document.getElementById("augmentations-container");
Engine.Display.augmentationsContent.style.display = "none";
@@ -1269,22 +1299,22 @@ const Engine = {
Engine.Display.missionContent = document.getElementById("mission-container");
Engine.Display.missionContent.style.display = "none";
//Character info
// Character info
Engine.Display.characterInfo = document.getElementById("character-content");
//Location page (page that shows up when you visit a specific location in World)
// Location page (page that shows up when you visit a specific location in World)
Engine.Display.locationContent = document.getElementById("location-container");
Engine.Display.locationContent.style.display = "none";
//Work In Progress
// Work In Progress
Engine.Display.workInProgressContent = document.getElementById("work-in-progress-container");
Engine.Display.workInProgressContent.style.display = "none";
//Red Pill / Hack World Daemon
// Red Pill / Hack World Daemon
Engine.Display.redPillContent = document.getElementById("red-pill-container");
Engine.Display.redPillContent.style.display = "none";
//Cinematic Text
// Cinematic Text
Engine.Display.cinematicTextContent = document.getElementById("cinematic-text-container");
Engine.Display.cinematicTextContent.style.display = "none";
@@ -1298,9 +1328,9 @@ const Engine = {
}
},
/* Initialization */
// Initialization
init: function() {
//Import game link
// Import game link
document.getElementById("import-game-link").onclick = function() {
saveObject.importGame();
};
@@ -1409,10 +1439,10 @@ const Engine = {
return false;
});
//Active scripts list
// Active scripts list
Engine.ActiveScriptsList = document.getElementById("active-scripts-list");
//Save, Delete, Import/Export buttons
// Save, Delete, Import/Export buttons
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
Engine.Clickables.saveMainMenuButton.addEventListener("click", function() {
saveObject.saveGame(indexedDb);
@@ -1430,7 +1460,7 @@ const Engine = {
return false;
});
//Character Overview buttons
// Character Overview buttons
document.getElementById("character-overview-save-button").addEventListener("click", function() {
saveObject.saveGame(indexedDb);
return false;
@@ -1441,13 +1471,13 @@ const Engine = {
return false;
});
//Create Program buttons
// Create Program buttons
initCreateProgramButtons();
//Message at the top of terminal
// Message at the top of terminal
postNetburnerText();
//Player was working cancel button
// Player was working cancel button
if (Player.isWorking) {
var cancelButton = document.getElementById("work-in-progress-cancel-button");
cancelButton.addEventListener("click", function() {
@@ -1469,10 +1499,10 @@ const Engine = {
Engine.loadWorkInProgressContent();
}
//character overview screen
// Character overview screen
document.getElementById("character-overview-container").style.display = "block";
//Remove classes from links (they might be set from tutorial)
// Remove classes from links (they might be set from tutorial)
document.getElementById("terminal-menu-link").removeAttribute("class");
document.getElementById("stats-menu-link").removeAttribute("class");
document.getElementById("create-script-menu-link").removeAttribute("class");
@@ -1518,7 +1548,7 @@ const Engine = {
}
});
//DEBUG Delete active Scripts on home
// DEBUG Delete active Scripts on home
document.getElementById("debug-delete-scripts-link").addEventListener("click", function() {
console.log("Deleting running scripts on home computer");
Player.getHomeComputer().runningScripts = [];
@@ -1527,7 +1557,7 @@ const Engine = {
return false;
});
//DEBUG Soft Reset
// DEBUG Soft Reset
document.getElementById("debug-soft-reset").addEventListener("click", function() {
dialogBoxCreate("Soft Reset!");
prestigeAugmentation();
@@ -1537,10 +1567,10 @@ const Engine = {
},
start: function() {
//Run main loop
// Run main loop
Engine.idleTimer();
//Scripts
// Script-processing loop
runScriptsLoop();
}
};
@@ -1548,18 +1578,20 @@ const Engine = {
var indexedDb, indexedDbRequest;
window.onload = function() {
if (!window.indexedDB) {
return Engine.load(null); //Will try to load from localstorage
return Engine.load(null); // Will try to load from localstorage
}
//DB is called bitburnerSave
//Object store is called savestring
//key for the Object store is called save
/**
* DB is called bitburnerSave
* Object store is called savestring
* key for the Object store is called save
*/
indexedDbRequest = window.indexedDB.open("bitburnerSave", 1);
indexedDbRequest.onerror = function(e) {
console.log("Error opening indexedDB: ");
console.log(e);
return Engine.load(null); //Try to load from localstorage
return Engine.load(null); // Try to load from localstorage
};
indexedDbRequest.onsuccess = function(e) {
@@ -1570,11 +1602,11 @@ window.onload = function() {
var request = objectStore.get("save");
request.onerror = function(e) {
console.log("Error in Database request to get savestring: " + e);
return Engine.load(null); //Try to load from localstorage
return Engine.load(null); // Try to load from localstorage
}
request.onsuccess = function(e) {
Engine.load(request.result); //Is this right?
Engine.load(request.result);
}
};

View File

@@ -117,7 +117,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag"> <strong style="background-color:#555;">Script name: </strong></p>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
<input id="script-editor-filename" type="text" maxlength="100" tabindex="1" />
</div>
<div id="ace-editor"></div>
@@ -223,8 +223,6 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<!-- Single Faction info (when you select a faction from the Factions menu) -->
<div id="faction-container" class="generic-menupage-container"></div>
<div id="faction-augmentations-container" class="generic-menupage-container"></div>
<!-- Augmentations -->
<div id="augmentations-container" class="generic-menupage-container"></div>

View File

@@ -0,0 +1,74 @@
/**
* Basic paragraph (p Element) that automatically re-renders itself every X seconds
*
* NOT recommended for usage - only if you really have to
*/
import * as React from "react";
interface IProps {
intervalTime?: number;
style?: object;
text: string;
tooltip?: string;
}
interface IState {
i: number;
}
type IInnerHTMLMarkup = {
__html: string;
}
export class AutoupdatingParagraph extends React.Component<IProps, IState> {
/**
* Timer ID for auto-updating implementation (returned value from setInterval())
*/
interval: number = 0;
constructor(props: IProps) {
super(props);
this.state = {
i: 0,
}
}
componentDidMount() {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = setInterval(() => this.tick(), time);
}
componentWillUnmount() {
clearInterval(this.interval);
}
tick() {
this.setState(prevState => ({
i: prevState.i + 1
}));
}
render() {
const hasTooltip = this.props.tooltip != null && this.props.tooltip !== "";
const className = "tooltip";
// Tooltip will be set using inner HTML
let tooltipMarkup: IInnerHTMLMarkup | null;
if (hasTooltip) {
tooltipMarkup = {
__html: this.props.tooltip!
}
}
return (
<p className={className} style={this.props.style}>
{this.props.text}
{
hasTooltip &&
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
}
</p>
)
}
}

View File

@@ -12,7 +12,7 @@ export interface IParagraphWithTooltipProps {
export class ParagraphWithTooltip extends React.Component<IParagraphWithTooltipProps, any> {
render() {
return (
<p className={"tooltip"}>
<p className={"tooltip"} style={this.props.style}>
{this.props.text}
<span className={"tooltiptext"}>
{this.props.tooltip}

View File

@@ -1,7 +1,11 @@
/* Pop up Dialog Box */
/**
* Create and display a pop-up dialog box.
* This dialog box does not allow for any interaction and should close when clicking
* outside of it
*/
let dialogBoxes = [];
//Close dialog box when clicking outside
// Close dialog box when clicking outside
$(document).click(function(event) {
if (dialogBoxOpened && dialogBoxes.length >= 1) {
if (!$(event.target).closest(dialogBoxes[0]).length){
@@ -17,7 +21,7 @@ $(document).click(function(event) {
});
//Dialog box close buttons
// Dialog box close buttons
$(document).on('click', '.dialog-box-close-button', function( event ) {
if (dialogBoxOpened && dialogBoxes.length >= 1) {
dialogBoxes[0].remove();
@@ -30,9 +34,10 @@ $(document).on('click', '.dialog-box-close-button', function( event ) {
}
});
var dialogBoxOpened = false;
let dialogBoxOpened = false;
function dialogBoxCreate(txt, preformatted=false) {
console.log(`dialogBoxCreate() called`)
var container = document.createElement("div");
container.setAttribute("class", "dialog-box-container");