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 {
> p {
width: 70%;
margin: 6px;
padding: 4px;
}

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

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

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

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

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

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

@ -15,6 +15,7 @@ import { Skill } from "./Skill";
import { City } from "./City";
import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
@ -25,7 +26,7 @@ import { addOffset } from "../../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { hackWorldDaemon, redPillFlag } from "../RedPill";
import { redPillFlag } from "../RedPill";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { Settings } from "../Settings/Settings";
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) {
case ActionTypes["Contract"]:
case ActionTypes["Operation"]: {
@ -1338,7 +1339,7 @@ export class Bladeburner implements IBladeburner {
// Operation Daedalus
if (action.name === "Operation Daedalus") {
this.resetAction();
return hackWorldDaemon(player.bitNodeN);
return router.toBitVerse(false, false);
}
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.actionTimeToComplete <= 0) {
throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`);
@ -1555,7 +1556,7 @@ export class Bladeburner implements IBladeburner {
this.actionTimeOverflow = 0;
if (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
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
@ -1958,7 +1959,7 @@ export class Bladeburner implements IBladeburner {
this.randomEventCounter += getRandomInt(240, 600);
}
this.processAction(player, seconds);
this.processAction(router, player, seconds);
// Automation
if (this.automateEnabled) {

@ -3,6 +3,7 @@ import { City } from "./City";
import { Skill } from "./Skill";
import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript";
export interface IBladeburner {
@ -103,11 +104,11 @@ export interface IBladeburner {
completeOperation(success: boolean): void;
getActionObject(actionId: IActionIdentifier): IAction | null;
completeContract(success: boolean): void;
completeAction(player: IPlayer): void;
completeAction(router: IRouter, player: IPlayer): 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;
calculateMaxStamina(player: IPlayer): 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 { AllPages } from "./AllPages";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
import { use } from "../../ui/Context";
import { IBladeburner } from "../IBladeburner";
interface IProps {
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 (
<div className="bladeburner-container">
<div style={{ height: "60%", display: "block", position: "relative" }}>
@ -25,9 +24,9 @@ export function Root(props: IProps): React.ReactElement {
border: "1px solid white",
}}
>
<Stats bladeburner={props.bladeburner} player={props.player} engine={props.engine} />
<Stats bladeburner={props.bladeburner} player={player} router={router} />
</div>
<Console bladeburner={props.bladeburner} player={props.player} />
<Console bladeburner={props.bladeburner} player={player} />
</div>
<div
style={{
@ -39,7 +38,7 @@ export function Root(props: IProps): React.ReactElement {
position: "relative",
}}
>
<AllPages bladeburner={props.bladeburner} player={props.player} />
<AllPages bladeburner={props.bladeburner} player={player} />
</div>
</div>
);

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

@ -1,6 +1,7 @@
import { IPlayer } from "./PersonObjects/IPlayer";
import { Bladeburner } from "./Bladeburner/Bladeburner";
import { IEngine } from "./IEngine";
import { IRouter } from "./ui/Router";
import React from "react";
import { TTheme as Theme } from "./ui/React/Theme";
@ -24,6 +25,7 @@ import { TimeSkip } from "./DevMenu/ui/TimeSkip";
interface IProps {
player: IPlayer;
engine: IEngine;
router: IRouter;
}
export function DevMenuRoot(props: IProps): React.ReactElement {
@ -31,7 +33,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement {
<Theme>
<>
<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} />
<Factions 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 { Money } from "../../ui/React/Money";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { hackWorldDaemon } from "../../RedPill";
import { IRouter } from "../../ui/Router";
interface IProps {
player: IPlayer;
router: IRouter;
}
export function General(props: IProps): React.ReactElement {
@ -26,19 +27,19 @@ export function General(props: IProps): React.ReactElement {
}
function quickB1tFlum3(): void {
hackWorldDaemon(props.player.bitNodeN, true, true);
props.router.toBitVerse(true, true);
}
function b1tflum3(): void {
hackWorldDaemon(props.player.bitNodeN, true);
props.router.toBitVerse(true, false);
}
function quickHackW0r1dD43m0n(): void {
hackWorldDaemon(props.player.bitNodeN, false, true);
props.router.toBitVerse(false, true);
}
function hackW0r1dD43m0n(): void {
hackWorldDaemon(props.player.bitNodeN);
props.router.toBitVerse(false, false);
}
return (

@ -30,7 +30,7 @@ export function TimeSkip(props: IProps): React.ReactElement {
return (
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<h2>Sleeves</h2>
<h2>Time skip</h2>
</AccordionSummary>
<AccordionDetails>
<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 hasAugmentationPrereqs(aug: Augmentation): boolean;
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 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 { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../Constants";
import { Engine } from "../engine";
import { Faction } from "./Faction";
import { Factions } from "./Factions";
import { HackingMission, setInMission } from "../Missions";
@ -65,29 +60,6 @@ export function startHackingMission(faction) {
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
//specified Augmentation
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 {
dialogBoxCreate(
"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
*/
import * as React from "react";
import React, { useState } from "react";
import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
type IProps = {
faction: Faction;
p: IPlayer;
routeToMainPage: () => void;
};
type IState = {
rerenderFlag: boolean;
sortOrder: PurchaseAugmentationsOrderSetting;
};
const infoStyleMarkup = {
width: "70%",
};
export class AugmentationsPage extends React.Component<IProps, IState> {
export function AugmentationsPage(props: IProps): React.ReactElement {
const player = use.Player();
// Flag for whether the player has a gang with this faction
isPlayersGang: boolean;
constructor(props: IProps) {
super(props);
const isPlayersGang = player.inGang() && player.getGangName() === props.faction.name;
this.isPlayersGang = props.p.inGang() && props.p.getGangName() === props.faction.name;
const setRerender = useState(false)[1];
this.state = {
rerenderFlag: false,
sortOrder: PurchaseAugmentationsOrderSetting.Default,
};
this.rerender = this.rerender.bind(this);
function rerender(): void {
setRerender((old) => !old);
}
getAugs(): string[] {
if (this.isPlayersGang) {
function getAugs(): string[] {
if (isPlayersGang) {
const augs: string[] = [];
for (const augName in Augmentations) {
const aug = Augmentations[augName];
@ -57,25 +42,25 @@ export class AugmentationsPage extends React.Component<IProps, IState> {
return augs;
} else {
return this.props.faction.augmentations.slice();
return props.faction.augmentations.slice();
}
}
getAugsSorted(): string[] {
function getAugsSorted(): string[] {
switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: {
return this.getAugsSortedByCost();
return getAugsSortedByCost();
}
case PurchaseAugmentationsOrderSetting.Reputation: {
return this.getAugsSortedByReputation();
return getAugsSortedByReputation();
}
default:
return this.getAugsSortedByDefault();
return getAugsSortedByDefault();
}
}
getAugsSortedByCost(): string[] {
const augs = this.getAugs();
function getAugsSortedByCost(): string[] {
const augs = getAugs();
augs.sort((augName1, augName2) => {
const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2];
@ -89,8 +74,8 @@ export class AugmentationsPage extends React.Component<IProps, IState> {
return augs;
}
getAugsSortedByReputation(): string[] {
const augs = this.getAugs();
function getAugsSortedByReputation(): string[] {
const augs = getAugs();
augs.sort((augName1, augName2) => {
const aug1 = Augmentations[augName1],
aug2 = Augmentations[augName2];
@ -103,88 +88,70 @@ export class AugmentationsPage extends React.Component<IProps, IState> {
return augs;
}
getAugsSortedByDefault(): string[] {
return this.getAugs();
function getAugsSortedByDefault(): string[] {
return getAugs();
}
switchSortOrder(newOrder: PurchaseAugmentationsOrderSetting): void {
function switchSortOrder(newOrder: PurchaseAugmentationsOrderSetting): void {
Settings.PurchaseAugmentationsOrder = newOrder;
this.rerender();
rerender();
}
rerender(): void {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
};
});
}
const augs = getAugsSorted();
const purchasable = augs.filter(
(aug: string) =>
aug === AugmentationNames.NeuroFluxGovernor ||
(!player.augmentations.some((a) => a.name === aug) && !player.queuedAugmentations.some((a) => a.name === aug)),
);
render(): React.ReactNode {
const augs = this.getAugsSorted();
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 => {
return <PurchaseableAugmentation augName={aug} faction={props.faction} key={aug} p={player} rerender={rerender} />;
};
const purchaseableAugmentation = (aug: string): React.ReactNode => {
return (
<PurchaseableAugmentation
augName={aug}
faction={this.props.faction}
key={aug}
p={this.props.p}
rerender={this.rerender}
/>
);
};
const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
let ownedElem = <></>;
const owned = augs.filter((aug: string) => !purchasable.includes(aug));
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"}
/>
let ownedElem = <></>;
const owned = augs.filter((aug: string) => !purchasable.includes(aug));
if (owned.length !== 0) {
ownedElem = (
<>
<br />
{augListElems}
{ownedElem}
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
</div>
<h2>Purchased Augmentations</h2>
<p>This factions also offers these augmentations but you already own them.</p>
{owned.map((aug) => purchaseableAugmentation(aug))}
</>
);
}
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.
*/
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton";
import { IEngine } from "../../IEngine";
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
interface ICreateGangPopupProps {
popupId: string;
facName: string;
p: IPlayer;
engine: IEngine;
}
interface ICreateGangPopupProps {
popupId: string;
facName: string;
}
export function CreateGangPopup(props: ICreateGangPopupProps): React.ReactElement {
const combatGangText = "This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
export function CreateGangPopup(props: ICreateGangPopupProps): React.ReactElement {
const player = use.Player();
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 " +
"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 " +
"is not as important.";
function isHacking(): boolean {
return ["NiteSec", "The Black Hand"].includes(props.facName);
}
function isHacking(): boolean {
return ["NiteSec", "The Black Hand"].includes(props.facName);
}
function createGang(): void {
props.p.startGang(props.facName, isHacking());
removePopup(props.popupId);
props.engine.loadGangContent();
}
function createGang(): void {
player.startGang(props.facName, isHacking());
removePopup(props.popupId);
router.toGang();
}
function onKeyUp(event: React.KeyboardEvent): void {
if (event.keyCode === 13) createGang();
}
function onKeyUp(event: React.KeyboardEvent): void {
if (event.keyCode === 13) createGang();
}
return (
<>
Would you like to create a new Gang with {props.facName}?
<br/>
<br/>
Note that this will prevent you from creating a Gang with any other Faction until this BitNode is destroyed. It also resets your reputation with this faction.
<br/>
<br/>
{ (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.
<div className="popup-box-input-div" >
<StdButton onClick={createGang} onKeyUp={onKeyUp} text="Create Gang" style={{float: "right"}} autoFocus={true}/>
</div>
</>
);
}
return (
<>
Would you like to create a new Gang with {props.facName}?
<br />
<br />
Note that this will prevent you from creating a Gang with any other Faction until this BitNode is destroyed. It
also resets your reputation with this faction.
<br />
<br />
{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.
<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
* accessible factions
*/
import * as React from "react";
import React, { useState } from "react";
import { AugmentationsPage } from "./AugmentationsPage";
import { DonateOption } from "./DonateOption";
@ -11,30 +11,21 @@ import { Info } from "./Info";
import { Option } from "./Option";
import { CONSTANTS } from "../../Constants";
import { IEngine } from "../../IEngine";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { createSleevePurchasesFromCovenantPopup } from "../../PersonObjects/Sleeve/SleeveCovenantPurchases";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context";
import { CreateGangPopup } from "./CreateGangPopup";
type IProps = {
engine: IEngine;
initiallyOnAugmentationsPage?: boolean;
faction: Faction;
p: IPlayer;
faction: Faction | null;
startHackingMissionFn: (faction: Faction) => void;
};
type IState = {
rerenderFlag: boolean;
purchasingAugs: boolean;
};
// Info text for all options on the UI
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " + "faction reputation";
const hackingMissionInfo =
@ -72,89 +63,68 @@ const GangNames = [
"The Black Hand",
];
export class FactionRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
export function FactionRoot(props: IProps): React.ReactElement {
const faction = props.faction;
if (faction === null) throw new Error("Trying to render the Faction page with null faction");
this.state = {
rerenderFlag: false,
purchasingAugs: props.initiallyOnAugmentationsPage ? props.initiallyOnAugmentationsPage : false,
};
const player = use.Player();
const router = use.Router();
const [, setRerenderFlag] = useState(false);
const [purchasingAugs, setPurchasingAugs] = useState(false);
this.manageGang = this.manageGang.bind(this);
this.rerender = this.rerender.bind(this);
this.routeToMain = this.routeToMain.bind(this);
this.routeToPurchaseAugs = this.routeToPurchaseAugs.bind(this);
this.sleevePurchases = this.sleevePurchases.bind(this);
this.startFieldWork = this.startFieldWork.bind(this);
this.startHackingContracts = this.startHackingContracts.bind(this);
this.startHackingMission = this.startHackingMission.bind(this);
this.startSecurityWork = this.startSecurityWork.bind(this);
}
manageGang(): void {
function manageGang(faction: Faction): void {
// If player already has a gang, just go to the gang UI
if (this.props.p.inGang()) {
return this.props.engine.loadGangContent();
if (player.inGang()) {
return router.toGang();
}
const popupId = "create-gang-popup";
createPopup(popupId, CreateGangPopup, {
popupId: popupId,
facName: this.props.faction.name,
p: this.props.p,
engine: this.props.engine,
facName: faction.name,
});
}
rerender(): void {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
};
});
function rerender(): void {
setRerenderFlag((old) => !old);
}
// Route to the main faction page
routeToMain(): void {
this.setState({ purchasingAugs: false });
function routeToMain(): void {
setPurchasingAugs(false);
}
// Route to the purchase augmentation UI for this faction
routeToPurchaseAugs(): void {
this.setState({ purchasingAugs: true });
function routeToPurchaseAugs(): void {
setPurchasingAugs(true);
}
sleevePurchases(): void {
createSleevePurchasesFromCovenantPopup(this.props.p);
function sleevePurchases(): void {
createSleevePurchasesFromCovenantPopup(player);
}
startFieldWork(): void {
this.props.p.startFactionFieldWork(this.props.faction);
function startFieldWork(faction: Faction): void {
player.startFactionFieldWork(faction);
router.toWork();
}
startHackingContracts(): void {
this.props.p.startFactionHackWork(this.props.faction);
function startHackingContracts(faction: Faction): void {
player.startFactionHackWork(faction);
router.toWork();
}
startHackingMission(): void {
const fac = this.props.faction;
this.props.p.singularityStopWork();
this.props.engine.loadMissionContent();
this.props.startHackingMissionFn(fac);
function startHackingMission(faction: Faction): void {
player.singularityStopWork();
props.startHackingMissionFn(faction);
}
startSecurityWork(): void {
this.props.p.startFactionSecurityWork(this.props.faction);
function startSecurityWork(faction: Faction): void {
player.startFactionSecurityWork(faction);
router.toWork();
}
render(): React.ReactNode {
return this.state.purchasingAugs ? this.renderAugmentationsPage() : this.renderMainPage();
}
renderMainPage(): React.ReactNode {
const p = this.props.p;
const faction = this.props.faction;
function MainPage({ faction }: { faction: Faction }): React.ReactElement {
const p = player;
const factionInfo = faction.getInfo();
// 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">
<h1>{faction.name}</h1>
<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 && (
<Option buttonText={"Hacking Mission"} infoText={hackingMissionInfo} onClick={this.startHackingMission} />
<Option
buttonText={"Hacking Mission"}
infoText={hackingMissionInfo}
onClick={() => startHackingMission(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerHackingWork && (
<Option
buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo}
onClick={this.startHackingContracts}
onClick={() => startHackingContracts(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerFieldWork && (
<Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={this.startFieldWork} />
<Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={() => startFieldWork(faction)} />
)}
{!isPlayersGang && factionInfo.offerSecurityWork && (
<Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={this.startSecurityWork} />
<Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={() => startSecurityWork(faction)} />
)}
{!isPlayersGang && factionInfo.offersWork() && (
<DonateOption
faction={this.props.faction}
p={this.props.p}
rerender={this.rerender}
faction={faction}
p={player}
rerender={rerender}
favorToDonate={favorToDonate}
disabled={!canDonate}
/>
)}
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={this.routeToPurchaseAugs} />
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={routeToPurchaseAugs} />
{canPurchaseSleeves && (
<Option
buttonText={"Purchase & Upgrade Duplicate Sleeves"}
infoText={sleevePurchasesInfo}
onClick={this.sleevePurchases}
onClick={sleevePurchases}
/>
)}
</div>
);
}
renderAugmentationsPage(): React.ReactNode {
return (
<>
<AugmentationsPage faction={this.props.faction} p={this.props.p} routeToMainPage={this.routeToMain} />
</>
);
}
return purchasingAugs ? (
<AugmentationsPage faction={faction} routeToMainPage={routeToMain} />
) : (
<MainPage faction={faction} />
);
}

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

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

@ -7,7 +7,6 @@ import * as React from "react";
import { getNextNeurofluxLevel, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
import { PurchaseAugmentationPopup } from "./PurchaseAugmentationPopup";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../../Faction/Faction";
@ -28,63 +27,56 @@ type IProps = {
rerender: () => void;
};
export class PurchaseableAugmentation extends React.Component<IProps, any> {
aug: Augmentation;
export function PurchaseableAugmentation(props: IProps): React.ReactElement {
const aug = Augmentations[props.augName];
if (aug == null) throw new Error(`aug ${props.augName} does not exists`);
constructor(props: IProps) {
super(props);
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);
function getMoneyCost(): number {
return aug.baseCost * props.faction.getInfo().augmentationPriceMult;
}
getMoneyCost(): number {
return this.aug.baseCost * this.props.faction.getInfo().augmentationPriceMult;
function getRepCost(): number {
return aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
}
getRepCost(): number {
return this.aug.baseRepRequirement * this.props.faction.getInfo().augmentationRepRequirementMult;
}
handleClick(): void {
function handleClick(): void {
if (!Settings.SuppressBuyAugmentationConfirmation) {
const popupId = "purchase-augmentation-popup";
createPopup(popupId, PurchaseAugmentationPopup, {
aug: this.aug,
faction: this.props.faction,
player: this.props.p,
aug: aug,
faction: props.faction,
player: props.p,
rerender: props.rerender,
popupId: popupId,
});
} else {
purchaseAugmentation(this.aug, this.props.faction);
purchaseAugmentation(aug, props.faction);
props.rerender();
}
}
// Whether the player has the prerequisite Augmentations
hasPrereqs(): boolean {
return hasAugmentationPrereqs(this.aug);
function hasPrereqs(): boolean {
return hasAugmentationPrereqs(aug);
}
// Whether the player has enough rep for this Augmentation
hasReputation(): boolean {
return this.props.faction.playerReputation >= this.getRepCost();
function hasReputation(): boolean {
return props.faction.playerReputation >= getRepCost();
}
// Whether the player has this augmentations (purchased OR installed)
owned(): boolean {
function owned(): boolean {
let owned = false;
for (const queuedAug of this.props.p.queuedAugmentations) {
if (queuedAug.name === this.props.augName) {
for (const queuedAug of props.p.queuedAugmentations) {
if (queuedAug.name === props.augName) {
owned = true;
break;
}
}
for (const installedAug of this.props.p.augmentations) {
if (installedAug.name === this.props.augName) {
for (const installedAug of props.p.augmentations) {
if (installedAug.name === props.augName) {
owned = true;
break;
}
@ -93,96 +85,94 @@ export class PurchaseableAugmentation extends React.Component<IProps, any> {
return owned;
}
render(): React.ReactNode {
if (this.aug == null) {
console.error(
`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${this.props.augName}`,
);
return null;
}
const moneyCost = this.getMoneyCost();
const repCost = this.getRepCost();
// Determine UI properties
let disabled = 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>
if (aug == null) {
console.error(
`Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`,
);
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 { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IAscensionResult } from "./IAscensionResult";
export interface IGang {
facName: string;
@ -37,8 +38,9 @@ export interface IGang {
getWantedPenalty(): number;
calculatePower(): number;
killMember(member: GangMember): void;
ascendMember(member: GangMember, workerScript: WorkerScript): void;
ascendMember(member: GangMember, workerScript: WorkerScript): IAscensionResult;
getDiscount(): number;
getAllTaskNames(): string[];
getUpgradeCost(upg: GangMemberUpgrade): number;
toJSON(): any;
}

@ -2,20 +2,19 @@
* React Component for all the gang stuff.
*/
import React, { useState, useEffect } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { ManagementSubpage } from "./ManagementSubpage";
import { TerritorySubpage } from "./TerritorySubpage";
import { IEngine } from "../../IEngine";
import { use } from "../../ui/Context";
import { Factions } from "../../Faction/Factions";
import { Gang } from "../Gang";
import { displayFactionContent } from "../../Faction/FactionHelpers";
interface IProps {
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 setRerender = useState(false)[1];
@ -25,8 +24,7 @@ export function Root(props: IProps): React.ReactElement {
}, []);
function back(): void {
props.engine.loadFactionContent();
displayFactionContent(props.gang.facName);
router.toFaction(Factions[props.gang.facName]);
}
return (
@ -48,11 +46,7 @@ export function Root(props: IProps): React.ReactElement {
>
Gang Territory
</a>
{management ? (
<ManagementSubpage gang={props.gang} player={props.player} />
) : (
<TerritorySubpage gang={props.gang} />
)}
{management ? <ManagementSubpage gang={props.gang} player={player} /> : <TerritorySubpage gang={props.gang} />}
</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 { IEngine } from "../../IEngine";
import { use } from "../../ui/Context";
import React, { useState } from "react";
import Grid from "@mui/material/Grid";
import { Countdown } from "./Countdown";
@ -14,8 +13,6 @@ import { WireCuttingGame } from "./WireCuttingGame";
import { Victory } from "./Victory";
interface IProps {
Player: IPlayer;
Engine: IEngine;
StartingDifficulty: number;
Difficulty: number;
MaxLevel: number;
@ -40,6 +37,8 @@ const minigames = [
];
export function Game(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [level, setLevel] = useState(1);
const [stage, setStage] = useState(Stage.Countdown);
const [results, setResults] = useState("");
@ -89,12 +88,10 @@ export function Game(props: IProps): React.ReactElement {
pushResult(false);
// Kill the player immediately if they use automation, so
// it's clear they're not meant to
const damage = options?.automated ? props.Player.hp : props.StartingDifficulty * 3;
if (props.Player.takeDamage(damage)) {
const menu = document.getElementById("mainmenu-container");
if (menu === null) throw new Error("mainmenu-container not found");
menu.style.visibility = "visible";
props.Engine.loadLocationContent();
const damage = options?.automated ? player.hp : props.StartingDifficulty * 3;
if (player.takeDamage(damage)) {
router.toCity();
return;
}
setupNextGame();
}
@ -112,8 +109,6 @@ export function Game(props: IProps): React.ReactElement {
case Stage.Sell:
stageComponent = (
<Victory
Player={props.Player}
Engine={props.Engine}
StartingDifficulty={props.StartingDifficulty}
Difficulty={props.Difficulty}
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 { StdButton } from "../../ui/React/StdButton";
import Grid from "@mui/material/Grid";
interface IProps {
Player: IPlayer;
Engine: IEngine;
Location: string;
Difficulty: 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 React, { useState } from "react";
import { StdButton } from "../../ui/React/StdButton";
@ -7,23 +5,21 @@ import Grid from "@mui/material/Grid";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { use } from "../../ui/Context";
interface IProps {
Player: IPlayer;
Engine: IEngine;
StartingDifficulty: number;
Difficulty: number;
MaxLevel: number;
}
export function Victory(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [faction, setFaction] = useState("none");
function quitInfiltration(): void {
const menu = document.getElementById("mainmenu-container");
if (!menu) throw new Error("mainmenu-container somehow null");
menu.style.visibility = "visible";
props.Engine.loadLocationContent();
router.toCity();
}
const levelBonus = props.MaxLevel * Math.pow(1.01, props.MaxLevel);
@ -43,8 +39,8 @@ export function Victory(props: IProps): React.ReactElement {
BitNodeMultipliers.InfiltrationMoney;
function sell(): void {
props.Player.gainMoney(moneyGain);
props.Player.recordMoneySource(moneyGain, "infiltration");
player.gainMoney(moneyGain);
player.recordMoneySource(moneyGain, "infiltration");
quitInfiltration();
}
@ -70,7 +66,7 @@ export function Victory(props: IProps): React.ReactElement {
<option key={"none"} value={"none"}>
{"none"}
</option>
{props.Player.factions
{player.factions
.filter((f) => Factions[f].getInfo().offersWork())
.map((f) => (
<option key={f} value={f}>

@ -63,8 +63,6 @@ function iTutorialStart() {
ITutorial.stepIsDone[i] = false;
}
Engine.loadTerminalContent();
// Don't autosave during this interactive tutorial
Engine.Counters.autoSaveCounter = Infinity;
ITutorial.currStep = 0;
@ -120,7 +118,6 @@ function iTutorialEvaluateStep() {
switch (ITutorial.currStep) {
case iTutorialSteps.Start:
Engine.loadTerminalContent();
iTutorialSetText(
"Welcome to Bitburner, a cyberpunk-themed incremental RPG! " +
"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";
break;
case iTutorialSteps.GoToCharacterPage:
Engine.loadTerminalContent();
iTutorialSetText(
"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)",
@ -138,7 +134,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none";
break;
case iTutorialSteps.CharacterPage:
Engine.loadCharacterContent();
iTutorialSetText(
"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. ",
@ -146,7 +141,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.CharacterGoToTerminalPage:
Engine.loadCharacterContent();
iTutorialSetText(
"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.",
@ -154,7 +148,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none";
break;
case iTutorialSteps.TerminalIntro:
Engine.loadTerminalContent();
iTutorialSetText(
"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.",
@ -162,7 +155,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.TerminalHelp:
Engine.loadTerminalContent();
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> " +
"(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
break;
case iTutorialSteps.TerminalLs:
Engine.loadTerminalContent();
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, " +
"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
break;
case iTutorialSteps.TerminalScan:
Engine.loadTerminalContent();
iTutorialSetText(
" <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. " +
@ -189,7 +179,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalScanAnalyze1:
Engine.loadTerminalContent();
iTutorialSetText(
"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 " +
@ -201,7 +190,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalScanAnalyze2:
Engine.loadTerminalContent();
iTutorialSetText(
"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 " +
@ -211,7 +199,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalConnect:
Engine.loadTerminalContent();
iTutorialSetText(
"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 " +
@ -222,7 +209,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalAnalyze:
Engine.loadTerminalContent();
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 " +
"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
break;
case iTutorialSteps.TerminalNuke:
Engine.loadTerminalContent();
iTutorialSetText(
"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>, " +
@ -247,7 +232,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalManualHack:
Engine.loadTerminalContent();
iTutorialSetText(
"You now have root access! You can hack the server using the <code class='interactive-tutorial-command'>hack</code> command. " +
"Try doing that now.",
@ -255,7 +239,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalHackingMechanics:
Engine.loadTerminalContent();
iTutorialSetText(
"You are now attempting to hack the server. Performing a hack takes time and " +
"only has a certain percentage chance " +
@ -270,7 +253,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.TerminalCreateScript:
Engine.loadTerminalContent();
iTutorialSetText(
"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 " +
@ -282,7 +264,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalTypeScript:
Engine.loadScriptEditorContent("n00dles.script", "");
iTutorialSetText(
"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>" +
@ -297,7 +278,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js)
break;
case iTutorialSteps.TerminalFree:
Engine.loadTerminalContent();
iTutorialSetText(
"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 " +
@ -307,7 +287,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalRunScript:
Engine.loadTerminalContent();
iTutorialSetText(
"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>.",
@ -315,7 +294,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal commmand
break;
case iTutorialSteps.TerminalGoToActiveScriptsPage:
Engine.loadTerminalContent();
iTutorialSetText(
"Your script is now running! " +
"It will continuously run in the background and will automatically stop if " +
@ -329,7 +307,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none";
break;
case iTutorialSteps.ActiveScriptsPage:
Engine.loadActiveScriptsContent();
iTutorialSetText(
"This page displays information about all of your scripts that are " +
"running across every server. You can use this to gauge how well " +
@ -338,7 +315,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none";
break;
case iTutorialSteps.ActiveScriptsToTerminal:
Engine.loadTerminalContent();
iTutorialSetText(
"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 " +
@ -347,7 +323,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none"; // next step triggered by terminal command
break;
case iTutorialSteps.TerminalTailScript:
Engine.loadTerminalContent();
iTutorialSetText(
"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>" +
@ -361,7 +336,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "inline-block";
break;
case iTutorialSteps.GoToHacknetNodesPage:
Engine.loadTerminalContent();
iTutorialSetText(
"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 " +
@ -370,14 +344,12 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none";
break;
case iTutorialSteps.HacknetNodesIntroduction:
Engine.loadHacknetNodesContent();
iTutorialSetText(
"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)
break;
case iTutorialSteps.HacknetNodesGoToWorldPage:
Engine.loadHacknetNodesContent();
iTutorialSetText(
"You just purchased a Hacknet Node! This Hacknet Node will passively " +
"earn you money over time, both online and offline. When you get enough " +
@ -388,7 +360,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none";
break;
case iTutorialSteps.WorldDescription:
Engine.loadLocationContent();
iTutorialSetText(
"This page lists all of the different locations you can currently " +
"travel to. Each location has something that you can do. " +
@ -399,7 +370,6 @@ function iTutorialEvaluateStep() {
nextBtn.style.display = "none";
break;
case iTutorialSteps.TutorialPageInfo:
Engine.loadTutorialContent();
iTutorialSetText(
"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 " +

@ -3,418 +3,347 @@
*
* 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 { Location } from "../Location";
import { Locations } from "../Locations";
import { LocationName } from "../data/LocationNames";
import { IEngine } from "../../IEngine";
import { Companies } from "../../Company/Companies";
import { Company } from "../../Company/Company";
import { CompanyPosition } from "../../Company/CompanyPosition";
import { CompanyPositions } from "../../Company/CompanyPositions";
import * as posNames from "../../Company/data/companypositionnames";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context";
import { QuitJobPopup } from "../../Company/ui/QuitJobPopup";
type IProps = {
engine: IEngine;
locName: LocationName;
p: IPlayer;
};
type IState = {
employedHere: boolean;
};
const blockStyleMarkup = {
display: "block",
};
export class CompanyLocation extends React.Component<IProps, IState> {
export function CompanyLocation(props: IProps): React.ReactElement {
const p = use.Player();
const router = use.Router();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
/**
* 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
*/
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
* (if he has one)
*/
companyPosition: CompanyPosition | null = null;
const companyPosition = jobTitle ? CompanyPositions[jobTitle] : null;
/**
* Stores button styling that sets them all to block display
*/
btnStyle: any;
p.location = props.locName;
/**
* 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 {
function applyForAgentJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForAgentJob();
this.checkIfEmployedHere(true);
p.applyForAgentJob();
rerender();
}
applyForBusinessConsultantJob(e: React.MouseEvent<HTMLElement>): void {
function applyForBusinessConsultantJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForBusinessConsultantJob();
this.checkIfEmployedHere(true);
p.applyForBusinessConsultantJob();
rerender();
}
applyForBusinessJob(e: React.MouseEvent<HTMLElement>): void {
function applyForBusinessJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForBusinessJob();
this.checkIfEmployedHere(true);
p.applyForBusinessJob();
rerender();
}
applyForEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
function applyForEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForEmployeeJob();
this.checkIfEmployedHere(true);
p.applyForEmployeeJob();
rerender();
}
applyForItJob(e: React.MouseEvent<HTMLElement>): void {
function applyForItJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForItJob();
this.checkIfEmployedHere(true);
p.applyForItJob();
rerender();
}
applyForPartTimeEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
function applyForPartTimeEmployeeJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForPartTimeEmployeeJob();
this.checkIfEmployedHere(true);
p.applyForPartTimeEmployeeJob();
rerender();
}
applyForPartTimeWaiterJob(e: React.MouseEvent<HTMLElement>): void {
function applyForPartTimeWaiterJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForPartTimeWaiterJob();
this.checkIfEmployedHere(true);
p.applyForPartTimeWaiterJob();
rerender();
}
applyForSecurityJob(e: React.MouseEvent<HTMLElement>): void {
function applyForSecurityJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForSecurityJob();
this.checkIfEmployedHere(true);
p.applyForSecurityJob();
rerender();
}
applyForSoftwareConsultantJob(e: React.MouseEvent<HTMLElement>): void {
function applyForSoftwareConsultantJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForSoftwareConsultantJob();
this.checkIfEmployedHere(true);
p.applyForSoftwareConsultantJob();
rerender();
}
applyForSoftwareJob(e: React.MouseEvent<HTMLElement>): void {
function applyForSoftwareJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForSoftwareJob();
this.checkIfEmployedHere(true);
p.applyForSoftwareJob();
rerender();
}
applyForWaiterJob(e: React.MouseEvent<HTMLElement>): void {
function applyForWaiterJob(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
this.props.p.applyForWaiterJob();
this.checkIfEmployedHere(true);
p.applyForWaiterJob();
rerender();
}
checkIfEmployedHere(updateState = false): 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 {
function startInfiltration(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
const loc = this.location;
if (!loc.infiltrationData) {
console.error(`trying to start infiltration at ${this.props.locName} but the infiltrationData is null`);
return;
}
const loc = location;
if (!loc.infiltrationData)
throw new Error(`trying to start infiltration at ${props.locName} but the infiltrationData is null`);
this.props.engine.loadInfiltrationContent(
this.props.locName,
loc.infiltrationData.startingSecurityLevel,
loc.infiltrationData.maxClearanceLevel,
);
const data = loc.infiltrationData;
if (data == null) {
return;
}
router.toInfiltration(props.locName);
}
work(e: React.MouseEvent<HTMLElement>): void {
function work(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
const pos = this.companyPosition;
const pos = companyPosition;
if (pos instanceof CompanyPosition) {
if (pos.isPartTimeJob() || pos.isSoftwareConsultantJob() || pos.isBusinessConsultantJob()) {
this.props.p.startWorkPartTime(this.props.locName);
p.startWorkPartTime(props.locName);
} 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;
const popupId = `quit-job-popup`;
createPopup(popupId, QuitJobPopup, {
locName: this.props.locName,
company: this.company,
player: this.props.p,
onQuit: () => this.checkIfEmployedHere(true),
locName: props.locName,
company: company,
player: p,
onQuit: rerender,
popupId: popupId,
});
}
render(): React.ReactNode {
const isEmployedHere = this.jobTitle != null;
const favorGain = this.company.getFavorGain();
const isEmployedHere = jobTitle != null;
const favorGain = company.getFavorGain();
return (
<div>
{isEmployedHere && (
<div>
<p>Job Title: {this.jobTitle}</p>
<br />
<p style={blockStyleMarkup}>-------------------------</p>
<br />
<p className={"tooltip"}>
Company reputation: {Reputation(this.company.playerReputation)}
<span className={"tooltiptext"}>
You will earn {Favor(favorGain[0])} company favor upon resetting after installing Augmentations
</span>
</p>
<br />
<br />
<p style={blockStyleMarkup}>-------------------------</p>
<br />
<p className={"tooltip"}>
Company Favor: {Favor(this.company.favor)}
<span className={"tooltiptext"}>
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
on how much reputation you have with the comapny.
</span>
</p>
<br />
<br />
<p style={blockStyleMarkup}>-------------------------</p>
<br />
<StdButton onClick={this.work} text={"Work"} />
&nbsp;&nbsp;&nbsp;&nbsp;
<StdButton onClick={this.quit} text={"Quit"} />
</div>
)}
{this.company.hasAgentPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.AgentCompanyPositions[0]]}
onClick={this.applyForAgentJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply for Agent Job"}
/>
)}
{this.company.hasBusinessConsultantPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]]}
onClick={this.applyForBusinessConsultantJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply for Business Consultant Job"}
/>
)}
{this.company.hasBusinessPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.BusinessCompanyPositions[0]]}
onClick={this.applyForBusinessJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply for Business Job"}
/>
)}
{this.company.hasEmployeePositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[1]]}
onClick={this.applyForEmployeeJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply to be an Employee"}
/>
)}
{this.company.hasEmployeePositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[1]]}
onClick={this.applyForPartTimeEmployeeJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply to be a part-time Employee"}
/>
)}
{this.company.hasITPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.ITCompanyPositions[0]]}
onClick={this.applyForItJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply for IT Job"}
/>
)}
{this.company.hasSecurityPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.SecurityCompanyPositions[2]]}
onClick={this.applyForSecurityJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply for Security Job"}
/>
)}
{this.company.hasSoftwareConsultantPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]]}
onClick={this.applyForSoftwareConsultantJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply for Software Consultant Job"}
/>
)}
{this.company.hasSoftwarePositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.SoftwareCompanyPositions[0]]}
onClick={this.applyForSoftwareJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply for Software Job"}
/>
)}
{this.company.hasWaiterPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[0]]}
onClick={this.applyForWaiterJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply to be a Waiter"}
/>
)}
{this.company.hasWaiterPositions() && (
<ApplyToJobButton
company={this.company}
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[0]]}
onClick={this.applyForPartTimeWaiterJob}
p={this.props.p}
style={this.btnStyle}
text={"Apply to be a part-time Waiter"}
/>
)}
{this.location.infiltrationData != null && (
<StdButton onClick={this.startInfiltration} style={this.btnStyle} text={"Infiltrate Company"} />
)}
<br />
<br />
<br />
<br />
<br />
</div>
);
}
return (
<div>
{isEmployedHere && (
<div>
<p>Job Title: {jobTitle}</p>
<br />
<p style={{ display: "block" }}>-------------------------</p>
<br />
<p className={"tooltip"}>
Company reputation: {Reputation(company.playerReputation)}
<span className={"tooltiptext"}>
You will earn {Favor(favorGain[0])} company favor upon resetting after installing Augmentations
</span>
</p>
<br />
<br />
<p style={{ display: "block" }}>-------------------------</p>
<br />
<p className={"tooltip"}>
Company Favor: {Favor(company.favor)}
<span className={"tooltiptext"}>
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 on
how much reputation you have with the comapny.
</span>
</p>
<br />
<br />
<p style={{ display: "block" }}>-------------------------</p>
<br />
<StdButton onClick={work} text={"Work"} />
&nbsp;&nbsp;&nbsp;&nbsp;
<StdButton onClick={quit} text={"Quit"} />
</div>
)}
{company.hasAgentPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.AgentCompanyPositions[0]]}
onClick={applyForAgentJob}
p={p}
style={{ display: "block" }}
text={"Apply for Agent Job"}
/>
)}
{company.hasBusinessConsultantPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]]}
onClick={applyForBusinessConsultantJob}
p={p}
style={{ display: "block" }}
text={"Apply for Business Consultant Job"}
/>
)}
{company.hasBusinessPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.BusinessCompanyPositions[0]]}
onClick={applyForBusinessJob}
p={p}
style={{ display: "block" }}
text={"Apply for Business Job"}
/>
)}
{company.hasEmployeePositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[1]]}
onClick={applyForEmployeeJob}
p={p}
style={{ display: "block" }}
text={"Apply to be an Employee"}
/>
)}
{company.hasEmployeePositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[1]]}
onClick={applyForPartTimeEmployeeJob}
p={p}
style={{ display: "block" }}
text={"Apply to be a part-time Employee"}
/>
)}
{company.hasITPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.ITCompanyPositions[0]]}
onClick={applyForItJob}
p={p}
style={{ display: "block" }}
text={"Apply for IT Job"}
/>
)}
{company.hasSecurityPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.SecurityCompanyPositions[2]]}
onClick={applyForSecurityJob}
p={p}
style={{ display: "block" }}
text={"Apply for Security Job"}
/>
)}
{company.hasSoftwareConsultantPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]]}
onClick={applyForSoftwareConsultantJob}
p={p}
style={{ display: "block" }}
text={"Apply for Software Consultant Job"}
/>
)}
{company.hasSoftwarePositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.SoftwareCompanyPositions[0]]}
onClick={applyForSoftwareJob}
p={p}
style={{ display: "block" }}
text={"Apply for Software Job"}
/>
)}
{company.hasWaiterPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.MiscCompanyPositions[0]]}
onClick={applyForWaiterJob}
p={p}
style={{ display: "block" }}
text={"Apply to be a Waiter"}
/>
)}
{company.hasWaiterPositions() && (
<ApplyToJobButton
company={company}
entryPosType={CompanyPositions[posNames.PartTimeCompanyPositions[0]]}
onClick={applyForPartTimeWaiterJob}
p={p}
style={{ display: "block" }}
text={"Apply to be a part-time Waiter"}
/>
)}
{location.infiltrationData != null && (
<StdButton onClick={startInfiltration} style={{ display: "block" }} text={"Infiltrate Company"} />
)}
<br />
<br />
<br />
<br />
<br />
</div>
);
}

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

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

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

@ -10,28 +10,43 @@ import { TravelConfirmationPopup } from "./TravelConfirmationPopup";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
import { createPopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money";
import { WorldMap } from "../../ui/React/WorldMap";
import { dialogBoxCreate } from "../../../utils/DialogBox";
type IProps = {
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) {
travel();
travel(p, router, city);
return;
}
const popupId = `travel-confirmation`;
createPopup(popupId, TravelConfirmationPopup, {
player: p,
city: city,
travel: travel,
travel: () => travel(p, router, city),
popupId: popupId,
});
}
@ -45,7 +60,7 @@ function ASCIIWorldMap(props: IProps): React.ReactElement {
</p>
<WorldMap
currentCity={props.p.city}
onTravel={(city: CityName) => createTravelPopup(props.p, city, () => props.travel(city))}
onTravel={(city: CityName) => createTravelPopup(props.p, props.router, city)}
/>
</div>
);
@ -66,7 +81,7 @@ function ListWorldMap(props: IProps): React.ReactElement {
return (
<StdButton
key={city}
onClick={() => createTravelPopup(props.p, city, () => props.travel(match[1]))}
onClick={() => createTravelPopup(props.p, props.router, city as CityName)}
style={{ display: "block" }}
text={`Travel to ${city}`}
/>
@ -76,10 +91,15 @@ function ListWorldMap(props: IProps): React.ReactElement {
);
}
export function TravelAgencyLocation(props: IProps): React.ReactElement {
if (Settings.DisableASCIIArt) {
return <ListWorldMap p={props.p} travel={props.travel} />;
} else {
return <ASCIIWorldMap p={props.p} travel={props.travel} />;
}
export function TravelAgencyRoot(props: IProps): React.ReactElement {
return (
<>
<h1>Travel Agency</h1>
{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 { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { getServer } from "../../Server/ServerHelpers";
import { Server } from "../../Server/Server";
import { SpecialServerIps } from "../../Server/SpecialServerIps";
import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
import { use } from "../../ui/Context";
type IProps = {
loc: Location;
p: IPlayer;
};
export class UniversityLocation extends React.Component<IProps, any> {
/**
* Stores button styling that sets them all to block display
*/
btnStyle: any;
export function UniversityLocation(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
constructor(props: IProps) {
super(props);
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);
function calculateCost(): number {
const ip = SpecialServerIps.getIp(props.loc.name);
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;
return this.props.loc.costMult * discount;
return props.loc.costMult * discount;
}
take(stat: string): void {
const loc = this.props.loc;
this.props.p.startClass(this.calculateCost(), loc.expMult, stat);
function take(stat: string): void {
const loc = props.loc;
player.startClass(calculateCost(), loc.expMult, stat);
router.toWork();
}
study(): void {
this.take(CONSTANTS.ClassStudyComputerScience);
function study(): void {
take(CONSTANTS.ClassStudyComputerScience);
}
dataStructures(): void {
this.take(CONSTANTS.ClassDataStructures);
function dataStructures(): void {
take(CONSTANTS.ClassDataStructures);
}
networks(): void {
this.take(CONSTANTS.ClassNetworks);
function networks(): void {
take(CONSTANTS.ClassNetworks);
}
algorithms(): void {
this.take(CONSTANTS.ClassAlgorithms);
function algorithms(): void {
take(CONSTANTS.ClassAlgorithms);
}
management(): void {
this.take(CONSTANTS.ClassManagement);
function management(): void {
take(CONSTANTS.ClassManagement);
}
leadership(): void {
this.take(CONSTANTS.ClassLeadership);
function leadership(): void {
take(CONSTANTS.ClassLeadership);
}
render(): React.ReactNode {
const costMult: number = this.calculateCost();
const costMult: number = calculateCost();
const dataStructuresCost = CONSTANTS.ClassDataStructuresBaseCost * costMult;
const networksCost = CONSTANTS.ClassNetworksBaseCost * costMult;
const algorithmsCost = CONSTANTS.ClassAlgorithmsBaseCost * costMult;
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
const dataStructuresCost = CONSTANTS.ClassDataStructuresBaseCost * costMult;
const networksCost = CONSTANTS.ClassNetworksBaseCost * costMult;
const algorithmsCost = CONSTANTS.ClassAlgorithmsBaseCost * costMult;
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
const earnHackingExpTooltip = `Gain hacking experience!`;
const earnCharismaExpTooltip = `Gain charisma experience!`;
const earnHackingExpTooltip = `Gain hacking experience!`;
const earnCharismaExpTooltip = `Gain charisma experience!`;
return (
<div>
<StdButton
onClick={this.study}
style={this.btnStyle}
text={`Study Computer Science (free)`}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={this.dataStructures}
style={this.btnStyle}
text={
<>
Take Data Structures course (
<Money money={dataStructuresCost} player={this.props.p} /> / sec)
</>
}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={this.networks}
style={this.btnStyle}
text={
<>
Take Networks course (
<Money money={networksCost} player={this.props.p} /> / sec)
</>
}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={this.algorithms}
style={this.btnStyle}
text={
<>
Take Algorithms course (
<Money money={algorithmsCost} player={this.props.p} /> / sec)
</>
}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={this.management}
style={this.btnStyle}
text={
<>
Take Management course (
<Money money={managementCost} player={this.props.p} /> / sec)
</>
}
tooltip={earnCharismaExpTooltip}
/>
<StdButton
onClick={this.leadership}
style={this.btnStyle}
text={
<>
Take Leadership course (
<Money money={leadershipCost} player={this.props.p} /> / sec)
</>
}
tooltip={earnCharismaExpTooltip}
/>
</div>
);
}
return (
<div>
<StdButton
onClick={study}
style={{ display: "block" }}
text={`Study Computer Science (free)`}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={dataStructures}
style={{ display: "block" }}
text={
<>
Take Data Structures course (
<Money money={dataStructuresCost} player={player} /> / sec)
</>
}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={networks}
style={{ display: "block" }}
text={
<>
Take Networks course (
<Money money={networksCost} player={player} /> / sec)
</>
}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={algorithms}
style={{ display: "block" }}
text={
<>
Take Algorithms course (
<Money money={algorithmsCost} player={player} /> / sec)
</>
}
tooltip={earnHackingExpTooltip}
/>
<StdButton
onClick={management}
style={{ display: "block" }}
text={
<>
Take Management course (
<Money money={managementCost} player={player} /> / sec)
</>
}
tooltip={earnCharismaExpTooltip}
/>
<StdButton
onClick={leadership}
style={{ display: "block" }}
text={
<>
Take Leadership course (
<Money money={leadershipCost} player={player} /> / sec)
</>
}
tooltip={earnCharismaExpTooltip}
/>
</div>
);
}

@ -1,6 +1,4 @@
import { CONSTANTS } from "./Constants";
import { Engine } from "./engine";
import { displayFactionContent } from "./Faction/FactionHelpers";
import { Player } from "./Player";
import { dialogBoxCreate } from "../utils/DialogBox";
@ -1187,7 +1185,7 @@ HackingMission.prototype.process = function (numCycles = 1) {
});
// Update timer and check if player lost
this.time -= storedCycles * Engine._idleSpeed;
this.time -= storedCycles * CONSTANTS._idleSpeed;
if (this.time <= 0) {
this.finishMission(false);
return;
@ -1595,18 +1593,6 @@ HackingMission.prototype.finishMission = function (win) {
} else {
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 };

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

@ -121,8 +121,33 @@ export interface IPlayer {
bladeburner_analysis_mult: number;
bladeburner_success_chance_mult: number;
workRepGained: number;
createProgramName: string;
timeWorkedCreateProgram: number;
crimeType: string;
timeNeededToCompleteWork: number;
focus: boolean;
className: string;
currentWorkFactionName: string;
workType: string;
currentWorkFactionDescription: string;
timeWorked: number;
workMoneyGained: number;
workMoneyGainRate: number;
workRepGained: number;
workRepGainRate: number;
workHackExpGained: number;
workHackExpGainRate: number;
workStrExpGained: number;
workStrExpGainRate: number;
workDefExpGained: number;
workDefExpGainRate: number;
workDexExpGained: number;
workDexExpGainRate: number;
workAgiExpGained: number;
workAgiExpGainRate: number;
workChaExpGained: number;
workChaExpGainRate: number;
workMoneyLossRate: number;
// Methods
applyForAgentJob(sing?: boolean): boolean | void;
@ -209,4 +234,12 @@ export interface IPlayer {
receiveInvite(factionName: string): void;
updateSkillLevels(): void;
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 { determineCrimeSuccess } from "../../Crime/CrimeHelpers";
import { Crimes } from "../../Crime/Crimes";
import { Engine } from "../../engine";
import { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions";
import { displayFactionContent } from "../../Faction/FactionHelpers";
import { resetGangs } from "../../Gang/AllGangs";
import { hasHacknetServers } from "../../Hacknet/HacknetHelpers";
import { Cities } from "../../Locations/Cities";
@ -48,18 +46,12 @@ import Decimal from "decimal.js";
import { numeralWrapper } from "../../ui/numeralFormat";
import { MoneySourceTracker } from "../../utils/MoneySourceTracker";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { Reputation } from "../../ui/React/Reputation";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { ReputationRate } from "../../ui/React/ReputationRate";
import React from "react";
import ReactDOM from "react-dom";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
export function init() {
/* Initialize Player's home computer */
@ -518,8 +510,6 @@ export function resetWorkStatus(generalType, group, workType) {
this.currentWorkFactionDescription = "";
this.createProgramName = "";
this.className = "";
ReactDOM.unmountComponentAtNode(document.getElementById("work-in-progress-text"));
}
export function processWorkEarnings(numCycles = 1) {
@ -573,25 +563,6 @@ export function startWork(companyName) {
this.workMoneyGainRate = this.getWorkMoneyGain();
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() {
@ -607,11 +578,11 @@ export function work(numCycles) {
// Cap the number of cycles being processed to whatever would put you at
// the work time limit (8 hours)
var overMax = false;
if (this.timeWorked + Engine._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
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.processWorkEarnings(numCycles);
@ -622,63 +593,7 @@ export function work(numCycles) {
}
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);
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) {
@ -731,10 +646,7 @@ export function finishWork(cancelled, sing = false) {
dialogBoxCreate(content);
}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
Engine.loadLocationContent(false);
if (sing) {
var res =
@ -781,27 +693,17 @@ export function startWorkPartTime(companyName) {
this.workMoneyGainRate = this.getWorkMoneyGain();
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) {
//Cap the number of cycles being processed to whatever would put you at the
//work time limit (8 hours)
var overMax = false;
if (this.timeWorked + Engine._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer8Hours) {
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.processWorkEarnings(numCycles);
@ -810,60 +712,6 @@ export function workPartTime(numCycles) {
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
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) {
@ -894,10 +742,7 @@ export function finishWorkPartTime(sing = false) {
dialogBoxCreate(content);
}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
Engine.loadLocationContent(false);
if (sing) {
var res =
"You worked for " +
@ -928,17 +773,11 @@ export function finishWorkPartTime(sing = false) {
}
export function startFocusing() {
const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "hidden";
this.focus = true;
Engine.loadWorkInProgressContent();
}
export function stopFocusing() {
const mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.focus = false;
Engine.loadTerminalContent();
}
/* Working for Faction */
@ -957,24 +796,6 @@ export function startFactionWork(faction) {
this.currentWorkFactionName = faction.name;
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) {
@ -1046,11 +867,11 @@ export function workForFaction(numCycles) {
//Cap the number of cycles being processed to whatever would put you at limit (20 hours)
var overMax = false;
if (this.timeWorked + Engine._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer20Hours) {
if (this.timeWorked + CONSTANTS._idleSpeed * numCycles >= CONSTANTS.MillisecondsPer20Hours) {
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);
@ -1058,44 +879,6 @@ export function workForFaction(numCycles) {
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer20Hours) {
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) {
@ -1125,13 +908,8 @@ export function finishFactionWork(cancelled, sing = false) {
);
}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
Engine.loadFactionContent();
displayFactionContent(faction.name);
if (sing) {
var res =
"You worked for your faction " +
@ -1419,19 +1197,6 @@ export function startCreateProgramWork(programName, time, reqLevel) {
}
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) {
@ -1441,28 +1206,12 @@ export function createProgramWork(numCycles) {
skillMult = 1 + (skillMult - 1) / 5; //The divider constant can be adjusted as necessary
//Skill multiplier directly applied to "time worked"
this.timeWorked += Engine._idleSpeed * numCycles;
this.timeWorkedCreateProgram += Engine._idleSpeed * numCycles * skillMult;
var programName = this.createProgramName;
this.timeWorked += CONSTANTS._idleSpeed * numCycles;
this.timeWorkedCreateProgram += CONSTANTS._idleSpeed * numCycles * skillMult;
if (this.timeWorkedCreateProgram >= this.timeNeededToCompleteWork) {
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) {
@ -1483,12 +1232,8 @@ export function finishCreateProgramWork(cancelled) {
this.gainIntelligenceExp(this.createProgramReqLvl / CONSTANTS.IntelligenceProgramBaseExpGain);
}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
Engine.loadTerminalContent();
this.resetWorkStatus();
}
@ -1501,7 +1246,7 @@ export function startClass(costMult, expMult, className) {
this.className = className;
const gameCPS = 1000 / Engine._idleSpeed;
const gameCPS = 1000 / CONSTANTS._idleSpeed;
//Find cost and exp gain per game cycle
var cost = 0;
@ -1564,62 +1309,11 @@ export function startClass(costMult, expMult, className) {
this.workDexExpGainRate = dexExp * this.dexterity_exp_mult * BitNodeMultipliers.ClassGymExpGain;
this.workAgiExpGainRate = agiExp * this.agility_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) {
this.timeWorked += Engine._idleSpeed * numCycles;
var className = this.className;
this.timeWorked += CONSTANTS._idleSpeed * 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
@ -1650,12 +1344,8 @@ export function finishClass(sing = false) {
);
}
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
Engine.loadLocationContent(false);
if (sing) {
var res =
"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.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) {
this.timeWorked += Engine._idleSpeed * numCycles;
this.timeWorked += CONSTANTS._idleSpeed * numCycles;
if (this.timeWorked >= this.timeNeededToCompleteWork) {
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;");
if (this.timeWorked >= this.timeNeededToCompleteWork) this.finishCrime(false);
}
export function finishCrime(cancelled) {
@ -1892,11 +1545,9 @@ export function finishCrime(cancelled) {
}
this.committingCrimeThruSingFn = false;
this.singFnCrimeWorkerScript = null;
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
this.isWorking = false;
this.crimeType = "";
this.resetWorkStatus();
Engine.loadLocationContent(false);
}
//Cancels the player's current "work" assignment and gives the proper rewards

@ -20,7 +20,7 @@ export function SleeveRoot(props: IProps): React.ReactElement {
}, []);
return (
<div style={{ width: "70%" }}>
<>
<h1>Sleeves</h1>
<p>
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>
))}
</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 { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner/Bladeburner";
import { writeCinematicText } from "./CinematicText";
import { Companies, initCompanies } from "./Company/Companies";
import { resetIndustryResearchTrees } from "./Corporation/IndustryData";
import { Programs } from "./Programs/Programs";
@ -25,13 +24,7 @@ import { SpecialServerIps, prestigeSpecialServerIps, SpecialServerNames } from "
import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal";
import { Page, routing } from "./ui/navigationTracking";
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";
@ -39,9 +32,6 @@ const BitNode8StartingMoney = 250e6;
// Prestige by purchasing augmentation
function prestigeAugmentation() {
// Set Navigation to Terminal screen, for any logic that depends on it
routing.navigateTo(Page.Terminal);
initBitNodeMultipliers(Player);
const megaCorpFactions = [
@ -62,9 +52,6 @@ function prestigeAugmentation() {
});
Player.prestigeAugmentation();
Terminal.clear();
Engine.loadTerminalContent();
// Delete all Worker Scripts objects
prestigeWorkerScripts();
@ -244,11 +231,6 @@ function prestigeSourceFile(flume) {
// Messages
initMessages();
var mainMenu = document.getElementById("mainmenu-container");
mainMenu.style.visibility = "visible";
Terminal.clear();
Engine.loadTerminalContent();
// BitNode 3: Corporatocracy
if (Player.bitNodeN === 3) {
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
if (Player.bitNodeN === 8) {
Player.money = new Decimal(BitNode8StartingMoney);

@ -1,6 +1,7 @@
import { BaseServer } from "../Server/BaseServer";
import { ITerminal } from "../Terminal/ITerminal";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
export interface IProgramCreate {
level: number;
@ -12,12 +13,12 @@ export interface IProgramCreate {
export class Program {
name = "";
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(
name: string,
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.create = create;

@ -3,6 +3,7 @@ import { CONSTANTS } from "../../Constants";
import { BaseServer } from "../../Server/BaseServer";
import { Server } from "../../Server/Server";
import { ITerminal } from "../../Terminal/ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { HacknetServer } from "../../Hacknet/HacknetServer";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
@ -30,7 +31,7 @@ export interface IProgramCreationParams {
key: string;
name: string;
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[] = [
@ -43,7 +44,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(1),
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)) {
terminal.error("Cannot nuke this kind of server.");
return;
@ -72,7 +73,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(50),
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)) {
terminal.error("Cannot run BruteSSH.exe on this kind of server.");
return;
@ -96,7 +97,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(100),
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)) {
terminal.error("Cannot run FTPCrack.exe on this kind of server.");
return;
@ -120,7 +121,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(250),
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)) {
terminal.error("Cannot run relaySMTP.exe on this kind of server.");
return;
@ -144,7 +145,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(500),
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)) {
terminal.error("Cannot run HTTPWorm.exe on this kind of server.");
return;
@ -168,7 +169,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(750),
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)) {
terminal.error("Cannot run SQLInject.exe on this kind of server.");
return;
@ -192,7 +193,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(75),
time: CONSTANTS.MillisecondsPerQuarterHour,
},
run: (terminal: ITerminal): void => {
run: (router: IRouter, terminal: ITerminal): void => {
terminal.print("This executable cannot be run.");
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),
time: CONSTANTS.MillisecondsPer2Hours,
},
run: (terminal: ITerminal): void => {
run: (router: IRouter, terminal: ITerminal): void => {
terminal.print("This executable cannot be run.");
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),
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) {
terminal.print("Must pass a server hostname or IP as an argument for ServerProfiler.exe");
return;
@ -270,7 +271,7 @@ export const programsMetadata: IProgramCreationParams[] = [
req: requireHackingLevel(25),
time: CONSTANTS.MillisecondsPerQuarterHour,
},
run: (terminal: ITerminal): void => {
run: (router: IRouter, terminal: ITerminal): void => {
terminal.print("This executable cannot be run.");
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.");
@ -285,10 +286,11 @@ export const programsMetadata: IProgramCreationParams[] = [
req: bitFlumeRequirements(),
time: CONSTANTS.MillisecondsPerFiveMinutes / 20,
},
run: (terminal: ITerminal, player: IPlayer): void => {
run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => {
const popupId = "bitflume-popup";
createPopup(popupId, BitFlumePopup, {
player: player,
router: router,
popupId: popupId,
});
},
@ -297,7 +299,7 @@ export const programsMetadata: IProgramCreationParams[] = [
key: "Flight",
name: "fl1ght.exe",
create: null,
run: (terminal: ITerminal, player: IPlayer): void => {
run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => {
const numAugReq = Math.round(BitNodeMultipliers.DaedalusAugsRequirement * 30);
const fulfilled =
player.augmentations.length >= numAugReq && player.money.gt(1e11) && player.hacking_skill >= 2500;

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

6
src/RedPill.d.ts vendored

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

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

@ -6,7 +6,6 @@
*/
import { calculateRamUsage } from "./RamCalculations";
import { ScriptUrl } from "./ScriptUrl";
import { Page, routing } from "../ui/navigationTracking";
import { setTimeoutRef } from "../utils/SetTimeoutRef";
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
*/
saveScript(code: string, serverIp: string, otherScripts: Script[]): void {
if (routing.isOn(Page.ScriptEditor)) {
// Update code and filename
this.code = code.replace(/^\s+|\s+$/g, "");
// Update code and filename
this.code = code.replace(/^\s+|\s+$/g, "");
const filenameElem: HTMLInputElement | null = document.getElementById(
"script-editor-filename",
) as HTMLInputElement;
if (filenameElem == null) {
console.error(`Failed to get Script filename DOM element`);
return;
}
this.filename = filenameElem.value;
this.server = serverIp;
this.updateRamUsage(otherScripts);
this.markUpdated();
const filenameElem: HTMLInputElement | null = document.getElementById("script-editor-filename") as HTMLInputElement;
if (filenameElem == null) {
console.error(`Failed to get Script filename DOM element`);
return;
}
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 { isValidFilePath } from "../../Terminal/DirectoryHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { parseFconfSettings } from "../../Fconf/Fconf";
import { isScriptFilename } from "../../Script/ScriptHelpersTS";
@ -53,7 +53,7 @@ interface IProps {
filename: string;
code: string;
player: IPlayer;
engine: IEngine;
router: IRouter;
}
/*
@ -103,7 +103,6 @@ export function Root(props: IProps): React.ReactElement {
}
lastPosition = null;
// TODO(hydroflame): re-enable the tutorial.
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial
if (filename !== "n00dles.script") {
@ -121,7 +120,7 @@ export function Root(props: IProps): React.ReactElement {
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
props.engine.loadTerminalContent();
props.router.toTerminal();
return iTutorialNextStep();
}
}
@ -160,7 +159,7 @@ export function Root(props: IProps): React.ReactElement {
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
props.engine.loadTerminalContent();
props.router.toTerminal();
return;
}
}
@ -173,7 +172,7 @@ export function Root(props: IProps): React.ReactElement {
for (let i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === filename) {
server.textFiles[i].write(code);
props.engine.loadTerminalContent();
props.router.toTerminal();
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)");
return;
}
props.engine.loadTerminalContent();
props.router.toTerminal();
}
function beautify(): void {
@ -308,7 +307,7 @@ export function Root(props: IProps): React.ReactElement {
}
return (
<div className="script-editor-wrapper">
<>
<div id="script-editor-filename-wrapper">
<p id="script-editor-filename-tag" className="noselect">
{" "}
@ -328,7 +327,7 @@ export function Root(props: IProps): React.ReactElement {
beforeMount={beforeMount}
onMount={onMount}
loading={<p>Loading script editor!</p>}
height="80%"
height="90%"
defaultLanguage="javascript"
defaultValue={code}
onChange={updateCode}
@ -352,6 +351,6 @@ export function Root(props: IProps): React.ReactElement {
Netscript Documentation
</a>
</div>
</div>
</>
);
}

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

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

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

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

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { parseAliasDeclaration, printAliases } from "../../Alias";
export function alias(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
export function analyze(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

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

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

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

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { findRunningScript } from "../../Script/ScriptHelpers";
@ -7,7 +7,7 @@ import { isScriptFilename } from "../../Script/ScriptHelpersTS";
export function check(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

@ -1,5 +1,5 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/ScriptHelpersTS";
@ -8,7 +8,7 @@ import JSZip from "jszip";
export function download(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
export function expr(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

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

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { TerminalHelpText, HelpTexts } from "../HelpText";
export function help(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
export function home(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
export function hostname(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
export function ifconfig(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { killWorkerScript } from "../../Netscript/killWorkerScript";
export function kill(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { killWorkerScript } from "../../Netscript/killWorkerScript";
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) {
killWorkerScript(server.runningScripts[i], server.ip, false);
}

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

@ -1,7 +1,7 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
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)");
}

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

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

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

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
export function ps(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { IReturnStatus } from "../../types";
export function rm(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

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

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

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

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { Programs } from "../../Programs/Programs";
export function scananalyze(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

@ -1,11 +1,11 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
export function sudov(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { FconfSettings } from "../../Fconf/FconfSettings";
export function theme(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { removeAlias } from "../../Alias";
export function unalias(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

@ -1,12 +1,12 @@
import { ITerminal } from "../ITerminal";
import { IEngine } from "../../IEngine";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { isScriptFilename } from "../../Script/ScriptHelpersTS";
export function wget(
terminal: ITerminal,
engine: IEngine,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],

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

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

@ -1,73 +1,44 @@
/**
* Game engine. Handles the main game loop as well as the main UI pages
*
* TODO: Separate UI functionality into its own component
* Game engine. Handles the main game loop.
*/
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { Augmentations } from "./Augmentation/Augmentations";
import { initAugmentations, installAugmentations } from "./Augmentation/AugmentationHelpers";
import { onExport } from "./ExportBonus";
import { AugmentationsRoot } from "./Augmentation/ui/Root";
import { initAugmentations } from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode";
import { Bladeburner } from "./Bladeburner/Bladeburner";
import { CharacterOverview } from "./ui/React/CharacterOverview";
import { generateRandomContract } from "./CodingContractGenerator";
import { initCompanies } from "./Company/Companies";
import { Corporation } from "./Corporation/Corporation";
import { CONSTANTS } from "./Constants";
import { DevMenuRoot } from "./DevMenu";
import { Factions, initFactions } from "./Faction/Factions";
import { processPassiveFactionRepGain, inviteToFaction } from "./Faction/FactionHelpers";
import { FactionList } from "./Faction/ui/FactionList";
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 { GameRoot, Router } from "./ui/GameRoot";
import { TTheme as Theme } from "./ui/React/Theme";
import { SleeveRoot } from "./PersonObjects/Sleeve/ui/SleeveRoot";
import { displayInfiltrationContent } from "./Infiltration/Helper";
import {
getHackingWorkRepGain,
getFactionSecurityWorkRepGain,
getFactionFieldWorkRepGain,
} from "./PersonObjects/formulas/reputation";
import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers";
import { HacknetRoot } from "./Hacknet/ui/HacknetRoot";
import { iTutorialStart } from "./InteractiveTutorial";
import { LocationName } from "./Locations/data/LocationNames";
import { LocationRoot } from "./Locations/ui/Root";
import { checkForMessagesToSend, initMessages } from "./Message/MessageHelpers";
import { inMission, currMission } from "./Missions";
import { workerScripts } from "./Netscript/WorkerScripts";
import { loadAllRunningScripts, updateOnlineScriptTimes } from "./NetscriptWorker";
import { Player } from "./Player";
import { prestigeAugmentation } from "./Prestige";
import { ProgramsRoot } from "./Programs/ui/ProgramsRoot";
import { saveObject, loadGame } from "./SaveObject";
import { Root as ScriptEditorRoot } from "./ScriptEditor/ui/Root";
import { initForeignServers, AllServers } from "./Server/AllServers";
import { initForeignServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { initSpecialServerIps } from "./Server/SpecialServerIps";
import { initSymbolToStockMap, processStockPrices, displayStockMarketContent } from "./StockMarket/StockMarket";
import { MilestonesRoot } from "./Milestones/ui/MilestonesRoot";
import { TerminalRoot } from "./Terminal/ui/TerminalRoot";
import { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal";
import { TutorialRoot } from "./Tutorial/ui/TutorialRoot";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import { CharacterInfo } from "./ui/CharacterInfo";
import { Page, routing } from "./ui/navigationTracking";
import { Money } from "./ui/React/Money";
import { Hashes } from "./ui/React/Hashes";
import { Reputation } from "./ui/React/Reputation";
import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
import { MainMenuLinks } from "./ui/MainMenu/Links";
import { dialogBoxCreate } from "../utils/DialogBox";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { removeLoadingScreen } from "../utils/uiHelpers/removeLoadingScreen";
@ -78,367 +49,19 @@ import React from "react";
import ReactDOM from "react-dom";
const Engine = {
// Clickable objects
Clickables: {
// Main menu buttons
saveMainMenuButton: null,
deleteMainMenuButton: null,
},
// Display objects
// TODO-Refactor this into its own component
Display: {
// Generic page that most react loads into.
content: null,
// Main menu content
infiltrationContent: null,
workInProgressContent: null,
redPillContent: null,
cinematicTextContent: null,
missionContent: null,
overview: null,
},
indexedDb: undefined,
// Time variables (milliseconds unix epoch time)
_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) {
const time = numCycles * Engine._idleSpeed;
const time = numCycles * CONSTANTS._idleSpeed;
if (Player.totalPlaytime == null) {
Player.totalPlaytime = 0;
}
@ -452,7 +75,7 @@ const Engine = {
Player.playtimeSinceLastAug += time;
Player.playtimeSinceLastBitnode += time;
Terminal.process(Player, numCycles);
Terminal.process(Router, Player, numCycles);
// Working
if (Player.isWorking) {
@ -600,7 +223,7 @@ const Engine = {
}
if (Player.bladeburner instanceof Bladeburner) {
try {
Player.bladeburner.process(Player);
Player.bladeburner.process(Router, Player);
} catch (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 game from save or create new game
if (loadGame(saveString)) {
initBitNodeMultipliers(Player);
Engine.setDisplayElements(); // Sets variables for important DOM elements
Engine.init(); // Initialize buttons, work, etc.
updateSourceFileFlags(Player);
initAugmentations(); // Also calls Player.reapplyAllAugmentations()
Player.reapplyAllSourceFiles();
@ -664,7 +256,7 @@ const Engine = {
Engine._lastUpdate = new Date().getTime();
const lastUpdate = Player.lastUpdate;
const timeOffline = Engine._lastUpdate - lastUpdate;
const numCyclesOffline = Math.floor(timeOffline / Engine._idleSpeed);
const numCyclesOffline = Math.floor(timeOffline / CONSTANTS._idleSpeed);
let offlineReputation = 0;
const offlineHackingIncome = (Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * 0.75;
@ -686,6 +278,7 @@ const Engine = {
} else {
Player.work(numCyclesOffline);
}
Player.focus = false;
} else {
for (let i = 0; i < Player.factions.length; i++) {
const facName = Player.factions[i];
@ -760,7 +353,7 @@ const Engine = {
}
// Update total playtime
var time = numCyclesOffline * Engine._idleSpeed;
var time = numCyclesOffline * CONSTANTS._idleSpeed;
if (Player.totalPlaytime == null) {
Player.totalPlaytime = 0;
}
@ -806,79 +399,34 @@ const Engine = {
ReactDOM.render(
<Theme>
<SidebarRoot engine={this} player={Player} />
<GameRoot terminal={Terminal} engine={this} player={Player} />
</Theme>,
document.getElementById("sidebar"),
document.getElementById("mainmenu-container"),
);
},
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.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 () {
// Run main loop
Engine.idleTimer();
// Get time difference
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