Merge pull request #1291 from danielyxie/big-container

Big container
This commit is contained in:
hydroflame 2021-09-17 19:46:30 -04:00 committed by GitHub
commit f359fe661e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 2566 additions and 2943 deletions

@ -6,7 +6,6 @@
.active-scripts-container { .active-scripts-container {
> p { > p {
width: 70%;
margin: 6px; margin: 6px;
padding: 4px; padding: 4px;
} }

@ -8,7 +8,6 @@
.augmentations-content { .augmentations-content {
> p { > p {
font-size: $defaultFontSize * 0.875; font-size: $defaultFontSize * 0.875;
width: 70%;
} }
} }

@ -7,7 +7,6 @@
.hacknet-general-info { .hacknet-general-info {
margin: 10px; margin: 10px;
width: 70vw;
} }
#hacknet-nodes-container li { #hacknet-nodes-container li {

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

@ -1,16 +1,17 @@
import React from "react"; import React from "react";
import { hackWorldDaemon } from "../../RedPill";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router";
import { removePopup } from "../../ui/React/createPopup"; import { removePopup } from "../../ui/React/createPopup";
interface IProps { interface IProps {
player: IPlayer; player: IPlayer;
router: IRouter;
popupId: string; popupId: string;
} }
export function BitFlumePopup(props: IProps): React.ReactElement { export function BitFlumePopup(props: IProps): React.ReactElement {
function flume(): void { function flume(): void {
hackWorldDaemon(props.player.bitNodeN, true, false); props.router.toBitVerse(true, false);
removePopup(props.popupId); removePopup(props.popupId);
} }
return ( return (

@ -1,18 +1,22 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IRouter } from "../../ui/Router";
import { BitNodes } from "../BitNode"; import { BitNodes } from "../BitNode";
import { enterBitNode } from "../../RedPill";
import { PortalPopup } from "./PortalPopup"; import { PortalPopup } from "./PortalPopup";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { CinematicText } from "../../ui/React/CinematicText"; import { CinematicText } from "../../ui/React/CinematicText";
import { use } from "../../ui/Context";
interface IPortalProps { interface IPortalProps {
n: number; n: number;
level: number; level: number;
destroyedBitNode: number; destroyedBitNode: number;
flume: boolean; flume: boolean;
enter: (flume: boolean, destroyedBitNode: number, newBitNode: number) => void; enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
} }
function BitNodePortal(props: IPortalProps): React.ReactElement { function BitNodePortal(props: IPortalProps): React.ReactElement {
const router = use.Router();
const bitNode = BitNodes[`BitNode${props.n}`]; const bitNode = BitNodes[`BitNode${props.n}`];
if (bitNode == null) { if (bitNode == null) {
return <>O</>; return <>O</>;
@ -32,6 +36,7 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
n: props.n, n: props.n,
level: props.level, level: props.level,
enter: props.enter, enter: props.enter,
router: router,
destroyedBitNode: props.destroyedBitNode, destroyedBitNode: props.destroyedBitNode,
flume: props.flume, flume: props.flume,
popupId: popupId, popupId: popupId,
@ -57,18 +62,20 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
interface IProps { interface IProps {
flume: boolean; flume: boolean;
destroyedBitNodeNum: number;
quick: boolean; quick: boolean;
enter: (flume: boolean, destroyedBitNode: number, newBitNode: number) => void; enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
} }
export function BitverseRoot(props: IProps): React.ReactElement { export function BitverseRoot(props: IProps): React.ReactElement {
const player = use.Player();
const enter = enterBitNode;
const destroyed = player.bitNodeN;
const [destroySequence, setDestroySequence] = useState(true && !props.quick); const [destroySequence, setDestroySequence] = useState(true && !props.quick);
// Update NextSourceFileFlags // Update NextSourceFileFlags
const nextSourceFileFlags = SourceFileFlags.slice(); const nextSourceFileFlags = SourceFileFlags.slice();
if (!props.flume) { if (!props.flume) {
if (nextSourceFileFlags[props.destroyedBitNodeNum] < 3) ++nextSourceFileFlags[props.destroyedBitNodeNum]; if (nextSourceFileFlags[destroyed] < 3) ++nextSourceFileFlags[destroyed];
} }
if (destroySequence) { if (destroySequence) {
@ -84,7 +91,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
"0020 7124696B 0000FF69 74652E6F FFFF1111", "0020 7124696B 0000FF69 74652E6F FFFF1111",
"----------------------------------------", "----------------------------------------",
"Failsafe initiated...", "Failsafe initiated...",
`Restarting BitNode-${props.destroyedBitNodeNum}...`, `Restarting BitNode-${destroyed}...`,
"...........", "...........",
"...........", "...........",
"[ERROR] FAILED TO AUTOMATICALLY REBOOT BITNODE", "[ERROR] FAILED TO AUTOMATICALLY REBOOT BITNODE",
@ -96,6 +103,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
"..............................................", "..............................................",
]} ]}
onDone={() => setDestroySequence(false)} onDone={() => setDestroySequence(false)}
auto={true}
/> />
); );
} }
@ -116,16 +124,16 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<pre> \| O | |_/ |\| \ O \__| \_| | O |/ </pre> <pre> \| O | |_/ |\| \ O \__| \_| | O |/ </pre>
<pre> | | |_/ | | \| / | \_| | | </pre> <pre> | | |_/ | | \| / | \_| | | </pre>
<pre> \| / \| | / / \ |/ </pre> <pre> \| / \| | / / \ |/ </pre>
<pre> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | </pre> <pre> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </pre>
<pre> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> </pre> <pre> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </pre>
<pre> | | | / / \ \ | | | </pre> <pre> | | | / / \ \ | | | </pre>
<pre> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> \ | |/ </pre> <pre> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </pre>
<pre> \ | / / | | \ \ | / </pre> <pre> \ | / / | | \ \ | / </pre>
<pre> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> PMUJ/ / </pre> <pre> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </pre>
<pre> \|| | | | | | | | | ||/ </pre> <pre> \|| | | | | | | | | ||/ </pre>
<pre> \| \_ | | | | | | _/ |/ </pre> <pre> \| \_ | | | | | | _/ |/ </pre>
<pre> \ \| / \ / \ |/ / </pre> <pre> \ \| / \ / \ |/ / </pre>
<pre> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={props.enter} flume={props.flume} destroyedBitNode={props.destroyedBitNodeNum} /> </pre> <pre> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </pre>
<pre> | | | | | | | | </pre> <pre> | | | | | | | | </pre>
<pre> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </pre> <pre> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </pre>
<br /> <br />

@ -1,13 +1,15 @@
import React from "react"; import React from "react";
import { BitNodes } from "../BitNode"; import { BitNodes } from "../BitNode";
import { IRouter } from "../../ui/Router";
import { removePopup } from "../../ui/React/createPopup"; import { removePopup } from "../../ui/React/createPopup";
interface IProps { interface IProps {
n: number; n: number;
level: number; level: number;
destroyedBitNode: number; destroyedBitNode: number;
flume: boolean; flume: boolean;
enter: (flume: boolean, destroyedBitNode: number, newBitNode: number) => void; router: IRouter;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
popupId: string; popupId: string;
} }
@ -33,7 +35,7 @@ export function PortalPopup(props: IProps): React.ReactElement {
<button <button
className="std-button" className="std-button"
onClick={() => { onClick={() => {
props.enter(props.flume, props.destroyedBitNode, props.n); props.enter(props.router, props.flume, props.destroyedBitNode, props.n);
removePopup(props.popupId); removePopup(props.popupId);
}} }}
> >

@ -15,6 +15,7 @@ import { Skill } from "./Skill";
import { City } from "./City"; import { City } from "./City";
import { IAction } from "./IAction"; import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
import { ConsoleHelpText } from "./data/Help"; import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { getRandomInt } from "../../utils/helpers/getRandomInt";
@ -25,7 +26,7 @@ import { addOffset } from "../../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction"; import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital"; import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { hackWorldDaemon, redPillFlag } from "../RedPill"; import { redPillFlag } from "../RedPill";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
@ -1203,7 +1204,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
completeAction(player: IPlayer): void { completeAction(router: IRouter, player: IPlayer): void {
switch (this.action.type) { switch (this.action.type) {
case ActionTypes["Contract"]: case ActionTypes["Contract"]:
case ActionTypes["Operation"]: { case ActionTypes["Operation"]: {
@ -1338,7 +1339,7 @@ export class Bladeburner implements IBladeburner {
// Operation Daedalus // Operation Daedalus
if (action.name === "Operation Daedalus") { if (action.name === "Operation Daedalus") {
this.resetAction(); this.resetAction();
return hackWorldDaemon(player.bitNodeN); return router.toBitVerse(false, false);
} }
if (this.logging.blackops) { if (this.logging.blackops) {
@ -1540,7 +1541,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
processAction(player: IPlayer, seconds: number): void { processAction(router: IRouter, player: IPlayer, seconds: number): void {
if (this.action.type === ActionTypes["Idle"]) return; if (this.action.type === ActionTypes["Idle"]) return;
if (this.actionTimeToComplete <= 0) { if (this.actionTimeToComplete <= 0) {
throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`); throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`);
@ -1555,7 +1556,7 @@ export class Bladeburner implements IBladeburner {
this.actionTimeOverflow = 0; this.actionTimeOverflow = 0;
if (this.actionTimeCurrent >= this.actionTimeToComplete) { if (this.actionTimeCurrent >= this.actionTimeToComplete) {
this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete; this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete;
return this.completeAction(player); return this.completeAction(router, player);
} }
} }
@ -1888,10 +1889,10 @@ export class Bladeburner implements IBladeburner {
}); });
} }
process(player: IPlayer): void { process(router: IRouter, player: IPlayer): void {
// Edge case condition...if Operation Daedalus is complete trigger the BitNode // Edge case condition...if Operation Daedalus is complete trigger the BitNode
if (redPillFlag === false && this.blackops.hasOwnProperty("Operation Daedalus")) { if (redPillFlag === false && this.blackops.hasOwnProperty("Operation Daedalus")) {
return hackWorldDaemon(player.bitNodeN); return router.toBitVerse(false, false);
} }
// If the Player starts doing some other actions, set action to idle and alert // If the Player starts doing some other actions, set action to idle and alert
@ -1958,7 +1959,7 @@ export class Bladeburner implements IBladeburner {
this.randomEventCounter += getRandomInt(240, 600); this.randomEventCounter += getRandomInt(240, 600);
} }
this.processAction(player, seconds); this.processAction(router, player, seconds);
// Automation // Automation
if (this.automateEnabled) { if (this.automateEnabled) {

@ -3,6 +3,7 @@ import { City } from "./City";
import { Skill } from "./Skill"; import { Skill } from "./Skill";
import { IAction } from "./IAction"; import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
export interface IBladeburner { export interface IBladeburner {
@ -103,11 +104,11 @@ export interface IBladeburner {
completeOperation(success: boolean): void; completeOperation(success: boolean): void;
getActionObject(actionId: IActionIdentifier): IAction | null; getActionObject(actionId: IActionIdentifier): IAction | null;
completeContract(success: boolean): void; completeContract(success: boolean): void;
completeAction(player: IPlayer): void; completeAction(router: IRouter, player: IPlayer): void;
changeRank(player: IPlayer, change: number): void; changeRank(player: IPlayer, change: number): void;
processAction(player: IPlayer, seconds: number): void; processAction(router: IRouter, player: IPlayer, seconds: number): void;
calculateStaminaGainPerSecond(player: IPlayer): number; calculateStaminaGainPerSecond(player: IPlayer): number;
calculateMaxStamina(player: IPlayer): void; calculateMaxStamina(player: IPlayer): void;
create(): void; create(): void;
process(player: IPlayer): void; process(router: IRouter, player: IPlayer): void;
} }

@ -0,0 +1,42 @@
import React from "react";
import { use } from "../../ui/Context";
import { CinematicText } from "../../ui/React/CinematicText";
import { dialogBoxCreate } from "../../../utils/DialogBox";
export function BladeburnerCinematic(): React.ReactElement {
const router = use.Router();
return (
<CinematicText
lines={[
"In the middle of the 21st century, OmniTek Incorporated advanced robot evolution ",
"with their Synthoids (synthetic androids), a being virtually identical to a human.",
"------",
"Their sixth-generation Synthoids, called MK-VI, were stronger, faster, and more ",
"intelligent than humans. Many argued that the MK-VI Synthoids were the first ",
"example of sentient AI.",
"------",
"Unfortunately, in 2070 a terrorist group called Ascendis Totalis hacked into OmniTek and ",
"uploaded a rogue AI into their Synthoid manufacturing facilities.",
"------",
"The MK-VI Synthoids infected by the rogue AI turned hostile toward humanity, initiating ",
"the deadliest conflict in human history. This dark chapter is now known as the Synthoid Uprising.",
"------",
"In the aftermath of the Uprising, further manufacturing of Synthoids with advanced AI ",
"was banned. MK-VI Synthoids that did not have the rogue Ascendis Totalis AI were ",
"allowed to continue their existence.",
"------",
"The intelligence community believes that not all of the rogue MK-VI Synthoids from the Uprising were ",
"found and destroyed, and that many of them are blending in as normal humans in society today. ",
"As a result, many nations have created Bladeburner divisions, special units that are tasked with ",
"investigating and dealing with Synthoid threats.",
]}
onDone={() => {
router.toTerminal();
dialogBoxCreate(
"Visit the National Security Agency (NSA) to apply for their Bladeburner " +
"division! You will need 100 of each combat stat before doing this.",
);
}}
/>
);
}

@ -3,17 +3,16 @@ import { Stats } from "./Stats";
import { Console } from "./Console"; import { Console } from "./Console";
import { AllPages } from "./AllPages"; import { AllPages } from "./AllPages";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { use } from "../../ui/Context";
import { IEngine } from "../../IEngine";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
engine: IEngine;
player: IPlayer;
} }
export function Root(props: IProps): React.ReactElement { export function BladeburnerRoot(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
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" }}>
@ -25,9 +24,9 @@ export function Root(props: IProps): React.ReactElement {
border: "1px solid white", border: "1px solid white",
}} }}
> >
<Stats bladeburner={props.bladeburner} player={props.player} engine={props.engine} /> <Stats bladeburner={props.bladeburner} player={player} router={router} />
</div> </div>
<Console bladeburner={props.bladeburner} player={props.player} /> <Console bladeburner={props.bladeburner} player={player} />
</div> </div>
<div <div
style={{ style={{
@ -39,7 +38,7 @@ export function Root(props: IProps): React.ReactElement {
position: "relative", position: "relative",
}} }}
> >
<AllPages bladeburner={props.bladeburner} player={props.player} /> <AllPages bladeburner={props.bladeburner} player={player} />
</div> </div>
</div> </div>
); );

@ -2,21 +2,21 @@ import React, { useState, useEffect } from "react";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { BladeburnerConstants } from "../data/Constants"; import { BladeburnerConstants } from "../data/Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { StatsTable } from "../../ui/React/StatsTable"; import { StatsTable } from "../../ui/React/StatsTable";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { joinFaction, displayFactionContent } from "../../Faction/FactionHelpers"; import { IRouter } from "../../ui/Router";
import { joinFaction } from "../../Faction/FactionHelpers";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { TravelPopup } from "./TravelPopup"; import { TravelPopup } from "./TravelPopup";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
engine: IEngine; router: IRouter;
player: IPlayer; player: IPlayer;
} }
@ -72,8 +72,7 @@ export function Stats(props: IProps): React.ReactElement {
function openFaction(): void { function openFaction(): void {
const faction = Factions["Bladeburners"]; const faction = Factions["Bladeburners"];
if (faction.isMember) { if (faction.isMember) {
props.engine.loadFactionContent(); props.router.toFaction(faction);
displayFactionContent("Bladeburners");
} else { } else {
if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) { if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) {
joinFaction(faction); joinFaction(faction);

@ -1,6 +1,7 @@
import { IPlayer } from "./PersonObjects/IPlayer"; import { IPlayer } from "./PersonObjects/IPlayer";
import { Bladeburner } from "./Bladeburner/Bladeburner"; import { Bladeburner } from "./Bladeburner/Bladeburner";
import { IEngine } from "./IEngine"; import { IEngine } from "./IEngine";
import { IRouter } from "./ui/Router";
import React from "react"; import React from "react";
import { TTheme as Theme } from "./ui/React/Theme"; import { TTheme as Theme } from "./ui/React/Theme";
@ -24,6 +25,7 @@ import { TimeSkip } from "./DevMenu/ui/TimeSkip";
interface IProps { interface IProps {
player: IPlayer; player: IPlayer;
engine: IEngine; engine: IEngine;
router: IRouter;
} }
export function DevMenuRoot(props: IProps): React.ReactElement { export function DevMenuRoot(props: IProps): React.ReactElement {
@ -31,7 +33,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
<Theme> <Theme>
<> <>
<h1>Development Menu - Only meant to be used for testing/debugging</h1> <h1>Development Menu - Only meant to be used for testing/debugging</h1>
<General player={props.player} /> <General player={props.player} router={props.router} />
<Stats player={props.player} /> <Stats player={props.player} />
<Factions player={props.player} /> <Factions player={props.player} />
<Augmentations player={props.player} /> <Augmentations player={props.player} />

@ -8,10 +8,11 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { hackWorldDaemon } from "../../RedPill"; import { IRouter } from "../../ui/Router";
interface IProps { interface IProps {
player: IPlayer; player: IPlayer;
router: IRouter;
} }
export function General(props: IProps): React.ReactElement { export function General(props: IProps): React.ReactElement {
@ -26,19 +27,19 @@ export function General(props: IProps): React.ReactElement {
} }
function quickB1tFlum3(): void { function quickB1tFlum3(): void {
hackWorldDaemon(props.player.bitNodeN, true, true); props.router.toBitVerse(true, true);
} }
function b1tflum3(): void { function b1tflum3(): void {
hackWorldDaemon(props.player.bitNodeN, true); props.router.toBitVerse(true, false);
} }
function quickHackW0r1dD43m0n(): void { function quickHackW0r1dD43m0n(): void {
hackWorldDaemon(props.player.bitNodeN, false, true); props.router.toBitVerse(false, true);
} }
function hackW0r1dD43m0n(): void { function hackW0r1dD43m0n(): void {
hackWorldDaemon(props.player.bitNodeN); props.router.toBitVerse(false, false);
} }
return ( return (

@ -30,7 +30,7 @@ export function TimeSkip(props: IProps): React.ReactElement {
return ( return (
<Accordion> <Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}> <AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Sleeves</h2> <h2>Time skip</h2>
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<Button onClick={timeskip(60 * 1000)}>1 minute</Button> <Button onClick={timeskip(60 * 1000)}>1 minute</Button>

@ -4,5 +4,5 @@ import { Faction } from "../Faction/Faction";
export declare function getNextNeurofluxLevel(): number; export declare function getNextNeurofluxLevel(): number;
export declare function hasAugmentationPrereqs(aug: Augmentation): boolean; export declare function hasAugmentationPrereqs(aug: Augmentation): boolean;
export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void; export declare function purchaseAugmentation(aug: Augmentation, fac: Faction, sing?: boolean): void;
export declare function displayFactionContent(factionName: string, initiallyOnAugmentationsPage: boolean = false);
export declare function joinFaction(faction: Faction): void; export declare function joinFaction(faction: Faction): void;
export declare function startHackingMission(faction: Faction): void;

@ -1,14 +1,9 @@
import React from "react";
import ReactDOM from "react-dom";
import { FactionRoot } from "./ui/Root";
import { Augmentations } from "../Augmentation/Augmentations"; import { Augmentations } from "../Augmentation/Augmentations";
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";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { Engine } from "../engine";
import { Faction } from "./Faction"; import { Faction } from "./Faction";
import { Factions } from "./Factions"; import { Factions } from "./Factions";
import { HackingMission, setInMission } from "../Missions"; import { HackingMission, setInMission } from "../Missions";
@ -65,29 +60,6 @@ export function startHackingMission(faction) {
mission.init(); mission.init();
} }
//Displays the HTML content for a specific faction
export function displayFactionContent(factionName, initiallyOnAugmentationsPage = false) {
const faction = Factions[factionName];
if (faction == null) {
throw new Error(`Invalid factionName passed into displayFactionContent(): ${factionName}`);
}
if (!faction.isMember) {
throw new Error(`Not a member of this faction. Cannot display faction information`);
}
ReactDOM.render(
<FactionRoot
engine={Engine}
initiallyOnAugmentationsPage={initiallyOnAugmentationsPage}
faction={faction}
p={Player}
startHackingMissionFn={startHackingMission}
/>,
Engine.Display.content,
);
}
//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) {
@ -187,9 +159,6 @@ export function purchaseAugmentation(aug, fac, sing = false) {
); );
} }
} }
// Force a rerender of the Augmentations page
displayFactionContent(fac.name, true);
} else { } else {
dialogBoxCreate( dialogBoxCreate(
"Hmm, something went wrong when trying to purchase an Augmentation. " + "Hmm, something went wrong when trying to purchase an Augmentation. " +

@ -1,52 +1,37 @@
/** /**
* Root React Component for displaying a faction's "Purchase Augmentations" page * Root React Component for displaying a faction's "Purchase Augmentations" page
*/ */
import * as React from "react"; import React, { useState } from "react";
import { PurchaseableAugmentation } from "./PurchaseableAugmentation"; import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
import { Augmentations } from "../../Augmentation/Augmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
type IProps = { type IProps = {
faction: Faction; faction: Faction;
p: IPlayer;
routeToMainPage: () => void; routeToMainPage: () => void;
}; };
type IState = { export function AugmentationsPage(props: IProps): React.ReactElement {
rerenderFlag: boolean; const player = use.Player();
sortOrder: PurchaseAugmentationsOrderSetting;
};
const infoStyleMarkup = {
width: "70%",
};
export class AugmentationsPage extends React.Component<IProps, IState> {
// Flag for whether the player has a gang with this faction // Flag for whether the player has a gang with this faction
isPlayersGang: boolean; const isPlayersGang = player.inGang() && player.getGangName() === props.faction.name;
constructor(props: IProps) {
super(props);
this.isPlayersGang = props.p.inGang() && props.p.getGangName() === props.faction.name; const setRerender = useState(false)[1];
this.state = { function rerender(): void {
rerenderFlag: false, setRerender((old) => !old);
sortOrder: PurchaseAugmentationsOrderSetting.Default,
};
this.rerender = this.rerender.bind(this);
} }
getAugs(): string[] { function getAugs(): string[] {
if (this.isPlayersGang) { if (isPlayersGang) {
const augs: string[] = []; const augs: string[] = [];
for (const augName in Augmentations) { for (const augName in Augmentations) {
const aug = Augmentations[augName]; const aug = Augmentations[augName];
@ -57,25 +42,25 @@ export class AugmentationsPage extends React.Component<IProps, IState> {
return augs; return augs;
} else { } else {
return this.props.faction.augmentations.slice(); return props.faction.augmentations.slice();
} }
} }
getAugsSorted(): string[] { function getAugsSorted(): string[] {
switch (Settings.PurchaseAugmentationsOrder) { switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: { case PurchaseAugmentationsOrderSetting.Cost: {
return this.getAugsSortedByCost(); return getAugsSortedByCost();
} }
case PurchaseAugmentationsOrderSetting.Reputation: { case PurchaseAugmentationsOrderSetting.Reputation: {
return this.getAugsSortedByReputation(); return getAugsSortedByReputation();
} }
default: default:
return this.getAugsSortedByDefault(); return getAugsSortedByDefault();
} }
} }
getAugsSortedByCost(): string[] { function getAugsSortedByCost(): string[] {
const augs = this.getAugs(); const augs = getAugs();
augs.sort((augName1, augName2) => { augs.sort((augName1, augName2) => {
const aug1 = Augmentations[augName1], const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2]; aug2 = Augmentations[augName2];
@ -89,8 +74,8 @@ export class AugmentationsPage extends React.Component<IProps, IState> {
return augs; return augs;
} }
getAugsSortedByReputation(): string[] { function getAugsSortedByReputation(): string[] {
const augs = this.getAugs(); const augs = getAugs();
augs.sort((augName1, augName2) => { augs.sort((augName1, augName2) => {
const aug1 = Augmentations[augName1], const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2]; aug2 = Augmentations[augName2];
@ -103,88 +88,70 @@ export class AugmentationsPage extends React.Component<IProps, IState> {
return augs; return augs;
} }
getAugsSortedByDefault(): string[] { function getAugsSortedByDefault(): string[] {
return this.getAugs(); return getAugs();
} }
switchSortOrder(newOrder: PurchaseAugmentationsOrderSetting): void { function switchSortOrder(newOrder: PurchaseAugmentationsOrderSetting): void {
Settings.PurchaseAugmentationsOrder = newOrder; Settings.PurchaseAugmentationsOrder = newOrder;
this.rerender(); rerender();
} }
rerender(): void { const augs = getAugsSorted();
this.setState((prevState) => { const purchasable = augs.filter(
return { (aug: string) =>
rerenderFlag: !prevState.rerenderFlag, aug === AugmentationNames.NeuroFluxGovernor ||
}; (!player.augmentations.some((a) => a.name === aug) && !player.queuedAugmentations.some((a) => a.name === aug)),
}); );
}
render(): React.ReactNode { const purchaseableAugmentation = (aug: string): React.ReactNode => {
const augs = this.getAugsSorted(); return <PurchaseableAugmentation augName={aug} faction={props.faction} key={aug} p={player} rerender={rerender} />;
const purchasable = augs.filter( };
(aug: string) => aug === AugmentationNames.NeuroFluxGovernor ||
(!this.props.p.augmentations.some((a) => a.name === aug) &&
!this.props.p.queuedAugmentations.some((a) => a.name === aug)),
);
const purchaseableAugmentation = (aug: string): React.ReactNode => { const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
return (
<PurchaseableAugmentation
augName={aug}
faction={this.props.faction}
key={aug}
p={this.props.p}
rerender={this.rerender}
/>
);
};
const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug)); let ownedElem = <></>;
const owned = augs.filter((aug: string) => !purchasable.includes(aug));
let ownedElem = <></>; if (owned.length !== 0) {
const owned = augs.filter((aug: string) => !purchasable.includes(aug)); ownedElem = (
if (owned.length !== 0) { <>
ownedElem = (
<>
<br />
<h2>Purchased Augmentations</h2>
<p style={infoStyleMarkup}>This factions also offers these augmentations but you already own them.</p>
{owned.map((aug) => purchaseableAugmentation(aug))}
</>
);
}
return (
<div>
<StdButton onClick={this.props.routeToMainPage} text={"Back"} />
<h1>Faction Augmentations</h1>
<p style={infoStyleMarkup}>
These are all of the Augmentations that are available to purchase from {this.props.faction.name}.
Augmentations are powerful upgrades that will enhance your abilities.
</p>
<StdButton onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)} text={"Sort by Cost"} />
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}
text={"Sort by Reputation"}
/>
<StdButton
onClick={() => this.switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}
text={"Sort by Default Order"}
/>
<br /> <br />
{augListElems} <h2>Purchased Augmentations</h2>
{ownedElem} <p>This factions also offers these augmentations but you already own them.</p>
<br /> {owned.map((aug) => purchaseableAugmentation(aug))}
<br /> </>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
</div>
); );
} }
return (
<div>
<StdButton onClick={props.routeToMainPage} text={"Back"} />
<h1>Faction Augmentations</h1>
<p>
These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are
powerful upgrades that will enhance your abilities.
</p>
<StdButton onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)} text={"Sort by Cost"} />
<StdButton
onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}
text={"Sort by Reputation"}
/>
<StdButton
onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}
text={"Sort by Default Order"}
/>
<br />
{augListElems}
{ownedElem}
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
</div>
);
} }

@ -1,58 +1,66 @@
/** /**
* React Component for the popup used to create a new gang. * React Component for the popup used to create a new gang.
*/ */
import React from "react"; import React from "react";
import { removePopup } from "../../ui/React/createPopup"; import { removePopup } from "../../ui/React/createPopup";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { StdButton } from "../../ui/React/StdButton";
import { StdButton } from "../../ui/React/StdButton"; import { use } from "../../ui/Context";
import { IEngine } from "../../IEngine";
interface ICreateGangPopupProps { interface ICreateGangPopupProps {
popupId: string; popupId: string;
facName: string; facName: string;
p: IPlayer; }
engine: IEngine;
}
export function CreateGangPopup(props: ICreateGangPopupProps): React.ReactElement { export function CreateGangPopup(props: ICreateGangPopupProps): React.ReactElement {
const player = use.Player();
const combatGangText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " + const router = use.Router();
const combatGangText =
"This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " + "Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones."; "is more important. However, well-managed combat gangs can progress faster than hacking ones.";
const hackingGangText = "This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " + const hackingGangText =
"This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " + "Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important."; "is not as important.";
function isHacking(): boolean { function isHacking(): boolean {
return ["NiteSec", "The Black Hand"].includes(props.facName); return ["NiteSec", "The Black Hand"].includes(props.facName);
} }
function createGang(): void { function createGang(): void {
props.p.startGang(props.facName, isHacking()); player.startGang(props.facName, isHacking());
removePopup(props.popupId); removePopup(props.popupId);
props.engine.loadGangContent(); router.toGang();
} }
function onKeyUp(event: React.KeyboardEvent): void { function onKeyUp(event: React.KeyboardEvent): void {
if (event.keyCode === 13) createGang(); if (event.keyCode === 13) createGang();
} }
return ( return (
<> <>
Would you like to create a new Gang with {props.facName}? Would you like to create a new Gang with {props.facName}?
<br/> <br />
<br/> <br />
Note that this will prevent you from creating a Gang with any other Faction until this BitNode is destroyed. It also resets your reputation with this faction. Note that this will prevent you from creating a Gang with any other Faction until this BitNode is destroyed. It
<br/> also resets your reputation with this faction.
<br/> <br />
{ (isHacking()) ? hackingGangText : combatGangText } <br />
<br/> {isHacking() ? hackingGangText : combatGangText}
<br/> <br />
Other than hacking vs combat, there are NO differences between the Factions you can create a Gang with, and each of these Factions have all Augmentations available. <br />
<div className="popup-box-input-div" > Other than hacking vs combat, there are NO differences between the Factions you can create a Gang with, and each
<StdButton onClick={createGang} onKeyUp={onKeyUp} text="Create Gang" style={{float: "right"}} autoFocus={true}/> of these Factions have all Augmentations available.
</div> <div className="popup-box-input-div">
</> <StdButton
); onClick={createGang}
} onKeyUp={onKeyUp}
text="Create Gang"
style={{ float: "right" }}
autoFocus={true}
/>
</div>
</>
);
}

@ -3,7 +3,7 @@
* This is the component for displaying a single faction's UI, not the list of all * This is the component for displaying a single faction's UI, not the list of all
* accessible factions * accessible factions
*/ */
import * as React from "react"; import React, { useState } from "react";
import { AugmentationsPage } from "./AugmentationsPage"; import { AugmentationsPage } from "./AugmentationsPage";
import { DonateOption } from "./DonateOption"; import { DonateOption } from "./DonateOption";
@ -11,30 +11,21 @@ import { Info } from "./Info";
import { Option } from "./Option"; import { Option } from "./Option";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IEngine } from "../../IEngine";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases"; import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context";
import { CreateGangPopup } from "./CreateGangPopup"; import { CreateGangPopup } from "./CreateGangPopup";
type IProps = { type IProps = {
engine: IEngine; faction: Faction | null;
initiallyOnAugmentationsPage?: boolean;
faction: Faction;
p: IPlayer;
startHackingMissionFn: (faction: Faction) => void; startHackingMissionFn: (faction: Faction) => void;
}; };
type IState = {
rerenderFlag: boolean;
purchasingAugs: boolean;
};
// Info text for all options on the UI // Info text for all options on the UI
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " + "faction reputation"; const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " + "faction reputation";
const hackingMissionInfo = const hackingMissionInfo =
@ -72,89 +63,68 @@ const GangNames = [
"The Black Hand", "The Black Hand",
]; ];
export class FactionRoot extends React.Component<IProps, IState> { export function FactionRoot(props: IProps): React.ReactElement {
constructor(props: IProps) { const faction = props.faction;
super(props); if (faction === null) throw new Error("Trying to render the Faction page with null faction");
this.state = { const player = use.Player();
rerenderFlag: false, const router = use.Router();
purchasingAugs: props.initiallyOnAugmentationsPage ? props.initiallyOnAugmentationsPage : false, const [, setRerenderFlag] = useState(false);
}; const [purchasingAugs, setPurchasingAugs] = useState(false);
this.manageGang = this.manageGang.bind(this); function manageGang(faction: Faction): void {
this.rerender = this.rerender.bind(this);
this.routeToMain = this.routeToMain.bind(this);
this.routeToPurchaseAugs = this.routeToPurchaseAugs.bind(this);
this.sleevePurchases = this.sleevePurchases.bind(this);
this.startFieldWork = this.startFieldWork.bind(this);
this.startHackingContracts = this.startHackingContracts.bind(this);
this.startHackingMission = this.startHackingMission.bind(this);
this.startSecurityWork = this.startSecurityWork.bind(this);
}
manageGang(): void {
// If player already has a gang, just go to the gang UI // If player already has a gang, just go to the gang UI
if (this.props.p.inGang()) { if (player.inGang()) {
return this.props.engine.loadGangContent(); return router.toGang();
} }
const popupId = "create-gang-popup"; const popupId = "create-gang-popup";
createPopup(popupId, CreateGangPopup, { createPopup(popupId, CreateGangPopup, {
popupId: popupId, popupId: popupId,
facName: this.props.faction.name, facName: faction.name,
p: this.props.p,
engine: this.props.engine,
}); });
} }
rerender(): void { function rerender(): void {
this.setState((prevState) => { setRerenderFlag((old) => !old);
return {
rerenderFlag: !prevState.rerenderFlag,
};
});
} }
// Route to the main faction page // Route to the main faction page
routeToMain(): void { function routeToMain(): void {
this.setState({ purchasingAugs: false }); setPurchasingAugs(false);
} }
// Route to the purchase augmentation UI for this faction // Route to the purchase augmentation UI for this faction
routeToPurchaseAugs(): void { function routeToPurchaseAugs(): void {
this.setState({ purchasingAugs: true }); setPurchasingAugs(true);
} }
sleevePurchases(): void { function sleevePurchases(): void {
createSleevePurchasesFromCovenantPopup(this.props.p); createSleevePurchasesFromCovenantPopup(player);
} }
startFieldWork(): void { function startFieldWork(faction: Faction): void {
this.props.p.startFactionFieldWork(this.props.faction); player.startFactionFieldWork(faction);
router.toWork();
} }
startHackingContracts(): void { function startHackingContracts(faction: Faction): void {
this.props.p.startFactionHackWork(this.props.faction); player.startFactionHackWork(faction);
router.toWork();
} }
startHackingMission(): void { function startHackingMission(faction: Faction): void {
const fac = this.props.faction; player.singularityStopWork();
this.props.p.singularityStopWork(); props.startHackingMissionFn(faction);
this.props.engine.loadMissionContent();
this.props.startHackingMissionFn(fac);
} }
startSecurityWork(): void { function startSecurityWork(faction: Faction): void {
this.props.p.startFactionSecurityWork(this.props.faction); player.startFactionSecurityWork(faction);
router.toWork();
} }
render(): React.ReactNode { function MainPage({ faction }: { faction: Faction }): React.ReactElement {
return this.state.purchasingAugs ? this.renderAugmentationsPage() : this.renderMainPage(); const p = player;
}
renderMainPage(): React.ReactNode {
const p = this.props.p;
const faction = this.props.faction;
const factionInfo = faction.getInfo(); const factionInfo = faction.getInfo();
// We have a special flag for whether the player this faction is the player's // We have a special flag for whether the player this faction is the player's
@ -181,49 +151,51 @@ export class FactionRoot extends React.Component<IProps, IState> {
<div className="faction-container"> <div className="faction-container">
<h1>{faction.name}</h1> <h1>{faction.name}</h1>
<Info faction={faction} factionInfo={factionInfo} /> <Info faction={faction} factionInfo={factionInfo} />
{canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={this.manageGang} />} {canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={() => manageGang(faction)} />}
{!isPlayersGang && factionInfo.offerHackingMission && ( {!isPlayersGang && factionInfo.offerHackingMission && (
<Option buttonText={"Hacking Mission"} infoText={hackingMissionInfo} onClick={this.startHackingMission} /> <Option
buttonText={"Hacking Mission"}
infoText={hackingMissionInfo}
onClick={() => startHackingMission(faction)}
/>
)} )}
{!isPlayersGang && factionInfo.offerHackingWork && ( {!isPlayersGang && factionInfo.offerHackingWork && (
<Option <Option
buttonText={"Hacking Contracts"} buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo} infoText={hackingContractsInfo}
onClick={this.startHackingContracts} onClick={() => startHackingContracts(faction)}
/> />
)} )}
{!isPlayersGang && factionInfo.offerFieldWork && ( {!isPlayersGang && factionInfo.offerFieldWork && (
<Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={this.startFieldWork} /> <Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={() => startFieldWork(faction)} />
)} )}
{!isPlayersGang && factionInfo.offerSecurityWork && ( {!isPlayersGang && factionInfo.offerSecurityWork && (
<Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={this.startSecurityWork} /> <Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={() => startSecurityWork(faction)} />
)} )}
{!isPlayersGang && factionInfo.offersWork() && ( {!isPlayersGang && factionInfo.offersWork() && (
<DonateOption <DonateOption
faction={this.props.faction} faction={faction}
p={this.props.p} p={player}
rerender={this.rerender} rerender={rerender}
favorToDonate={favorToDonate} favorToDonate={favorToDonate}
disabled={!canDonate} disabled={!canDonate}
/> />
)} )}
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={this.routeToPurchaseAugs} /> <Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={routeToPurchaseAugs} />
{canPurchaseSleeves && ( {canPurchaseSleeves && (
<Option <Option
buttonText={"Purchase & Upgrade Duplicate Sleeves"} buttonText={"Purchase & Upgrade Duplicate Sleeves"}
infoText={sleevePurchasesInfo} infoText={sleevePurchasesInfo}
onClick={this.sleevePurchases} onClick={sleevePurchases}
/> />
)} )}
</div> </div>
); );
} }
renderAugmentationsPage(): React.ReactNode { return purchasingAugs ? (
return ( <AugmentationsPage faction={faction} routeToMainPage={routeToMain} />
<> ) : (
<AugmentationsPage faction={this.props.faction} p={this.props.p} routeToMainPage={this.routeToMain} /> <MainPage faction={faction} />
</> );
);
}
} }

@ -1,20 +1,20 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine"; import { IRouter } from "../../ui/Router";
import { Factions } from "../Factions"; import { Factions } from "../Factions";
import { displayFactionContent, joinFaction } from "../FactionHelpers"; import { Faction } from "../Faction";
import { joinFaction } from "../FactionHelpers";
interface IProps { interface IProps {
player: IPlayer; player: IPlayer;
engine: IEngine; router: IRouter;
} }
export function FactionList(props: IProps): React.ReactElement { export function FactionsRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function openFaction(faction: string): void { function openFaction(faction: Faction): void {
props.engine.loadFactionContent(); props.router.toFaction(faction);
displayFactionContent(faction);
} }
function acceptInvitation(event: React.MouseEvent<HTMLElement>, faction: string): void { function acceptInvitation(event: React.MouseEvent<HTMLElement>, faction: string): void {
@ -33,7 +33,7 @@ export function FactionList(props: IProps): React.ReactElement {
<li key={faction}> <li key={faction}>
<a <a
className="a-link-button" className="a-link-button"
onClick={() => openFaction(faction)} onClick={() => openFaction(Factions[faction])}
style={{ padding: "4px", margin: "4px", display: "inline-block" }} style={{ padding: "4px", margin: "4px", display: "inline-block" }}
> >
{faction} {faction}

@ -12,6 +12,7 @@ interface IProps {
player: IPlayer; player: IPlayer;
faction: Faction; faction: Faction;
aug: Augmentation; aug: Augmentation;
rerender: () => void;
popupId: string; popupId: string;
} }
@ -24,6 +25,7 @@ export function PurchaseAugmentationPopup(props: IProps): React.ReactElement {
} }
purchaseAugmentation(props.aug, props.faction); purchaseAugmentation(props.aug, props.faction);
props.rerender();
removePopup(props.popupId); removePopup(props.popupId);
} }

@ -7,7 +7,6 @@ import * as React from "react";
import { getNextNeurofluxLevel, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers"; import { getNextNeurofluxLevel, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
import { PurchaseAugmentationPopup } from "./PurchaseAugmentationPopup"; import { PurchaseAugmentationPopup } from "./PurchaseAugmentationPopup";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations"; import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
@ -28,63 +27,56 @@ type IProps = {
rerender: () => void; rerender: () => void;
}; };
export class PurchaseableAugmentation extends React.Component<IProps, any> { export function PurchaseableAugmentation(props: IProps): React.ReactElement {
aug: Augmentation; const aug = Augmentations[props.augName];
if (aug == null) throw new Error(`aug ${props.augName} does not exists`);
constructor(props: IProps) { function getMoneyCost(): number {
super(props); return aug.baseCost * props.faction.getInfo().augmentationPriceMult;
const aug = Augmentations[this.props.augName];
if (aug == null) throw new Error(`aug ${this.props.augName} does not exists`);
this.aug = aug;
this.handleClick = this.handleClick.bind(this);
} }
getMoneyCost(): number { function getRepCost(): number {
return this.aug.baseCost * this.props.faction.getInfo().augmentationPriceMult; return aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
} }
getRepCost(): number { function handleClick(): void {
return this.aug.baseRepRequirement * this.props.faction.getInfo().augmentationRepRequirementMult;
}
handleClick(): void {
if (!Settings.SuppressBuyAugmentationConfirmation) { if (!Settings.SuppressBuyAugmentationConfirmation) {
const popupId = "purchase-augmentation-popup"; const popupId = "purchase-augmentation-popup";
createPopup(popupId, PurchaseAugmentationPopup, { createPopup(popupId, PurchaseAugmentationPopup, {
aug: this.aug, aug: aug,
faction: this.props.faction, faction: props.faction,
player: this.props.p, player: props.p,
rerender: props.rerender,
popupId: popupId, popupId: popupId,
}); });
} else { } else {
purchaseAugmentation(this.aug, this.props.faction); purchaseAugmentation(aug, props.faction);
props.rerender();
} }
} }
// Whether the player has the prerequisite Augmentations // Whether the player has the prerequisite Augmentations
hasPrereqs(): boolean { function hasPrereqs(): boolean {
return hasAugmentationPrereqs(this.aug); return hasAugmentationPrereqs(aug);
} }
// Whether the player has enough rep for this Augmentation // Whether the player has enough rep for this Augmentation
hasReputation(): boolean { function hasReputation(): boolean {
return this.props.faction.playerReputation >= this.getRepCost(); return props.faction.playerReputation >= getRepCost();
} }
// Whether the player has this augmentations (purchased OR installed) // Whether the player has this augmentations (purchased OR installed)
owned(): boolean { function owned(): boolean {
let owned = false; let owned = false;
for (const queuedAug of this.props.p.queuedAugmentations) { for (const queuedAug of props.p.queuedAugmentations) {
if (queuedAug.name === this.props.augName) { if (queuedAug.name === props.augName) {
owned = true; owned = true;
break; break;
} }
} }
for (const installedAug of this.props.p.augmentations) { for (const installedAug of props.p.augmentations) {
if (installedAug.name === this.props.augName) { if (installedAug.name === props.augName) {
owned = true; owned = true;
break; break;
} }
@ -93,96 +85,94 @@ export class PurchaseableAugmentation extends React.Component<IProps, any> {
return owned; return owned;
} }
render(): React.ReactNode { if (aug == null) {
if (this.aug == null) { console.error(
console.error( `Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`,
`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${this.props.augName}`,
);
return null;
}
const moneyCost = this.getMoneyCost();
const repCost = this.getRepCost();
// Determine UI properties
let disabled = false;
let status: JSX.Element = <></>;
let color = "";
if (!this.hasPrereqs()) {
disabled = true;
status = <>LOCKED (Requires {this.aug.prereqs.map((aug) => AugFormat(aug))} as prerequisite)</>;
color = "red";
} else if (this.aug.name !== AugmentationNames.NeuroFluxGovernor && (this.aug.owned || this.owned())) {
disabled = true;
} else if (this.hasReputation()) {
status = (
<>
UNLOCKED (at {Reputation(repCost)} faction reputation) - <Money money={moneyCost} player={this.props.p} />
</>
);
} else {
disabled = true;
status = (
<>
LOCKED (Requires {Reputation(repCost)} faction reputation - <Money money={moneyCost} player={this.props.p} />)
</>
);
color = "red";
}
const txtStyle: IMap<string> = {
display: "inline-block",
};
if (color !== "") {
txtStyle.color = color;
}
// Determine button txt
let btnTxt = this.aug.name;
if (this.aug.name === AugmentationNames.NeuroFluxGovernor) {
btnTxt += ` - Level ${getNextNeurofluxLevel()}`;
}
let tooltip = <></>;
if (typeof this.aug.info === "string")
tooltip = (
<>
<span dangerouslySetInnerHTML={{ __html: this.aug.info }} />
<br />
<br />
{this.aug.stats}
</>
);
else
tooltip = (
<>
{this.aug.info}
<br />
<br />
{this.aug.stats}
</>
);
return (
<li key={this.aug.name}>
<span
style={{
margin: "4px",
padding: "4px",
}}
>
<StdButton
disabled={disabled}
onClick={this.handleClick}
style={{
display: "inline-block",
}}
text={btnTxt}
tooltip={tooltip}
/>
<p style={txtStyle}>{status}</p>
</span>
</li>
); );
return <></>;
} }
const moneyCost = getMoneyCost();
const repCost = getRepCost();
// Determine UI properties
let disabled = false;
let status: JSX.Element = <></>;
let color = "";
if (!hasPrereqs()) {
disabled = true;
status = <>LOCKED (Requires {aug.prereqs.map((aug) => AugFormat(aug))} as prerequisite)</>;
color = "red";
} else if (aug.name !== AugmentationNames.NeuroFluxGovernor && (aug.owned || owned())) {
disabled = true;
} else if (hasReputation()) {
status = (
<>
UNLOCKED (at {Reputation(repCost)} faction reputation) - <Money money={moneyCost} player={props.p} />
</>
);
} else {
disabled = true;
status = (
<>
LOCKED (Requires {Reputation(repCost)} faction reputation - <Money money={moneyCost} player={props.p} />)
</>
);
color = "red";
}
const txtStyle: IMap<string> = {
display: "inline-block",
};
if (color !== "") {
txtStyle.color = color;
}
// Determine button txt
let btnTxt = aug.name;
if (aug.name === AugmentationNames.NeuroFluxGovernor) {
btnTxt += ` - Level ${getNextNeurofluxLevel()}`;
}
let tooltip = <></>;
if (typeof aug.info === "string")
tooltip = (
<>
<span dangerouslySetInnerHTML={{ __html: aug.info }} />
<br />
<br />
{aug.stats}
</>
);
else
tooltip = (
<>
{aug.info}
<br />
<br />
{aug.stats}
</>
);
return (
<li key={aug.name}>
<span
style={{
margin: "4px",
padding: "4px",
}}
>
<StdButton
disabled={disabled}
onClick={handleClick}
style={{
display: "inline-block",
}}
text={btnTxt}
tooltip={tooltip}
/>
<p style={txtStyle}>{status}</p>
</span>
</li>
);
} }

@ -2,6 +2,7 @@ import { GangMemberUpgrade } from "./GangMemberUpgrade";
import { GangMember } from "./GangMember"; import { GangMember } from "./GangMember";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IAscensionResult } from "./IAscensionResult";
export interface IGang { export interface IGang {
facName: string; facName: string;
@ -37,8 +38,9 @@ export interface IGang {
getWantedPenalty(): number; getWantedPenalty(): number;
calculatePower(): number; calculatePower(): number;
killMember(member: GangMember): void; killMember(member: GangMember): void;
ascendMember(member: GangMember, workerScript: WorkerScript): void; ascendMember(member: GangMember, workerScript: WorkerScript): IAscensionResult;
getDiscount(): number; getDiscount(): number;
getAllTaskNames(): string[]; getAllTaskNames(): string[];
getUpgradeCost(upg: GangMemberUpgrade): number; getUpgradeCost(upg: GangMemberUpgrade): number;
toJSON(): any;
} }

@ -2,20 +2,19 @@
* React Component for all the gang stuff. * React Component for all the gang stuff.
*/ */
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { ManagementSubpage } from "./ManagementSubpage"; import { ManagementSubpage } from "./ManagementSubpage";
import { TerritorySubpage } from "./TerritorySubpage"; import { TerritorySubpage } from "./TerritorySubpage";
import { IEngine } from "../../IEngine"; import { use } from "../../ui/Context";
import { Factions } from "../../Faction/Factions";
import { Gang } from "../Gang"; import { Gang } from "../Gang";
import { displayFactionContent } from "../../Faction/FactionHelpers";
interface IProps { interface IProps {
gang: Gang; gang: Gang;
player: IPlayer;
engine: IEngine;
} }
export function Root(props: IProps): React.ReactElement { export function GangRoot(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [management, setManagement] = useState(true); const [management, setManagement] = useState(true);
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
@ -25,8 +24,7 @@ export function Root(props: IProps): React.ReactElement {
}, []); }, []);
function back(): void { function back(): void {
props.engine.loadFactionContent(); router.toFaction(Factions[props.gang.facName]);
displayFactionContent(props.gang.facName);
} }
return ( return (
@ -48,11 +46,7 @@ export function Root(props: IProps): React.ReactElement {
> >
Gang Territory Gang Territory
</a> </a>
{management ? ( {management ? <ManagementSubpage gang={props.gang} player={player} /> : <TerritorySubpage gang={props.gang} />}
<ManagementSubpage gang={props.gang} player={props.player} />
) : (
<TerritorySubpage gang={props.gang} />
)}
</div> </div>
); );
} }

@ -1,51 +0,0 @@
import { Page, routing } from ".././ui/navigationTracking";
import { Root } from "./ui/Root";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IEngine } from "../IEngine";
import * as React from "react";
import * as ReactDOM from "react-dom";
let container: HTMLElement;
(function () {
function setContainer(): void {
const c = document.getElementById("infiltration-container");
if (c === null) throw new Error("huh?");
container = c;
document.removeEventListener("DOMContentLoaded", setContainer);
}
document.addEventListener("DOMContentLoaded", setContainer);
})();
function calcDifficulty(player: IPlayer, startingDifficulty: number): number {
const totalStats = player.strength + player.defense + player.dexterity + player.agility + player.charisma;
const difficulty = startingDifficulty - Math.pow(totalStats, 0.9) / 250 - player.intelligence / 1600;
if (difficulty < 0) return 0;
if (difficulty > 3) return 3;
return difficulty;
}
export function displayInfiltrationContent(
engine: IEngine,
player: IPlayer,
location: string,
startingDifficulty: number,
maxLevel: number,
): void {
if (!routing.isOn(Page.Infiltration)) return;
const difficulty = calcDifficulty(player, startingDifficulty);
ReactDOM.render(
<Root
Engine={engine}
Player={player}
Location={location}
StartingDifficulty={startingDifficulty}
Difficulty={difficulty}
MaxLevel={maxLevel}
/>,
container,
);
}

@ -1,5 +1,4 @@
import { IPlayer } from "../../PersonObjects/IPlayer"; import { use } from "../../ui/Context";
import { IEngine } from "../../IEngine";
import React, { useState } from "react"; import React, { useState } from "react";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
import { Countdown } from "./Countdown"; import { Countdown } from "./Countdown";
@ -14,8 +13,6 @@ import { WireCuttingGame } from "./WireCuttingGame";
import { Victory } from "./Victory"; import { Victory } from "./Victory";
interface IProps { interface IProps {
Player: IPlayer;
Engine: IEngine;
StartingDifficulty: number; StartingDifficulty: number;
Difficulty: number; Difficulty: number;
MaxLevel: number; MaxLevel: number;
@ -40,6 +37,8 @@ const minigames = [
]; ];
export function Game(props: IProps): React.ReactElement { export function Game(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [level, setLevel] = useState(1); const [level, setLevel] = useState(1);
const [stage, setStage] = useState(Stage.Countdown); const [stage, setStage] = useState(Stage.Countdown);
const [results, setResults] = useState(""); const [results, setResults] = useState("");
@ -89,12 +88,10 @@ export function Game(props: IProps): React.ReactElement {
pushResult(false); pushResult(false);
// Kill the player immediately if they use automation, so // Kill the player immediately if they use automation, so
// it's clear they're not meant to // it's clear they're not meant to
const damage = options?.automated ? props.Player.hp : props.StartingDifficulty * 3; const damage = options?.automated ? player.hp : props.StartingDifficulty * 3;
if (props.Player.takeDamage(damage)) { if (player.takeDamage(damage)) {
const menu = document.getElementById("mainmenu-container"); router.toCity();
if (menu === null) throw new Error("mainmenu-container not found"); return;
menu.style.visibility = "visible";
props.Engine.loadLocationContent();
} }
setupNextGame(); setupNextGame();
} }
@ -112,8 +109,6 @@ export function Game(props: IProps): React.ReactElement {
case Stage.Sell: case Stage.Sell:
stageComponent = ( stageComponent = (
<Victory <Victory
Player={props.Player}
Engine={props.Engine}
StartingDifficulty={props.StartingDifficulty} StartingDifficulty={props.StartingDifficulty}
Difficulty={props.Difficulty} Difficulty={props.Difficulty}
MaxLevel={props.MaxLevel} MaxLevel={props.MaxLevel}

@ -0,0 +1,53 @@
import { IPlayer } from "../../PersonObjects/IPlayer";
import React, { useState } from "react";
import { Intro } from "./Intro";
import { Game } from "./Game";
import { LocationName } from "../../Locations/data/LocationNames";
import { Locations } from "../../Locations/Locations";
import { use } from "../../ui/Context";
interface IProps {
location: LocationName;
}
function calcDifficulty(player: IPlayer, startingDifficulty: number): number {
const totalStats = player.strength + player.defense + player.dexterity + player.agility + player.charisma;
const difficulty = startingDifficulty - Math.pow(totalStats, 0.9) / 250 - player.intelligence / 1600;
if (difficulty < 0) return 0;
if (difficulty > 3) return 3;
return difficulty;
}
export function InfiltrationRoot(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [start, setStart] = useState(false);
const loc = Locations[props.location];
if (loc.infiltrationData === undefined) throw new Error("Trying to do infiltration on invalid location.");
const startingDifficulty = loc.infiltrationData.startingSecurityLevel;
const difficulty = calcDifficulty(player, startingDifficulty);
function cancel(): void {
router.toCity();
}
if (!start) {
return (
<Intro
Location={props.location}
Difficulty={difficulty}
MaxLevel={loc.infiltrationData.maxClearanceLevel}
start={() => setStart(true)}
cancel={cancel}
/>
);
}
return (
<Game
StartingDifficulty={startingDifficulty}
Difficulty={difficulty}
MaxLevel={loc.infiltrationData.maxClearanceLevel}
/>
);
}

@ -1,12 +1,8 @@
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
import React from "react"; import React from "react";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
interface IProps { interface IProps {
Player: IPlayer;
Engine: IEngine;
Location: string; Location: string;
Difficulty: number; Difficulty: number;
MaxLevel: number; MaxLevel: number;

@ -1,49 +0,0 @@
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
import React, { useState } from "react";
import { Intro } from "./Intro";
import { Game } from "./Game";
interface IProps {
Player: IPlayer;
Engine: IEngine;
Location: string;
StartingDifficulty: number;
Difficulty: number;
MaxLevel: number;
}
export function Root(props: IProps): React.ReactElement {
const [start, setStart] = useState(false);
function cancel(): void {
const menu = document.getElementById("mainmenu-container");
if (menu === null) throw new Error("mainmenu-container not found");
menu.style.visibility = "visible";
props.Engine.loadLocationContent();
}
if (!start) {
return (
<Intro
Player={props.Player}
Engine={props.Engine}
Location={props.Location}
Difficulty={props.Difficulty}
MaxLevel={props.MaxLevel}
start={() => setStart(true)}
cancel={cancel}
/>
);
}
return (
<Game
Player={props.Player}
Engine={props.Engine}
StartingDifficulty={props.StartingDifficulty}
Difficulty={props.Difficulty}
MaxLevel={props.MaxLevel}
/>
);
}

@ -1,5 +1,3 @@
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import React, { useState } from "react"; import React, { useState } from "react";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
@ -7,23 +5,21 @@ import Grid from "@mui/material/Grid";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { use } from "../../ui/Context";
interface IProps { interface IProps {
Player: IPlayer;
Engine: IEngine;
StartingDifficulty: number; StartingDifficulty: number;
Difficulty: number; Difficulty: number;
MaxLevel: number; MaxLevel: number;
} }
export function Victory(props: IProps): React.ReactElement { export function Victory(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [faction, setFaction] = useState("none"); const [faction, setFaction] = useState("none");
function quitInfiltration(): void { function quitInfiltration(): void {
const menu = document.getElementById("mainmenu-container"); router.toCity();
if (!menu) throw new Error("mainmenu-container somehow null");
menu.style.visibility = "visible";
props.Engine.loadLocationContent();
} }
const levelBonus = props.MaxLevel * Math.pow(1.01, props.MaxLevel); const levelBonus = props.MaxLevel * Math.pow(1.01, props.MaxLevel);
@ -43,8 +39,8 @@ export function Victory(props: IProps): React.ReactElement {
BitNodeMultipliers.InfiltrationMoney; BitNodeMultipliers.InfiltrationMoney;
function sell(): void { function sell(): void {
props.Player.gainMoney(moneyGain); player.gainMoney(moneyGain);
props.Player.recordMoneySource(moneyGain, "infiltration"); player.recordMoneySource(moneyGain, "infiltration");
quitInfiltration(); quitInfiltration();
} }
@ -70,7 +66,7 @@ export function Victory(props: IProps): React.ReactElement {
<option key={"none"} value={"none"}> <option key={"none"} value={"none"}>
{"none"} {"none"}
</option> </option>
{props.Player.factions {player.factions
.filter((f) => Factions[f].getInfo().offersWork()) .filter((f) => Factions[f].getInfo().offersWork())
.map((f) => ( .map((f) => (
<option key={f} value={f}> <option key={f} value={f}>

@ -63,8 +63,6 @@ function iTutorialStart() {
ITutorial.stepIsDone[i] = false; ITutorial.stepIsDone[i] = false;
} }
Engine.loadTerminalContent();
// Don't autosave during this interactive tutorial // Don't autosave during this interactive tutorial
Engine.Counters.autoSaveCounter = Infinity; Engine.Counters.autoSaveCounter = Infinity;
ITutorial.currStep = 0; ITutorial.currStep = 0;
@ -120,7 +118,6 @@ function iTutorialEvaluateStep() {
switch (ITutorial.currStep) { switch (ITutorial.currStep) {
case iTutorialSteps.Start: case iTutorialSteps.Start:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Welcome to Bitburner, a cyberpunk-themed incremental RPG! " + "Welcome to Bitburner, a cyberpunk-themed incremental RPG! " +
"The game takes place in a dark, dystopian future... The year is 2077...<br><br>" + "The game takes place in a dark, dystopian future... The year is 2077...<br><br>" +
@ -130,7 +127,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block"; nextBtn.style.display = "inline-block";
break; break;
case iTutorialSteps.GoToCharacterPage: case iTutorialSteps.GoToCharacterPage:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Let's start by heading to the Stats page. Click the <code class='interactive-tutorial-tab flashing-button'>Stats</code> tab on " + "Let's start by heading to the Stats page. Click the <code class='interactive-tutorial-tab flashing-button'>Stats</code> tab on " +
"the main navigation menu (left-hand side of the screen)", "the main navigation menu (left-hand side of the screen)",
@ -138,7 +134,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; nextBtn.style.display = "none";
break; break;
case iTutorialSteps.CharacterPage: case iTutorialSteps.CharacterPage:
Engine.loadCharacterContent();
iTutorialSetText( iTutorialSetText(
"The <code class='interactive-tutorial-tab'>Stats</code> page shows a lot of important information about your progress, " + "The <code class='interactive-tutorial-tab'>Stats</code> page shows a lot of important information about your progress, " +
"such as your skills, money, and bonuses. ", "such as your skills, money, and bonuses. ",
@ -146,7 +141,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block"; nextBtn.style.display = "inline-block";
break; break;
case iTutorialSteps.CharacterGoToTerminalPage: case iTutorialSteps.CharacterGoToTerminalPage:
Engine.loadCharacterContent();
iTutorialSetText( iTutorialSetText(
"Let's head to your computer's terminal by clicking the <code class='interactive-tutorial-tab flashing-button'>Terminal</code> tab on the " + "Let's head to your computer's terminal by clicking the <code class='interactive-tutorial-tab flashing-button'>Terminal</code> tab on the " +
"main navigation menu.", "main navigation menu.",
@ -154,7 +148,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; nextBtn.style.display = "none";
break; break;
case iTutorialSteps.TerminalIntro: case iTutorialSteps.TerminalIntro:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"The <code class='interactive-tutorial-tab'>Terminal</code> is used to interface with your home computer as well as " + "The <code class='interactive-tutorial-tab'>Terminal</code> is used to interface with your home computer as well as " +
"all of the other machines around the world.", "all of the other machines around the world.",
@ -162,7 +155,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block"; nextBtn.style.display = "inline-block";
break; break;
case iTutorialSteps.TerminalHelp: case iTutorialSteps.TerminalHelp:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Let's try it out. Start by entering the <code class='interactive-tutorial-command'>help</code> command into the <code class='interactive-tutorial-tab'>Terminal</code> " + "Let's try it out. Start by entering the <code class='interactive-tutorial-command'>help</code> command into the <code class='interactive-tutorial-tab'>Terminal</code> " +
"(Don't forget to press Enter after typing the command)", "(Don't forget to press Enter after typing the command)",
@ -170,7 +162,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalLs: case iTutorialSteps.TerminalLs:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"The <code class='interactive-tutorial-command'>help</code> command displays a list of all available <code class='interactive-tutorial-tab'>Terminal</code> commands, how to use them, " + "The <code class='interactive-tutorial-command'>help</code> command displays a list of all available <code class='interactive-tutorial-tab'>Terminal</code> commands, how to use them, " +
"and a description of what they do. <br><br>Let's try another command. Enter the <code class='interactive-tutorial-command'>ls</code> command.", "and a description of what they do. <br><br>Let's try another command. Enter the <code class='interactive-tutorial-command'>ls</code> command.",
@ -178,7 +169,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalScan: case iTutorialSteps.TerminalScan:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
" <code class='interactive-tutorial-command'>ls</code> is a basic command that shows files " + " <code class='interactive-tutorial-command'>ls</code> is a basic command that shows files " +
"on the computer. Right now, it shows that you have a program called <code class='interactive-tutorial-command'>NUKE.exe</code> on your computer. " + "on the computer. Right now, it shows that you have a program called <code class='interactive-tutorial-command'>NUKE.exe</code> on your computer. " +
@ -189,7 +179,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalScanAnalyze1: case iTutorialSteps.TerminalScanAnalyze1:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"The <code class='interactive-tutorial-command'>scan</code> command shows all available network connections. In other words, " + "The <code class='interactive-tutorial-command'>scan</code> command shows all available network connections. In other words, " +
"it displays a list of all servers that can be connected to from your " + "it displays a list of all servers that can be connected to from your " +
@ -201,7 +190,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalScanAnalyze2: case iTutorialSteps.TerminalScanAnalyze2:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"You just ran <code class='interactive-tutorial-command'>scan-analyze</code> with a depth of one. This command shows more detailed " + "You just ran <code class='interactive-tutorial-command'>scan-analyze</code> with a depth of one. This command shows more detailed " +
"information about each server that you can connect to (servers that are a distance of " + "information about each server that you can connect to (servers that are a distance of " +
@ -211,7 +199,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalConnect: case iTutorialSteps.TerminalConnect:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Now you can see information about all servers that are up to two nodes away, as well " + "Now you can see information about all servers that are up to two nodes away, as well " +
"as figure out how to navigate to those servers through the network. You can only connect to " + "as figure out how to navigate to those servers through the network. You can only connect to " +
@ -222,7 +209,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalAnalyze: case iTutorialSteps.TerminalAnalyze:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"You are now connected to another machine! What can you do now? You can hack it!<br><br> In the year 2077, currency has " + "You are now connected to another machine! What can you do now? You can hack it!<br><br> In the year 2077, currency has " +
"become digital and decentralized. People and corporations store their money " + "become digital and decentralized. People and corporations store their money " +
@ -233,7 +219,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalNuke: case iTutorialSteps.TerminalNuke:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"When the <code class='interactive-tutorial-command'>analyze</code> command finishes running it will show useful information " + "When the <code class='interactive-tutorial-command'>analyze</code> command finishes running it will show useful information " +
"about hacking the server. <br><br> For this server, the required hacking skill is only <span class='character-hack-cell'>1</span>, " + "about hacking the server. <br><br> For this server, the required hacking skill is only <span class='character-hack-cell'>1</span>, " +
@ -247,7 +232,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalManualHack: case iTutorialSteps.TerminalManualHack:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"You now have root access! You can hack the server using the <code class='interactive-tutorial-command'>hack</code> command. " + "You now have root access! You can hack the server using the <code class='interactive-tutorial-command'>hack</code> command. " +
"Try doing that now.", "Try doing that now.",
@ -255,7 +239,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalHackingMechanics: case iTutorialSteps.TerminalHackingMechanics:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"You are now attempting to hack the server. Performing a hack takes time and " + "You are now attempting to hack the server. Performing a hack takes time and " +
"only has a certain percentage chance " + "only has a certain percentage chance " +
@ -270,7 +253,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block"; nextBtn.style.display = "inline-block";
break; break;
case iTutorialSteps.TerminalCreateScript: case iTutorialSteps.TerminalCreateScript:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Hacking is the core mechanic of the game and is necessary for progressing. However, " + "Hacking is the core mechanic of the game and is necessary for progressing. However, " +
"you don't want to be hacking manually the entire time. You can automate your hacking " + "you don't want to be hacking manually the entire time. You can automate your hacking " +
@ -282,7 +264,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalTypeScript: case iTutorialSteps.TerminalTypeScript:
Engine.loadScriptEditorContent("n00dles.script", "");
iTutorialSetText( iTutorialSetText(
"This is the script editor. You can use it to program your scripts. Scripts are " + "This is the script editor. You can use it to program your scripts. Scripts are " +
"written in a simplified version of javascript. Copy and paste the following code into the script editor: <br><br>" + "written in a simplified version of javascript. Copy and paste the following code into the script editor: <br><br>" +
@ -297,7 +278,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js) nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js)
break; break;
case iTutorialSteps.TerminalFree: case iTutorialSteps.TerminalFree:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Now we'll run the script. Scripts require a certain amount of RAM to run, and can be " + "Now we'll run the script. Scripts require a certain amount of RAM to run, and can be " +
"run on any machine which you have root access to. Different servers have different " + "run on any machine which you have root access to. Different servers have different " +
@ -307,7 +287,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal commmand nextBtn.style.display = "none"; // next step triggered by terminal commmand
break; break;
case iTutorialSteps.TerminalRunScript: case iTutorialSteps.TerminalRunScript:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"We have 4GB of free RAM on this machine, which is enough to run our " + "We have 4GB of free RAM on this machine, which is enough to run our " +
"script. Let's run our script using <code class='interactive-tutorial-command'>run n00dles.script</code>.", "script. Let's run our script using <code class='interactive-tutorial-command'>run n00dles.script</code>.",
@ -315,7 +294,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal commmand nextBtn.style.display = "none"; // next step triggered by terminal commmand
break; break;
case iTutorialSteps.TerminalGoToActiveScriptsPage: case iTutorialSteps.TerminalGoToActiveScriptsPage:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Your script is now running! " + "Your script is now running! " +
"It will continuously run in the background and will automatically stop if " + "It will continuously run in the background and will automatically stop if " +
@ -329,7 +307,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; nextBtn.style.display = "none";
break; break;
case iTutorialSteps.ActiveScriptsPage: case iTutorialSteps.ActiveScriptsPage:
Engine.loadActiveScriptsContent();
iTutorialSetText( iTutorialSetText(
"This page displays information about all of your scripts that are " + "This page displays information about all of your scripts that are " +
"running across every server. You can use this to gauge how well " + "running across every server. You can use this to gauge how well " +
@ -338,7 +315,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; nextBtn.style.display = "none";
break; break;
case iTutorialSteps.ActiveScriptsToTerminal: case iTutorialSteps.ActiveScriptsToTerminal:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"One last thing about scripts, each active script contains logs that detail " + "One last thing about scripts, each active script contains logs that detail " +
"what it's doing. We can check these logs using the <code class='interactive-tutorial-command'>tail</code> command. Do that " + "what it's doing. We can check these logs using the <code class='interactive-tutorial-command'>tail</code> command. Do that " +
@ -347,7 +323,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command nextBtn.style.display = "none"; // next step triggered by terminal command
break; break;
case iTutorialSteps.TerminalTailScript: case iTutorialSteps.TerminalTailScript:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"The log for this script won't show much right now (it might show nothing at all) because it " + "The log for this script won't show much right now (it might show nothing at all) because it " +
"just started running...but check back again in a few minutes! <br><br>" + "just started running...but check back again in a few minutes! <br><br>" +
@ -361,7 +336,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block"; nextBtn.style.display = "inline-block";
break; break;
case iTutorialSteps.GoToHacknetNodesPage: case iTutorialSteps.GoToHacknetNodesPage:
Engine.loadTerminalContent();
iTutorialSetText( iTutorialSetText(
"Hacking is not the only way to earn money. One other way to passively " + "Hacking is not the only way to earn money. One other way to passively " +
"earn money is by purchasing and upgrading Hacknet Nodes. Let's go to " + "earn money is by purchasing and upgrading Hacknet Nodes. Let's go to " +
@ -370,14 +344,12 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; nextBtn.style.display = "none";
break; break;
case iTutorialSteps.HacknetNodesIntroduction: case iTutorialSteps.HacknetNodesIntroduction:
Engine.loadHacknetNodesContent();
iTutorialSetText( iTutorialSetText(
"here you can purchase new Hacknet Nodes and upgrade your " + "existing ones. Let's purchase a new one now.", "here you can purchase new Hacknet Nodes and upgrade your " + "existing ones. Let's purchase a new one now.",
); );
nextBtn.style.display = "none"; // Next step triggered by purchaseHacknet() (HacknetNode.js) nextBtn.style.display = "none"; // Next step triggered by purchaseHacknet() (HacknetNode.js)
break; break;
case iTutorialSteps.HacknetNodesGoToWorldPage: case iTutorialSteps.HacknetNodesGoToWorldPage:
Engine.loadHacknetNodesContent();
iTutorialSetText( iTutorialSetText(
"You just purchased a Hacknet Node! This Hacknet Node will passively " + "You just purchased a Hacknet Node! This Hacknet Node will passively " +
"earn you money over time, both online and offline. When you get enough " + "earn you money over time, both online and offline. When you get enough " +
@ -388,7 +360,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; nextBtn.style.display = "none";
break; break;
case iTutorialSteps.WorldDescription: case iTutorialSteps.WorldDescription:
Engine.loadLocationContent();
iTutorialSetText( iTutorialSetText(
"This page lists all of the different locations you can currently " + "This page lists all of the different locations you can currently " +
"travel to. Each location has something that you can do. " + "travel to. Each location has something that you can do. " +
@ -399,7 +370,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; nextBtn.style.display = "none";
break; break;
case iTutorialSteps.TutorialPageInfo: case iTutorialSteps.TutorialPageInfo:
Engine.loadTutorialContent();
iTutorialSetText( iTutorialSetText(
"This page contains a lot of different documentation about the game's " + "This page contains a lot of different documentation about the game's " +
"content and mechanics. <strong style='background-color:#444;'> I know it's a lot, but I highly suggest you read " + "content and mechanics. <strong style='background-color:#444;'> I know it's a lot, but I highly suggest you read " +

@ -3,418 +3,347 @@
* *
* This subcomponent renders all of the buttons for applying to jobs at a company * This subcomponent renders all of the buttons for applying to jobs at a company
*/ */
import * as React from "react"; import React, { useState } from "react";
import { ApplyToJobButton } from "./ApplyToJobButton"; import { ApplyToJobButton } from "./ApplyToJobButton";
import { Location } from "../Location";
import { Locations } from "../Locations"; import { Locations } from "../Locations";
import { LocationName } from "../data/LocationNames"; import { LocationName } from "../data/LocationNames";
import { IEngine } from "../../IEngine";
import { Companies } from "../../Company/Companies"; import { Companies } from "../../Company/Companies";
import { Company } from "../../Company/Company";
import { CompanyPosition } from "../../Company/CompanyPosition"; import { CompanyPosition } from "../../Company/CompanyPosition";
import { CompanyPositions } from "../../Company/CompanyPositions"; import { CompanyPositions } from "../../Company/CompanyPositions";
import * as posNames from "../../Company/data/companypositionnames"; import * as posNames from "../../Company/data/companypositionnames";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor"; import { Favor } from "../../ui/React/Favor";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context";
import { QuitJobPopup } from "../../Company/ui/QuitJobPopup"; import { QuitJobPopup } from "../../Company/ui/QuitJobPopup";
type IProps = { type IProps = {
engine: IEngine;
locName: LocationName; locName: LocationName;
p: IPlayer;
}; };
type IState = { export function CompanyLocation(props: IProps): React.ReactElement {
employedHere: boolean; const p = use.Player();
}; const router = use.Router();
const setRerender = useState(false)[1];
const blockStyleMarkup = { function rerender(): void {
display: "block", setRerender((old) => !old);
}; }
export class CompanyLocation extends React.Component<IProps, IState> {
/** /**
* We'll keep a reference to the Company that this component is being rendered for, * We'll keep a reference to the Company that this component is being rendered for,
* so we don't have to look it up every time * so we don't have to look it up every time
*/ */
company: Company; const company = Companies[props.locName];
if (company == null) throw new Error(`CompanyLocation component constructed with invalid company: ${props.locName}`);
/**
* Reference to the Location that this component is being rendered for
*/
const location = Locations[props.locName];
if (location == null) {
throw new Error(`CompanyLocation component constructed with invalid location: ${props.locName}`);
}
/**
* Name of company position that player holds, if applicable
*/
const jobTitle = p.jobs[props.locName] ? p.jobs[props.locName] : null;
/** /**
* CompanyPosition object for the job that the player holds at this company * CompanyPosition object for the job that the player holds at this company
* (if he has one) * (if he has one)
*/ */
companyPosition: CompanyPosition | null = null; const companyPosition = jobTitle ? CompanyPositions[jobTitle] : null;
/** p.location = props.locName;
* Stores button styling that sets them all to block display
*/
btnStyle: any;
/** function applyForAgentJob(e: React.MouseEvent<HTMLElement>): void {
* Reference to the Location that this component is being rendered for
*/
location: Location;
/**
* Name of company position that player holds, if applicable
*/
jobTitle: string | null = null;
constructor(props: IProps) {
super(props);
this.btnStyle = { display: "block" };
this.quit = this.quit.bind(this);
this.applyForAgentJob = this.applyForAgentJob.bind(this);
this.applyForBusinessConsultantJob = this.applyForBusinessConsultantJob.bind(this);
this.applyForBusinessJob = this.applyForBusinessJob.bind(this);
this.applyForEmployeeJob = this.applyForEmployeeJob.bind(this);
this.applyForItJob = this.applyForItJob.bind(this);
this.applyForPartTimeEmployeeJob = this.applyForPartTimeEmployeeJob.bind(this);
this.applyForPartTimeWaiterJob = this.applyForPartTimeWaiterJob.bind(this);
this.applyForSecurityJob = this.applyForSecurityJob.bind(this);
this.applyForSoftwareConsultantJob = this.applyForSoftwareConsultantJob.bind(this);
this.applyForSoftwareJob = this.applyForSoftwareJob.bind(this);
this.applyForWaiterJob = this.applyForWaiterJob.bind(this);
this.startInfiltration = this.startInfiltration.bind(this);
this.work = this.work.bind(this);
this.location = Locations[props.locName];
if (this.location == null) {
throw new Error(`CompanyLocation component constructed with invalid location: ${props.locName}`);
}
this.company = Companies[props.locName];
if (this.company == null) {
throw new Error(`CompanyLocation component constructed with invalid company: ${props.locName}`);
}
this.state = {
employedHere: false,
};
this.props.p.location = props.locName;
this.checkIfEmployedHere(false);
}
applyForAgentJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForAgentJob(); p.applyForAgentJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForBusinessConsultantJob(e: React.MouseEvent<HTMLElement>): void { function applyForBusinessConsultantJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForBusinessConsultantJob(); p.applyForBusinessConsultantJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForBusinessJob(e: React.MouseEvent<HTMLElement>): void { function applyForBusinessJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForBusinessJob(); p.applyForBusinessJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForEmployeeJob(e: React.MouseEvent<HTMLElement>): void { function applyForEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForEmployeeJob(); p.applyForEmployeeJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForItJob(e: React.MouseEvent<HTMLElement>): void { function applyForItJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForItJob(); p.applyForItJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForPartTimeEmployeeJob(e: React.MouseEvent<HTMLElement>): void { function applyForPartTimeEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForPartTimeEmployeeJob(); p.applyForPartTimeEmployeeJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForPartTimeWaiterJob(e: React.MouseEvent<HTMLElement>): void { function applyForPartTimeWaiterJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForPartTimeWaiterJob(); p.applyForPartTimeWaiterJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForSecurityJob(e: React.MouseEvent<HTMLElement>): void { function applyForSecurityJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForSecurityJob(); p.applyForSecurityJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForSoftwareConsultantJob(e: React.MouseEvent<HTMLElement>): void { function applyForSoftwareConsultantJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForSoftwareConsultantJob(); p.applyForSoftwareConsultantJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForSoftwareJob(e: React.MouseEvent<HTMLElement>): void { function applyForSoftwareJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForSoftwareJob(); p.applyForSoftwareJob();
this.checkIfEmployedHere(true); rerender();
} }
applyForWaiterJob(e: React.MouseEvent<HTMLElement>): void { function applyForWaiterJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
this.props.p.applyForWaiterJob(); p.applyForWaiterJob();
this.checkIfEmployedHere(true); rerender();
} }
checkIfEmployedHere(updateState = false): void { function startInfiltration(e: React.MouseEvent<HTMLElement>): void {
this.jobTitle = this.props.p.jobs[this.props.locName];
if (this.jobTitle != null) {
this.companyPosition = CompanyPositions[this.jobTitle];
}
if (updateState) {
this.setState({
employedHere: this.jobTitle != null,
});
}
}
startInfiltration(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
const loc = this.location; const loc = location;
if (!loc.infiltrationData) { if (!loc.infiltrationData)
console.error(`trying to start infiltration at ${this.props.locName} but the infiltrationData is null`); throw new Error(`trying to start infiltration at ${props.locName} but the infiltrationData is null`);
return;
}
this.props.engine.loadInfiltrationContent( router.toInfiltration(props.locName);
this.props.locName,
loc.infiltrationData.startingSecurityLevel,
loc.infiltrationData.maxClearanceLevel,
);
const data = loc.infiltrationData;
if (data == null) {
return;
}
} }
work(e: React.MouseEvent<HTMLElement>): void { function work(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
const pos = this.companyPosition; const pos = companyPosition;
if (pos instanceof CompanyPosition) { if (pos instanceof CompanyPosition) {
if (pos.isPartTimeJob() || pos.isSoftwareConsultantJob() || pos.isBusinessConsultantJob()) { if (pos.isPartTimeJob() || pos.isSoftwareConsultantJob() || pos.isBusinessConsultantJob()) {
this.props.p.startWorkPartTime(this.props.locName); p.startWorkPartTime(props.locName);
} else { } else {
this.props.p.startWork(this.props.locName); p.startWork(props.locName);
} }
router.toWork();
} }
} }
quit(e: React.MouseEvent<HTMLElement>): void { function quit(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) return; if (!e.isTrusted) return;
const popupId = `quit-job-popup`; const popupId = `quit-job-popup`;
createPopup(popupId, QuitJobPopup, { createPopup(popupId, QuitJobPopup, {
locName: this.props.locName, locName: props.locName,
company: this.company, company: company,
player: this.props.p, player: p,
onQuit: () => this.checkIfEmployedHere(true), onQuit: rerender,
popupId: popupId, popupId: popupId,
}); });
} }
render(): React.ReactNode { const isEmployedHere = jobTitle != null;
const isEmployedHere = this.jobTitle != null; const favorGain = company.getFavorGain();
const favorGain = this.company.getFavorGain();
return ( return (
<div> <div>
{isEmployedHere && ( {isEmployedHere && (
<div> <div>
<p>Job Title: {this.jobTitle}</p> <p>Job Title: {jobTitle}</p>
<br /> <br />
<p style={blockStyleMarkup}>-------------------------</p> <p style={{ display: "block" }}>-------------------------</p>
<br /> <br />
<p className={"tooltip"}> <p className={"tooltip"}>
Company reputation: {Reputation(this.company.playerReputation)} Company reputation: {Reputation(company.playerReputation)}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
You will earn {Favor(favorGain[0])} company favor upon resetting after installing Augmentations You will earn {Favor(favorGain[0])} company favor upon resetting after installing Augmentations
</span> </span>
</p> </p>
<br /> <br />
<br /> <br />
<p style={blockStyleMarkup}>-------------------------</p> <p style={{ display: "block" }}>-------------------------</p>
<br /> <br />
<p className={"tooltip"}> <p className={"tooltip"}>
Company Favor: {Favor(this.company.favor)} Company Favor: {Favor(company.favor)}
<span className={"tooltiptext"}> <span className={"tooltiptext"}>
Company favor increases the rate at which you earn reputation for this company by 1% per favor. Company Company favor increases the rate at which you earn reputation for this company by 1% per favor. Company
favor is gained whenever you reset after installing Augmentations. The amount of favor you gain depends favor is gained whenever you reset after installing Augmentations. The amount of favor you gain depends on
on how much reputation you have with the comapny. how much reputation you have with the comapny.
</span> </span>
</p> </p>
<br /> <br />
<br /> <br />
<p style={blockStyleMarkup}>-------------------------</p> <p style={{ display: "block" }}>-------------------------</p>
<br /> <br />
<StdButton onClick={this.work} text={"Work"} /> <StdButton onClick={work} text={"Work"} />
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;
<StdButton onClick={this.quit} text={"Quit"} /> <StdButton onClick={quit} text={"Quit"} />
</div> </div>
)} )}
{this.company.hasAgentPositions() && ( {company.hasAgentPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.AgentCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.AgentCompanyPositions[0]]}
onClick={this.applyForAgentJob} onClick={applyForAgentJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply for Agent Job"} text={"Apply for Agent Job"}
/> />
)} )}
{this.company.hasBusinessConsultantPositions() && ( {company.hasBusinessConsultantPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]]}
onClick={this.applyForBusinessConsultantJob} onClick={applyForBusinessConsultantJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply for Business Consultant Job"} text={"Apply for Business Consultant Job"}
/> />
)} )}
{this.company.hasBusinessPositions() && ( {company.hasBusinessPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.BusinessCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.BusinessCompanyPositions[0]]}
onClick={this.applyForBusinessJob} onClick={applyForBusinessJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply for Business Job"} text={"Apply for Business Job"}
/> />
)} )}
{this.company.hasEmployeePositions() && ( {company.hasEmployeePositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[1]]} entryPosType={CompanyPositions[posNames.MiscCompanyPositions[1]]}
onClick={this.applyForEmployeeJob} onClick={applyForEmployeeJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply to be an Employee"} text={"Apply to be an Employee"}
/> />
)} )}
{this.company.hasEmployeePositions() && ( {company.hasEmployeePositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[1]]} entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[1]]}
onClick={this.applyForPartTimeEmployeeJob} onClick={applyForPartTimeEmployeeJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply to be a part-time Employee"} text={"Apply to be a part-time Employee"}
/> />
)} )}
{this.company.hasITPositions() && ( {company.hasITPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.ITCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.ITCompanyPositions[0]]}
onClick={this.applyForItJob} onClick={applyForItJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply for IT Job"} text={"Apply for IT Job"}
/> />
)} )}
{this.company.hasSecurityPositions() && ( {company.hasSecurityPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.SecurityCompanyPositions[2]]} entryPosType={CompanyPositions[posNames.SecurityCompanyPositions[2]]}
onClick={this.applyForSecurityJob} onClick={applyForSecurityJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply for Security Job"} text={"Apply for Security Job"}
/> />
)} )}
{this.company.hasSoftwareConsultantPositions() && ( {company.hasSoftwareConsultantPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]]}
onClick={this.applyForSoftwareConsultantJob} onClick={applyForSoftwareConsultantJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply for Software Consultant Job"} text={"Apply for Software Consultant Job"}
/> />
)} )}
{this.company.hasSoftwarePositions() && ( {company.hasSoftwarePositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.SoftwareCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.SoftwareCompanyPositions[0]]}
onClick={this.applyForSoftwareJob} onClick={applyForSoftwareJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply for Software Job"} text={"Apply for Software Job"}
/> />
)} )}
{this.company.hasWaiterPositions() && ( {company.hasWaiterPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.MiscCompanyPositions[0]]}
onClick={this.applyForWaiterJob} onClick={applyForWaiterJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply to be a Waiter"} text={"Apply to be a Waiter"}
/> />
)} )}
{this.company.hasWaiterPositions() && ( {company.hasWaiterPositions() && (
<ApplyToJobButton <ApplyToJobButton
company={this.company} company={company}
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[0]]} entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[0]]}
onClick={this.applyForPartTimeWaiterJob} onClick={applyForPartTimeWaiterJob}
p={this.props.p} p={p}
style={this.btnStyle} style={{ display: "block" }}
text={"Apply to be a part-time Waiter"} text={"Apply to be a part-time Waiter"}
/> />
)} )}
{this.location.infiltrationData != null && ( {location.infiltrationData != null && (
<StdButton onClick={this.startInfiltration} style={this.btnStyle} text={"Infiltrate Company"} /> <StdButton onClick={startInfiltration} style={{ display: "block" }} text={"Infiltrate Company"} />
)} )}
<br /> <br />
<br /> <br />
<br /> <br />
<br /> <br />
<br /> <br />
</div> </div>
); );
}
} }

@ -12,7 +12,7 @@ import { HospitalLocation } from "./HospitalLocation";
import { SlumsLocation } from "./SlumsLocation"; import { SlumsLocation } from "./SlumsLocation";
import { SpecialLocation } from "./SpecialLocation"; import { SpecialLocation } from "./SpecialLocation";
import { TechVendorLocation } from "./TechVendorLocation"; import { TechVendorLocation } from "./TechVendorLocation";
import { TravelAgencyLocation } from "./TravelAgencyLocation"; import { TravelAgencyRoot } from "./TravelAgencyRoot";
import { UniversityLocation } from "./UniversityLocation"; import { UniversityLocation } from "./UniversityLocation";
import { CasinoLocation } from "./CasinoLocation"; import { CasinoLocation } from "./CasinoLocation";
@ -21,6 +21,7 @@ import { LocationType } from "../LocationTypeEnum";
import { CityName } from "../data/CityNames"; import { CityName } from "../data/CityNames";
import { IEngine } from "../../IEngine"; import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
@ -32,6 +33,7 @@ import { CorruptableText } from "../../ui/React/CorruptableText";
type IProps = { type IProps = {
engine: IEngine; engine: IEngine;
router: IRouter;
loc: Location; loc: Location;
p: IPlayer; p: IPlayer;
returnToCity: () => void; returnToCity: () => void;
@ -58,14 +60,7 @@ export class GenericLocation extends React.Component<IProps, any> {
const content: React.ReactNode[] = []; const content: React.ReactNode[] = [];
if (this.props.loc.types.includes(LocationType.Company)) { if (this.props.loc.types.includes(LocationType.Company)) {
content.push( content.push(<CompanyLocation key={"companylocation"} locName={this.props.loc.name} />);
<CompanyLocation
engine={this.props.engine}
key={"companylocation"}
locName={this.props.loc.name}
p={this.props.p}
/>,
);
} }
if (this.props.loc.types.includes(LocationType.Gym)) { if (this.props.loc.types.includes(LocationType.Gym)) {
@ -77,7 +72,7 @@ export class GenericLocation extends React.Component<IProps, any> {
} }
if (this.props.loc.types.includes(LocationType.Slums)) { if (this.props.loc.types.includes(LocationType.Slums)) {
content.push(<SlumsLocation key={"slumslocation"} p={this.props.p} />); content.push(<SlumsLocation key={"slumslocation"} />);
} }
if (this.props.loc.types.includes(LocationType.Special)) { if (this.props.loc.types.includes(LocationType.Special)) {
@ -91,11 +86,11 @@ export class GenericLocation extends React.Component<IProps, any> {
} }
if (this.props.loc.types.includes(LocationType.TravelAgency)) { if (this.props.loc.types.includes(LocationType.TravelAgency)) {
content.push(<TravelAgencyLocation key={"travelagencylocation"} p={this.props.p} travel={this.props.travel} />); content.push(<TravelAgencyRoot key={"travelagencylocation"} p={this.props.p} router={this.props.router} />);
} }
if (this.props.loc.types.includes(LocationType.University)) { if (this.props.loc.types.includes(LocationType.University)) {
content.push(<UniversityLocation key={"universitylocation"} loc={this.props.loc} p={this.props.p} />); content.push(<UniversityLocation key={"universitylocation"} loc={this.props.loc} />);
} }
if (this.props.loc.types.includes(LocationType.Casino)) { if (this.props.loc.types.includes(LocationType.Casino)) {

@ -15,6 +15,7 @@ import { LocationName } from "../data/LocationNames";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IEngine } from "../../IEngine"; import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
@ -22,6 +23,7 @@ import { dialogBoxCreate } from "../../../utils/DialogBox";
type IProps = { type IProps = {
initiallyInCity?: boolean; initiallyInCity?: boolean;
engine: IEngine; engine: IEngine;
router: IRouter;
p: IPlayer; p: IPlayer;
}; };
@ -47,6 +49,10 @@ export class LocationRoot extends React.Component<IProps, IState> {
} }
enterLocation(to: LocationName): void { enterLocation(to: LocationName): void {
if (to == LocationName.TravelAgency) {
this.props.router.toTravel();
return;
}
this.props.p.gotoLocation(to); this.props.p.gotoLocation(to);
this.setState({ this.setState({
inCity: false, inCity: false,
@ -98,6 +104,7 @@ export class LocationRoot extends React.Component<IProps, IState> {
return ( return (
<GenericLocation <GenericLocation
engine={this.props.engine} engine={this.props.engine}
router={this.props.router}
loc={loc} loc={loc}
p={this.props.p} p={this.props.p}
returnToCity={this.returnToCity} returnToCity={this.returnToCity}

@ -6,225 +6,209 @@
import * as React from "react"; import * as React from "react";
import { Crimes } from "../../Crime/Crimes"; import { Crimes } from "../../Crime/Crimes";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton"; import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
import { use } from "../../ui/Context";
type IProps = { export function SlumsLocation(): React.ReactElement {
p: IPlayer; const player = use.Player();
}; const router = use.Router();
function shoplift(e: React.MouseEvent<HTMLElement>): void {
export class SlumsLocation extends React.Component<IProps, any> {
/**
* Stores button styling that sets them all to block display
*/
btnStyle: any;
constructor(props: IProps) {
super(props);
this.btnStyle = { display: "block" };
this.shoplift = this.shoplift.bind(this);
this.robStore = this.robStore.bind(this);
this.mug = this.mug.bind(this);
this.larceny = this.larceny.bind(this);
this.dealDrugs = this.dealDrugs.bind(this);
this.bondForgery = this.bondForgery.bind(this);
this.traffickArms = this.traffickArms.bind(this);
this.homicide = this.homicide.bind(this);
this.grandTheftAuto = this.grandTheftAuto.bind(this);
this.kidnap = this.kidnap.bind(this);
this.assassinate = this.assassinate.bind(this);
this.heist = this.heist.bind(this);
}
shoplift(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Shoplift.commit(this.props.p); Crimes.Shoplift.commit(player);
router.toWork();
} }
robStore(e: React.MouseEvent<HTMLElement>): void { function robStore(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.RobStore.commit(this.props.p); Crimes.RobStore.commit(player);
router.toWork();
} }
mug(e: React.MouseEvent<HTMLElement>): void { function mug(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Mug.commit(this.props.p); Crimes.Mug.commit(player);
router.toWork();
} }
larceny(e: React.MouseEvent<HTMLElement>): void { function larceny(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Larceny.commit(this.props.p); Crimes.Larceny.commit(player);
router.toWork();
} }
dealDrugs(e: React.MouseEvent<HTMLElement>): void { function dealDrugs(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.DealDrugs.commit(this.props.p); Crimes.DealDrugs.commit(player);
router.toWork();
} }
bondForgery(e: React.MouseEvent<HTMLElement>): void { function bondForgery(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.BondForgery.commit(this.props.p); Crimes.BondForgery.commit(player);
router.toWork();
} }
traffickArms(e: React.MouseEvent<HTMLElement>): void { function traffickArms(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.TraffickArms.commit(this.props.p); Crimes.TraffickArms.commit(player);
router.toWork();
} }
homicide(e: React.MouseEvent<HTMLElement>): void { function homicide(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Homicide.commit(this.props.p); Crimes.Homicide.commit(player);
router.toWork();
} }
grandTheftAuto(e: React.MouseEvent<HTMLElement>): void { function grandTheftAuto(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.GrandTheftAuto.commit(this.props.p); Crimes.GrandTheftAuto.commit(player);
router.toWork();
} }
kidnap(e: React.MouseEvent<HTMLElement>): void { function kidnap(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Kidnap.commit(this.props.p); Crimes.Kidnap.commit(player);
router.toWork();
} }
assassinate(e: React.MouseEvent<HTMLElement>): void { function assassinate(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Assassination.commit(this.props.p); Crimes.Assassination.commit(player);
router.toWork();
} }
heist(e: React.MouseEvent<HTMLElement>): void { function heist(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Heist.commit(this.props.p); Crimes.Heist.commit(player);
router.toWork();
} }
render(): React.ReactNode { const shopliftChance = Crimes.Shoplift.successRate(player);
const shopliftChance = Crimes.Shoplift.successRate(this.props.p); const robStoreChance = Crimes.RobStore.successRate(player);
const robStoreChance = Crimes.RobStore.successRate(this.props.p); const mugChance = Crimes.Mug.successRate(player);
const mugChance = Crimes.Mug.successRate(this.props.p); const larcenyChance = Crimes.Larceny.successRate(player);
const larcenyChance = Crimes.Larceny.successRate(this.props.p); const drugsChance = Crimes.DealDrugs.successRate(player);
const drugsChance = Crimes.DealDrugs.successRate(this.props.p); const bondChance = Crimes.BondForgery.successRate(player);
const bondChance = Crimes.BondForgery.successRate(this.props.p); const armsChance = Crimes.TraffickArms.successRate(player);
const armsChance = Crimes.TraffickArms.successRate(this.props.p); const homicideChance = Crimes.Homicide.successRate(player);
const homicideChance = Crimes.Homicide.successRate(this.props.p); const gtaChance = Crimes.GrandTheftAuto.successRate(player);
const gtaChance = Crimes.GrandTheftAuto.successRate(this.props.p); const kidnapChance = Crimes.Kidnap.successRate(player);
const kidnapChance = Crimes.Kidnap.successRate(this.props.p); const assassinateChance = Crimes.Assassination.successRate(player);
const assassinateChance = Crimes.Assassination.successRate(this.props.p); const heistChance = Crimes.Heist.successRate(player);
const heistChance = Crimes.Heist.successRate(this.props.p);
return ( return (
<div> <div>
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.shoplift} onClick={shoplift}
style={this.btnStyle} style={{ display: "block" }}
text={`Shoplift (${numeralWrapper.formatPercentage(shopliftChance)} chance of success)`} text={`Shoplift (${numeralWrapper.formatPercentage(shopliftChance)} chance of success)`}
tooltip={"Attempt to shoplift from a low-end retailer"} tooltip={"Attempt to shoplift from a low-end retailer"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.robStore} onClick={robStore}
style={this.btnStyle} style={{ display: "block" }}
text={`Rob store (${numeralWrapper.formatPercentage(robStoreChance)} chance of success)`} text={`Rob store (${numeralWrapper.formatPercentage(robStoreChance)} chance of success)`}
tooltip={"Attempt to commit armed robbery on a high-end store"} tooltip={"Attempt to commit armed robbery on a high-end store"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.mug} onClick={mug}
style={this.btnStyle} style={{ display: "block" }}
text={`Mug someone (${numeralWrapper.formatPercentage(mugChance)} chance of success)`} text={`Mug someone (${numeralWrapper.formatPercentage(mugChance)} chance of success)`}
tooltip={"Attempt to mug a random person on the street"} tooltip={"Attempt to mug a random person on the street"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.larceny} onClick={larceny}
style={this.btnStyle} style={{ display: "block" }}
text={`Larceny (${numeralWrapper.formatPercentage(larcenyChance)} chance of success)`} text={`Larceny (${numeralWrapper.formatPercentage(larcenyChance)} chance of success)`}
tooltip={"Attempt to rob property from someone's house"} tooltip={"Attempt to rob property from someone's house"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.dealDrugs} onClick={dealDrugs}
style={this.btnStyle} style={{ display: "block" }}
text={`Deal Drugs (${numeralWrapper.formatPercentage(drugsChance)} chance of success)`} text={`Deal Drugs (${numeralWrapper.formatPercentage(drugsChance)} chance of success)`}
tooltip={"Attempt to deal drugs"} tooltip={"Attempt to deal drugs"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.bondForgery} onClick={bondForgery}
style={this.btnStyle} style={{ display: "block" }}
text={`Bond Forgery (${numeralWrapper.formatPercentage(bondChance)} chance of success)`} text={`Bond Forgery (${numeralWrapper.formatPercentage(bondChance)} chance of success)`}
tooltip={"Attempt to forge corporate bonds"} tooltip={"Attempt to forge corporate bonds"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.traffickArms} onClick={traffickArms}
style={this.btnStyle} style={{ display: "block" }}
text={`Traffick illegal Arms (${numeralWrapper.formatPercentage(armsChance)} chance of success)`} text={`Traffick illegal Arms (${numeralWrapper.formatPercentage(armsChance)} chance of success)`}
tooltip={"Attempt to smuggle illegal arms into the city"} tooltip={"Attempt to smuggle illegal arms into the city"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.homicide} onClick={homicide}
style={this.btnStyle} style={{ display: "block" }}
text={`Homicide (${numeralWrapper.formatPercentage(homicideChance)} chance of success)`} text={`Homicide (${numeralWrapper.formatPercentage(homicideChance)} chance of success)`}
tooltip={"Attempt to murder a random person on the street"} tooltip={"Attempt to murder a random person on the street"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.grandTheftAuto} onClick={grandTheftAuto}
style={this.btnStyle} style={{ display: "block" }}
text={`Grand theft Auto (${numeralWrapper.formatPercentage(gtaChance)} chance of success)`} text={`Grand theft Auto (${numeralWrapper.formatPercentage(gtaChance)} chance of success)`}
tooltip={"Attempt to commit grand theft auto"} tooltip={"Attempt to commit grand theft auto"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.kidnap} onClick={kidnap}
style={this.btnStyle} style={{ display: "block" }}
text={`Kidnap and Ransom (${numeralWrapper.formatPercentage(kidnapChance)} chance of success)`} text={`Kidnap and Ransom (${numeralWrapper.formatPercentage(kidnapChance)} chance of success)`}
tooltip={"Attempt to kidnap and ransom a high-profile-target"} tooltip={"Attempt to kidnap and ransom a high-profile-target"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.assassinate} onClick={assassinate}
style={this.btnStyle} style={{ display: "block" }}
text={`Assassinate (${numeralWrapper.formatPercentage(assassinateChance)} chance of success)`} text={`Assassinate (${numeralWrapper.formatPercentage(assassinateChance)} chance of success)`}
tooltip={"Attempt to assassinate a high-profile target"} tooltip={"Attempt to assassinate a high-profile target"}
/> />
<AutoupdatingStdButton <AutoupdatingStdButton
intervalTime={5e3} intervalTime={5e3}
onClick={this.heist} onClick={heist}
style={this.btnStyle} style={{ display: "block" }}
text={`Heist (${numeralWrapper.formatPercentage(heistChance)} chance of success)`} text={`Heist (${numeralWrapper.formatPercentage(heistChance)} chance of success)`}
tooltip={"Attempt to pull off the ultimate heist"} tooltip={"Attempt to pull off the ultimate heist"}
/> />
</div> </div>
); );
}
} }

@ -10,28 +10,43 @@ import { TravelConfirmationPopup } from "./TravelConfirmationPopup";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { WorldMap } from "../../ui/React/WorldMap"; import { WorldMap } from "../../ui/React/WorldMap";
import { dialogBoxCreate } from "../../../utils/DialogBox";
type IProps = { type IProps = {
p: IPlayer; p: IPlayer;
travel: (to: CityName) => void; router: IRouter;
}; };
function createTravelPopup(p: IPlayer, city: string, travel: () => void): void { function travel(p: IPlayer, router: IRouter, to: CityName): void {
const cost = CONSTANTS.TravelCost;
if (!p.canAfford(cost)) {
dialogBoxCreate(`You cannot afford to travel to ${to}`);
return;
}
p.loseMoney(cost);
p.travel(to);
dialogBoxCreate(<span className="noselect">You are now in {to}!</span>);
router.toCity();
}
function createTravelPopup(p: IPlayer, router: IRouter, city: CityName): void {
if (Settings.SuppressTravelConfirmation) { if (Settings.SuppressTravelConfirmation) {
travel(); travel(p, router, city);
return; return;
} }
const popupId = `travel-confirmation`; const popupId = `travel-confirmation`;
createPopup(popupId, TravelConfirmationPopup, { createPopup(popupId, TravelConfirmationPopup, {
player: p, player: p,
city: city, city: city,
travel: travel, travel: () => travel(p, router, city),
popupId: popupId, popupId: popupId,
}); });
} }
@ -45,7 +60,7 @@ function ASCIIWorldMap(props: IProps): React.ReactElement {
</p> </p>
<WorldMap <WorldMap
currentCity={props.p.city} currentCity={props.p.city}
onTravel={(city: CityName) => createTravelPopup(props.p, city, () => props.travel(city))} onTravel={(city: CityName) => createTravelPopup(props.p, props.router, city)}
/> />
</div> </div>
); );
@ -66,7 +81,7 @@ function ListWorldMap(props: IProps): React.ReactElement {
return ( return (
<StdButton <StdButton
key={city} key={city}
onClick={() => createTravelPopup(props.p, city, () => props.travel(match[1]))} onClick={() => createTravelPopup(props.p, props.router, city as CityName)}
style={{ display: "block" }} style={{ display: "block" }}
text={`Travel to ${city}`} text={`Travel to ${city}`}
/> />
@ -76,10 +91,15 @@ function ListWorldMap(props: IProps): React.ReactElement {
); );
} }
export function TravelAgencyLocation(props: IProps): React.ReactElement { export function TravelAgencyRoot(props: IProps): React.ReactElement {
if (Settings.DisableASCIIArt) { return (
return <ListWorldMap p={props.p} travel={props.travel} />; <>
} else { <h1>Travel Agency</h1>
return <ASCIIWorldMap p={props.p} travel={props.travel} />; {Settings.DisableASCIIArt ? (
} <ListWorldMap p={props.p} router={props.router} />
) : (
<ASCIIWorldMap p={props.p} router={props.router} />
)}
</>
);
} }

@ -8,154 +8,134 @@ import * as React from "react";
import { Location } from "../Location"; import { Location } from "../Location";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { getServer } from "../../Server/ServerHelpers"; import { getServer } from "../../Server/ServerHelpers";
import { Server } from "../../Server/Server"; import { Server } from "../../Server/Server";
import { SpecialServerIps } from "../../Server/SpecialServerIps"; import { SpecialServerIps } from "../../Server/SpecialServerIps";
import { StdButton } from "../../ui/React/StdButton"; import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { use } from "../../ui/Context";
type IProps = { type IProps = {
loc: Location; loc: Location;
p: IPlayer;
}; };
export class UniversityLocation extends React.Component<IProps, any> { export function UniversityLocation(props: IProps): React.ReactElement {
/** const player = use.Player();
* Stores button styling that sets them all to block display const router = use.Router();
*/
btnStyle: any;
constructor(props: IProps) { function calculateCost(): number {
super(props); const ip = SpecialServerIps.getIp(props.loc.name);
this.btnStyle = { display: "block" };
this.take = this.take.bind(this);
this.study = this.study.bind(this);
this.dataStructures = this.dataStructures.bind(this);
this.networks = this.networks.bind(this);
this.algorithms = this.algorithms.bind(this);
this.management = this.management.bind(this);
this.leadership = this.leadership.bind(this);
this.calculateCost = this.calculateCost.bind(this);
}
calculateCost(): number {
const ip = SpecialServerIps.getIp(this.props.loc.name);
const server = getServer(ip); const server = getServer(ip);
if (server == null || !server.hasOwnProperty("backdoorInstalled")) return this.props.loc.costMult; if (server == null || !server.hasOwnProperty("backdoorInstalled")) return props.loc.costMult;
const discount = (server as Server).backdoorInstalled ? 0.9 : 1; const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
return this.props.loc.costMult * discount; return props.loc.costMult * discount;
} }
take(stat: string): void { function take(stat: string): void {
const loc = this.props.loc; const loc = props.loc;
this.props.p.startClass(this.calculateCost(), loc.expMult, stat); player.startClass(calculateCost(), loc.expMult, stat);
router.toWork();
} }
study(): void { function study(): void {
this.take(CONSTANTS.ClassStudyComputerScience); take(CONSTANTS.ClassStudyComputerScience);
} }
dataStructures(): void { function dataStructures(): void {
this.take(CONSTANTS.ClassDataStructures); take(CONSTANTS.ClassDataStructures);
} }
networks(): void { function networks(): void {
this.take(CONSTANTS.ClassNetworks); take(CONSTANTS.ClassNetworks);
} }
algorithms(): void { function algorithms(): void {
this.take(CONSTANTS.ClassAlgorithms); take(CONSTANTS.ClassAlgorithms);
} }
management(): void { function management(): void {
this.take(CONSTANTS.ClassManagement); take(CONSTANTS.ClassManagement);
} }
leadership(): void { function leadership(): void {
this.take(CONSTANTS.ClassLeadership); take(CONSTANTS.ClassLeadership);
} }
render(): React.ReactNode { const costMult: number = calculateCost();
const costMult: number = this.calculateCost();
const dataStructuresCost = CONSTANTS.ClassDataStructuresBaseCost * costMult; const dataStructuresCost = CONSTANTS.ClassDataStructuresBaseCost * costMult;
const networksCost = CONSTANTS.ClassNetworksBaseCost * costMult; const networksCost = CONSTANTS.ClassNetworksBaseCost * costMult;
const algorithmsCost = CONSTANTS.ClassAlgorithmsBaseCost * costMult; const algorithmsCost = CONSTANTS.ClassAlgorithmsBaseCost * costMult;
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult; const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult; const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
const earnHackingExpTooltip = `Gain hacking experience!`; const earnHackingExpTooltip = `Gain hacking experience!`;
const earnCharismaExpTooltip = `Gain charisma experience!`; const earnCharismaExpTooltip = `Gain charisma experience!`;
return ( return (
<div> <div>
<StdButton <StdButton
onClick={this.study} onClick={study}
style={this.btnStyle} style={{ display: "block" }}
text={`Study Computer Science (free)`} text={`Study Computer Science (free)`}
tooltip={earnHackingExpTooltip} tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.dataStructures} onClick={dataStructures}
style={this.btnStyle} style={{ display: "block" }}
text={ text={
<> <>
Take Data Structures course ( Take Data Structures course (
<Money money={dataStructuresCost} player={this.props.p} /> / sec) <Money money={dataStructuresCost} player={player} /> / sec)
</> </>
} }
tooltip={earnHackingExpTooltip} tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.networks} onClick={networks}
style={this.btnStyle} style={{ display: "block" }}
text={ text={
<> <>
Take Networks course ( Take Networks course (
<Money money={networksCost} player={this.props.p} /> / sec) <Money money={networksCost} player={player} /> / sec)
</> </>
} }
tooltip={earnHackingExpTooltip} tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.algorithms} onClick={algorithms}
style={this.btnStyle} style={{ display: "block" }}
text={ text={
<> <>
Take Algorithms course ( Take Algorithms course (
<Money money={algorithmsCost} player={this.props.p} /> / sec) <Money money={algorithmsCost} player={player} /> / sec)
</> </>
} }
tooltip={earnHackingExpTooltip} tooltip={earnHackingExpTooltip}
/> />
<StdButton <StdButton
onClick={this.management} onClick={management}
style={this.btnStyle} style={{ display: "block" }}
text={ text={
<> <>
Take Management course ( Take Management course (
<Money money={managementCost} player={this.props.p} /> / sec) <Money money={managementCost} player={player} /> / sec)
</> </>
} }
tooltip={earnCharismaExpTooltip} tooltip={earnCharismaExpTooltip}
/> />
<StdButton <StdButton
onClick={this.leadership} onClick={leadership}
style={this.btnStyle} style={{ display: "block" }}
text={ text={
<> <>
Take Leadership course ( Take Leadership course (
<Money money={leadershipCost} player={this.props.p} /> / sec) <Money money={leadershipCost} player={player} /> / sec)
</> </>
} }
tooltip={earnCharismaExpTooltip} tooltip={earnCharismaExpTooltip}
/> />
</div> </div>
); );
}
} }

@ -1,6 +1,4 @@
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player"; import { Player } from "./Player";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
@ -1187,7 +1185,7 @@ HackingMission.prototype.process = function (numCycles = 1) {
}); });
// Update timer and check if player lost // Update timer and check if player lost
this.time -= storedCycles * Engine._idleSpeed; this.time -= storedCycles * CONSTANTS._idleSpeed;
if (this.time <= 0) { if (this.time <= 0) {
this.finishMission(false); this.finishMission(false);
return; return;
@ -1595,18 +1593,6 @@ HackingMission.prototype.finishMission = function (win) {
} else { } else {
dialogBoxCreate("Mission lost/forfeited! You did not gain any faction reputation."); dialogBoxCreate("Mission lost/forfeited! You did not gain any faction reputation.");
} }
// Clear mission container
var container = document.getElementById("mission-container");
while (container.firstChild) {
container.removeChild(container.firstChild);
}
// Return to Faction page
document.getElementById("mainmenu-container").style.visibility = "visible";
document.getElementById("character-overview").style.visibility = "visible";
Engine.loadFactionContent();
displayFactionContent(this.faction.name);
}; };
export { HackingMission, inMission, setInMission, currMission }; export { HackingMission, inMission, setInMission, currMission };

@ -119,13 +119,7 @@ import { SpecialServerIps } from "./Server/SpecialServerIps";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags"; import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { buyStock, sellStock, shortStock, sellShort } from "./StockMarket/BuyingAndSelling"; import { buyStock, sellStock, shortStock, sellShort } from "./StockMarket/BuyingAndSelling";
import { influenceStockThroughServerHack, influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing"; import { influenceStockThroughServerHack, influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing";
import { import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder } from "./StockMarket/StockMarket";
StockMarket,
SymbolToStockMap,
placeOrder,
cancelOrder,
displayStockMarketContent,
} from "./StockMarket/StockMarket";
import { getBuyTransactionCost, getSellTransactionGain } from "./StockMarket/StockMarketHelpers"; import { getBuyTransactionCost, getSellTransactionGain } from "./StockMarket/StockMarketHelpers";
import { OrderTypes } from "./StockMarket/data/OrderTypes"; import { OrderTypes } from "./StockMarket/data/OrderTypes";
import { PositionTypes } from "./StockMarket/data/PositionTypes"; import { PositionTypes } from "./StockMarket/data/PositionTypes";
@ -1964,18 +1958,14 @@ function NetscriptFunctions(workerScript) {
updateDynamicRam("buyStock", getRamCost("buyStock")); updateDynamicRam("buyStock", getRamCost("buyStock"));
checkTixApiAccess("buyStock"); checkTixApiAccess("buyStock");
const stock = getStockFromSymbol(symbol, "buyStock"); const stock = getStockFromSymbol(symbol, "buyStock");
const res = buyStock(stock, shares, workerScript, { const res = buyStock(stock, shares, workerScript, {});
rerenderFn: displayStockMarketContent,
});
return res ? stock.price : 0; return res ? stock.price : 0;
}, },
sellStock: function (symbol, shares) { sellStock: function (symbol, shares) {
updateDynamicRam("sellStock", getRamCost("sellStock")); updateDynamicRam("sellStock", getRamCost("sellStock"));
checkTixApiAccess("sellStock"); checkTixApiAccess("sellStock");
const stock = getStockFromSymbol(symbol, "sellStock"); const stock = getStockFromSymbol(symbol, "sellStock");
const res = sellStock(stock, shares, workerScript, { const res = sellStock(stock, shares, workerScript, {});
rerenderFn: displayStockMarketContent,
});
return res ? stock.price : 0; return res ? stock.price : 0;
}, },
@ -1991,9 +1981,7 @@ function NetscriptFunctions(workerScript) {
} }
} }
const stock = getStockFromSymbol(symbol, "shortStock"); const stock = getStockFromSymbol(symbol, "shortStock");
const res = shortStock(stock, shares, workerScript, { const res = shortStock(stock, shares, workerScript, {});
rerenderFn: displayStockMarketContent,
});
return res ? stock.price : 0; return res ? stock.price : 0;
}, },
@ -2009,9 +1997,7 @@ function NetscriptFunctions(workerScript) {
} }
} }
const stock = getStockFromSymbol(symbol, "sellShort"); const stock = getStockFromSymbol(symbol, "sellShort");
const res = sellShort(stock, shares, workerScript, { const res = sellShort(stock, shares, workerScript, {});
rerenderFn: displayStockMarketContent,
});
return res ? stock.price : 0; return res ? stock.price : 0;
}, },
@ -2168,7 +2154,6 @@ function NetscriptFunctions(workerScript) {
Player.has4SData = true; Player.has4SData = true;
Player.loseMoney(getStockMarket4SDataCost()); Player.loseMoney(getStockMarket4SDataCost());
workerScript.log("purchase4SMarketData", "Purchased 4S Market Data"); workerScript.log("purchase4SMarketData", "Purchased 4S Market Data");
displayStockMarketContent();
return true; return true;
}, },
purchase4SMarketDataTixApi: function () { purchase4SMarketDataTixApi: function () {
@ -2188,7 +2173,6 @@ function NetscriptFunctions(workerScript) {
Player.has4SDataTixApi = true; Player.has4SDataTixApi = true;
Player.loseMoney(getStockMarket4STixApiCost()); Player.loseMoney(getStockMarket4STixApiCost());
workerScript.log("purchase4SMarketDataTixApi", "Purchased 4S Market Data TIX API"); workerScript.log("purchase4SMarketDataTixApi", "Purchased 4S Market Data TIX API");
displayStockMarketContent();
return true; return true;
}, },
getPurchasedServerLimit: function () { getPurchasedServerLimit: function () {

@ -121,8 +121,33 @@ export interface IPlayer {
bladeburner_analysis_mult: number; bladeburner_analysis_mult: number;
bladeburner_success_chance_mult: number; bladeburner_success_chance_mult: number;
workRepGained: number; createProgramName: string;
timeWorkedCreateProgram: number;
crimeType: string;
timeNeededToCompleteWork: number;
focus: boolean; 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 // Methods
applyForAgentJob(sing?: boolean): boolean | void; applyForAgentJob(sing?: boolean): boolean | void;
@ -209,4 +234,12 @@ export interface IPlayer {
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;
finishFactionWork(cancelled: boolean, sing?: boolean): void;
finishClass(sing?: boolean): void;
finishWork(cancelled: boolean, sing?: boolean): void;
cancelationPenalty(): number;
finishWorkPartTime(sing?: boolean): void;
finishCrime(cancelled: boolean): void;
finishCreateProgramWork(cancelled: boolean): void;
} }

@ -14,10 +14,8 @@ import { CONSTANTS } from "../../Constants";
import { Programs } from "../../Programs/Programs"; import { Programs } from "../../Programs/Programs";
import { determineCrimeSuccess } from "../../Crime/CrimeHelpers"; import { determineCrimeSuccess } from "../../Crime/CrimeHelpers";
import { Crimes } from "../../Crime/Crimes"; import { Crimes } from "../../Crime/Crimes";
import { Engine } from "../../engine";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { displayFactionContent } from "../../Faction/FactionHelpers";
import { resetGangs } from "../../Gang/AllGangs"; import { resetGangs } from "../../Gang/AllGangs";
import { hasHacknetServers } from "../../Hacknet/HacknetHelpers"; import { hasHacknetServers } from "../../Hacknet/HacknetHelpers";
import { Cities } from "../../Locations/Cities"; import { Cities } from "../../Locations/Cities";
@ -48,18 +46,12 @@ import Decimal from "decimal.js";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { ReputationRate } from "../../ui/React/ReputationRate";
import React from "react"; import React from "react";
import ReactDOM from "react-dom";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
export function init() { export function init() {
/* Initialize Player's home computer */ /* Initialize Player's home computer */
@ -518,8 +510,6 @@ export function resetWorkStatus(generalType, group, workType) {
this.currentWorkFactionDescription = ""; this.currentWorkFactionDescription = "";
this.createProgramName = ""; this.createProgramName = "";
this.className = ""; this.className = "";
ReactDOM.unmountComponentAtNode(document.getElementById("work-in-progress-text"));
} }
export function processWorkEarnings(numCycles = 1) { export function processWorkEarnings(numCycles = 1) {
@ -573,25 +563,6 @@ export function startWork(companyName) {
this.workMoneyGainRate = this.getWorkMoneyGain(); this.workMoneyGainRate = this.getWorkMoneyGain();
this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer8Hours; this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer8Hours;
//Remove all old event listeners from Cancel button
var newCancelButton = clearEventListeners("work-in-progress-cancel-button");
newCancelButton.innerHTML = "Cancel Work";
newCancelButton.addEventListener("click", () => {
this.finishWork(true);
return false;
});
const focusButton = clearEventListeners("work-in-progress-something-else-button");
focusButton.style.visibility = "visible";
focusButton.innerHTML = "Do something else simultaneously";
focusButton.addEventListener("click", () => {
this.stopFocusing();
return false;
});
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
} }
export function cancelationPenalty() { export function cancelationPenalty() {
@ -607,11 +578,11 @@ export function work(numCycles) {
// Cap the number of cycles being processed to whatever would put you at // Cap the number of cycles being processed to whatever would put you at
// the work time limit (8 hours) // the work time limit (8 hours)
var overMax = false; var overMax = false;
if (this.timeWorked + Engine._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) { if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
overMax = true; overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / Engine._idleSpeed); numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / CONSTANTS._idleSpeed);
} }
this.timeWorked += Engine._idleSpeed * numCycles; this.timeWorked += CONSTANTS._idleSpeed * numCycles;
this.workRepGainRate = this.getWorkRepGain(); this.workRepGainRate = this.getWorkRepGain();
this.processWorkEarnings(numCycles); this.processWorkEarnings(numCycles);
@ -622,63 +593,7 @@ export function work(numCycles) {
} }
const comp = Companies[this.companyName]; const comp = Companies[this.companyName];
let companyRep = "0";
if (comp == null || !(comp instanceof Company)) {
console.error(`Could not find Company: ${this.companyName}`);
} else {
companyRep = comp.playerReputation;
}
influenceStockThroughCompanyWork(comp, this.workRepGainRate, numCycles); influenceStockThroughCompanyWork(comp, this.workRepGainRate, numCycles);
const position = this.jobs[this.companyName];
const penalty = this.cancelationPenalty();
const penaltyString = penalty === 0.5 ? "half" : "three-quarters";
var elem = document.getElementById("work-in-progress-text");
ReactDOM.render(
<>
You are currently working as a {position} at {this.companyName} (Current Company Reputation:{" "}
{Reputation(companyRep)})<br />
<br />
You have been working for {convertTimeMsToTimeElapsedString(this.timeWorked)}
<br />
<br />
You have earned: <br />
<br />
<Money money={this.workMoneyGained} /> ({MoneyRate(this.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<br />
{Reputation(this.workRepGained)} ({ReputationRate(this.workRepGainRate * CYCLES_PER_SEC)}) reputation for this
company <br />
<br />
{numeralWrapper.formatExp(this.workHackExpGained)} (
{`${numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}
) hacking exp <br />
<br />
{numeralWrapper.formatExp(this.workStrExpGained)} (
{`${numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}
) strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} (
{`${numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}
) defense exp <br />
{numeralWrapper.formatExp(this.workDexExpGained)} (
{`${numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}
) dexterity exp <br />
{numeralWrapper.formatExp(this.workAgiExpGained)} (
{`${numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}
) agility exp <br />
<br />
{numeralWrapper.formatExp(this.workChaExpGained)} (
{`${numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}
) charisma exp <br />
<br />
You will automatically finish after working for 8 hours. You can cancel earlier if you wish, but you will only
gain {penaltyString} of the reputation you've earned so far.
</>,
elem,
);
} }
export function finishWork(cancelled, sing = false) { export function finishWork(cancelled, sing = false) {
@ -731,10 +646,7 @@ export function finishWork(cancelled, sing = false) {
dialogBoxCreate(content); dialogBoxCreate(content);
} }
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
Engine.loadLocationContent(false);
if (sing) { if (sing) {
var res = var res =
@ -781,27 +693,17 @@ export function startWorkPartTime(companyName) {
this.workMoneyGainRate = this.getWorkMoneyGain(); this.workMoneyGainRate = this.getWorkMoneyGain();
this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer8Hours; this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer8Hours;
var newCancelButton = clearEventListeners("work-in-progress-cancel-button");
newCancelButton.innerHTML = "Stop Working";
newCancelButton.addEventListener("click", () => {
this.finishWorkPartTime();
return false;
});
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
} }
export function workPartTime(numCycles) { export function workPartTime(numCycles) {
//Cap the number of cycles being processed to whatever would put you at the //Cap the number of cycles being processed to whatever would put you at the
//work time limit (8 hours) //work time limit (8 hours)
var overMax = false; var overMax = false;
if (this.timeWorked + Engine._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) { if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
overMax = true; overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / Engine._idleSpeed); numCycles = Math.round((CONSTANTS.MillisecondsPer8Hours - this.timeWorked) / CONSTANTS._idleSpeed);
} }
this.timeWorked += Engine._idleSpeed * numCycles; this.timeWorked += CONSTANTS._idleSpeed * numCycles;
this.workRepGainRate = this.getWorkRepGain(); this.workRepGainRate = this.getWorkRepGain();
this.processWorkEarnings(numCycles); this.processWorkEarnings(numCycles);
@ -810,60 +712,6 @@ export function workPartTime(numCycles) {
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) { if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
return this.finishWorkPartTime(); return this.finishWorkPartTime();
} }
var comp = Companies[this.companyName],
companyRep = "0";
if (comp == null || !(comp instanceof Company)) {
console.error(`Could not find Company: ${this.companyName}`);
} else {
companyRep = comp.playerReputation;
}
const position = this.jobs[this.companyName];
const elem = document.getElementById("work-in-progress-text");
ReactDOM.render(
<>
You are currently working as a {position} at {this.companyName} (Current Company Reputation:{" "}
{Reputation(companyRep)})<br />
<br />
You have been working for {convertTimeMsToTimeElapsedString(this.timeWorked)}
<br />
<br />
You have earned: <br />
<br />
<Money money={this.workMoneyGained} /> ({MoneyRate(this.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<br />
{Reputation(this.workRepGained)} (
{Reputation(`${numeralWrapper.formatExp(this.workRepGainRate * CYCLES_PER_SEC)} / sec`)}
) reputation for this company <br />
<br />
{numeralWrapper.formatExp(this.workHackExpGained)} (
{`${numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec`}
) hacking exp <br />
<br />
{numeralWrapper.formatExp(this.workStrExpGained)} (
{`${numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec`}
) strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} (
{`${numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec`}
) defense exp <br />
{numeralWrapper.formatExp(this.workDexExpGained)} (
{`${numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec`}
) dexterity exp <br />
{numeralWrapper.formatExp(this.workAgiExpGained)} (
{`${numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec`}
) agility exp <br />
<br />
{numeralWrapper.formatExp(this.workChaExpGained)} (
{`${numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec`}
) charisma exp <br />
<br />
You will automatically finish after working for 8 hours. You can cancel earlier if you wish, and there will be no
penalty because this is a part-time job.
</>,
elem,
);
} }
export function finishWorkPartTime(sing = false) { export function finishWorkPartTime(sing = false) {
@ -894,10 +742,7 @@ export function finishWorkPartTime(sing = false) {
dialogBoxCreate(content); dialogBoxCreate(content);
} }
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
Engine.loadLocationContent(false);
if (sing) { if (sing) {
var res = var res =
"You worked for " + "You worked for " +
@ -928,17 +773,11 @@ export function finishWorkPartTime(sing = false) {
} }
export function startFocusing() { export function startFocusing() {
const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden";
this.focus = true; this.focus = true;
Engine.loadWorkInProgressContent();
} }
export function stopFocusing() { export function stopFocusing() {
const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.focus = false; this.focus = false;
Engine.loadTerminalContent();
} }
/* Working for Faction */ /* Working for Faction */
@ -957,24 +796,6 @@ export function startFactionWork(faction) {
this.currentWorkFactionName = faction.name; this.currentWorkFactionName = faction.name;
this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer20Hours; this.timeNeededToCompleteWork = CONSTANTS.MillisecondsPer20Hours;
const cancelButton = clearEventListeners("work-in-progress-cancel-button");
cancelButton.innerHTML = "Stop Faction Work";
cancelButton.addEventListener("click", () => {
this.finishFactionWork(true);
return false;
});
const focusButton = clearEventListeners("work-in-progress-something-else-button");
focusButton.style.visibility = "visible";
focusButton.innerHTML = "Do something else simultaneously";
focusButton.addEventListener("click", () => {
this.stopFocusing();
return false;
});
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
} }
export function startFactionHackWork(faction) { export function startFactionHackWork(faction) {
@ -1046,11 +867,11 @@ export function workForFaction(numCycles) {
//Cap the number of cycles being processed to whatever would put you at limit (20 hours) //Cap the number of cycles being processed to whatever would put you at limit (20 hours)
var overMax = false; var overMax = false;
if (this.timeWorked + Engine._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer20Hours) { if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer20Hours) {
overMax = true; overMax = true;
numCycles = Math.round((CONSTANTS.MillisecondsPer20Hours - this.timeWorked) / Engine._idleSpeed); numCycles = Math.round((CONSTANTS.MillisecondsPer20Hours - this.timeWorked) / CONSTANTS._idleSpeed);
} }
this.timeWorked += Engine._idleSpeed * numCycles; this.timeWorked += CONSTANTS._idleSpeed * numCycles;
this.processWorkEarnings(numCycles); this.processWorkEarnings(numCycles);
@ -1058,44 +879,6 @@ export function workForFaction(numCycles) {
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) { if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) {
return this.finishFactionWork(false); return this.finishFactionWork(false);
} }
const elem = document.getElementById("work-in-progress-text");
ReactDOM.render(
<>
You are currently {this.currentWorkFactionDescription} for your faction {faction.name}
<br />
(Current Faction Reputation: {Reputation(faction.playerReputation)}). <br />
You have been doing this for {convertTimeMsToTimeElapsedString(this.timeWorked)}
<br />
<br />
You have earned: <br />
<br />
<Money money={this.workMoneyGained} /> ({MoneyRate(this.workMoneyGainRate * CYCLES_PER_SEC)}) <br />
<br />
{Reputation(this.workRepGained)} ({ReputationRate(this.workRepGainRate * CYCLES_PER_SEC)}) reputation for this
faction <br />
<br />
{numeralWrapper.formatExp(this.workHackExpGained)} (
{numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp <br />
<br />
{numeralWrapper.formatExp(this.workStrExpGained)} (
{numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} (
{numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp <br />
{numeralWrapper.formatExp(this.workDexExpGained)} (
{numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp <br />
{numeralWrapper.formatExp(this.workAgiExpGained)} (
{numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp <br />
<br />
{numeralWrapper.formatExp(this.workChaExpGained)} (
{numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp <br />
<br />
You will automatically finish after working for 20 hours. You can cancel earlier if you wish.
<br />
There is no penalty for cancelling earlier.
</>,
elem,
);
} }
export function finishFactionWork(cancelled, sing = false) { export function finishFactionWork(cancelled, sing = false) {
@ -1125,13 +908,8 @@ export function finishFactionWork(cancelled, sing = false) {
); );
} }
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
Engine.loadFactionContent();
displayFactionContent(faction.name);
if (sing) { if (sing) {
var res = var res =
"You worked for your faction " + "You worked for your faction " +
@ -1419,19 +1197,6 @@ export function startCreateProgramWork(programName, time, reqLevel) {
} }
this.createProgramName = programName; this.createProgramName = programName;
var cancelButton = clearEventListeners("work-in-progress-cancel-button");
cancelButton.innerHTML = "Cancel work on creating program";
cancelButton.addEventListener("click", () => {
this.finishCreateProgramWork(true);
return false;
});
const focusButton = clearEventListeners("work-in-progress-something-else-button");
focusButton.style.visibility = "hidden";
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
} }
export function createProgramWork(numCycles) { export function createProgramWork(numCycles) {
@ -1441,28 +1206,12 @@ export function createProgramWork(numCycles) {
skillMult = 1 + (skillMult - 1) / 5; //The divider constant can be adjusted as necessary skillMult = 1 + (skillMult - 1) / 5; //The divider constant can be adjusted as necessary
//Skill multiplier directly applied to "time worked" //Skill multiplier directly applied to "time worked"
this.timeWorked += Engine._idleSpeed * numCycles; this.timeWorked += CONSTANTS._idleSpeed * numCycles;
this.timeWorkedCreateProgram += Engine._idleSpeed * numCycles * skillMult; this.timeWorkedCreateProgram += CONSTANTS._idleSpeed * numCycles * skillMult;
var programName = this.createProgramName;
if (this.timeWorkedCreateProgram >= this.timeNeededToCompleteWork) { if (this.timeWorkedCreateProgram >= this.timeNeededToCompleteWork) {
this.finishCreateProgramWork(false); this.finishCreateProgramWork(false);
} }
const elem = document.getElementById("work-in-progress-text");
ReactDOM.render(
<>
You are currently working on coding {programName}.<br />
<br />
You have been working for {convertTimeMsToTimeElapsedString(this.timeWorked)}
<br />
<br />
The program is {((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 100).toFixed(2)}
% complete. <br />
If you cancel, your work will be saved and you can come back to complete the program later.
</>,
elem,
);
} }
export function finishCreateProgramWork(cancelled) { export function finishCreateProgramWork(cancelled) {
@ -1483,12 +1232,8 @@ export function finishCreateProgramWork(cancelled) {
this.gainIntelligenceExp(this.createProgramReqLvl / CONSTANTS.IntelligenceProgramBaseExpGain); this.gainIntelligenceExp(this.createProgramReqLvl / CONSTANTS.IntelligenceProgramBaseExpGain);
} }
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
Engine.loadTerminalContent();
this.resetWorkStatus(); this.resetWorkStatus();
} }
@ -1501,7 +1246,7 @@ export function startClass(costMult, expMult, className) {
this.className = className; this.className = className;
const gameCPS = 1000 / Engine._idleSpeed; const gameCPS = 1000 / CONSTANTS._idleSpeed;
//Find cost and exp gain per game cycle //Find cost and exp gain per game cycle
var cost = 0; var cost = 0;
@ -1564,62 +1309,11 @@ export function startClass(costMult, expMult, className) {
this.workDexExpGainRate = dexExp * this.dexterity_exp_mult * BitNodeMultipliers.ClassGymExpGain; this.workDexExpGainRate = dexExp * this.dexterity_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workAgiExpGainRate = agiExp * this.agility_exp_mult * BitNodeMultipliers.ClassGymExpGain; this.workAgiExpGainRate = agiExp * this.agility_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workChaExpGainRate = chaExp * this.charisma_exp_mult * BitNodeMultipliers.ClassGymExpGain; this.workChaExpGainRate = chaExp * this.charisma_exp_mult * BitNodeMultipliers.ClassGymExpGain;
var cancelButton = clearEventListeners("work-in-progress-cancel-button");
if (
className == CONSTANTS.ClassGymStrength ||
className == CONSTANTS.ClassGymDefense ||
className == CONSTANTS.ClassGymDexterity ||
className == CONSTANTS.ClassGymAgility
) {
cancelButton.innerHTML = "Stop training at gym";
} else {
cancelButton.innerHTML = "Stop taking course";
}
cancelButton.addEventListener("click", () => {
this.finishClass();
return false;
});
const focusButton = clearEventListeners("work-in-progress-something-else-button");
focusButton.style.visibility = "hidden";
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
} }
export function takeClass(numCycles) { export function takeClass(numCycles) {
this.timeWorked += Engine._idleSpeed * numCycles; this.timeWorked += CONSTANTS._idleSpeed * numCycles;
var className = this.className;
this.processWorkEarnings(numCycles); this.processWorkEarnings(numCycles);
const elem = document.getElementById("work-in-progress-text");
ReactDOM.render(
<>
You have been {className} for {convertTimeMsToTimeElapsedString(this.timeWorked)}
<br />
<br />
This has cost you: <br />
<Money money={-this.workMoneyGained} /> ({MoneyRate(this.workMoneyLossRate * CYCLES_PER_SEC)}) <br />
<br />
You have gained: <br />
{numeralWrapper.formatExp(this.workHackExpGained)} (
{numeralWrapper.formatExp(this.workHackExpGainRate * CYCLES_PER_SEC)} / sec) hacking exp <br />
{numeralWrapper.formatExp(this.workStrExpGained)} (
{numeralWrapper.formatExp(this.workStrExpGainRate * CYCLES_PER_SEC)} / sec) strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} (
{numeralWrapper.formatExp(this.workDefExpGainRate * CYCLES_PER_SEC)} / sec) defense exp <br />
{numeralWrapper.formatExp(this.workDexExpGained)} (
{numeralWrapper.formatExp(this.workDexExpGainRate * CYCLES_PER_SEC)} / sec) dexterity exp <br />
{numeralWrapper.formatExp(this.workAgiExpGained)} (
{numeralWrapper.formatExp(this.workAgiExpGainRate * CYCLES_PER_SEC)} / sec) agility exp <br />
{numeralWrapper.formatExp(this.workChaExpGained)} (
{numeralWrapper.formatExp(this.workChaExpGainRate * CYCLES_PER_SEC)} / sec) charisma exp <br />
You may cancel at any time
</>,
elem,
);
} }
//The 'sing' argument defines whether or not this function was called //The 'sing' argument defines whether or not this function was called
@ -1650,12 +1344,8 @@ export function finishClass(sing = false) {
); );
} }
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
Engine.loadLocationContent(false);
if (sing) { if (sing) {
var res = var res =
"After " + "After " +
@ -1708,49 +1398,12 @@ export function startCrime(crimeType, hackExp, strExp, defExp, dexExp, agiExp, c
this.workMoneyGained = money * this.crime_money_mult * BitNodeMultipliers.CrimeMoney; this.workMoneyGained = money * this.crime_money_mult * BitNodeMultipliers.CrimeMoney;
this.timeNeededToCompleteWork = time; this.timeNeededToCompleteWork = time;
//Remove all old event listeners from Cancel button
const newCancelButton = clearEventListeners("work-in-progress-cancel-button");
newCancelButton.innerHTML = "Cancel crime";
newCancelButton.addEventListener("click", () => {
this.finishCrime(true);
return false;
});
const focusButton = clearEventListeners("work-in-progress-something-else-button");
focusButton.style.visibility = "hidden";
//Display Work In Progress Screen
Engine.loadWorkInProgressContent();
} }
export function commitCrime(numCycles) { export function commitCrime(numCycles) {
this.timeWorked += Engine._idleSpeed * numCycles; this.timeWorked += CONSTANTS._idleSpeed * numCycles;
if (this.timeWorked >= this.timeNeededToCompleteWork) { if (this.timeWorked >= this.timeNeededToCompleteWork) this.finishCrime(false);
this.finishCrime(false);
return;
}
var percent = Math.round((this.timeWorked / this.timeNeededToCompleteWork) * 100);
var numBars = Math.round(percent / 5);
if (numBars < 0) {
numBars = 0;
}
if (numBars > 20) {
numBars = 20;
}
var progressBar = "[" + Array(numBars + 1).join("|") + Array(20 - numBars + 1).join(" ") + "]";
var txt = document.getElementById("work-in-progress-text");
txt.innerHTML =
"You are attempting to " +
this.crimeType +
".<br>" +
"Time remaining: " +
convertTimeMsToTimeElapsedString(this.timeNeededToCompleteWork - this.timeWorked) +
"<br>" +
progressBar.replace(/ /g, "&nbsp;");
} }
export function finishCrime(cancelled) { export function finishCrime(cancelled) {
@ -1892,11 +1545,9 @@ export function finishCrime(cancelled) {
} }
this.committingCrimeThruSingFn = false; this.committingCrimeThruSingFn = false;
this.singFnCrimeWorkerScript = null; this.singFnCrimeWorkerScript = null;
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false; this.isWorking = false;
this.crimeType = "";
this.resetWorkStatus(); this.resetWorkStatus();
Engine.loadLocationContent(false);
} }
//Cancels the player's current "work" assignment and gives the proper rewards //Cancels the player's current "work" assignment and gives the proper rewards

@ -20,7 +20,7 @@ export function SleeveRoot(props: IProps): React.ReactElement {
}, []); }, []);
return ( return (
<div style={{ width: "70%" }}> <>
<h1>Sleeves</h1> <h1>Sleeves</h1>
<p> <p>
Duplicate Sleeves are MK-V Synthoids (synthetic androids) into which your consciousness has been copied. In Duplicate Sleeves are MK-V Synthoids (synthetic androids) into which your consciousness has been copied. In
@ -50,6 +50,6 @@ export function SleeveRoot(props: IProps): React.ReactElement {
</li> </li>
))} ))}
</ul> </ul>
</div> </>
); );
} }

2
src/Prestige.d.ts vendored Normal file

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

@ -3,7 +3,6 @@ import { augmentationExists, initAugmentations } from "./Augmentation/Augmentati
import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner/Bladeburner"; import { Bladeburner } from "./Bladeburner/Bladeburner";
import { writeCinematicText } from "./CinematicText";
import { Companies, initCompanies } from "./Company/Companies"; import { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/IndustryData"; import { resetIndustryResearchTrees } from "./Corporation/IndustryData";
import { Programs } from "./Programs/Programs"; import { Programs } from "./Programs/Programs";
@ -25,13 +24,7 @@ import { SpecialServerIps, prestigeSpecialServerIps, SpecialServerNames } from "
import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket"; import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import { Page, routing } from "./ui/navigationTracking";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { createElement } from "../utils/uiHelpers/createElement";
import { createPopup } from "../utils/uiHelpers/createPopup";
import Decimal from "decimal.js"; import Decimal from "decimal.js";
@ -39,9 +32,6 @@ const BitNode8StartingMoney = 250e6;
// Prestige by purchasing augmentation // Prestige by purchasing augmentation
function prestigeAugmentation() { function prestigeAugmentation() {
// Set Navigation to Terminal screen, for any logic that depends on it
routing.navigateTo(Page.Terminal);
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
const megaCorpFactions = [ const megaCorpFactions = [
@ -62,9 +52,6 @@ function prestigeAugmentation() {
}); });
Player.prestigeAugmentation(); Player.prestigeAugmentation();
Terminal.clear();
Engine.loadTerminalContent();
// Delete all Worker Scripts objects // Delete all Worker Scripts objects
prestigeWorkerScripts(); prestigeWorkerScripts();
@ -244,11 +231,6 @@ function prestigeSourceFile(flume) {
// Messages // Messages
initMessages(); initMessages();
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
Terminal.clear();
Engine.loadTerminalContent();
// BitNode 3: Corporatocracy // BitNode 3: Corporatocracy
if (Player.bitNodeN === 3) { if (Player.bitNodeN === 3) {
homeComp.messages.push(LiteratureNames.CorporationManagementHandbook); homeComp.messages.push(LiteratureNames.CorporationManagementHandbook);
@ -258,56 +240,6 @@ function prestigeSourceFile(flume) {
); );
} }
// BitNode 6: Bladeburner
if (Player.bitNodeN === 6) {
var cinematicText = [
"In the middle of the 21st century, OmniTek Incorporated advanced robot evolution " +
"with their Synthoids (synthetic androids), a being virtually identical to a human.",
"------",
"Their sixth-generation Synthoids, called MK-VI, were stronger, faster, and more " +
"intelligent than humans. Many argued that the MK-VI Synthoids were the first " +
"example of sentient AI.",
"------",
"Unfortunately, in 2070 a terrorist group called Ascendis Totalis hacked into OmniTek and " +
"uploaded a rogue AI into their Synthoid manufacturing facilities.",
"------",
"The MK-VI Synthoids infected by the rogue AI turned hostile toward humanity, initiating " +
"the deadliest conflict in human history. This dark chapter is now known as the Synthoid Uprising.",
"------",
"In the aftermath of the Uprising, further manufacturing of Synthoids with advanced AI " +
"was banned. MK-VI Synthoids that did not have the rogue Ascendis Totalis AI were " +
"allowed to continue their existence.",
"------",
"The intelligence community believes that not all of the rogue MK-VI Synthoids from the Uprising were " +
"found and destroyed, and that many of them are blending in as normal humans in society today. " +
"As a result, many nations have created Bladeburner divisions, special units that are tasked with " +
"investigating and dealing with Synthoid threats.",
];
writeCinematicText(cinematicText)
.then(function () {
var popupId = "bladeburner-bitnode-start-nsa-notification";
var txt = createElement("p", {
innerText:
"Visit the National Security Agency (NSA) to apply for their Bladeburner " +
"division! You will need 100 of each combat stat before doing this.",
});
var brEl = createElement("br");
var okBtn = createElement("a", {
class: "a-link-button",
innerText: "Got it!",
padding: "8px",
clickListener: () => {
removeElementById(popupId);
return false;
},
});
createPopup(popupId, [txt, brEl, okBtn]);
})
.catch(function (e) {
exceptionAlert(e);
});
}
// BitNode 8: Ghost of Wall Street // BitNode 8: Ghost of Wall Street
if (Player.bitNodeN === 8) { if (Player.bitNodeN === 8) {
Player.money = new Decimal(BitNode8StartingMoney); Player.money = new Decimal(BitNode8StartingMoney);

@ -1,6 +1,7 @@
import { BaseServer } from "../Server/BaseServer"; import { BaseServer } from "../Server/BaseServer";
import { ITerminal } from "../Terminal/ITerminal"; import { ITerminal } from "../Terminal/ITerminal";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
export interface IProgramCreate { export interface IProgramCreate {
level: number; level: number;
@ -12,12 +13,12 @@ export interface IProgramCreate {
export class Program { export class Program {
name = ""; name = "";
create: IProgramCreate | null; create: IProgramCreate | null;
run: (terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]) => void; run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]) => void;
constructor( constructor(
name: string, name: string,
create: IProgramCreate | null, create: IProgramCreate | null,
run: (terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]) => void, run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]) => void,
) { ) {
this.name = name; this.name = name;
this.create = create; this.create = create;

@ -3,6 +3,7 @@ import { CONSTANTS } from "../../Constants";
import { BaseServer } from "../../Server/BaseServer"; import { BaseServer } from "../../Server/BaseServer";
import { Server } from "../../Server/Server"; import { Server } from "../../Server/Server";
import { ITerminal } from "../../Terminal/ITerminal"; import { ITerminal } from "../../Terminal/ITerminal";
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 { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
@ -30,7 +31,7 @@ export interface IProgramCreationParams {
key: string; key: string;
name: string; name: string;
create: IProgramCreate | null; create: IProgramCreate | null;
run: (terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]) => void; run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]) => void;
} }
export const programsMetadata: IProgramCreationParams[] = [ export const programsMetadata: IProgramCreationParams[] = [
@ -43,7 +44,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(1), req: requireHackingLevel(1),
time: CONSTANTS.MillisecondsPerFiveMinutes, time: CONSTANTS.MillisecondsPerFiveMinutes,
}, },
run: (terminal: ITerminal, player: IPlayer, server: BaseServer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer): void => {
if (!(server instanceof Server)) { if (!(server instanceof Server)) {
terminal.error("Cannot nuke this kind of server."); terminal.error("Cannot nuke this kind of server.");
return; return;
@ -72,7 +73,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(50), req: requireHackingLevel(50),
time: CONSTANTS.MillisecondsPerFiveMinutes * 2, time: CONSTANTS.MillisecondsPerFiveMinutes * 2,
}, },
run: (terminal: ITerminal, player: IPlayer, server: BaseServer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer): void => {
if (!(server instanceof Server)) { if (!(server instanceof Server)) {
terminal.error("Cannot run BruteSSH.exe on this kind of server."); terminal.error("Cannot run BruteSSH.exe on this kind of server.");
return; return;
@ -96,7 +97,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(100), req: requireHackingLevel(100),
time: CONSTANTS.MillisecondsPerHalfHour, time: CONSTANTS.MillisecondsPerHalfHour,
}, },
run: (terminal: ITerminal, player: IPlayer, server: BaseServer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer): void => {
if (!(server instanceof Server)) { if (!(server instanceof Server)) {
terminal.error("Cannot run FTPCrack.exe on this kind of server."); terminal.error("Cannot run FTPCrack.exe on this kind of server.");
return; return;
@ -120,7 +121,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(250), req: requireHackingLevel(250),
time: CONSTANTS.MillisecondsPer2Hours, time: CONSTANTS.MillisecondsPer2Hours,
}, },
run: (terminal: ITerminal, player: IPlayer, server: BaseServer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer): void => {
if (!(server instanceof Server)) { if (!(server instanceof Server)) {
terminal.error("Cannot run relaySMTP.exe on this kind of server."); terminal.error("Cannot run relaySMTP.exe on this kind of server.");
return; return;
@ -144,7 +145,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(500), req: requireHackingLevel(500),
time: CONSTANTS.MillisecondsPer4Hours, time: CONSTANTS.MillisecondsPer4Hours,
}, },
run: (terminal: ITerminal, player: IPlayer, server: BaseServer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer): void => {
if (!(server instanceof Server)) { if (!(server instanceof Server)) {
terminal.error("Cannot run HTTPWorm.exe on this kind of server."); terminal.error("Cannot run HTTPWorm.exe on this kind of server.");
return; return;
@ -168,7 +169,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(750), req: requireHackingLevel(750),
time: CONSTANTS.MillisecondsPer8Hours, time: CONSTANTS.MillisecondsPer8Hours,
}, },
run: (terminal: ITerminal, player: IPlayer, server: BaseServer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer): void => {
if (!(server instanceof Server)) { if (!(server instanceof Server)) {
terminal.error("Cannot run SQLInject.exe on this kind of server."); terminal.error("Cannot run SQLInject.exe on this kind of server.");
return; return;
@ -192,7 +193,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(75), req: requireHackingLevel(75),
time: CONSTANTS.MillisecondsPerQuarterHour, time: CONSTANTS.MillisecondsPerQuarterHour,
}, },
run: (terminal: ITerminal): void => { run: (router: IRouter, terminal: ITerminal): void => {
terminal.print("This executable cannot be run."); terminal.print("This executable cannot be run.");
terminal.print("DeepscanV1.exe lets you run 'scan-analyze' with a depth up to 5."); terminal.print("DeepscanV1.exe lets you run 'scan-analyze' with a depth up to 5.");
}, },
@ -206,7 +207,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(400), req: requireHackingLevel(400),
time: CONSTANTS.MillisecondsPer2Hours, time: CONSTANTS.MillisecondsPer2Hours,
}, },
run: (terminal: ITerminal): void => { run: (router: IRouter, terminal: ITerminal): void => {
terminal.print("This executable cannot be run."); terminal.print("This executable cannot be run.");
terminal.print("DeepscanV2.exe lets you run 'scan-analyze' with a depth up to 10."); terminal.print("DeepscanV2.exe lets you run 'scan-analyze' with a depth up to 10.");
}, },
@ -220,7 +221,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(75), req: requireHackingLevel(75),
time: CONSTANTS.MillisecondsPerHalfHour, time: CONSTANTS.MillisecondsPerHalfHour,
}, },
run: (terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer, server: BaseServer, args: string[]): void => {
if (args.length !== 1) { if (args.length !== 1) {
terminal.print("Must pass a server hostname or IP as an argument for ServerProfiler.exe"); terminal.print("Must pass a server hostname or IP as an argument for ServerProfiler.exe");
return; return;
@ -270,7 +271,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(25), req: requireHackingLevel(25),
time: CONSTANTS.MillisecondsPerQuarterHour, time: CONSTANTS.MillisecondsPerQuarterHour,
}, },
run: (terminal: ITerminal): void => { run: (router: IRouter, terminal: ITerminal): void => {
terminal.print("This executable cannot be run."); terminal.print("This executable cannot be run.");
terminal.print("AutoLink.exe lets you automatically connect to other servers when using 'scan-analyze'."); terminal.print("AutoLink.exe lets you automatically connect to other servers when using 'scan-analyze'.");
terminal.print("When using scan-analyze, click on a server's hostname to connect to it."); terminal.print("When using scan-analyze, click on a server's hostname to connect to it.");
@ -285,10 +286,11 @@ export const programsMetadata: IProgramCreationParams[] = [
req: bitFlumeRequirements(), req: bitFlumeRequirements(),
time: CONSTANTS.MillisecondsPerFiveMinutes / 20, time: CONSTANTS.MillisecondsPerFiveMinutes / 20,
}, },
run: (terminal: ITerminal, player: IPlayer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => {
const popupId = "bitflume-popup"; const popupId = "bitflume-popup";
createPopup(popupId, BitFlumePopup, { createPopup(popupId, BitFlumePopup, {
player: player, player: player,
router: router,
popupId: popupId, popupId: popupId,
}); });
}, },
@ -297,7 +299,7 @@ export const programsMetadata: IProgramCreationParams[] = [
key: "Flight", key: "Flight",
name: "fl1ght.exe", name: "fl1ght.exe",
create: null, create: null,
run: (terminal: ITerminal, player: IPlayer): void => { run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => {
const numAugReq = Math.round(BitNodeMultipliers.DaedalusAugsRequirement * 30); const numAugReq = Math.round(BitNodeMultipliers.DaedalusAugsRequirement * 30);
const fulfilled = const fulfilled =
player.augmentations.length >= numAugReq && player.money.gt(1e11) && player.hacking_skill >= 2500; player.augmentations.length >= numAugReq && player.money.gt(1e11) && player.hacking_skill >= 2500;

@ -1,15 +1,13 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { use } from "../../ui/Context";
import { getAvailableCreatePrograms } from "../ProgramHelpers"; import { getAvailableCreatePrograms } from "../ProgramHelpers";
import { Box, ButtonGroup, Tooltip, Typography } from "@mui/material"; import { Box, ButtonGroup, Tooltip, Typography } from "@mui/material";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
interface IProps { export function ProgramsRoot(): React.ReactElement {
player: IPlayer; const player = use.Player();
} const router = use.Router();
export function ProgramsRoot(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);
@ -25,26 +23,31 @@ export function ProgramsRoot(props: IProps): React.ReactElement {
<div> <div>
<Box> <Box>
<Typography> <Typography>
This page displays any programs that you are able to create. Writing the code for a program takes time, which This page displays any programs that you are able to create. Writing the code for a program takes time,
can vary based on how complex the program is. If you are working on creating a program you can cancel at any which can vary based on how complex the program is. If you are working on creating a program you can cancel
time. Your progress will be saved and you can continue later. at any time. Your progress will be saved and you can continue later.
</Typography> </Typography>
</Box> </Box>
<ButtonGroup> <ButtonGroup>
{getAvailableCreatePrograms(props.player).map((program) => { {getAvailableCreatePrograms(player).map((program) => {
const create = program.create; const create = program.create;
if (create === null) return <></>; if (create === null) return <></>;
return ( return (
<Tooltip title={create.tooltip}> <Tooltip key={program.name} title={create.tooltip}>
<Button onClick={() => props.player.startCreateProgramWork(program.name, create.time, create.level)}> <Button
{program.name} onClick={() => {
player.startCreateProgramWork(program.name, create.time, create.level);
router.toWork();
}}
>
{program.name}
</Button> </Button>
</Tooltip> </Tooltip>
) );
})} })}
</ButtonGroup> </ButtonGroup>
</div> </div>
</> </>
) );
} }

6
src/RedPill.d.ts vendored

@ -1,6 +1,2 @@
export declare let redPillFlag: boolean; export declare let redPillFlag: boolean;
export declare function hackWorldDaemon( export declare function enterBitNode(router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number);
currentNodeNumber: number,
flume: boolean = false,
quick: boolean = false,
): void;

@ -1,7 +1,6 @@
/** /**
* Implementation for what happens when you destroy a BitNode * Implementation for what happens when you destroy a BitNode
*/ */
import { Engine } from "./engine";
import { Player } from "./Player"; import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige"; import { prestigeSourceFile } from "./Prestige";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile"; import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
@ -9,20 +8,10 @@ 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 { BitverseRoot } from "./BitNode/ui/BitverseRoot";
import React from "react";
import ReactDOM from "react-dom";
let redPillFlag = false; let redPillFlag = false;
function hackWorldDaemon(currentNodeNumber, flume = false, quick = false) { function hackWorldDaemon(router, flume = false, quick = false) {
// Clear the screen router.toBitVerse(flume, quick);
const container = document.getElementById("red-pill-container");
ReactDOM.unmountComponentAtNode(container);
Engine.loadRedPillContent();
ReactDOM.render(
<BitverseRoot destroyedBitNodeNum={currentNodeNumber} flume={flume} enter={enterBitNode} quick={quick} />,
container,
);
redPillFlag = true; redPillFlag = true;
} }
@ -68,12 +57,12 @@ function giveSourceFile(bitNodeNumber) {
Player.intelligence = 1; Player.intelligence = 1;
} }
dialogBoxCreate( dialogBoxCreate(
"You received a Source-File for destroying a Bit Node!<br><br>" + sourceFile.name + "<br><br>" + sourceFile.info, "You received a Source-File for destroying a BitNode!<br><br>" + sourceFile.name + "<br><br>" + sourceFile.info,
); );
} }
} }
function enterBitNode(flume, destroyedBitNode, newBitNode) { export function enterBitNode(router, flume, destroyedBitNode, newBitNode) {
if (!flume) { if (!flume) {
giveSourceFile(destroyedBitNode); giveSourceFile(destroyedBitNode);
} else { } else {
@ -86,12 +75,14 @@ function enterBitNode(flume, destroyedBitNode, newBitNode) {
Player.intelligence = 1; Player.intelligence = 1;
} }
redPillFlag = false; redPillFlag = false;
const container = document.getElementById("red-pill-container");
ReactDOM.unmountComponentAtNode(container);
// Set new Bit Node // Set new Bit Node
Player.bitNodeN = newBitNode; Player.bitNodeN = newBitNode;
if (newBitNode === 6) {
router.toBladeburnerCinematic();
} else {
router.toTerminal();
}
prestigeSourceFile(flume); prestigeSourceFile(flume);
} }

@ -6,7 +6,6 @@
*/ */
import { calculateRamUsage } from "./RamCalculations"; import { calculateRamUsage } from "./RamCalculations";
import { ScriptUrl } from "./ScriptUrl"; import { ScriptUrl } from "./ScriptUrl";
import { Page, routing } from "../ui/navigationTracking";
import { setTimeoutRef } from "../utils/SetTimeoutRef"; import { setTimeoutRef } from "../utils/SetTimeoutRef";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
@ -93,22 +92,18 @@ export class Script {
* @param {Script[]} otherScripts - Other scripts on the server. Used to process imports * @param {Script[]} otherScripts - Other scripts on the server. Used to process imports
*/ */
saveScript(code: string, serverIp: string, otherScripts: Script[]): void { saveScript(code: string, serverIp: string, otherScripts: Script[]): void {
if (routing.isOn(Page.ScriptEditor)) { // Update code and filename
// Update code and filename this.code = code.replace(/^\s+|\s+$/g, "");
this.code = code.replace(/^\s+|\s+$/g, "");
const filenameElem: HTMLInputElement | null = document.getElementById( const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
"script-editor-filename", if (filenameElem == null) {
) as HTMLInputElement; console.error(`Failed to get Script filename DOM element`);
if (filenameElem == null) { return;
console.error(`Failed to get Script filename DOM element`);
return;
}
this.filename = filenameElem.value;
this.server = serverIp;
this.updateRamUsage(otherScripts);
this.markUpdated();
} }
this.filename = filenameElem.value;
this.server = serverIp;
this.updateRamUsage(otherScripts);
this.markUpdated();
} }
/** /**

@ -9,7 +9,7 @@ import { Options } from "./Options";
import { js_beautify as beautifyCode } from "js-beautify"; import { js_beautify as beautifyCode } from "js-beautify";
import { isValidFilePath } from "../../Terminal/DirectoryHelpers"; import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine"; import { IRouter } from "../../ui/Router";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { parseFconfSettings } from "../../Fconf/Fconf"; import { parseFconfSettings } from "../../Fconf/Fconf";
import { isScriptFilename } from "../../Script/ScriptHelpersTS"; import { isScriptFilename } from "../../Script/ScriptHelpersTS";
@ -53,7 +53,7 @@ interface IProps {
filename: string; filename: string;
code: string; code: string;
player: IPlayer; player: IPlayer;
engine: IEngine; router: IRouter;
} }
/* /*
@ -103,7 +103,6 @@ export function Root(props: IProps): React.ReactElement {
} }
lastPosition = null; lastPosition = null;
// TODO(hydroflame): re-enable the tutorial.
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) { if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial //Make sure filename + code properly follow tutorial
if (filename !== "n00dles.script") { if (filename !== "n00dles.script") {
@ -121,7 +120,7 @@ export function Root(props: IProps): React.ReactElement {
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) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts); server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
props.engine.loadTerminalContent(); props.router.toTerminal();
return iTutorialNextStep(); return iTutorialNextStep();
} }
} }
@ -160,7 +159,7 @@ export function Root(props: IProps): React.ReactElement {
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) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts); server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
props.engine.loadTerminalContent(); props.router.toTerminal();
return; return;
} }
} }
@ -173,7 +172,7 @@ export function Root(props: IProps): React.ReactElement {
for (let i = 0; i < server.textFiles.length; ++i) { for (let i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === filename) { if (server.textFiles[i].fn === filename) {
server.textFiles[i].write(code); server.textFiles[i].write(code);
props.engine.loadTerminalContent(); props.router.toTerminal();
return; return;
} }
} }
@ -183,7 +182,7 @@ export function Root(props: IProps): React.ReactElement {
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)"); dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)");
return; return;
} }
props.engine.loadTerminalContent(); props.router.toTerminal();
} }
function beautify(): void { function beautify(): void {
@ -308,7 +307,7 @@ export function Root(props: IProps): React.ReactElement {
} }
return ( return (
<div className="script-editor-wrapper"> <>
<div id="script-editor-filename-wrapper"> <div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag" className="noselect"> <p id="script-editor-filename-tag" className="noselect">
{" "} {" "}
@ -328,7 +327,7 @@ export function Root(props: IProps): React.ReactElement {
beforeMount={beforeMount} beforeMount={beforeMount}
onMount={onMount} onMount={onMount}
loading={<p>Loading script editor!</p>} loading={<p>Loading script editor!</p>}
height="80%" height="90%"
defaultLanguage="javascript" defaultLanguage="javascript"
defaultValue={code} defaultValue={code}
onChange={updateCode} onChange={updateCode}
@ -352,6 +351,6 @@ export function Root(props: IProps): React.ReactElement {
Netscript Documentation Netscript Documentation
</a> </a>
</div> </div>
</div> </>
); );
} }

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import clsx from "clsx"; import clsx from "clsx";
import { styled, useTheme, Theme, CSSObject } from "@mui/material/styles"; import { styled, Theme, CSSObject } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import MuiDrawer from "@mui/material/Drawer"; import MuiDrawer from "@mui/material/Drawer";
@ -15,8 +15,6 @@ import Typography from "@mui/material/Typography";
import Collapse from "@mui/material/Collapse"; import Collapse from "@mui/material/Collapse";
import Badge from "@mui/material/Badge"; import Badge from "@mui/material/Badge";
import { TTheme as BBTheme, colors } from "../../ui/React/Theme";
import ComputerIcon from "@mui/icons-material/Computer"; import ComputerIcon from "@mui/icons-material/Computer";
import LastPageIcon from "@mui/icons-material/LastPage"; // Terminal import LastPageIcon from "@mui/icons-material/LastPage"; // Terminal
import CreateIcon from "@mui/icons-material/Create"; // Create Script import CreateIcon from "@mui/icons-material/Create"; // Create Script
@ -44,7 +42,7 @@ import LiveHelpIcon from "@mui/icons-material/LiveHelp";
import ExpandLessIcon from "@mui/icons-material/ExpandLess"; import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { IEngine } from "../../IEngine"; import { IRouter, Page } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial"; import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial";
@ -56,9 +54,6 @@ import { inMission } from "../../Missions";
import { cinematicTextFlag } from "../../CinematicText"; import { cinematicTextFlag } from "../../CinematicText";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
import { FconfSettings } from "../../Fconf/FconfSettings"; import { FconfSettings } from "../../Fconf/FconfSettings";
import { Page, routing } from "../../ui/navigationTracking";
const drawerWidth = 240;
const openedMixin = (theme: Theme): CSSObject => ({ const openedMixin = (theme: Theme): CSSObject => ({
width: theme.spacing(31), width: theme.spacing(31),
@ -83,7 +78,6 @@ const closedMixin = (theme: Theme): CSSObject => ({
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open" })(({ theme, open }) => ({ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open" })(({ theme, open }) => ({
width: theme.spacing(31), width: theme.spacing(31),
flexShrink: 0,
whiteSpace: "nowrap", whiteSpace: "nowrap",
boxSizing: "border-box", boxSizing: "border-box",
...(open && { ...(open && {
@ -99,7 +93,7 @@ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open"
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
active: { active: {
borderLeft: "3px solid " + colors.primary, borderLeft: "3px solid " + theme.palette.primary.main,
}, },
listitem: {}, listitem: {},
}), }),
@ -107,7 +101,8 @@ const useStyles = makeStyles((theme: Theme) =>
interface IProps { interface IProps {
player: IPlayer; player: IPlayer;
engine: IEngine; router: IRouter;
page: Page;
} }
export function SidebarRoot(props: IProps): React.ReactElement { export function SidebarRoot(props: IProps): React.ReactElement {
@ -121,7 +116,6 @@ export function SidebarRoot(props: IProps): React.ReactElement {
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
const [activeTab, setActiveTab] = useState("Terminal");
const [hackingOpen, setHackingOpen] = useState(true); const [hackingOpen, setHackingOpen] = useState(true);
const [characterOpen, setCharacterOpen] = useState(true); const [characterOpen, setCharacterOpen] = useState(true);
const [worldOpen, setWorldOpen] = useState(true); const [worldOpen, setWorldOpen] = useState(true);
@ -173,108 +167,88 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const canBladeburner = !!(props.player.bladeburner as any); const canBladeburner = !!(props.player.bladeburner as any);
function clickTerminal(): void { function clickTerminal(): void {
setActiveTab("Terminal"); props.router.toTerminal();
props.engine.loadTerminalContent();
if (flashTerminal) iTutorialNextStep(); if (flashTerminal) iTutorialNextStep();
} }
function clickCreateScripts(): void { function clickCreateScripts(): void {
setActiveTab("CreateScripts"); props.router.toScriptEditor();
props.engine.loadScriptEditorContent();
} }
function clickStats(): void { function clickStats(): void {
setActiveTab("Stats"); props.router.toCharacterInfo();
props.engine.loadCharacterContent();
if (flashStats) iTutorialNextStep(); if (flashStats) iTutorialNextStep();
} }
function clickActiveScripts(): void { function clickActiveScripts(): void {
setActiveTab("ActiveScripts"); props.router.toActiveScripts();
props.engine.loadActiveScriptsContent();
if (flashActiveScripts) iTutorialNextStep(); if (flashActiveScripts) iTutorialNextStep();
} }
function clickCreateProgram(): void { function clickCreateProgram(): void {
setActiveTab("CreateProgram"); props.router.toCreateProgram();
props.engine.loadCreateProgramContent();
} }
function clickFactions(): void { function clickFactions(): void {
setActiveTab("Factions"); props.router.toFactions();
props.engine.loadFactionsContent();
} }
function clickAugmentations(): void { function clickAugmentations(): void {
setActiveTab("Augmentations"); props.router.toAugmentations();
props.engine.loadAugmentationsContent();
} }
function clickSleeves(): void { function clickSleeves(): void {
setActiveTab("Sleeves"); props.router.toSleeves();
props.engine.loadSleevesContent();
} }
function clickHacknet(): void { function clickHacknet(): void {
setActiveTab("Hacknet"); props.router.toHacknetNodes();
props.engine.loadHacknetNodesContent();
if (flashHacknet) iTutorialNextStep(); if (flashHacknet) iTutorialNextStep();
} }
function clickCity(): void { function clickCity(): void {
setActiveTab("City"); props.router.toCity();
props.engine.loadLocationContent();
if (flashCity) iTutorialNextStep(); if (flashCity) iTutorialNextStep();
} }
function clickTravel(): void { function clickTravel(): void {
setActiveTab("Travel"); props.router.toTravel();
props.engine.loadTravelContent();
} }
function clickJob(): void { function clickJob(): void {
setActiveTab("Job"); props.router.toJob();
props.engine.loadJobContent();
} }
function clickStockMarket(): void { function clickStockMarket(): void {
setActiveTab("StockMarket"); props.router.toStockMarket();
props.engine.loadStockMarketContent();
} }
function clickBladeburner(): void { function clickBladeburner(): void {
setActiveTab("Bladeburner"); props.router.toBladeburner();
props.engine.loadBladeburnerContent();
} }
function clickCorp(): void { function clickCorp(): void {
setActiveTab("Corp"); props.router.toCorporation();
props.engine.loadCorporationContent();
} }
function clickGang(): void { function clickGang(): void {
setActiveTab("Gang"); props.router.toGang();
props.engine.loadGangContent();
} }
function clickTutorial(): void { function clickTutorial(): void {
setActiveTab("Tutorial"); props.router.toTutorial();
props.engine.loadTutorialContent();
if (flashTutorial) iTutorialNextStep(); if (flashTutorial) iTutorialNextStep();
} }
function clickMilestones(): void { function clickMilestones(): void {
setActiveTab("Milestones"); props.router.toMilestones();
props.engine.loadMilestonesContent();
} }
function clickOptions(): void { function clickOptions(): void {
setActiveTab("Options"); props.router.toGameOptions();
props.engine.loadGameOptionsContent();
} }
function clickDev(): void { function clickDev(): void {
setActiveTab("Dev"); props.router.toDevMenu();
props.engine.loadDevMenuContent();
} }
useEffect(() => { useEffect(() => {
@ -324,7 +298,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
clickCreateProgram(); clickCreateProgram();
} else if (event.keyCode === KEY.F && event.altKey) { } else if (event.keyCode === KEY.F && event.altKey) {
// Overriden by Fconf // Overriden by Fconf
if (routing.isOn(Page.Terminal) && FconfSettings.ENABLE_BASH_HOTKEYS) { if (props.page == Page.Terminal && FconfSettings.ENABLE_BASH_HOTKEYS) {
return; return;
} }
event.preventDefault(); event.preventDefault();
@ -356,421 +330,427 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const toggleDrawer = (): void => setOpen((old) => !old); const toggleDrawer = (): void => setOpen((old) => !old);
return ( return (
<BBTheme> <Drawer open={open} anchor="left" variant="permanent">
<Drawer open={open} anchor="left" variant="permanent"> <ListItem classes={{ root: classes.listitem }} button onClick={toggleDrawer}>
<ListItem classes={{ root: classes.listitem }} button onClick={toggleDrawer}> <ListItemIcon>
{!open ? <ChevronRightIcon color={"primary"} /> : <ChevronLeftIcon color={"primary"} />}
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Bitburner v{CONSTANTS.Version}</Typography>} />
</ListItem>
<Divider />
<List>
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHackingOpen((old) => !old)}>
<ListItemIcon> <ListItemIcon>
{!open ? <ChevronRightIcon color={"primary"} /> : <ChevronLeftIcon color={"primary"} />} <ComputerIcon color={"primary"} />
</ListItemIcon> </ListItemIcon>
<ListItemText primary={<Typography color="primary">Bitburner v{CONSTANTS.Version}</Typography>} /> <ListItemText primary={<Typography color="primary">Hacking</Typography>} />
{hackingOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
</ListItem> </ListItem>
<Divider /> <Collapse in={hackingOpen} timeout="auto" unmountOnExit>
<List> <List>
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHackingOpen((old) => !old)}> <ListItem
<ListItemIcon> classes={{ root: classes.listitem }}
<ComputerIcon color={"primary"} /> button
</ListItemIcon> key={"Terminal"}
<ListItemText primary={<Typography color="primary">Hacking</Typography>} /> className={clsx({
{hackingOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />} [classes.active]: props.page === Page.Terminal,
</ListItem> })}
<Collapse in={hackingOpen} timeout="auto" unmountOnExit> onClick={clickTerminal}
<List> >
<ListItem <ListItemIcon>
classes={{ root: classes.listitem }} <LastPageIcon
button color={flashTerminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"}
key={"Terminal"} />
className={clsx({ </ListItemIcon>
[classes.active]: activeTab === "Terminal", <ListItemText>
})} <Typography color={flashTerminal ? "error" : props.page !== Page.Terminal ? "secondary" : "primary"}>
onClick={clickTerminal} Terminal
> </Typography>
<ListItemIcon> </ListItemText>
<LastPageIcon color={flashTerminal ? "error" : activeTab !== "Terminal" ? "secondary" : "primary"} /> </ListItem>
</ListItemIcon> <ListItem
<ListItemText> classes={{ root: classes.listitem }}
<Typography color={flashTerminal ? "error" : activeTab !== "Terminal" ? "secondary" : "primary"}> button
Terminal key={"Create Scripts"}
</Typography> className={clsx({
</ListItemText> [classes.active]: props.page === Page.CreateScript,
</ListItem> })}
<ListItem onClick={clickCreateScripts}
classes={{ root: classes.listitem }} >
button <ListItemIcon>
key={"Create Scripts"} <CreateIcon color={props.page !== Page.CreateScript ? "secondary" : "primary"} />
className={clsx({ </ListItemIcon>
[classes.active]: activeTab === "CreateScripts", <ListItemText>
})} <Typography color={props.page !== Page.CreateScript ? "secondary" : "primary"}>
onClick={clickCreateScripts} Create Script
> </Typography>
<ListItemIcon> </ListItemText>
<CreateIcon color={activeTab !== "CreateScripts" ? "secondary" : "primary"} /> </ListItem>
</ListItemIcon> <ListItem
<ListItemText> classes={{ root: classes.listitem }}
<Typography color={activeTab !== "CreateScripts" ? "secondary" : "primary"}>Create Script</Typography> button
</ListItemText> key={"Active Scripts"}
</ListItem> className={clsx({
<ListItem [classes.active]: props.page === Page.ActiveScripts,
classes={{ root: classes.listitem }} })}
button onClick={clickActiveScripts}
key={"Active Scripts"} >
className={clsx({ <ListItemIcon>
[classes.active]: activeTab === "ActiveScripts", <StorageIcon
})} color={flashActiveScripts ? "error" : props.page !== Page.ActiveScripts ? "secondary" : "primary"}
onClick={clickActiveScripts} />
> </ListItemIcon>
<ListItemIcon> <ListItemText>
<StorageIcon <Typography
color={flashActiveScripts ? "error" : activeTab !== "ActiveScripts" ? "secondary" : "primary"} color={flashActiveScripts ? "error" : props.page !== Page.ActiveScripts ? "secondary" : "primary"}
/>
</ListItemIcon>
<ListItemText>
<Typography
color={flashActiveScripts ? "error" : activeTab !== "ActiveScripts" ? "secondary" : "primary"}
>
Active Scripts
</Typography>
</ListItemText>
</ListItem>
{canCreateProgram && (
<ListItem
button
key={"Create Program"}
className={clsx({
[classes.active]: activeTab === "CreateProgram",
})}
onClick={clickCreateProgram}
> >
<ListItemIcon> Active Scripts
<Badge badgeContent={programCount > 0 ? programCount : undefined} color="error">
<BugReportIcon color={activeTab !== "CreateProgram" ? "secondary" : "primary"} />
</Badge>
</ListItemIcon>
<ListItemText>
<Typography color={activeTab !== "CreateProgram" ? "secondary" : "primary"}>
Create Program
</Typography>
</ListItemText>
</ListItem>
)}
</List>
</Collapse>
<Divider />
<ListItem classes={{ root: classes.listitem }} button onClick={() => setCharacterOpen((old) => !old)}>
<ListItemIcon>
<AccountBoxIcon color={"primary"} />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Character</Typography>} />
{characterOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
</ListItem>
<Collapse in={characterOpen} timeout="auto" unmountOnExit>
<ListItem
button
key={"Stats"}
className={clsx({
[classes.active]: activeTab === "Stats",
})}
onClick={clickStats}
>
<ListItemIcon>
<EqualizerIcon color={flashStats ? "error" : activeTab !== "Stats" ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={flashStats ? "error" : activeTab !== "Stats" ? "secondary" : "primary"}>
Stats
</Typography> </Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
{canOpenFactions && ( {canCreateProgram && (
<ListItem <ListItem
classes={{ root: classes.listitem }}
button button
key={"Factions"} key={"Create Program"}
className={clsx({ className={clsx({
[classes.active]: activeTab === "Factions", [classes.active]: props.page === Page.CreateProgram,
})} })}
onClick={clickFactions} onClick={clickCreateProgram}
> >
<ListItemIcon> <ListItemIcon>
<Badge badgeContent={invitationsCount !== 0 ? invitationsCount : undefined} color="error"> <Badge badgeContent={programCount > 0 ? programCount : undefined} color="error">
<ContactsIcon color={activeTab !== "Factions" ? "secondary" : "primary"} /> <BugReportIcon color={props.page !== Page.CreateProgram ? "secondary" : "primary"} />
</Badge> </Badge>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={activeTab !== "Factions" ? "secondary" : "primary"}>Factions</Typography> <Typography color={props.page !== Page.CreateProgram ? "secondary" : "primary"}>
Create Program
</Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
)} )}
{canOpenAugmentations && ( </List>
<ListItem </Collapse>
classes={{ root: classes.listitem }}
button
key={"Augmentations"}
className={clsx({
[classes.active]: activeTab === "Augmentations",
})}
onClick={clickAugmentations}
>
<ListItemIcon>
<Badge badgeContent={augmentationCount !== 0 ? augmentationCount : undefined} color="error">
<DoubleArrowIcon
style={{ transform: "rotate(-90deg)" }}
color={activeTab !== "Augmentations" ? "secondary" : "primary"}
/>
</Badge>
</ListItemIcon>
<ListItemText>
<Typography color={activeTab !== "Augmentations" ? "secondary" : "primary"}>Augmentations</Typography>
</ListItemText>
</ListItem>
)}
<ListItem
button
key={"Hacknet"}
className={clsx({
[classes.active]: activeTab === "Hacknet",
})}
onClick={clickHacknet}
>
<ListItemIcon>
<AccountTreeIcon color={flashHacknet ? "error" : activeTab !== "Hacknet" ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={flashHacknet ? "error" : activeTab !== "Hacknet" ? "secondary" : "primary"}>
Hacknet
</Typography>
</ListItemText>
</ListItem>
{canOpenSleeves && (
<ListItem
classes={{ root: classes.listitem }}
button
key={"Sleeves"}
className={clsx({
[classes.active]: activeTab === "Sleeves",
})}
onClick={clickSleeves}
>
<ListItemIcon>
<PeopleAltIcon color={activeTab !== "Sleeves" ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={activeTab !== "Sleeves" ? "secondary" : "primary"}>Sleeves</Typography>
</ListItemText>
</ListItem>
)}
</Collapse>
<Divider /> <Divider />
<ListItem classes={{ root: classes.listitem }} button onClick={() => setWorldOpen((old) => !old)}> <ListItem classes={{ root: classes.listitem }} button onClick={() => setCharacterOpen((old) => !old)}>
<ListItemIcon>
<AccountBoxIcon color={"primary"} />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Character</Typography>} />
{characterOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
</ListItem>
<Collapse in={characterOpen} timeout="auto" unmountOnExit>
<ListItem
button
key={"Stats"}
className={clsx({
[classes.active]: props.page === Page.Stats,
})}
onClick={clickStats}
>
<ListItemIcon> <ListItemIcon>
<PublicIcon color={"primary"} /> <EqualizerIcon color={flashStats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"} />
</ListItemIcon> </ListItemIcon>
<ListItemText primary={<Typography color="primary">World</Typography>} /> <ListItemText>
{worldOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />} <Typography color={flashStats ? "error" : props.page !== Page.Stats ? "secondary" : "primary"}>
Stats
</Typography>
</ListItemText>
</ListItem> </ListItem>
<Collapse in={worldOpen} timeout="auto" unmountOnExit> {canOpenFactions && (
<ListItem <ListItem
classes={{ root: classes.listitem }}
button button
key={"City"} key={"Factions"}
className={clsx({ className={clsx({
[classes.active]: activeTab === "City", [classes.active]: [Page.Factions, Page.Faction].includes(props.page),
})} })}
onClick={clickCity} onClick={clickFactions}
> >
<ListItemIcon> <ListItemIcon>
<LocationCityIcon color={flashCity ? "error" : activeTab !== "City" ? "secondary" : "primary"} /> <Badge badgeContent={invitationsCount !== 0 ? invitationsCount : undefined} color="error">
<ContactsIcon color={![Page.Factions, Page.Faction].includes(props.page) ? "secondary" : "primary"} />
</Badge>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={flashCity ? "error" : activeTab !== "City" ? "secondary" : "primary"}> <Typography color={![Page.Factions, Page.Faction].includes(props.page) ? "secondary" : "primary"}>
City Factions
</Typography> </Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
)}
{canOpenAugmentations && (
<ListItem <ListItem
classes={{ root: classes.listitem }}
button button
key={"Travel"} key={"Augmentations"}
className={clsx({ className={clsx({
[classes.active]: activeTab === "Travel", [classes.active]: props.page === Page.Augmentations,
})} })}
onClick={clickTravel} onClick={clickAugmentations}
> >
<ListItemIcon> <ListItemIcon>
<AirplanemodeActiveIcon color={activeTab !== "Travel" ? "secondary" : "primary"} /> <Badge badgeContent={augmentationCount !== 0 ? augmentationCount : undefined} color="error">
<DoubleArrowIcon
style={{ transform: "rotate(-90deg)" }}
color={props.page !== Page.Augmentations ? "secondary" : "primary"}
/>
</Badge>
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={activeTab !== "Travel" ? "secondary" : "primary"}>Travel</Typography> <Typography color={props.page !== Page.Augmentations ? "secondary" : "primary"}>
Augmentations
</Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
{canJob && ( )}
<ListItem <ListItem
classes={{ root: classes.listitem }} button
button key={"Hacknet"}
key={"Job"} className={clsx({
className={clsx({ [classes.active]: props.page === Page.Hacknet,
[classes.active]: activeTab === "Job", })}
})} onClick={clickHacknet}
onClick={clickJob} >
> <ListItemIcon>
<ListItemIcon> <AccountTreeIcon color={flashHacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"} />
<WorkIcon color={activeTab !== "Job" ? "secondary" : "primary"} /> </ListItemIcon>
</ListItemIcon> <ListItemText>
<ListItemText> <Typography color={flashHacknet ? "error" : props.page !== Page.Hacknet ? "secondary" : "primary"}>
<Typography color={activeTab !== "Job" ? "secondary" : "primary"}>Job</Typography> Hacknet
</ListItemText> </Typography>
</ListItem> </ListItemText>
)} </ListItem>
{canStockMarket && ( {canOpenSleeves && (
<ListItem <ListItem
classes={{ root: classes.listitem }} classes={{ root: classes.listitem }}
button button
key={"Stock Market"} key={"Sleeves"}
className={clsx({ className={clsx({
[classes.active]: activeTab === "StockMarket", [classes.active]: props.page === Page.Sleeves,
})} })}
onClick={clickStockMarket} onClick={clickSleeves}
> >
<ListItemIcon> <ListItemIcon>
<TrendingUpIcon color={activeTab !== "StockMarket" ? "secondary" : "primary"} /> <PeopleAltIcon color={props.page !== Page.Sleeves ? "secondary" : "primary"} />
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={activeTab !== "StockMarket" ? "secondary" : "primary"}>Stock Market</Typography> <Typography color={props.page !== Page.Sleeves ? "secondary" : "primary"}>Sleeves</Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
)} )}
{canBladeburner && ( </Collapse>
<ListItem
classes={{ root: classes.listitem }}
button
key={"Bladeburner"}
className={clsx({
[classes.active]: activeTab === "Bladeburner",
})}
onClick={clickBladeburner}
>
<ListItemIcon>
<FormatBoldIcon color={activeTab !== "Bladeburner" ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={activeTab !== "Bladeburner" ? "secondary" : "primary"}>Bladeburner</Typography>
</ListItemText>
</ListItem>
)}
{canCorporation && (
<ListItem
classes={{ root: classes.listitem }}
button
key={"Corp"}
className={clsx({
[classes.active]: activeTab === "Corp",
})}
onClick={clickCorp}
>
<ListItemIcon>
<BusinessIcon color={activeTab !== "Corp" ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={activeTab !== "Corp" ? "secondary" : "primary"}>Corp</Typography>
</ListItemText>
</ListItem>
)}
{canGang && (
<ListItem
classes={{ root: classes.listitem }}
button
key={"Gang"}
className={clsx({
[classes.active]: activeTab === "Gang",
})}
onClick={clickGang}
>
<ListItemIcon>
<SportsMmaIcon color={activeTab !== "Gang" ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={activeTab !== "Gang" ? "secondary" : "primary"}>Gang</Typography>
</ListItemText>
</ListItem>
)}
</Collapse>
<Divider /> <Divider />
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHelpOpen((old) => !old)}> <ListItem classes={{ root: classes.listitem }} button onClick={() => setWorldOpen((old) => !old)}>
<ListItemIcon>
<PublicIcon color={"primary"} />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">World</Typography>} />
{worldOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
</ListItem>
<Collapse in={worldOpen} timeout="auto" unmountOnExit>
<ListItem
button
key={"City"}
className={clsx({
[classes.active]: props.page === Page.City,
})}
onClick={clickCity}
>
<ListItemIcon> <ListItemIcon>
<LiveHelpIcon color={"primary"} /> <LocationCityIcon color={flashCity ? "error" : props.page !== Page.City ? "secondary" : "primary"} />
</ListItemIcon> </ListItemIcon>
<ListItemText primary={<Typography color="primary">Help</Typography>} /> <ListItemText>
{helpOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />} <Typography color={flashCity ? "error" : props.page !== Page.City ? "secondary" : "primary"}>
City
</Typography>
</ListItemText>
</ListItem> </ListItem>
<Collapse in={helpOpen} timeout="auto" unmountOnExit> <ListItem
button
key={"Travel"}
className={clsx({
[classes.active]: props.page === Page.Travel,
})}
onClick={clickTravel}
>
<ListItemIcon>
<AirplanemodeActiveIcon color={props.page !== Page.Travel ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={props.page !== Page.Travel ? "secondary" : "primary"}>Travel</Typography>
</ListItemText>
</ListItem>
{canJob && (
<ListItem <ListItem
classes={{ root: classes.listitem }}
button button
key={"Milestones"} key={"Job"}
className={clsx({ className={clsx({
[classes.active]: activeTab === "Milestones", [classes.active]: props.page === Page.Job,
})} })}
onClick={clickMilestones} onClick={clickJob}
> >
<ListItemIcon> <ListItemIcon>
<CheckIcon color={activeTab !== "Milestones" ? "secondary" : "primary"} /> <WorkIcon color={props.page !== Page.Job ? "secondary" : "primary"} />
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={activeTab !== "Milestones" ? "secondary" : "primary"}>Milestones</Typography> <Typography color={props.page !== Page.Job ? "secondary" : "primary"}>Job</Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
)}
{canStockMarket && (
<ListItem <ListItem
classes={{ root: classes.listitem }}
button button
key={"Tutorial"} key={"Stock Market"}
className={clsx({ className={clsx({
[classes.active]: activeTab === "Tutorial", [classes.active]: props.page === Page.StockMarket,
})} })}
onClick={clickTutorial} onClick={clickStockMarket}
> >
<ListItemIcon> <ListItemIcon>
<HelpIcon color={flashTutorial ? "error" : activeTab !== "Tutorial" ? "secondary" : "primary"} /> <TrendingUpIcon color={props.page !== Page.StockMarket ? "secondary" : "primary"} />
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={flashTutorial ? "error" : activeTab !== "Tutorial" ? "secondary" : "primary"}> <Typography color={props.page !== Page.StockMarket ? "secondary" : "primary"}>Stock Market</Typography>
Tutorial
</Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
)}
{canBladeburner && (
<ListItem <ListItem
classes={{ root: classes.listitem }}
button button
key={"Options"} key={"Bladeburner"}
className={clsx({ className={clsx({
[classes.active]: activeTab === "Options", [classes.active]: props.page === Page.Bladeburner,
})} })}
onClick={clickOptions} onClick={clickBladeburner}
> >
<ListItemIcon> <ListItemIcon>
<SettingsIcon color={activeTab !== "Options" ? "secondary" : "primary"} /> <FormatBoldIcon color={props.page !== Page.Bladeburner ? "secondary" : "primary"} />
</ListItemIcon> </ListItemIcon>
<ListItemText> <ListItemText>
<Typography color={activeTab !== "Options" ? "secondary" : "primary"}>Options</Typography> <Typography color={props.page !== Page.Bladeburner ? "secondary" : "primary"}>Bladeburner</Typography>
</ListItemText> </ListItemText>
</ListItem> </ListItem>
{process.env.NODE_ENV === "development" && ( )}
<ListItem {canCorporation && (
classes={{ root: classes.listitem }} <ListItem
button classes={{ root: classes.listitem }}
key={"Dev"} button
className={clsx({ key={"Corp"}
[classes.active]: activeTab === "Dev", className={clsx({
})} [classes.active]: props.page === Page.Corporation,
onClick={clickDev} })}
> onClick={clickCorp}
<ListItemIcon> >
<DeveloperBoardIcon color={activeTab !== "Dev" ? "secondary" : "primary"} /> <ListItemIcon>
</ListItemIcon> <BusinessIcon color={props.page !== Page.Corporation ? "secondary" : "primary"} />
<ListItemText> </ListItemIcon>
<Typography color={activeTab !== "Dev" ? "secondary" : "primary"}>Dev</Typography> <ListItemText>
</ListItemText> <Typography color={props.page !== Page.Corporation ? "secondary" : "primary"}>Corp</Typography>
</ListItem> </ListItemText>
)} </ListItem>
</Collapse> )}
</List> {canGang && (
</Drawer> <ListItem
</BBTheme> classes={{ root: classes.listitem }}
button
key={"Gang"}
className={clsx({
[classes.active]: props.page === Page.Gang,
})}
onClick={clickGang}
>
<ListItemIcon>
<SportsMmaIcon color={props.page !== Page.Gang ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={props.page !== Page.Gang ? "secondary" : "primary"}>Gang</Typography>
</ListItemText>
</ListItem>
)}
</Collapse>
<Divider />
<ListItem classes={{ root: classes.listitem }} button onClick={() => setHelpOpen((old) => !old)}>
<ListItemIcon>
<LiveHelpIcon color={"primary"} />
</ListItemIcon>
<ListItemText primary={<Typography color="primary">Help</Typography>} />
{helpOpen ? <ExpandLessIcon color={"primary"} /> : <ExpandMoreIcon color={"primary"} />}
</ListItem>
<Collapse in={helpOpen} timeout="auto" unmountOnExit>
<ListItem
button
key={"Milestones"}
className={clsx({
[classes.active]: props.page === Page.Milestones,
})}
onClick={clickMilestones}
>
<ListItemIcon>
<CheckIcon color={props.page !== Page.Milestones ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={props.page !== Page.Milestones ? "secondary" : "primary"}>Milestones</Typography>
</ListItemText>
</ListItem>
<ListItem
button
key={"Tutorial"}
className={clsx({
[classes.active]: props.page === Page.Tutorial,
})}
onClick={clickTutorial}
>
<ListItemIcon>
<HelpIcon color={flashTutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={flashTutorial ? "error" : props.page !== Page.Tutorial ? "secondary" : "primary"}>
Tutorial
</Typography>
</ListItemText>
</ListItem>
<ListItem
button
key={"Options"}
className={clsx({
[classes.active]: props.page === Page.Options,
})}
onClick={clickOptions}
>
<ListItemIcon>
<SettingsIcon color={props.page !== Page.Options ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={props.page !== Page.Options ? "secondary" : "primary"}>Options</Typography>
</ListItemText>
</ListItem>
{process.env.NODE_ENV === "development" && (
<ListItem
classes={{ root: classes.listitem }}
button
key={"Dev"}
className={clsx({
[classes.active]: props.page === Page.DevMenu,
})}
onClick={clickDev}
>
<ListItemIcon>
<DeveloperBoardIcon color={props.page !== Page.DevMenu ? "secondary" : "primary"} />
</ListItemIcon>
<ListItemText>
<Typography color={props.page !== Page.DevMenu ? "secondary" : "primary"}>Dev</Typography>
</ListItemText>
</ListItem>
)}
</Collapse>
</List>
</Drawer>
); );
} }

@ -21,7 +21,6 @@ import { dialogBoxCreate } from "../../utils/DialogBox";
import * as React from "react"; import * as React from "react";
export interface IProcessOrderRefs { export interface IProcessOrderRefs {
rerenderFn: () => void;
stockMarket: IStockMarket; stockMarket: IStockMarket;
symbolToStockMap: IMap<Stock>; symbolToStockMap: IMap<Stock>;
} }
@ -116,7 +115,6 @@ function executeOrder(order: Order, refs: IProcessOrderRefs): void {
// When orders are executed, the buying and selling functions shouldn't // When orders are executed, the buying and selling functions shouldn't
// emit popup dialog boxes. This options object configures the functions for that // emit popup dialog boxes. This options object configures the functions for that
const opts = { const opts = {
rerenderFn: refs.rerenderFn,
suppressDialog: true, suppressDialog: true,
}; };
@ -158,7 +156,6 @@ function executeOrder(order: Order, refs: IProcessOrderRefs): void {
{numeralWrapper.formatShares(Math.round(order.shares))} shares) {numeralWrapper.formatShares(Math.round(order.shares))} shares)
</>, </>,
); );
refs.rerenderFn();
return; return;
} }
} }

@ -1,4 +1,3 @@
import { buyStock, sellStock, shortStock, sellShort } from "./BuyingAndSelling";
import { IOrderBook } from "./IOrderBook"; import { IOrderBook } from "./IOrderBook";
import { IStockMarket } from "./IStockMarket"; import { IStockMarket } from "./IStockMarket";
import { Order } from "./Order"; import { Order } from "./Order";
@ -9,24 +8,23 @@ import { InitStockMetadata } from "./data/InitStockMetadata";
import { OrderTypes } from "./data/OrderTypes"; import { OrderTypes } from "./data/OrderTypes";
import { PositionTypes } from "./data/PositionTypes"; import { PositionTypes } from "./data/PositionTypes";
import { StockSymbols } from "./data/StockSymbols"; import { StockSymbols } from "./data/StockSymbols";
import { StockMarketRoot } from "./ui/Root";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { Player } from "../Player";
import { IMap } from "../types"; import { IMap } from "../types";
import { EventEmitter } from "../utils/EventEmitter"; import { EventEmitter } from "../utils/EventEmitter";
import { Page, routing } from ".././ui/navigationTracking";
import { numeralWrapper } from ".././ui/numeralFormat"; import { numeralWrapper } from ".././ui/numeralFormat";
import { dialogBoxCreate } from "../../utils/DialogBox"; import { dialogBoxCreate } from "../../utils/DialogBox";
import { Reviver } from "../../utils/JSONReviver"; import { Reviver } from "../../utils/JSONReviver";
import * as React from "react"; export let StockMarket: IStockMarket = {
import * as ReactDOM from "react-dom"; lastUpdate: 0,
Orders: {},
export let StockMarket: IStockMarket | IMap<any> = {}; // Maps full stock name -> Stock object storedCycles: 0,
ticksUntilCycle: 0,
} as IStockMarket; // Maps full stock name -> Stock object
export const SymbolToStockMap: IMap<Stock> = {}; // Maps symbol -> Stock object export const SymbolToStockMap: IMap<Stock> = {}; // Maps symbol -> Stock object
export function placeOrder( export function placeOrder(
@ -70,12 +68,10 @@ export function placeOrder(
// Process to see if it should be executed immediately // Process to see if it should be executed immediately
const processOrderRefs = { const processOrderRefs = {
rerenderFn: displayStockMarketContent,
stockMarket: StockMarket as IStockMarket, stockMarket: StockMarket as IStockMarket,
symbolToStockMap: SymbolToStockMap, symbolToStockMap: SymbolToStockMap,
}; };
processOrders(stock, order.type, order.pos, processOrderRefs); processOrders(stock, order.type, order.pos, processOrderRefs);
displayStockMarketContent();
return true; return true;
} }
@ -100,7 +96,6 @@ export function cancelOrder(params: ICancelOrderParams, workerScript: WorkerScri
for (let i = 0; i < stockOrders.length; ++i) { for (let i = 0; i < stockOrders.length; ++i) {
if (order == stockOrders[i]) { if (order == stockOrders[i]) {
stockOrders.splice(i, 1); stockOrders.splice(i, 1);
displayStockMarketContent();
return true; return true;
} }
} }
@ -125,7 +120,6 @@ export function cancelOrder(params: ICancelOrderParams, workerScript: WorkerScri
params.pos === order.pos params.pos === order.pos
) { ) {
stockOrders.splice(i, 1); stockOrders.splice(i, 1);
displayStockMarketContent();
if (workerScript) { if (workerScript) {
workerScript.scriptRef.log("Successfully cancelled order: " + orderTxt); workerScript.scriptRef.log("Successfully cancelled order: " + orderTxt);
} }
@ -142,14 +136,24 @@ export function cancelOrder(params: ICancelOrderParams, workerScript: WorkerScri
export function loadStockMarket(saveString: string): void { export function loadStockMarket(saveString: string): void {
if (saveString === "") { if (saveString === "") {
StockMarket = {}; StockMarket = {
lastUpdate: 0,
Orders: {},
storedCycles: 0,
ticksUntilCycle: 0,
} as IStockMarket;
} else { } else {
StockMarket = JSON.parse(saveString, Reviver); StockMarket = JSON.parse(saveString, Reviver);
} }
} }
export function deleteStockMarket(): void { export function deleteStockMarket(): void {
StockMarket = {}; StockMarket = {
lastUpdate: 0,
Orders: {},
storedCycles: 0,
ticksUntilCycle: 0,
} as IStockMarket;
} }
export function initStockMarket(): void { export function initStockMarket(): void {
@ -269,8 +273,7 @@ export function processStockPrices(numCycles = 1): void {
const c = Math.random(); const c = Math.random();
const processOrderRefs = { const processOrderRefs = {
rerenderFn: displayStockMarketContent, stockMarket: StockMarket,
stockMarket: StockMarket as IStockMarket,
symbolToStockMap: SymbolToStockMap, symbolToStockMap: SymbolToStockMap,
}; };
if (c < chc) { if (c < chc) {
@ -301,48 +304,11 @@ export function processStockPrices(numCycles = 1): void {
// Shares required for price movement gradually approaches max over time // Shares required for price movement gradually approaches max over time
stock.shareTxUntilMovement = Math.min(stock.shareTxUntilMovement + 10, stock.shareTxForMovement); stock.shareTxUntilMovement = Math.min(stock.shareTxUntilMovement + 10, stock.shareTxForMovement);
} }
displayStockMarketContent();
} }
let stockMarketContainer: HTMLElement | null = null; export function initStockMarketFnForReact(): void {
function setStockMarketContainer(): void {
stockMarketContainer = document.getElementById("generic-react-container");
document.removeEventListener("DOMContentLoaded", setStockMarketContainer);
}
document.addEventListener("DOMContentLoaded", setStockMarketContainer);
function initStockMarketFnForReact(): void {
initStockMarket(); initStockMarket();
initSymbolToStockMap(); initSymbolToStockMap();
} }
const eventEmitterForUiReset = new EventEmitter(); export const eventEmitterForUiReset = new EventEmitter();
export function displayStockMarketContent(): void {
if (!routing.isOn(Page.StockMarket)) {
return;
}
eventEmitterForUiReset.emitEvent();
if (stockMarketContainer instanceof HTMLElement) {
const castedStockMarket = StockMarket as IStockMarket;
ReactDOM.render(
<StockMarketRoot
buyStockLong={buyStock}
buyStockShort={shortStock}
cancelOrder={cancelOrder}
eventEmitterForReset={eventEmitterForUiReset}
initStockMarket={initStockMarketFnForReact}
p={Player}
placeOrder={placeOrder}
sellStockLong={sellStock}
sellStockShort={sellShort}
stockMarket={castedStockMarket}
/>,
stockMarketContainer,
);
}
}

@ -1,82 +0,0 @@
/**
* Root React component for the Stock Market UI
*/
import * as React from "react";
import { InfoAndPurchases } from "./InfoAndPurchases";
import { StockTickers } from "./StockTickers";
import { IStockMarket } from "../IStockMarket";
import { Stock } from "../Stock";
import { OrderTypes } from "../data/OrderTypes";
import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter";
type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = (
stock: Stock,
shares: number,
price: number,
ordType: OrderTypes,
posType: PositionTypes,
) => boolean;
type IProps = {
buyStockLong: txFn;
buyStockShort: txFn;
cancelOrder: (params: any) => void;
eventEmitterForReset?: EventEmitter;
initStockMarket: () => void;
p: IPlayer;
placeOrder: placeOrderFn;
sellStockLong: txFn;
sellStockShort: txFn;
stockMarket: IStockMarket;
};
type IState = {
rerenderFlag: boolean;
};
export class StockMarketRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
};
this.rerender = this.rerender.bind(this);
}
rerender(): void {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
};
});
}
render(): React.ReactNode {
return (
<div className="stock-market-container">
<InfoAndPurchases initStockMarket={this.props.initStockMarket} p={this.props.p} rerender={this.rerender} />
{this.props.p.hasWseAccount && (
<StockTickers
buyStockLong={this.props.buyStockLong}
buyStockShort={this.props.buyStockShort}
cancelOrder={this.props.cancelOrder}
eventEmitterForReset={this.props.eventEmitterForReset}
p={this.props.p}
placeOrder={this.props.placeOrder}
sellStockLong={this.props.sellStockLong}
sellStockShort={this.props.sellStockShort}
stockMarket={this.props.stockMarket}
/>
)}
</div>
);
}
}

@ -0,0 +1,67 @@
/**
* Root React component for the Stock Market UI
*/
import React, { useState, useEffect } from "react";
import { InfoAndPurchases } from "./InfoAndPurchases";
import { StockTickers } from "./StockTickers";
import { IStockMarket } from "../IStockMarket";
import { Stock } from "../Stock";
import { OrderTypes } from "../data/OrderTypes";
import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter";
type txFn = (stock: Stock, shares: number) => boolean;
export type placeOrderFn = (
stock: Stock,
shares: number,
price: number,
ordType: OrderTypes,
posType: PositionTypes,
) => boolean;
type IProps = {
buyStockLong: txFn;
buyStockShort: txFn;
cancelOrder: (params: any) => void;
eventEmitterForReset?: EventEmitter;
initStockMarket: () => void;
p: IPlayer;
placeOrder: placeOrderFn;
sellStockLong: txFn;
sellStockShort: txFn;
stockMarket: IStockMarket;
};
export function StockMarketRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
return (
<div className="stock-market-container">
<InfoAndPurchases initStockMarket={props.initStockMarket} p={props.p} rerender={rerender} />
{props.p.hasWseAccount && (
<StockTickers
buyStockLong={props.buyStockLong}
buyStockShort={props.buyStockShort}
cancelOrder={props.cancelOrder}
eventEmitterForReset={props.eventEmitterForReset}
p={props.p}
placeOrder={props.placeOrder}
sellStockLong={props.sellStockLong}
sellStockShort={props.sellStockShort}
stockMarket={props.stockMarket}
/>
)}
</div>
);
}

@ -1,7 +1,7 @@
import { TextFile } from "../TextFile"; import { TextFile } from "../TextFile";
import { Script } from "../Script/Script"; import { Script } from "../Script/Script";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IEngine } from "../IEngine"; import { IRouter } from "../ui/Router";
export class Output { export class Output {
text: string; text: string;
@ -56,10 +56,10 @@ export interface ITerminal {
startAnalyze(): void; startAnalyze(): void;
startBackdoor(player: IPlayer): void; startBackdoor(player: IPlayer): void;
startHack(player: IPlayer): void; startHack(player: IPlayer): void;
finishHack(player: IPlayer, cancelled?: boolean): void; finishHack(router: IRouter, player: IPlayer, cancelled?: boolean): void;
finishBackdoor(player: IPlayer, cancelled?: boolean): void; finishBackdoor(router: IRouter, player: IPlayer, cancelled?: boolean): void;
finishAnalyze(player: IPlayer, cancelled?: boolean): void; finishAnalyze(player: IPlayer, cancelled?: boolean): void;
finishAction(player: IPlayer, cancelled?: boolean): void; finishAction(router: IRouter, player: IPlayer, cancelled?: boolean): void;
getFilepath(filename: string): string; getFilepath(filename: string): string;
getFile(player: IPlayer, filename: string): Script | TextFile | string | null; getFile(player: IPlayer, filename: string): Script | TextFile | string | null;
getScript(player: IPlayer, filename: string): Script | null; getScript(player: IPlayer, filename: string): Script | null;
@ -70,11 +70,11 @@ export interface ITerminal {
runContract(player: IPlayer, name: string): void; runContract(player: IPlayer, name: string): void;
executeScanAnalyzeCommand(player: IPlayer, depth?: number, all?: boolean): void; executeScanAnalyzeCommand(player: IPlayer, depth?: number, all?: boolean): void;
connectToServer(player: IPlayer, server: string): void; connectToServer(player: IPlayer, server: string): void;
executeCommand(engine: IEngine, player: IPlayer, command: string): void; executeCommand(router: IRouter, player: IPlayer, command: string): void;
executeCommands(engine: IEngine, player: IPlayer, commands: string): void; executeCommands(router: IRouter, player: IPlayer, commands: string): void;
// If there was any changes, will return true, once. // If there was any changes, will return true, once.
pollChanges(): boolean; pollChanges(): boolean;
process(player: IPlayer, cycles: number): void; process(router: IRouter, player: IPlayer, cycles: number): void;
prestige(): void; prestige(): void;
getProgressText(): string; getProgressText(): string;
} }

@ -1,9 +1,8 @@
import { ITerminal, Output, Link, TTimer } from "./ITerminal"; import { ITerminal, Output, Link, TTimer } from "./ITerminal";
import { IEngine } from "../IEngine"; 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 { hackWorldDaemon } from "../RedPill";
import { Programs } from "../Programs/Programs"; import { Programs } from "../Programs/Programs";
import { CodingContractResult } from "../CodingContracts"; import { CodingContractResult } from "../CodingContracts";
@ -86,11 +85,11 @@ export class Terminal implements ITerminal {
// Excludes the trailing forward slash // Excludes the trailing forward slash
currDir = "/"; currDir = "/";
process(player: IPlayer, cycles: number): void { process(router: IRouter, player: IPlayer, cycles: number): void {
if (this.action === null) return; if (this.action === null) return;
this.action.timeLeft -= (CONSTANTS._idleSpeed * cycles) / 1000; this.action.timeLeft -= (CONSTANTS._idleSpeed * cycles) / 1000;
this.hasChanges = true; this.hasChanges = true;
if (this.action.timeLeft < 0) this.finishAction(player, false); if (this.action.timeLeft < 0) this.finishAction(router, player, false);
} }
pollChanges(): boolean { pollChanges(): boolean {
@ -138,7 +137,7 @@ export class Terminal implements ITerminal {
} }
// Complete the hack/analyze command // Complete the hack/analyze command
finishHack(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();
@ -156,7 +155,7 @@ export class Terminal implements ITerminal {
if (player.bitNodeN == null) { if (player.bitNodeN == null) {
player.bitNodeN = 1; player.bitNodeN = 1;
} }
hackWorldDaemon(player.bitNodeN); router.toBitVerse(false, false);
return; return;
} }
server.backdoorInstalled = true; server.backdoorInstalled = true;
@ -190,7 +189,7 @@ export class Terminal implements ITerminal {
} }
} }
finishBackdoor(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 ( if (
@ -200,7 +199,7 @@ export class Terminal implements ITerminal {
if (player.bitNodeN == null) { if (player.bitNodeN == null) {
player.bitNodeN = 1; player.bitNodeN = 1;
} }
hackWorldDaemon(player.bitNodeN); router.toBitVerse(false, false);
return; return;
} }
server.backdoorInstalled = true; server.backdoorInstalled = true;
@ -238,16 +237,16 @@ export class Terminal implements ITerminal {
} }
} }
finishAction(player: IPlayer, cancelled = false): void { finishAction(router: IRouter, player: IPlayer, cancelled = false): void {
if (this.action === null) { if (this.action === null) {
if (!cancelled) throw new Error("Finish action called when there was no action"); if (!cancelled) throw new Error("Finish action called when there was no action");
return; return;
} }
this.print(this.getProgressText()); this.print(this.getProgressText());
if (this.action.action === "h") { if (this.action.action === "h") {
this.finishHack(player, cancelled); this.finishHack(router, player, cancelled);
} else if (this.action.action === "b") { } else if (this.action.action === "b") {
this.finishBackdoor(player, cancelled); this.finishBackdoor(router, player, cancelled);
} else if (this.action.action === "a") { } else if (this.action.action === "a") {
this.finishAnalyze(player, cancelled); this.finishAnalyze(player, cancelled);
} }
@ -468,7 +467,7 @@ export class Terminal implements ITerminal {
} }
} }
executeCommands(engine: IEngine, player: IPlayer, commands: string): void { executeCommands(router: IRouter, player: IPlayer, commands: string): void {
// Sanitize input // Sanitize input
commands = commands.trim(); commands = commands.trim();
commands = commands.replace(/\s\s+/g, " "); // Replace all extra whitespace in command with a single space commands = commands.replace(/\s\s+/g, " "); // Replace all extra whitespace in command with a single space
@ -484,7 +483,7 @@ export class Terminal implements ITerminal {
const allCommands = ParseCommands(commands); const allCommands = ParseCommands(commands);
for (let i = 0; i < allCommands.length; i++) { for (let i = 0; i < allCommands.length; i++) {
this.executeCommand(engine, player, allCommands[i]); this.executeCommand(router, player, allCommands[i]);
} }
} }
@ -499,7 +498,7 @@ export class Terminal implements ITerminal {
this.clear(); this.clear();
} }
executeCommand(engine: IEngine, player: IPlayer, command: string): void { executeCommand(router: IRouter, player: IPlayer, command: string): void {
if (this.action !== null) { if (this.action !== null) {
this.error(`Cannot execute command (${command}) while an action is in progress`); this.error(`Cannot execute command (${command}) while an action is in progress`);
return; return;
@ -532,7 +531,7 @@ export class Terminal implements ITerminal {
break; break;
case iTutorialSteps.TerminalLs: case iTutorialSteps.TerminalLs:
if (commandArray.length === 1 && commandArray[0] == "ls") { if (commandArray.length === 1 && commandArray[0] == "ls") {
ls(this, engine, player, s, commandArray.slice(1)); ls(this, router, player, s, commandArray.slice(1));
iTutorialNextStep(); iTutorialNextStep();
} else { } else {
this.print("Bad command. Please follow the tutorial"); this.print("Bad command. Please follow the tutorial");
@ -540,7 +539,7 @@ export class Terminal implements ITerminal {
break; break;
case iTutorialSteps.TerminalScan: case iTutorialSteps.TerminalScan:
if (commandArray.length === 1 && commandArray[0] == "scan") { if (commandArray.length === 1 && commandArray[0] == "scan") {
scan(this, engine, player, s, commandArray.slice(1)); scan(this, router, player, s, commandArray.slice(1));
iTutorialNextStep(); iTutorialNextStep();
} else { } else {
this.print("Bad command. Please follow the tutorial"); this.print("Bad command. Please follow the tutorial");
@ -609,7 +608,7 @@ export class Terminal implements ITerminal {
break; break;
case iTutorialSteps.TerminalCreateScript: case iTutorialSteps.TerminalCreateScript:
if (commandArray.length == 2 && commandArray[0] == "nano" && commandArray[1] == "n00dles.script") { if (commandArray.length == 2 && commandArray[0] == "nano" && commandArray[1] == "n00dles.script") {
engine.loadScriptEditorContent("n00dles.script", ""); router.toScriptEditor("n00dles.script", "");
iTutorialNextStep(); iTutorialNextStep();
} else { } else {
this.print("Bad command. Please follow the tutorial"); this.print("Bad command. Please follow the tutorial");
@ -617,7 +616,7 @@ export class Terminal implements ITerminal {
break; break;
case iTutorialSteps.TerminalFree: case iTutorialSteps.TerminalFree:
if (commandArray.length == 1 && commandArray[0] == "free") { if (commandArray.length == 1 && commandArray[0] == "free") {
free(this, engine, player, s, commandArray.slice(1)); free(this, router, player, s, commandArray.slice(1));
iTutorialNextStep(); iTutorialNextStep();
} else { } else {
this.print("Bad command. Please follow the tutorial"); this.print("Bad command. Please follow the tutorial");
@ -625,7 +624,7 @@ export class Terminal implements ITerminal {
break; break;
case iTutorialSteps.TerminalRunScript: case iTutorialSteps.TerminalRunScript:
if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "n00dles.script") { if (commandArray.length == 2 && commandArray[0] == "run" && commandArray[1] == "n00dles.script") {
run(this, engine, player, s, commandArray.slice(1)); run(this, router, player, s, commandArray.slice(1));
iTutorialNextStep(); iTutorialNextStep();
} else { } else {
this.print("Bad command. Please follow the tutorial"); this.print("Bad command. Please follow the tutorial");
@ -662,7 +661,7 @@ export class Terminal implements ITerminal {
const commands: { const commands: {
[key: string]: ( [key: string]: (
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],
@ -713,7 +712,7 @@ export class Terminal implements ITerminal {
return; return;
} }
f(this, engine, player, s, commandArray.slice(1)); f(this, router, player, s, commandArray.slice(1));
} }
getProgressText(): string { getProgressText(): string {

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { parseAliasDeclaration, printAliases } from "../../Alias"; import { parseAliasDeclaration, printAliases } from "../../Alias";
export function alias( export function alias(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
export function analyze( export function analyze(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { Server } from "../../Server/Server"; import { Server } from "../../Server/Server";
@ -7,7 +7,7 @@ import { HacknetServer } from "../../Hacknet/HacknetServer";
export function backdoor( export function backdoor(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { listAllDarkwebItems, buyDarkwebItem } from "../../DarkWeb/DarkWeb"; import { listAllDarkwebItems, buyDarkwebItem } from "../../DarkWeb/DarkWeb";
@ -7,7 +7,7 @@ import { SpecialServerIps } from "../../Server/SpecialServerIps";
export function buy( export function buy(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { showMessage } from "../../Message/MessageHelpers"; import { showMessage } from "../../Message/MessageHelpers";
@ -8,7 +8,7 @@ import { showLiterature } from "../../Literature/LiteratureHelpers";
export function cat( export function cat(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
@ -7,7 +7,7 @@ import { evaluateDirectoryPath, removeTrailingSlash } from "../DirectoryHelpers"
export function cd( export function cd(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
@ -7,7 +7,7 @@ import { isScriptFilename } from "../../Script/ScriptHelpersTS";
export function check( export function check(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { getServerOnNetwork } from "../../Server/ServerHelpers"; import { getServerOnNetwork } from "../../Server/ServerHelpers";
export function connect( export function connect(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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/ScriptHelpersTS";
@ -8,7 +8,7 @@ import JSZip from "jszip";
export function download( export function download(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
export function expr( export function expr(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
export function free( export function free(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { Server } from "../../Server/Server"; import { Server } from "../../Server/Server";
export function hack( export function hack(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { TerminalHelpText, HelpTexts } from "../HelpText"; import { TerminalHelpText, HelpTexts } from "../HelpText";
export function help( export function help(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
export function home( export function home(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
export function hostname( export function hostname(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
export function ifconfig( export function ifconfig(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { killWorkerScript } from "../../Netscript/killWorkerScript"; import { killWorkerScript } from "../../Netscript/killWorkerScript";
export function kill( export function kill(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { killWorkerScript } from "../../Netscript/killWorkerScript"; import { killWorkerScript } from "../../Netscript/killWorkerScript";
import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter"; import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
export function killall(terminal: ITerminal, engine: IEngine, player: IPlayer, server: BaseServer): void { export function killall(terminal: ITerminal, router: IRouter, player: IPlayer, server: BaseServer): void {
for (let i = server.runningScripts.length - 1; i >= 0; --i) { for (let i = server.runningScripts.length - 1; i >= 0; --i) {
killWorkerScript(server.runningScripts[i], server.ip, false); killWorkerScript(server.runningScripts[i], server.ip, false);
} }

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { Message } from "../../Message/Message"; import { Message } from "../../Message/Message";
@ -7,7 +7,7 @@ import { getFirstParentDirectory, isValidDirectoryPath, evaluateDirectoryPath }
export function ls( export function ls(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,7 +1,7 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
export function lscpu(terminal: ITerminal, engine: IEngine, player: IPlayer): void { export function lscpu(terminal: ITerminal, router: IRouter, player: IPlayer): void {
terminal.print(player.getCurrentServer().cpuCores + " Core(s)"); terminal.print(player.getCurrentServer().cpuCores + " Core(s)");
} }

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
export function mem( export function mem(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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/ScriptHelpersTS";
@ -8,7 +8,7 @@ import { Script } from "../../Script/Script";
export function mv( export function mv(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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/ScriptHelpersTS";
@ -7,7 +7,7 @@ import { createFconf } from "../../Fconf/Fconf";
export function nano( export function nano(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],
@ -21,7 +21,7 @@ export function nano(
const filename = args[0] + ""; const filename = args[0] + "";
if (filename === ".fconf") { if (filename === ".fconf") {
const text = createFconf(); const text = createFconf();
engine.loadScriptEditorContent(filename, text); router.toScriptEditor(filename, text);
return; return;
} else if (isScriptFilename(filename)) { } else if (isScriptFilename(filename)) {
const filepath = terminal.getFilepath(filename); const filepath = terminal.getFilepath(filename);
@ -33,17 +33,17 @@ export function nano(
}`; }`;
} }
engine.loadScriptEditorContent(filepath, code); router.toScriptEditor(filepath, code);
} else { } else {
engine.loadScriptEditorContent(filepath, script.code); router.toScriptEditor(filepath, script.code);
} }
} else if (filename.endsWith(".txt")) { } else if (filename.endsWith(".txt")) {
const filepath = terminal.getFilepath(filename); const filepath = terminal.getFilepath(filename);
const txt = terminal.getTextFile(player, filename); const txt = terminal.getTextFile(player, filename);
if (txt == null) { if (txt == null) {
engine.loadScriptEditorContent(filepath); router.toScriptEditor(filepath);
} else { } else {
engine.loadScriptEditorContent(filepath, txt.text); router.toScriptEditor(filepath, txt.text);
} }
} else { } else {
terminal.error( terminal.error(

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
export function ps( export function ps(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { IReturnStatus } from "../../types"; import { IReturnStatus } from "../../types";
export function rm( export function rm(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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/ScriptHelpersTS";
@ -8,7 +8,7 @@ import { runProgram } from "./runProgram";
export function run( export function run(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],
@ -30,11 +30,11 @@ export function run(
// Check if its a script or just a program/executable // Check if its a script or just a program/executable
if (isScriptFilename(executableName)) { if (isScriptFilename(executableName)) {
runScript(terminal, engine, player, server, args); runScript(terminal, router, player, server, args);
} else if (executableName.endsWith(".cct")) { } else if (executableName.endsWith(".cct")) {
terminal.runContract(player, executableName); terminal.runContract(player, executableName);
} else { } else {
runProgram(terminal, engine, player, server, args); runProgram(terminal, router, player, server, args);
} }
} }
} }

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { Programs } from "../../Programs/Programs"; import { Programs } from "../../Programs/Programs";
export function runProgram( export function runProgram(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],
@ -31,6 +31,7 @@ export function runProgram(
for (const program of Object.values(Programs)) { for (const program of Object.values(Programs)) {
if (program.name === programName) { if (program.name === programName) {
program.run( program.run(
router,
terminal, terminal,
player, player,
server, server,

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { logBoxCreate } from "../../../utils/LogBox"; import { logBoxCreate } from "../../../utils/LogBox";
@ -10,7 +10,7 @@ import * as libarg from "arg";
export function runScript( export function runScript(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
commandArgs: (string | number)[], commandArgs: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { getServerOnNetwork } from "../../Server/ServerHelpers"; import { getServerOnNetwork } from "../../Server/ServerHelpers";
export function scan( export function scan(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { Programs } from "../../Programs/Programs"; import { Programs } from "../../Programs/Programs";
export function scananalyze( export function scananalyze(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { Message } from "../../Message/Message"; import { Message } from "../../Message/Message";
@ -8,7 +8,7 @@ import { isScriptFilename } from "../../Script/ScriptHelpersTS";
export function scp( export function scp(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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";
export function sudov( export function sudov(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { logBoxCreate } from "../../../utils/LogBox"; import { logBoxCreate } from "../../../utils/LogBox";
@ -9,7 +9,7 @@ import { isScriptFilename } from "../../Script/ScriptHelpersTS";
export function tail( export function tail(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
commandArray: (string | number)[], commandArray: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { FconfSettings } from "../../Fconf/FconfSettings"; import { FconfSettings } from "../../Fconf/FconfSettings";
export function theme( export function theme(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { getRamUsageFromRunningScript } from "../../Script/RunningScriptHelpers"; import { getRamUsageFromRunningScript } from "../../Script/RunningScriptHelpers";
@ -7,7 +7,7 @@ import { numeralWrapper } from "../../ui/numeralFormat";
export function top( export function top(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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 { removeAlias } from "../../Alias"; import { removeAlias } from "../../Alias";
export function unalias( export function unalias(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; 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/ScriptHelpersTS";
export function wget( export function wget(
terminal: ITerminal, terminal: ITerminal,
engine: IEngine, router: IRouter,
player: IPlayer, player: IPlayer,
server: BaseServer, server: BaseServer,
args: (string | number)[], args: (string | number)[],

@ -8,7 +8,7 @@ import Paper from "@mui/material/Paper";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
import { ITerminal } from "../ITerminal"; import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { determineAllPossibilitiesForTabCompletion } from "../determineAllPossibilitiesForTabCompletion"; import { determineAllPossibilitiesForTabCompletion } from "../determineAllPossibilitiesForTabCompletion";
import { tabCompletion } from "../tabCompletion"; import { tabCompletion } from "../tabCompletion";
@ -17,21 +17,21 @@ import { FconfSettings } from "../../Fconf/FconfSettings";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
textfield: { textfield: {
margin: 0, margin: theme.spacing(0),
width: "100%", width: "100%",
}, },
input: { input: {
backgroundColor: "#000", backgroundColor: "#000",
}, },
nopadding: { nopadding: {
padding: 0, padding: theme.spacing(0),
}, },
preformatted: { preformatted: {
whiteSpace: "pre-wrap", whiteSpace: "pre-wrap",
margin: 0, margin: theme.spacing(0),
}, },
list: { list: {
padding: 0, padding: theme.spacing(0),
height: "100%", height: "100%",
}, },
}), }),
@ -39,11 +39,11 @@ const useStyles = makeStyles((theme: Theme) =>
interface IProps { interface IProps {
terminal: ITerminal; terminal: ITerminal;
engine: IEngine; router: IRouter;
player: IPlayer; player: IPlayer;
} }
export function TerminalInput({ terminal, engine, player }: IProps): React.ReactElement { export function TerminalInput({ terminal, router, player }: IProps): React.ReactElement {
const terminalInput = useRef<HTMLInputElement>(null); const terminalInput = useRef<HTMLInputElement>(null);
const [value, setValue] = useState(""); const [value, setValue] = useState("");
@ -147,7 +147,7 @@ export function TerminalInput({ terminal, engine, player }: IProps): React.React
if (ref) ref.focus(); if (ref) ref.focus();
// Cancel action // Cancel action
if (event.keyCode === KEY.C && event.ctrlKey) { if (event.keyCode === KEY.C && event.ctrlKey) {
terminal.finishAction(player, true); terminal.finishAction(router, player, true);
} }
} }
document.addEventListener("keydown", keyDown); document.addEventListener("keydown", keyDown);
@ -159,7 +159,7 @@ export function TerminalInput({ terminal, engine, player }: IProps): React.React
if (event.keyCode === KEY.ENTER && value !== "") { if (event.keyCode === KEY.ENTER && value !== "") {
event.preventDefault(); event.preventDefault();
terminal.print(`[${player.getCurrentServer().hostname} ~${terminal.cwd()}]> ${value}`); terminal.print(`[${player.getCurrentServer().hostname} ~${terminal.cwd()}]> ${value}`);
terminal.executeCommands(engine, player, value); terminal.executeCommands(router, player, value);
setValue(""); setValue("");
return; return;
} }

@ -8,7 +8,7 @@ import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { ITerminal, Output, Link } from "../ITerminal"; import { ITerminal, Output, Link } from "../ITerminal";
import { IEngine } from "../../IEngine"; import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { TerminalInput } from "./TerminalInput"; import { TerminalInput } from "./TerminalInput";
@ -42,11 +42,11 @@ const useStyles = makeStyles((theme: Theme) =>
interface IProps { interface IProps {
terminal: ITerminal; terminal: ITerminal;
engine: IEngine; router: IRouter;
player: IPlayer; player: IPlayer;
} }
export function TerminalRoot({ terminal, engine, player }: IProps): React.ReactElement { export function TerminalRoot({ terminal, router, player }: IProps): React.ReactElement {
const scrollHook = useRef<HTMLDivElement>(null); const scrollHook = useRef<HTMLDivElement>(null);
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -76,7 +76,7 @@ export function TerminalRoot({ terminal, engine, player }: IProps): React.ReactE
const classes = useStyles(); const classes = useStyles();
return ( return (
<> <>
<Box width="100%" minHeight="100vh" px={1} display={"flex"} alignItems={"flex-end"}> <Box width="100%" minHeight="100vh" display={"flex"} alignItems={"flex-end"}>
<List classes={{ root: classes.list }}> <List classes={{ root: classes.list }}>
{terminal.outputHistory.map((item, i) => { {terminal.outputHistory.map((item, i) => {
if (item instanceof Output) if (item instanceof Output)
@ -110,8 +110,8 @@ export function TerminalRoot({ terminal, engine, player }: IProps): React.ReactE
</List> </List>
<div ref={scrollHook}></div> <div ref={scrollHook}></div>
</Box> </Box>
<Box position="sticky" bottom={0} width="100%" px={1}> <Box position="sticky" bottom={0} width="100%" px={0}>
<TerminalInput player={player} engine={engine} terminal={terminal} /> <TerminalInput player={player} router={router} terminal={terminal} />
</Box> </Box>
</> </>
); );

@ -1,73 +1,44 @@
/** /**
* Game engine. Handles the main game loop as well as the main UI pages * Game engine. Handles the main game loop.
*
* TODO: Separate UI functionality into its own component
*/ */
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { Augmentations } from "./Augmentation/Augmentations"; import { Augmentations } from "./Augmentation/Augmentations";
import { initAugmentations, installAugmentations } from "./Augmentation/AugmentationHelpers"; import { initAugmentations } from "./Augmentation/AugmentationHelpers";
import { onExport } from "./ExportBonus";
import { AugmentationsRoot } from "./Augmentation/ui/Root";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner/Bladeburner"; import { Bladeburner } from "./Bladeburner/Bladeburner";
import { CharacterOverview } from "./ui/React/CharacterOverview";
import { generateRandomContract } from "./CodingContractGenerator"; import { generateRandomContract } from "./CodingContractGenerator";
import { initCompanies } from "./Company/Companies"; import { initCompanies } from "./Company/Companies";
import { Corporation } from "./Corporation/Corporation"; import { Corporation } from "./Corporation/Corporation";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { DevMenuRoot } from "./DevMenu";
import { Factions, initFactions } from "./Faction/Factions"; import { Factions, initFactions } from "./Faction/Factions";
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers"; import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
import { FactionList } from "./Faction/ui/FactionList"; import { GameRoot, Router } from "./ui/GameRoot";
import { Root as BladeburnerRoot } from "./Bladeburner/ui/Root";
import { Root as GangRoot } from "./Gang/ui/Root";
import { SidebarRoot } from "./Sidebar/ui/SidebarRoot";
import { CorporationRoot } from "./Corporation/ui/CorporationRoot";
import { ResleeveRoot } from "./PersonObjects/Resleeving/ui/ResleeveRoot";
import { GameOptionsRoot } from "./ui/React/GameOptionsRoot";
import { TTheme as Theme } from "./ui/React/Theme"; import { TTheme as Theme } from "./ui/React/Theme";
import { SleeveRoot } from "./PersonObjects/Sleeve/ui/SleeveRoot";
import { displayInfiltrationContent } from "./Infiltration/Helper";
import { import {
getHackingWorkRepGain, getHackingWorkRepGain,
getFactionSecurityWorkRepGain, getFactionSecurityWorkRepGain,
getFactionFieldWorkRepGain, getFactionFieldWorkRepGain,
} from "./PersonObjects/formulas/reputation"; } from "./PersonObjects/formulas/reputation";
import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers"; import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers";
import { HacknetRoot } from "./Hacknet/ui/HacknetRoot";
import { iTutorialStart } from "./InteractiveTutorial"; import { iTutorialStart } from "./InteractiveTutorial";
import { LocationName } from "./Locations/data/LocationNames";
import { LocationRoot } from "./Locations/ui/Root";
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers"; import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import { inMission, currMission } from "./Missions"; import { inMission, currMission } from "./Missions";
import { workerScripts } from "./Netscript/WorkerScripts";
import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker"; import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker";
import { Player } from "./Player"; import { Player } from "./Player";
import { prestigeAugmentation } from "./Prestige";
import { ProgramsRoot } from "./Programs/ui/ProgramsRoot";
import { saveObject, loadGame } from "./SaveObject"; import { saveObject, loadGame } from "./SaveObject";
import { Root as ScriptEditorRoot } from "./ScriptEditor/ui/Root"; import { initForeignServers } from "./Server/AllServers";
import { initForeignServers, AllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags"; import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { initSpecialServerIps } from "./Server/SpecialServerIps"; import { initSpecialServerIps } from "./Server/SpecialServerIps";
import { initSymbolToStockMap, processStockPrices, displayStockMarketContent } from "./StockMarket/StockMarket"; import { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket";
import { MilestonesRoot } from "./Milestones/ui/MilestonesRoot";
import { TerminalRoot } from "./Terminal/ui/TerminalRoot";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import { TutorialRoot } from "./Tutorial/ui/TutorialRoot";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve"; import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import { CharacterInfo } from "./ui/CharacterInfo";
import { Page, routing } from "./ui/navigationTracking";
import { Money } from "./ui/React/Money"; import { Money } from "./ui/React/Money";
import { Hashes } from "./ui/React/Hashes"; import { Hashes } from "./ui/React/Hashes";
import { Reputation } from "./ui/React/Reputation"; import { Reputation } from "./ui/React/Reputation";
import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
import { MainMenuLinks } from "./ui/MainMenu/Links";
import { dialogBoxCreate } from "../utils/DialogBox"; import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen"; import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen";
@ -78,367 +49,19 @@ import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
const Engine = { const Engine = {
// Clickable objects
Clickables: {
// Main menu buttons
saveMainMenuButton: null,
deleteMainMenuButton: null,
},
// Display objects // Display objects
// TODO-Refactor this into its own component // TODO-Refactor this into its own component
Display: { Display: {
// Generic page that most react loads into.
content: null,
// Main menu content
infiltrationContent: null,
workInProgressContent: null,
redPillContent: null,
cinematicTextContent: null,
missionContent: null, missionContent: null,
overview: null,
}, },
indexedDb: undefined, indexedDb: undefined,
// Time variables (milliseconds unix epoch time) // Time variables (milliseconds unix epoch time)
_lastUpdate: new Date().getTime(), _lastUpdate: new Date().getTime(),
_idleSpeed: 200, // Speed (in ms) at which the main loop is updated
loadTerminalContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.CharacterInfo);
ReactDOM.render(
<Theme>
<TerminalRoot terminal={Terminal} engine={this} player={Player} />
</Theme>,
Engine.Display.content,
);
MainMenuLinks.Stats.classList.add("active");
},
loadCharacterContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.CharacterInfo);
ReactDOM.render(<CharacterInfo player={Player} />, Engine.Display.content);
MainMenuLinks.Stats.classList.add("active");
},
loadScriptEditorContent: function (filename = "", code = "") {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.ScriptEditor);
MainMenuLinks.ScriptEditor.classList.add("active");
ReactDOM.render(
<ScriptEditorRoot filename={filename} code={code} player={Player} engine={this} />,
Engine.Display.content,
);
},
loadActiveScriptsContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.ActiveScripts);
MainMenuLinks.ActiveScripts.classList.add("active");
ReactDOM.render(<ActiveScriptsRoot p={Player} workerScripts={workerScripts} />, Engine.Display.content);
},
loadHacknetNodesContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.HacknetNodes);
MainMenuLinks.HacknetNodes.classList.add("active");
ReactDOM.render(<HacknetRoot player={Player} />, Engine.Display.content);
},
loadCreateProgramContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.CreateProgram);
MainMenuLinks.CreateProgram.classList.add("active");
ReactDOM.render(
<Theme>
<ProgramsRoot player={Player} />
</Theme>,
Engine.Display.content,
);
},
loadFactionsContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Factions);
MainMenuLinks.Factions.classList.add("active");
ReactDOM.render(<FactionList player={Player} engine={this} />, Engine.Display.content);
},
// TODO reactify
loadFactionContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Faction);
},
loadAugmentationsContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Augmentations);
MainMenuLinks.Augmentations.classList.add("active");
function backup() {
saveObject.exportGame();
onExport(Player);
}
ReactDOM.render(
<AugmentationsRoot exportGameFn={backup} installAugmentationsFn={installAugmentations} />,
Engine.Display.content,
);
},
loadMilestonesContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Milestones);
MainMenuLinks.Milestones.classList.add("active");
ReactDOM.render(<MilestonesRoot player={Player} />, Engine.Display.content);
},
loadTutorialContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Tutorial);
MainMenuLinks.Tutorial.classList.add("active");
ReactDOM.render(<TutorialRoot />, Engine.Display.content);
},
loadDevMenuContent: function () {
Engine.hideAllContent();
if (process.env.NODE_ENV !== "development") {
throw new Error("Cannot create Dev Menu because you are not in a dev build");
}
Engine.Display.content.style.display = "block";
ReactDOM.render(<DevMenuRoot player={Player} engine={this} />, Engine.Display.content);
routing.navigateTo(Page.DevMenu);
MainMenuLinks.DevMenu.classList.add("active");
},
loadLocationContent: function (initiallyInCity = true) {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Location);
MainMenuLinks.City.classList.add("active");
ReactDOM.render(
<LocationRoot initiallyInCity={initiallyInCity} engine={Engine} p={Player} />,
Engine.Display.content,
);
},
loadTravelContent: function () {
// Same as loadLocationContent() except first set the location to the travel agency,
// and make sure that the 'City' main menu link doesnt become 'active'
Engine.hideAllContent();
Player.gotoLocation(LocationName.TravelAgency);
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Location);
MainMenuLinks.Travel.classList.add("active");
ReactDOM.render(<LocationRoot initiallyInCity={false} engine={Engine} p={Player} />, Engine.Display.content);
},
loadJobContent: function () {
// Same as loadLocationContent(), except first set the location to the job.
// Make sure that the 'City' main menu link doesnt become 'active'
if (Player.companyName == "") {
dialogBoxCreate(
"You do not currently have a job! You can visit various companies " + "in the city and try to find a job.",
);
return;
}
Engine.hideAllContent();
Player.gotoLocation(Player.companyName);
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Location);
MainMenuLinks.Job.classList.add("active");
ReactDOM.render(<LocationRoot initiallyInCity={false} engine={Engine} p={Player} />, Engine.Display.content);
},
// TODO reactify
loadWorkInProgressContent: function () {
Engine.hideAllContent();
const mainMenu = document.getElementById("mainmenu-container");
console.log("hiding loadWorkInProgressContent");
mainMenu.style.visibility = "hidden";
Engine.Display.workInProgressContent.style.display = "block";
console.log(Engine.Display.workInProgressContent);
routing.navigateTo(Page.WorkInProgress);
},
loadRedPillContent: function () {
Engine.hideAllContent();
const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden";
Engine.Display.redPillContent.style.display = "block";
routing.navigateTo(Page.RedPill);
},
// TODO reactify
loadCinematicTextContent: function () {
Engine.hideAllContent();
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden";
Engine.Display.cinematicTextContent.style.display = "block";
routing.navigateTo(Page.CinematicText);
},
// TODO reactify
loadInfiltrationContent: function (name, difficulty, maxLevel) {
Engine.hideAllContent();
const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden";
Engine.Display.infiltrationContent.style.display = "block";
routing.navigateTo(Page.Infiltration);
displayInfiltrationContent(this, Player, name, difficulty, maxLevel);
},
loadStockMarketContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.StockMarket);
MainMenuLinks.StockMarket.classList.add("active");
displayStockMarketContent();
},
loadGangContent: function () {
if (!Player.inGang()) return;
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Gang);
MainMenuLinks.Gang.classList.add("active");
ReactDOM.render(<GangRoot engine={this} gang={Player.gang} player={Player} />, Engine.Display.content);
},
loadMissionContent: function () {
Engine.hideAllContent();
document.getElementById("mainmenu-container").style.visibility = "hidden";
document.getElementById("character-overview").style.visibility = "hidden";
Engine.Display.missionContent.style.display = "block";
routing.navigateTo(Page.Mission);
},
loadCorporationContent: function () {
if (!(Player.corporation instanceof Corporation)) return;
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Corporation);
MainMenuLinks.Corporation.classList.add("active");
ReactDOM.render(<CorporationRoot corp={Player.corporation} player={Player} />, Engine.Display.content);
},
loadBladeburnerContent: function () {
if (!(Player.bladeburner instanceof Bladeburner)) return;
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Bladeburner);
MainMenuLinks.Bladeburner.classList.add("active");
ReactDOM.render(
<BladeburnerRoot bladeburner={Player.bladeburner} player={Player} engine={this} />,
Engine.Display.content,
);
},
loadSleevesContent: function () {
Engine.hideAllContent();
Engine.Display.content.style.display = "block";
routing.navigateTo(Page.Sleeves);
ReactDOM.render(<SleeveRoot player={Player} />, Engine.Display.content);
},
loadResleevingContent: function () {
Engine.hideAllContent();
routing.navigateTo(Page.Resleeves);
Engine.Display.content.style.display = "block";
MainMenuLinks.City.classList.add("active");
ReactDOM.render(<ResleeveRoot player={Player} />, Engine.Display.content);
},
loadGameOptionsContent: function () {
Engine.hideAllContent();
routing.navigateTo(Page.GameOptions);
Engine.Display.content.style.display = "block";
MainMenuLinks.City.classList.add("active");
ReactDOM.render(
<Theme>
<GameOptionsRoot
player={Player}
save={() => saveObject.saveGame(Engine.indexedDb)}
delete={() => saveObject.deleteGame(Engine.indexedDb)}
export={() => saveObject.exportGame()}
import={() => saveObject.importGame()}
forceKill={() => {
for (const hostname of Object.keys(AllServers)) {
AllServers[hostname].runningScripts = [];
}
dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page.");
}}
softReset={() => {
dialogBoxCreate("Soft Reset!");
prestigeAugmentation();
}}
/>
</Theme>,
Engine.Display.content,
);
},
// Helper function that hides all content
hideAllContent: function () {
Engine.Display.content.style.display = "none";
Engine.Display.content.scrollTop = 0;
ReactDOM.unmountComponentAtNode(Engine.Display.content);
Engine.Display.infiltrationContent.style.display = "none";
ReactDOM.unmountComponentAtNode(Engine.Display.infiltrationContent);
Engine.Display.workInProgressContent.style.display = "none";
Engine.Display.redPillContent.style.display = "none";
Engine.Display.cinematicTextContent.style.display = "none";
Engine.Display.missionContent.style.display = "none";
},
displayCharacterOverviewInfo: function () {
console.log("rendering");
ReactDOM.render(
<Theme>
<CharacterOverview player={Player} save={() => saveObject.saveGame(Engine.indexedDb)} />
</Theme>,
document.getElementById("character-overview"),
);
},
// Main Game Loop
idleTimer: function () {
// Get time difference
const _thisUpdate = new Date().getTime();
let diff = _thisUpdate - Engine._lastUpdate;
const offset = diff % Engine._idleSpeed;
// Divide this by cycle time to determine how many cycles have elapsed since last update
diff = Math.floor(diff / Engine._idleSpeed);
if (diff > 0) {
// Update the game engine by the calculated number of cycles
Engine._lastUpdate = _thisUpdate - offset;
Player.lastUpdate = _thisUpdate - offset;
Engine.updateGame(diff);
}
window.requestAnimationFrame(Engine.idleTimer);
},
updateGame: function (numCycles = 1) { updateGame: function (numCycles = 1) {
const time = numCycles * Engine._idleSpeed; const time = numCycles * CONSTANTS._idleSpeed;
if (Player.totalPlaytime == null) { if (Player.totalPlaytime == null) {
Player.totalPlaytime = 0; Player.totalPlaytime = 0;
} }
@ -452,7 +75,7 @@ const Engine = {
Player.playtimeSinceLastAug += time; Player.playtimeSinceLastAug += time;
Player.playtimeSinceLastBitnode += time; Player.playtimeSinceLastBitnode += time;
Terminal.process(Player, numCycles); Terminal.process(Router, Player, numCycles);
// Working // Working
if (Player.isWorking) { if (Player.isWorking) {
@ -600,7 +223,7 @@ const Engine = {
} }
if (Player.bladeburner instanceof Bladeburner) { if (Player.bladeburner instanceof Bladeburner) {
try { try {
Player.bladeburner.process(Player); Player.bladeburner.process(Router, Player);
} catch (e) { } catch (e) {
exceptionAlert("Exception caught in Bladeburner.process(): " + e); exceptionAlert("Exception caught in Bladeburner.process(): " + e);
} }
@ -617,42 +240,11 @@ const Engine = {
} }
}, },
/**
* Used in game when clicking on a main menu header (NOT used for initialization)
* @param open {boolean} Whether header is being opened or closed
* @param elems {HTMLElement[]} li Elements under header
* @param links {HTMLElement[]} a elements under header
*/
toggleMainMenuHeader: function (open, elems, links) {
for (var i = 0; i < elems.length; ++i) {
if (open) {
elems[i].style.opacity = 1;
elems[i].style.maxHeight = elems[i].scrollHeight + "px";
} else {
elems[i].style.opacity = 0;
elems[i].style.maxHeight = null;
}
}
for (var 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 = null;
links[i].style.pointerEvents = "none";
}
}
},
load: function (saveString) { load: function (saveString) {
// Load game from save or create new game // Load game from save or create new game
if (loadGame(saveString)) { if (loadGame(saveString)) {
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
Engine.setDisplayElements(); // Sets variables for important DOM elements Engine.setDisplayElements(); // Sets variables for important DOM elements
Engine.init(); // Initialize buttons, work, etc.
updateSourceFileFlags(Player); updateSourceFileFlags(Player);
initAugmentations(); // Also calls Player.reapplyAllAugmentations() initAugmentations(); // Also calls Player.reapplyAllAugmentations()
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
@ -664,7 +256,7 @@ const Engine = {
Engine._lastUpdate = new Date().getTime(); Engine._lastUpdate = new Date().getTime();
const lastUpdate = Player.lastUpdate; const lastUpdate = Player.lastUpdate;
const timeOffline = Engine._lastUpdate - lastUpdate; const timeOffline = Engine._lastUpdate - lastUpdate;
const numCyclesOffline = Math.floor(timeOffline / Engine._idleSpeed); const numCyclesOffline = Math.floor(timeOffline / CONSTANTS._idleSpeed);
let offlineReputation = 0; let offlineReputation = 0;
const offlineHackingIncome = (Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * 0.75; const offlineHackingIncome = (Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * 0.75;
@ -686,6 +278,7 @@ const Engine = {
} else { } else {
Player.work(numCyclesOffline); Player.work(numCyclesOffline);
} }
Player.focus = false;
} else { } else {
for (let i = 0; i < Player.factions.length; i++) { for (let i = 0; i < Player.factions.length; i++) {
const facName = Player.factions[i]; const facName = Player.factions[i];
@ -760,7 +353,7 @@ const Engine = {
} }
// Update total playtime // Update total playtime
var time = numCyclesOffline * Engine._idleSpeed; var time = numCyclesOffline * CONSTANTS._idleSpeed;
if (Player.totalPlaytime == null) { if (Player.totalPlaytime == null) {
Player.totalPlaytime = 0; Player.totalPlaytime = 0;
} }
@ -806,79 +399,34 @@ const Engine = {
ReactDOM.render( ReactDOM.render(
<Theme> <Theme>
<SidebarRoot engine={this} player={Player} /> <GameRoot terminal={Terminal} engine={this} player={Player} />
</Theme>, </Theme>,
document.getElementById("sidebar"), document.getElementById("mainmenu-container"),
); );
}, },
setDisplayElements: function () { setDisplayElements: function () {
Engine.Display.content = document.getElementById("generic-react-container");
Engine.Display.content.style.display = "none";
Engine.Display.missionContent = document.getElementById("mission-container"); Engine.Display.missionContent = document.getElementById("mission-container");
Engine.Display.missionContent.style.display = "none"; Engine.Display.missionContent.style.display = "none";
// Work In Progress
Engine.Display.workInProgressContent = document.getElementById("work-in-progress-container");
Engine.Display.workInProgressContent.style.display = "none";
// Red Pill / Hack World Daemon
Engine.Display.redPillContent = document.getElementById("red-pill-container");
Engine.Display.redPillContent.style.display = "none";
Engine.Display.infiltrationContent = document.getElementById("infiltration-container");
Engine.Display.infiltrationContent.style.display = "none";
// Cinematic Text
Engine.Display.cinematicTextContent = document.getElementById("cinematic-text-container");
Engine.Display.cinematicTextContent.style.display = "none";
Engine.Display.overview = document.getElementById("character-overview");
},
// Initialization
init: function () {
// Player was working cancel button
Engine.displayCharacterOverviewInfo();
if (Player.isWorking) {
var cancelButton = document.getElementById("work-in-progress-cancel-button");
cancelButton.addEventListener("click", function () {
if (Player.workType == CONSTANTS.WorkTypeFaction) {
Player.finishFactionWork(true);
} else if (Player.workType == CONSTANTS.WorkTypeCreateProgram) {
Player.finishCreateProgramWork(true);
} else if (Player.workType == CONSTANTS.WorkTypeStudyClass) {
Player.finishClass();
} else if (Player.workType == CONSTANTS.WorkTypeCrime) {
Player.finishCrime(true);
} else if (Player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
Player.finishWorkPartTime();
} else {
Player.finishWork(true);
}
});
const focusButton = document.getElementById("work-in-progress-something-else-button");
focusButton.style.visibility = "hidden";
const focusable = [CONSTANTS.WorkTypeFaction, CONSTANTS.WorkTypeCompanyPartTime, CONSTANTS.WorkTypeCompany];
if (focusable.includes(Player.workType)) {
focusButton.style.visibility = "visible";
focusButton.addEventListener("click", function () {
Player.stopFocusing();
});
}
Engine.loadWorkInProgressContent();
} else {
Engine.loadTerminalContent();
}
}, },
start: function () { start: function () {
// Run main loop // Get time difference
Engine.idleTimer(); const _thisUpdate = new Date().getTime();
let diff = _thisUpdate - Engine._lastUpdate;
const offset = diff % CONSTANTS._idleSpeed;
// Divide this by cycle time to determine how many cycles have elapsed since last update
diff = Math.floor(diff / CONSTANTS._idleSpeed);
if (diff > 0) {
// Update the game engine by the calculated number of cycles
Engine._lastUpdate = _thisUpdate - offset;
Player.lastUpdate = _thisUpdate - offset;
Engine.updateGame(diff);
}
window.requestAnimationFrame(Engine.start);
}, },
}; };

Some files were not shown because too many files have changed in this diff Show More