Merge pull request #1376 from danielyxie/dev

Almost everything in typescript.
This commit is contained in:
hydroflame 2021-09-24 19:10:30 -04:00 committed by GitHub
commit b5c105b6fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 1603 additions and 1621 deletions

38
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -5,7 +5,7 @@ function createWindow() {
const win = new BrowserWindow({ const win = new BrowserWindow({
show: false, show: false,
webPreferences: { webPreferences: {
devTools: false, devTools: true,
}, },
}); });
@ -13,7 +13,7 @@ function createWindow() {
win.maximize(); win.maximize();
win.loadFile("index.html"); win.loadFile("index.html");
win.show(); win.show();
// win.webContents.openDevTools(); win.webContents.openDevTools();
globalShortcut.register("f5", function () { globalShortcut.register("f5", function () {
win.loadFile("index.html"); win.loadFile("index.html");
}); });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11
package-lock.json generated

@ -16,6 +16,7 @@
"@mui/lab": "^5.0.0-alpha.46", "@mui/lab": "^5.0.0-alpha.46",
"@mui/material": "^5.0.0-rc.1", "@mui/material": "^5.0.0-rc.1",
"@mui/styles": "^5.0.0-rc.1", "@mui/styles": "^5.0.0-rc.1",
"@types/escodegen": "^0.0.7",
"@types/js-beautify": "^1.13.2", "@types/js-beautify": "^1.13.2",
"@types/numeral": "0.0.25", "@types/numeral": "0.0.25",
"@types/react": "^17.0.21", "@types/react": "^17.0.21",
@ -4522,6 +4523,11 @@
"@babel/types": "^7.3.0" "@babel/types": "^7.3.0"
} }
}, },
"node_modules/@types/escodegen": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@types/escodegen/-/escodegen-0.0.7.tgz",
"integrity": "sha512-46oENdSRNEJXCNrPJoC3vRolZJpfeEm7yvATkd2bCncKFG0PUEyfBCaoacfpcXH4Y5RRuqdVj3J7TI+wwn2SbQ=="
},
"node_modules/@types/file-saver": { "node_modules/@types/file-saver": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.3.tgz",
@ -31410,6 +31416,11 @@
"@babel/types": "^7.3.0" "@babel/types": "^7.3.0"
} }
}, },
"@types/escodegen": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@types/escodegen/-/escodegen-0.0.7.tgz",
"integrity": "sha512-46oENdSRNEJXCNrPJoC3vRolZJpfeEm7yvATkd2bCncKFG0PUEyfBCaoacfpcXH4Y5RRuqdVj3J7TI+wwn2SbQ=="
},
"@types/file-saver": { "@types/file-saver": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.3.tgz",

@ -17,6 +17,7 @@
"@mui/lab": "^5.0.0-alpha.46", "@mui/lab": "^5.0.0-alpha.46",
"@mui/material": "^5.0.0-rc.1", "@mui/material": "^5.0.0-rc.1",
"@mui/styles": "^5.0.0-rc.1", "@mui/styles": "^5.0.0-rc.1",
"@types/escodegen": "^0.0.7",
"@types/js-beautify": "^1.13.2", "@types/js-beautify": "^1.13.2",
"@types/numeral": "0.0.25", "@types/numeral": "0.0.25",
"@types/react": "^17.0.21", "@types/react": "^17.0.21",

@ -10,7 +10,7 @@ import { Money } from "../ui/React/Money";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
interface IConstructorParams { export interface IConstructorParams {
info: string | JSX.Element; info: string | JSX.Element;
stats?: JSX.Element; stats?: JSX.Element;
isSpecial?: boolean; isSpecial?: boolean;

@ -1,2 +0,0 @@
export declare function isRepeatableAug(aug: Augmentation): boolean;
export declare function installAugmentations(): void;

@ -1,6 +1,6 @@
import { Augmentation } from "./Augmentation"; import { Augmentation, IConstructorParams } from "./Augmentation";
import { Augmentations } from "./Augmentations"; import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
@ -18,13 +18,13 @@ import { WHRNG } from "../Casino/RNG";
import React from "react"; import React from "react";
function AddToAugmentations(aug) { function AddToAugmentations(aug: Augmentation): void {
var name = aug.name; const name = aug.name;
Augmentations[name] = aug; Augmentations[name] = aug;
} }
function getRandomBonus() { function getRandomBonus(): any {
var bonuses = [ const bonuses = [
{ {
bonuses: { bonuses: {
hacking_chance_mult: 1.25, hacking_chance_mult: 1.25,
@ -124,7 +124,7 @@ function initAugmentations() {
//Time-Based Augment Test //Time-Based Augment Test
const randomBonuses = getRandomBonus(); const randomBonuses = getRandomBonus();
const UnstableCircadianModulatorParams = { const UnstableCircadianModulatorParams: IConstructorParams = {
name: AugmentationNames.UnstableCircadianModulator, name: AugmentationNames.UnstableCircadianModulator,
moneyCost: 5e9, moneyCost: 5e9,
repCost: 3.625e5, repCost: 3.625e5,
@ -133,7 +133,7 @@ function initAugmentations() {
"unpredictable results based on your circadian rhythm.", "unpredictable results based on your circadian rhythm.",
}; };
Object.keys(randomBonuses.bonuses).forEach( Object.keys(randomBonuses.bonuses).forEach(
(key) => (UnstableCircadianModulatorParams[key] = randomBonuses.bonuses[key]), (key) => ((UnstableCircadianModulatorParams as any)[key] = randomBonuses.bonuses[key]),
); );
const UnstableCircadianModulator = new Augmentation(UnstableCircadianModulatorParams); const UnstableCircadianModulator = new Augmentation(UnstableCircadianModulatorParams);
@ -2359,7 +2359,7 @@ function initAugmentations() {
} }
//Resets an Augmentation during (re-initizliation) //Resets an Augmentation during (re-initizliation)
function resetAugmentation(newAugObject) { function resetAugmentation(newAugObject: Augmentation): void {
if (!(newAugObject instanceof Augmentation)) { if (!(newAugObject instanceof Augmentation)) {
throw new Error("Invalid argument 'newAugObject' passed into resetAugmentation"); throw new Error("Invalid argument 'newAugObject' passed into resetAugmentation");
} }
@ -2370,18 +2370,15 @@ function resetAugmentation(newAugObject) {
AddToAugmentations(newAugObject); AddToAugmentations(newAugObject);
} }
function applyAugmentation(aug, reapply = false) { function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void {
Augmentations[aug.name].owned = true; Augmentations[aug.name].owned = true;
const augObj = Augmentations[aug.name]; const augObj = Augmentations[aug.name];
// Apply multipliers // Apply multipliers
for (const mult in augObj.mults) { for (const mult in augObj.mults) {
if (Player[mult] == null) { const v = Player.getMult(mult) * augObj.mults[mult];
console.warn(`Augmentation has unrecognized multiplier property: ${mult}`); Player.setMult(mult, v);
} else {
Player[mult] *= augObj.mults[mult];
}
} }
// Special logic for NeuroFlux Governor // Special logic for NeuroFlux Governor
@ -2445,11 +2442,11 @@ function installAugmentations() {
prestigeAugmentation(); prestigeAugmentation();
} }
function augmentationExists(name) { function augmentationExists(name: string) {
return Augmentations.hasOwnProperty(name); return Augmentations.hasOwnProperty(name);
} }
export function isRepeatableAug(aug) { export function isRepeatableAug(aug: Augmentation): boolean {
const augName = aug instanceof Augmentation ? aug.name : aug; const augName = aug instanceof Augmentation ? aug.name : aug;
if (augName === AugmentationNames.NeuroFluxGovernor) { if (augName === AugmentationNames.NeuroFluxGovernor) {

@ -6,13 +6,11 @@ import { AllPages } from "./AllPages";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
interface IProps { export function BladeburnerRoot(): React.ReactElement {
bladeburner: IBladeburner;
}
export function BladeburnerRoot(props: IProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
const router = use.Router(); const router = use.Router();
const bladeburner = player.bladeburner;
if (bladeburner === null) return <></>;
return ( return (
<div className="bladeburner-container"> <div className="bladeburner-container">
<div style={{ height: "60%", display: "block", position: "relative" }}> <div style={{ height: "60%", display: "block", position: "relative" }}>
@ -24,9 +22,9 @@ export function BladeburnerRoot(props: IProps): React.ReactElement {
border: "1px solid white", border: "1px solid white",
}} }}
> >
<Stats bladeburner={props.bladeburner} player={player} router={router} /> <Stats bladeburner={bladeburner} player={player} router={router} />
</div> </div>
<Console bladeburner={props.bladeburner} player={player} /> <Console bladeburner={bladeburner} player={player} />
</div> </div>
<div <div
style={{ style={{
@ -38,7 +36,7 @@ export function BladeburnerRoot(props: IProps): React.ReactElement {
position: "relative", position: "relative",
}} }}
> >
<AllPages bladeburner={props.bladeburner} player={player} /> <AllPages bladeburner={bladeburner} player={player} />
</div> </div>
</div> </div>
); );

@ -10,6 +10,7 @@ import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { MainPanel } from "./MainPanel"; import { MainPanel } from "./MainPanel";
import { Industries } from "../IndustryData"; import { Industries } from "../IndustryData";
import { use } from "../../ui/Context";
interface IExpandButtonProps { interface IExpandButtonProps {
corp: ICorporation; corp: ICorporation;
@ -38,12 +39,10 @@ function ExpandButton(props: IExpandButtonProps): React.ReactElement {
return <HeaderTab current={false} onClick={openNewIndustryPopup} text={"Expand into new Industry"} />; return <HeaderTab current={false} onClick={openNewIndustryPopup} text={"Expand into new Industry"} />;
} }
interface IProps { export function CorporationRoot(): React.ReactElement {
corp: ICorporation; const player = use.Player();
player: IPlayer; const corporation = player.corporation;
} if (corporation === null) return <></>;
export function CorporationRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
@ -62,9 +61,9 @@ export function CorporationRoot(props: IProps): React.ReactElement {
current={divisionName === "Overview"} current={divisionName === "Overview"}
key={"overview"} key={"overview"}
onClick={() => setDivisionName("Overview")} onClick={() => setDivisionName("Overview")}
text={props.corp.name} text={corporation.name}
/> />
{props.corp.divisions.map((division: IIndustry) => ( {corporation.divisions.map((division: IIndustry) => (
<HeaderTab <HeaderTab
current={division.name === divisionName} current={division.name === divisionName}
key={division.name} key={division.name}
@ -72,9 +71,9 @@ export function CorporationRoot(props: IProps): React.ReactElement {
text={division.name} text={division.name}
/> />
))} ))}
<ExpandButton corp={props.corp} setDivisionName={setDivisionName} /> <ExpandButton corp={corporation} setDivisionName={setDivisionName} />
</div> </div>
<MainPanel rerender={rerender} corp={props.corp} divisionName={divisionName} player={props.player} /> <MainPanel rerender={rerender} corp={corporation} divisionName={divisionName} player={player} />
</div> </div>
); );
} }

@ -2,6 +2,7 @@ import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve"; import { IPlayerOrSleeve } from "../PersonObjects/IPlayerOrSleeve";
import { IRouter } from "../ui/Router"; import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript";
export interface IConstructorParams { export interface IConstructorParams {
hacking_success_weight?: number; hacking_success_weight?: number;
@ -86,7 +87,7 @@ export class Crime {
this.kills = params.kills ? params.kills : 0; this.kills = params.kills ? params.kills : 0;
} }
commit(router: IRouter, p: IPlayer, div = 1, singParams: any = null): number { commit(router: IRouter, p: IPlayer, div = 1, workerScript: WorkerScript | null = null): number {
if (div <= 0) { if (div <= 0) {
div = 1; div = 1;
} }
@ -101,7 +102,7 @@ export class Crime {
this.charisma_exp / div, this.charisma_exp / div,
this.money / div, this.money / div,
this.time, this.time,
singParams, workerScript,
); );
return this.time; return this.time;

@ -14,7 +14,8 @@ export function checkIfConnectedToDarkweb(): void {
if (!isValidIPAddress(darkwebIp)) { if (!isValidIPAddress(darkwebIp)) {
return; return;
} }
if (darkwebIp == Player.getCurrentServer().ip) { const server = Player.getCurrentServer();
if (server !== null && darkwebIp == server.ip) {
Terminal.print( Terminal.print(
"You are now connected to the dark web. From the dark web you can purchase illegal items. " + "You are now connected to the dark web. From the dark web you can purchase illegal items. " +
"Use the 'buy -l' command to display a list of all the items you can buy. Use 'buy [item-name] " + "Use the 'buy -l' command to display a list of all the items you can buy. Use 'buy [item-name] " +

@ -15,43 +15,42 @@ interface IProps {
} }
export function Bladeburner(props: IProps): React.ReactElement { export function Bladeburner(props: IProps): React.ReactElement {
const bladeburner = props.player.bladeburner;
if (bladeburner === null) return <></>;
function modifyBladeburnerRank(modify: number): (x: number) => void { function modifyBladeburnerRank(modify: number): (x: number) => void {
return function (rank: number): void { return function (rank: number): void {
if (props.player.bladeburner) { if (!bladeburner) return;
props.player.bladeburner.changeRank(props.player, rank * modify); bladeburner.changeRank(props.player, rank * modify);
}
}; };
} }
function resetBladeburnerRank(): void { function resetBladeburnerRank(): void {
props.player.bladeburner.rank = 0; if (!bladeburner) return;
props.player.bladeburner.maxRank = 0; bladeburner.rank = 0;
bladeburner.maxRank = 0;
} }
function addTonsBladeburnerRank(): void { function addTonsBladeburnerRank(): void {
if (props.player.bladeburner) { if (!bladeburner) return;
props.player.bladeburner.changeRank(props.player, bigNumber);
} bladeburner.changeRank(props.player, bigNumber);
} }
function modifyBladeburnerCycles(modify: number): (x: number) => void { function modifyBladeburnerCycles(modify: number): (x: number) => void {
return function (cycles: number): void { return function (cycles: number): void {
if (props.player.bladeburner) { if (!bladeburner) return;
props.player.bladeburner.storedCycles += cycles * modify; bladeburner.storedCycles += cycles * modify;
}
}; };
} }
function resetBladeburnerCycles(): void { function resetBladeburnerCycles(): void {
if (props.player.bladeburner) { if (!bladeburner) return;
props.player.bladeburner.storedCycles = 0; bladeburner.storedCycles = 0;
}
} }
function addTonsBladeburnerCycles(): void { function addTonsBladeburnerCycles(): void {
if (props.player.bladeburner) { if (!bladeburner) return;
props.player.bladeburner.storedCycles += bigNumber; bladeburner.storedCycles += bigNumber;
}
} }
return ( return (

@ -35,6 +35,6 @@ export function ExploitName(exploit: string): string {
return names[exploit]; return names[exploit];
} }
export function sanitizeExploits(exploits: string[]): string[] { export function sanitizeExploits(exploits: Exploit[]): Exploit[] {
return exploits.filter((e: string) => Object.keys(Exploit).includes(e)); return exploits.filter((e: Exploit) => Object.keys(Exploit).includes(e));
} }

@ -1,8 +0,0 @@
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 purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void;
export declare function joinFaction(faction: Faction): void;
export declare function startHackingMission(faction: Faction): void;

@ -1,4 +1,5 @@
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
import { Augmentation } from "../Augmentation/Augmentation";
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
@ -20,7 +21,7 @@ import { dialogBoxCreate } from "../../utils/DialogBox";
import { createPopup } from "../ui/React/createPopup"; import { createPopup } from "../ui/React/createPopup";
import { InvitationPopup } from "./ui/InvitationPopup"; import { InvitationPopup } from "./ui/InvitationPopup";
export function inviteToFaction(faction) { export function inviteToFaction(faction: Faction): void {
Player.factionInvitations.push(faction.name); Player.factionInvitations.push(faction.name);
faction.alreadyInvited = true; faction.alreadyInvited = true;
if (!Settings.SuppressFactionInvites) { if (!Settings.SuppressFactionInvites) {
@ -33,7 +34,7 @@ export function inviteToFaction(faction) {
} }
} }
export function joinFaction(faction) { export function joinFaction(faction: Faction): void {
if (faction.isMember) return; if (faction.isMember) return;
faction.isMember = true; faction.isMember = true;
Player.factions.push(faction.name); Player.factions.push(faction.name);
@ -54,7 +55,7 @@ export function joinFaction(faction) {
} }
} }
export function startHackingMission(faction) { export function startHackingMission(faction: Faction): void {
const mission = new HackingMission(faction.playerReputation, faction); const mission = new HackingMission(faction.playerReputation, faction);
setInMission(true, mission); //Sets inMission flag to true setInMission(true, mission); //Sets inMission flag to true
mission.init(); mission.init();
@ -62,7 +63,7 @@ export function startHackingMission(faction) {
//Returns a boolean indicating whether the player has the prerequisites for the //Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation //specified Augmentation
export function hasAugmentationPrereqs(aug) { export function hasAugmentationPrereqs(aug: Augmentation): boolean {
let hasPrereqs = true; let hasPrereqs = true;
if (aug.prereqs && aug.prereqs.length > 0) { if (aug.prereqs && aug.prereqs.length > 0) {
for (let i = 0; i < aug.prereqs.length; ++i) { for (let i = 0; i < aug.prereqs.length; ++i) {
@ -88,7 +89,7 @@ export function hasAugmentationPrereqs(aug) {
return hasPrereqs; return hasPrereqs;
} }
export function purchaseAugmentation(aug, fac, sing = false) { export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
const factionInfo = fac.getInfo(); const factionInfo = fac.getInfo();
var hasPrereqs = hasAugmentationPrereqs(aug); var hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) { if (!hasPrereqs) {
@ -111,13 +112,6 @@ export function purchaseAugmentation(aug, fac, sing = false) {
} }
dialogBoxCreate(txt); dialogBoxCreate(txt);
} else if (aug.baseCost === 0 || Player.money.gte(aug.baseCost * factionInfo.augmentationPriceMult)) { } 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); var queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
if (aug.name == AugmentationNames.NeuroFluxGovernor) { if (aug.name == AugmentationNames.NeuroFluxGovernor) {
queuedAugmentation.level = getNextNeurofluxLevel(); queuedAugmentation.level = getNextNeurofluxLevel();
@ -166,9 +160,10 @@ export function purchaseAugmentation(aug, fac, sing = false) {
"reproduce this.", "reproduce this.",
); );
} }
return "";
} }
export function getNextNeurofluxLevel() { export function getNextNeurofluxLevel(): number {
// Get current Neuroflux level based on Player's augmentations // Get current Neuroflux level based on Player's augmentations
let currLevel = 0; let currLevel = 0;
for (var i = 0; i < Player.augmentations.length; ++i) { for (var i = 0; i < Player.augmentations.length; ++i) {
@ -186,7 +181,7 @@ export function getNextNeurofluxLevel() {
return currLevel + 1; return currLevel + 1;
} }
export function processPassiveFactionRepGain(numCycles) { export function processPassiveFactionRepGain(numCycles: number): void {
for (const name in Factions) { for (const name in Factions) {
if (name === Player.currentWorkFactionName) continue; if (name === Player.currentWorkFactionName) continue;
if (!Factions.hasOwnProperty(name)) continue; if (!Factions.hasOwnProperty(name)) continue;

@ -1,2 +0,0 @@
export declare function parseFconfSettings(config: string): void;
export declare function createFconf(): string;

@ -1,268 +0,0 @@
import { FconfSettings } from "./FconfSettings";
import { parse, Node } from "acorn";
import { dialogBoxCreate } from "../../utils/DialogBox";
var FconfComments = {
ENABLE_BASH_HOTKEYS:
"Improved Bash emulation mode. Setting this to 1 enables several\n" +
"new Terminal shortcuts and features that more closely resemble\n" +
"a real Bash-style shell. Note that when this mode is enabled,\n" +
"the default browser shortcuts are overriden by the new Bash\n" +
"shortcuts.\n\n" +
"To see a full list of the Terminal shortcuts that this enables, see:\n" +
"http://bitburner.readthedocs.io/en/latest/shortcuts.html",
ENABLE_TIMESTAMPS:
"Terminal commands and log entries will be timestamped. The timestamp\n" + "will have the format: M/D h:m",
MAIN_MENU_STYLE:
"Customize the main navigation menu on the left-hand side. Current options:\n\n" + "default, classic, compact",
THEME_BACKGROUND_COLOR:
"Sets the background color for not only the Terminal, but also for\n" +
"most of the game's UI.\n\n" +
"The color must be specified as a pound sign (#) followed by a \n" +
"3-digit or 6-digit hex color code (e.g. #123456). Default color: #000000",
THEME_FONT_COLOR:
"Sets the font color for not only the Terminal, but also for\n" +
"most of the game's UI.\n\n" +
"The color must be specified as a pound sign (#) followed by a \n" +
"3-digit or 6-digit hex color code (e.g. #123456). Default color: #66ff33",
THEME_HIGHLIGHT_COLOR:
"Sets the highlight color for not only the Terminal, but also for \n" +
"most of the game's UI.\n\n" +
"The color must be specified as a pound sign (#) followed by a \n" +
"3-digit or 6-digit hex color code (e.g. #123456). Default color: #ffffff",
THEME_PROMPT_COLOR:
"Sets the prompt color in the Terminal\n\n" +
"The color must be specified as a pound sign (#) followed by a \n" +
"3-digit or 6-digit hex color code (e.g. #123456). Default color: #f92672",
WRAP_INPUT:
"Wrap Terminal Input. If this is enabled, then when a Terminal command is\n" +
"too long and overflows, then it will wrap to the next line instead of\n" +
"side-scrolling\n\n" +
"Note that after you enable/disable this, you'll have to run a command\n" +
"before its effect takes place.",
};
const MainMenuStyleOptions = ["default", "classic", "compact"];
//Parse Fconf settings from the config text
//Throws an exception if parsing fails
function parseFconfSettings(config) {
var ast = parse(config, { sourceType: "module" });
var queue = [];
queue.push(ast);
while (queue.length != 0) {
var exp = queue.shift();
switch (exp.type) {
case "BlockStatement":
case "Program":
for (var i = 0; i < exp.body.length; ++i) {
if (exp.body[i] instanceof Node) {
queue.push(exp.body[i]);
}
}
break;
case "AssignmentExpression":
var setting, value;
if (exp.left != null && exp.left.name != null) {
setting = exp.left.name;
} else {
break;
}
if (exp.right != null && exp.right.raw != null) {
value = exp.right.raw;
} else {
break;
}
parseFconfSetting(setting, value);
break;
default:
break;
}
for (var prop in exp) {
if (exp.hasOwnProperty(prop)) {
if (exp[prop] instanceof Node) {
queue.push(exp[prop]);
}
}
}
}
setTheme();
setMainMenuStyle();
}
function parseFconfSetting(setting, value) {
setting = String(setting);
value = String(value);
if (setting == null || value == null || FconfSettings[setting] == null) {
console.warn(`Invalid .fconf setting: ${setting}`);
return;
}
function sanitizeString(value) {
value = value.toLowerCase();
if (value.startsWith('"')) {
value = value.slice(1);
}
if (value.endsWith('"')) {
value = value.slice(0, -1);
}
return value;
}
switch (setting) {
case "ENABLE_BASH_HOTKEYS":
case "ENABLE_TIMESTAMPS":
case "WRAP_INPUT":
// Need to convert entered value to boolean/strings accordingly
var value = value.toLowerCase();
if (value === "1" || value === "true" || value === "y") {
value = true;
} else {
value = false;
}
FconfSettings[setting] = value;
break;
case "MAIN_MENU_STYLE":
var value = sanitizeString(value);
if (MainMenuStyleOptions.includes(value)) {
FconfSettings[setting] = value;
} else {
dialogBoxCreate(`Invalid option specified for ${setting}. Options: ${MainMenuStyleOptions.toString()}`);
}
break;
case "THEME_BACKGROUND_COLOR":
case "THEME_FONT_COLOR":
case "THEME_HIGHLIGHT_COLOR":
case "THEME_PROMPT_COLOR":
var value = sanitizeString(value);
if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(value)) {
FconfSettings[setting] = value;
} else {
dialogBoxCreate(`Invalid color specified for ${setting}. Must be a hex color code preceded by a pound (#)`);
}
break;
default:
break;
}
}
//Create the .fconf file text from the settings
function createFconf() {
var res = "";
for (var setting in FconfSettings) {
if (FconfSettings.hasOwnProperty(setting)) {
//Setting comments (description)
var comment = FconfComments[setting];
if (comment == null) {
continue;
}
var comment = comment.split("\n");
for (var i = 0; i < comment.length; ++i) {
res += "//" + comment[i] + "\n";
}
var value = 0;
if (FconfSettings[setting] === true) {
value = "1";
} else if (FconfSettings[setting] === false) {
value = "0";
} else {
value = '"' + String(FconfSettings[setting]) + '"';
}
res += `${setting} = ${value}\n\n`;
}
}
return res;
}
function loadFconf(saveString) {
let tempFconfSettings = JSON.parse(saveString);
for (var setting in tempFconfSettings) {
if (tempFconfSettings.hasOwnProperty(setting)) {
FconfSettings[setting] = tempFconfSettings[setting];
}
}
// Initialize themes/styles after loading
setTheme();
setMainMenuStyle();
}
function setTheme() {
if (
FconfSettings.THEME_HIGHLIGHT_COLOR == null ||
FconfSettings.THEME_FONT_COLOR == null ||
FconfSettings.THEME_BACKGROUND_COLOR == null ||
FconfSettings.THEME_PROMPT_COLOR == null
) {
console.error("Cannot find Theme Settings");
return;
}
if (
/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_HIGHLIGHT_COLOR) &&
/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_FONT_COLOR) &&
/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_BACKGROUND_COLOR) &&
/^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i.test(FconfSettings.THEME_PROMPT_COLOR)
) {
// document.body.style.setProperty("--my-highlight-color", FconfSettings.THEME_HIGHLIGHT_COLOR);
// document.body.style.setProperty("--my-font-color", FconfSettings.THEME_FONT_COLOR);
// document.body.style.setProperty("--my-background-color", FconfSettings.THEME_BACKGROUND_COLOR);
// document.body.style.setProperty("--my-prompt-color", FconfSettings.THEME_PROMPT_COLOR);
}
}
function setMainMenuStyle() {
const mainMenu = document.getElementById("mainmenu");
const hackingMenuHdr = document.getElementById("hacking-menu-header");
const characterMenuHdr = document.getElementById("character-menu-header");
const worldMenuHdr = document.getElementById("world-menu-header");
const helpMenuHdr = document.getElementById("help-menu-header");
function removeAllAccordionHeaderClasses() {
hackingMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic");
characterMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic");
worldMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic");
helpMenuHdr.classList.remove("mainmenu-accordion-header", "mainmenu-accordion-header-classic");
}
function addClassToAllAccordionHeaders(clsName) {
hackingMenuHdr.classList.add(clsName);
characterMenuHdr.classList.add(clsName);
worldMenuHdr.classList.add(clsName);
helpMenuHdr.classList.add(clsName);
}
if (FconfSettings["MAIN_MENU_STYLE"] === "default") {
removeAllAccordionHeaderClasses();
mainMenu.classList.remove("classic");
mainMenu.classList.remove("compact");
addClassToAllAccordionHeaders("mainmenu-accordion-header");
} else if (FconfSettings["MAIN_MENU_STYLE"] === "classic") {
removeAllAccordionHeaderClasses();
mainMenu.classList.remove("compact");
mainMenu.classList.add("classic");
addClassToAllAccordionHeaders("mainmenu-accordion-header-classic");
} else if (FconfSettings["MAIN_MENU_STYLE"] === "compact") {
removeAllAccordionHeaderClasses();
mainMenu.classList.remove("classic");
mainMenu.classList.add("compact");
addClassToAllAccordionHeaders("mainmenu-accordion-header-compact");
} else {
return;
}
// Click each header twice to reset lol
hackingMenuHdr.click();
hackingMenuHdr.click();
characterMenuHdr.click();
characterMenuHdr.click();
worldMenuHdr.click();
worldMenuHdr.click();
helpMenuHdr.click();
helpMenuHdr.click();
}
export { FconfSettings, createFconf, parseFconfSettings, loadFconf };

@ -1,10 +0,0 @@
export const FconfSettings = {
ENABLE_BASH_HOTKEYS: false,
ENABLE_TIMESTAMPS: false,
MAIN_MENU_STYLE: "default",
THEME_BACKGROUND_COLOR: "#000000",
THEME_FONT_COLOR: "#66ff33",
THEME_HIGHLIGHT_COLOR: "#ffffff",
THEME_PROMPT_COLOR: "#f92672",
WRAP_INPUT: false,
};

@ -8,13 +8,13 @@ import { use } from "../../ui/Context";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Gang } from "../Gang"; import { Gang } from "../Gang";
interface IProps { export function GangRoot(): React.ReactElement {
gang: Gang;
}
export function GangRoot(props: IProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
const router = use.Router(); const router = use.Router();
const gang = (function () {
if (player.gang === null) throw new Error("Gang should not be null");
return player.gang;
})();
const [management, setManagement] = useState(true); const [management, setManagement] = useState(true);
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
@ -24,7 +24,7 @@ export function GangRoot(props: IProps): React.ReactElement {
}, []); }, []);
function back(): void { function back(): void {
router.toFaction(Factions[props.gang.facName]); router.toFaction(Factions[gang.facName]);
} }
return ( return (
@ -46,7 +46,7 @@ export function GangRoot(props: IProps): React.ReactElement {
> >
Gang Territory Gang Territory
</a> </a>
{management ? <ManagementSubpage gang={props.gang} player={player} /> : <TerritorySubpage gang={props.gang} />} {management ? <ManagementSubpage gang={gang} player={player} /> : <TerritorySubpage gang={gang} />}
</div> </div>
); );
} }

@ -479,13 +479,12 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
break; break;
} }
case "Sell for Corporation Funds": { case "Sell for Corporation Funds": {
// This will throw if player doesn't have a corporation const corp = player.corporation;
try { if (corp === null) {
player.corporation.funds = player.corporation.funds.plus(upg.value);
} catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName);
return false; return false;
} }
corp.funds = corp.funds.plus(upg.value);
break; break;
} }
case "Reduce Minimum Security": { case "Reduce Minimum Security": {
@ -530,36 +529,35 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
} }
case "Exchange for Corporation Research": { case "Exchange for Corporation Research": {
// This will throw if player doesn't have a corporation // This will throw if player doesn't have a corporation
try { const corp = player.corporation;
for (const division of player.corporation.divisions) { if (corp === null) {
division.sciResearch.qty += upg.value;
}
} catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName);
return false; return false;
} }
for (const division of corp.divisions) {
division.sciResearch.qty += upg.value;
}
break; break;
} }
case "Exchange for Bladeburner Rank": { case "Exchange for Bladeburner Rank": {
// This will throw if player isnt in Bladeburner // This will throw if player isnt in Bladeburner
try { const bladeburner = player.bladeburner;
player.bladeburner.changeRank(player, upg.value); if (bladeburner === null) {
} catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName);
return false; return false;
} }
bladeburner.changeRank(player, upg.value);
break; break;
} }
case "Exchange for Bladeburner SP": { case "Exchange for Bladeburner SP": {
// This will throw if player isn't in Bladeburner // This will throw if player isnt in Bladeburner
try { const bladeburner = player.bladeburner;
// As long as we don't change `Bladeburner.totalSkillPoints`, this if (bladeburner === null) {
// shouldn't affect anything else
player.bladeburner.skillPoints += upg.value;
} catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName);
return false; return false;
} }
bladeburner.skillPoints += upg.value;
break; break;
} }
case "Generate Coding Contract": { case "Generate Coding Contract": {

@ -3,32 +3,25 @@
* to TypeScript at the moment * to TypeScript at the moment
*/ */
export interface IEngine { export interface IEngine {
indexedDb: any;
_lastUpdate: number; _lastUpdate: number;
hideAllContent: () => void; updateGame: (numCycles?: number) => void;
loadTerminalContent: () => void; Counters: {
loadScriptEditorContent: (filename?: string, code?: string) => void; [key: string]: number | undefined;
loadActiveScriptsContent: () => void; autoSaveCounter: number;
loadCreateProgramContent: () => void; updateSkillLevelsCounter: number;
loadCharacterContent: () => void; updateDisplays: number;
loadFactionsContent: () => void; updateDisplaysLong: number;
loadAugmentationsContent: () => void; updateActiveScriptsDisplay: number;
loadHacknetNodesContent: () => void; createProgramNotifications: number;
loadSleevesContent: () => void; augmentationsNotifications: number;
loadLocationContent: () => void; checkFactionInvitations: number;
loadTravelContent: () => void; passiveFactionGrowth: number;
loadJobContent: () => void; messages: number;
loadStockMarketContent: () => void; mechanicProcess: number;
loadBladeburnerContent: () => void; contractGeneration: number;
loadCorporationContent: () => void; };
loadGangContent: () => void; decrementAllCounters: (numCycles?: number) => void;
loadMilestonesContent: () => void; checkCounters: () => void;
loadTutorialContent: () => void; load: (saveString: string) => void;
loadDevMenuContent: () => void; start: () => void;
loadFactionContent: () => void;
loadInfiltrationContent: (name: string, difficulty: number, maxLevel: number) => void;
loadMissionContent: () => void;
loadResleevingContent: () => void;
loadGameOptionsContent: () => void;
load: (save: string) => void;
} }

@ -1 +0,0 @@
export declare function showMessage(msg: Message): void;

@ -11,7 +11,7 @@ import { dialogBoxCreate } from "../../utils/DialogBox";
import { Reviver } from "../../utils/JSONReviver"; import { Reviver } from "../../utils/JSONReviver";
//Sends message to player, including a pop up //Sends message to player, including a pop up
function sendMessage(msg, forced = false) { function sendMessage(msg: Message, forced = false): void {
msg.recvd = true; msg.recvd = true;
if (forced || !Settings.SuppressMessages) { if (forced || !Settings.SuppressMessages) {
showMessage(msg); showMessage(msg);
@ -19,7 +19,7 @@ function sendMessage(msg, forced = false) {
addMessageToServer(msg, "home"); addMessageToServer(msg, "home");
} }
function showMessage(msg) { function showMessage(msg: Message): void {
var txt = var txt =
"Message received from unknown sender: <br><br>" + "Message received from unknown sender: <br><br>" +
"<i>" + "<i>" +
@ -32,14 +32,16 @@ function showMessage(msg) {
} }
//Adds a message to a server //Adds a message to a server
function addMessageToServer(msg, serverHostname) { function addMessageToServer(msg: Message, serverHostname: string): void {
var server = GetServerByHostname(serverHostname); var server = GetServerByHostname(serverHostname);
if (server == null) { if (server == null) {
console.warn(`Could not find server ${serverHostname}`); console.warn(`Could not find server ${serverHostname}`);
return; return;
} }
for (var i = 0; i < server.messages.length; ++i) { for (var i = 0; i < server.messages.length; ++i) {
if (server.messages[i].filename === msg.filename) { const msg = server.messages[i];
if (typeof msg === "string") continue;
if (msg.filename === msg.filename) {
return; //Already exists return; //Already exists
} }
} }
@ -95,13 +97,13 @@ function checkForMessagesToSend() {
} }
} }
function AddToAllMessages(msg) { function AddToAllMessages(msg: Message): void {
Messages[msg.filename] = msg; Messages[msg.filename] = msg;
} }
let Messages = {}; let Messages: { [key: string]: Message | undefined } = {};
function loadMessages(saveString) { function loadMessages(saveString: string): void {
Messages = JSON.parse(saveString, Reviver); Messages = JSON.parse(saveString, Reviver);
} }

7
src/Missions.d.ts vendored

@ -1 +1,8 @@
export declare let inMission: boolean; export declare let inMission: boolean;
export declare class HackingMission {
constructor(reputation: number, faction: Faction);
init(): void;
process(numCycles: number): void;
}
export declare function setInMission(inMission: boolean, mission: HackingMission): void;
export declare let currMission: HackingMission;

@ -68,7 +68,7 @@ export class WorkerScript {
* Used for static RAM calculation. Stores names of all functions that have * Used for static RAM calculation. Stores names of all functions that have
* already been checked by this script * already been checked by this script
*/ */
loadedFns: IMap<string> = {}; loadedFns: IMap<boolean> = {};
/** /**
* Filename of script * Filename of script

@ -2,8 +2,9 @@ import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
import { AllServers } from "./Server/AllServers"; import { AllServers } from "./Server/AllServers";
import { WorkerScript } from "./Netscript/WorkerScript";
export function netscriptDelay(time, workerScript) { export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
return new Promise(function (resolve) { return new Promise(function (resolve) {
workerScript.delay = setTimeoutRef(() => { workerScript.delay = setTimeoutRef(() => {
workerScript.delay = null; workerScript.delay = null;
@ -13,7 +14,7 @@ export function netscriptDelay(time, workerScript) {
}); });
} }
export function makeRuntimeRejectMsg(workerScript, msg, exp = null) { export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string, exp: any = null) {
var lineNum = ""; var lineNum = "";
if (exp != null) { if (exp != null) {
var num = getErrorLineNumber(exp, workerScript); var num = getErrorLineNumber(exp, workerScript);
@ -21,13 +22,17 @@ export function makeRuntimeRejectMsg(workerScript, msg, exp = null) {
} }
const server = AllServers[workerScript.serverIp]; const server = AllServers[workerScript.serverIp];
if (server == null) { if (server == null) {
throw new Error(`WorkerScript constructed with invalid server ip: ${this.serverIp}`); throw new Error(`WorkerScript constructed with invalid server ip: ${workerScript.serverIp}`);
} }
return "|" + server.hostname + "|" + workerScript.name + "|" + msg + lineNum; return "|" + server.hostname + "|" + workerScript.name + "|" + msg + lineNum;
} }
export function resolveNetscriptRequestedThreads(workerScript, functionName, requestedThreads) { export function resolveNetscriptRequestedThreads(
workerScript: WorkerScript,
functionName: string,
requestedThreads: number,
) {
const threads = workerScript.scriptRef.threads; const threads = workerScript.scriptRef.threads;
if (!requestedThreads) { if (!requestedThreads) {
return isNaN(threads) || threads < 1 ? 1 : threads; return isNaN(threads) || threads < 1 ? 1 : threads;
@ -48,19 +53,22 @@ export function resolveNetscriptRequestedThreads(workerScript, functionName, req
return requestedThreadsAsInt; return requestedThreadsAsInt;
} }
export function getErrorLineNumber(exp, workerScript) { export function getErrorLineNumber(exp: any, workerScript: WorkerScript): number {
var code = workerScript.scriptRef.codeCode();
//Split code up to the start of the node
try {
code = code.substring(0, exp.start);
return (code.match(/\n/g) || []).length + 1;
} catch (e) {
return -1; return -1;
} // TODO wtf is codeCode?
// var code = workerScript.scriptRef.codeCode();
// //Split code up to the start of the node
// try {
// code = code.substring(0, exp.start);
// return (code.match(/\n/g) || []).length + 1;
// } catch (e) {
// return -1;
// }
} }
export function isScriptErrorMessage(msg) { export function isScriptErrorMessage(msg: string): boolean {
if (!isString(msg)) { if (!isString(msg)) {
return false; return false;
} }

@ -103,7 +103,8 @@ import { Player } from "./Player";
import { Programs } from "./Programs/Programs"; import { Programs } from "./Programs/Programs";
import { Script } from "./Script/Script"; import { Script } from "./Script/Script";
import { findRunningScript, findRunningScriptByPid } from "./Script/ScriptHelpers"; import { findRunningScript, findRunningScriptByPid } from "./Script/ScriptHelpers";
import { isScriptFilename } from "./Script/ScriptHelpersTS"; import { isScriptFilename } from "./Script/isScriptFilename";
import { AllServers, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers"; import { AllServers, AddToAllServers, createUniqueRandomIp } from "./Server/AllServers";
import { RunningScript } from "./Script/RunningScript"; import { RunningScript } from "./Script/RunningScript";
import { import {
@ -132,7 +133,7 @@ import { NetscriptPorts, runScriptFromScript, startWorkerScript } from "./Netscr
import { killWorkerScript } from "./Netscript/killWorkerScript"; import { killWorkerScript } from "./Netscript/killWorkerScript";
import { workerScripts } from "./Netscript/WorkerScripts"; import { workerScripts } from "./Netscript/WorkerScripts";
import { makeRuntimeRejectMsg, netscriptDelay, resolveNetscriptRequestedThreads } from "./NetscriptEvaluator"; import { makeRuntimeRejectMsg, netscriptDelay, resolveNetscriptRequestedThreads } from "./NetscriptEvaluator";
import { Interpreter } from "./JSInterpreter"; import { Interpreter } from "./ThirdParty/JSInterpreter";
import { NetscriptPort } from "./NetscriptPort"; import { NetscriptPort } from "./NetscriptPort";
import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum"; import { SleeveTaskType } from "./PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { findSleevePurchasableAugs } from "./PersonObjects/Sleeve/SleeveHelpers"; import { findSleevePurchasableAugs } from "./PersonObjects/Sleeve/SleeveHelpers";
@ -3808,7 +3809,7 @@ function NetscriptFunctions(workerScript) {
throw makeRuntimeErrorMsg("commitCrime", `Invalid crime: '${crimeRoughName}'`); throw makeRuntimeErrorMsg("commitCrime", `Invalid crime: '${crimeRoughName}'`);
} }
workerScript.log("commitCrime", `Attempting to commit ${crime.name}...`); workerScript.log("commitCrime", `Attempting to commit ${crime.name}...`);
return crime.commit(Router, Player, 1, { workerscript: workerScript }); return crime.commit(Router, Player, 1, workerScript);
}, },
getCrimeChance: function (crimeRoughName) { getCrimeChance: function (crimeRoughName) {
updateDynamicRam("getCrimeChance", getRamCost("getCrimeChance")); updateDynamicRam("getCrimeChance", getRamCost("getCrimeChance"));

@ -1,8 +1,10 @@
import { makeRuntimeRejectMsg } from "./NetscriptEvaluator"; import { makeRuntimeRejectMsg } from "./NetscriptEvaluator";
import { ScriptUrl } from "./Script/ScriptUrl"; import { ScriptUrl } from "./Script/ScriptUrl";
import { WorkerScript } from "./Netscript/WorkerScript";
import { Script } from "./Script/Script";
// Makes a blob that contains the code of a given script. // Makes a blob that contains the code of a given script.
function makeScriptBlob(code) { function makeScriptBlob(code: string): Blob {
return new Blob([code], { type: "text/javascript" }); return new Blob([code], { type: "text/javascript" });
} }
@ -14,10 +16,11 @@ function makeScriptBlob(code) {
// (i.e. hack, grow, etc.). // (i.e. hack, grow, etc.).
// When the promise returned by this resolves, we'll have finished // When the promise returned by this resolves, we'll have finished
// running the main function of the script. // running the main function of the script.
export async function executeJSScript(scripts = [], workerScript) { export async function executeJSScript(scripts: Script[] = [], workerScript: WorkerScript) {
let loadedModule; let loadedModule;
let urls = null; let uurls: ScriptUrl[] = [];
let script = workerScript.getScript(); let script = workerScript.getScript();
if (script === null) throw new Error("script is null");
if (shouldCompile(script, scripts)) { if (shouldCompile(script, scripts)) {
// The URL at the top is the one we want to import. It will // The URL at the top is the one we want to import. It will
// recursively import all the other modules in the urlStack. // recursively import all the other modules in the urlStack.
@ -27,16 +30,15 @@ export async function executeJSScript(scripts = [], workerScript) {
// load fully dynamic content. So we hide the import from webpack // load fully dynamic content. So we hide the import from webpack
// by placing it inside an eval call. // by placing it inside an eval call.
script.markUpdated(); script.markUpdated();
urls = _getScriptUrls(script, scripts, []); uurls = _getScriptUrls(script, scripts, []);
script.url = urls[urls.length - 1].url; script.url = uurls[uurls.length - 1].url;
script.module = new Promise((resolve) => resolve(eval("import(urls[urls.length - 1].url)"))); script.module = new Promise((resolve) => resolve(eval("import(uurls[uurls.length - 1].url)")));
script.dependencies = urls; script.dependencies = uurls;
} }
loadedModule = await script.module; loadedModule = await script.module;
let ns = workerScript.env.vars; let ns = workerScript.env.vars;
try {
// TODO: putting await in a non-async function yields unhelpful // TODO: putting await in a non-async function yields unhelpful
// "SyntaxError: unexpected reserved word" with no line number information. // "SyntaxError: unexpected reserved word" with no line number information.
if (!loadedModule.main) { if (!loadedModule.main) {
@ -46,12 +48,6 @@ export async function executeJSScript(scripts = [], workerScript) {
); );
} }
return loadedModule.main(ns); return loadedModule.main(ns);
} finally {
// Revoke the generated URLs
if (urls != null) {
for (const b in urls) URL.revokeObjectURL(b.url);
}
}
} }
/** Returns whether we should compile the script parameter. /** Returns whether we should compile the script parameter.
@ -59,7 +55,7 @@ export async function executeJSScript(scripts = [], workerScript) {
* @param {Script} script * @param {Script} script
* @param {Script[]} scripts * @param {Script[]} scripts
*/ */
function shouldCompile(script, scripts) { function shouldCompile(script: Script, scripts: Script[]): boolean {
if (script.module === "") return true; if (script.module === "") return true;
return script.dependencies.some((dep) => { return script.dependencies.some((dep) => {
const depScript = scripts.find((s) => s.filename == dep.filename); const depScript = scripts.find((s) => s.filename == dep.filename);
@ -93,7 +89,7 @@ function shouldCompile(script, scripts) {
* the script parameter. * the script parameter.
*/ */
// BUG: apparently seen is never consulted. Oops. // BUG: apparently seen is never consulted. Oops.
function _getScriptUrls(script, scripts, seen) { function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): ScriptUrl[] {
// Inspired by: https://stackoverflow.com/a/43834063/91401 // Inspired by: https://stackoverflow.com/a/43834063/91401
/** @type {ScriptUrl[]} */ /** @type {ScriptUrl[]} */
const urlStack = []; const urlStack = [];

@ -1,6 +1,14 @@
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
interface IPort {} export interface IPort {
write: (value: any) => any;
tryWrite: (value: any) => boolean;
read: () => any;
peek: () => any;
full: () => boolean;
empty: () => boolean;
clear: () => void;
}
export function NetscriptPort(): IPort { export function NetscriptPort(): IPort {
const data: any[] = []; const data: any[] = [];

@ -1 +0,0 @@
export declare function startWorkerScript(script: RunningScript, server: BaseServer): boolean;

@ -9,16 +9,19 @@ import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStart
import { generateNextPid } from "./Netscript/Pid"; import { generateNextPid } from "./Netscript/Pid";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { Interpreter } from "./JSInterpreter"; import { Interpreter } from "./ThirdParty/JSInterpreter";
import { isScriptErrorMessage, makeRuntimeRejectMsg } from "./NetscriptEvaluator"; import { isScriptErrorMessage, makeRuntimeRejectMsg } from "./NetscriptEvaluator";
import { NetscriptFunctions } from "./NetscriptFunctions"; import { NetscriptFunctions } from "./NetscriptFunctions";
import { executeJSScript } from "./NetscriptJSEvaluator"; import { executeJSScript } from "./NetscriptJSEvaluator";
import { NetscriptPort } from "./NetscriptPort"; import { NetscriptPort, IPort } from "./NetscriptPort";
import { Player } from "./Player"; import { Player } from "./Player";
import { RunningScript } from "./Script/RunningScript"; import { RunningScript } from "./Script/RunningScript";
import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers"; import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers";
import { scriptCalculateOfflineProduction } from "./Script/ScriptHelpers"; import { scriptCalculateOfflineProduction } from "./Script/ScriptHelpers";
import { Script } from "./Script/Script";
import { AllServers } from "./Server/AllServers"; import { AllServers } from "./Server/AllServers";
import { Server } from "./Server/Server";
import { BaseServer } from "./Server/BaseServer";
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
import { setTimeoutRef } from "./utils/SetTimeoutRef"; import { setTimeoutRef } from "./utils/SetTimeoutRef";
@ -28,12 +31,13 @@ import { dialogBoxCreate } from "../utils/DialogBox";
import { arrayToString } from "../utils/helpers/arrayToString"; import { arrayToString } from "../utils/helpers/arrayToString";
import { roundToTwo } from "../utils/helpers/roundToTwo"; import { roundToTwo } from "../utils/helpers/roundToTwo";
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
import { sprintf } from "sprintf-js";
import { parse } from "acorn"; import { parse } from "acorn";
import { simple as walksimple } from "acorn-walk"; import { simple as walksimple } from "acorn-walk";
// Netscript Ports are instantiated here // Netscript Ports are instantiated here
export const NetscriptPorts = []; export const NetscriptPorts: IPort[] = [];
for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) { for (var i = 0; i < CONSTANTS.NumNetscriptPorts; ++i) {
NetscriptPorts.push(NetscriptPort()); NetscriptPorts.push(NetscriptPort());
} }
@ -51,20 +55,20 @@ export function prestigeWorkerScripts() {
// JS script promises need a little massaging to have the same guarantees as netscript // JS script promises need a little massaging to have the same guarantees as netscript
// promises. This does said massaging and kicks the script off. It returns a promise // promises. This does said massaging and kicks the script off. It returns a promise
// that resolves or rejects when the corresponding worker script is done. // that resolves or rejects when the corresponding worker script is done.
function startNetscript2Script(workerScript) { function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript> {
workerScript.running = true; workerScript.running = true;
// The name of the currently running netscript function, to prevent concurrent // The name of the currently running netscript function, to prevent concurrent
// calls to hack, grow, etc. // calls to hack, grow, etc.
let runningFn = null; let runningFn: string | null = null;
// We need to go through the environment and wrap each function in such a way that it // We need to go through the environment and wrap each function in such a way that it
// can be called at most once at a time. This will prevent situations where multiple // can be called at most once at a time. This will prevent situations where multiple
// hack promises are outstanding, for example. // hack promises are outstanding, for example.
function wrap(propName, f) { function wrap(propName: string, f: Function): Function {
// This function unfortunately cannot be an async function, because we don't // This function unfortunately cannot be an async function, because we don't
// know if the original one was, and there's no way to tell. // know if the original one was, and there's no way to tell.
return function (...args) { return function (...args: any[]) {
// Wrap every netscript function with a check for the stop flag. // Wrap every netscript function with a check for the stop flag.
// This prevents cases where we never stop because we are only calling // This prevents cases where we never stop because we are only calling
// netscript functions that don't check this. // netscript functions that don't check this.
@ -115,12 +119,13 @@ function startNetscript2Script(workerScript) {
// Note: the environment that we pass to the JS script only needs to contain the functions visible // Note: the environment that we pass to the JS script only needs to contain the functions visible
// to that script, which env.vars does at this point. // to that script, which env.vars does at this point.
return executeJSScript(workerScript.getServer().scripts, workerScript) return new Promise<WorkerScript>((resolve, reject) => {
.then(function (mainReturnValue) { executeJSScript(workerScript.getServer().scripts, workerScript)
if (mainReturnValue === undefined) return workerScript; .then(() => {
return [mainReturnValue, workerScript]; resolve(workerScript);
}) })
.catch((e) => { .catch((e) => reject(e));
}).catch((e) => {
if (e instanceof Error) { if (e instanceof Error) {
workerScript.errorMessage = makeRuntimeRejectMsg( workerScript.errorMessage = makeRuntimeRejectMsg(
workerScript, workerScript,
@ -135,7 +140,7 @@ function startNetscript2Script(workerScript) {
}); });
} }
function startNetscript1Script(workerScript) { function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript> {
const code = workerScript.code; const code = workerScript.code;
workerScript.running = true; workerScript.running = true;
@ -150,10 +155,10 @@ function startNetscript1Script(workerScript) {
workerScript.env.stopFlag = true; workerScript.env.stopFlag = true;
workerScript.running = false; workerScript.running = false;
killWorkerScript(workerScript); killWorkerScript(workerScript);
return; return Promise.resolve(workerScript);
} }
var interpreterInitialization = function (int, scope) { var interpreterInitialization = function (int: any, scope: any) {
//Add the Netscript environment //Add the Netscript environment
var ns = NetscriptFunctions(workerScript); var ns = NetscriptFunctions(workerScript);
for (let name in ns) { for (let name in ns) {
@ -183,10 +188,10 @@ function startNetscript1Script(workerScript) {
let cb = arguments[arguments.length - 1]; let cb = arguments[arguments.length - 1];
let fnPromise = entry.apply(null, fnArgs); let fnPromise = entry.apply(null, fnArgs);
fnPromise fnPromise
.then(function (res) { .then(function (res: any) {
cb(res); cb(res);
}) })
.catch(function (err) { .catch(function (err: any) {
console.error(err); console.error(err);
}); });
}; };
@ -242,7 +247,7 @@ function startNetscript1Script(workerScript) {
int.setProperty(scope, "args", int.nativeToPseudo(workerScript.args)); int.setProperty(scope, "args", int.nativeToPseudo(workerScript.args));
}; };
var interpreter; var interpreter: any;
try { try {
interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset); interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset);
} catch (e) { } catch (e) {
@ -250,7 +255,7 @@ function startNetscript1Script(workerScript) {
workerScript.env.stopFlag = true; workerScript.env.stopFlag = true;
workerScript.running = false; workerScript.running = false;
killWorkerScript(workerScript); killWorkerScript(workerScript);
return; return Promise.resolve(workerScript);
} }
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
@ -301,9 +306,9 @@ function startNetscript1Script(workerScript) {
Should typically be positive Should typically be positive
} }
*/ */
function processNetscript1Imports(code, workerScript) { function processNetscript1Imports(code: string, workerScript: WorkerScript): any {
//allowReserved prevents 'import' from throwing error in ES5 //allowReserved prevents 'import' from throwing error in ES5
const ast = parse(code, { const ast: any = parse(code, {
ecmaVersion: 9, ecmaVersion: 9,
allowReserved: true, allowReserved: true,
sourceType: "module", sourceType: "module",
@ -314,7 +319,7 @@ function processNetscript1Imports(code, workerScript) {
throw new Error("Failed to find underlying Server object for script"); throw new Error("Failed to find underlying Server object for script");
} }
function getScript(scriptName) { function getScript(scriptName: string): Script | null {
for (let i = 0; i < server.scripts.length; ++i) { for (let i = 0; i < server.scripts.length; ++i) {
if (server.scripts[i].filename === scriptName) { if (server.scripts[i].filename === scriptName) {
return server.scripts[i]; return server.scripts[i];
@ -328,7 +333,7 @@ function processNetscript1Imports(code, workerScript) {
// Walk over the tree and process ImportDeclaration nodes // Walk over the tree and process ImportDeclaration nodes
walksimple(ast, { walksimple(ast, {
ImportDeclaration: (node) => { ImportDeclaration: (node: any) => {
hasImports = true; hasImports = true;
let scriptName = node.source.value; let scriptName = node.source.value;
if (scriptName.startsWith("./")) { if (scriptName.startsWith("./")) {
@ -347,10 +352,10 @@ function processNetscript1Imports(code, workerScript) {
if (node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier") { if (node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier") {
// import * as namespace from script // import * as namespace from script
let namespace = node.specifiers[0].local.name; let namespace = node.specifiers[0].local.name;
let fnNames = []; //Names only let fnNames: string[] = []; //Names only
let fnDeclarations = []; //FunctionDeclaration Node objects let fnDeclarations: any[] = []; //FunctionDeclaration Node objects
walksimple(scriptAst, { walksimple(scriptAst, {
FunctionDeclaration: (node) => { FunctionDeclaration: (node: any) => {
fnNames.push(node.id.name); fnNames.push(node.id.name);
fnDeclarations.push(node); fnDeclarations.push(node);
}, },
@ -360,7 +365,7 @@ function processNetscript1Imports(code, workerScript) {
generatedCode += "var " + namespace + ";\n" + "(function (namespace) {\n"; generatedCode += "var " + namespace + ";\n" + "(function (namespace) {\n";
//Add the function declarations //Add the function declarations
fnDeclarations.forEach((fn) => { fnDeclarations.forEach((fn: any) => {
generatedCode += generate(fn); generatedCode += generate(fn);
generatedCode += "\n"; generatedCode += "\n";
}); });
@ -377,15 +382,15 @@ function processNetscript1Imports(code, workerScript) {
//import {...} from script //import {...} from script
//Get array of all fns to import //Get array of all fns to import
let fnsToImport = []; let fnsToImport: string[] = [];
node.specifiers.forEach((e) => { node.specifiers.forEach((e: any) => {
fnsToImport.push(e.local.name); fnsToImport.push(e.local.name);
}); });
//Walk through script and get FunctionDeclaration code for all specified fns //Walk through script and get FunctionDeclaration code for all specified fns
let fnDeclarations = []; let fnDeclarations: any[] = [];
walksimple(scriptAst, { walksimple(scriptAst, {
FunctionDeclaration: (node) => { FunctionDeclaration: (node: any) => {
if (fnsToImport.includes(node.id.name)) { if (fnsToImport.includes(node.id.name)) {
fnDeclarations.push(node); fnDeclarations.push(node);
} }
@ -393,7 +398,7 @@ function processNetscript1Imports(code, workerScript) {
}); });
//Convert FunctionDeclarations into code //Convert FunctionDeclarations into code
fnDeclarations.forEach((fn) => { fnDeclarations.forEach((fn: any) => {
generatedCode += generate(fn); generatedCode += generate(fn);
generatedCode += "\n"; generatedCode += "\n";
}); });
@ -442,11 +447,11 @@ function processNetscript1Imports(code, workerScript) {
* @param {Server} server - Server on which the script is to be run * @param {Server} server - Server on which the script is to be run
* @returns {number} pid of started script * @returns {number} pid of started script
*/ */
export function startWorkerScript(runningScript, server, parent) { export function startWorkerScript(runningScript: RunningScript, server: BaseServer, parent?: WorkerScript): number {
if (createAndAddWorkerScript(runningScript, server, parent)) { if (createAndAddWorkerScript(runningScript, server, parent)) {
// Push onto runningScripts. // Push onto runningScripts.
// This has to come after createAndAddWorkerScript() because that fn updates RAM usage // This has to come after createAndAddWorkerScript() because that fn updates RAM usage
server.runScript(runningScript, Player.hacknet_node_money_mult); server.runScript(runningScript);
// Once the WorkerScript is constructed in createAndAddWorkerScript(), the RunningScript // Once the WorkerScript is constructed in createAndAddWorkerScript(), the RunningScript
// object should have a PID assigned to it, so we return that // object should have a PID assigned to it, so we return that
@ -463,7 +468,11 @@ export function startWorkerScript(runningScript, server, parent) {
* @param {Server} server - Server on which the script is to be run * @param {Server} server - Server on which the script is to be run
* returns {boolean} indicating whether or not the workerScript was successfully added * returns {boolean} indicating whether or not the workerScript was successfully added
*/ */
export function createAndAddWorkerScript(runningScriptObj, server, parent) { export function createAndAddWorkerScript(
runningScriptObj: RunningScript,
server: BaseServer,
parent?: WorkerScript,
): boolean {
// Update server's ram usage // Update server's ram usage
let threads = 1; let threads = 1;
if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) { if (runningScriptObj.threads && !isNaN(runningScriptObj.threads)) {
@ -503,7 +512,7 @@ export function createAndAddWorkerScript(runningScriptObj, server, parent) {
WorkerScriptStartStopEventEmitter.emit(); WorkerScriptStartStopEventEmitter.emit();
// Start the script's execution // Start the script's execution
let p = null; // Script's resulting promise let p: Promise<WorkerScript> | null = null; // Script's resulting promise
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) { if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
p = startNetscript2Script(s); p = startNetscript2Script(s);
} else { } else {
@ -515,17 +524,19 @@ export function createAndAddWorkerScript(runningScriptObj, server, parent) {
// Once the code finishes (either resolved or rejected, doesnt matter), set its // Once the code finishes (either resolved or rejected, doesnt matter), set its
// running status to false // running status to false
p.then(function (w) { p.then(function (w: WorkerScript) {
// On natural death, the earnings are transfered to the parent if it still exists. // On natural death, the earnings are transfered to the parent if it still exists.
if (parent && parent.running) { if (parent !== undefined) {
if (parent.running) {
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained; parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade; parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
} }
}
// If the WorkerScript is no longer "running", then this means its execution was // If the WorkerScript is no longer "running", then this means its execution was
// already stopped somewhere else (maybe by something like exit()). This prevents // already stopped somewhere else (maybe by something like exit()). This prevents
// the script from being cleaned up twice // the script from being cleaned up twice
if (!w.running) { if (w === undefined || !w.running) {
return; return;
} }
@ -541,7 +552,7 @@ export function createAndAddWorkerScript(runningScriptObj, server, parent) {
const errorTextArray = w.errorMessage.split("|"); const errorTextArray = w.errorMessage.split("|");
if (errorTextArray.length != 4) { if (errorTextArray.length != 4) {
console.error("ERROR: Something wrong with Error text in evaluator..."); console.error("ERROR: Something wrong with Error text in evaluator...");
console.error("Error text: " + errorText); console.error("Error text: " + w.errorMessage);
return; return;
} }
const serverIp = errorTextArray[1]; const serverIp = errorTextArray[1];
@ -630,14 +641,21 @@ export function loadAllRunningScripts() {
/** /**
* Run a script from inside another script (run(), exec(), spawn(), etc.) * Run a script from inside another script (run(), exec(), spawn(), etc.)
*/ */
export function runScriptFromScript(caller, server, scriptname, args, workerScript, threads = 1) { export function runScriptFromScript(
caller: string,
server: BaseServer,
scriptname: string,
args: any[],
workerScript: WorkerScript,
threads = 1,
): number {
// Sanitize arguments // Sanitize arguments
if (!(workerScript instanceof WorkerScript)) { if (!(workerScript instanceof WorkerScript)) {
return 0; return 0;
} }
if (typeof scriptname !== "string" || !Array.isArray(args)) { if (typeof scriptname !== "string" || !Array.isArray(args)) {
workerScript.log(caller, `Invalid arguments: scriptname='${scriptname} args='${ags}'`); workerScript.log(caller, `Invalid arguments: scriptname='${scriptname} args='${args}'`);
console.error(`runScriptFromScript() failed due to invalid arguments`); console.error(`runScriptFromScript() failed due to invalid arguments`);
return 0; return 0;
} }

@ -26,6 +26,8 @@ import { IGang } from "../Gang/IGang";
import { IBladeburner } from "../Bladeburner/IBladeburner"; import { IBladeburner } from "../Bladeburner/IBladeburner";
import { ICodingContractReward } from "../CodingContracts"; import { ICodingContractReward } from "../CodingContracts";
import { IRouter } from "../ui/Router"; import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript";
import { HacknetServer } from "../Hacknet/HacknetServer";
export interface IPlayer { export interface IPlayer {
// Class members // Class members
@ -33,14 +35,12 @@ export interface IPlayer {
bitNodeN: number; bitNodeN: number;
city: CityName; city: CityName;
companyName: string; companyName: string;
corporation: ICorporation; corporation: ICorporation | null;
gang: IGang; gang: IGang | null;
bladeburner: IBladeburner; bladeburner: IBladeburner | null;
currentServer: string; currentServer: string;
factions: string[]; factions: string[];
factionInvitations: string[]; factionInvitations: string[];
firstProgramAvailable: boolean;
firstTimeTraveled: boolean;
hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server
has4SData: boolean; has4SData: boolean;
has4SDataTixApi: boolean; has4SDataTixApi: boolean;
@ -122,9 +122,13 @@ export interface IPlayer {
bladeburner_analysis_mult: number; bladeburner_analysis_mult: number;
bladeburner_success_chance_mult: number; bladeburner_success_chance_mult: number;
createProgramReqLvl: number;
factionWorkType: string;
createProgramName: string; createProgramName: string;
timeWorkedCreateProgram: number; timeWorkedCreateProgram: number;
crimeType: string; crimeType: string;
committingCrimeThruSingFn: boolean;
singFnCrimeWorkerScript: WorkerScript | null;
timeNeededToCompleteWork: number; timeNeededToCompleteWork: number;
focus: boolean; focus: boolean;
className: string; className: string;
@ -151,20 +155,23 @@ export interface IPlayer {
workMoneyLossRate: number; workMoneyLossRate: number;
// Methods // Methods
applyForAgentJob(sing?: boolean): boolean | void; work(numCycles: number): boolean;
applyForBusinessConsultantJob(sing?: boolean): boolean | void; workPartTime(numCycles: number): boolean;
applyForBusinessJob(sing?: boolean): boolean | void; workForFaction(numCycles: number): boolean;
applyForEmployeeJob(sing?: boolean): boolean | void; applyForAgentJob(sing?: boolean): boolean;
applyForItJob(sing?: boolean): boolean | void; applyForBusinessConsultantJob(sing?: boolean): boolean;
applyForJob(entryPosType: CompanyPosition, sing?: boolean): boolean | void; applyForBusinessJob(sing?: boolean): boolean;
applyForNetworkEngineerJob(sing?: boolean): boolean | void; applyForEmployeeJob(sing?: boolean): boolean;
applyForPartTimeEmployeeJob(sing?: boolean): boolean | void; applyForItJob(sing?: boolean): boolean;
applyForPartTimeWaiterJob(sing?: boolean): boolean | void; applyForJob(entryPosType: CompanyPosition, sing?: boolean): boolean;
applyForSecurityEngineerJob(sing?: boolean): boolean | void; applyForNetworkEngineerJob(sing?: boolean): boolean;
applyForSecurityJob(sing?: boolean): boolean | void; applyForPartTimeEmployeeJob(sing?: boolean): boolean;
applyForSoftwareConsultantJob(sing?: boolean): boolean | void; applyForPartTimeWaiterJob(sing?: boolean): boolean;
applyForSoftwareJob(sing?: boolean): boolean | void; applyForSecurityEngineerJob(sing?: boolean): boolean;
applyForWaiterJob(sing?: boolean): boolean | void; applyForSecurityJob(sing?: boolean): boolean;
applyForSoftwareConsultantJob(sing?: boolean): boolean;
applyForSoftwareJob(sing?: boolean): boolean;
applyForWaiterJob(sing?: boolean): boolean;
canAccessBladeburner(): boolean; canAccessBladeburner(): boolean;
canAccessCorporation(): boolean; canAccessCorporation(): boolean;
canAccessGang(): boolean; canAccessGang(): boolean;
@ -178,11 +185,11 @@ export interface IPlayer {
gainCharismaExp(exp: number): void; gainCharismaExp(exp: number): void;
gainIntelligenceExp(exp: number): void; gainIntelligenceExp(exp: number): void;
gainMoney(money: number): void; gainMoney(money: number): void;
getCurrentServer(): Server; getCurrentServer(): Server | HacknetServer;
getGangFaction(): Faction; getGangFaction(): Faction;
getGangName(): string; getGangName(): string;
getHomeComputer(): Server; getHomeComputer(): Server;
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition; getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition | null;
getUpgradeHomeRamCost(): number; getUpgradeHomeRamCost(): number;
gotoLocation(to: LocationName): boolean; gotoLocation(to: LocationName): boolean;
hasAugmentation(aug: Augmentation): boolean; hasAugmentation(aug: Augmentation): boolean;
@ -194,13 +201,14 @@ export interface IPlayer {
inGang(): boolean; inGang(): boolean;
isQualified(company: Company, position: CompanyPosition): boolean; isQualified(company: Company, position: CompanyPosition): boolean;
loseMoney(money: number): void; loseMoney(money: number): void;
reapplyAllAugmentations(resetMultipliers: boolean): void; reapplyAllAugmentations(resetMultipliers?: boolean): void;
reapplyAllSourceFiles(): void; reapplyAllSourceFiles(): void;
regenerateHp(amt: number): void; regenerateHp(amt: number): void;
recordMoneySource(amt: number, source: string): void; recordMoneySource(amt: number, source: string): void;
setMoney(amt: number): void; setMoney(amt: number): void;
singularityStopWork(): void; singularityStopWork(): void;
startBladeburner(p: any): void; startBladeburner(p: any): void;
startFactionWork(router: IRouter, faction: Faction): void;
startClass(router: IRouter, costMult: number, expMult: number, className: string): void; startClass(router: IRouter, costMult: number, expMult: number, className: string): void;
startCorporation(corpName: string, additionalShares?: number): void; startCorporation(corpName: string, additionalShares?: number): void;
startCrime( startCrime(
@ -230,18 +238,40 @@ export interface IPlayer {
getIntelligenceBonus(weight: number): number; getIntelligenceBonus(weight: number): number;
getCasinoWinnings(): number; getCasinoWinnings(): number;
quitJob(company: string): void; quitJob(company: string): void;
createHacknetServer(): void; createHacknetServer(): HacknetServer;
startCreateProgramWork(router: IRouter, programName: string, time: number, reqLevel: number): void; startCreateProgramWork(router: IRouter, programName: string, time: number, reqLevel: number): void;
queueAugmentation(augmentationName: string): void; queueAugmentation(augmentationName: string): void;
receiveInvite(factionName: string): void; receiveInvite(factionName: string): void;
updateSkillLevels(): void; updateSkillLevels(): void;
gainCodingContractReward(reward: ICodingContractReward, difficulty?: number): string; gainCodingContractReward(reward: ICodingContractReward, difficulty?: number): string;
stopFocusing(): void; stopFocusing(): void;
finishFactionWork(cancelled: boolean, sing?: boolean): void; finishFactionWork(cancelled: boolean, sing?: boolean): string;
finishClass(sing?: boolean): void; finishClass(sing?: boolean): string;
finishWork(cancelled: boolean, sing?: boolean): void; finishWork(cancelled: boolean, sing?: boolean): string;
cancelationPenalty(): number; cancelationPenalty(): number;
finishWorkPartTime(sing?: boolean): void; finishWorkPartTime(sing?: boolean): string;
finishCrime(cancelled: boolean): void; finishCrime(cancelled: boolean): string;
finishCreateProgramWork(cancelled: boolean): void; finishCreateProgramWork(cancelled: boolean): string;
resetMultipliers(): void;
prestigeAugmentation(): void;
prestigeSourceFile(): void;
calculateSkill(exp: number, mult?: number): number;
resetWorkStatus(generalType?: string, group?: string, workType?: string): void;
getWorkHackExpGain(): number;
getWorkStrExpGain(): number;
getWorkDefExpGain(): number;
getWorkDexExpGain(): number;
getWorkAgiExpGain(): number;
getWorkChaExpGain(): number;
getWorkRepGain(): number;
getWorkMoneyGain(): number;
processWorkEarnings(cycles: number): void;
hospitalize(): void;
createProgramWork(numCycles: number): boolean;
takeClass(numCycles: number): boolean;
commitCrime(numCycles: number): boolean;
checkForFactionInvitations(): Faction[];
setBitNodeNumber(n: number): void;
getMult(name: string): number;
setMult(name: string, mult: number): void;
} }

@ -1,224 +0,0 @@
import * as augmentationMethods from "./PlayerObjectAugmentationMethods";
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 { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../../utils/JSONReviver";
import Decimal from "decimal.js";
export function PlayerObject() {
//Skills and stats
this.hacking_skill = 1;
//Combat stats
this.hp = 10;
this.max_hp = 10;
this.strength = 1;
this.defense = 1;
this.dexterity = 1;
this.agility = 1;
//Labor stats
this.charisma = 1;
//Special stats
this.intelligence = 0;
//Hacking multipliers
this.hacking_chance_mult = 1;
this.hacking_speed_mult = 1;
this.hacking_money_mult = 1;
this.hacking_grow_mult = 1;
//Experience and multipliers
this.hacking_exp = 0;
this.strength_exp = 0;
this.defense_exp = 0;
this.dexterity_exp = 0;
this.agility_exp = 0;
this.charisma_exp = 0;
this.intelligence_exp = 0;
this.hacking_mult = 1;
this.strength_mult = 1;
this.defense_mult = 1;
this.dexterity_mult = 1;
this.agility_mult = 1;
this.charisma_mult = 1;
this.hacking_exp_mult = 1;
this.strength_exp_mult = 1;
this.defense_exp_mult = 1;
this.dexterity_exp_mult = 1;
this.agility_exp_mult = 1;
this.charisma_exp_mult = 1;
this.company_rep_mult = 1;
this.faction_rep_mult = 1;
//Money
this.money = new Decimal(1000);
//IP Address of Starting (home) computer
this.homeComputer = "";
//Location information
this.city = CityName.Sector12;
this.location = "";
// Jobs that the player holds
// Map of company name (key) -> name of company position (value. Just the name, not the CompanyPosition object)
// The CompanyPosition name must match a key value in CompanyPositions
this.jobs = {};
// Company at which player is CURRENTLY working (only valid when the player is actively working)
this.companyName = ""; // Name of Company. Must match a key value in Companies map
// Servers
this.currentServer = ""; //IP address of Server currently being accessed through terminal
this.purchasedServers = []; //IP Addresses of purchased servers
// Hacknet Nodes/Servers
this.hacknetNodes = []; // Note: For Hacknet Servers, this array holds the IP addresses of the servers
this.hashManager = new HashManager();
//Factions
this.factions = []; //Names of all factions player has joined
this.factionInvitations = []; //Outstanding faction invitations
//Augmentations
this.queuedAugmentations = [];
this.augmentations = [];
this.sourceFiles = [];
//Crime statistics
this.numPeopleKilled = 0;
this.karma = 0;
this.crime_money_mult = 1;
this.crime_success_mult = 1;
//Flags/variables for working (Company, Faction, Creating Program, Taking Class)
this.isWorking = false;
this.focus = false;
this.workType = "";
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";
this.workHackExpGainRate = 0;
this.workStrExpGainRate = 0;
this.workDefExpGainRate = 0;
this.workDexExpGainRate = 0;
this.workAgiExpGainRate = 0;
this.workChaExpGainRate = 0;
this.workRepGainRate = 0;
this.workMoneyGainRate = 0;
this.workMoneyLossRate = 0;
this.workHackExpGained = 0;
this.workStrExpGained = 0;
this.workDefExpGained = 0;
this.workDexExpGained = 0;
this.workAgiExpGained = 0;
this.workChaExpGained = 0;
this.workRepGained = 0;
this.workMoneyGained = 0;
this.createProgramName = "";
this.createProgramReqLvl = 0;
this.className = "";
this.crimeType = "";
this.timeWorked = 0; //in ms
this.timeWorkedCreateProgram = 0;
this.timeNeededToCompleteWork = 0;
this.work_money_mult = 1;
//Hacknet Node multipliers
this.hacknet_node_money_mult = 1;
this.hacknet_node_purchase_cost_mult = 1;
this.hacknet_node_ram_cost_mult = 1;
this.hacknet_node_core_cost_mult = 1;
this.hacknet_node_level_cost_mult = 1;
//Stock Market
this.hasWseAccount = false;
this.hasTixApiAccess = false;
this.has4SData = false;
this.has4SDataTixApi = false;
//Gang
this.gang = null;
//Corporation
this.corporation = null;
//Bladeburner
this.bladeburner = null;
this.bladeburner_max_stamina_mult = 1;
this.bladeburner_stamina_gain_mult = 1;
this.bladeburner_analysis_mult = 1; //Field Analysis Only
this.bladeburner_success_chance_mult = 1;
// Sleeves & Re-sleeving
this.sleeves = [];
this.resleeves = [];
this.sleevesFromCovenant = 0; // # of Duplicate sleeves purchased from the covenant
//bitnode
this.bitNodeN = 1;
//Flags for determining whether certain "thresholds" have been achieved
this.firstFacInvRecvd = false;
this.firstAugPurchased = false;
this.firstTimeTraveled = false;
this.firstProgramAvailable = false;
//Used to store the last update time.
this.lastUpdate = 0;
this.totalPlaytime = 0;
this.playtimeSinceLastAug = 0;
this.playtimeSinceLastBitnode = 0;
// Keep track of where money comes from
this.moneySourceA = new MoneySourceTracker(); // Where money comes from since last-installed Augmentation
this.moneySourceB = new MoneySourceTracker(); // Where money comes from for this entire BitNode run
// Production since last Augmentation installation
this.scriptProdSinceLastAug = 0;
this.exploits = [];
}
// Apply player methods to the prototype using Object.assign()
Object.assign(
PlayerObject.prototype,
generalMethods,
serverMethods,
bladeburnerMethods,
corporationMethods,
gangMethods,
augmentationMethods,
);
PlayerObject.prototype.toJSON = function () {
return Generic_toJSON("PlayerObject", this);
};
PlayerObject.fromJSON = function (value) {
return Generic_fromJSON(PlayerObject, value.data);
};
Reviver.constructors.PlayerObject = PlayerObject;

@ -0,0 +1,596 @@
import * as augmentationMethods from "./PlayerObjectAugmentationMethods";
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 { IMap } from "../../types";
import { Resleeve } from "../Resleeving/Resleeve";
import { Sleeve } from "../Sleeve/Sleeve";
import { IPlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile";
import { Exploit } from "../../Exploits/Exploit";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { CompanyPosition } from "../../Company/CompanyPosition";
import { Server } from "../../Server/Server";
import { HacknetServer } from "../../Hacknet/HacknetServer";
import { Faction } from "../../Faction/Faction";
import { Company } from "../../Company/Company";
import { Augmentation } from "../../Augmentation/Augmentation";
import { IRouter } from "../../ui/Router";
import { ICodingContractReward } from "../../CodingContracts";
import { IPlayer } from "../IPlayer";
import { LocationName } from "../../Locations/data/LocationNames";
import { IPlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
import { ICorporation } from "../../Corporation/ICorporation";
import { IGang } from "../../Gang/IGang";
import { IBladeburner } from "../../Bladeburner/IBladeburner";
import { HacknetNode } from "../../Hacknet/HacknetNode";
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 Decimal from "decimal.js";
export class PlayerObject implements IPlayer {
// Class members
augmentations: IPlayerOwnedAugmentation[];
bitNodeN: number;
city: CityName;
companyName: string;
corporation: ICorporation | null;
gang: IGang | null;
bladeburner: IBladeburner | null;
currentServer: string;
factions: string[];
factionInvitations: string[];
hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server
has4SData: boolean;
has4SDataTixApi: boolean;
hashManager: HashManager;
hasTixApiAccess: boolean;
hasWseAccount: boolean;
homeComputer: string;
hp: number;
jobs: IMap<string>;
init: () => void;
isWorking: boolean;
karma: number;
numPeopleKilled: number;
location: LocationName;
max_hp: number;
money: any;
moneySourceA: MoneySourceTracker;
moneySourceB: MoneySourceTracker;
playtimeSinceLastAug: number;
playtimeSinceLastBitnode: number;
purchasedServers: any[];
queuedAugmentations: IPlayerOwnedAugmentation[];
resleeves: Resleeve[];
scriptProdSinceLastAug: number;
sleeves: Sleeve[];
sleevesFromCovenant: number;
sourceFiles: IPlayerOwnedSourceFile[];
exploits: Exploit[];
lastUpdate: number;
totalPlaytime: number;
// Stats
hacking_skill: number;
strength: number;
defense: number;
dexterity: number;
agility: number;
charisma: number;
intelligence: number;
// Experience
hacking_exp: number;
strength_exp: number;
defense_exp: number;
dexterity_exp: number;
agility_exp: number;
charisma_exp: number;
intelligence_exp: number;
// Multipliers
hacking_chance_mult: number;
hacking_speed_mult: number;
hacking_money_mult: number;
hacking_grow_mult: number;
hacking_mult: number;
hacking_exp_mult: number;
strength_mult: number;
strength_exp_mult: number;
defense_mult: number;
defense_exp_mult: number;
dexterity_mult: number;
dexterity_exp_mult: number;
agility_mult: number;
agility_exp_mult: number;
charisma_mult: number;
charisma_exp_mult: number;
hacknet_node_money_mult: number;
hacknet_node_purchase_cost_mult: number;
hacknet_node_ram_cost_mult: number;
hacknet_node_core_cost_mult: number;
hacknet_node_level_cost_mult: number;
company_rep_mult: number;
faction_rep_mult: number;
work_money_mult: number;
crime_success_mult: number;
crime_money_mult: number;
bladeburner_max_stamina_mult: number;
bladeburner_stamina_gain_mult: number;
bladeburner_analysis_mult: number;
bladeburner_success_chance_mult: number;
createProgramReqLvl: number;
factionWorkType: string;
createProgramName: string;
timeWorkedCreateProgram: number;
crimeType: string;
committingCrimeThruSingFn: boolean;
singFnCrimeWorkerScript: WorkerScript | null;
timeNeededToCompleteWork: number;
focus: boolean;
className: string;
currentWorkFactionName: string;
workType: string;
currentWorkFactionDescription: string;
timeWorked: number;
workMoneyGained: number;
workMoneyGainRate: number;
workRepGained: number;
workRepGainRate: number;
workHackExpGained: number;
workHackExpGainRate: number;
workStrExpGained: number;
workStrExpGainRate: number;
workDefExpGained: number;
workDefExpGainRate: number;
workDexExpGained: number;
workDexExpGainRate: number;
workAgiExpGained: number;
workAgiExpGainRate: number;
workChaExpGained: number;
workChaExpGainRate: number;
workMoneyLossRate: number;
// Methods
work: (numCycles: number) => boolean;
workPartTime: (numCycles: number) => boolean;
workForFaction: (numCycles: number) => boolean;
applyForAgentJob: (sing?: boolean) => boolean;
applyForBusinessConsultantJob: (sing?: boolean) => boolean;
applyForBusinessJob: (sing?: boolean) => boolean;
applyForEmployeeJob: (sing?: boolean) => boolean;
applyForItJob: (sing?: boolean) => boolean;
applyForJob: (entryPosType: CompanyPosition, sing?: boolean) => boolean;
applyForNetworkEngineerJob: (sing?: boolean) => boolean;
applyForPartTimeEmployeeJob: (sing?: boolean) => boolean;
applyForPartTimeWaiterJob: (sing?: boolean) => boolean;
applyForSecurityEngineerJob: (sing?: boolean) => boolean;
applyForSecurityJob: (sing?: boolean) => boolean;
applyForSoftwareConsultantJob: (sing?: boolean) => boolean;
applyForSoftwareJob: (sing?: boolean) => boolean;
applyForWaiterJob: (sing?: boolean) => boolean;
canAccessBladeburner: () => boolean;
canAccessCorporation: () => boolean;
canAccessGang: () => boolean;
canAccessResleeving: () => boolean;
canAfford: (cost: number) => boolean;
gainHackingExp: (exp: number) => void;
gainStrengthExp: (exp: number) => void;
gainDefenseExp: (exp: number) => void;
gainDexterityExp: (exp: number) => void;
gainAgilityExp: (exp: number) => void;
gainCharismaExp: (exp: number) => void;
gainIntelligenceExp: (exp: number) => void;
gainMoney: (money: number) => void;
getCurrentServer: () => Server | HacknetServer;
getGangFaction: () => Faction;
getGangName: () => string;
getHomeComputer: () => Server;
getNextCompanyPosition: (company: Company, entryPosType: CompanyPosition) => CompanyPosition | null;
getUpgradeHomeRamCost: () => number;
gotoLocation: (to: LocationName) => boolean;
hasAugmentation: (aug: Augmentation) => boolean;
hasCorporation: () => boolean;
hasGangWith: (facName: string) => boolean;
hasTorRouter: () => boolean;
hasProgram: (program: string) => boolean;
inBladeburner: () => boolean;
inGang: () => boolean;
isQualified: (company: Company, position: CompanyPosition) => boolean;
loseMoney: (money: number) => void;
reapplyAllAugmentations: (resetMultipliers?: boolean) => void;
reapplyAllSourceFiles: () => void;
regenerateHp: (amt: number) => void;
recordMoneySource: (amt: number, source: string) => void;
setMoney: (amt: number) => void;
singularityStopWork: () => void;
startBladeburner: (p: any) => void;
startFactionWork: (router: IRouter, faction: Faction) => void;
startClass: (router: IRouter, costMult: number, expMult: number, className: string) => void;
startCorporation: (corpName: string, additionalShares?: number) => void;
startCrime: (
router: IRouter,
crimeType: string,
hackExp: number,
strExp: number,
defExp: number,
dexExp: number,
agiExp: number,
chaExp: number,
money: number,
time: number,
singParams: any,
) => void;
startFactionFieldWork: (router: IRouter, faction: Faction) => void;
startFactionHackWork: (router: IRouter, faction: Faction) => void;
startFactionSecurityWork: (router: IRouter, faction: Faction) => void;
startFocusing: () => void;
startGang: (facName: string, isHacking: boolean) => void;
startWork: (router: IRouter, companyName: string) => void;
startWorkPartTime: (router: IRouter, companyName: string) => void;
takeDamage: (amt: number) => boolean;
travel: (to: CityName) => boolean;
giveExploit: (exploit: Exploit) => void;
queryStatFromString: (str: string) => number;
getIntelligenceBonus: (weight: number) => number;
getCasinoWinnings: () => number;
quitJob: (company: string) => void;
createHacknetServer: () => HacknetServer;
startCreateProgramWork: (router: IRouter, programName: string, time: number, reqLevel: number) => void;
queueAugmentation: (augmentationName: string) => void;
receiveInvite: (factionName: string) => void;
updateSkillLevels: () => void;
gainCodingContractReward: (reward: ICodingContractReward, difficulty?: number) => string;
stopFocusing: () => void;
finishFactionWork: (cancelled: boolean, sing?: boolean) => string;
finishClass: (sing?: boolean) => string;
finishWork: (cancelled: boolean, sing?: boolean) => string;
cancelationPenalty: () => number;
finishWorkPartTime: (sing?: boolean) => string;
finishCrime: (cancelled: boolean) => string;
finishCreateProgramWork: (cancelled: boolean) => string;
resetMultipliers: () => void;
prestigeAugmentation: () => void;
prestigeSourceFile: () => void;
calculateSkill: (exp: number, mult?: number) => number;
resetWorkStatus: (generalType?: string, group?: string, workType?: string) => void;
getWorkHackExpGain: () => number;
getWorkStrExpGain: () => number;
getWorkDefExpGain: () => number;
getWorkDexExpGain: () => number;
getWorkAgiExpGain: () => number;
getWorkChaExpGain: () => number;
getWorkRepGain: () => number;
getWorkMoneyGain: () => number;
processWorkEarnings: (cycles: number) => void;
hospitalize: () => void;
createProgramWork: (numCycles: number) => boolean;
takeClass: (numCycles: number) => boolean;
commitCrime: (numCycles: number) => boolean;
checkForFactionInvitations: () => Faction[];
setBitNodeNumber: (n: number) => void;
getMult: (name: string) => number;
setMult: (name: string, mult: number) => void;
constructor() {
//Skills and stats
this.hacking_skill = 1;
//Combat stats
this.hp = 10;
this.max_hp = 10;
this.strength = 1;
this.defense = 1;
this.dexterity = 1;
this.agility = 1;
//Labor stats
this.charisma = 1;
//Special stats
this.intelligence = 0;
//Hacking multipliers
this.hacking_chance_mult = 1;
this.hacking_speed_mult = 1;
this.hacking_money_mult = 1;
this.hacking_grow_mult = 1;
//Experience and multipliers
this.hacking_exp = 0;
this.strength_exp = 0;
this.defense_exp = 0;
this.dexterity_exp = 0;
this.agility_exp = 0;
this.charisma_exp = 0;
this.intelligence_exp = 0;
this.hacking_mult = 1;
this.strength_mult = 1;
this.defense_mult = 1;
this.dexterity_mult = 1;
this.agility_mult = 1;
this.charisma_mult = 1;
this.hacking_exp_mult = 1;
this.strength_exp_mult = 1;
this.defense_exp_mult = 1;
this.dexterity_exp_mult = 1;
this.agility_exp_mult = 1;
this.charisma_exp_mult = 1;
this.company_rep_mult = 1;
this.faction_rep_mult = 1;
//Money
this.money = new Decimal(1000);
//IP Address of Starting (home) computer
this.homeComputer = "";
//Location information
this.city = CityName.Sector12;
this.location = LocationName.TravelAgency;
// Jobs that the player holds
// Map of company name (key) -> name of company position (value. Just the name, not the CompanyPosition object)
// The CompanyPosition name must match a key value in CompanyPositions
this.jobs = {};
// Company at which player is CURRENTLY working (only valid when the player is actively working)
this.companyName = ""; // Name of Company. Must match a key value in Companies ma;
// Servers
this.currentServer = ""; //IP address of Server currently being accessed through termina;
this.purchasedServers = []; //IP Addresses of purchased server;
// Hacknet Nodes/Servers
this.hacknetNodes = []; // Note= For Hacknet Servers, this array holds the IP addresses of the server;
this.hashManager = new HashManager();
//Factions
this.factions = []; //Names of all factions player has joine;
this.factionInvitations = []; //Outstanding faction invitation;
//Augmentations
this.queuedAugmentations = [];
this.augmentations = [];
this.sourceFiles = [];
//Crime statistics
this.numPeopleKilled = 0;
this.karma = 0;
this.crime_money_mult = 1;
this.crime_success_mult = 1;
//Flags/variables for working (Company, Faction, Creating Program, Taking Class)
this.isWorking = false;
this.focus = false;
this.workType = "";
this.currentWorkFactionName = "";
this.currentWorkFactionDescription = "";
this.workHackExpGainRate = 0;
this.workStrExpGainRate = 0;
this.workDefExpGainRate = 0;
this.workDexExpGainRate = 0;
this.workAgiExpGainRate = 0;
this.workChaExpGainRate = 0;
this.workRepGainRate = 0;
this.workMoneyGainRate = 0;
this.workMoneyLossRate = 0;
this.workHackExpGained = 0;
this.workStrExpGained = 0;
this.workDefExpGained = 0;
this.workDexExpGained = 0;
this.workAgiExpGained = 0;
this.workChaExpGained = 0;
this.workRepGained = 0;
this.workMoneyGained = 0;
this.createProgramName = "";
this.createProgramReqLvl = 0;
this.className = "";
this.crimeType = "";
(this.timeWorked = 0), //in m;
(this.timeWorkedCreateProgram = 0);
this.timeNeededToCompleteWork = 0;
this.work_money_mult = 1;
//Hacknet Node multipliers
this.hacknet_node_money_mult = 1;
this.hacknet_node_purchase_cost_mult = 1;
this.hacknet_node_ram_cost_mult = 1;
this.hacknet_node_core_cost_mult = 1;
this.hacknet_node_level_cost_mult = 1;
//Stock Market
this.hasWseAccount = false;
this.hasTixApiAccess = false;
this.has4SData = false;
this.has4SDataTixApi = false;
//Gang
this.gang = null;
//Corporation
this.corporation = null;
//Bladeburner
this.bladeburner = null;
this.bladeburner_max_stamina_mult = 1;
this.bladeburner_stamina_gain_mult = 1;
(this.bladeburner_analysis_mult = 1), //Field Analysis Onl;
(this.bladeburner_success_chance_mult = 1);
// Sleeves & Re-sleeving
this.sleeves = [];
this.resleeves = [];
(this.sleevesFromCovenant = 0), // # of Duplicate sleeves purchased from the covenan;
//bitnode
(this.bitNodeN = 1);
//Used to store the last update time.
this.lastUpdate = 0;
this.totalPlaytime = 0;
this.playtimeSinceLastAug = 0;
this.playtimeSinceLastBitnode = 0;
// Keep track of where money comes from
(this.moneySourceA = new MoneySourceTracker()), // Where money comes from since last-installed Augmentatio;
(this.moneySourceB = new MoneySourceTracker()), // Where money comes from for this entire BitNode ru;
// Production since last Augmentation installation
(this.scriptProdSinceLastAug = 0);
this.exploits = [];
this.init = generalMethods.init;
this.prestigeAugmentation = generalMethods.prestigeAugmentation;
this.prestigeSourceFile = generalMethods.prestigeSourceFile;
this.receiveInvite = generalMethods.receiveInvite;
this.calculateSkill = generalMethods.calculateSkill;
this.updateSkillLevels = generalMethods.updateSkillLevels;
this.resetMultipliers = generalMethods.resetMultipliers;
this.hasProgram = generalMethods.hasProgram;
this.setMoney = generalMethods.setMoney;
this.gainMoney = generalMethods.gainMoney;
this.loseMoney = generalMethods.loseMoney;
this.canAfford = generalMethods.canAfford;
this.recordMoneySource = generalMethods.recordMoneySource;
this.gainHackingExp = generalMethods.gainHackingExp;
this.gainStrengthExp = generalMethods.gainStrengthExp;
this.gainDefenseExp = generalMethods.gainDefenseExp;
this.gainDexterityExp = generalMethods.gainDexterityExp;
this.gainAgilityExp = generalMethods.gainAgilityExp;
this.gainCharismaExp = generalMethods.gainCharismaExp;
this.gainIntelligenceExp = generalMethods.gainIntelligenceExp;
this.queryStatFromString = generalMethods.queryStatFromString;
this.resetWorkStatus = generalMethods.resetWorkStatus;
this.processWorkEarnings = generalMethods.processWorkEarnings;
this.startWork = generalMethods.startWork;
this.cancelationPenalty = generalMethods.cancelationPenalty;
this.work = generalMethods.work;
this.finishWork = generalMethods.finishWork;
this.startWorkPartTime = generalMethods.startWorkPartTime;
this.workPartTime = generalMethods.workPartTime;
this.finishWorkPartTime = generalMethods.finishWorkPartTime;
this.startFocusing = generalMethods.startFocusing;
this.stopFocusing = generalMethods.stopFocusing;
this.startFactionWork = generalMethods.startFactionWork;
this.startFactionHackWork = generalMethods.startFactionHackWork;
this.startFactionFieldWork = generalMethods.startFactionFieldWork;
this.startFactionSecurityWork = generalMethods.startFactionSecurityWork;
this.workForFaction = generalMethods.workForFaction;
this.finishFactionWork = generalMethods.finishFactionWork;
this.getWorkMoneyGain = generalMethods.getWorkMoneyGain;
this.getWorkHackExpGain = generalMethods.getWorkHackExpGain;
this.getWorkStrExpGain = generalMethods.getWorkStrExpGain;
this.getWorkDefExpGain = generalMethods.getWorkDefExpGain;
this.getWorkDexExpGain = generalMethods.getWorkDexExpGain;
this.getWorkAgiExpGain = generalMethods.getWorkAgiExpGain;
this.getWorkChaExpGain = generalMethods.getWorkChaExpGain;
this.getWorkRepGain = generalMethods.getWorkRepGain;
this.startCreateProgramWork = generalMethods.startCreateProgramWork;
this.createProgramWork = generalMethods.createProgramWork;
this.finishCreateProgramWork = generalMethods.finishCreateProgramWork;
this.startClass = generalMethods.startClass;
this.takeClass = generalMethods.takeClass;
this.finishClass = generalMethods.finishClass;
this.startCrime = generalMethods.startCrime;
this.commitCrime = generalMethods.commitCrime;
this.finishCrime = generalMethods.finishCrime;
this.singularityStopWork = generalMethods.singularityStopWork;
this.takeDamage = generalMethods.takeDamage;
this.regenerateHp = generalMethods.regenerateHp;
this.hospitalize = generalMethods.hospitalize;
this.applyForJob = generalMethods.applyForJob;
this.getNextCompanyPosition = generalMethods.getNextCompanyPosition;
this.quitJob = generalMethods.quitJob;
this.applyForSoftwareJob = generalMethods.applyForSoftwareJob;
this.applyForSoftwareConsultantJob = generalMethods.applyForSoftwareConsultantJob;
this.applyForItJob = generalMethods.applyForItJob;
this.applyForSecurityEngineerJob = generalMethods.applyForSecurityEngineerJob;
this.applyForNetworkEngineerJob = generalMethods.applyForNetworkEngineerJob;
this.applyForBusinessJob = generalMethods.applyForBusinessJob;
this.applyForBusinessConsultantJob = generalMethods.applyForBusinessConsultantJob;
this.applyForSecurityJob = generalMethods.applyForSecurityJob;
this.applyForAgentJob = generalMethods.applyForAgentJob;
this.applyForEmployeeJob = generalMethods.applyForEmployeeJob;
this.applyForPartTimeEmployeeJob = generalMethods.applyForPartTimeEmployeeJob;
this.applyForWaiterJob = generalMethods.applyForWaiterJob;
this.applyForPartTimeWaiterJob = generalMethods.applyForPartTimeWaiterJob;
this.isQualified = generalMethods.isQualified;
this.reapplyAllAugmentations = generalMethods.reapplyAllAugmentations;
this.reapplyAllSourceFiles = generalMethods.reapplyAllSourceFiles;
this.checkForFactionInvitations = generalMethods.checkForFactionInvitations;
this.setBitNodeNumber = generalMethods.setBitNodeNumber;
this.queueAugmentation = generalMethods.queueAugmentation;
this.gainCodingContractReward = generalMethods.gainCodingContractReward;
this.travel = generalMethods.travel;
this.gotoLocation = generalMethods.gotoLocation;
this.canAccessResleeving = generalMethods.canAccessResleeving;
this.giveExploit = generalMethods.giveExploit;
this.getIntelligenceBonus = generalMethods.getIntelligenceBonus;
this.getCasinoWinnings = generalMethods.getCasinoWinnings;
this.hasAugmentation = augmentationMethods.hasAugmentation;
this.canAccessBladeburner = bladeburnerMethods.canAccessBladeburner;
this.inBladeburner = bladeburnerMethods.inBladeburner;
this.startBladeburner = bladeburnerMethods.startBladeburner;
this.canAccessCorporation = corporationMethods.canAccessCorporation;
this.hasCorporation = corporationMethods.hasCorporation;
this.startCorporation = corporationMethods.startCorporation;
this.canAccessGang = gangMethods.canAccessGang;
this.getGangFaction = gangMethods.getGangFaction;
this.getGangName = gangMethods.getGangName;
this.hasGangWith = gangMethods.hasGangWith;
this.inGang = gangMethods.inGang;
this.startGang = gangMethods.startGang;
this.hasTorRouter = serverMethods.hasTorRouter;
this.getCurrentServer = serverMethods.getCurrentServer;
this.getHomeComputer = serverMethods.getHomeComputer;
this.getUpgradeHomeRamCost = serverMethods.getUpgradeHomeRamCost;
this.createHacknetServer = serverMethods.createHacknetServer;
this.factionWorkType = "";
this.committingCrimeThruSingFn = false;
this.singFnCrimeWorkerScript = null;
this.getMult = generalMethods.getMult;
this.setMult = generalMethods.setMult;
}
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
return Generic_toJSON("PlayerObject", this);
}
/**
* Initiatizes a PlayerObject object from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): PlayerObject {
return Generic_fromJSON(PlayerObject, value.data);
}
}
Reviver.constructors.PlayerObject = PlayerObject;

@ -1,7 +1,8 @@
import { Bladeburner } from "../../Bladeburner/Bladeburner"; import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IPlayer } from "../IPlayer";
export function canAccessBladeburner() { export function canAccessBladeburner(this: IPlayer) {
if (this.bitNodeN === 8) { if (this.bitNodeN === 8) {
return false; return false;
} }
@ -9,13 +10,13 @@ export function canAccessBladeburner() {
return this.bitNodeN === 6 || this.bitNodeN === 7 || SourceFileFlags[6] > 0 || SourceFileFlags[7] > 0; return this.bitNodeN === 6 || this.bitNodeN === 7 || SourceFileFlags[6] > 0 || SourceFileFlags[7] > 0;
} }
export function inBladeburner() { export function inBladeburner(this: IPlayer): boolean {
if (this.bladeburner == null) { if (this.bladeburner == null) {
return false; return false;
} }
return this.bladeburner instanceof Bladeburner; return this.bladeburner instanceof Bladeburner;
} }
export function startBladeburner() { export function startBladeburner(this: IPlayer): void {
this.bladeburner = new Bladeburner(this); this.bladeburner = new Bladeburner(this);
} }

@ -1,18 +1,19 @@
import { Corporation } from "../../Corporation/Corporation"; import { Corporation } from "../../Corporation/Corporation";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IPlayer } from "../IPlayer";
export function canAccessCorporation() { export function canAccessCorporation(this: IPlayer): boolean {
return this.bitNodeN === 3 || SourceFileFlags[3] > 0; return this.bitNodeN === 3 || SourceFileFlags[3] > 0;
} }
export function hasCorporation() { export function hasCorporation(this: IPlayer): boolean {
if (this.corporation == null) { if (this.corporation == null) {
return false; return false;
} }
return this.corporation instanceof Corporation; return this.corporation instanceof Corporation;
} }
export function startCorporation(corpName, additionalShares = 0) { export function startCorporation(this: IPlayer, corpName: string, additionalShares = 0): void {
this.corporation = new Corporation({ this.corporation = new Corporation({
name: corpName, name: corpName,
}); });

@ -1,53 +0,0 @@
import { Factions } from "../../Faction/Factions";
import { Gang } from "../../Gang/Gang";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
// 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 <= BitNodeMultipliers.GangKarmaRequirement * GangKarmaRequirement;
}
export function getGangFaction() {
const fac = Factions[this.gang.facName];
if (fac == null) {
throw new Error(`Gang has invalid faction name: ${this.gang.facName}`);
}
return fac;
}
export function getGangName() {
return this.inGang() ? this.gang.facName : "";
}
export function hasGangWith(facName) {
return this.inGang() && this.gang.facName === 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;
}

@ -0,0 +1,69 @@
import { Factions } from "../../Faction/Factions";
import { Faction } from "../../Faction/Faction";
import { Gang } from "../../Gang/Gang";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { IPlayer } from "../IPlayer";
// Amount of negative karma needed to manage a gang in BitNodes other than 2
const GangKarmaRequirement = -54000;
export function canAccessGang(this: IPlayer): boolean {
if (this.bitNodeN === 2) {
return true;
}
if (SourceFileFlags[2] <= 0) {
return false;
}
return this.karma <= BitNodeMultipliers.GangKarmaRequirement * GangKarmaRequirement;
}
export function getGangFaction(this: IPlayer): Faction {
const gang = this.gang;
if (gang === null) {
throw new Error("Cannot get gang faction because player is not in a gang.");
}
const fac = Factions[gang.facName];
if (fac == null) {
throw new Error(`Gang has invalid faction name: ${gang.facName}`);
}
return fac;
}
export function getGangName(this: IPlayer): string {
if (!this.inGang()) return "";
const gang = this.gang;
if (gang === null) {
throw new Error("Cannot get gang faction because player is not in a gang.");
}
return gang.facName;
}
export function hasGangWith(this: IPlayer, facName: string): boolean {
if (!this.inGang()) return false;
const gang = this.gang;
if (gang === null) {
throw new Error("Cannot get gang faction because player is not in a gang.");
}
return gang.facName === facName;
}
export function inGang(this: IPlayer): boolean {
if (this.gang == null || this.gang == undefined) {
return false;
}
return this.gang instanceof Gang;
}
export function startGang(this: IPlayer, factionName: string, hacking: boolean): void {
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;
}

@ -15,12 +15,16 @@ export function hasTorRouter(this: IPlayer): boolean {
return SpecialServerIps.hasOwnProperty("Darkweb Server"); return SpecialServerIps.hasOwnProperty("Darkweb Server");
} }
export function getCurrentServer(this: IPlayer): Server | HacknetServer | null { export function getCurrentServer(this: IPlayer): Server | HacknetServer {
return AllServers[this.currentServer]; const server = AllServers[this.currentServer];
if (server === null) throw new Error("somehow connected to a server that does not exist.");
return server;
} }
export function getHomeComputer(this: IPlayer): Server | HacknetServer | null { export function getHomeComputer(this: IPlayer): Server {
return AllServers[this.homeComputer]; const home = AllServers[this.homeComputer];
if (home instanceof Server) return home;
throw new Error("home computer was not a normal server");
} }
export function getUpgradeHomeRamCost(this: IPlayer): number { export function getUpgradeHomeRamCost(this: IPlayer): number {

3
src/Player.d.ts vendored

@ -1,3 +0,0 @@
import { IPlayer } from "./PersonObjects/IPlayer";
export declare let Player: IPlayer;

@ -8,7 +8,7 @@ import Decimal from "decimal.js";
export let Player = new PlayerObject(); export let Player = new PlayerObject();
export function loadPlayer(saveString) { export function loadPlayer(saveString: string): void {
Player = JSON.parse(saveString, Reviver); Player = JSON.parse(saveString, Reviver);
// Parse Decimal.js objects // Parse Decimal.js objects
@ -19,8 +19,8 @@ export function loadPlayer(saveString) {
Player.corporation.revenue = new Decimal(Player.corporation.revenue); Player.corporation.revenue = new Decimal(Player.corporation.revenue);
Player.corporation.expenses = new Decimal(Player.corporation.expenses); Player.corporation.expenses = new Decimal(Player.corporation.expenses);
for (var i = 0; i < Player.corporation.divisions.length; ++i) { for (let i = 0; i < Player.corporation.divisions.length; ++i) {
var ind = Player.corporation.divisions[i]; const ind = Player.corporation.divisions[i];
ind.lastCycleRevenue = new Decimal(ind.lastCycleRevenue); ind.lastCycleRevenue = new Decimal(ind.lastCycleRevenue);
ind.lastCycleExpenses = new Decimal(ind.lastCycleExpenses); ind.lastCycleExpenses = new Decimal(ind.lastCycleExpenses);
ind.thisCycleRevenue = new Decimal(ind.thisCycleRevenue); ind.thisCycleRevenue = new Decimal(ind.thisCycleRevenue);

2
src/Prestige.d.ts vendored

@ -1,2 +0,0 @@
export declare function prestigeAugmentation(): void;
export declare function prestigeSourceFile(flume: boolean): void;

@ -14,6 +14,7 @@ import { updateHashManagerCapacity } from "./Hacknet/HacknetHelpers";
import { initMessages } from "./Message/MessageHelpers"; import { initMessages } from "./Message/MessageHelpers";
import { prestigeWorkerScripts } from "./NetscriptWorker"; import { prestigeWorkerScripts } from "./NetscriptWorker";
import { Player } from "./Player"; import { Player } from "./Player";
import { Router } from "./ui/GameRoot";
import { resetPidCounter } from "./Netscript/Pid"; import { resetPidCounter } from "./Netscript/Pid";
import { LiteratureNames } from "./Literature/data/LiteratureNames"; import { LiteratureNames } from "./Literature/data/LiteratureNames";
@ -31,7 +32,7 @@ import Decimal from "decimal.js";
const BitNode8StartingMoney = 250e6; const BitNode8StartingMoney = 250e6;
// Prestige by purchasing augmentation // Prestige by purchasing augmentation
function prestigeAugmentation() { function prestigeAugmentation(): void {
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
const maintainMembership = Player.factions.filter(function (faction) { const maintainMembership = Player.factions.filter(function (faction) {
@ -42,7 +43,7 @@ function prestigeAugmentation() {
// Delete all Worker Scripts objects // Delete all Worker Scripts objects
prestigeWorkerScripts(); prestigeWorkerScripts();
var homeComp = Player.getHomeComputer(); const homeComp = Player.getHomeComputer();
// Delete all servers except home computer // Delete all servers except home computer
prestigeAllServers(); prestigeAllServers();
@ -70,23 +71,22 @@ function prestigeAugmentation() {
initForeignServers(Player.getHomeComputer()); initForeignServers(Player.getHomeComputer());
// Gain favor for Companies // Gain favor for Companies
for (var member in Companies) { for (const member in Companies) {
if (Companies.hasOwnProperty(member)) { if (Companies.hasOwnProperty(member)) {
Companies[member].gainFavor(); Companies[member].gainFavor();
} }
} }
// Gain favor for factions // Gain favor for factions
for (var member in Factions) { for (const member in Factions) {
if (Factions.hasOwnProperty(member)) { if (Factions.hasOwnProperty(member)) {
Factions[member].gainFavor(); Factions[member].gainFavor();
} }
} }
// Stop a Terminal action if there is onerror // Stop a Terminal action if there is onerror
if (Engine._actionInProgress) { if (Terminal.action !== null) {
Engine._actionInProgress = false; Terminal.finishAction(Router, Player, true);
Terminal.finishAction(true);
} }
// Re-initialize things - This will update any changes // Re-initialize things - This will update any changes
@ -102,8 +102,9 @@ function prestigeAugmentation() {
initMessages(); initMessages();
// Gang // Gang
if (Player.inGang()) { const gang = Player.gang;
const faction = Factions[Player.gang.facName]; if (Player.inGang() && gang !== null) {
const faction = Factions[gang.facName];
if (faction instanceof Faction) { if (faction instanceof Faction) {
joinFaction(faction); joinFaction(faction);
} }
@ -131,8 +132,12 @@ function prestigeAugmentation() {
// Red Pill // Red Pill
if (augmentationExists(AugmentationNames.TheRedPill) && Augmentations[AugmentationNames.TheRedPill].owned) { if (augmentationExists(AugmentationNames.TheRedPill) && Augmentations[AugmentationNames.TheRedPill].owned) {
var WorldDaemon = AllServers[SpecialServerIps[SpecialServerNames.WorldDaemon]]; const WorldDaemonIP = SpecialServerIps[SpecialServerNames.WorldDaemon];
var DaedalusServer = AllServers[SpecialServerIps[SpecialServerNames.DaedalusServer]]; if (typeof WorldDaemonIP !== "string") throw new Error("WorldDaemonIP should be string");
const WorldDaemon = AllServers[WorldDaemonIP];
const DaedalusServerIP = SpecialServerIps[SpecialServerNames.DaedalusServer];
if (typeof DaedalusServerIP !== "string") throw new Error("DaedalusServerIP should be string");
const DaedalusServer = AllServers[DaedalusServerIP];
if (WorldDaemon && DaedalusServer) { if (WorldDaemon && DaedalusServer) {
WorldDaemon.serversOnNetwork.push(DaedalusServer.ip); WorldDaemon.serversOnNetwork.push(DaedalusServer.ip);
DaedalusServer.serversOnNetwork.push(WorldDaemon.ip); DaedalusServer.serversOnNetwork.push(WorldDaemon.ip);
@ -143,7 +148,7 @@ function prestigeAugmentation() {
} }
// Prestige by destroying Bit Node and gaining a Source File // Prestige by destroying Bit Node and gaining a Source File
function prestigeSourceFile(flume) { function prestigeSourceFile(flume: boolean): void {
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
updateSourceFileFlags(Player); updateSourceFileFlags(Player);
@ -189,9 +194,8 @@ function prestigeSourceFile(flume) {
} }
// Stop a Terminal action if there is one // Stop a Terminal action if there is one
if (Engine._actionInProgress) { if (Terminal.action !== null) {
Engine._actionInProgress = false; Terminal.finishAction(Router, Player, true);
Terminal.finishAction(true);
} }
// Delete all Augmentations // Delete all Augmentations
@ -249,7 +253,6 @@ function prestigeSourceFile(flume) {
deleteStockMarket(); deleteStockMarket();
} }
if (Player.inGang()) clearGangUI();
Player.gang = null; Player.gang = null;
Player.corporation = null; Player.corporation = null;
resetIndustryResearchTrees(); resetIndustryResearchTrees();

@ -53,10 +53,9 @@ export const programsMetadata: IProgramCreationParams[] = [
terminal.print("You already have root access to this computer. There is no reason to run NUKE.exe"); terminal.print("You already have root access to this computer. There is no reason to run NUKE.exe");
return; return;
} }
if (server.openPortCount >= server.numOpenPortsRequired) {
if (server.openPortCount >= player.getCurrentServer().numOpenPortsRequired) {
server.hasAdminRights = true; server.hasAdminRights = true;
terminal.print("NUKE successful! Gained root access to " + player.getCurrentServer().hostname); terminal.print("NUKE successful! Gained root access to " + server.hostname);
// TODO: Make this take time rather than be instant // TODO: Make this take time rather than be instant
return; return;
} }

3
src/RedPill.d.ts vendored

@ -1,3 +0,0 @@
export declare let redPillFlag: boolean;
export declare function enterBitNode(router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number);
export declare function setRedPillFlag(b: boolean): void;

@ -8,14 +8,15 @@ import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { SourceFiles } from "./SourceFile/SourceFiles"; import { SourceFiles } from "./SourceFile/SourceFiles";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { IRouter } from "./ui/Router";
export let redPillFlag = false; export let redPillFlag = false;
export function setRedPillFlag(b) { export function setRedPillFlag(b: boolean): void {
redPillFlag = b; redPillFlag = b;
} }
function giveSourceFile(bitNodeNumber) { function giveSourceFile(bitNodeNumber: number): void {
var sourceFileKey = "SourceFile" + bitNodeNumber.toString(); var sourceFileKey = "SourceFile" + bitNodeNumber.toString();
var sourceFile = SourceFiles[sourceFileKey]; var sourceFile = SourceFiles[sourceFileKey];
if (sourceFile == null) { if (sourceFile == null) {
@ -62,7 +63,7 @@ function giveSourceFile(bitNodeNumber) {
} }
} }
export function enterBitNode(router, flume, destroyedBitNode, newBitNode) { export function enterBitNode(router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number): void {
if (!flume) { if (!flume) {
giveSourceFile(destroyedBitNode); giveSourceFile(destroyedBitNode);
} else { } else {

@ -27,25 +27,23 @@ import Decimal from "decimal.js";
/* SaveObject.js /* SaveObject.js
* Defines the object used to save/load games * Defines the object used to save/load games
*/ */
let saveObject = new BitburnerSaveObject();
function BitburnerSaveObject() { class BitburnerSaveObject {
this.PlayerSave = ""; PlayerSave = "";
this.AllServersSave = ""; AllServersSave = "";
this.CompaniesSave = ""; CompaniesSave = "";
this.FactionsSave = ""; FactionsSave = "";
this.SpecialServerIpsSave = ""; SpecialServerIpsSave = "";
this.AliasesSave = ""; AliasesSave = "";
this.GlobalAliasesSave = ""; GlobalAliasesSave = "";
this.MessagesSave = ""; MessagesSave = "";
this.StockMarketSave = ""; StockMarketSave = "";
this.SettingsSave = ""; SettingsSave = "";
this.VersionSave = ""; VersionSave = "";
this.AllGangsSave = ""; AllGangsSave = "";
this.LastExportBonus = ""; LastExportBonus = "";
}
BitburnerSaveObject.prototype.getSaveString = function () { getSaveString(): string {
this.PlayerSave = JSON.stringify(Player); this.PlayerSave = JSON.stringify(Player);
// Delete all logs from all running scripts // Delete all logs from all running scripts
@ -79,38 +77,75 @@ BitburnerSaveObject.prototype.getSaveString = function () {
var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this)))); var saveString = btoa(unescape(encodeURIComponent(JSON.stringify(this))));
return saveString; return saveString;
}; }
BitburnerSaveObject.prototype.saveGame = function () { saveGame(): void {
const saveString = this.getSaveString(); const saveString = this.getSaveString();
save(saveString) save(saveString)
.then(() => createStatusText("Game saved!")) .then(() => createStatusText("Game saved!"))
.catch((err) => console.error(err)); .catch((err) => console.error(err));
}; }
exportGame(): void {
const saveString = this.getSaveString();
// Save file name is based on current timestamp and BitNode
const epochTime = Math.round(Date.now() / 1000);
const bn = Player.bitNodeN;
const filename = `bitburnerSave_BN${bn}x${SourceFileFlags[bn]}_${epochTime}.json`;
var file = new Blob([saveString], { type: "text/plain" });
if (window.navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeoutRef(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
toJSON(): any {
return Generic_toJSON("BitburnerSaveObject", this);
}
static fromJSON(value: { data: any }): BitburnerSaveObject {
return Generic_fromJSON(BitburnerSaveObject, value.data);
}
}
// Makes necessary changes to the loaded/imported data to ensure // Makes necessary changes to the loaded/imported data to ensure
// the game stills works with new versions // the game stills works with new versions
function evaluateVersionCompatibility(ver) { function evaluateVersionCompatibility(ver: string) {
// We have to do this because ts won't let us otherwise
const anyPlayer = Player as any;
// This version refactored the Company/job-related code // This version refactored the Company/job-related code
if (ver <= "0.41.2") { if (ver <= "0.41.2") {
// Player's company position is now a string // Player's company position is now a string
if (Player.companyPosition != null && typeof Player.companyPosition !== "string") { if (anyPlayer.companyPosition != null && typeof anyPlayer.companyPosition !== "string") {
Player.companyPosition = Player.companyPosition.data.positionName; anyPlayer.companyPosition = anyPlayer.companyPosition.data.positionName;
if (Player.companyPosition == null) { if (anyPlayer.companyPosition == null) {
Player.companyPosition = ""; anyPlayer.companyPosition = "";
} }
} }
// The "companyName" property of all Companies is renamed to "name" // The "companyName" property of all Companies is renamed to "name"
for (var companyName in Companies) { for (const companyName in Companies) {
const company = Companies[companyName]; const company: any = Companies[companyName];
if ((company.name == null || company.name === 0 || company.name === "") && company.companyName != null) { if (company.name == 0 && company.companyName != null) {
company.name = company.companyName; company.name = company.companyName;
} }
if (company.companyPositions instanceof Array) { if (company.companyPositions instanceof Array) {
const pos = {}; const pos: any = {};
for (let i = 0; i < company.companyPositions.length; ++i) { for (let i = 0; i < company.companyPositions.length; ++i) {
pos[company.companyPositions[i]] = true; pos[company.companyPositions[i]] = true;
@ -122,15 +157,15 @@ function evaluateVersionCompatibility(ver) {
// This version allowed players to hold multiple jobs // This version allowed players to hold multiple jobs
if (ver < "0.43.0") { if (ver < "0.43.0") {
if (Player.companyName !== "" && Player.companyPosition != null && Player.companyPosition !== "") { if (anyPlayer.companyName !== "" && anyPlayer.companyPosition != null && anyPlayer.companyPosition !== "") {
Player.jobs[Player.companyName] = Player.companyPosition; anyPlayer.jobs[anyPlayer.companyName] = anyPlayer.companyPosition;
} }
delete Player.companyPosition; delete anyPlayer.companyPosition;
} }
} }
function loadGame(saveString) { function loadGame(saveString: string): boolean {
if (!saveString) return false; if (!saveString) return false;
saveString = decodeURIComponent(escape(atob(saveString))); saveString = decodeURIComponent(escape(atob(saveString)));
@ -230,32 +265,6 @@ function loadGame(saveString) {
return true; return true;
} }
BitburnerSaveObject.prototype.exportGame = function () {
const saveString = this.getSaveString();
// Save file name is based on current timestamp and BitNode
const epochTime = Math.round(Date.now() / 1000);
const bn = Player.bitNodeN;
const filename = `bitburnerSave_BN${bn}x${SourceFileFlags[bn]}_${epochTime}.json`;
var file = new Blob([saveString], { type: "text/plain" });
if (window.navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeoutRef(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
};
function createNewUpdateText() { function createNewUpdateText() {
dialogBoxCreate( dialogBoxCreate(
"New update!<br>" + "New update!<br>" +
@ -275,14 +284,8 @@ function createBetaUpdateText() {
); );
} }
BitburnerSaveObject.prototype.toJSON = function () {
return Generic_toJSON("BitburnerSaveObject", this);
};
BitburnerSaveObject.fromJSON = function (value) {
return Generic_fromJSON(BitburnerSaveObject, value.data);
};
Reviver.constructors.BitburnerSaveObject = BitburnerSaveObject; Reviver.constructors.BitburnerSaveObject = BitburnerSaveObject;
export { saveObject, loadGame }; export { saveObject, loadGame };
let saveObject = new BitburnerSaveObject();

@ -1,3 +0,0 @@
import { Script } from "./Script";
export declare function calculateRamUsage(codeCopy: string, otherScripts: Script[]): number;

@ -11,6 +11,8 @@ import { parse } from "acorn";
import { RamCalculationErrorCode } from "./RamCalculationErrorCodes"; import { RamCalculationErrorCode } from "./RamCalculationErrorCodes";
import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator"; import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator";
import { Script } from "../Script/Script";
import { WorkerScript } from "../Netscript/WorkerScript";
// These special strings are used to reference the presence of a given logical // These special strings are used to reference the presence of a given logical
// construct within a user script. // construct within a user script.
@ -29,7 +31,11 @@ const memCheckGlobalKey = ".__GLOBAL__";
* @param {WorkerScript} workerScript - Object containing RAM costs of Netscript functions. Also used to * @param {WorkerScript} workerScript - Object containing RAM costs of Netscript functions. Also used to
* keep track of what functions have/havent been accounted for * keep track of what functions have/havent been accounted for
*/ */
async function parseOnlyRamCalculate(otherScripts, code, workerScript) { async function parseOnlyRamCalculate(
otherScripts: Script[],
code: string,
workerScript: WorkerScript,
): Promise<number | RamCalculationErrorCode> {
try { try {
/** /**
* Maps dependent identifiers to their dependencies. * Maps dependent identifiers to their dependencies.
@ -41,16 +47,16 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
* We walk the dependency graph to calculate RAM usage, given that some identifiers * We walk the dependency graph to calculate RAM usage, given that some identifiers
* reference Netscript functions which have a RAM cost. * reference Netscript functions which have a RAM cost.
*/ */
let dependencyMap = {}; let dependencyMap: { [key: string]: string[] } = {};
// Scripts we've parsed. // Scripts we've parsed.
const completedParses = new Set(); const completedParses = new Set();
// Scripts we've discovered that need to be parsed. // Scripts we've discovered that need to be parsed.
const parseQueue = []; const parseQueue: string[] = [];
// Parses a chunk of code with a given module name, and updates parseQueue and dependencyMap. // Parses a chunk of code with a given module name, and updates parseQueue and dependencyMap.
function parseCode(code, moduleName) { function parseCode(code: string, moduleName: string): void {
const result = parseOnlyCalculateDeps(code, moduleName); const result = parseOnlyCalculateDeps(code, moduleName);
completedParses.add(moduleName); completedParses.add(moduleName);
@ -72,6 +78,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
// Process additional modules, which occurs if the "main" script has any imports // Process additional modules, which occurs if the "main" script has any imports
while (parseQueue.length > 0) { while (parseQueue.length > 0) {
const nextModule = parseQueue.shift(); const nextModule = parseQueue.shift();
if (nextModule === undefined) throw new Error("nextModule should not be undefined");
// Additional modules can either be imported from the web (in which case we use // Additional modules can either be imported from the web (in which case we use
// a dynamic import), or from other in-game scripts // a dynamic import), or from other in-game scripts
@ -122,6 +129,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
const resolvedRefs = new Set(); const resolvedRefs = new Set();
while (unresolvedRefs.length > 0) { while (unresolvedRefs.length > 0) {
const ref = unresolvedRefs.shift(); const ref = unresolvedRefs.shift();
if (ref === undefined) throw new Error("ref should not be undefined");
// Check if this is one of the special keys, and add the appropriate ram cost if so. // Check if this is one of the special keys, and add the appropriate ram cost if so.
if (ref === "hacknet" && !resolvedRefs.has("hacknet")) { if (ref === "hacknet" && !resolvedRefs.has("hacknet")) {
@ -154,7 +162,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
// Check if this identifier is a function in the workerScript environment. // Check if this identifier is a function in the workerScript environment.
// If it is, then we need to get its RAM cost. // If it is, then we need to get its RAM cost.
try { try {
function applyFuncRam(func) { function applyFuncRam(func: any) {
if (typeof func === "function") { if (typeof func === "function") {
try { try {
let res; let res;
@ -216,27 +224,28 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
* for RAM usage calculations. It also returns an array of additional modules * for RAM usage calculations. It also returns an array of additional modules
* that need to be parsed (i.e. are 'import'ed scripts). * that need to be parsed (i.e. are 'import'ed scripts).
*/ */
function parseOnlyCalculateDeps(code, currentModule) { function parseOnlyCalculateDeps(code: string, currentModule: string): any {
const ast = parse(code, { sourceType: "module", ecmaVersion: "latest" }); const ast = parse(code, { sourceType: "module", ecmaVersion: "latest" });
// Everything from the global scope goes in ".". Everything else goes in ".function", where only // Everything from the global scope goes in ".". Everything else goes in ".function", where only
// the outermost layer of functions counts. // the outermost layer of functions counts.
const globalKey = currentModule + memCheckGlobalKey; const globalKey = currentModule + memCheckGlobalKey;
const dependencyMap = {}; const dependencyMap: { [key: string]: Set<string> | undefined } = {};
dependencyMap[globalKey] = new Set(); dependencyMap[globalKey] = new Set<string>();
// If we reference this internal name, we're really referencing that external name. // If we reference this internal name, we're really referencing that external name.
// Filled when we import names from other modules. // Filled when we import names from other modules.
let internalToExternal = {}; let internalToExternal: { [key: string]: string | undefined } = {};
var additionalModules = []; let additionalModules: string[] = [];
// References get added pessimistically. They are added for thisModule.name, name, and for // References get added pessimistically. They are added for thisModule.name, name, and for
// any aliases. // any aliases.
function addRef(key, name) { function addRef(key: string, name: string): void {
const s = dependencyMap[key] || (dependencyMap[key] = new Set()); const s = dependencyMap[key] || (dependencyMap[key] = new Set());
if (name in internalToExternal) { const external = internalToExternal[name];
s.add(internalToExternal[name]); if (external !== undefined) {
s.add(external);
} }
s.add(currentModule + "." + name); s.add(currentModule + "." + name);
s.add(name); // For builtins like hack. s.add(name); // For builtins like hack.
@ -249,36 +258,36 @@ function parseOnlyCalculateDeps(code, currentModule) {
// walkDeeper is for doing recursive walks of expressions in composites that we handle. // walkDeeper is for doing recursive walks of expressions in composites that we handle.
function commonVisitors() { function commonVisitors() {
return { return {
Identifier: (node, st) => { Identifier: (node: any, st: any) => {
if (objectPrototypeProperties.includes(node.name)) { if (objectPrototypeProperties.includes(node.name)) {
return; return;
} }
addRef(st.key, node.name); addRef(st.key, node.name);
}, },
WhileStatement: (node, st, walkDeeper) => { WhileStatement: (node: any, st: any, walkDeeper: any) => {
addRef(st.key, specialReferenceWHILE); addRef(st.key, specialReferenceWHILE);
node.test && walkDeeper(node.test, st); node.test && walkDeeper(node.test, st);
node.body && walkDeeper(node.body, st); node.body && walkDeeper(node.body, st);
}, },
DoWhileStatement: (node, st, walkDeeper) => { DoWhileStatement: (node: any, st: any, walkDeeper: any) => {
addRef(st.key, specialReferenceWHILE); addRef(st.key, specialReferenceWHILE);
node.test && walkDeeper(node.test, st); node.test && walkDeeper(node.test, st);
node.body && walkDeeper(node.body, st); node.body && walkDeeper(node.body, st);
}, },
ForStatement: (node, st, walkDeeper) => { ForStatement: (node: any, st: any, walkDeeper: any) => {
addRef(st.key, specialReferenceFOR); addRef(st.key, specialReferenceFOR);
node.init && walkDeeper(node.init, st); node.init && walkDeeper(node.init, st);
node.test && walkDeeper(node.test, st); node.test && walkDeeper(node.test, st);
node.update && walkDeeper(node.update, st); node.update && walkDeeper(node.update, st);
node.body && walkDeeper(node.body, st); node.body && walkDeeper(node.body, st);
}, },
IfStatement: (node, st, walkDeeper) => { IfStatement: (node: any, st: any, walkDeeper: any) => {
addRef(st.key, specialReferenceIF); addRef(st.key, specialReferenceIF);
node.test && walkDeeper(node.test, st); node.test && walkDeeper(node.test, st);
node.consequent && walkDeeper(node.consequent, st); node.consequent && walkDeeper(node.consequent, st);
node.alternate && walkDeeper(node.alternate, st); node.alternate && walkDeeper(node.alternate, st);
}, },
MemberExpression: (node, st, walkDeeper) => { MemberExpression: (node: any, st: any, walkDeeper: any) => {
node.object && walkDeeper(node.object, st); node.object && walkDeeper(node.object, st);
node.property && walkDeeper(node.property, st); node.property && walkDeeper(node.property, st);
}, },
@ -290,13 +299,15 @@ function parseOnlyCalculateDeps(code, currentModule) {
{ key: globalKey }, { key: globalKey },
Object.assign( Object.assign(
{ {
ImportDeclaration: (node, st) => { ImportDeclaration: (node: any, st: any) => {
const importModuleName = node.source.value; const importModuleName = node.source.value;
additionalModules.push(importModuleName); additionalModules.push(importModuleName);
// This module's global scope refers to that module's global scope, no matter how we // This module's global scope refers to that module's global scope, no matter how we
// import it. // import it.
dependencyMap[st.key].add(importModuleName + memCheckGlobalKey); const set = dependencyMap[st.key];
if (set === undefined) throw new Error("set should not be undefined");
set.add(importModuleName + memCheckGlobalKey);
for (let i = 0; i < node.specifiers.length; ++i) { for (let i = 0; i < node.specifiers.length; ++i) {
const spec = node.specifiers[i]; const spec = node.specifiers[i];
@ -305,11 +316,13 @@ function parseOnlyCalculateDeps(code, currentModule) {
internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name; internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name;
} else { } else {
// We depend on everything. // We depend on everything.
dependencyMap[st.key].add(importModuleName + ".*"); const set = dependencyMap[st.key];
if (set === undefined) throw new Error("set should not be undefined");
set.add(importModuleName + ".*");
} }
} }
}, },
FunctionDeclaration: (node) => { FunctionDeclaration: (node: any) => {
const key = currentModule + "." + node.id.name; const key = currentModule + "." + node.id.name;
walk.recursive(node, { key: key }, commonVisitors()); walk.recursive(node, { key: key }, commonVisitors());
}, },
@ -327,7 +340,10 @@ function parseOnlyCalculateDeps(code, currentModule) {
* @param {Script[]} otherScripts - All other scripts on the server. * @param {Script[]} otherScripts - All other scripts on the server.
* Used to account for imported scripts * Used to account for imported scripts
*/ */
export async function calculateRamUsage(codeCopy, otherScripts) { export async function calculateRamUsage(
codeCopy: string,
otherScripts: Script[],
): Promise<RamCalculationErrorCode | number> {
// We don't need a real WorkerScript for this. Just an object that keeps // We don't need a real WorkerScript for this. Just an object that keeps
// track of whatever's needed for RAM calculations // track of whatever's needed for RAM calculations
const workerScript = { const workerScript = {
@ -335,7 +351,7 @@ export async function calculateRamUsage(codeCopy, otherScripts) {
env: { env: {
vars: RamCosts, vars: RamCosts,
}, },
}; } as WorkerScript;
try { try {
return await parseOnlyRamCalculate(otherScripts, codeCopy, workerScript); return await parseOnlyRamCalculate(otherScripts, codeCopy, workerScript);

@ -3,7 +3,6 @@
* A Script can have multiple active instances * A Script can have multiple active instances
*/ */
import { Script } from "./Script"; import { Script } from "./Script";
import { FconfSettings } from "../Fconf/FconfSettings";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { IMap } from "../types"; import { IMap } from "../types";
import { Terminal } from "../Terminal"; import { Terminal } from "../Terminal";

@ -1,6 +0,0 @@
export declare function findRunningScript(
filename: string,
args: (string | number)[],
server: BaseServer,
): RunningScript | null;
export declare function findRunningScriptByPid(pid: number, server: BaseServer): RunningScript | null;

@ -1,13 +1,16 @@
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Player } from "../Player"; import { Player } from "../Player";
import { AllServers } from "../Server/AllServers"; import { AllServers } from "../Server/AllServers";
import { BaseServer } from "../Server/BaseServer";
import { Server } from "../Server/Server";
import { RunningScript } from "../Script/RunningScript";
import { processSingleServerGrowth } from "../Server/ServerHelpers"; import { processSingleServerGrowth } from "../Server/ServerHelpers";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { compareArrays } from "../../utils/helpers/compareArrays"; import { compareArrays } from "../../utils/helpers/compareArrays";
export function scriptCalculateOfflineProduction(runningScriptObj) { export function scriptCalculateOfflineProduction(runningScript: RunningScript) {
//The Player object stores the last update time from when we were online //The Player object stores the last update time from when we were online
const thisUpdate = new Date().getTime(); const thisUpdate = new Date().getTime();
const lastUpdate = Player.lastUpdate; const lastUpdate = Player.lastUpdate;
@ -16,7 +19,7 @@ export function scriptCalculateOfflineProduction(runningScriptObj) {
//Calculate the "confidence" rating of the script's true production. This is based //Calculate the "confidence" rating of the script's true production. This is based
//entirely off of time. We will arbitrarily say that if a script has been running for //entirely off of time. We will arbitrarily say that if a script has been running for
//4 hours (14400 sec) then we are completely confident in its ability //4 hours (14400 sec) then we are completely confident in its ability
let confidence = runningScriptObj.onlineRunningTime / 14400; let confidence = runningScript.onlineRunningTime / 14400;
if (confidence >= 1) { if (confidence >= 1) {
confidence = 1; confidence = 1;
} }
@ -24,9 +27,9 @@ export function scriptCalculateOfflineProduction(runningScriptObj) {
//Data map: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken] //Data map: [MoneyStolen, NumTimesHacked, NumTimesGrown, NumTimesWeaken]
// Grow // Grow
for (const ip in runningScriptObj.dataMap) { for (const ip in runningScript.dataMap) {
if (runningScriptObj.dataMap.hasOwnProperty(ip)) { if (runningScript.dataMap.hasOwnProperty(ip)) {
if (runningScriptObj.dataMap[ip][2] == 0 || runningScriptObj.dataMap[ip][2] == null) { if (runningScript.dataMap[ip][2] == 0 || runningScript.dataMap[ip][2] == null) {
continue; continue;
} }
const serv = AllServers[ip]; const serv = AllServers[ip];
@ -34,12 +37,13 @@ export function scriptCalculateOfflineProduction(runningScriptObj) {
continue; continue;
} }
const timesGrown = Math.round( const timesGrown = Math.round(
((0.5 * runningScriptObj.dataMap[ip][2]) / runningScriptObj.onlineRunningTime) * timePassed, ((0.5 * runningScript.dataMap[ip][2]) / runningScript.onlineRunningTime) * timePassed,
); );
runningScriptObj.log(`Called on ${serv.hostname} ${timesGrown} times while offline`); runningScript.log(`Called on ${serv.hostname} ${timesGrown} times while offline`);
const host = AllServers[runningScriptObj.server]; const host = AllServers[runningScript.server];
if (!(serv instanceof Server)) throw new Error("trying to grow a non-normal server");
const growth = processSingleServerGrowth(serv, timesGrown, Player, host.cpuCores); const growth = processSingleServerGrowth(serv, timesGrown, Player, host.cpuCores);
runningScriptObj.log( runningScript.log(
`'${serv.hostname}' grown by ${numeralWrapper.format(growth * 100 - 100, "0.000000%")} while offline`, `'${serv.hostname}' grown by ${numeralWrapper.format(growth * 100 - 100, "0.000000%")} while offline`,
); );
} }
@ -47,28 +51,30 @@ export function scriptCalculateOfflineProduction(runningScriptObj) {
// Offline EXP gain // Offline EXP gain
// A script's offline production will always be at most half of its online production. // A script's offline production will always be at most half of its online production.
const expGain = confidence * (runningScriptObj.onlineExpGained / runningScriptObj.onlineRunningTime) * timePassed; const expGain = confidence * (runningScript.onlineExpGained / runningScript.onlineRunningTime) * timePassed;
Player.gainHackingExp(expGain); Player.gainHackingExp(expGain);
// Update script stats // Update script stats
runningScriptObj.offlineRunningTime += timePassed; runningScript.offlineRunningTime += timePassed;
runningScriptObj.offlineExpGained += expGain; runningScript.offlineExpGained += expGain;
// Weaken // Weaken
for (const ip in runningScriptObj.dataMap) { for (const ip in runningScript.dataMap) {
if (runningScriptObj.dataMap.hasOwnProperty(ip)) { if (runningScript.dataMap.hasOwnProperty(ip)) {
if (runningScriptObj.dataMap[ip][3] == 0 || runningScriptObj.dataMap[ip][3] == null) { if (runningScript.dataMap[ip][3] == 0 || runningScript.dataMap[ip][3] == null) {
continue; continue;
} }
const serv = AllServers[ip]; const serv = AllServers[ip];
if (serv == null) { if (serv == null) {
continue; continue;
} }
const host = AllServers[runningScriptObj.server];
if (!(serv instanceof Server)) throw new Error("trying to weaken a non-normal server");
const host = AllServers[runningScript.server];
const timesWeakened = Math.round( const timesWeakened = Math.round(
((0.5 * runningScriptObj.dataMap[ip][3]) / runningScriptObj.onlineRunningTime) * timePassed, ((0.5 * runningScript.dataMap[ip][3]) / runningScript.onlineRunningTime) * timePassed,
); );
runningScriptObj.log(`Called weaken() on ${serv.hostname} ${timesWeakened} times while offline`); runningScript.log(`Called weaken() on ${serv.hostname} ${timesWeakened} times while offline`);
const coreBonus = 1 + (host.cpuCores - 1) / 16; const coreBonus = 1 + (host.cpuCores - 1) / 16;
serv.weaken(CONSTANTS.ServerWeakenAmount * timesWeakened * coreBonus); serv.weaken(CONSTANTS.ServerWeakenAmount * timesWeakened * coreBonus);
} }
@ -77,7 +83,7 @@ export function scriptCalculateOfflineProduction(runningScriptObj) {
//Returns a RunningScript object matching the filename and arguments on the //Returns a RunningScript object matching the filename and arguments on the
//designated server, and false otherwise //designated server, and false otherwise
export function findRunningScript(filename, args, server) { export function findRunningScript(filename: string, args: (string | number)[], server: BaseServer) {
for (var i = 0; i < server.runningScripts.length; ++i) { for (var i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].filename === filename && compareArrays(server.runningScripts[i].args, args)) { if (server.runningScripts[i].filename === filename && compareArrays(server.runningScripts[i].args, args)) {
return server.runningScripts[i]; return server.runningScripts[i];
@ -88,7 +94,7 @@ export function findRunningScript(filename, args, server) {
//Returns a RunningScript object matching the pid on the //Returns a RunningScript object matching the pid on the
//designated server, and false otherwise //designated server, and false otherwise
export function findRunningScriptByPid(pid, server) { export function findRunningScriptByPid(pid: number, server: BaseServer) {
for (var i = 0; i < server.runningScripts.length; ++i) { for (var i = 0; i < server.runningScripts.length; ++i) {
if (server.runningScripts[i].pid === pid) { if (server.runningScripts[i].pid === pid) {
return server.runningScripts[i]; return server.runningScripts[i];

@ -1,4 +1,3 @@
// Script helper functions
export function isScriptFilename(f: string): boolean { export function isScriptFilename(f: string): boolean {
return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns"); return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns");
} }

@ -11,8 +11,7 @@ import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { parseFconfSettings } from "../../Fconf/Fconf"; import { isScriptFilename } from "../../Script/isScriptFilename";
import { isScriptFilename } from "../../Script/ScriptHelpersTS";
import { Script } from "../../Script/Script"; import { Script } from "../../Script/Script";
import { TextFile } from "../../TextFile"; import { TextFile } from "../../TextFile";
import { calculateRamUsage } from "../../Script/RamCalculations"; import { calculateRamUsage } from "../../Script/RamCalculations";
@ -144,7 +143,7 @@ export function Root(props: IProps): React.ReactElement {
return; return;
} }
if (filename !== ".fconf" && !isValidFilePath(filename)) { if (!isValidFilePath(filename)) {
dialogBoxCreate( dialogBoxCreate(
"Script filename can contain only alphanumerics, hyphens, and underscores, and must end with an extension.", "Script filename can contain only alphanumerics, hyphens, and underscores, and must end with an extension.",
); );
@ -153,14 +152,7 @@ export function Root(props: IProps): React.ReactElement {
const server = props.player.getCurrentServer(); const server = props.player.getCurrentServer();
if (server === null) throw new Error("Server should not be null but it is."); if (server === null) throw new Error("Server should not be null but it is.");
if (filename === ".fconf") { if (isScriptFilename(filename)) {
try {
parseFconfSettings(code);
} catch (e) {
dialogBoxCreate(`Invalid .fconf file: ${e}`);
return;
}
} else if (isScriptFilename(filename)) {
//If the current script already exists on the server, overwrite it //If the current script already exists on the server, overwrite it
for (let i = 0; i < server.scripts.length; i++) { for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) { if (filename == server.scripts[i].filename) {

@ -9,7 +9,7 @@ import { isValidFilePath } from "../Terminal/DirectoryHelpers";
import { TextFile } from "../TextFile"; import { TextFile } from "../TextFile";
import { IReturnStatus } from "../types"; import { IReturnStatus } from "../types";
import { isScriptFilename } from "../Script/ScriptHelpersTS"; import { isScriptFilename } from "../Script/isScriptFilename";
import { createRandomIp } from "../../utils/IPAddress"; import { createRandomIp } from "../../utils/IPAddress";
import { compareArrays } from "../../utils/helpers/compareArrays"; import { compareArrays } from "../../utils/helpers/compareArrays";

@ -295,7 +295,6 @@ export function SidebarRoot(props: IProps): React.ReactElement {
event.preventDefault(); event.preventDefault();
clickCreateProgram(); clickCreateProgram();
} else if (event.keyCode === KEY.F && event.altKey) { } else if (event.keyCode === KEY.F && event.altKey) {
// Overriden by Fconf
if (props.page == Page.Terminal && Settings.EnableBashHotkeys) { if (props.page == Page.Terminal && Settings.EnableBashHotkeys) {
return; return;
} }

1
src/Terminal.d.ts vendored

@ -1 +0,0 @@
export declare const Terminal: ITerminal;

@ -3,13 +3,14 @@ import { IRouter } from "../ui/Router";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { HacknetServer } from "../Hacknet/HacknetServer"; import { HacknetServer } from "../Hacknet/HacknetServer";
import { BaseServer } from "../Server/BaseServer"; import { BaseServer } from "../Server/BaseServer";
import { Server } from "../Server/Server";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { CodingContractResult } from "../CodingContracts"; import { CodingContractResult } from "../CodingContracts";
import { TerminalEvents, TerminalClearEvents } from "./TerminalEvents"; import { TerminalEvents, TerminalClearEvents } from "./TerminalEvents";
import { TextFile } from "../TextFile"; import { TextFile } from "../TextFile";
import { Script } from "../Script/Script"; import { Script } from "../Script/Script";
import { isScriptFilename } from "../Script/ScriptHelpersTS"; import { isScriptFilename } from "../Script/isScriptFilename";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { AllServers } from "../Server/AllServers"; import { AllServers } from "../Server/AllServers";
@ -91,7 +92,7 @@ export class Terminal implements ITerminal {
append(item: Output | Link): void { append(item: Output | Link): void {
this.outputHistory.push(item); this.outputHistory.push(item);
if (this.outputHistory.length > Settings.MaxTerminalCapacity) { if (this.outputHistory.length > Settings.MaxTerminalCapacity) {
this.outputHistory.slice(this.outputHistory.length - Settings.MaxTerminalCapacity); this.outputHistory.splice(0, this.outputHistory.length - Settings.MaxTerminalCapacity);
} }
TerminalEvents.emit(); TerminalEvents.emit();
} }
@ -106,12 +107,22 @@ export class Terminal implements ITerminal {
startHack(player: IPlayer): void { startHack(player: IPlayer): void {
// Hacking through Terminal should be faster than hacking through a script // Hacking through Terminal should be faster than hacking through a script
this.startAction(calculateHackingTime(player.getCurrentServer(), player) / 4, "h"); const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot hack this kind of server");
return;
}
this.startAction(calculateHackingTime(server, player) / 4, "h");
} }
startBackdoor(player: IPlayer): void { startBackdoor(player: IPlayer): void {
// Backdoor should take the same amount of time as hack // Backdoor should take the same amount of time as hack
this.startAction(calculateHackingTime(player.getCurrentServer(), player) / 4, "b"); const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot backdoor this kind of server");
return;
}
this.startAction(calculateHackingTime(server, player) / 4, "b");
} }
startAnalyze(): void { startAnalyze(): void {
@ -127,6 +138,10 @@ export class Terminal implements ITerminal {
finishHack(router: IRouter, player: IPlayer, cancelled = false): void { finishHack(router: IRouter, player: IPlayer, cancelled = false): void {
if (cancelled) return; if (cancelled) return;
const server = player.getCurrentServer(); const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot hack this kind of server");
return;
}
// Calculate whether hack was successful // Calculate whether hack was successful
const hackChance = calculateHackingChance(server, player); const hackChance = calculateHackingChance(server, player);
@ -179,6 +194,10 @@ export class Terminal implements ITerminal {
finishBackdoor(router: IRouter, player: IPlayer, cancelled = false): void { finishBackdoor(router: IRouter, player: IPlayer, cancelled = false): void {
if (!cancelled) { if (!cancelled) {
const server = player.getCurrentServer(); const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot hack this kind of server");
return;
}
if ( if (
SpecialServerIps[SpecialServerNames.WorldDaemon] && SpecialServerIps[SpecialServerNames.WorldDaemon] &&
SpecialServerIps[SpecialServerNames.WorldDaemon] == server.ip SpecialServerIps[SpecialServerNames.WorldDaemon] == server.ip
@ -203,6 +222,7 @@ export class Terminal implements ITerminal {
this.print("Organization name: " + (!isHacknet ? org : "player")); this.print("Organization name: " + (!isHacknet ? org : "player"));
const hasAdminRights = (!isHacknet && currServ.hasAdminRights) || isHacknet; const hasAdminRights = (!isHacknet && currServ.hasAdminRights) || isHacknet;
this.print("Root Access: " + (hasAdminRights ? "YES" : "NO")); this.print("Root Access: " + (hasAdminRights ? "YES" : "NO"));
if (currServ instanceof Server) {
const hackingSkill = currServ.requiredHackingSkill; const hackingSkill = currServ.requiredHackingSkill;
this.print("Required hacking skill: " + (!isHacknet ? hackingSkill : "N/A")); this.print("Required hacking skill: " + (!isHacknet ? hackingSkill : "N/A"));
const security = currServ.hackDifficulty; const security = currServ.hackDifficulty;
@ -211,9 +231,13 @@ export class Terminal implements ITerminal {
this.print("Chance to hack: " + (!isHacknet ? numeralWrapper.formatPercentage(hackingChance) : "N/A")); this.print("Chance to hack: " + (!isHacknet ? numeralWrapper.formatPercentage(hackingChance) : "N/A"));
const hackingTime = calculateHackingTime(currServ, player) * 1000; const hackingTime = calculateHackingTime(currServ, player) * 1000;
this.print("Time to hack: " + (!isHacknet ? convertTimeMsToTimeElapsedString(hackingTime, true) : "N/A")); this.print("Time to hack: " + (!isHacknet ? convertTimeMsToTimeElapsedString(hackingTime, true) : "N/A"));
}
this.print( this.print(
`Total money available on server: ${!isHacknet ? numeralWrapper.formatMoney(currServ.moneyAvailable) : "N/A"}`, `Total money available on server: ${
!(currServ instanceof HacknetServer) ? numeralWrapper.formatMoney(currServ.moneyAvailable) : "N/A"
}`,
); );
if (currServ instanceof Server) {
const numPort = currServ.numOpenPortsRequired; const numPort = currServ.numOpenPortsRequired;
this.print("Required number of open ports for NUKE: " + (!isHacknet ? numPort : "N/A")); this.print("Required number of open ports for NUKE: " + (!isHacknet ? numPort : "N/A"));
this.print("SSH port: " + (currServ.sshPortOpen ? "Open" : "Closed")); this.print("SSH port: " + (currServ.sshPortOpen ? "Open" : "Closed"));
@ -223,6 +247,7 @@ export class Terminal implements ITerminal {
this.print("SQL port: " + (currServ.sqlPortOpen ? "Open" : "Closed")); this.print("SQL port: " + (currServ.sqlPortOpen ? "Open" : "Closed"));
} }
} }
}
finishAction(router: IRouter, player: IPlayer, cancelled = false): void { finishAction(router: IRouter, player: IPlayer, cancelled = false): void {
if (this.action === null) { if (this.action === null) {

@ -3,7 +3,7 @@ import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { findRunningScript } from "../../Script/ScriptHelpers"; import { findRunningScript } from "../../Script/ScriptHelpers";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/isScriptFilename";
export function check( export function check(
terminal: ITerminal, terminal: ITerminal,

@ -2,7 +2,7 @@ import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/isScriptFilename";
import FileSaver from "file-saver"; import FileSaver from "file-saver";
import JSZip from "jszip"; import JSZip from "jszip";

@ -2,7 +2,7 @@ import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/isScriptFilename";
import { TextFile } from "../../TextFile"; import { TextFile } from "../../TextFile";
import { Script } from "../../Script/Script"; import { Script } from "../../Script/Script";

@ -2,8 +2,7 @@ import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/isScriptFilename";
import { createFconf } from "../../Fconf/Fconf";
export function nano( export function nano(
terminal: ITerminal, terminal: ITerminal,
@ -19,11 +18,7 @@ export function nano(
try { try {
const filename = args[0] + ""; const filename = args[0] + "";
if (filename === ".fconf") { if (isScriptFilename(filename)) {
const text = createFconf();
router.toScriptEditor(filename, text);
return;
} else if (isScriptFilename(filename)) {
const filepath = terminal.getFilepath(filename); const filepath = terminal.getFilepath(filename);
const script = terminal.getScript(player, filename); const script = terminal.getScript(player, filename);
if (script == null) { if (script == null) {

@ -2,7 +2,7 @@ import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/isScriptFilename";
import { runScript } from "./runScript"; import { runScript } from "./runScript";
import { runProgram } from "./runProgram"; import { runProgram } from "./runProgram";

@ -4,7 +4,7 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { Message } from "../../Message/Message"; import { Message } from "../../Message/Message";
import { getServer } from "../../Server/ServerHelpers"; import { getServer } from "../../Server/ServerHelpers";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/isScriptFilename";
export function scp( export function scp(
terminal: ITerminal, terminal: ITerminal,

@ -4,8 +4,8 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { logBoxCreate } from "../../../utils/LogBox"; import { logBoxCreate } from "../../../utils/LogBox";
import { findRunningScriptByPid } from "../../Script/ScriptHelpers"; import { findRunningScriptByPid } from "../../Script/ScriptHelpers";
import { isScriptFilename } from "../../Script/isScriptFilename";
import { compareArrays } from "../../../utils/helpers/compareArrays"; import { compareArrays } from "../../../utils/helpers/compareArrays";
import { isScriptFilename } from "../../Script/ScriptHelpersTS";
export function tail( export function tail(
terminal: ITerminal, terminal: ITerminal,

@ -2,7 +2,7 @@ import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/isScriptFilename";
export function wget( export function wget(
terminal: ITerminal, terminal: ITerminal,

@ -12,6 +12,7 @@ import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { TerminalInput } from "./TerminalInput"; import { TerminalInput } from "./TerminalInput";
import { TerminalEvents, TerminalClearEvents } from "../TerminalEvents"; import { TerminalEvents, TerminalClearEvents } from "../TerminalEvents";
import _ from "lodash";
interface IActionTimerProps { interface IActionTimerProps {
terminal: ITerminal; terminal: ITerminal;

1
src/ThirdParty/JSInterpreter.d.ts vendored Normal file

@ -0,0 +1 @@
export declare let Interpreter: any;

1
src/ThirdParty/sprintf-js.d.ts vendored Normal file

@ -0,0 +1 @@
declare module "sprintf-js";

@ -50,7 +50,29 @@ import { startUnclickable } from "./Exploits/unclickable";
import React from "react"; import React from "react";
const Engine = { const Engine: {
_lastUpdate: number;
updateGame: (numCycles?: number) => void;
Counters: {
[key: string]: number | undefined;
autoSaveCounter: number;
updateSkillLevelsCounter: number;
updateDisplays: number;
updateDisplaysLong: number;
updateActiveScriptsDisplay: number;
createProgramNotifications: number;
augmentationsNotifications: number;
checkFactionInvitations: number;
passiveFactionGrowth: number;
messages: number;
mechanicProcess: number;
contractGeneration: number;
};
decrementAllCounters: (numCycles?: number) => void;
checkCounters: () => void;
load: (saveString: string) => void;
start: () => void;
} = {
// Time variables (milliseconds unix epoch time) // Time variables (milliseconds unix epoch time)
_lastUpdate: new Date().getTime(), _lastUpdate: new Date().getTime(),
@ -106,7 +128,7 @@ const Engine = {
} }
// Gang, if applicable // Gang, if applicable
if (Player.inGang()) { if (Player.inGang() && Player.gang !== null) {
Player.gang.process(numCycles, Player); Player.gang.process(numCycles, Player);
} }
@ -175,10 +197,10 @@ const Engine = {
}, },
decrementAllCounters: function (numCycles = 1) { decrementAllCounters: function (numCycles = 1) {
for (var counter in Engine.Counters) { for (const counterName in Engine.Counters) {
if (Engine.Counters.hasOwnProperty(counter)) { const counter = Engine.Counters[counterName];
Engine.Counters[counter] = Engine.Counters[counter] - numCycles; if (counter === undefined) throw new Error("counter should not be undefined");
} Engine.Counters[counterName] = counter - numCycles;
} }
}, },
@ -328,8 +350,9 @@ const Engine = {
} }
// Gang progress for BitNode 2 // Gang progress for BitNode 2
if (Player.inGang()) { const gang = Player.gang;
Player.gang.process(numCyclesOffline, Player); if (Player.inGang() && gang !== null) {
gang.process(numCyclesOffline, Player);
} }
// Corporation offline progress // Corporation offline progress

@ -326,11 +326,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
) : page === Page.DevMenu ? ( ) : page === Page.DevMenu ? (
<DevMenuRoot player={player} engine={engine} router={Router} /> <DevMenuRoot player={player} engine={engine} router={Router} />
) : page === Page.Gang ? ( ) : page === Page.Gang ? (
<GangRoot gang={player.gang} /> <GangRoot />
) : page === Page.Corporation ? ( ) : page === Page.Corporation ? (
<CorporationRoot corp={player.corporation} player={player} /> <CorporationRoot />
) : page === Page.Bladeburner ? ( ) : page === Page.Bladeburner ? (
<BladeburnerRoot bladeburner={player.bladeburner} /> <BladeburnerRoot />
) : page === Page.Resleeves ? ( ) : page === Page.Resleeves ? (
<ResleeveRoot player={player} /> <ResleeveRoot player={player} />
) : page === Page.Travel ? ( ) : page === Page.Travel ? (

@ -1,177 +0,0 @@
// Implement the collapsible main menu headers
import { MainMenuLinks } from "./Links";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IMainMenuHeaders {
Hacking: HTMLElement | null;
Character: HTMLElement | null;
World: HTMLElement | null;
Help: HTMLElement | null;
}
export const MainMenuHeaders: IMainMenuHeaders = {
Hacking: null,
Character: null,
World: null,
Help: null,
};
// Implements collapsible toggle feature when a header is clicked
function toggleHeader(open: boolean, elems: HTMLElement[], links: HTMLElement[]): void {
for (let i = 0; i < elems.length; ++i) {
if (open) {
elems[i].style.opacity = "1";
elems[i].style.maxHeight = elems[i].scrollHeight + "px";
} else {
elems[i].style.opacity = "0";
elems[i].style.maxHeight = "";
}
}
for (let i = 0; i < links.length; ++i) {
if (open) {
links[i].style.opacity = "1";
links[i].style.maxHeight = links[i].scrollHeight + "px";
links[i].style.pointerEvents = "auto";
} else {
links[i].style.opacity = "0";
links[i].style.maxHeight = "";
links[i].style.pointerEvents = "none";
}
}
}
export function initializeMainMenuHeaders(p: IPlayer, dev = false): boolean {
function safeGetElement(id: string): HTMLElement {
const elem: HTMLElement | null = document.getElementById(id);
if (elem == null) {
throw new Error(`Failed to find element with id ${id} in initializeMainMenuHeaders()`);
}
return elem;
}
try {
// Get references to the DOM elements
MainMenuHeaders.Hacking = safeGetElement("hacking-menu-header");
MainMenuHeaders.Character = safeGetElement("character-menu-header");
MainMenuHeaders.World = safeGetElement("world-menu-header");
MainMenuHeaders.Help = safeGetElement("help-menu-header");
// Set click handlers to turn the headers into collapsibles
MainMenuHeaders.Hacking.onclick = function () {
const terminal: HTMLElement = safeGetElement("terminal-tab");
const createScript: HTMLElement = safeGetElement("create-script-tab");
const activeScripts: HTMLElement = safeGetElement("active-scripts-tab");
const createProgram: HTMLElement = safeGetElement("create-program-tab");
const createProgramNot: HTMLElement = safeGetElement("create-program-notification");
createProgram.style.display = p.firstProgramAvailable ? "list-item" : "none";
(this as any).classList.toggle("opened");
const elems: HTMLElement[] = [terminal, createScript, activeScripts, createProgram];
const links: HTMLElement[] = [
MainMenuLinks.Terminal,
MainMenuLinks.ScriptEditor,
MainMenuLinks.ActiveScripts,
MainMenuLinks.CreateProgram,
];
if (terminal.style.maxHeight) {
toggleHeader(false, elems, links);
createProgramNot.style.display = "none";
} else {
toggleHeader(true, elems, links);
createProgramNot.style.display = "block";
}
};
MainMenuHeaders.Character.onclick = function () {
const stats: HTMLElement = safeGetElement("stats-tab");
const factions: HTMLElement = safeGetElement("factions-tab");
const augmentations: HTMLElement = safeGetElement("augmentations-tab");
const hacknetnodes: HTMLElement = safeGetElement("hacknet-nodes-tab");
const sleeves: HTMLElement = safeGetElement("sleeves-tab");
sleeves.style.display = p.sleeves.length > 0 ? "list-item" : "none";
(this as any).classList.toggle("opened");
const elems: HTMLElement[] = [stats, factions, augmentations, hacknetnodes, sleeves];
const links: HTMLElement[] = [
MainMenuLinks.Stats,
MainMenuLinks.Factions,
MainMenuLinks.Augmentations,
MainMenuLinks.HacknetNodes,
MainMenuLinks.Sleeves,
];
if (stats.style.maxHeight) {
toggleHeader(false, elems, links);
} else {
toggleHeader(true, elems, links);
}
};
MainMenuHeaders.World.onclick = function () {
const city: HTMLElement = safeGetElement("city-tab");
const travel: HTMLElement = safeGetElement("travel-tab");
const job: HTMLElement = safeGetElement("job-tab");
const stockmarket: HTMLElement = safeGetElement("stock-market-tab");
const bladeburner: HTMLElement = safeGetElement("bladeburner-tab");
const corporation: HTMLElement = safeGetElement("corporation-tab");
const gang: HTMLElement = safeGetElement("gang-tab");
// Determine whether certain links should show up
job.style.display = p.companyName !== "" ? "list-item" : "none";
stockmarket.style.display = p.hasWseAccount ? "list-item" : "none";
bladeburner.style.display = p.inBladeburner() ? "list-item" : "none";
corporation.style.display = p.hasCorporation() ? "list-item" : "none";
gang.style.display = p.inGang() ? "list-item" : "none";
(this as any).classList.toggle("opened");
const elems: HTMLElement[] = [city, travel, job, stockmarket, bladeburner, corporation, gang];
const links: HTMLElement[] = [
MainMenuLinks.City,
MainMenuLinks.Travel,
MainMenuLinks.Job,
MainMenuLinks.StockMarket,
MainMenuLinks.Bladeburner,
MainMenuLinks.Corporation,
MainMenuLinks.Gang,
];
if (city.style.maxHeight) {
toggleHeader(false, elems, links);
} else {
toggleHeader(true, elems, links);
}
};
MainMenuHeaders.Help.onclick = function () {
const milestones: HTMLElement = safeGetElement("milestones-tab");
const tutorial: HTMLElement = safeGetElement("tutorial-tab");
const options: HTMLElement = safeGetElement("options-tab");
(this as any).classList.toggle("opened");
const elems: HTMLElement[] = [milestones, tutorial, options];
const links: HTMLElement[] = [MainMenuLinks.Milestones, MainMenuLinks.Tutorial, MainMenuLinks.Options];
if (dev) {
elems.push(safeGetElement("dev-tab"));
links.push(safeGetElement("dev-menu-link"));
}
if (tutorial.style.maxHeight) {
toggleHeader(false, elems, links);
} else {
toggleHeader(true, elems, links);
}
};
return true;
} catch (e) {
console.error(`Failed to initialize Main Menu Headers: ${e}`);
return false;
}
}

@ -1,98 +0,0 @@
// Get references to the Main Menu link DOM elements
// Does NOT include collapsible headers for the links
import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners";
interface IMainMenuLinks {
[key: string]: HTMLElement | undefined;
Terminal: HTMLElement;
ScriptEditor: HTMLElement;
ActiveScripts: HTMLElement;
CreateProgram: HTMLElement;
Stats: HTMLElement;
Factions: HTMLElement;
Augmentations: HTMLElement;
HacknetNodes: HTMLElement;
Sleeves: HTMLElement;
City: HTMLElement;
Travel: HTMLElement;
Job: HTMLElement;
StockMarket: HTMLElement;
Bladeburner: HTMLElement;
Corporation: HTMLElement;
Gang: HTMLElement;
Milestones: HTMLElement;
Tutorial: HTMLElement;
Options: HTMLElement;
DevMenu: HTMLElement;
}
const emptyElement: HTMLElement = ((): HTMLElement => {
const elem = document.createElement("div");
if (elem === null) throw new Error("unable to create empty div element");
return elem;
})();
export const MainMenuLinks: IMainMenuLinks = {
Terminal: emptyElement,
ScriptEditor: emptyElement,
ActiveScripts: emptyElement,
CreateProgram: emptyElement,
Stats: emptyElement,
Factions: emptyElement,
Augmentations: emptyElement,
HacknetNodes: emptyElement,
Sleeves: emptyElement,
City: emptyElement,
Travel: emptyElement,
Job: emptyElement,
StockMarket: emptyElement,
Bladeburner: emptyElement,
Corporation: emptyElement,
Gang: emptyElement,
Milestones: emptyElement,
Tutorial: emptyElement,
Options: emptyElement,
DevMenu: emptyElement,
};
export function initializeMainMenuLinks(): boolean {
return true;
try {
function safeGetLink(id: string): HTMLElement {
const elem: HTMLElement | null = clearEventListeners(id);
if (elem == null) {
throw new Error(`clearEventListeners() failed for element with id: ${id}`);
}
return elem;
}
MainMenuLinks.Terminal = safeGetLink("terminal-menu-link");
MainMenuLinks.ScriptEditor = safeGetLink("create-script-menu-link");
MainMenuLinks.ActiveScripts = safeGetLink("active-scripts-menu-link");
MainMenuLinks.CreateProgram = safeGetLink("create-program-menu-link");
MainMenuLinks.Stats = safeGetLink("stats-menu-link");
MainMenuLinks.Factions = safeGetLink("factions-menu-link");
MainMenuLinks.Augmentations = safeGetLink("augmentations-menu-link");
MainMenuLinks.HacknetNodes = safeGetLink("hacknet-nodes-menu-link");
MainMenuLinks.Sleeves = safeGetLink("sleeves-menu-link");
MainMenuLinks.City = safeGetLink("city-menu-link");
MainMenuLinks.Travel = safeGetLink("travel-menu-link");
MainMenuLinks.Job = safeGetLink("job-menu-link");
MainMenuLinks.StockMarket = safeGetLink("stock-market-menu-link");
MainMenuLinks.Bladeburner = safeGetLink("bladeburner-menu-link");
MainMenuLinks.Corporation = safeGetLink("corporation-menu-link");
MainMenuLinks.Gang = safeGetLink("gang-menu-link");
MainMenuLinks.Milestones = safeGetLink("milestones-menu-link");
MainMenuLinks.Tutorial = safeGetLink("tutorial-menu-link");
// const op: HTMLElement | null = document.getElementById("options-menu-link");
// if (op === null) throw new Error(`Could not find element with id: "options-menu-link"`);
// MainMenuLinks.Options = op; // This click listener is already set, so don't clear it
MainMenuLinks.DevMenu = safeGetLink("dev-menu-link");
return true;
} catch (e) {
console.error(`Failed to initialize Main Menu Links: ${e}`);
return false;
}
}

@ -195,7 +195,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
return; return;
} }
const contents = result; const contents = result;
save(contents).then(() => location.reload()); save(contents).then(() => setTimeout(() => location.reload(), 1000));
}; };
reader.readAsText(file); reader.readAsText(file);
} }
@ -628,7 +628,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
onConfirm={() => { onConfirm={() => {
setDeleteOpen(false); setDeleteOpen(false);
deleteGame() deleteGame()
.then(() => location.reload()) .then(() => setTimeout(() => location.reload(), 1000))
.catch((r) => console.error(`Could not delete game: ${r}`)); .catch((r) => console.error(`Could not delete game: ${r}`));
}} }}
open={deleteGameOpen} open={deleteGameOpen}

@ -5,17 +5,17 @@ interface IError {
lineNumber?: number; lineNumber?: number;
} }
export function exceptionAlert(e: IError): void { export function exceptionAlert(e: IError | string): void {
console.error(e); console.error(e);
dialogBoxCreate( dialogBoxCreate(
"Caught an exception: " + "Caught an exception: " +
e + e +
"<br><br>" + "<br><br>" +
"Filename: " + "Filename: " +
(e.fileName || "UNKNOWN FILE NAME") + ((e as any).fileName || "UNKNOWN FILE NAME") +
"<br><br>" + "<br><br>" +
"Line Number: " + "Line Number: " +
(e.lineNumber || "UNKNOWN LINE NUMBER") + ((e as any).lineNumber || "UNKNOWN LINE NUMBER") +
"<br><br>" + "<br><br>" +
"This is a bug, please report to game developer with this " + "This is a bug, please report to game developer with this " +
"message as well as details about how to reproduce the bug.<br><br>" + "message as well as details about how to reproduce the bug.<br><br>" +