Merge branch 'dev' into cityFix

This commit is contained in:
Snarling 2022-09-27 16:38:39 -04:00 committed by GitHub
commit 582c9f40e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
362 changed files with 4082 additions and 6915 deletions

6
dist/main.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -347,18 +347,18 @@ export const achievements: IMap<Achievement> = {
FIRST_HACKNET_NODE: { FIRST_HACKNET_NODE: {
...achievementData["FIRST_HACKNET_NODE"], ...achievementData["FIRST_HACKNET_NODE"],
Icon: "node", Icon: "node",
Condition: () => !hasHacknetServers(Player) && Player.hacknetNodes.length > 0, Condition: () => !hasHacknetServers() && Player.hacknetNodes.length > 0,
}, },
"30_HACKNET_NODE": { "30_HACKNET_NODE": {
...achievementData["30_HACKNET_NODE"], ...achievementData["30_HACKNET_NODE"],
Icon: "hacknet-all", Icon: "hacknet-all",
Condition: () => !hasHacknetServers(Player) && Player.hacknetNodes.length >= 30, Condition: () => !hasHacknetServers() && Player.hacknetNodes.length >= 30,
}, },
MAX_HACKNET_NODE: { MAX_HACKNET_NODE: {
...achievementData["MAX_HACKNET_NODE"], ...achievementData["MAX_HACKNET_NODE"],
Icon: "hacknet-max", Icon: "hacknet-max",
Condition: (): boolean => { Condition: (): boolean => {
if (hasHacknetServers(Player)) return false; if (hasHacknetServers()) return false;
for (const h of Player.hacknetNodes) { for (const h of Player.hacknetNodes) {
if (!(h instanceof HacknetNode)) return false; if (!(h instanceof HacknetNode)) return false;
if ( if (
@ -374,7 +374,7 @@ export const achievements: IMap<Achievement> = {
HACKNET_NODE_10M: { HACKNET_NODE_10M: {
...achievementData["HACKNET_NODE_10M"], ...achievementData["HACKNET_NODE_10M"],
Icon: "hacknet-10m", Icon: "hacknet-10m",
Condition: () => !hasHacknetServers(Player) && Player.moneySourceB.hacknet >= 10e6, Condition: () => !hasHacknetServers() && Player.moneySourceB.hacknet >= 10e6,
}, },
REPUTATION_10M: { REPUTATION_10M: {
...achievementData["REPUTATION_10M"], ...achievementData["REPUTATION_10M"],
@ -515,14 +515,14 @@ export const achievements: IMap<Achievement> = {
...achievementData["FIRST_HACKNET_SERVER"], ...achievementData["FIRST_HACKNET_SERVER"],
Icon: "HASHNET", Icon: "HASHNET",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length > 0, Condition: () => hasHacknetServers() && Player.hacknetNodes.length > 0,
AdditionalUnlock: [achievementData.FIRST_HACKNET_NODE.ID], AdditionalUnlock: [achievementData.FIRST_HACKNET_NODE.ID],
}, },
ALL_HACKNET_SERVER: { ALL_HACKNET_SERVER: {
...achievementData["ALL_HACKNET_SERVER"], ...achievementData["ALL_HACKNET_SERVER"],
Icon: "HASHNETALL", Icon: "HASHNETALL",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length === HacknetServerConstants.MaxServers, Condition: () => hasHacknetServers() && Player.hacknetNodes.length === HacknetServerConstants.MaxServers,
AdditionalUnlock: [achievementData["30_HACKNET_NODE"].ID], AdditionalUnlock: [achievementData["30_HACKNET_NODE"].ID],
}, },
MAX_HACKNET_SERVER: { MAX_HACKNET_SERVER: {
@ -530,7 +530,7 @@ export const achievements: IMap<Achievement> = {
Icon: "HASHNETALL", Icon: "HASHNETALL",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: (): boolean => { Condition: (): boolean => {
if (!hasHacknetServers(Player)) return false; if (!hasHacknetServers()) return false;
for (const h of Player.hacknetNodes) { for (const h of Player.hacknetNodes) {
if (typeof h !== "string") return false; if (typeof h !== "string") return false;
const hs = GetServer(h); const hs = GetServer(h);
@ -551,7 +551,7 @@ export const achievements: IMap<Achievement> = {
...achievementData["HACKNET_SERVER_1B"], ...achievementData["HACKNET_SERVER_1B"],
Icon: "HASHNETMONEY", Icon: "HASHNETMONEY",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.moneySourceB.hacknet >= 1e9, Condition: () => hasHacknetServers() && Player.moneySourceB.hacknet >= 1e9,
AdditionalUnlock: [achievementData.HACKNET_NODE_10M.ID], AdditionalUnlock: [achievementData.HACKNET_NODE_10M.ID],
}, },
MAX_CACHE: { MAX_CACHE: {
@ -559,7 +559,7 @@ export const achievements: IMap<Achievement> = {
Icon: "HASHNETCAP", Icon: "HASHNETCAP",
Visible: () => hasAccessToSF(Player, 9), Visible: () => hasAccessToSF(Player, 9),
Condition: () => Condition: () =>
hasHacknetServers(Player) && hasHacknetServers() &&
Player.hashManager.hashes === Player.hashManager.capacity && Player.hashManager.hashes === Player.hashManager.capacity &&
Player.hashManager.capacity > 0, Player.hashManager.capacity > 0,
}, },

@ -2,7 +2,7 @@ import React, { useState } from "react";
import { BBCabinetRoot } from "./BBCabinet"; import { BBCabinetRoot } from "./BBCabinet";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { AlertEvents } from "../../ui/React/AlertManager"; import { AlertEvents } from "../../ui/React/AlertManager";
enum Page { enum Page {
@ -11,11 +11,10 @@ enum Page {
} }
export function ArcadeRoot(): React.ReactElement { export function ArcadeRoot(): React.ReactElement {
const player = use.Player();
const [page, setPage] = useState(Page.None); const [page, setPage] = useState(Page.None);
function mbBurner2000(): void { function mbBurner2000(): void {
if (player.sourceFileLvl(1) === 0) { if (Player.sourceFileLvl(1) === 0) {
AlertEvents.emit("This machine is broken."); AlertEvents.emit("This machine is broken.");
} else { } else {
setPage(Page.Megabyteburner2000); setPage(Page.Megabyteburner2000);

@ -1,6 +1,6 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { Exploit } from "../../Exploits/Exploit"; import { Exploit } from "../../Exploits/Exploit";
const metaBB = "https://bitburner-official.github.io/bitburner-legacy/"; const metaBB = "https://bitburner-official.github.io/bitburner-legacy/";
@ -12,11 +12,10 @@ const style = {
}; };
export function BBCabinetRoot(): React.ReactElement { export function BBCabinetRoot(): React.ReactElement {
const player = use.Player();
useEffect(() => { useEffect(() => {
window.addEventListener("message", function (this: Window, ev: MessageEvent<boolean>) { window.addEventListener("message", function (this: Window, ev: MessageEvent<boolean>) {
if (ev.isTrusted && ev.origin == "https://bitburner-official.github.io" && ev.data) { if (ev.isTrusted && ev.origin == "https://bitburner-official.github.io" && ev.data) {
player.giveExploit(Exploit.TrueRecursion); Player.giveExploit(Exploit.TrueRecursion);
} }
}); });
}); });

@ -8,7 +8,7 @@ import { Money } from "../ui/React/Money";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { FactionNames } from "../Faction/data/FactionNames"; import { FactionNames } from "../Faction/data/FactionNames";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { StaticAugmentations } from "./StaticAugmentations"; import { StaticAugmentations } from "./StaticAugmentations";
@ -531,26 +531,26 @@ export class Augmentation {
} }
} }
getCost(player: IPlayer): AugmentationCosts { getCost(): AugmentationCosts {
const augmentationReference = StaticAugmentations[this.name]; const augmentationReference = StaticAugmentations[this.name];
let moneyCost = augmentationReference.baseCost; let moneyCost = augmentationReference.baseCost;
let repCost = augmentationReference.baseRepRequirement; let repCost = augmentationReference.baseRepRequirement;
if (augmentationReference.name === AugmentationNames.NeuroFluxGovernor) { if (augmentationReference.name === AugmentationNames.NeuroFluxGovernor) {
let nextLevel = this.getLevel(player); let nextLevel = this.getLevel();
--nextLevel; --nextLevel;
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel); const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
repCost = augmentationReference.baseRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost; repCost = augmentationReference.baseRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost;
moneyCost = augmentationReference.baseCost * multiplier * BitNodeMultipliers.AugmentationMoneyCost; moneyCost = augmentationReference.baseCost * multiplier * BitNodeMultipliers.AugmentationMoneyCost;
for (let i = 0; i < player.queuedAugmentations.length; ++i) { for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
moneyCost *= getBaseAugmentationPriceMultiplier(); moneyCost *= getBaseAugmentationPriceMultiplier();
} }
} else if (augmentationReference.factions.includes(FactionNames.ShadowsOfAnarchy)) { } else if (augmentationReference.factions.includes(FactionNames.ShadowsOfAnarchy)) {
const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name); const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name);
const soaMultiplier = Math.pow( const soaMultiplier = Math.pow(
CONSTANTS.SoACostMult, CONSTANTS.SoACostMult,
soaAugmentationNames.filter((augmentationName) => player.hasAugmentation(augmentationName)).length, soaAugmentationNames.filter((augmentationName) => Player.hasAugmentation(augmentationName)).length,
); );
moneyCost = augmentationReference.baseCost * soaMultiplier; moneyCost = augmentationReference.baseCost * soaMultiplier;
if (soaAugmentationNames.find((augmentationName) => augmentationName === augmentationReference.name)) { if (soaAugmentationNames.find((augmentationName) => augmentationName === augmentationReference.name)) {
@ -566,19 +566,19 @@ export class Augmentation {
return { moneyCost, repCost }; return { moneyCost, repCost };
} }
getLevel(player: IPlayer): number { getLevel(): number {
// Get current Neuroflux level based on Player's augmentations // Get current Neuroflux level based on Player's augmentations
if (this.name === AugmentationNames.NeuroFluxGovernor) { if (this.name === AugmentationNames.NeuroFluxGovernor) {
let currLevel = 0; let currLevel = 0;
for (let i = 0; i < player.augmentations.length; ++i) { for (let i = 0; i < Player.augmentations.length; ++i) {
if (player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) { if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = player.augmentations[i].level; currLevel = Player.augmentations[i].level;
} }
} }
// Account for purchased but uninstalled Augmentations // Account for purchased but uninstalled Augmentations
for (let i = 0; i < player.queuedAugmentations.length; ++i) { for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
if (player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) { if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel; ++currLevel;
} }
} }

@ -1,6 +1,6 @@
import { Augmentation } from "./Augmentation"; import { Augmentation } from "./Augmentation";
import { StaticAugmentations } from "./StaticAugmentations"; import { StaticAugmentations } from "./StaticAugmentations";
import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation"; import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
@ -21,7 +21,7 @@ import {
initUnstableCircadianModulator, initUnstableCircadianModulator,
} from "./data/AugmentationCreator"; } from "./data/AugmentationCreator";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { mergeAugmentation } from "../PersonObjects/Multipliers"; import { mergeMultipliers } from "../PersonObjects/Multipliers";
export function AddToStaticAugmentations(aug: Augmentation): void { export function AddToStaticAugmentations(aug: Augmentation): void {
const name = aug.name; const name = aug.name;
@ -71,11 +71,11 @@ function resetAugmentation(aug: Augmentation): void {
AddToStaticAugmentations(aug); AddToStaticAugmentations(aug);
} }
function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void { function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void {
const staticAugmentation = StaticAugmentations[aug.name]; const staticAugmentation = StaticAugmentations[aug.name];
// Apply multipliers // Apply multipliers
Player.mults = mergeAugmentation(Player.mults, staticAugmentation.mults); Player.mults = mergeMultipliers(Player.mults, staticAugmentation.mults);
// Special logic for Congruity Implant // Special logic for Congruity Implant
if (aug.name === AugmentationNames.CongruityImplant && !reapply) { if (aug.name === AugmentationNames.CongruityImplant && !reapply) {
@ -126,15 +126,15 @@ function installAugmentations(force?: boolean): boolean {
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) { if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
level = ` - ${ownedAug.level}`; level = ` - ${ownedAug.level}`;
} }
augmentationList += aug.name + level + "<br>"; augmentationList += aug.name + level + "\n";
} }
Player.queuedAugmentations = []; Player.queuedAugmentations = [];
if (!force) { if (!force) {
dialogBoxCreate( dialogBoxCreate(
"You slowly drift to sleep as scientists put you under in order " + "You slowly drift to sleep as scientists put you under in order " +
"to install the following Augmentations:<br>" + "to install the following Augmentations:\n" +
augmentationList + augmentationList +
"<br>You wake up in your home...you feel different...", "\nYou wake up in your home...you feel different...",
); );
} }
prestigeAugmentation(); prestigeAugmentation();
@ -146,8 +146,8 @@ function augmentationExists(name: string): boolean {
return StaticAugmentations.hasOwnProperty(name); return StaticAugmentations.hasOwnProperty(name);
} }
export function isRepeatableAug(aug: Augmentation): boolean { export function isRepeatableAug(aug: Augmentation | string): boolean {
const augName = aug instanceof Augmentation ? aug.name : aug; const augName = typeof aug === "string" ? aug : aug.name;
return augName === AugmentationNames.NeuroFluxGovernor; return augName === AugmentationNames.NeuroFluxGovernor;
} }

@ -6,8 +6,3 @@ export class PlayerOwnedAugmentation {
this.name = name; this.name = name;
} }
} }
export interface IPlayerOwnedAugmentation {
level: number;
name: string;
}

@ -6,7 +6,6 @@ import { WHRNG } from "../../Casino/RNG";
import React from "react"; import React from "react";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { Faction } from "src/Faction/Faction";
interface CircadianBonus { interface CircadianBonus {
bonuses: { bonuses: {

@ -10,8 +10,6 @@ import { PurchasedAugmentations } from "./PurchasedAugmentations";
import { SourceFilesElement } from "./SourceFiles"; import { SourceFilesElement } from "./SourceFiles";
import { canGetBonus } from "../../ExportBonus"; import { canGetBonus } from "../../ExportBonus";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
@ -20,7 +18,7 @@ import Paper from "@mui/material/Paper";
import Container from "@mui/material/Container"; import Container from "@mui/material/Container";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { ConfirmationModal } from "../../ui/React/ConfirmationModal"; import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { AugmentationNames } from "../data/AugmentationNames"; import { AugmentationNames } from "../data/AugmentationNames";
import { StaticAugmentations } from "../StaticAugmentations"; import { StaticAugmentations } from "../StaticAugmentations";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
@ -29,12 +27,8 @@ import { Info } from "@mui/icons-material";
import { Link } from "@mui/material"; import { Link } from "@mui/material";
import { AlertEvents } from "../../ui/React/AlertManager"; import { AlertEvents } from "../../ui/React/AlertManager";
interface NFGDisplayProps { const NeuroFluxDisplay = (): React.ReactElement => {
player: IPlayer; const level = Player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
}
const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
const level = player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
const openBloodDonation = () => { const openBloodDonation = () => {
AlertEvents.emit( AlertEvents.emit(
@ -67,18 +61,14 @@ const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
); );
}; };
interface EntropyDisplayProps { const EntropyDisplay = (): React.ReactElement => {
player: IPlayer; return Player.entropy > 0 ? (
}
const EntropyDisplay = ({ player }: EntropyDisplayProps): React.ReactElement => {
return player.entropy > 0 ? (
<Paper sx={{ p: 1 }}> <Paper sx={{ p: 1 }}>
<Typography variant="h5" color={Settings.theme.error}> <Typography variant="h5" color={Settings.theme.error}>
Entropy Virus - Level {player.entropy} Entropy Virus - Level {Player.entropy}
</Typography> </Typography>
<Typography color={Settings.theme.error}> <Typography color={Settings.theme.error}>
<b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}% <b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** Player.entropy) * 100, 3)}%
(multiplicative) (multiplicative)
</Typography> </Typography>
</Paper> </Paper>
@ -94,7 +84,6 @@ interface IProps {
export function AugmentationsRoot(props: IProps): React.ReactElement { export function AugmentationsRoot(props: IProps): React.ReactElement {
const [installOpen, setInstallOpen] = useState(false); const [installOpen, setInstallOpen] = useState(false);
const player = use.Player();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((o) => !o); setRerender((o) => !o);
@ -187,7 +176,7 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
<Box sx={{ display: "grid", width: "100%", gridTemplateColumns: "1fr 1fr" }}> <Box sx={{ display: "grid", width: "100%", gridTemplateColumns: "1fr 1fr" }}>
<Tooltip title={<Typography>'I never asked for this'</Typography>}> <Tooltip title={<Typography>'I never asked for this'</Typography>}>
<span> <span>
<Button sx={{ width: "100%" }} disabled={player.queuedAugmentations.length === 0} onClick={doInstall}> <Button sx={{ width: "100%" }} disabled={Player.queuedAugmentations.length === 0} onClick={doInstall}>
Install Augmentations Install Augmentations
</Button> </Button>
</span> </span>
@ -199,7 +188,7 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
</Tooltip> </Tooltip>
</Box> </Box>
</Paper> </Paper>
{player.queuedAugmentations.length > 0 ? ( {Player.queuedAugmentations.length > 0 ? (
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}> <Box sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
<PurchasedAugmentations /> <PurchasedAugmentations />
<PlayerMultipliers /> <PlayerMultipliers />
@ -216,14 +205,14 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
my: 1, my: 1,
display: "grid", display: "grid",
gridTemplateColumns: `repeat(${ gridTemplateColumns: `repeat(${
+!!((player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0) > 0) + +!!((Player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0) > 0) +
+!!(player.entropy > 0) +!!(Player.entropy > 0)
}, 1fr)`, }, 1fr)`,
gap: 1, gap: 1,
}} }}
> >
<NeuroFluxDisplay player={player} /> <NeuroFluxDisplay />
<EntropyDisplay player={player} /> <EntropyDisplay />
</Box> </Box>
<Box> <Box>

@ -12,14 +12,13 @@ import Tooltip from "@mui/material/Tooltip";
import React, { useState } from "react"; import React, { useState } from "react";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { StaticAugmentations } from "../StaticAugmentations"; import { StaticAugmentations } from "../StaticAugmentations";
import { AugmentationNames } from "../data/AugmentationNames"; import { AugmentationNames } from "../data/AugmentationNames";
export function InstalledAugmentations(): React.ReactElement { export function InstalledAugmentations(): React.ReactElement {
const setRerender = useState(true)[1]; const setRerender = useState(true)[1];
const player = use.Player(); const sourceAugs = Player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor);
const sourceAugs = player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor);
const [selectedAug, setSelectedAug] = useState(sourceAugs[0]); const [selectedAug, setSelectedAug] = useState(sourceAugs[0]);

@ -4,7 +4,7 @@
import { DoubleArrow } from "@mui/icons-material"; import { DoubleArrow } from "@mui/icons-material";
import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material"; import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material";
import * as React from "react"; import * as React from "react";
import { Multipliers, defaultMultipliers, mergeAugmentation } from "../../PersonObjects/Multipliers"; import { Multipliers, defaultMultipliers, mergeMultipliers } from "../../PersonObjects/Multipliers";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
@ -15,7 +15,7 @@ function calculateAugmentedStats(): Multipliers {
let augP: Multipliers = defaultMultipliers(); let augP: Multipliers = defaultMultipliers();
for (const aug of Player.queuedAugmentations) { for (const aug of Player.queuedAugmentations) {
const augObj = StaticAugmentations[aug.name]; const augObj = StaticAugmentations[aug.name];
augP = mergeAugmentation(augP, augObj.mults); augP = mergeMultipliers(augP, augObj.mults);
} }
return augP; return augP;
} }

@ -6,7 +6,7 @@ import { CheckBox, CheckBoxOutlineBlank, CheckCircle, NewReleases, Report } from
import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material"; import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Augmentation } from "../Augmentation"; import { Augmentation } from "../Augmentation";
@ -15,12 +15,11 @@ import { StaticAugmentations } from "../StaticAugmentations";
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal"; import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
interface IPreReqsProps { interface IPreReqsProps {
player: IPlayer;
aug: Augmentation; aug: Augmentation;
} }
const PreReqs = (props: IPreReqsProps): React.ReactElement => { const PreReqs = (props: IPreReqsProps): React.ReactElement => {
const ownedPreReqs = props.aug.prereqs.filter((aug) => props.player.hasAugmentation(aug)); const ownedPreReqs = props.aug.prereqs.filter((aug) => Player.hasAugmentation(aug));
const hasPreReqs = props.aug.prereqs.length > 0 && ownedPreReqs.length === props.aug.prereqs.length; const hasPreReqs = props.aug.prereqs.length > 0 && ownedPreReqs.length === props.aug.prereqs.length;
return ( return (
@ -32,7 +31,7 @@ const PreReqs = (props: IPreReqsProps): React.ReactElement => {
</Typography> </Typography>
{props.aug.prereqs.map((preAug) => ( {props.aug.prereqs.map((preAug) => (
<Requirement <Requirement
fulfilled={props.player.hasAugmentation(preAug)} fulfilled={Player.hasAugmentation(preAug)}
value={preAug} value={preAug}
color={Settings.theme.money} color={Settings.theme.money}
key={preAug} key={preAug}
@ -68,7 +67,6 @@ const PreReqs = (props: IPreReqsProps): React.ReactElement => {
}; };
interface IExclusiveProps { interface IExclusiveProps {
player: IPlayer;
aug: Augmentation; aug: Augmentation;
} }
@ -85,18 +83,16 @@ const Exclusive = (props: IExclusiveProps): React.ReactElement => {
<li> <li>
<b>{props.aug.factions[0]}</b> faction <b>{props.aug.factions[0]}</b> faction
</li> </li>
{props.player.isAwareOfGang() && !props.aug.isSpecial && ( {Player.isAwareOfGang() && !props.aug.isSpecial && (
<li> <li>
Certain <b>gangs</b> Certain <b>gangs</b>
</li> </li>
)} )}
{props.player.canAccessGrafting() && {Player.canAccessGrafting() && !props.aug.isSpecial && props.aug.name !== AugmentationNames.TheRedPill && (
!props.aug.isSpecial && <li>
props.aug.name !== AugmentationNames.TheRedPill && ( <b>Grafting</b>
<li> </li>
<b>Grafting</b> )}
</li>
)}
</Typography> </Typography>
</ul> </ul>
</> </>
@ -130,10 +126,9 @@ const Requirement = (props: IReqProps): React.ReactElement => {
interface IPurchasableAugsProps { interface IPurchasableAugsProps {
augNames: string[]; augNames: string[];
ownedAugNames: string[]; ownedAugNames: string[];
player: IPlayer;
canPurchase: (player: IPlayer, aug: Augmentation) => boolean; canPurchase: (aug: Augmentation) => boolean;
purchaseAugmentation: (player: IPlayer, aug: Augmentation, showModal: (open: boolean) => void) => void; purchaseAugmentation: (aug: Augmentation, showModal: (open: boolean) => void) => void;
rep?: number; rep?: number;
sleeveAugs?: boolean; sleeveAugs?: boolean;
@ -167,7 +162,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const aug = StaticAugmentations[props.augName]; const aug = StaticAugmentations[props.augName];
const augCosts = aug.getCost(props.parent.player); const augCosts = aug.getCost();
const cost = props.parent.sleeveAugs ? aug.baseCost : augCosts.moneyCost; const cost = props.parent.sleeveAugs ? aug.baseCost : augCosts.moneyCost;
const repCost = augCosts.repCost; const repCost = augCosts.repCost;
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info; const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
@ -195,11 +190,11 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
<Box sx={{ display: "flex", alignItems: "center" }}> <Box sx={{ display: "flex", alignItems: "center" }}>
<Button <Button
onClick={() => onClick={() =>
props.parent.purchaseAugmentation(props.parent.player, aug, (open): void => { props.parent.purchaseAugmentation(aug, (open): void => {
setOpen(open); setOpen(open);
}) })
} }
disabled={!props.parent.canPurchase(props.parent.player, aug) || props.owned} disabled={!props.parent.canPurchase(aug) || props.owned}
sx={{ width: "48px", height: "36px", float: "left", clear: "none", mr: 1 }} sx={{ width: "48px", height: "36px", float: "left", clear: "none", mr: 1 }}
> >
{props.owned ? "Owned" : "Buy"} {props.owned ? "Owned" : "Buy"}
@ -212,8 +207,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
<> <>
<Typography variant="h5"> <Typography variant="h5">
{props.augName} {props.augName}
{props.augName === AugmentationNames.NeuroFluxGovernor && {props.augName === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel()}`}
` - Level ${aug.getLevel(props.parent.player)}`}
</Typography> </Typography>
<Typography>{description}</Typography> <Typography>{description}</Typography>
</> </>
@ -226,20 +220,16 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
whiteSpace: "nowrap", whiteSpace: "nowrap",
overflow: "hidden", overflow: "hidden",
color: color:
props.owned || !props.parent.canPurchase(props.parent.player, aug) props.owned || !props.parent.canPurchase(aug) ? Settings.theme.disabled : Settings.theme.primary,
? Settings.theme.disabled
: Settings.theme.primary,
}} }}
> >
{aug.name} {aug.name}
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel(props.parent.player)}`} {aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel()}`}
</Typography> </Typography>
</Tooltip> </Tooltip>
{aug.factions.length === 1 && !props.parent.sleeveAugs && ( {aug.factions.length === 1 && !props.parent.sleeveAugs && <Exclusive aug={aug} />}
<Exclusive player={props.parent.player} aug={aug} /> {aug.prereqs.length > 0 && !props.parent.sleeveAugs && <PreReqs aug={aug} />}
)}
{aug.prereqs.length > 0 && !props.parent.sleeveAugs && <PreReqs player={props.parent.player} aug={aug} />}
</Box> </Box>
</Box> </Box>
</Box> </Box>
@ -247,7 +237,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
{props.owned || ( {props.owned || (
<Box sx={{ display: "grid", alignItems: "center", gridTemplateColumns: "1fr 1fr" }}> <Box sx={{ display: "grid", alignItems: "center", gridTemplateColumns: "1fr 1fr" }}>
<Requirement <Requirement
fulfilled={cost === 0 || props.parent.player.money > cost} fulfilled={cost === 0 || Player.money > cost}
value={numeralWrapper.formatMoney(cost)} value={numeralWrapper.formatMoney(cost)}
color={Settings.theme.money} color={Settings.theme.money}
/> />

@ -6,7 +6,7 @@ import { purchaseAugmentation } from "../../Faction/FactionHelpers";
import { isRepeatableAug } from "../AugmentationHelpers"; import { isRepeatableAug } from "../AugmentationHelpers";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -18,14 +18,12 @@ interface IProps {
} }
export function PurchaseAugmentationModal(props: IProps): React.ReactElement { export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
if (typeof props.aug === "undefined" || typeof props.faction === "undefined") { if (!props.aug || !props.faction) {
return <></>; return <></>;
} }
const player = use.Player();
function buy(): void { function buy(): void {
if (!isRepeatableAug(props.aug as Augmentation) && player.hasAugmentation(props.aug as Augmentation)) { if (!props.aug || (!isRepeatableAug(props.aug) && Player.hasAugmentation(props.aug.name))) {
return; return;
} }
@ -44,7 +42,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
<br /> <br />
<br /> <br />
Would you like to purchase the {props.aug.name} Augmentation for&nbsp; Would you like to purchase the {props.aug.name} Augmentation for&nbsp;
<Money money={props.aug.getCost(player).moneyCost} />? <Money money={props.aug.getCost().moneyCost} />?
<br /> <br />
<br /> <br />
</Typography> </Typography>

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { BitNodeMultipliers, IBitNodeMultipliers } from "./BitNodeMultipliers"; import { BitNodeMultipliers, IBitNodeMultipliers } from "./BitNodeMultipliers";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { IMap } from "../types"; import { IMap } from "../types";
import { FactionNames } from "../Faction/data/FactionNames"; import { FactionNames } from "../Faction/data/FactionNames";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
@ -876,6 +876,6 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
} }
} }
export function initBitNodeMultipliers(p: IPlayer): void { export function initBitNodeMultipliers(): void {
Object.assign(BitNodeMultipliers, getBitNodeMultipliers(p.bitNodeN, p.sourceFileLvl(p.bitNodeN))); Object.assign(BitNodeMultipliers, getBitNodeMultipliers(Player.bitNodeN, Player.sourceFileLvl(Player.bitNodeN)));
} }

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context"; import { Router } from "../../ui/GameRoot";
import { EventEmitter } from "../../utils/EventEmitter"; import { EventEmitter } from "../../utils/EventEmitter";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -8,10 +8,9 @@ import Button from "@mui/material/Button";
export const BitFlumeEvent = new EventEmitter<[]>(); export const BitFlumeEvent = new EventEmitter<[]>();
export function BitFlumeModal(): React.ReactElement { export function BitFlumeModal(): React.ReactElement {
const router = use.Router();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
function flume(): void { function flume(): void {
router.toBitVerse(true, false); Router.toBitVerse(true, false);
setOpen(false); setOpen(false);
} }

@ -5,7 +5,7 @@ import { uniqueId } from "lodash";
import React from "react"; import React from "react";
import { SpecialServers } from "../../Server/data/SpecialServers"; import { SpecialServers } from "../../Server/data/SpecialServers";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { StatsRow } from "../../ui/React/StatsRow"; import { StatsRow } from "../../ui/React/StatsRow";
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode"; import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
import { IBitNodeMultipliers } from "../BitNodeMultipliers"; import { IBitNodeMultipliers } from "../BitNodeMultipliers";
@ -33,13 +33,12 @@ export function BitnodeMultiplierDescription({ n, level }: IProps): React.ReactE
} }
export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElement => { export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElement => {
const player = use.Player();
// If a level argument has been provided, use that as the multiplier level // If a level argument has been provided, use that as the multiplier level
// If not, then we have to assume that we want the next level up from the // If not, then we have to assume that we want the next level up from the
// current node's source file, so we get the min of that, the SF's max level, // current node's source file, so we get the min of that, the SF's max level,
// or if it's BN12, ∞ // or if it's BN12, ∞
const maxSfLevel = n === 12 ? Infinity : 3; const maxSfLevel = n === 12 ? Infinity : 3;
const mults = getBitNodeMultipliers(n, level ?? Math.min(player.sourceFileLvl(n) + 1, maxSfLevel)); const mults = getBitNodeMultipliers(n, level ?? Math.min(Player.sourceFileLvl(n) + 1, maxSfLevel));
return ( return (
<Box sx={{ columnCount: 2, columnGap: 1, mb: -2 }}> <Box sx={{ columnCount: 2, columnGap: 1, mb: -2 }}>
@ -277,8 +276,7 @@ function InfiltrationMults({ mults }: IMultsProps): React.ReactElement {
} }
function BladeburnerMults({ mults }: IMultsProps): React.ReactElement { function BladeburnerMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player(); if (!Player.canAccessBladeburner()) return <></>;
if (!player.canAccessBladeburner()) return <></>;
if (mults.BladeburnerRank === 0) { if (mults.BladeburnerRank === 0) {
const rows: IBNMultRows = { const rows: IBNMultRows = {
@ -297,8 +295,7 @@ function BladeburnerMults({ mults }: IMultsProps): React.ReactElement {
} }
function StanekMults({ mults }: IMultsProps): React.ReactElement { function StanekMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player(); if (!Player.canAccessCotMG()) return <></>;
if (!player.canAccessCotMG()) return <></>;
const extraSize = mults.StaneksGiftExtraSize.toFixed(3); const extraSize = mults.StaneksGiftExtraSize.toFixed(3);
const rows: IBNMultRows = { const rows: IBNMultRows = {
@ -313,8 +310,7 @@ function StanekMults({ mults }: IMultsProps): React.ReactElement {
} }
function GangMults({ mults }: IMultsProps): React.ReactElement { function GangMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player(); if (Player.bitNodeN !== 2 && Player.sourceFileLvl(2) <= 0) return <></>;
if (player.bitNodeN !== 2 && player.sourceFileLvl(2) <= 0) return <></>;
const rows: IBNMultRows = { const rows: IBNMultRows = {
GangSoftcap: { GangSoftcap: {
@ -328,8 +324,7 @@ function GangMults({ mults }: IMultsProps): React.ReactElement {
} }
function CorporationMults({ mults }: IMultsProps): React.ReactElement { function CorporationMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player(); if (!Player.canAccessCorporation()) return <></>;
if (!player.canAccessCorporation()) return <></>;
if (mults.CorporationSoftcap < 0.15) { if (mults.CorporationSoftcap < 0.15) {
const rows: IBNMultRows = { const rows: IBNMultRows = {

@ -1,10 +1,8 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { IRouter } from "../../ui/Router";
import { BitNodes } from "../BitNode"; import { BitNodes } from "../BitNode";
import { enterBitNode } from "../../RedPill";
import { PortalModal } from "./PortalModal"; import { PortalModal } from "./PortalModal";
import { CinematicText } from "../../ui/React/CinematicText"; import { CinematicText } from "../../ui/React/CinematicText";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
@ -46,7 +44,6 @@ interface IPortalProps {
level: number; level: number;
destroyedBitNode: number; destroyedBitNode: number;
flume: boolean; flume: boolean;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
} }
function BitNodePortal(props: IPortalProps): React.ReactElement { function BitNodePortal(props: IPortalProps): React.ReactElement {
const [portalOpen, setPortalOpen] = useState(false); const [portalOpen, setPortalOpen] = useState(false);
@ -105,7 +102,6 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
onClose={() => setPortalOpen(false)} onClose={() => setPortalOpen(false)}
n={props.n} n={props.n}
level={props.level} level={props.level}
enter={props.enter}
destroyedBitNode={props.destroyedBitNode} destroyedBitNode={props.destroyedBitNode}
flume={props.flume} flume={props.flume}
/> />
@ -118,13 +114,10 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
interface IProps { interface IProps {
flume: boolean; flume: boolean;
quick: boolean; quick: boolean;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
} }
export function BitverseRoot(props: IProps): React.ReactElement { export function BitverseRoot(props: IProps): React.ReactElement {
const player = use.Player(); const destroyed = Player.bitNodeN;
const enter = enterBitNode;
const destroyed = player.bitNodeN;
const [destroySequence, setDestroySequence] = useState(!props.quick); const [destroySequence, setDestroySequence] = useState(!props.quick);
if (destroySequence) { if (destroySequence) {
@ -158,7 +151,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
} }
const nextSourceFileLvl = (n: number): number => { const nextSourceFileLvl = (n: number): number => {
const lvl = player.sourceFileLvl(n); const lvl = Player.sourceFileLvl(n);
if (n !== destroyed) { if (n !== destroyed) {
return lvl; return lvl;
} }
@ -181,7 +174,6 @@ export function BitverseRoot(props: IProps): React.ReactElement {
key={node.number} key={node.number}
n={node.number} n={node.number}
level={nextSourceFileLvl(node.number)} level={nextSourceFileLvl(node.number)}
enter={enter}
flume={props.flume} flume={props.flume}
destroyedBitNode={destroyed} destroyedBitNode={destroyed}
/> />
@ -234,19 +226,19 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={n(13)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={n(13)} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={n(10)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={n(11)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={n(10)} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={n(11)} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={n(9)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={n(12)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={n(9)} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={n(12)} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={n(7)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={n(8)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={n(7)} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={n(8)} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={n(5)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={n(6)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={n(5)} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={n(6)} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={n(1)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={n(2)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={n(3)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={n(4)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={n(1)} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={n(2)} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={n(3)} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={n(4)} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
<br /> <br />

@ -1,8 +1,7 @@
import React from "react"; import React from "react";
import { enterBitNode } from "../../RedPill";
import { BitNodes } from "../BitNode"; import { BitNodes } from "../BitNode";
import { IRouter } from "../../ui/Router";
import { use } from "../../ui/Context";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -15,11 +14,9 @@ interface IProps {
level: number; level: number;
destroyedBitNode: number; destroyedBitNode: number;
flume: boolean; flume: boolean;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
} }
export function PortalModal(props: IProps): React.ReactElement { export function PortalModal(props: IProps): React.ReactElement {
const router = use.Router();
const bitNodeKey = "BitNode" + props.n; const bitNodeKey = "BitNode" + props.n;
const bitNode = BitNodes[bitNodeKey]; const bitNode = BitNodes[bitNodeKey];
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`); if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
@ -48,7 +45,7 @@ export function PortalModal(props: IProps): React.ReactElement {
aria-label={`enter-bitnode-${bitNode.number.toString()}`} aria-label={`enter-bitnode-${bitNode.number.toString()}`}
autoFocus={true} autoFocus={true}
onClick={() => { onClick={() => {
props.enter(router, props.flume, props.destroyedBitNode, props.n); enterBitNode(props.flume, props.destroyedBitNode, props.n);
props.onClose(); props.onClose();
}} }}
> >

@ -3,9 +3,12 @@ import { getRandomInt } from "../utils/helpers/getRandomInt";
import { addOffset } from "../utils/helpers/addOffset"; import { addOffset } from "../utils/helpers/addOffset";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { BladeburnerConstants } from "./data/Constants"; import { BladeburnerConstants } from "./data/Constants";
import { IBladeburner } from "./IBladeburner"; import { Bladeburner } from "./Bladeburner";
import { IAction, ISuccessChanceParams } from "./IAction"; import { Person } from "../PersonObjects/Person";
import { IPerson } from "../PersonObjects/IPerson";
interface ISuccessChanceParams {
est: boolean;
}
class StatsMultiplier { class StatsMultiplier {
[key: string]: number; [key: string]: number;
@ -41,7 +44,7 @@ export interface IActionParams {
teamCount?: number; teamCount?: number;
} }
export class Action implements IAction { export class Action {
name = ""; name = "";
// Difficulty scales with level. See getDifficulty() method // Difficulty scales with level. See getDifficulty() method
@ -153,7 +156,7 @@ export class Action implements IAction {
* Tests for success. Should be called when an action has completed * Tests for success. Should be called when an action has completed
* @param inst {Bladeburner} - Bladeburner instance * @param inst {Bladeburner} - Bladeburner instance
*/ */
attempt(inst: IBladeburner, person: IPerson): boolean { attempt(inst: Bladeburner, person: Person): boolean {
return Math.random() < this.getSuccessChance(inst, person); return Math.random() < this.getSuccessChance(inst, person);
} }
@ -162,7 +165,7 @@ export class Action implements IAction {
return 1; return 1;
} }
getActionTime(inst: IBladeburner, person: IPerson): number { getActionTime(inst: Bladeburner, person: Person): number {
const difficulty = this.getDifficulty(); const difficulty = this.getDifficulty();
let baseTime = difficulty / BladeburnerConstants.DifficultyToTimeFactor; let baseTime = difficulty / BladeburnerConstants.DifficultyToTimeFactor;
const skillFac = inst.skillMultipliers.actionTime; // Always < 1 const skillFac = inst.skillMultipliers.actionTime; // Always < 1
@ -183,16 +186,16 @@ export class Action implements IAction {
// For actions that have teams. To be implemented by subtypes. // For actions that have teams. To be implemented by subtypes.
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
getTeamSuccessBonus(inst: IBladeburner): number { getTeamSuccessBonus(inst: Bladeburner): number {
return 1; return 1;
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
getActionTypeSkillSuccessBonus(inst: IBladeburner): number { getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
return 1; return 1;
} }
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number { getChaosCompetencePenalty(inst: Bladeburner, params: ISuccessChanceParams): number {
const city = inst.getCurrentCity(); const city = inst.getCurrentCity();
if (params.est) { if (params.est) {
return Math.pow(city.popEst / BladeburnerConstants.PopulationThreshold, BladeburnerConstants.PopulationExponent); return Math.pow(city.popEst / BladeburnerConstants.PopulationThreshold, BladeburnerConstants.PopulationExponent);
@ -201,7 +204,7 @@ export class Action implements IAction {
} }
} }
getChaosDifficultyBonus(inst: IBladeburner /*, params: ISuccessChanceParams*/): number { getChaosDifficultyBonus(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
const city = inst.getCurrentCity(); const city = inst.getCurrentCity();
if (city.chaos > BladeburnerConstants.ChaosThreshold) { if (city.chaos > BladeburnerConstants.ChaosThreshold) {
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold); const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
@ -212,7 +215,7 @@ export class Action implements IAction {
return 1; return 1;
} }
getEstSuccessChance(inst: IBladeburner, person: IPerson): [number, number] { getEstSuccessChance(inst: Bladeburner, person: Person): [number, number] {
function clamp(x: number): number { function clamp(x: number): number {
return Math.max(0, Math.min(x, 1)); return Math.max(0, Math.min(x, 1));
} }
@ -233,7 +236,7 @@ export class Action implements IAction {
* @params - options: * @params - options:
* est (bool): Get success chance estimate instead of real success chance * est (bool): Get success chance estimate instead of real success chance
*/ */
getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams = { est: false }): number { getSuccessChance(inst: Bladeburner, person: Person, params: ISuccessChanceParams = { est: false }): number {
if (inst == null) { if (inst == null) {
throw new Error("Invalid Bladeburner instance passed into Action.getSuccessChance"); throw new Error("Invalid Bladeburner instance passed into Action.getSuccessChance");
} }

@ -1,4 +1,3 @@
import { IActionIdentifier } from "./IActionIdentifier";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
interface IParams { interface IParams {
@ -6,7 +5,7 @@ interface IParams {
type?: number; type?: number;
} }
export class ActionIdentifier implements IActionIdentifier { export class ActionIdentifier {
name = ""; name = "";
type = -1; type = -1;

@ -12,11 +12,11 @@ export class BlackOperation extends Operation {
return 1.5; return 1.5;
} }
getChaosCompetencePenalty(/*inst: IBladeburner, params: ISuccessChanceParams*/): number { getChaosCompetencePenalty(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
return 1; return 1;
} }
getChaosDifficultyBonus(/*inst: IBladeburner, params: ISuccessChanceParams*/): number { getChaosDifficultyBonus(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
return 1; return 1;
} }

@ -1,6 +1,4 @@
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { IBladeburner } from "./IBladeburner";
import { IActionIdentifier } from "./IActionIdentifier";
import { ActionIdentifier } from "./ActionIdentifier"; import { ActionIdentifier } from "./ActionIdentifier";
import { ActionTypes } from "./data/ActionTypes"; import { ActionTypes } from "./data/ActionTypes";
import { Growths } from "./data/Growths"; import { Growths } from "./data/Growths";
@ -13,11 +11,11 @@ import { formatNumber } from "../utils/StringHelperFunctions";
import { Skills } from "./Skills"; import { Skills } from "./Skills";
import { Skill } from "./Skill"; import { Skill } from "./Skill";
import { City } from "./City"; import { City } from "./City";
import { IAction } from "./IAction"; import { Action } from "./Action";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker"; import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IPerson } from "../PersonObjects/IPerson"; import { Person } from "../PersonObjects/Person";
import { IRouter } from "../ui/Router"; import { Router } from "../ui/GameRoot";
import { ConsoleHelpText } from "./data/Help"; import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
@ -25,7 +23,6 @@ import { BladeburnerConstants } from "./data/Constants";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../utils/helpers/addOffset"; import { addOffset } from "../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital"; import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
@ -39,13 +36,13 @@ import { KEY } from "../utils/helpers/keyCodes";
import { isSleeveInfiltrateWork } from "../PersonObjects/Sleeve/Work/SleeveInfiltrateWork"; import { isSleeveInfiltrateWork } from "../PersonObjects/Sleeve/Work/SleeveInfiltrateWork";
import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportWork"; import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportWork";
interface BlackOpsAttempt { export interface BlackOpsAttempt {
error?: string; error?: string;
isAvailable?: boolean; isAvailable?: boolean;
action?: BlackOperation; action?: BlackOperation;
} }
export class Bladeburner implements IBladeburner { export class Bladeburner {
numHosp = 0; numHosp = 0;
moneyLost = 0; moneyLost = 0;
rank = 0; rank = 0;
@ -67,7 +64,7 @@ export class Bladeburner implements IBladeburner {
actionTimeCurrent = 0; actionTimeCurrent = 0;
actionTimeOverflow = 0; actionTimeOverflow = 0;
action: IActionIdentifier = new ActionIdentifier({ action: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"], type: ActionTypes["Idle"],
}); });
@ -89,18 +86,18 @@ export class Bladeburner implements IBladeburner {
events: true, events: true,
}; };
automateEnabled = false; automateEnabled = false;
automateActionHigh: IActionIdentifier = new ActionIdentifier({ automateActionHigh: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"], type: ActionTypes["Idle"],
}); });
automateThreshHigh = 0; automateThreshHigh = 0;
automateActionLow: IActionIdentifier = new ActionIdentifier({ automateActionLow: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"], type: ActionTypes["Idle"],
}); });
automateThreshLow = 0; automateThreshLow = 0;
consoleHistory: string[] = []; consoleHistory: string[] = [];
consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"]; consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"];
constructor(player?: IPlayer) { constructor() {
for (let i = 0; i < BladeburnerConstants.CityNames.length; ++i) { for (let i = 0; i < BladeburnerConstants.CityNames.length; ++i) {
this.cities[BladeburnerConstants.CityNames[i]] = new City(BladeburnerConstants.CityNames[i]); this.cities[BladeburnerConstants.CityNames[i]] = new City(BladeburnerConstants.CityNames[i]);
} }
@ -108,16 +105,14 @@ export class Bladeburner implements IBladeburner {
this.updateSkillMultipliers(); // Calls resetSkillMultipliers() this.updateSkillMultipliers(); // Calls resetSkillMultipliers()
// Max Stamina is based on stats and Bladeburner-specific bonuses // Max Stamina is based on stats and Bladeburner-specific bonuses
if (player) this.calculateMaxStamina(player); this.calculateMaxStamina();
this.stamina = this.maxStamina; this.stamina = this.maxStamina;
this.create(); this.create();
} }
getCurrentCity(): City { getCurrentCity(): City {
const city = this.cities[this.city]; const city = this.cities[this.city];
if (!(city instanceof City)) { if (!city) throw new Error("Invalid city in Bladeburner.getCurrentCity()");
throw new Error("Bladeburner.getCurrentCity() did not properly return a City object");
}
return city; return city;
} }
@ -125,7 +120,7 @@ export class Bladeburner implements IBladeburner {
return Math.min(1, this.stamina / (0.5 * this.maxStamina)); return Math.min(1, this.stamina / (0.5 * this.maxStamina));
} }
canAttemptBlackOp(actionId: IActionIdentifier): BlackOpsAttempt { canAttemptBlackOp(actionId: ActionIdentifier): BlackOpsAttempt {
// Safety measure - don't repeat BlackOps that are already done // Safety measure - don't repeat BlackOps that are already done
if (this.blackops[actionId.name] != null) { if (this.blackops[actionId.name] != null) {
return { error: "Tried to start a Black Operation that had already been completed" }; return { error: "Tried to start a Black Operation that had already been completed" };
@ -162,7 +157,8 @@ export class Bladeburner implements IBladeburner {
return { isAvailable: true, action }; return { isAvailable: true, action };
} }
startAction(person: IPerson, actionId: IActionIdentifier): void { /** This function is only for the player. Sleeves use their own functions to perform blade work. */
startAction(actionId: ActionIdentifier): void {
if (actionId == null) return; if (actionId == null) return;
this.action = actionId; this.action = actionId;
this.actionTimeCurrent = 0; this.actionTimeCurrent = 0;
@ -179,7 +175,7 @@ export class Bladeburner implements IBladeburner {
if (action.count < 1) { if (action.count < 1) {
return this.resetAction(); return this.resetAction();
} }
this.actionTimeToComplete = action.getActionTime(this, person); this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
@ -196,7 +192,7 @@ export class Bladeburner implements IBladeburner {
if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) { if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
return this.resetAction(); return this.resetAction();
} }
this.actionTimeToComplete = action.getActionTime(this, person); this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
@ -214,14 +210,14 @@ export class Bladeburner implements IBladeburner {
if (testBlackOp.action === undefined) { if (testBlackOp.action === undefined) {
throw new Error("action should not be null"); throw new Error("action should not be null");
} }
this.actionTimeToComplete = testBlackOp.action.getActionTime(this, person); this.actionTimeToComplete = testBlackOp.action.getActionTime(this, Player);
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
break; break;
} }
case ActionTypes["Recruitment"]: case ActionTypes["Recruitment"]:
this.actionTimeToComplete = this.getRecruitmentTime(person); this.actionTimeToComplete = this.getRecruitmentTime(Player);
break; break;
case ActionTypes["Training"]: case ActionTypes["Training"]:
case ActionTypes["FieldAnalysis"]: case ActionTypes["FieldAnalysis"]:
@ -234,7 +230,7 @@ export class Bladeburner implements IBladeburner {
this.actionTimeToComplete = 60; this.actionTimeToComplete = 60;
break; break;
default: default:
throw new Error("Invalid Action Type in startAction(Bladeburner,player, ): " + actionId.type); throw new Error("Invalid Action Type in bladeburner.startAction(): " + actionId.type);
} }
} }
@ -252,7 +248,7 @@ export class Bladeburner implements IBladeburner {
this.updateSkillMultipliers(); this.updateSkillMultipliers();
} }
executeConsoleCommands(player: IPlayer, commands: string): void { executeConsoleCommands(commands: string): void {
try { try {
// Console History // Console History
if (this.consoleHistory[this.consoleHistory.length - 1] != commands) { if (this.consoleHistory[this.consoleHistory.length - 1] != commands) {
@ -264,7 +260,7 @@ export class Bladeburner implements IBladeburner {
const arrayOfCommands = commands.split(";"); const arrayOfCommands = commands.split(";");
for (let i = 0; i < arrayOfCommands.length; ++i) { for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]); this.executeConsoleCommand(arrayOfCommands[i]);
} }
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
@ -309,7 +305,7 @@ export class Bladeburner implements IBladeburner {
} }
// working on // working on
getActionIdFromTypeAndName(type = "", name = ""): IActionIdentifier | null { getActionIdFromTypeAndName(type = "", name = ""): ActionIdentifier | null {
if (type === "" || name === "") { if (type === "" || name === "") {
return null; return null;
} }
@ -394,7 +390,7 @@ export class Bladeburner implements IBladeburner {
return null; return null;
} }
executeStartConsoleCommand(player: IPlayer, args: string[]): void { executeStartConsoleCommand(args: string[]): void {
if (args.length !== 3) { if (args.length !== 3) {
this.postToConsole("Invalid usage of 'start' console command: start [type] [name]"); this.postToConsole("Invalid usage of 'start' console command: start [type] [name]");
this.postToConsole("Use 'help start' for more info"); this.postToConsole("Use 'help start' for more info");
@ -407,7 +403,7 @@ export class Bladeburner implements IBladeburner {
if (GeneralActions[name] != null) { if (GeneralActions[name] != null) {
this.action.type = ActionTypes[name]; this.action.type = ActionTypes[name];
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid action name specified: " + args[2]); this.postToConsole("Invalid action name specified: " + args[2]);
} }
@ -417,7 +413,7 @@ export class Bladeburner implements IBladeburner {
if (this.contracts[name] != null) { if (this.contracts[name] != null) {
this.action.type = ActionTypes.Contract; this.action.type = ActionTypes.Contract;
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid contract name specified: " + args[2]); this.postToConsole("Invalid contract name specified: " + args[2]);
} }
@ -429,7 +425,7 @@ export class Bladeburner implements IBladeburner {
if (this.operations[name] != null) { if (this.operations[name] != null) {
this.action.type = ActionTypes.Operation; this.action.type = ActionTypes.Operation;
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid Operation name specified: " + args[2]); this.postToConsole("Invalid Operation name specified: " + args[2]);
} }
@ -441,7 +437,7 @@ export class Bladeburner implements IBladeburner {
if (BlackOperations[name] != null) { if (BlackOperations[name] != null) {
this.action.type = ActionTypes.BlackOperation; this.action.type = ActionTypes.BlackOperation;
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid BlackOp name specified: " + args[2]); this.postToConsole("Invalid BlackOp name specified: " + args[2]);
} }
@ -542,7 +538,7 @@ export class Bladeburner implements IBladeburner {
case 3: { case 3: {
const skillName = args[2]; const skillName = args[2];
const skill = Skills[skillName]; const skill = Skills[skillName];
if (skill == null || !(skill instanceof Skill)) { if (!skill) {
this.postToConsole("Invalid skill name (Note that it is case-sensitive): " + skillName); this.postToConsole("Invalid skill name (Note that it is case-sensitive): " + skillName);
break; break;
} }
@ -684,10 +680,7 @@ export class Bladeburner implements IBladeburner {
".", ".",
); );
} else if (flag.toLowerCase().includes("en")) { } else if (flag.toLowerCase().includes("en")) {
if ( if (!this.automateActionLow || !this.automateActionHigh) {
!(this.automateActionLow instanceof ActionIdentifier) ||
!(this.automateActionHigh instanceof ActionIdentifier)
) {
return this.log("Failed to enable automation. Actions were not set"); return this.log("Failed to enable automation. Actions were not set");
} }
this.automateEnabled = true; this.automateEnabled = true;
@ -820,7 +813,7 @@ export class Bladeburner implements IBladeburner {
return args; return args;
} }
executeConsoleCommand(player: IPlayer, command: string): void { executeConsoleCommand(command: string): void {
command = command.trim(); command = command.trim();
command = command.replace(/\s\s+/g, " "); // Replace all whitespace w/ a single space command = command.replace(/\s\s+/g, " "); // Replace all whitespace w/ a single space
@ -845,7 +838,7 @@ export class Bladeburner implements IBladeburner {
this.executeSkillConsoleCommand(args); this.executeSkillConsoleCommand(args);
break; break;
case "start": case "start":
this.executeStartConsoleCommand(player, args); this.executeStartConsoleCommand(args);
break; break;
case "stop": case "stop":
this.resetAction(); this.resetAction();
@ -898,9 +891,7 @@ export class Bladeburner implements IBladeburner {
// Choose random source/destination city for events // Choose random source/destination city for events
const sourceCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)]; const sourceCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)];
const sourceCity = this.cities[sourceCityName]; const sourceCity = this.cities[sourceCityName];
if (!(sourceCity instanceof City)) { if (!sourceCity) throw new Error("Invalid sourceCity in Bladeburner.randomEvent()");
throw new Error("sourceCity was not a City object in Bladeburner.randomEvent()");
}
let destCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)]; let destCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)];
while (destCityName === sourceCityName) { while (destCityName === sourceCityName) {
@ -908,9 +899,7 @@ export class Bladeburner implements IBladeburner {
} }
const destCity = this.cities[destCityName]; const destCity = this.cities[destCityName];
if (!(sourceCity instanceof City) || !(destCity instanceof City)) { if (!sourceCity || !destCity) throw new Error("Invalid sourceCity or destCity in Bladeburner.randomEvent()");
throw new Error("sourceCity/destCity was not a City object in Bladeburner.randomEvent()");
}
if (chance <= 0.05) { if (chance <= 0.05) {
// New Synthoid Community, 5% // New Synthoid Community, 5%
@ -994,7 +983,7 @@ export class Bladeburner implements IBladeburner {
* @param action(Action obj) - Derived action class * @param action(Action obj) - Derived action class
* @param success(bool) - Whether action was successful * @param success(bool) - Whether action was successful
*/ */
getActionStats(action: IAction, person: IPerson, success: boolean): ITaskTracker { getActionStats(action: Action, person: Person, success: boolean): ITaskTracker {
const difficulty = action.getDifficulty(); const difficulty = action.getDifficulty();
/** /**
@ -1024,7 +1013,7 @@ export class Bladeburner implements IBladeburner {
}; };
} }
getDiplomacyEffectiveness(person: IPerson): number { getDiplomacyEffectiveness(person: Person): number {
// Returns a decimal by which the city's chaos level should be multiplied (e.g. 0.98) // Returns a decimal by which the city's chaos level should be multiplied (e.g. 0.98)
const CharismaLinearFactor = 1e3; const CharismaLinearFactor = 1e3;
const CharismaExponentialFactor = 0.045; const CharismaExponentialFactor = 0.045;
@ -1034,11 +1023,11 @@ export class Bladeburner implements IBladeburner {
return (100 - charismaEff) / 100; return (100 - charismaEff) / 100;
} }
getRecruitmentSuccessChance(person: IPerson): number { getRecruitmentSuccessChance(person: Person): number {
return Math.pow(person.skills.charisma, 0.45) / (this.teamSize - this.sleeveSize + 1); return Math.pow(person.skills.charisma, 0.45) / (this.teamSize - this.sleeveSize + 1);
} }
getRecruitmentTime(person: IPerson): number { getRecruitmentTime(person: Person): number {
const effCharisma = person.skills.charisma * this.skillMultipliers.effCha; const effCharisma = person.skills.charisma * this.skillMultipliers.effCha;
const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90; const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90;
return Math.max(10, Math.round(BladeburnerConstants.BaseRecruitmentTimeNeeded - charismaFactor)); return Math.max(10, Math.round(BladeburnerConstants.BaseRecruitmentTimeNeeded - charismaFactor));
@ -1105,7 +1094,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
completeOperation(success: boolean, player: IPlayer): void { completeOperation(success: boolean): void {
if (this.action.type !== ActionTypes.Operation) { if (this.action.type !== ActionTypes.Operation) {
throw new Error("completeOperation() called even though current action is not an Operation"); throw new Error("completeOperation() called even though current action is not an Operation");
} }
@ -1126,7 +1115,7 @@ export class Bladeburner implements IBladeburner {
const losses = getRandomInt(0, max); const losses = getRandomInt(0, max);
this.teamSize -= losses; this.teamSize -= losses;
if (this.teamSize < this.sleeveSize) { if (this.teamSize < this.sleeveSize) {
const sup = player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork)); const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) { for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
const r = Math.floor(Math.random() * sup.length); const r = Math.floor(Math.random() * sup.length);
sup[r].takeDamage(sup[r].hp.max); sup[r].takeDamage(sup[r].hp.max);
@ -1201,7 +1190,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
getActionObject(actionId: IActionIdentifier): IAction | null { getActionObject(actionId: ActionIdentifier): Action | null {
/** /**
* Given an ActionIdentifier object, returns the corresponding * Given an ActionIdentifier object, returns the corresponding
* GeneralAction, Contract, Operation, or BlackOperation object * GeneralAction, Contract, Operation, or BlackOperation object
@ -1231,7 +1220,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
completeContract(success: boolean, actionIdent: IActionIdentifier): void { completeContract(success: boolean, actionIdent: ActionIdentifier): void {
if (actionIdent.type !== ActionTypes.Contract) { if (actionIdent.type !== ActionTypes.Contract) {
throw new Error("completeContract() called even though current action is not a Contract"); throw new Error("completeContract() called even though current action is not a Contract");
} }
@ -1256,7 +1245,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer = true): ITaskTracker { completeAction(person: Person, actionIdent: ActionIdentifier, isPlayer = true): ITaskTracker {
let retValue = createTaskTracker(); let retValue = createTaskTracker();
switch (actionIdent.type) { switch (actionIdent.type) {
case ActionTypes["Contract"]: case ActionTypes["Contract"]:
@ -1304,24 +1293,16 @@ export class Bladeburner implements IBladeburner {
this.changeRank(person, gain); this.changeRank(person, gain);
if (isOperation && this.logging.ops) { if (isOperation && this.logging.ops) {
this.log( this.log(
`${person.whoAmI()}: ` + `${person.whoAmI()}: ${action.name} successfully completed! Gained ${formatNumber(gain, 3)} rank`,
action.name +
" successfully completed! Gained " +
formatNumber(gain, 3) +
" rank",
); );
} else if (!isOperation && this.logging.contracts) { } else if (!isOperation && this.logging.contracts) {
this.log( this.log(
`${person.whoAmI()}: ` + `${person.whoAmI()}: ${action.name} contract successfully completed! Gained ` +
action.name + `${formatNumber(gain, 3)} rank and ${numeralWrapper.formatMoney(moneyGain)}`,
" contract successfully completed! Gained " +
formatNumber(gain, 3) +
" rank and " +
numeralWrapper.formatMoney(moneyGain),
); );
} }
} }
isOperation ? this.completeOperation(true, player) : this.completeContract(true, actionIdent); isOperation ? this.completeOperation(true) : this.completeContract(true, actionIdent);
} else { } else {
retValue = this.getActionStats(action, person, false); retValue = this.getActionStats(action, person, false);
++action.failures; ++action.failures;
@ -1335,7 +1316,7 @@ export class Bladeburner implements IBladeburner {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
this.hpLost += damage; this.hpLost += damage;
const cost = calculateHospitalizationCost(player, damage); const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) { if (person.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += cost; this.moneyLost += cost;
@ -1353,7 +1334,7 @@ export class Bladeburner implements IBladeburner {
} else if (!isOperation && this.logging.contracts) { } else if (!isOperation && this.logging.contracts) {
this.log(`${person.whoAmI()}: ` + action.name + " contract failed! " + logLossText); this.log(`${person.whoAmI()}: ` + action.name + " contract failed! " + logLossText);
} }
isOperation ? this.completeOperation(false, player) : this.completeContract(false, actionIdent); isOperation ? this.completeOperation(false) : this.completeContract(false, actionIdent);
} }
if (action.autoLevel) { if (action.autoLevel) {
action.level = action.maxLevel; action.level = action.maxLevel;
@ -1412,7 +1393,7 @@ export class Bladeburner implements IBladeburner {
if (action.hpLoss) { if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(player, damage); const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) { if (person.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += cost; this.moneyLost += cost;
@ -1440,7 +1421,7 @@ export class Bladeburner implements IBladeburner {
const losses = getRandomInt(1, teamLossMax); const losses = getRandomInt(1, teamLossMax);
this.teamSize -= losses; this.teamSize -= losses;
if (this.teamSize < this.sleeveSize) { if (this.teamSize < this.sleeveSize) {
const sup = player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork)); const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) { for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
const r = Math.floor(Math.random() * sup.length); const r = Math.floor(Math.random() * sup.length);
sup[r].takeDamage(sup[r].hp.max); sup[r].takeDamage(sup[r].hp.max);
@ -1603,8 +1584,8 @@ export class Bladeburner implements IBladeburner {
return retValue; return retValue;
} }
infiltrateSynthoidCommunities(p: IPlayer): void { infiltrateSynthoidCommunities(): void {
const infilSleeves = p.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length; const infilSleeves = Player.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length;
const amt = Math.pow(infilSleeves, -0.5) / 2; const amt = Math.pow(infilSleeves, -0.5) / 2;
for (const contract of Object.keys(this.contracts)) { for (const contract of Object.keys(this.contracts)) {
this.contracts[contract].count += amt; this.contracts[contract].count += amt;
@ -1617,7 +1598,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
changeRank(person: IPerson, change: number): void { changeRank(person: Person, change: number): void {
if (isNaN(change)) { if (isNaN(change)) {
throw new Error("NaN passed into Bladeburner.changeRank()"); throw new Error("NaN passed into Bladeburner.changeRank()");
} }
@ -1630,7 +1611,7 @@ export class Bladeburner implements IBladeburner {
const bladeburnersFactionName = FactionNames.Bladeburners; const bladeburnersFactionName = FactionNames.Bladeburners;
if (factionExists(bladeburnersFactionName)) { if (factionExists(bladeburnersFactionName)) {
const bladeburnerFac = Factions[bladeburnersFactionName]; const bladeburnerFac = Factions[bladeburnersFactionName];
if (!(bladeburnerFac instanceof Faction)) { if (!bladeburnerFac) {
throw new Error( throw new Error(
`Could not properly get ${FactionNames.Bladeburners} Faction object in ${FactionNames.Bladeburners} UI Overview Faction button`, `Could not properly get ${FactionNames.Bladeburners} Faction object in ${FactionNames.Bladeburners} UI Overview Faction button`,
); );
@ -1654,12 +1635,12 @@ export class Bladeburner implements IBladeburner {
} }
} }
processAction(router: IRouter, player: IPlayer, seconds: number): void { processAction(seconds: number): void {
if (this.action.type === ActionTypes["Idle"]) return; if (this.action.type === ActionTypes["Idle"]) return;
if (this.actionTimeToComplete <= 0) { if (this.actionTimeToComplete <= 0) {
throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`); throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`);
} }
if (!(this.action instanceof ActionIdentifier)) { if (!this.action) {
throw new Error("Bladeburner.action is not an ActionIdentifier Object"); throw new Error("Bladeburner.action is not an ActionIdentifier Object");
} }
@ -1670,31 +1651,31 @@ export class Bladeburner implements IBladeburner {
if (this.actionTimeCurrent >= this.actionTimeToComplete) { if (this.actionTimeCurrent >= this.actionTimeToComplete) {
this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete; this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete;
const action = this.getActionObject(this.action); const action = this.getActionObject(this.action);
const retValue = this.completeAction(player, player, this.action); const retValue = this.completeAction(Player, this.action);
player.gainMoney(retValue.money, "bladeburner"); Player.gainMoney(retValue.money, "bladeburner");
player.gainStats(retValue); Player.gainStats(retValue);
// Operation Daedalus // Operation Daedalus
if (action == null) { if (action == null) {
throw new Error("Failed to get BlackOperation Object for: " + this.action.name); throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
} else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) { } else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) {
this.startAction(player, this.action); // Repeat action this.startAction(this.action); // Repeat action
} }
} }
} }
calculateStaminaGainPerSecond(player: IPlayer): number { calculateStaminaGainPerSecond(): number {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi; const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor; const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor;
const gain = (BladeburnerConstants.StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17); const gain = (BladeburnerConstants.StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17);
return gain * (this.skillMultipliers.stamina * player.mults.bladeburner_stamina_gain); return gain * (this.skillMultipliers.stamina * Player.mults.bladeburner_stamina_gain);
} }
calculateMaxStamina(player: IPlayer): void { calculateMaxStamina(): void {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi; const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStamina = const maxStamina =
(Math.pow(effAgility, 0.8) + this.staminaBonus) * (Math.pow(effAgility, 0.8) + this.staminaBonus) *
this.skillMultipliers.stamina * this.skillMultipliers.stamina *
player.mults.bladeburner_max_stamina; Player.mults.bladeburner_max_stamina;
if (this.maxStamina !== maxStamina) { if (this.maxStamina !== maxStamina) {
const oldMax = this.maxStamina; const oldMax = this.maxStamina;
this.maxStamina = maxStamina; this.maxStamina = maxStamina;
@ -1974,12 +1955,12 @@ export class Bladeburner implements IBladeburner {
}); });
} }
process(router: IRouter, player: IPlayer): void { process(): void {
// Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized. // Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized.
if (!router.isInitialized) return; if (!Router.isInitialized) return;
// If the Player starts doing some other actions, set action to idle and alert // If the Player starts doing some other actions, set action to idle and alert
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.currentWork) { if (!Player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && Player.currentWork) {
if (this.action.type !== ActionTypes["Idle"]) { if (this.action.type !== ActionTypes["Idle"]) {
let msg = "Your Bladeburner action was cancelled because you started doing something else."; let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) { if (this.automateEnabled) {
@ -2006,8 +1987,8 @@ export class Bladeburner implements IBladeburner {
this.storedCycles -= seconds * BladeburnerConstants.CyclesPerSecond; this.storedCycles -= seconds * BladeburnerConstants.CyclesPerSecond;
// Stamina // Stamina
this.calculateMaxStamina(player); this.calculateMaxStamina();
this.stamina += this.calculateStaminaGainPerSecond(player) * seconds; this.stamina += this.calculateStaminaGainPerSecond() * seconds;
this.stamina = Math.min(this.maxStamina, this.stamina); this.stamina = Math.min(this.maxStamina, this.stamina);
// Count increase for contracts/operations // Count increase for contracts/operations
@ -2027,9 +2008,7 @@ export class Bladeburner implements IBladeburner {
// Chaos goes down very slowly // Chaos goes down very slowly
for (const cityName of BladeburnerConstants.CityNames) { for (const cityName of BladeburnerConstants.CityNames) {
const city = this.cities[cityName]; const city = this.cities[cityName];
if (!(city instanceof City)) { if (!city) throw new Error("Invalid city when processing passive chaos reduction in Bladeburner.process");
throw new Error("Invalid City object when processing passive chaos reduction in Bladeburner.process");
}
city.chaos -= 0.0001 * seconds; city.chaos -= 0.0001 * seconds;
city.chaos = Math.max(0, city.chaos); city.chaos = Math.max(0, city.chaos);
} }
@ -2042,7 +2021,7 @@ export class Bladeburner implements IBladeburner {
this.randomEventCounter += getRandomInt(240, 600); this.randomEventCounter += getRandomInt(240, 600);
} }
this.processAction(router, player, seconds); this.processAction(seconds);
// Automation // Automation
if (this.automateEnabled) { if (this.automateEnabled) {
@ -2053,7 +2032,7 @@ export class Bladeburner implements IBladeburner {
type: this.automateActionLow.type, type: this.automateActionLow.type,
name: this.automateActionLow.name, name: this.automateActionLow.name,
}); });
this.startAction(player, this.action); this.startAction(this.action);
} }
} else if (this.stamina >= this.automateThreshHigh) { } else if (this.stamina >= this.automateThreshHigh) {
if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) { if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) {
@ -2061,14 +2040,14 @@ export class Bladeburner implements IBladeburner {
type: this.automateActionHigh.type, type: this.automateActionHigh.type,
name: this.automateActionHigh.name, name: this.automateActionHigh.name,
}); });
this.startAction(player, this.action); this.startAction(this.action);
} }
} }
} }
} }
} }
getTypeAndNameFromActionId(actionId: IActionIdentifier): { getTypeAndNameFromActionId(actionId: ActionIdentifier): {
type: string; type: string;
name: string; name: string;
} { } {
@ -2121,7 +2100,7 @@ export class Bladeburner implements IBladeburner {
return Object.keys(Skills); return Object.keys(Skills);
} }
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean { startActionNetscriptFn(type: string, name: string, workerScript: WorkerScript): boolean {
const errorLogText = `Invalid action: type='${type}' name='${name}'`; const errorLogText = `Invalid action: type='${type}' name='${name}'`;
const actionId = this.getActionIdFromTypeAndName(type, name); const actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) { if (actionId == null) {
@ -2139,7 +2118,7 @@ export class Bladeburner implements IBladeburner {
} }
try { try {
this.startAction(player, actionId); this.startAction(actionId);
workerScript.log( workerScript.log(
"bladeburner.startAction", "bladeburner.startAction",
() => `Starting bladeburner action with type '${type}' and name '${name}'`, () => `Starting bladeburner action with type '${type}' and name '${name}'`,
@ -2153,7 +2132,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string { getActionTimeNetscriptFn(person: Person, type: string, name: string): number | string {
const actionId = this.getActionIdFromTypeAndName(type, name); const actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) { if (actionId == null) {
return "bladeburner.getActionTime"; return "bladeburner.getActionTime";
@ -2184,7 +2163,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string { getActionEstimatedSuccessChanceNetscriptFn(person: Person, type: string, name: string): [number, number] | string {
const actionId = this.getActionIdFromTypeAndName(type, name); const actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) { if (actionId == null) {
return "bladeburner.getActionEstimatedSuccessChance"; return "bladeburner.getActionEstimatedSuccessChance";

@ -1,4 +1,4 @@
import { IBladeburner } from "./IBladeburner"; import { Bladeburner } from "./Bladeburner";
import { Action, IActionParams } from "./Action"; import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
@ -7,7 +7,7 @@ export class Contract extends Action {
super(params); super(params);
} }
getActionTypeSkillSuccessBonus(inst: IBladeburner): number { getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
return inst.skillMultipliers.successChanceContract; return inst.skillMultipliers.successChanceContract;
} }

@ -1,72 +0,0 @@
import { IReviverValue } from "../utils/JSONReviver";
import { IPerson } from "../PersonObjects/IPerson";
import { IBladeburner } from "./IBladeburner";
interface IStatsMultiplier {
[key: string]: number;
hack: number;
str: number;
def: number;
dex: number;
agi: number;
cha: number;
int: number;
}
export interface ISuccessChanceParams {
est: boolean;
}
export interface IAction {
name: string;
// Difficulty scales with level. See getDifficulty() method
level: number;
maxLevel: number;
autoLevel: boolean;
baseDifficulty: number;
difficultyFac: number;
// Rank increase/decrease is affected by this exponent
rewardFac: number;
successes: number;
failures: number;
// All of these scale with level/difficulty
rankGain: number;
rankLoss: number;
hpLoss: number;
hpLost: number;
// Action Category. Current categories are stealth and kill
isStealth: boolean;
isKill: boolean;
/**
* Number of this contract remaining, and its growth rate
* Growth rate is an integer and the count will increase by that integer every "cycle"
*/
count: number;
// Weighting of each stat in determining action success rate
weights: IStatsMultiplier;
// Diminishing returns of stats (stat ^ decay where 0 <= decay <= 1)
decays: IStatsMultiplier;
teamCount: number;
getDifficulty(): number;
attempt(inst: IBladeburner, person: IPerson): boolean;
getActionTimePenalty(): number;
getActionTime(inst: IBladeburner, person: IPerson): number;
getTeamSuccessBonus(inst: IBladeburner): number;
getActionTypeSkillSuccessBonus(inst: IBladeburner): number;
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number;
getChaosDifficultyBonus(inst: IBladeburner): number;
getEstSuccessChance(inst: IBladeburner, person: IPerson): [number, number];
getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams): number;
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number;
setMaxLevel(baseSuccessesPerLevel: number): void;
toJSON(): IReviverValue;
}

@ -1,4 +0,0 @@
export interface IActionIdentifier {
name: string;
type: number;
}

@ -1,121 +0,0 @@
import { IActionIdentifier } from "./IActionIdentifier";
import { City } from "./City";
import { Skill } from "./Skill";
import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IPerson } from "../PersonObjects/IPerson";
import { ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript";
import { Contract } from "./Contract";
import { Operation } from "./Operation";
export interface IBladeburner {
numHosp: number;
moneyLost: number;
rank: number;
maxRank: number;
skillPoints: number;
totalSkillPoints: number;
teamSize: number;
teamLost: number;
hpLost: number;
storedCycles: number;
randomEventCounter: number;
actionTimeToComplete: number;
actionTimeCurrent: number;
actionTimeOverflow: number;
action: IActionIdentifier;
cities: Record<string, City>;
city: string;
skills: Record<string, number>;
skillMultipliers: Record<string, number>;
staminaBonus: number;
maxStamina: number;
stamina: number;
contracts: Record<string, Contract>;
operations: Record<string, Operation>;
blackops: Record<string, boolean>;
logging: {
general: boolean;
contracts: boolean;
ops: boolean;
blackops: boolean;
events: boolean;
};
automateEnabled: boolean;
automateActionHigh: IActionIdentifier;
automateThreshHigh: number;
automateActionLow: IActionIdentifier;
automateThreshLow: number;
consoleHistory: string[];
consoleLogs: string[];
getCurrentCity(): City;
calculateStaminaPenalty(): number;
startAction(player: IPlayer, action: IActionIdentifier): void;
upgradeSkill(skill: Skill): void;
executeConsoleCommands(player: IPlayer, command: string): void;
postToConsole(input: string, saveToLogs?: boolean): void;
log(input: string): void;
resetAction(): void;
clearConsole(): void;
prestige(): void;
storeCycles(numCycles?: number): void;
getTypeAndNameFromActionId(actionId: IActionIdentifier): {
type: string;
name: string;
};
getContractNamesNetscriptFn(): string[];
getOperationNamesNetscriptFn(): string[];
getBlackOpNamesNetscriptFn(): string[];
getGeneralActionNamesNetscriptFn(): string[];
getSkillNamesNetscriptFn(): string[];
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean;
getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string;
getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string;
getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
getSkillLevelNetscriptFn(skillName: string, workerScript: WorkerScript): number;
getSkillUpgradeCostNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): number;
upgradeSkillNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): boolean;
getTeamSizeNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
setTeamSizeNetscriptFn(type: string, name: string, size: number, workerScript: WorkerScript): number;
joinBladeburnerFactionNetscriptFn(workerScript: WorkerScript): boolean;
getActionIdFromTypeAndName(type: string, name: string): IActionIdentifier | null;
executeStartConsoleCommand(player: IPlayer, args: string[]): void;
executeSkillConsoleCommand(args: string[]): void;
executeLogConsoleCommand(args: string[]): void;
executeHelpConsoleCommand(args: string[]): void;
executeAutomateConsoleCommand(args: string[]): void;
parseCommandArguments(command: string): string[];
executeConsoleCommand(player: IPlayer, command: string): void;
triggerMigration(sourceCityName: string): void;
triggerPotentialMigration(sourceCityName: string, chance: number): void;
randomEvent(): void;
getDiplomacyEffectiveness(player: IPlayer): number;
getRecruitmentSuccessChance(player: IPerson): number;
getRecruitmentTime(player: IPerson): number;
resetSkillMultipliers(): void;
updateSkillMultipliers(): void;
completeOperation(success: boolean, player: IPlayer): void;
getActionObject(actionId: IActionIdentifier): IAction | null;
completeContract(success: boolean, actionIdent: IActionIdentifier): void;
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer?: boolean): ITaskTracker;
infiltrateSynthoidCommunities(p: IPlayer): void;
changeRank(player: IPlayer, change: number): void;
processAction(router: IRouter, player: IPlayer, seconds: number): void;
calculateStaminaGainPerSecond(player: IPlayer): number;
calculateMaxStamina(player: IPlayer): void;
create(): void;
process(router: IRouter, player: IPlayer): void;
getActionStats(action: IAction, person: IPerson, success: boolean): ITaskTracker;
sleeveSupport(joining: boolean): void;
}

@ -1,4 +1,4 @@
import { IBladeburner } from "./IBladeburner"; import { Bladeburner } from "./Bladeburner";
import { BladeburnerConstants } from "./data/Constants"; import { BladeburnerConstants } from "./data/Constants";
import { Action, IActionParams } from "./Action"; import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
@ -19,7 +19,7 @@ export class Operation extends Action {
} }
// For actions that have teams. To be implemented by subtypes. // For actions that have teams. To be implemented by subtypes.
getTeamSuccessBonus(inst: IBladeburner): number { getTeamSuccessBonus(inst: Bladeburner): number {
if (this.teamCount && this.teamCount > 0) { if (this.teamCount && this.teamCount > 0) {
this.teamCount = Math.min(this.teamCount, inst.teamSize); this.teamCount = Math.min(this.teamCount, inst.teamSize);
const teamMultiplier = Math.pow(this.teamCount, 0.05); const teamMultiplier = Math.pow(this.teamCount, 0.05);
@ -29,11 +29,11 @@ export class Operation extends Action {
return 1; return 1;
} }
getActionTypeSkillSuccessBonus(inst: IBladeburner): number { getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
return inst.skillMultipliers.successChanceOperation; return inst.skillMultipliers.successChanceOperation;
} }
getChaosDifficultyBonus(inst: IBladeburner /*, params: ISuccessChanceParams*/): number { getChaosDifficultyBonus(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
const city = inst.getCurrentCity(); const city = inst.getCurrentCity();
if (city.chaos > BladeburnerConstants.ChaosThreshold) { if (city.chaos > BladeburnerConstants.ChaosThreshold) {
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold); const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);

@ -1,33 +1,5 @@
import { CityName } from "./../../Locations/data/CityNames"; import { CityName } from "./../../Locations/data/CityNames";
export const BladeburnerConstants: { export const BladeburnerConstants = {
CityNames: string[];
CyclesPerSecond: number;
StaminaGainPerSecond: number;
BaseStaminaLoss: number;
MaxStaminaToGainFactor: number;
DifficultyToTimeFactor: number;
DiffMultExponentialFactor: number;
DiffMultLinearFactor: number;
EffAgiLinearFactor: number;
EffDexLinearFactor: number;
EffAgiExponentialFactor: number;
EffDexExponentialFactor: number;
BaseRecruitmentTimeNeeded: number;
PopulationThreshold: number;
PopulationExponent: number;
ChaosThreshold: number;
BaseStatGain: number;
BaseIntGain: number;
ActionCountGrowthPeriod: number;
RankToFactionRepFactor: number;
RankNeededForFaction: number;
ContractSuccessesPerLevel: number;
OperationSuccessesPerLevel: number;
RanksPerSkillPoint: number;
ContractBaseMoneyGain: number;
HrcHpGain: number;
HrcStaminaGain: number;
} = {
CityNames: [ CityNames: [
CityName.Aevum, CityName.Aevum,
CityName.Chongqing, CityName.Chongqing,

@ -1,19 +1,4 @@
export const SkillNames: { export const SkillNames = {
BladesIntuition: string;
Cloak: string;
Marksman: string;
WeaponProficiency: string;
ShortCircuit: string;
DigitalObserver: string;
Tracer: string;
Overclock: string;
Reaper: string;
EvasiveSystem: string;
Datamancer: string;
CybersEdge: string;
HandsOfMidas: string;
Hyperdrive: string;
} = {
BladesIntuition: "Blade's Intuition", BladesIntuition: "Blade's Intuition",
Cloak: "Cloak", Cloak: "Cloak",
Marksman: "Marksman", Marksman: "Marksman",

@ -1,8 +1,7 @@
import React from "react"; import React from "react";
import { IAction } from "../IAction"; import { Action } from "../Action";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { BladeburnerConstants } from "../data/Constants"; import { BladeburnerConstants } from "../data/Constants";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
@ -12,29 +11,27 @@ import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown"; import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
interface IProps { interface IProps {
action: IAction; action: Action;
isActive: boolean; isActive: boolean;
bladeburner: IBladeburner; bladeburner: Bladeburner;
rerender: () => void; rerender: () => void;
} }
export function ActionLevel({ action, isActive, bladeburner, rerender }: IProps): React.ReactElement { export function ActionLevel({ action, isActive, bladeburner, rerender }: IProps): React.ReactElement {
const player = use.Player();
const canIncrease = action.level < action.maxLevel; const canIncrease = action.level < action.maxLevel;
const canDecrease = action.level > 1; const canDecrease = action.level > 1;
function increaseLevel(): void { function increaseLevel(): void {
if (!canIncrease) return; if (!canIncrease) return;
++action.level; ++action.level;
if (isActive) bladeburner.startAction(player, bladeburner.action); if (isActive) bladeburner.startAction(bladeburner.action);
rerender(); rerender();
} }
function decreaseLevel(): void { function decreaseLevel(): void {
if (!canDecrease) return; if (!canDecrease) return;
--action.level; --action.level;
if (isActive) bladeburner.startAction(player, bladeburner.action); if (isActive) bladeburner.startAction(bladeburner.action);
rerender(); rerender();
} }

@ -4,16 +4,14 @@ import { ContractPage } from "./ContractPage";
import { OperationPage } from "./OperationPage"; import { OperationPage } from "./OperationPage";
import { BlackOpPage } from "./BlackOpPage"; import { BlackOpPage } from "./BlackOpPage";
import { SkillPage } from "./SkillPage"; import { SkillPage } from "./SkillPage";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Tabs from "@mui/material/Tabs"; import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab"; import Tab from "@mui/material/Tab";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function AllPages(props: IProps): React.ReactElement { export function AllPages(props: IProps): React.ReactElement {
@ -33,10 +31,10 @@ export function AllPages(props: IProps): React.ReactElement {
<Tab label="Skills" /> <Tab label="Skills" />
</Tabs> </Tabs>
<Box sx={{ p: 1 }}> <Box sx={{ p: 1 }}>
{value === 0 && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />} {value === 0 && <GeneralActionPage bladeburner={props.bladeburner} />}
{value === 1 && <ContractPage bladeburner={props.bladeburner} player={props.player} />} {value === 1 && <ContractPage bladeburner={props.bladeburner} />}
{value === 2 && <OperationPage bladeburner={props.bladeburner} player={props.player} />} {value === 2 && <OperationPage bladeburner={props.bladeburner} />}
{value === 3 && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />} {value === 3 && <BlackOpPage bladeburner={props.bladeburner} />}
{value === 4 && <SkillPage bladeburner={props.bladeburner} />} {value === 4 && <SkillPage bladeburner={props.bladeburner} />}
</Box> </Box>
</> </>

@ -1,12 +1,12 @@
import React from "react"; import React from "react";
import { IAction } from "../IAction"; import { Action } from "../Action";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
interface IProps { interface IProps {
action: IAction; action: Action;
rerender: () => void; rerender: () => void;
} }

@ -3,10 +3,10 @@ import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/Stri
import { ActionTypes } from "../data/ActionTypes"; import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText"; import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { TeamSizeButton } from "./TeamSizeButton"; import { TeamSizeButton } from "./TeamSizeButton";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { BlackOperation } from "../BlackOperation"; import { BlackOperation } from "../BlackOperation";
import { BlackOperations } from "../data/BlackOperations"; import { BlackOperations } from "../data/BlackOperations";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import { SuccessChance } from "./SuccessChance"; import { SuccessChance } from "./SuccessChance";
import { StartButton } from "./StartButton"; import { StartButton } from "./StartButton";
@ -15,8 +15,7 @@ import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
action: BlackOperation; action: BlackOperation;
} }
@ -37,7 +36,7 @@ export function BlackOpElem(props: IProps): React.ReactElement {
const isActive = const isActive =
props.bladeburner.action.type === ActionTypes["BlackOperation"] && props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
props.action.name === props.bladeburner.action.name; props.action.name === props.bladeburner.action.name;
const actionTime = props.action.getActionTime(props.bladeburner, props.player); const actionTime = props.action.getActionTime(props.bladeburner, Player);
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank; const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
const computedActionTimeCurrent = Math.min( const computedActionTimeCurrent = Math.min(
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,

@ -2,12 +2,10 @@ import React from "react";
import { BlackOperations } from "../BlackOperations"; import { BlackOperations } from "../BlackOperations";
import { BlackOperation } from "../BlackOperation"; import { BlackOperation } from "../BlackOperation";
import { BlackOpElem } from "./BlackOpElem"; import { BlackOpElem } from "./BlackOpElem";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function BlackOpList(props: IProps): React.ReactElement { export function BlackOpList(props: IProps): React.ReactElement {
@ -35,7 +33,7 @@ export function BlackOpList(props: IProps): React.ReactElement {
return ( return (
<> <>
{blackops.map((blackop: BlackOperation) => ( {blackops.map((blackop: BlackOperation) => (
<BlackOpElem key={blackop.name} bladeburner={props.bladeburner} action={blackop} player={props.player} /> <BlackOpElem key={blackop.name} bladeburner={props.bladeburner} action={blackop} />
))} ))}
</> </>
); );

@ -1,21 +1,18 @@
import * as React from "react"; import * as React from "react";
import { BlackOpList } from "./BlackOpList"; import { BlackOpList } from "./BlackOpList";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { use } from "../../ui/Context"; import { Router } from "../../ui/GameRoot";
import { BlackOperationNames } from "../data/BlackOperationNames"; import { BlackOperationNames } from "../data/BlackOperationNames";
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import { CorruptableText } from "../../ui/React/CorruptableText"; import { CorruptableText } from "../../ui/React/CorruptableText";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function BlackOpPage(props: IProps): React.ReactElement { export function BlackOpPage(props: IProps): React.ReactElement {
const router = use.Router();
return ( return (
<> <>
<Typography> <Typography>
@ -33,11 +30,11 @@ export function BlackOpPage(props: IProps): React.ReactElement {
losses. losses.
</Typography> </Typography>
{props.bladeburner.blackops[BlackOperationNames.OperationDaedalus] ? ( {props.bladeburner.blackops[BlackOperationNames.OperationDaedalus] ? (
<Button sx={{ my: 1, p: 1 }} onClick={() => router.toBitVerse(false, false)}> <Button sx={{ my: 1, p: 1 }} onClick={() => Router.toBitVerse(false, false)}>
<CorruptableText content="Destroy w0rld_d34mon"></CorruptableText> <CorruptableText content="Destroy w0rld_d34mon"></CorruptableText>
</Button> </Button>
) : ( ) : (
<BlackOpList bladeburner={props.bladeburner} player={props.player} /> <BlackOpList bladeburner={props.bladeburner} />
)} )}
</> </>
); );

@ -1,11 +1,10 @@
import React from "react"; import React from "react";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { use } from "../../ui/Context"; import { Router } from "../../ui/GameRoot";
import { CinematicText } from "../../ui/React/CinematicText"; import { CinematicText } from "../../ui/React/CinematicText";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
export function BladeburnerCinematic(): React.ReactElement { export function BladeburnerCinematic(): React.ReactElement {
const router = use.Router();
return ( return (
<CinematicText <CinematicText
lines={[ lines={[
@ -32,7 +31,7 @@ export function BladeburnerCinematic(): React.ReactElement {
"investigating and dealing with Synthoid threats.", "investigating and dealing with Synthoid threats.",
]} ]}
onDone={() => { onDone={() => {
router.toTerminal(); Router.toTerminal();
dialogBoxCreate( dialogBoxCreate(
`Visit the National Security Agency (NSA) to apply for their ${FactionNames.Bladeburners} ` + `Visit the National Security Agency (NSA) to apply for their ${FactionNames.Bladeburners} ` +
"division! You will need 100 of each combat stat before doing this.", "division! You will need 100 of each combat stat before doing this.",

@ -3,12 +3,10 @@ import { Stats } from "./Stats";
import { Console } from "./Console"; import { Console } from "./Console";
import { AllPages } from "./AllPages"; import { AllPages } from "./AllPages";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
export function BladeburnerRoot(): React.ReactElement { export function BladeburnerRoot(): React.ReactElement {
const player = use.Player();
const router = use.Router();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
@ -19,16 +17,16 @@ export function BladeburnerRoot(): React.ReactElement {
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) return <></>; if (!bladeburner) return <></>;
return ( return (
<Box display="flex" flexDirection="column"> <Box display="flex" flexDirection="column">
<Box sx={{ display: "grid", gridTemplateColumns: "4fr 8fr", p: 1 }}> <Box sx={{ display: "grid", gridTemplateColumns: "4fr 8fr", p: 1 }}>
<Stats bladeburner={bladeburner} player={player} router={router} /> <Stats bladeburner={bladeburner} />
<Console bladeburner={bladeburner} player={player} /> <Console bladeburner={bladeburner} />
</Box> </Box>
<AllPages bladeburner={bladeburner} player={player} /> <AllPages bladeburner={bladeburner} />
</Box> </Box>
); );
} }

@ -1,8 +1,7 @@
import React, { useState, useRef, useEffect } from "react"; import React, { useState, useRef, useEffect } from "react";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../utils/helpers/keyCodes";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import List from "@mui/material/List"; import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem"; import ListItem from "@mui/material/ListItem";
@ -49,8 +48,7 @@ function Line(props: ILineProps): React.ReactElement {
} }
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function Console(props: IProps): React.ReactElement { export function Console(props: IProps): React.ReactElement {
@ -81,7 +79,7 @@ export function Console(props: IProps): React.ReactElement {
event.preventDefault(); event.preventDefault();
if (command.length > 0) { if (command.length > 0) {
props.bladeburner.postToConsole("> " + command); props.bladeburner.postToConsole("> " + command);
props.bladeburner.executeConsoleCommands(props.player, command); props.bladeburner.executeConsoleCommands(command);
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length); setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
setCommand(""); setCommand("");
} }

@ -3,9 +3,9 @@ import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText"; import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { Contracts } from "../data/Contracts"; import { Contracts } from "../data/Contracts";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IAction } from "../IAction"; import { Action } from "../Action";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { SuccessChance } from "./SuccessChance"; import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import { ActionLevel } from "./ActionLevel"; import { ActionLevel } from "./ActionLevel";
@ -16,9 +16,8 @@ import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer; action: Action;
action: IAction;
} }
export function ContractElem(props: IProps): React.ReactElement { export function ContractElem(props: IProps): React.ReactElement {
@ -32,7 +31,7 @@ export function ContractElem(props: IProps): React.ReactElement {
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
props.bladeburner.actionTimeToComplete, props.bladeburner.actionTimeToComplete,
); );
const actionTime = props.action.getActionTime(props.bladeburner, props.player); const actionTime = props.action.getActionTime(props.bladeburner, Player);
const actionData = Contracts[props.action.name]; const actionData = Contracts[props.action.name];
if (actionData === undefined) { if (actionData === undefined) {

@ -1,11 +1,9 @@
import React from "react"; import React from "react";
import { ContractElem } from "./ContractElem"; import { ContractElem } from "./ContractElem";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function ContractList(props: IProps): React.ReactElement { export function ContractList(props: IProps): React.ReactElement {
@ -14,7 +12,7 @@ export function ContractList(props: IProps): React.ReactElement {
return ( return (
<> <>
{names.map((name: string) => ( {names.map((name: string) => (
<ContractElem key={name} bladeburner={props.bladeburner} action={contracts[name]} player={props.player} /> <ContractElem key={name} bladeburner={props.bladeburner} action={contracts[name]} />
))} ))}
</> </>
); );

@ -1,12 +1,10 @@
import * as React from "react"; import * as React from "react";
import { ContractList } from "./ContractList"; import { ContractList } from "./ContractList";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function ContractPage(props: IProps): React.ReactElement { export function ContractPage(props: IProps): React.ReactElement {
@ -20,7 +18,7 @@ export function ContractPage(props: IProps): React.ReactElement {
You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more
difficult, but grant more rank, experience, and money. difficult, but grant more rank, experience, and money.
</Typography> </Typography>
<ContractList bladeburner={props.bladeburner} player={props.player} /> <ContractList bladeburner={props.bladeburner} />
</> </>
); );
} }

@ -2,10 +2,10 @@ import React, { useState } from "react";
import { ActionTypes } from "../data/ActionTypes"; import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText"; import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IAction } from "../IAction"; import { Action } from "../Action";
import { GeneralActions } from "../data/GeneralActions"; import { GeneralActions } from "../data/GeneralActions";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import { StartButton } from "./StartButton"; import { StartButton } from "./StartButton";
@ -15,9 +15,8 @@ import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer; action: Action;
action: IAction;
} }
export function GeneralActionElem(props: IProps): React.ReactElement { export function GeneralActionElem(props: IProps): React.ReactElement {
@ -40,13 +39,13 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
case "Incite Violence": case "Incite Violence":
return 60; return 60;
case "Recruitment": case "Recruitment":
return props.bladeburner.getRecruitmentTime(props.player); return props.bladeburner.getRecruitmentTime(Player);
} }
return -1; // dead code return -1; // dead code
})(); })();
const successChance = const successChance =
props.action.name === "Recruitment" props.action.name === "Recruitment"
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1)) ? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(Player), 1))
: -1; : -1;
const actionData = GeneralActions[props.action.name]; const actionData = GeneralActions[props.action.name];

@ -2,12 +2,10 @@ import React from "react";
import { GeneralActionElem } from "./GeneralActionElem"; import { GeneralActionElem } from "./GeneralActionElem";
import { Action } from "../Action"; import { Action } from "../Action";
import { GeneralActions } from "../GeneralActions"; import { GeneralActions } from "../GeneralActions";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function GeneralActionList(props: IProps): React.ReactElement { export function GeneralActionList(props: IProps): React.ReactElement {
@ -20,7 +18,7 @@ export function GeneralActionList(props: IProps): React.ReactElement {
return ( return (
<> <>
{actions.map((action: Action) => ( {actions.map((action: Action) => (
<GeneralActionElem key={action.name} bladeburner={props.bladeburner} action={action} player={props.player} /> <GeneralActionElem key={action.name} bladeburner={props.bladeburner} action={action} />
))} ))}
</> </>
); );

@ -1,19 +1,17 @@
import * as React from "react"; import * as React from "react";
import { GeneralActionList } from "./GeneralActionList"; import { GeneralActionList } from "./GeneralActionList";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function GeneralActionPage(props: IProps): React.ReactElement { export function GeneralActionPage(props: IProps): React.ReactElement {
return ( return (
<> <>
<Typography>These are generic actions that will assist you in your Bladeburner duties.</Typography> <Typography>These are generic actions that will assist you in your Bladeburner duties.</Typography>
<GeneralActionList bladeburner={props.bladeburner} player={props.player} /> <GeneralActionList bladeburner={props.bladeburner} />
</> </>
); );
} }

@ -7,18 +7,17 @@ import { ActionLevel } from "./ActionLevel";
import { Autolevel } from "./Autolevel"; import { Autolevel } from "./Autolevel";
import { StartButton } from "./StartButton"; import { StartButton } from "./StartButton";
import { TeamSizeButton } from "./TeamSizeButton"; import { TeamSizeButton } from "./TeamSizeButton";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { Operation } from "../Operation"; import { Operation } from "../Operation";
import { Operations } from "../data/Operations"; import { Operations } from "../data/Operations";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
action: Operation; action: Operation;
} }
@ -33,7 +32,7 @@ export function OperationElem(props: IProps): React.ReactElement {
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
props.bladeburner.actionTimeToComplete, props.bladeburner.actionTimeToComplete,
); );
const actionTime = props.action.getActionTime(props.bladeburner, props.player); const actionTime = props.action.getActionTime(props.bladeburner, Player);
const actionData = Operations[props.action.name]; const actionData = Operations[props.action.name];
if (actionData === undefined) { if (actionData === undefined) {

@ -1,11 +1,9 @@
import React from "react"; import React from "react";
import { OperationElem } from "./OperationElem"; import { OperationElem } from "./OperationElem";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function OperationList(props: IProps): React.ReactElement { export function OperationList(props: IProps): React.ReactElement {
@ -14,7 +12,7 @@ export function OperationList(props: IProps): React.ReactElement {
return ( return (
<> <>
{names.map((name: string) => ( {names.map((name: string) => (
<OperationElem key={name} bladeburner={props.bladeburner} action={operations[name]} player={props.player} /> <OperationElem key={name} bladeburner={props.bladeburner} action={operations[name]} />
))} ))}
</> </>
); );

@ -1,12 +1,10 @@
import * as React from "react"; import * as React from "react";
import { OperationList } from "./OperationList"; import { OperationList } from "./OperationList";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
player: IPlayer;
} }
export function OperationPage(props: IProps): React.ReactElement { export function OperationPage(props: IProps): React.ReactElement {
@ -29,7 +27,7 @@ export function OperationPage(props: IProps): React.ReactElement {
You can unlock higher-level operations by successfully completing them. Higher-level operations are more You can unlock higher-level operations by successfully completing them. Higher-level operations are more
difficult, but grant more rank and experience. difficult, but grant more rank and experience.
</Typography> </Typography>
<OperationList bladeburner={props.bladeburner} player={props.player} /> <OperationList bladeburner={props.bladeburner} />
</> </>
); );
} }

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
@ -13,7 +13,7 @@ import { Skill } from "../Skill";
interface IProps { interface IProps {
skill: Skill; skill: Skill;
bladeburner: IBladeburner; bladeburner: Bladeburner;
onUpgrade: () => void; onUpgrade: () => void;
} }

@ -1,10 +1,10 @@
import * as React from "react"; import * as React from "react";
import { SkillElem } from "./SkillElem"; import { SkillElem } from "./SkillElem";
import { Skills } from "../Skills"; import { Skills } from "../Skills";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
onUpgrade: () => void; onUpgrade: () => void;
} }

@ -2,10 +2,10 @@ import React, { useState } from "react";
import { SkillList } from "./SkillList"; import { SkillList } from "./SkillList";
import { BladeburnerConstants } from "../data/Constants"; import { BladeburnerConstants } from "../data/Constants";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
} }
export function SkillPage(props: IProps): React.ReactElement { export function SkillPage(props: IProps): React.ReactElement {

@ -1,20 +1,20 @@
import React from "react"; import React from "react";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { BlackOperation } from "../BlackOperation"; import { BlackOperation } from "../BlackOperation";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { ActionIdentifier } from "../ActionIdentifier";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
type: number; type: number;
name: string; name: string;
rerender: () => void; rerender: () => void;
} }
export function StartButton(props: IProps): React.ReactElement { export function StartButton(props: IProps): React.ReactElement {
const player = use.Player(); const action = props.bladeburner.getActionObject(new ActionIdentifier({ name: props.name, type: props.type }));
const action = props.bladeburner.getActionObject({ name: props.name, type: props.type });
if (action == null) { if (action == null) {
throw new Error("Failed to get Operation Object for: " + props.name); throw new Error("Failed to get Operation Object for: " + props.name);
} }
@ -33,8 +33,8 @@ export function StartButton(props: IProps): React.ReactElement {
if (disabled) return; if (disabled) return;
props.bladeburner.action.type = props.type; props.bladeburner.action.type = props.type;
props.bladeburner.action.name = props.name; props.bladeburner.action.name = props.name;
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) player.finishWork(true); if (!Player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) Player.finishWork(true);
props.bladeburner.startAction(player, props.bladeburner.action); props.bladeburner.startAction(props.bladeburner.action);
props.rerender(); props.rerender();
} }

@ -1,13 +1,13 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { BladeburnerConstants } from "../data/Constants"; import { BladeburnerConstants } from "../data/Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { IRouter } from "../../ui/Router"; import { Router } from "../../ui/GameRoot";
import { joinFaction } from "../../Faction/FactionHelpers"; import { joinFaction } from "../../Faction/FactionHelpers";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { TravelModal } from "./TravelModal"; import { TravelModal } from "./TravelModal";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -18,9 +18,7 @@ import Paper from "@mui/material/Paper";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
router: IRouter;
player: IPlayer;
} }
export function Stats(props: IProps): React.ReactElement { export function Stats(props: IProps): React.ReactElement {
@ -40,7 +38,7 @@ export function Stats(props: IProps): React.ReactElement {
joinFaction(faction); joinFaction(faction);
} }
props.router.toFaction(faction); Router.toFaction(faction);
} }
return ( return (
@ -170,13 +168,13 @@ export function Stats(props: IProps): React.ReactElement {
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography> <Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
<br /> <br />
<Typography> <Typography>
Aug. Success Chance mult: {formatNumber(props.player.mults.bladeburner_success_chance * 100, 1)}% Aug. Success Chance mult: {formatNumber(Player.mults.bladeburner_success_chance * 100, 1)}%
<br /> <br />
Aug. Max Stamina mult: {formatNumber(props.player.mults.bladeburner_max_stamina * 100, 1)}% Aug. Max Stamina mult: {formatNumber(Player.mults.bladeburner_max_stamina * 100, 1)}%
<br /> <br />
Aug. Stamina Gain mult: {formatNumber(props.player.mults.bladeburner_stamina_gain * 100, 1)}% Aug. Stamina Gain mult: {formatNumber(Player.mults.bladeburner_stamina_gain * 100, 1)}%
<br /> <br />
Aug. Field Analysis mult: {formatNumber(props.player.mults.bladeburner_analysis * 100, 1)}% Aug. Field Analysis mult: {formatNumber(Player.mults.bladeburner_analysis * 100, 1)}%
</Typography> </Typography>
</Box> </Box>
</Paper> </Paper>

@ -2,13 +2,13 @@ import React from "react";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import { StealthIcon } from "./StealthIcon"; import { StealthIcon } from "./StealthIcon";
import { KillIcon } from "./KillIcon"; import { KillIcon } from "./KillIcon";
import { IAction } from "../IAction"; import { Action } from "../Action";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { Player } from "../../Player"; import { Player } from "../../Player";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
action: IAction; action: Action;
} }
export function SuccessChance(props: IProps): React.ReactElement { export function SuccessChance(props: IProps): React.ReactElement {

@ -1,12 +1,12 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Operation } from "../Operation"; import { Operation } from "../Operation";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { TeamSizeModal } from "./TeamSizeModal"; import { TeamSizeModal } from "./TeamSizeModal";
import { formatNumber } from "../../utils/StringHelperFunctions"; import { formatNumber } from "../../utils/StringHelperFunctions";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
interface IProps { interface IProps {
action: Operation; action: Operation;
bladeburner: IBladeburner; bladeburner: Bladeburner;
} }
export function TeamSizeButton(props: IProps): React.ReactElement { export function TeamSizeButton(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);

@ -2,13 +2,13 @@ import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { Action } from "../Action"; import { Action } from "../Action";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
action: Action; action: Action;
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { IBladeburner } from "../IBladeburner"; import { Bladeburner } from "../Bladeburner";
import { WorldMap } from "../../ui/React/WorldMap"; import { WorldMap } from "../../ui/React/WorldMap";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { CityName } from "../../Locations/data/CityNames"; import { CityName } from "../../Locations/data/CityNames";
@ -8,7 +8,7 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: Bladeburner;
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
} }

@ -1,8 +1,8 @@
import * as React from "react"; import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { Money } from "../ui/React/Money"; import { Money } from "../ui/React/Money";
import { Game, reachedLimit } from "./Game"; import { win, reachedLimit } from "./Game";
import { Deck } from "./CardDeck/Deck"; import { Deck } from "./CardDeck/Deck";
import { Hand } from "./CardDeck/Hand"; import { Hand } from "./CardDeck/Hand";
import { InputAdornment } from "@mui/material"; import { InputAdornment } from "@mui/material";
@ -24,10 +24,6 @@ enum Result {
Tie = "Push! (Tie)", Tie = "Push! (Tie)",
} }
type Props = {
p: IPlayer;
};
type State = { type State = {
playerHand: Hand; playerHand: Hand;
dealerHand: Hand; dealerHand: Hand;
@ -40,11 +36,11 @@ type State = {
wagerInvalidHelperText: string; wagerInvalidHelperText: string;
}; };
export class Blackjack extends Game<Props, State> { export class Blackjack extends React.Component<Record<string, never>, State> {
deck: Deck; deck: Deck;
constructor(props: Props) { constructor() {
super(props); super({});
this.deck = new Deck(DECK_COUNT); this.deck = new Deck(DECK_COUNT);
@ -64,20 +60,19 @@ export class Blackjack extends Game<Props, State> {
} }
canStartGame = (): boolean => { canStartGame = (): boolean => {
const { p } = this.props;
const { bet } = this.state; const { bet } = this.state;
return p.canAfford(bet); return Player.canAfford(bet);
}; };
startGame = (): void => { startGame = (): void => {
if (!this.canStartGame() || reachedLimit(this.props.p)) { if (!this.canStartGame() || reachedLimit()) {
return; return;
} }
// Take money from player right away so that player's dont just "leave" to avoid the loss (I mean they could // Take money from player right away so that player's dont just "leave" to avoid the loss (I mean they could
// always reload without saving but w.e) TODO: Save/Restore the RNG state to limit the value of save-scumming. // always reload without saving but w.e) TODO: Save/Restore the RNG state to limit the value of save-scumming.
this.props.p.loseMoney(this.state.bet, "casino"); win(-this.state.bet);
const playerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]); const playerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]);
const dealerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]); const dealerHand = new Hand([this.deck.safeDrawCard(), this.deck.safeDrawCard()]);
@ -230,7 +225,7 @@ export class Blackjack extends Game<Props, State> {
: (() => { : (() => {
throw new Error(`Unexpected result: ${result}`); throw new Error(`Unexpected result: ${result}`);
})(); // This can't happen, right? })(); // This can't happen, right?
this.win(this.props.p, gains); win(gains);
this.setState({ this.setState({
gameInProgress: false, gameInProgress: false,
result, result,
@ -239,7 +234,6 @@ export class Blackjack extends Game<Props, State> {
}; };
wagerOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => { wagerOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { p } = this.props;
const betInput = event.target.value; const betInput = event.target.value;
const wager = Math.round(parseFloat(betInput)); const wager = Math.round(parseFloat(betInput));
if (isNaN(wager)) { if (isNaN(wager)) {
@ -263,7 +257,7 @@ export class Blackjack extends Game<Props, State> {
wagerInvalid: true, wagerInvalid: true,
wagerInvalidHelperText: "Exceeds max bet", wagerInvalidHelperText: "Exceeds max bet",
}); });
} else if (!p.canAfford(wager)) { } else if (!Player.canAfford(wager)) {
this.setState({ this.setState({
bet: 0, bet: 0,
betInput, betInput,

@ -5,7 +5,6 @@
*/ */
import React, { useState } from "react"; import React, { useState } from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { BadRNG } from "./RNG"; import { BadRNG } from "./RNG";
import { win, reachedLimit } from "./Game"; import { win, reachedLimit } from "./Game";
import { trusted } from "./utils"; import { trusted } from "./utils";
@ -15,14 +14,10 @@ import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
type IProps = {
p: IPlayer;
};
const minPlay = 0; const minPlay = 0;
const maxPlay = 10e3; const maxPlay = 10e3;
export function CoinFlip(props: IProps): React.ReactElement { export function CoinFlip(): React.ReactElement {
const [investment, setInvestment] = useState(1000); const [investment, setInvestment] = useState(1000);
const [result, setResult] = useState(<span> </span>); const [result, setResult] = useState(<span> </span>);
const [status, setStatus] = useState(""); const [status, setStatus] = useState("");
@ -43,7 +38,7 @@ export function CoinFlip(props: IProps): React.ReactElement {
} }
function play(guess: string): void { function play(guess: string): void {
if (reachedLimit(props.p)) return; if (reachedLimit()) return;
const v = BadRNG.random(); const v = BadRNG.random();
let letter: string; let letter: string;
if (v < 0.5) { if (v < 0.5) {
@ -65,11 +60,11 @@ export function CoinFlip(props: IProps): React.ReactElement {
setTimeout(() => setPlayLock(false), 250); setTimeout(() => setPlayLock(false), 250);
if (correct) { if (correct) {
win(props.p, investment); win(investment);
} else { } else {
win(props.p, -investment); win(-investment);
} }
if (reachedLimit(props.p)) return; if (reachedLimit()) return;
} }
return ( return (

16
src/Casino/Game.ts Normal file

@ -0,0 +1,16 @@
import { Player } from "../Player";
import { dialogBoxCreate } from "../ui/React/DialogBox";
const gainLimit = 10e9;
export function win(n: number): void {
Player.gainMoney(n, "casino");
}
export function reachedLimit(): boolean {
const reached = Player.getCasinoWinnings() > gainLimit;
if (reached) {
dialogBoxCreate("Alright cheater get out of here. You're not allowed here anymore.");
}
return reached;
}

@ -1,31 +0,0 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../ui/React/DialogBox";
const gainLimit = 10e9;
export function win(p: IPlayer, n: number): void {
p.gainMoney(n, "casino");
}
export function reachedLimit(p: IPlayer): boolean {
const reached = p.getCasinoWinnings() > gainLimit;
if (reached) {
dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.</>);
}
return reached;
}
export class Game<T, U> extends React.Component<T, U> {
win(p: IPlayer, n: number): void {
p.gainMoney(n, "casino");
}
reachedLimit(p: IPlayer): boolean {
const reached = p.getCasinoWinnings() > gainLimit;
if (reached) {
dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.</>);
}
return reached;
}
}

@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Money } from "../ui/React/Money"; import { Money } from "../ui/React/Money";
import { win, reachedLimit } from "./Game"; import { win, reachedLimit } from "./Game";
import { WHRNG } from "./RNG"; import { WHRNG } from "./RNG";
@ -9,10 +8,6 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
type IProps = {
p: IPlayer;
};
const minPlay = 0; const minPlay = 0;
const maxPlay = 1e7; const maxPlay = 1e7;
@ -111,7 +106,7 @@ function Single(s: number): Strategy {
}; };
} }
export function Roulette(props: IProps): React.ReactElement { export function Roulette(): React.ReactElement {
const [rng] = useState(new WHRNG(new Date().getTime())); const [rng] = useState(new WHRNG(new Date().getTime()));
const [investment, setInvestment] = useState(1000); const [investment, setInvestment] = useState(1000);
const [canPlay, setCanPlay] = useState(true); const [canPlay, setCanPlay] = useState(true);
@ -151,7 +146,7 @@ export function Roulette(props: IProps): React.ReactElement {
} }
function play(strategy: Strategy): void { function play(strategy: Strategy): void {
if (reachedLimit(props.p)) return; if (reachedLimit()) return;
setCanPlay(false); setCanPlay(false);
setLock(false); setLock(false);
@ -184,14 +179,14 @@ export function Roulette(props: IProps): React.ReactElement {
</> </>
); );
} }
win(props.p, gain); win(gain);
setCanPlay(true); setCanPlay(true);
setLock(true); setLock(true);
setStatus(status); setStatus(status);
setN(n); setN(n);
reachedLimit(props.p); reachedLimit();
}, 1600); }, 1600);
} }

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { Money } from "../ui/React/Money"; import { Money } from "../ui/React/Money";
import { WHRNG } from "./RNG"; import { WHRNG } from "./RNG";
import { win, reachedLimit } from "./Game"; import { win, reachedLimit } from "./Game";
@ -9,10 +9,6 @@ import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
type IProps = {
p: IPlayer;
};
// statically shuffled array of symbols. // statically shuffled array of symbols.
const symbols = [ const symbols = [
"D", "D",
@ -141,8 +137,8 @@ const payLines = [
const minPlay = 0; const minPlay = 0;
const maxPlay = 1e6; const maxPlay = 1e6;
export function SlotMachine(props: IProps): React.ReactElement { export function SlotMachine(): React.ReactElement {
const [rng] = useState(new WHRNG(props.p.totalPlaytime)); const [rng] = useState(new WHRNG(Player.totalPlaytime));
const [index, setIndex] = useState<number[]>([0, 0, 0, 0, 0]); const [index, setIndex] = useState<number[]>([0, 0, 0, 0, 0]);
const [locks, setLocks] = useState<number[]>([0, 0, 0, 0, 0]); const [locks, setLocks] = useState<number[]>([0, 0, 0, 0, 0]);
const [investment, setInvestment] = useState(1000); const [investment, setInvestment] = useState(1000);
@ -191,9 +187,9 @@ export function SlotMachine(props: IProps): React.ReactElement {
} }
function play(): void { function play(): void {
if (reachedLimit(props.p)) return; if (reachedLimit()) return;
setStatus("playing"); setStatus("playing");
win(props.p, -investment); win(-investment);
if (!canPlay) return; if (!canPlay) return;
unlock(); unlock();
setTimeout(lock, rng.random() * 2000 + 1000); setTimeout(lock, rng.random() * 2000 + 1000);
@ -235,7 +231,7 @@ export function SlotMachine(props: IProps): React.ReactElement {
if (count < 3) continue; if (count < 3) continue;
const payout = getPayout(data[0], count - 3); const payout = getPayout(data[0], count - 3);
gains += investment * payout; gains += investment * payout;
win(props.p, investment * payout); win(investment * payout);
} }
setStatus( setStatus(
@ -244,7 +240,7 @@ export function SlotMachine(props: IProps): React.ReactElement {
</>, </>,
); );
setCanPlay(true); setCanPlay(true);
if (reachedLimit(props.p)) return; if (reachedLimit()) return;
} }
function unlock(): void { function unlock(): void {

@ -29,7 +29,7 @@ export function initCompanies(): void {
for (const companyName of Object.keys(Companies)) { for (const companyName of Object.keys(Companies)) {
const company = Companies[companyName]; const company = Companies[companyName];
const oldCompany = oldCompanies[companyName]; const oldCompany = oldCompanies[companyName];
if (!(oldCompany instanceof Company)) { if (!oldCompany) {
// New game, so no OldCompanies data // New game, so no OldCompanies data
company.favor = 0; company.favor = 0;
} else { } else {

@ -88,11 +88,7 @@ export class Company {
} }
hasPosition(pos: CompanyPosition | string): boolean { hasPosition(pos: CompanyPosition | string): boolean {
if (pos instanceof CompanyPosition) { return this.companyPositions[typeof pos === "string" ? pos : pos.name] != null;
return this.companyPositions[pos.name] != null;
} else {
return this.companyPositions[pos] != null;
}
} }
hasAgentPositions(): boolean { hasAgentPositions(): boolean {

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { Company } from "../Company"; import { Company } from "../Company";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -14,9 +14,8 @@ interface IProps {
} }
export function QuitJobModal(props: IProps): React.ReactElement { export function QuitJobModal(props: IProps): React.ReactElement {
const player = use.Player();
function quit(): void { function quit(): void {
player.quitJob(props.locName); Player.quitJob(props.locName);
props.onQuit(); props.onQuit();
props.onClose(); props.onClose();
} }

@ -1,9 +1,6 @@
import { Player } from "../Player"; import { Player } from "../Player";
import { IPlayer } from "src/PersonObjects/IPlayer";
import { MaterialSizes } from "./MaterialSizes"; import { MaterialSizes } from "./MaterialSizes";
import { ICorporation } from "./ICorporation";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
import { IIndustry } from "./IIndustry";
import { IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData"; import { IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { Industry } from "./Industry"; import { Industry } from "./Industry";
import { CorporationConstants } from "./data/Constants"; import { CorporationConstants } from "./data/Constants";
@ -18,7 +15,7 @@ import { EmployeePositions } from "./EmployeePositions";
import { ResearchMap } from "./ResearchMap"; import { ResearchMap } from "./ResearchMap";
import { isRelevantMaterial } from "./ui/Helpers"; import { isRelevantMaterial } from "./ui/Helpers";
export function NewIndustry(corporation: ICorporation, industry: string, name: string): void { export function NewIndustry(corporation: Corporation, industry: string, name: string): void {
if (corporation.divisions.find(({ type }) => industry == type)) if (corporation.divisions.find(({ type }) => industry == type))
throw new Error(`You have already expanded into the ${industry} industry!`); throw new Error(`You have already expanded into the ${industry} industry!`);
@ -48,7 +45,7 @@ export function NewIndustry(corporation: ICorporation, industry: string, name: s
} }
} }
export function NewCity(corporation: ICorporation, division: IIndustry, city: string): void { export function NewCity(corporation: Corporation, division: Industry, city: string): void {
if (corporation.funds < CorporationConstants.OfficeInitialCost) { if (corporation.funds < CorporationConstants.OfficeInitialCost) {
throw new Error("You don't have enough company funds to open a new office!"); throw new Error("You don't have enough company funds to open a new office!");
} }
@ -62,7 +59,7 @@ export function NewCity(corporation: ICorporation, division: IIndustry, city: st
}); });
} }
export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void { export function UnlockUpgrade(corporation: Corporation, upgrade: CorporationUnlockUpgrade): void {
if (corporation.funds < upgrade.price) { if (corporation.funds < upgrade.price) {
throw new Error("Insufficient funds"); throw new Error("Insufficient funds");
} }
@ -72,7 +69,7 @@ export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnl
corporation.unlock(upgrade); corporation.unlock(upgrade);
} }
export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgrade): void { export function LevelUpgrade(corporation: Corporation, upgrade: CorporationUpgrade): void {
const baseCost = upgrade.basePrice; const baseCost = upgrade.basePrice;
const priceMult = upgrade.priceMult; const priceMult = upgrade.priceMult;
const level = corporation.upgrades[upgrade.index]; const level = corporation.upgrades[upgrade.index];
@ -84,7 +81,7 @@ export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgr
} }
} }
export function IssueDividends(corporation: ICorporation, rate: number): void { export function IssueDividends(corporation: Corporation, rate: number): void {
if (isNaN(rate) || rate < 0 || rate > CorporationConstants.DividendMaxRate) { if (isNaN(rate) || rate < 0 || rate > CorporationConstants.DividendMaxRate) {
throw new Error(`Invalid value. Must be an number between 0 and ${CorporationConstants.DividendMaxRate}`); throw new Error(`Invalid value. Must be an number between 0 and ${CorporationConstants.DividendMaxRate}`);
} }
@ -253,7 +250,7 @@ export function BuyMaterial(material: Material, amt: number): void {
material.buy = amt; material.buy = amt;
} }
export function BulkPurchase(corp: ICorporation, warehouse: Warehouse, material: Material, amt: number): void { export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material: Material, amt: number): void {
const matSize = MaterialSizes[material.name]; const matSize = MaterialSizes[material.name];
const maxAmount = (warehouse.size - warehouse.sizeUsed) / matSize; const maxAmount = (warehouse.size - warehouse.sizeUsed) / matSize;
if (isNaN(amt) || amt < 0) { if (isNaN(amt) || amt < 0) {
@ -271,7 +268,7 @@ export function BulkPurchase(corp: ICorporation, warehouse: Warehouse, material:
} }
} }
export function SellShares(corporation: ICorporation, player: IPlayer, numShares: number): number { export function SellShares(corporation: Corporation, numShares: number): number {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); if (isNaN(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) throw new Error("Invalid value for number of shares"); if (numShares < 0) throw new Error("Invalid value for number of shares");
if (numShares > corporation.numShares) throw new Error("You don't have that many shares to sell!"); if (numShares > corporation.numShares) throw new Error("You don't have that many shares to sell!");
@ -287,20 +284,20 @@ export function SellShares(corporation: ICorporation, player: IPlayer, numShares
corporation.sharePrice = newSharePrice; corporation.sharePrice = newSharePrice;
corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate; corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
corporation.shareSaleCooldown = CorporationConstants.SellSharesCooldown; corporation.shareSaleCooldown = CorporationConstants.SellSharesCooldown;
player.gainMoney(profit, "corporation"); Player.gainMoney(profit, "corporation");
return profit; return profit;
} }
export function BuyBackShares(corporation: ICorporation, player: IPlayer, numShares: number): boolean { export function BuyBackShares(corporation: Corporation, numShares: number): boolean {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); if (isNaN(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) throw new Error("Invalid value for number of shares"); if (numShares < 0) throw new Error("Invalid value for number of shares");
if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!"); if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!");
if (!corporation.public) throw new Error("You haven't gone public!"); if (!corporation.public) throw new Error("You haven't gone public!");
const buybackPrice = corporation.sharePrice * 1.1; const buybackPrice = corporation.sharePrice * 1.1;
if (player.money < numShares * buybackPrice) throw new Error("You cant afford that many shares!"); if (Player.money < numShares * buybackPrice) throw new Error("You cant afford that many shares!");
corporation.numShares += numShares; corporation.numShares += numShares;
corporation.issuedShares -= numShares; corporation.issuedShares -= numShares;
player.loseMoney(numShares * buybackPrice, "corporation"); Player.loseMoney(numShares * buybackPrice, "corporation");
return true; return true;
} }
@ -319,7 +316,7 @@ export function AutoAssignJob(office: OfficeSpace, job: string, count: number):
return office.autoAssignJob(job, count); return office.autoAssignJob(job, count);
} }
export function UpgradeOfficeSize(corp: ICorporation, office: OfficeSpace, size: number): void { export function UpgradeOfficeSize(corp: Corporation, office: OfficeSpace, size: number): void {
const initialPriceMult = Math.round(office.size / CorporationConstants.OfficeInitialSize); const initialPriceMult = Math.round(office.size / CorporationConstants.OfficeInitialSize);
const costMultiplier = 1.09; const costMultiplier = 1.09;
// Calculate cost to upgrade size by 15 employees // Calculate cost to upgrade size by 15 employees
@ -333,7 +330,7 @@ export function UpgradeOfficeSize(corp: ICorporation, office: OfficeSpace, size:
corp.funds = corp.funds - cost; corp.funds = corp.funds - cost;
} }
export function BuyCoffee(corp: ICorporation, office: OfficeSpace): boolean { export function BuyCoffee(corp: Corporation, office: OfficeSpace): boolean {
const cost = office.getCoffeeCost(); const cost = office.getCoffeeCost();
if (corp.funds < cost) { if (corp.funds < cost) {
return false; return false;
@ -347,7 +344,7 @@ export function BuyCoffee(corp: ICorporation, office: OfficeSpace): boolean {
return true; return true;
} }
export function ThrowParty(corp: ICorporation, office: OfficeSpace, costPerEmployee: number): number { export function ThrowParty(corp: Corporation, office: OfficeSpace, costPerEmployee: number): number {
const mult = 1 + costPerEmployee / 10e6; const mult = 1 + costPerEmployee / 10e6;
const cost = costPerEmployee * office.employees.length; const cost = costPerEmployee * office.employees.length;
if (corp.funds < cost) { if (corp.funds < cost) {
@ -362,9 +359,9 @@ export function ThrowParty(corp: ICorporation, office: OfficeSpace, costPerEmplo
return mult; return mult;
} }
export function PurchaseWarehouse(corp: ICorporation, division: IIndustry, city: string): void { export function PurchaseWarehouse(corp: Corporation, division: Industry, city: string): void {
if (corp.funds < CorporationConstants.WarehouseInitialCost) return; if (corp.funds < CorporationConstants.WarehouseInitialCost) return;
if (division.warehouses[city] instanceof Warehouse) return; if (division.warehouses[city]) return;
division.warehouses[city] = new Warehouse({ division.warehouses[city] = new Warehouse({
corp: corp, corp: corp,
industry: division, industry: division,
@ -381,7 +378,7 @@ export function UpgradeWarehouseCost(warehouse: Warehouse, amt: number): number
); );
} }
export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse, amt = 1): void { export function UpgradeWarehouse(corp: Corporation, division: Industry, warehouse: Warehouse, amt = 1): void {
const sizeUpgradeCost = UpgradeWarehouseCost(warehouse, amt); const sizeUpgradeCost = UpgradeWarehouseCost(warehouse, amt);
if (corp.funds < sizeUpgradeCost) return; if (corp.funds < sizeUpgradeCost) return;
warehouse.level += amt; warehouse.level += amt;
@ -389,7 +386,7 @@ export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, wareho
corp.funds = corp.funds - sizeUpgradeCost; corp.funds = corp.funds - sizeUpgradeCost;
} }
export function HireAdVert(corp: ICorporation, division: IIndustry): void { export function HireAdVert(corp: Corporation, division: Industry): void {
const cost = division.getAdVertCost(); const cost = division.getAdVertCost();
if (corp.funds < cost) return; if (corp.funds < cost) return;
corp.funds = corp.funds - cost; corp.funds = corp.funds - cost;
@ -397,8 +394,8 @@ export function HireAdVert(corp: ICorporation, division: IIndustry): void {
} }
export function MakeProduct( export function MakeProduct(
corp: ICorporation, corp: Corporation,
division: IIndustry, division: Industry,
city: string, city: string,
productName: string, productName: string,
designInvest: number, designInvest: number,
@ -442,7 +439,7 @@ export function MakeProduct(
designCost: designInvest, designCost: designInvest,
advCost: marketingInvest, advCost: marketingInvest,
}); });
if (products[product.name] instanceof Product) { if (products[product.name]) {
throw new Error(`You already have a product with this name!`); throw new Error(`You already have a product with this name!`);
} }
@ -450,7 +447,7 @@ export function MakeProduct(
products[product.name] = product; products[product.name] = product;
} }
export function Research(division: IIndustry, researchName: string): void { export function Research(division: Industry, researchName: string): void {
const researchTree = IndustryResearchTrees[division.type]; const researchTree = IndustryResearchTrees[division.type];
if (researchTree === undefined) throw new Error(`No research tree for industry '${division.type}'`); if (researchTree === undefined) throw new Error(`No research tree for industry '${division.type}'`);
const allResearch = researchTree.getAllNodes(); const allResearch = researchTree.getAllNodes();
@ -473,10 +470,10 @@ export function Research(division: IIndustry, researchName: string): void {
for (let i = 0; i < CorporationConstants.Cities.length; ++i) { for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
const city = CorporationConstants.Cities[i]; const city = CorporationConstants.Cities[i];
const warehouse = division.warehouses[city]; const warehouse = division.warehouses[city];
if (!(warehouse instanceof Warehouse)) { if (!warehouse) {
continue; continue;
} }
if (Player.corporation instanceof Corporation) { if (Player.corporation) {
// Stores cycles in a "buffer". Processed separately using Engine Counters // Stores cycles in a "buffer". Processed separately using Engine Counters
warehouse.updateSize(Player.corporation, division); warehouse.updateSize(Player.corporation, division);
} }

@ -1,14 +1,13 @@
import { CorporationState } from "./CorporationState"; import { CorporationState } from "./CorporationState";
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "./data/CorporationUnlockUpgrades"; import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "./data/CorporationUnlockUpgrades";
import { CorporationUpgrade, CorporationUpgrades } from "./data/CorporationUpgrades"; import { CorporationUpgrade, CorporationUpgrades } from "./data/CorporationUpgrades";
import { Warehouse } from "./Warehouse";
import { CorporationConstants } from "./data/Constants"; import { CorporationConstants } from "./data/Constants";
import { Industry } from "./Industry"; import { Industry } from "./Industry";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { showLiterature } from "../Literature/LiteratureHelpers"; import { showLiterature } from "../Literature/LiteratureHelpers";
import { LiteratureNames } from "../Literature/data/LiteratureNames"; import { LiteratureNames } from "../Literature/data/LiteratureNames";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
@ -76,7 +75,7 @@ export class Corporation {
this.storedCycles += numCycles; this.storedCycles += numCycles;
} }
process(player: IPlayer): void { process(): void {
if (this.storedCycles >= CorporationConstants.CyclesPerIndustryStateCycle) { if (this.storedCycles >= CorporationConstants.CyclesPerIndustryStateCycle) {
const state = this.getState(); const state = this.getState();
const marketCycles = 1; const marketCycles = 1;
@ -120,7 +119,7 @@ export class Corporation {
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) { if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
dialogBoxCreate( dialogBoxCreate(
"There was an error calculating your Corporations funds and they got reset to 0. " + "There was an error calculating your Corporations funds and they got reset to 0. " +
"This is a bug. Please report to game developer.<br><br>" + "This is a bug. Please report to game developer.\n\n" +
"(Your funds have been set to $150b for the inconvenience)", "(Your funds have been set to $150b for the inconvenience)",
); );
this.funds = 150e9; this.funds = 150e9;
@ -139,7 +138,7 @@ export class Corporation {
} else { } else {
const totalDividends = this.dividendRate * cycleProfit; const totalDividends = this.dividendRate * cycleProfit;
const retainedEarnings = cycleProfit - totalDividends; const retainedEarnings = cycleProfit - totalDividends;
player.gainMoney(this.getCycleDividends(), "corporation"); Player.gainMoney(this.getCycleDividends(), "corporation");
this.addFunds(retainedEarnings); this.addFunds(retainedEarnings);
} }
} else { } else {
@ -331,7 +330,7 @@ export class Corporation {
for (const city of Object.keys(industry.warehouses)) { for (const city of Object.keys(industry.warehouses)) {
const warehouse = industry.warehouses[city]; const warehouse = industry.warehouses[city];
if (warehouse === 0) continue; if (warehouse === 0) continue;
if (industry.warehouses.hasOwnProperty(city) && warehouse instanceof Warehouse) { if (industry.warehouses.hasOwnProperty(city) && warehouse) {
warehouse.updateSize(this, industry); warehouse.updateSize(this, industry);
} }
} }
@ -428,9 +427,9 @@ export class Corporation {
// Adds the Corporation Handbook (Starter Guide) to the player's home computer. // Adds the Corporation Handbook (Starter Guide) to the player's home computer.
// This is a lit file that gives introductory info to the player // This is a lit file that gives introductory info to the player
// This occurs when the player clicks the "Getting Started Guide" button on the overview panel // This occurs when the player clicks the "Getting Started Guide" button on the overview panel
getStarterGuide(player: IPlayer): void { getStarterGuide(): void {
// Check if player already has Corporation Handbook // Check if player already has Corporation Handbook
const homeComp = player.getHomeComputer(); const homeComp = Player.getHomeComputer();
let hasHandbook = false; let hasHandbook = false;
const handbookFn = LiteratureNames.CorporationManagementHandbook; const handbookFn = LiteratureNames.CorporationManagementHandbook;
for (let i = 0; i < homeComp.messages.length; ++i) { for (let i = 0; i < homeComp.messages.length; ++i) {

@ -2,8 +2,8 @@ import { CorporationConstants } from "./data/Constants";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { EmployeePositions } from "./EmployeePositions"; import { EmployeePositions } from "./EmployeePositions";
import { ICorporation } from "./ICorporation"; import { Corporation } from "./Corporation";
import { IIndustry } from "./IIndustry"; import { Industry } from "./Industry";
interface IParams { interface IParams {
name?: string; name?: string;
@ -77,7 +77,7 @@ export class Employee {
return salary; return salary;
} }
calculateProductivity(corporation: ICorporation, industry: IIndustry): number { calculateProductivity(corporation: Corporation, industry: Industry): number {
const effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(), const effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(),
effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(), effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(),
effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(), effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(),

@ -1,63 +0,0 @@
import { Industry } from "./Industry";
import { IPlayer } from "../PersonObjects/IPlayer";
import { CorporationUnlockUpgrade } from "./data/CorporationUnlockUpgrades";
import { CorporationUpgrade } from "./data/CorporationUpgrades";
import { CorporationState } from "./CorporationState";
import { IReviverValue } from "../utils/JSONReviver";
export interface ICorporation {
name: string;
divisions: Industry[];
funds: number;
revenue: number;
expenses: number;
fundingRound: number;
public: boolean;
totalShares: number;
numShares: number;
shareSalesUntilPriceUpdate: number;
shareSaleCooldown: number;
issueNewSharesCooldown: number;
dividendRate: number;
dividendTax: number;
issuedShares: number;
sharePrice: number;
storedCycles: number;
valuation: number;
unlockUpgrades: number[];
upgrades: number[];
upgradeMultipliers: number[];
state: CorporationState;
addFunds(amt: number): void;
getState(): string;
storeCycles(numCycles: number): void;
process(player: IPlayer): void;
determineValuation(): void;
determineCycleValuation(): number;
getTargetSharePrice(): number;
updateSharePrice(): void;
immediatelyUpdateSharePrice(): void;
calculateShareSale(numShares: number): [number, number, number];
convertCooldownToString(cd: number): string;
unlock(upgrade: CorporationUnlockUpgrade): void;
upgrade(upgrade: CorporationUpgrade): void;
getProductionMultiplier(): number;
getStorageMultiplier(): number;
getDreamSenseGain(): number;
getAdvertisingMultiplier(): number;
getEmployeeCreMultiplier(): number;
getEmployeeChaMultiplier(): number;
getEmployeeIntMultiplier(): number;
getEmployeeEffMultiplier(): number;
getSalesMultiplier(): number;
getScientificResearchMultiplier(): number;
getStarterGuide(player: IPlayer): void;
updateDividendTax(): void;
getCycleDividends(): number;
toJSON(): IReviverValue;
}

@ -1,78 +0,0 @@
import { Material } from "./Material";
import { Warehouse } from "./Warehouse";
import { ICorporation } from "./ICorporation";
import { OfficeSpace } from "./OfficeSpace";
import { Product } from "./Product";
import { IReviverValue } from "../utils/JSONReviver";
export interface IIndustry {
name: string;
type: string;
sciResearch: Material;
researched: { [key: string]: boolean | undefined };
reqMats: { [key: string]: number | undefined };
prodMats: string[];
products: { [key: string]: Product | undefined };
makesProducts: boolean;
awareness: number;
popularity: number;
startingCost: number;
reFac: number;
sciFac: number;
hwFac: number;
robFac: number;
aiFac: number;
advFac: number;
prodMult: number;
// Decimal
lastCycleRevenue: number;
lastCycleExpenses: number;
thisCycleRevenue: number;
thisCycleExpenses: number;
state: string;
newInd: boolean;
warehouses: { [key: string]: Warehouse | 0 };
offices: { [key: string]: OfficeSpace | 0 };
numAdVerts: number;
init(): void;
getProductDescriptionText(): string;
getMaximumNumberProducts(): number;
hasMaximumNumberProducts(): boolean;
calculateProductionFactors(): void;
updateWarehouseSizeUsed(warehouse: Warehouse): void;
process(marketCycles: number, state: string, corporation: ICorporation): void;
processMaterialMarket(): void;
processProductMarket(marketCycles: number): void;
processMaterials(marketCycles: number, corporation: ICorporation): [number, number];
processProducts(marketCycles: number, corporation: ICorporation): [number, number];
processProduct(marketCycles: number, product: Product, corporation: ICorporation): number;
resetImports(state: string): void;
discontinueProduct(product: Product): void;
getAdVertCost(): number;
applyAdVert(corporation: ICorporation): void;
getOfficeProductivity(office: OfficeSpace, params?: { forProduct?: boolean }): number;
getBusinessFactor(office: OfficeSpace): number;
getAdvertisingFactors(): [number, number, number, number];
getMarketFactor(mat: { dmd: number; cmp: number }): number;
hasResearch(name: string): boolean;
updateResearchTree(): void;
getAdvertisingMultiplier(): number;
getEmployeeChaMultiplier(): number;
getEmployeeCreMultiplier(): number;
getEmployeeEffMultiplier(): number;
getEmployeeIntMultiplier(): number;
getProductionMultiplier(): number;
getProductProductionMultiplier(): number;
getSalesMultiplier(): number;
getScientificResearchMultiplier(): number;
getStorageMultiplier(): number;
toJSON(): IReviverValue;
}

@ -12,16 +12,15 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
import { MaterialSizes } from "./MaterialSizes"; import { MaterialSizes } from "./MaterialSizes";
import { Warehouse } from "./Warehouse"; import { Warehouse } from "./Warehouse";
import { ICorporation } from "./ICorporation"; import { Corporation } from "./Corporation";
import { IIndustry } from "./IIndustry";
interface IParams { interface IParams {
name?: string; name?: string;
corp?: ICorporation; corp?: Corporation;
type?: string; type?: string;
} }
export class Industry implements IIndustry { export class Industry {
name = ""; name = "";
type = Industries.Agriculture; type = Industries.Agriculture;
sciResearch = new Material({ name: "Scientific Research" }); sciResearch = new Material({ name: "Scientific Research" });
@ -356,9 +355,7 @@ export class Industry implements IIndustry {
for (let i = 0; i < CorporationConstants.Cities.length; ++i) { for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
const city = CorporationConstants.Cities[i]; const city = CorporationConstants.Cities[i];
const warehouse = this.warehouses[city]; const warehouse = this.warehouses[city];
if (!(warehouse instanceof Warehouse)) { if (!warehouse) continue;
continue;
}
const materials = warehouse.materials; const materials = warehouse.materials;
@ -385,7 +382,7 @@ export class Industry implements IIndustry {
} }
} }
process(marketCycles = 1, state: string, corporation: ICorporation): void { process(marketCycles = 1, state: string, corporation: Corporation): void {
this.state = state; this.state = state;
//At the start of a cycle, store and reset revenue/expenses //At the start of a cycle, store and reset revenue/expenses
@ -414,10 +411,7 @@ export class Industry implements IIndustry {
let employeeSalary = 0; let employeeSalary = 0;
for (const officeLoc of Object.keys(this.offices)) { for (const officeLoc of Object.keys(this.offices)) {
const office = this.offices[officeLoc]; const office = this.offices[officeLoc];
if (office === 0) continue; if (office) employeeSalary += office.process(marketCycles, corporation, this);
if (office instanceof OfficeSpace) {
employeeSalary += office.process(marketCycles, corporation, this);
}
} }
this.thisCycleExpenses = this.thisCycleExpenses + employeeSalary; this.thisCycleExpenses = this.thisCycleExpenses + employeeSalary;
@ -468,7 +462,7 @@ export class Industry implements IIndustry {
for (let i = 0; i < CorporationConstants.Cities.length; ++i) { for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
//If this industry has a warehouse in this city, process the market //If this industry has a warehouse in this city, process the market
//for every material this industry requires or produces //for every material this industry requires or produces
if (this.warehouses[CorporationConstants.Cities[i]] instanceof Warehouse) { if (this.warehouses[CorporationConstants.Cities[i]]) {
const wh = this.warehouses[CorporationConstants.Cities[i]]; const wh = this.warehouses[CorporationConstants.Cities[i]];
if (wh === 0) continue; if (wh === 0) continue;
for (const name of Object.keys(reqMats)) { for (const name of Object.keys(reqMats)) {
@ -518,7 +512,7 @@ export class Industry implements IIndustry {
} }
//Process production, purchase, and import/export of materials //Process production, purchase, and import/export of materials
processMaterials(marketCycles = 1, corporation: ICorporation): [number, number] { processMaterials(marketCycles = 1, corporation: Corporation): [number, number] {
let revenue = 0, let revenue = 0,
expenses = 0; expenses = 0;
this.calculateProductionFactors(); this.calculateProductionFactors();
@ -528,7 +522,7 @@ export class Industry implements IIndustry {
const office = this.offices[city]; const office = this.offices[city];
if (office === 0) continue; if (office === 0) continue;
if (this.warehouses[city] instanceof Warehouse) { if (this.warehouses[city]) {
const warehouse = this.warehouses[city]; const warehouse = this.warehouses[city];
if (warehouse === 0) continue; if (warehouse === 0) continue;
@ -825,14 +819,7 @@ export class Industry implements IIndustry {
sellAmt = eval(tmp); sellAmt = eval(tmp);
} catch (e) { } catch (e) {
dialogBoxCreate( dialogBoxCreate(
"Error evaluating your sell amount for material " + `Error evaluating your sell amount for material ${mat.name} in ${this.name}'s ${city} office. The sell amount is being set to zero`,
mat.name +
" in " +
this.name +
"'s " +
city +
" office. The sell amount " +
"is being set to zero",
); );
sellAmt = 0; sellAmt = 0;
} }
@ -879,27 +866,13 @@ export class Industry implements IIndustry {
amt = eval(amtStr); amt = eval(amtStr);
} catch (e) { } catch (e) {
dialogBoxCreate( dialogBoxCreate(
"Calculating export for " + `Calculating export for ${mat.name} in ${this.name}'s ${city} division failed with error: ${e}`,
mat.name +
" in " +
this.name +
"'s " +
city +
" division failed with " +
"error: " +
e,
); );
continue; continue;
} }
if (isNaN(amt)) { if (isNaN(amt)) {
dialogBoxCreate( dialogBoxCreate(
"Error calculating export amount for " + `Error calculating export amount for ${mat.name} in ${this.name}'s ${city} division.`,
mat.name +
" in " +
this.name +
"'s " +
city +
" division.",
); );
continue; continue;
} }
@ -915,7 +888,7 @@ export class Industry implements IIndustry {
if (corporation.divisions[foo].name === exp.ind) { if (corporation.divisions[foo].name === exp.ind) {
const expIndustry = corporation.divisions[foo]; const expIndustry = corporation.divisions[foo];
const expWarehouse = expIndustry.warehouses[exp.city]; const expWarehouse = expIndustry.warehouses[exp.city];
if (!(expWarehouse instanceof Warehouse)) { if (!expWarehouse) {
console.error(`Invalid export! ${expIndustry.name} ${exp.city}`); console.error(`Invalid export! ${expIndustry.name} ${exp.city}`);
break; break;
} }
@ -958,7 +931,7 @@ export class Industry implements IIndustry {
//Produce Scientific Research based on R&D employees //Produce Scientific Research based on R&D employees
//Scientific Research can be produced without a warehouse //Scientific Research can be produced without a warehouse
if (office instanceof OfficeSpace) { if (office) {
this.sciResearch.qty += this.sciResearch.qty +=
0.004 * 0.004 *
Math.pow(office.employeeProd[EmployeePositions.RandD], 0.5) * Math.pow(office.employeeProd[EmployeePositions.RandD], 0.5) *
@ -970,7 +943,7 @@ export class Industry implements IIndustry {
} }
//Process production & sale of this industry's FINISHED products (including all of their stats) //Process production & sale of this industry's FINISHED products (including all of their stats)
processProducts(marketCycles = 1, corporation: ICorporation): [number, number] { processProducts(marketCycles = 1, corporation: Corporation): [number, number] {
let revenue = 0; let revenue = 0;
const expenses = 0; const expenses = 0;
@ -997,7 +970,7 @@ export class Industry implements IIndustry {
for (const prodName of Object.keys(this.products)) { for (const prodName of Object.keys(this.products)) {
if (this.products.hasOwnProperty(prodName)) { if (this.products.hasOwnProperty(prodName)) {
const prod = this.products[prodName]; const prod = this.products[prodName];
if (prod instanceof Product && prod.fin) { if (prod && prod.fin) {
revenue += this.processProduct(marketCycles, prod, corporation); revenue += this.processProduct(marketCycles, prod, corporation);
} }
} }
@ -1006,14 +979,14 @@ export class Industry implements IIndustry {
} }
//Processes FINISHED products //Processes FINISHED products
processProduct(marketCycles = 1, product: Product, corporation: ICorporation): number { processProduct(marketCycles = 1, product: Product, corporation: Corporation): number {
let totalProfit = 0; let totalProfit = 0;
for (let i = 0; i < CorporationConstants.Cities.length; ++i) { for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
const city = CorporationConstants.Cities[i]; const city = CorporationConstants.Cities[i];
const office = this.offices[city]; const office = this.offices[city];
if (office === 0) continue; if (office === 0) continue;
const warehouse = this.warehouses[city]; const warehouse = this.warehouses[city];
if (warehouse instanceof Warehouse) { if (warehouse) {
switch (this.state) { switch (this.state) {
case "PRODUCTION": { case "PRODUCTION": {
//Calculate the maximum production of this material based //Calculate the maximum production of this material based
@ -1172,13 +1145,7 @@ export class Industry implements IIndustry {
tmp = eval(tmp); tmp = eval(tmp);
} catch (e) { } catch (e) {
dialogBoxCreate( dialogBoxCreate(
"Error evaluating your sell price expression for " + `Error evaluating your sell price expression for ${product.name} in ${this.name}'s ${city} office. Sell price is being set to MAX`,
product.name +
" in " +
this.name +
"'s " +
city +
" office. Sell price is being set to MAX",
); );
tmp = product.maxsll; tmp = product.maxsll;
} }
@ -1223,7 +1190,7 @@ export class Industry implements IIndustry {
if (state === "EXPORT") { if (state === "EXPORT") {
for (let i = 0; i < CorporationConstants.Cities.length; ++i) { for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
const city = CorporationConstants.Cities[i]; const city = CorporationConstants.Cities[i];
if (!(this.warehouses[city] instanceof Warehouse)) { if (!this.warehouses[city]) {
continue; continue;
} }
const warehouse = this.warehouses[city]; const warehouse = this.warehouses[city];
@ -1252,7 +1219,7 @@ export class Industry implements IIndustry {
return 1e9 * Math.pow(1.06, this.numAdVerts); return 1e9 * Math.pow(1.06, this.numAdVerts);
} }
applyAdVert(corporation: ICorporation): void { applyAdVert(corporation: Corporation): void {
const advMult = corporation.getAdvertisingMultiplier() * this.getAdvertisingMultiplier(); const advMult = corporation.getAdvertisingMultiplier() * this.getAdvertisingMultiplier();
const awareness = (this.awareness + 3 * advMult) * (1.01 * advMult); const awareness = (this.awareness + 3 * advMult) * (1.01 * advMult);
this.awareness = Math.min(awareness, Number.MAX_VALUE); this.awareness = Math.min(awareness, Number.MAX_VALUE);

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { ResearchTree } from "./ResearchTree"; import { ResearchTree } from "./ResearchTree";
import { ICorporation } from "./ICorporation"; import { Corporation } from "./Corporation";
import { getBaseResearchTreeCopy, getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree"; import { getBaseResearchTreeCopy, getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree";
import { MoneyCost } from "./ui/MoneyCost"; import { MoneyCost } from "./ui/MoneyCost";
@ -59,8 +59,8 @@ export const IndustryStartingCosts: IIndustryMap<number> = {
}; };
// Map of description for each industry // Map of description for each industry
export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.ReactElement> = { export const IndustryDescriptions: IIndustryMap<(corp: Corporation) => React.ReactElement> = {
Energy: (corp: ICorporation) => ( Energy: (corp: Corporation) => (
<> <>
Engage in the production and distribution of energy. Engage in the production and distribution of energy.
<br /> <br />
@ -70,7 +70,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Utilities: (corp: ICorporation) => ( Utilities: (corp: Corporation) => (
<> <>
Distribute water and provide wastewater services. Distribute water and provide wastewater services.
<br /> <br />
@ -80,7 +80,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Agriculture: (corp: ICorporation) => ( Agriculture: (corp: Corporation) => (
<> <>
Cultivate crops and breed livestock to produce food. Cultivate crops and breed livestock to produce food.
<br /> <br />
@ -90,7 +90,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: YES Recommended starting Industry: YES
</> </>
), ),
Fishing: (corp: ICorporation) => ( Fishing: (corp: Corporation) => (
<> <>
Produce food through the breeding and processing of fish and fish products. Produce food through the breeding and processing of fish and fish products.
<br /> <br />
@ -100,7 +100,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Mining: (corp: ICorporation) => ( Mining: (corp: Corporation) => (
<> <>
Extract and process metals from the earth. Extract and process metals from the earth.
<br /> <br />
@ -110,7 +110,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Food: (corp: ICorporation) => ( Food: (corp: Corporation) => (
<> <>
Create your own restaurants all around the world. Create your own restaurants all around the world.
<br /> <br />
@ -120,7 +120,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: YES Recommended starting Industry: YES
</> </>
), ),
Tobacco: (corp: ICorporation) => ( Tobacco: (corp: Corporation) => (
<> <>
Create and distribute tobacco and tobacco-related products. Create and distribute tobacco and tobacco-related products.
<br /> <br />
@ -130,7 +130,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: YES Recommended starting Industry: YES
</> </>
), ),
Chemical: (corp: ICorporation) => ( Chemical: (corp: Corporation) => (
<> <>
Produce industrial chemicals. Produce industrial chemicals.
<br /> <br />
@ -140,7 +140,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Pharmaceutical: (corp: ICorporation) => ( Pharmaceutical: (corp: Corporation) => (
<> <>
Discover, develop, and create new pharmaceutical drugs. Discover, develop, and create new pharmaceutical drugs.
<br /> <br />
@ -150,7 +150,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Computer: (corp: ICorporation) => ( Computer: (corp: Corporation) => (
<> <>
Develop and manufacture new computer hardware and networking infrastructures. Develop and manufacture new computer hardware and networking infrastructures.
<br /> <br />
@ -160,7 +160,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Robotics: (corp: ICorporation) => ( Robotics: (corp: Corporation) => (
<> <>
Develop and create robots. Develop and create robots.
<br /> <br />
@ -170,7 +170,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
Software: (corp: ICorporation) => ( Software: (corp: Corporation) => (
<> <>
Develop computer software and create AI Cores. Develop computer software and create AI Cores.
<br /> <br />
@ -180,7 +180,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: YES Recommended starting Industry: YES
</> </>
), ),
Healthcare: (corp: ICorporation) => ( Healthcare: (corp: Corporation) => (
<> <>
Create and manage hospitals. Create and manage hospitals.
<br /> <br />
@ -190,7 +190,7 @@ export const IndustryDescriptions: IIndustryMap<(corp: ICorporation) => React.Re
Recommended starting Industry: NO Recommended starting Industry: NO
</> </>
), ),
RealEstate: (corp: ICorporation) => ( RealEstate: (corp: Corporation) => (
<> <>
Develop and manage real estate properties. Develop and manage real estate properties.
<br /> <br />

@ -4,8 +4,8 @@ import { getRandomInt } from "../utils/helpers/getRandomInt";
import { generateRandomString } from "../utils/StringHelperFunctions"; import { generateRandomString } from "../utils/StringHelperFunctions";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { Employee } from "./Employee"; import { Employee } from "./Employee";
import { IIndustry } from "./IIndustry"; import { Industry } from "./Industry";
import { ICorporation } from "./ICorporation"; import { Corporation } from "./Corporation";
interface IParams { interface IParams {
loc?: string; loc?: string;
@ -68,7 +68,7 @@ export class OfficeSpace {
return this.employees.length >= this.size; return this.employees.length >= this.size;
} }
process(marketCycles = 1, corporation: ICorporation, industry: IIndustry): number { process(marketCycles = 1, corporation: Corporation, industry: Industry): number {
// HRBuddy AutoRecruitment and training // HRBuddy AutoRecruitment and training
if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) { if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) {
const emp = this.hireRandomEmployee(); const emp = this.hireRandomEmployee();
@ -177,7 +177,7 @@ export class OfficeSpace {
} }
} }
calculateEmployeeProductivity(corporation: ICorporation, industry: IIndustry): void { calculateEmployeeProductivity(corporation: Corporation, industry: Industry): void {
//Reset //Reset
for (const name of Object.keys(this.employeeProd)) { for (const name of Object.keys(this.employeeProd)) {
this.employeeProd[name] = 0; this.employeeProd[name] = 0;

@ -1,6 +1,6 @@
import { EmployeePositions } from "./EmployeePositions"; import { EmployeePositions } from "./EmployeePositions";
import { MaterialSizes } from "./MaterialSizes"; import { MaterialSizes } from "./MaterialSizes";
import { IIndustry } from "./IIndustry"; import { Industry } from "./Industry";
import { ProductRatingWeights, IProductRatingWeight } from "./ProductRatingWeights"; import { ProductRatingWeights, IProductRatingWeight } from "./ProductRatingWeights";
import { createCityMap } from "../Locations/createCityMap"; import { createCityMap } from "../Locations/createCityMap";
@ -157,7 +157,7 @@ export class Product {
} }
// @param industry - Industry object. Reference to industry that makes this Product // @param industry - Industry object. Reference to industry that makes this Product
finishProduct(industry: IIndustry): void { finishProduct(industry: Industry): void {
this.fin = true; this.fin = true;
// Calculate properties // Calculate properties
@ -248,7 +248,7 @@ export class Product {
} }
} }
calculateRating(industry: IIndustry): void { calculateRating(industry: Industry): void {
const weights: IProductRatingWeight = ProductRatingWeights[industry.type]; const weights: IProductRatingWeight = ProductRatingWeights[industry.type];
if (weights == null) { if (weights == null) {
console.error(`Could not find product rating weights for: ${industry}`); console.error(`Could not find product rating weights for: ${industry}`);

@ -1,14 +1,14 @@
import { Material } from "./Material"; import { Material } from "./Material";
import { ICorporation } from "./ICorporation"; import { Corporation } from "./Corporation";
import { IIndustry } from "./IIndustry"; import { Industry } from "./Industry";
import { MaterialSizes } from "./MaterialSizes"; import { MaterialSizes } from "./MaterialSizes";
import { IMap } from "../types"; import { IMap } from "../types";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
interface IConstructorParams { interface IConstructorParams {
corp?: ICorporation; corp?: Corporation;
industry?: IIndustry; industry?: Industry;
loc?: string; loc?: string;
size?: number; size?: number;
} }
@ -96,7 +96,7 @@ export class Warehouse {
} }
} }
updateSize(corporation: ICorporation, industry: IIndustry): void { updateSize(corporation: Corporation, industry: Industry): void {
try { try {
this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier(); this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier();
} catch (e: unknown) { } catch (e: unknown) {

@ -1,39 +1,7 @@
import { CityName } from "./../../Locations/data/CityNames"; import { CityName } from "./../../Locations/data/CityNames";
const CyclesPerMarketCycle = 50; const CyclesPerMarketCycle = 50;
const AllCorporationStates = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"]; const AllCorporationStates = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
export const CorporationConstants: { export const CorporationConstants = {
INITIALSHARES: number;
SHARESPERPRICEUPDATE: number;
IssueNewSharesCooldown: number;
SellSharesCooldown: number;
CyclesPerMarketCycle: number;
CyclesPerIndustryStateCycle: number;
SecsPerMarketCycle: number;
Cities: string[];
WarehouseInitialCost: number;
WarehouseInitialSize: number;
WarehouseUpgradeBaseCost: number;
OfficeInitialCost: number;
OfficeInitialSize: number;
OfficeUpgradeBaseCost: number;
BribeThreshold: number;
BribeToRepRatio: number;
ProductProductionCostRatio: number;
DividendMaxRate: number;
EmployeeSalaryMultiplier: number;
CyclesPerEmployeeRaise: number;
EmployeeRaiseAmount: number;
BaseMaxProducts: number;
AllCorporationStates: string[];
AllMaterials: string[];
AllIndustryTypes: string[];
AllUnlocks: string[];
AllUpgrades: string[];
AllResearch: string[];
FundingRoundShares: number[];
FundingRoundMultiplier: number[];
ValuationLength: number;
} = {
INITIALSHARES: 1e9, //Total number of shares you have at your company INITIALSHARES: 1e9, //Total number of shares you have at your company
SHARESPERPRICEUPDATE: 1e6, //When selling large number of shares, price is dynamically updated for every batch of this amount SHARESPERPRICEUPDATE: 1e6, //When selling large number of shares, price is dynamically updated for every batch of this amount
IssueNewSharesCooldown: 216e3, // 12 Hour in terms of game cycles IssueNewSharesCooldown: 216e3, // 12 Hour in terms of game cycles

@ -1,14 +1,11 @@
import React, { useContext } from "react"; import React, { useContext } from "react";
import { ICorporation } from "../ICorporation"; import { Corporation } from "../Corporation";
import { IIndustry } from "../IIndustry"; import { Industry } from "../Industry";
export const Context: { export const Context = {
Corporation: React.Context<ICorporation>; Corporation: React.createContext<Corporation>({} as Corporation),
Division: React.Context<IIndustry>; Division: React.createContext<Industry>({} as Industry),
} = {
Corporation: React.createContext<ICorporation>({} as ICorporation),
Division: React.createContext<IIndustry>({} as IIndustry),
}; };
export const useCorporation = (): ICorporation => useContext(Context.Corporation); export const useCorporation = (): Corporation => useContext(Context.Corporation);
export const useDivision = (): IIndustry => useContext(Context.Division); export const useDivision = (): Industry => useContext(Context.Division);

@ -2,11 +2,11 @@
// These are the tabs at the top of the UI that let you switch to different // These are the tabs at the top of the UI that let you switch to different
// divisions, see an overview of your corporation, or create a new industry // divisions, see an overview of your corporation, or create a new industry
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { IIndustry } from "../IIndustry"; import { Industry } from "../Industry";
import { MainPanel } from "./MainPanel"; import { MainPanel } from "./MainPanel";
import { Industries } from "../IndustryData"; import { Industries } from "../IndustryData";
import { ExpandIndustryTab } from "./ExpandIndustryTab"; import { ExpandIndustryTab } from "./ExpandIndustryTab";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { Context } from "./Context"; import { Context } from "./Context";
import { Overview } from "./Overview"; import { Overview } from "./Overview";
@ -14,8 +14,7 @@ import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab"; import Tab from "@mui/material/Tab";
export function CorporationRoot(): React.ReactElement { export function CorporationRoot(): React.ReactElement {
const player = use.Player(); const corporation = Player.corporation;
const corporation = player.corporation;
if (corporation === null) return <></>; if (corporation === null) return <></>;
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -33,7 +32,7 @@ export function CorporationRoot(): React.ReactElement {
const canExpand = const canExpand =
Object.keys(Industries).filter( Object.keys(Industries).filter(
(industryType: string) => (industryType: string) =>
corporation.divisions.find((division: IIndustry) => division.type === industryType) === undefined, corporation.divisions.find((division: Industry) => division.type === industryType) === undefined,
).length > 0; ).length > 0;
return ( return (

@ -2,7 +2,7 @@ import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { IndustryStartingCosts, Industries, IndustryDescriptions } from "../IndustryData"; import { IndustryStartingCosts, Industries, IndustryDescriptions } from "../IndustryData";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import { IIndustry } from "../IIndustry"; import { Industry } from "../Industry";
import { NewIndustry } from "../Actions"; import { NewIndustry } from "../Actions";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -23,7 +23,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
const possibleIndustries = allIndustries const possibleIndustries = allIndustries
.filter( .filter(
(industryType: string) => (industryType: string) =>
corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined, corp.divisions.find((division: Industry) => division.type === industryType) === undefined,
) )
.sort(); .sort();
const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : ""); const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : "");

@ -1,8 +1,8 @@
import { IIndustry } from "../IIndustry"; import { Industry } from "../Industry";
// Returns a boolean indicating whether the given material is relevant for the // Returns a boolean indicating whether the given material is relevant for the
// current industry. // current industry.
export function isRelevantMaterial(matName: string, division: IIndustry): boolean { export function isRelevantMaterial(matName: string, division: Industry): boolean {
// Materials that affect Production multiplier // Materials that affect Production multiplier
const prodMultiplierMats = ["Hardware", "Robots", "AICores", "RealEstate", "AI Cores", "Real Estate"]; const prodMultiplierMats = ["Hardware", "Robots", "AICores", "RealEstate", "AI Cores", "Real Estate"];

@ -7,7 +7,6 @@ import { IndustryOverview } from "./IndustryOverview";
import { IndustryWarehouse } from "./IndustryWarehouse"; import { IndustryWarehouse } from "./IndustryWarehouse";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { use } from "../../ui/Context";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "./Context";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
@ -19,7 +18,6 @@ interface IProps {
} }
export function Industry(props: IProps): React.ReactElement { export function Industry(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const division = useDivision(); const division = useDivision();
return ( return (
@ -31,7 +29,6 @@ export function Industry(props: IProps): React.ReactElement {
<Box sx={{ width: "50%" }}> <Box sx={{ width: "50%" }}>
<IndustryWarehouse <IndustryWarehouse
rerender={props.rerender} rerender={props.rerender}
player={player}
corp={corp} corp={corp}
currentCity={props.city} currentCity={props.city}
division={division} division={division}

@ -1,9 +1,9 @@
import React from "react"; import React from "react";
import { IIndustry } from "../IIndustry"; import { Industry } from "../Industry";
import { MathJaxWrapper } from "../../MathJaxWrapper"; import { MathJaxWrapper } from "../../MathJaxWrapper";
interface IProps { interface IProps {
division: IIndustry; division: Industry;
} }
export function IndustryProductEquation(props: IProps): React.ReactElement { export function IndustryProductEquation(props: IProps): React.ReactElement {

@ -3,8 +3,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { Material } from "../Material";
import { Product } from "../Product";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { SmartSupplyModal } from "./modals/SmartSupplyModal"; import { SmartSupplyModal } from "./modals/SmartSupplyModal";
import { ProductElem } from "./ProductElem"; import { ProductElem } from "./ProductElem";
@ -13,9 +11,8 @@ import { MaterialSizes } from "../MaterialSizes";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { ICorporation } from "../ICorporation"; import { Corporation } from "../Corporation";
import { IIndustry } from "../IIndustry"; import { Industry } from "../Industry";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { isRelevantMaterial } from "./Helpers"; import { isRelevantMaterial } from "./Helpers";
import { IndustryProductEquation } from "./IndustryProductEquation"; import { IndustryProductEquation } from "./IndustryProductEquation";
@ -31,11 +28,10 @@ import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
interface IProps { interface IProps {
corp: ICorporation; corp: Corporation;
division: IIndustry; division: Industry;
warehouse: Warehouse | 0; warehouse: Warehouse | 0;
currentCity: string; currentCity: string;
player: IPlayer;
rerender: () => void; rerender: () => void;
} }
@ -94,7 +90,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
// Create React components for materials // Create React components for materials
const mats = []; const mats = [];
for (const matName of Object.keys(props.warehouse.materials)) { for (const matName of Object.keys(props.warehouse.materials)) {
if (!(props.warehouse.materials[matName] instanceof Material)) continue; if (!props.warehouse.materials[matName]) continue;
// Only create UI for materials that are relevant for the industry or in stock // Only create UI for materials that are relevant for the industry or in stock
const isInStock = props.warehouse.materials[matName].qty > 0; const isInStock = props.warehouse.materials[matName].qty > 0;
const isRelevant = isRelevantMaterial(matName, division); const isRelevant = isRelevantMaterial(matName, division);
@ -115,7 +111,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
if (division.makesProducts && Object.keys(division.products).length > 0) { if (division.makesProducts && Object.keys(division.products).length > 0) {
for (const productName of Object.keys(division.products)) { for (const productName of Object.keys(division.products)) {
const product = division.products[productName]; const product = division.products[productName];
if (!(product instanceof Product)) continue; if (!product) continue;
products.push( products.push(
<ProductElem rerender={props.rerender} city={props.currentCity} key={productName} product={product} />, <ProductElem rerender={props.rerender} city={props.currentCity} key={productName} product={product} />,
); );
@ -219,7 +215,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
} }
export function IndustryWarehouse(props: IProps): React.ReactElement { export function IndustryWarehouse(props: IProps): React.ReactElement {
if (props.warehouse instanceof Warehouse) { if (props.warehouse) {
return <WarehouseRoot {...props} />; return <WarehouseRoot {...props} />;
} else { } else {
return <EmptyWarehouse rerender={props.rerender} city={props.currentCity} />; return <EmptyWarehouse rerender={props.rerender} city={props.currentCity} />;

@ -4,7 +4,7 @@
import React from "react"; import React from "react";
import { CityTabs } from "./CityTabs"; import { CityTabs } from "./CityTabs";
import { IIndustry } from "../IIndustry"; import { Industry } from "../Industry";
import { Context, useCorporation } from "./Context"; import { Context, useCorporation } from "./Context";
import { CityName } from "../../Locations/data/CityNames"; import { CityName } from "../../Locations/data/CityNames";
@ -18,7 +18,7 @@ export function MainPanel(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = const division =
props.divisionName !== "Overview" props.divisionName !== "Overview"
? corp.divisions.find((division: IIndustry) => division.name === props.divisionName) ? corp.divisions.find((division: Industry) => division.name === props.divisionName)
: undefined; // use undefined because find returns undefined : undefined; // use undefined because find returns undefined
if (division === undefined) throw new Error("Cannot find division"); if (division === undefined) throw new Error("Cannot find division");

@ -2,7 +2,6 @@
// (right-side panel in the Industry UI) // (right-side panel in the Industry UI)
import React, { useState } from "react"; import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace";
import { Material } from "../Material"; import { Material } from "../Material";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { ExportModal } from "./modals/ExportModal"; import { ExportModal } from "./modals/ExportModal";
@ -45,7 +44,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
const mat = props.mat; const mat = props.mat;
const markupLimit = mat.getMarkupLimit(); const markupLimit = mat.getMarkupLimit();
const office = division.offices[city]; const office = division.offices[city];
if (!(office instanceof OfficeSpace)) { if (!office) {
throw new Error(`Could not get OfficeSpace object for this city (${city})`); throw new Error(`Could not get OfficeSpace object for this city (${city})`);
} }

@ -1,6 +1,6 @@
import * as React from "react"; import * as React from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { ICorporation } from "../ICorporation"; import { Corporation } from "../Corporation";
import { Theme } from "@mui/material/styles"; import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
@ -18,7 +18,7 @@ const useStyles = makeStyles((theme: Theme) =>
interface IProps { interface IProps {
money: number; money: number;
corp: ICorporation; corp: Corporation;
} }
export function MoneyCost(props: IProps): React.ReactElement { export function MoneyCost(props: IProps): React.ReactElement {

@ -21,7 +21,7 @@ import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFuncti
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate"; import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsTable } from "../../ui/React/StatsTable"; import { StatsTable } from "../../ui/React/StatsTable";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
@ -34,7 +34,6 @@ interface IProps {
rerender: () => void; rerender: () => void;
} }
export function Overview({ rerender }: IProps): React.ReactElement { export function Overview({ rerender }: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const profit: number = corp.revenue - corp.expenses; const profit: number = corp.revenue - corp.expenses;
@ -100,7 +99,7 @@ export function Overview({ rerender }: IProps): React.ReactElement {
</Typography> </Typography>
} }
> >
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button> <Button onClick={() => corp.getStarterGuide()}>Getting Started Guide</Button>
</Tooltip> </Tooltip>
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />} {corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
<BribeButton /> <BribeButton />
@ -240,12 +239,11 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
} }
function BribeButton(): React.ReactElement { function BribeButton(): React.ReactElement {
const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const canBribe = const canBribe =
corp.valuation >= CorporationConstants.BribeThreshold && corp.valuation >= CorporationConstants.BribeThreshold &&
player.factions.filter((f) => Factions[f].getInfo().offersWork()).length > 0; Player.factions.filter((f) => Factions[f].getInfo().offersWork()).length > 0;
function openBribe(): void { function openBribe(): void {
if (!canBribe) return; if (!canBribe) return;

@ -4,7 +4,7 @@ import { CorporationConstants } from "../../data/Constants";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { use } from "../../../ui/Context"; import { Player } from "../../../Player";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -19,11 +19,10 @@ interface IProps {
} }
export function BribeFactionModal(props: IProps): React.ReactElement { export function BribeFactionModal(props: IProps): React.ReactElement {
const player = use.Player(); const factions = Player.factions.filter((name: string) => {
const factions = player.factions.filter((name: string) => {
const info = Factions[name].getInfo(); const info = Factions[name].getInfo();
if (!info.offersWork()) return false; if (!info.offersWork()) return false;
if (player.hasGangWith(name)) return false; if (Player.hasGangWith(name)) return false;
return true; return true;
}); });
const corp = useCorporation(); const corp = useCorporation();
@ -60,9 +59,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
const fac = Factions[selectedFaction]; const fac = Factions[selectedFaction];
if (disabled) return; if (disabled) return;
const rep = repGain(money); const rep = repGain(money);
dialogBoxCreate( dialogBoxCreate(`You gained ${numeralWrapper.formatReputation(rep)} reputation with ${fac.name} by bribing them.`);
"You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
);
fac.playerReputation += rep; fac.playerReputation += rep;
corp.funds = corp.funds - money; corp.funds = corp.funds - money;
props.onClose(); props.onClose();
@ -79,7 +76,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
{factions.map((name: string) => { {factions.map((name: string) => {
const info = Factions[name].getInfo(); const info = Factions[name].getInfo();
if (!info.offersWork()) return; if (!info.offersWork()) return;
if (player.hasGangWith(name)) return; if (Player.hasGangWith(name)) return;
return ( return (
<MenuItem key={name} value={name}> <MenuItem key={name} value={name}>
{name} {name}

@ -1,7 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { use } from "../../../ui/Context"; import { Player } from "../../../Player";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -19,7 +19,6 @@ interface IProps {
// Create a popup that lets the player buyback shares // Create a popup that lets the player buyback shares
// This is created when the player clicks the "Buyback Shares" button in the overview panel // This is created when the player clicks the "Buyback Shares" button in the overview panel
export function BuybackSharesModal(props: IProps): React.ReactElement { export function BuybackSharesModal(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState<number>(NaN); const [shares, setShares] = useState<number>(NaN);
@ -30,12 +29,12 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
isNaN(shares) || isNaN(shares) ||
shares <= 0 || shares <= 0 ||
shares > corp.issuedShares || shares > corp.issuedShares ||
shares * buybackPrice > player.money; shares * buybackPrice > Player.money;
function buy(): void { function buy(): void {
if (disabled) return; if (disabled) return;
try { try {
BuyBackShares(corp, player, shares); BuyBackShares(corp, shares);
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }

@ -2,7 +2,8 @@ import React, { useState } from "react";
import { Money } from "../../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { use } from "../../../ui/Context"; import { Router } from "../../../ui/GameRoot";
import { Player } from "../../../Player";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
@ -13,10 +14,8 @@ interface IProps {
} }
export function CreateCorporationModal(props: IProps): React.ReactElement { export function CreateCorporationModal(props: IProps): React.ReactElement {
const player = use.Player(); const canSelfFund = Player.canAfford(150e9);
const router = use.Router(); if (!Player.canAccessCorporation() || Player.hasCorporation()) {
const canSelfFund = player.canAfford(150e9);
if (!player.canAccessCorporation() || player.hasCorporation()) {
props.onClose(); props.onClose();
return <></>; return <></>;
} }
@ -35,11 +34,11 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
return; return;
} }
player.startCorporation(name); Player.startCorporation(name);
player.loseMoney(150e9, "corporation"); Player.loseMoney(150e9, "corporation");
props.onClose(); props.onClose();
router.toCorporation(); Router.toCorporation();
} }
function seed(): void { function seed(): void {
@ -47,17 +46,17 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
return; return;
} }
player.startCorporation(name, 500e6); Player.startCorporation(name, 500e6);
props.onClose(); props.onClose();
router.toCorporation(); Router.toCorporation();
} }
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography> <Typography>
Would you like to start a corporation? This will require $150b for registration and initial funding.{" "} Would you like to start a corporation? This will require $150b for registration and initial funding.{" "}
{player.bitNodeN === 3 && {Player.bitNodeN === 3 &&
`This $150b `This $150b
can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million
shares`} shares`}
@ -66,13 +65,13 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
If you would like to start one, please enter a name for your corporation below: If you would like to start one, please enter a name for your corporation below:
</Typography> </Typography>
<TextField autoFocus={true} placeholder="Corporation Name" onChange={onChange} value={name} /> <TextField autoFocus={true} placeholder="Corporation Name" onChange={onChange} value={name} />
{player.bitNodeN === 3 && ( {Player.bitNodeN === 3 && (
<Button onClick={seed} disabled={name == ""}> <Button onClick={seed} disabled={name == ""}>
Use seed money Use seed money
</Button> </Button>
)} )}
<Button onClick={selfFund} disabled={name == "" || !canSelfFund}> <Button onClick={selfFund} disabled={name == "" || !canSelfFund}>
Self-Fund (<Money money={150e9} player={player} />) Self-Fund (<Money money={150e9} forPurchase={true} />)
</Button> </Button>
</Modal> </Modal>
); );

@ -2,7 +2,7 @@ import React, { useState } from "react";
import { dialogBoxCreate } from "../../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Material } from "../../Material"; import { Material } from "../../Material";
import { Export } from "../../Export"; import { Export } from "../../Export";
import { IIndustry } from "../../IIndustry"; import { Industry } from "../../Industry";
import { ExportMaterial } from "../../Actions"; import { ExportMaterial } from "../../Actions";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
@ -23,9 +23,7 @@ interface IProps {
// Create a popup that lets the player manage exports // Create a popup that lets the player manage exports
export function ExportModal(props: IProps): React.ReactElement { export function ExportModal(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const possibleDivisions = corp.divisions.filter((division: IIndustry) => const possibleDivisions = corp.divisions.filter((division: Industry) => isRelevantMaterial(props.mat.name, division));
isRelevantMaterial(props.mat.name, division),
);
if (possibleDivisions.length === 0) throw new Error("Export popup created with no divisions."); if (possibleDivisions.length === 0) throw new Error("Export popup created with no divisions.");
const defaultDivision = possibleDivisions[0]; const defaultDivision = possibleDivisions[0];
if (Object.keys(defaultDivision.warehouses).length === 0) if (Object.keys(defaultDivision.warehouses).length === 0)
@ -72,7 +70,7 @@ export function ExportModal(props: IProps): React.ReactElement {
rerender(); rerender();
} }
const currentDivision = corp.divisions.find((division: IIndustry) => division.name === industry); const currentDivision = corp.divisions.find((division: Industry) => division.name === industry);
if (currentDivision === undefined) if (currentDivision === undefined)
throw new Error(`Export popup somehow ended up with undefined division '${currentDivision}'`); throw new Error(`Export popup somehow ended up with undefined division '${currentDivision}'`);
const possibleCities = Object.keys(currentDivision.warehouses).filter( const possibleCities = Object.keys(currentDivision.warehouses).filter(
@ -90,8 +88,8 @@ export function ExportModal(props: IProps): React.ReactElement {
</Typography> </Typography>
<Select onChange={onIndustryChange} value={industry}> <Select onChange={onIndustryChange} value={industry}>
{corp.divisions {corp.divisions
.filter((division: IIndustry) => isRelevantMaterial(props.mat.name, division)) .filter((division: Industry) => isRelevantMaterial(props.mat.name, division))
.map((division: IIndustry) => ( .map((division: Industry) => (
<MenuItem key={division.name} value={division.name}> <MenuItem key={division.name} value={division.name}>
{division.name} {division.name}
</MenuItem> </MenuItem>

@ -89,14 +89,11 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
let dialogContents = let dialogContents =
`Issued ${numeralWrapper.format(newShares, "0.000a")} new shares` + `Issued ${numeralWrapper.format(newShares, "0.000a")} new shares` +
` and raised ${numeralWrapper.formatMoney(profit)}.`; ` and raised ${numeralWrapper.formatMoney(profit)}.` +
if (privateShares > 0) { (privateShares > 0)
dialogContents += `<br>${numeralWrapper.format( ? "\n" + numeralWrapper.format(privateShares, "0.000a") + "of these shares were bought by private investors."
privateShares, : "";
"0.000a", dialogContents += `\n\nStock price decreased to ${numeralWrapper.formatMoney(corp.sharePrice)}`;
)} of these shares were bought by private investors.`;
}
dialogContents += `<br><br>Stock price decreased to ${numeralWrapper.formatMoney(corp.sharePrice)}`;
dialogBoxCreate(dialogContents); dialogBoxCreate(dialogContents);
} }

@ -2,7 +2,7 @@ import React, { useState } from "react";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { IndustryResearchTrees } from "../../IndustryData"; import { IndustryResearchTrees } from "../../IndustryData";
import { CorporationConstants } from "../../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { IIndustry } from "../../IIndustry"; import { Industry } from "../../Industry";
import { Research } from "../../Actions"; import { Research } from "../../Actions";
import { Node } from "../../ResearchTree"; import { Node } from "../../ResearchTree";
import { ResearchMap } from "../../ResearchMap"; import { ResearchMap } from "../../ResearchMap";
@ -20,7 +20,7 @@ import CheckIcon from "@mui/icons-material/Check";
interface INodeProps { interface INodeProps {
n: Node | null; n: Node | null;
division: IIndustry; division: Industry;
} }
function Upgrade({ n, division }: INodeProps): React.ReactElement { function Upgrade({ n, division }: INodeProps): React.ReactElement {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@ -42,9 +42,7 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
} }
dialogBoxCreate( dialogBoxCreate(
`Researched ${n.text}. It may take a market cycle ` + `Researched ${n.text}. It may take a market cycle (~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of the Research apply.`,
`(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
`the Research apply.`,
); );
} }
@ -131,7 +129,7 @@ function Upgrade({ n, division }: INodeProps): React.ReactElement {
interface IProps { interface IProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
industry: IIndustry; industry: Industry;
} }
// Create the Research Tree UI for this Industry // Create the Research Tree UI for this Industry

@ -2,9 +2,8 @@ import React, { useState } from "react";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { use } from "../../../ui/Context";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import { ICorporation } from "../../ICorporation"; import { Corporation } from "../../Corporation";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Money } from "../../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
@ -20,13 +19,12 @@ interface IProps {
// Create a popup that lets the player sell Corporation shares // Create a popup that lets the player sell Corporation shares
// This is created when the player clicks the "Sell Shares" button in the overview panel // This is created when the player clicks the "Sell Shares" button in the overview panel
export function SellSharesModal(props: IProps): React.ReactElement { export function SellSharesModal(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState<number>(NaN); const [shares, setShares] = useState<number>(NaN);
const disabled = isNaN(shares) || shares <= 0 || shares > corp.numShares; const disabled = isNaN(shares) || shares <= 0 || shares > corp.numShares;
function ProfitIndicator(props: { shares: number | null; corp: ICorporation }): React.ReactElement { function ProfitIndicator(props: { shares: number | null; corp: Corporation }): React.ReactElement {
if (props.shares === null) return <></>; if (props.shares === null) return <></>;
let text = ""; let text = "";
if (isNaN(props.shares) || props.shares <= 0) { if (isNaN(props.shares) || props.shares <= 0) {
@ -49,7 +47,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
function sell(): void { function sell(): void {
if (disabled) return; if (disabled) return;
try { try {
const profit = SellShares(corp, player, shares); const profit = SellShares(corp, shares);
props.onClose(); props.onClose();
dialogBoxCreate( dialogBoxCreate(
<> <>

@ -2,7 +2,6 @@ import React, { useState } from "react";
import { Warehouse } from "../../Warehouse"; import { Warehouse } from "../../Warehouse";
import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../../Actions"; import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../../Actions";
import { Material } from "../../Material";
import { dialogBoxCreate } from "../../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context"; import { useDivision } from "../Context";
@ -62,7 +61,7 @@ export function SmartSupplyModal(props: IProps): React.ReactElement {
// Create React components for materials // Create React components for materials
const mats = []; const mats = [];
for (const matName of Object.keys(props.warehouse.materials)) { for (const matName of Object.keys(props.warehouse.materials)) {
if (!(props.warehouse.materials[matName] instanceof Material)) continue; if (!props.warehouse.materials[matName]) continue;
if (!Object.keys(division.reqMats).includes(matName)) continue; if (!Object.keys(division.reqMats).includes(matName)) continue;
mats.push(<Leftover key={matName} warehouse={props.warehouse} matName={matName} />); mats.push(<Leftover key={matName} warehouse={props.warehouse} matName={matName} />);
} }

@ -41,8 +41,7 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
if (mult > 0) { if (mult > 0) {
dialogBoxCreate( dialogBoxCreate(
"You threw a party for the office! The morale and happiness " + "You threw a party for the office! The morale and happiness of each employee increased by " +
"of each employee increased by " +
numeralWrapper.formatPercentage(mult - 1), numeralWrapper.formatPercentage(mult - 1),
); );
} }

@ -2,7 +2,7 @@ import React from "react";
import { numeralWrapper } from "../../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { CorporationConstants } from "../../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { OfficeSpace } from "../../OfficeSpace"; import { OfficeSpace } from "../../OfficeSpace";
import { ICorporation } from "../../ICorporation"; import { Corporation } from "../../Corporation";
import { UpgradeOfficeSize } from "../../Actions"; import { UpgradeOfficeSize } from "../../Actions";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
@ -14,7 +14,7 @@ import Box from "@mui/material/Box";
interface IUpgradeButton { interface IUpgradeButton {
cost: number; cost: number;
size: number; size: number;
corp: ICorporation; corp: Corporation;
office: OfficeSpace; office: OfficeSpace;
onClose: () => void; onClose: () => void;
rerender: () => void; rerender: () => void;

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