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

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

@ -1,6 +1,6 @@
import React, { useEffect } from "react";
import Typography from "@mui/material/Typography";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { Exploit } from "../../Exploits/Exploit";
const metaBB = "https://bitburner-official.github.io/bitburner-legacy/";
@ -12,11 +12,10 @@ const style = {
};
export function BBCabinetRoot(): React.ReactElement {
const player = use.Player();
useEffect(() => {
window.addEventListener("message", function (this: Window, ev: MessageEvent<boolean>) {
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 { FactionNames } from "../Faction/data/FactionNames";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Player } from "../Player";
import { AugmentationNames } from "./data/AugmentationNames";
import { CONSTANTS } from "../Constants";
import { StaticAugmentations } from "./StaticAugmentations";
@ -531,26 +531,26 @@ export class Augmentation {
}
}
getCost(player: IPlayer): AugmentationCosts {
getCost(): AugmentationCosts {
const augmentationReference = StaticAugmentations[this.name];
let moneyCost = augmentationReference.baseCost;
let repCost = augmentationReference.baseRepRequirement;
if (augmentationReference.name === AugmentationNames.NeuroFluxGovernor) {
let nextLevel = this.getLevel(player);
let nextLevel = this.getLevel();
--nextLevel;
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
repCost = augmentationReference.baseRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost;
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();
}
} else if (augmentationReference.factions.includes(FactionNames.ShadowsOfAnarchy)) {
const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name);
const soaMultiplier = Math.pow(
CONSTANTS.SoACostMult,
soaAugmentationNames.filter((augmentationName) => player.hasAugmentation(augmentationName)).length,
soaAugmentationNames.filter((augmentationName) => Player.hasAugmentation(augmentationName)).length,
);
moneyCost = augmentationReference.baseCost * soaMultiplier;
if (soaAugmentationNames.find((augmentationName) => augmentationName === augmentationReference.name)) {
@ -566,19 +566,19 @@ export class Augmentation {
return { moneyCost, repCost };
}
getLevel(player: IPlayer): number {
getLevel(): number {
// Get current Neuroflux level based on Player's augmentations
if (this.name === AugmentationNames.NeuroFluxGovernor) {
let currLevel = 0;
for (let i = 0; i < player.augmentations.length; ++i) {
if (player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = player.augmentations[i].level;
for (let i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
}
}
// Account for purchased but uninstalled Augmentations
for (let i = 0; i < player.queuedAugmentations.length; ++i) {
if (player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel;
}
}

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

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

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

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

@ -12,14 +12,13 @@ import Tooltip from "@mui/material/Tooltip";
import React, { useState } from "react";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { StaticAugmentations } from "../StaticAugmentations";
import { AugmentationNames } from "../data/AugmentationNames";
export function InstalledAugmentations(): React.ReactElement {
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]);

@ -4,7 +4,7 @@
import { DoubleArrow } from "@mui/icons-material";
import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material";
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 { Player } from "../../Player";
import { Settings } from "../../Settings/Settings";
@ -15,7 +15,7 @@ function calculateAugmentedStats(): Multipliers {
let augP: Multipliers = defaultMultipliers();
for (const aug of Player.queuedAugmentations) {
const augObj = StaticAugmentations[aug.name];
augP = mergeAugmentation(augP, augObj.mults);
augP = mergeMultipliers(augP, augObj.mults);
}
return augP;
}

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

@ -6,7 +6,7 @@ import { purchaseAugmentation } from "../../Faction/FactionHelpers";
import { isRepeatableAug } from "../AugmentationHelpers";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@ -18,14 +18,12 @@ interface IProps {
}
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
if (typeof props.aug === "undefined" || typeof props.faction === "undefined") {
if (!props.aug || !props.faction) {
return <></>;
}
const player = use.Player();
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;
}
@ -44,7 +42,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
<br />
<br />
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 />
</Typography>

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

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

@ -5,7 +5,7 @@ import { uniqueId } from "lodash";
import React from "react";
import { SpecialServers } from "../../Server/data/SpecialServers";
import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { StatsRow } from "../../ui/React/StatsRow";
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
import { IBitNodeMultipliers } from "../BitNodeMultipliers";
@ -33,13 +33,12 @@ export function BitnodeMultiplierDescription({ n, level }: IProps): React.ReactE
}
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 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,
// or if it's BN12, ∞
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 (
<Box sx={{ columnCount: 2, columnGap: 1, mb: -2 }}>
@ -277,8 +276,7 @@ function InfiltrationMults({ 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) {
const rows: IBNMultRows = {
@ -297,8 +295,7 @@ function BladeburnerMults({ 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 rows: IBNMultRows = {
@ -313,8 +310,7 @@ function StanekMults({ 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 = {
GangSoftcap: {
@ -328,8 +324,7 @@ function GangMults({ 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) {
const rows: IBNMultRows = {

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

@ -1,8 +1,7 @@
import React from "react";
import { enterBitNode } from "../../RedPill";
import { BitNodes } from "../BitNode";
import { IRouter } from "../../ui/Router";
import { use } from "../../ui/Context";
import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@ -15,11 +14,9 @@ interface IProps {
level: number;
destroyedBitNode: number;
flume: boolean;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
export function PortalModal(props: IProps): React.ReactElement {
const router = use.Router();
const bitNodeKey = "BitNode" + props.n;
const bitNode = BitNodes[bitNodeKey];
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()}`}
autoFocus={true}
onClick={() => {
props.enter(router, props.flume, props.destroyedBitNode, props.n);
enterBitNode(props.flume, props.destroyedBitNode, props.n);
props.onClose();
}}
>

@ -3,9 +3,12 @@ import { getRandomInt } from "../utils/helpers/getRandomInt";
import { addOffset } from "../utils/helpers/addOffset";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { BladeburnerConstants } from "./data/Constants";
import { IBladeburner } from "./IBladeburner";
import { IAction, ISuccessChanceParams } from "./IAction";
import { IPerson } from "../PersonObjects/IPerson";
import { Bladeburner } from "./Bladeburner";
import { Person } from "../PersonObjects/Person";
interface ISuccessChanceParams {
est: boolean;
}
class StatsMultiplier {
[key: string]: number;
@ -41,7 +44,7 @@ export interface IActionParams {
teamCount?: number;
}
export class Action implements IAction {
export class Action {
name = "";
// 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
* @param inst {Bladeburner} - Bladeburner instance
*/
attempt(inst: IBladeburner, person: IPerson): boolean {
attempt(inst: Bladeburner, person: Person): boolean {
return Math.random() < this.getSuccessChance(inst, person);
}
@ -162,7 +165,7 @@ export class Action implements IAction {
return 1;
}
getActionTime(inst: IBladeburner, person: IPerson): number {
getActionTime(inst: Bladeburner, person: Person): number {
const difficulty = this.getDifficulty();
let baseTime = difficulty / BladeburnerConstants.DifficultyToTimeFactor;
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.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getTeamSuccessBonus(inst: IBladeburner): number {
getTeamSuccessBonus(inst: Bladeburner): number {
return 1;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getActionTypeSkillSuccessBonus(inst: IBladeburner): number {
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
return 1;
}
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number {
getChaosCompetencePenalty(inst: Bladeburner, params: ISuccessChanceParams): number {
const city = inst.getCurrentCity();
if (params.est) {
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();
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
@ -212,7 +215,7 @@ export class Action implements IAction {
return 1;
}
getEstSuccessChance(inst: IBladeburner, person: IPerson): [number, number] {
getEstSuccessChance(inst: Bladeburner, person: Person): [number, number] {
function clamp(x: number): number {
return Math.max(0, Math.min(x, 1));
}
@ -233,7 +236,7 @@ export class Action implements IAction {
* @params - options:
* 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) {
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";
interface IParams {
@ -6,7 +5,7 @@ interface IParams {
type?: number;
}
export class ActionIdentifier implements IActionIdentifier {
export class ActionIdentifier {
name = "";
type = -1;

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

@ -1,6 +1,4 @@
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { IBladeburner } from "./IBladeburner";
import { IActionIdentifier } from "./IActionIdentifier";
import { ActionIdentifier } from "./ActionIdentifier";
import { ActionTypes } from "./data/ActionTypes";
import { Growths } from "./data/Growths";
@ -13,11 +11,11 @@ import { formatNumber } from "../utils/StringHelperFunctions";
import { Skills } from "./Skills";
import { Skill } from "./Skill";
import { City } from "./City";
import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Action } from "./Action";
import { Player } from "../Player";
import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IPerson } from "../PersonObjects/IPerson";
import { IRouter } from "../ui/Router";
import { Person } from "../PersonObjects/Person";
import { Router } from "../ui/GameRoot";
import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt";
@ -25,7 +23,6 @@ import { BladeburnerConstants } from "./data/Constants";
import { numeralWrapper } from "../ui/numeralFormat";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { dialogBoxCreate } from "../ui/React/DialogBox";
@ -39,13 +36,13 @@ import { KEY } from "../utils/helpers/keyCodes";
import { isSleeveInfiltrateWork } from "../PersonObjects/Sleeve/Work/SleeveInfiltrateWork";
import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportWork";
interface BlackOpsAttempt {
export interface BlackOpsAttempt {
error?: string;
isAvailable?: boolean;
action?: BlackOperation;
}
export class Bladeburner implements IBladeburner {
export class Bladeburner {
numHosp = 0;
moneyLost = 0;
rank = 0;
@ -67,7 +64,7 @@ export class Bladeburner implements IBladeburner {
actionTimeCurrent = 0;
actionTimeOverflow = 0;
action: IActionIdentifier = new ActionIdentifier({
action: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"],
});
@ -89,18 +86,18 @@ export class Bladeburner implements IBladeburner {
events: true,
};
automateEnabled = false;
automateActionHigh: IActionIdentifier = new ActionIdentifier({
automateActionHigh: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"],
});
automateThreshHigh = 0;
automateActionLow: IActionIdentifier = new ActionIdentifier({
automateActionLow: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"],
});
automateThreshLow = 0;
consoleHistory: string[] = [];
consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"];
constructor(player?: IPlayer) {
constructor() {
for (let i = 0; i < BladeburnerConstants.CityNames.length; ++i) {
this.cities[BladeburnerConstants.CityNames[i]] = new City(BladeburnerConstants.CityNames[i]);
}
@ -108,16 +105,14 @@ export class Bladeburner implements IBladeburner {
this.updateSkillMultipliers(); // Calls resetSkillMultipliers()
// Max Stamina is based on stats and Bladeburner-specific bonuses
if (player) this.calculateMaxStamina(player);
this.calculateMaxStamina();
this.stamina = this.maxStamina;
this.create();
}
getCurrentCity(): City {
const city = this.cities[this.city];
if (!(city instanceof City)) {
throw new Error("Bladeburner.getCurrentCity() did not properly return a City object");
}
if (!city) throw new Error("Invalid city in Bladeburner.getCurrentCity()");
return city;
}
@ -125,7 +120,7 @@ export class Bladeburner implements IBladeburner {
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
if (this.blackops[actionId.name] != null) {
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 };
}
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;
this.action = actionId;
this.actionTimeCurrent = 0;
@ -179,7 +175,7 @@ export class Bladeburner implements IBladeburner {
if (action.count < 1) {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this, person);
this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) {
exceptionAlert(e);
}
@ -196,7 +192,7 @@ export class Bladeburner implements IBladeburner {
if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this, person);
this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) {
exceptionAlert(e);
}
@ -214,14 +210,14 @@ export class Bladeburner implements IBladeburner {
if (testBlackOp.action === undefined) {
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) {
exceptionAlert(e);
}
break;
}
case ActionTypes["Recruitment"]:
this.actionTimeToComplete = this.getRecruitmentTime(person);
this.actionTimeToComplete = this.getRecruitmentTime(Player);
break;
case ActionTypes["Training"]:
case ActionTypes["FieldAnalysis"]:
@ -234,7 +230,7 @@ export class Bladeburner implements IBladeburner {
this.actionTimeToComplete = 60;
break;
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();
}
executeConsoleCommands(player: IPlayer, commands: string): void {
executeConsoleCommands(commands: string): void {
try {
// Console History
if (this.consoleHistory[this.consoleHistory.length - 1] != commands) {
@ -264,7 +260,7 @@ export class Bladeburner implements IBladeburner {
const arrayOfCommands = commands.split(";");
for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]);
this.executeConsoleCommand(arrayOfCommands[i]);
}
} catch (e: unknown) {
exceptionAlert(e);
@ -309,7 +305,7 @@ export class Bladeburner implements IBladeburner {
}
// working on
getActionIdFromTypeAndName(type = "", name = ""): IActionIdentifier | null {
getActionIdFromTypeAndName(type = "", name = ""): ActionIdentifier | null {
if (type === "" || name === "") {
return null;
}
@ -394,7 +390,7 @@ export class Bladeburner implements IBladeburner {
return null;
}
executeStartConsoleCommand(player: IPlayer, args: string[]): void {
executeStartConsoleCommand(args: string[]): void {
if (args.length !== 3) {
this.postToConsole("Invalid usage of 'start' console command: start [type] [name]");
this.postToConsole("Use 'help start' for more info");
@ -407,7 +403,7 @@ export class Bladeburner implements IBladeburner {
if (GeneralActions[name] != null) {
this.action.type = ActionTypes[name];
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid action name specified: " + args[2]);
}
@ -417,7 +413,7 @@ export class Bladeburner implements IBladeburner {
if (this.contracts[name] != null) {
this.action.type = ActionTypes.Contract;
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid contract name specified: " + args[2]);
}
@ -429,7 +425,7 @@ export class Bladeburner implements IBladeburner {
if (this.operations[name] != null) {
this.action.type = ActionTypes.Operation;
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid Operation name specified: " + args[2]);
}
@ -441,7 +437,7 @@ export class Bladeburner implements IBladeburner {
if (BlackOperations[name] != null) {
this.action.type = ActionTypes.BlackOperation;
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid BlackOp name specified: " + args[2]);
}
@ -542,7 +538,7 @@ export class Bladeburner implements IBladeburner {
case 3: {
const skillName = args[2];
const skill = Skills[skillName];
if (skill == null || !(skill instanceof Skill)) {
if (!skill) {
this.postToConsole("Invalid skill name (Note that it is case-sensitive): " + skillName);
break;
}
@ -684,10 +680,7 @@ export class Bladeburner implements IBladeburner {
".",
);
} else if (flag.toLowerCase().includes("en")) {
if (
!(this.automateActionLow instanceof ActionIdentifier) ||
!(this.automateActionHigh instanceof ActionIdentifier)
) {
if (!this.automateActionLow || !this.automateActionHigh) {
return this.log("Failed to enable automation. Actions were not set");
}
this.automateEnabled = true;
@ -820,7 +813,7 @@ export class Bladeburner implements IBladeburner {
return args;
}
executeConsoleCommand(player: IPlayer, command: string): void {
executeConsoleCommand(command: string): void {
command = command.trim();
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);
break;
case "start":
this.executeStartConsoleCommand(player, args);
this.executeStartConsoleCommand(args);
break;
case "stop":
this.resetAction();
@ -898,9 +891,7 @@ export class Bladeburner implements IBladeburner {
// Choose random source/destination city for events
const sourceCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)];
const sourceCity = this.cities[sourceCityName];
if (!(sourceCity instanceof City)) {
throw new Error("sourceCity was not a City object in Bladeburner.randomEvent()");
}
if (!sourceCity) throw new Error("Invalid sourceCity in Bladeburner.randomEvent()");
let destCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)];
while (destCityName === sourceCityName) {
@ -908,9 +899,7 @@ export class Bladeburner implements IBladeburner {
}
const destCity = this.cities[destCityName];
if (!(sourceCity instanceof City) || !(destCity instanceof City)) {
throw new Error("sourceCity/destCity was not a City object in Bladeburner.randomEvent()");
}
if (!sourceCity || !destCity) throw new Error("Invalid sourceCity or destCity in Bladeburner.randomEvent()");
if (chance <= 0.05) {
// New Synthoid Community, 5%
@ -994,7 +983,7 @@ export class Bladeburner implements IBladeburner {
* @param action(Action obj) - Derived action class
* @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();
/**
@ -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)
const CharismaLinearFactor = 1e3;
const CharismaExponentialFactor = 0.045;
@ -1034,11 +1023,11 @@ export class Bladeburner implements IBladeburner {
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);
}
getRecruitmentTime(person: IPerson): number {
getRecruitmentTime(person: Person): number {
const effCharisma = person.skills.charisma * this.skillMultipliers.effCha;
const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90;
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) {
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);
this.teamSize -= losses;
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--) {
const r = Math.floor(Math.random() * sup.length);
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
* 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) {
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();
switch (actionIdent.type) {
case ActionTypes["Contract"]:
@ -1304,24 +1293,16 @@ export class Bladeburner implements IBladeburner {
this.changeRank(person, gain);
if (isOperation && this.logging.ops) {
this.log(
`${person.whoAmI()}: ` +
action.name +
" successfully completed! Gained " +
formatNumber(gain, 3) +
" rank",
`${person.whoAmI()}: ${action.name} successfully completed! Gained ${formatNumber(gain, 3)} rank`,
);
} else if (!isOperation && this.logging.contracts) {
this.log(
`${person.whoAmI()}: ` +
action.name +
" contract successfully completed! Gained " +
formatNumber(gain, 3) +
" rank and " +
numeralWrapper.formatMoney(moneyGain),
`${person.whoAmI()}: ${action.name} 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 {
retValue = this.getActionStats(action, person, false);
++action.failures;
@ -1335,7 +1316,7 @@ export class Bladeburner implements IBladeburner {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
this.hpLost += damage;
const cost = calculateHospitalizationCost(player, damage);
const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) {
++this.numHosp;
this.moneyLost += cost;
@ -1353,7 +1334,7 @@ export class Bladeburner implements IBladeburner {
} else if (!isOperation && this.logging.contracts) {
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) {
action.level = action.maxLevel;
@ -1412,7 +1393,7 @@ export class Bladeburner implements IBladeburner {
if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(player, damage);
const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) {
++this.numHosp;
this.moneyLost += cost;
@ -1440,7 +1421,7 @@ export class Bladeburner implements IBladeburner {
const losses = getRandomInt(1, teamLossMax);
this.teamSize -= losses;
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--) {
const r = Math.floor(Math.random() * sup.length);
sup[r].takeDamage(sup[r].hp.max);
@ -1603,8 +1584,8 @@ export class Bladeburner implements IBladeburner {
return retValue;
}
infiltrateSynthoidCommunities(p: IPlayer): void {
const infilSleeves = p.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length;
infiltrateSynthoidCommunities(): void {
const infilSleeves = Player.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length;
const amt = Math.pow(infilSleeves, -0.5) / 2;
for (const contract of Object.keys(this.contracts)) {
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)) {
throw new Error("NaN passed into Bladeburner.changeRank()");
}
@ -1630,7 +1611,7 @@ export class Bladeburner implements IBladeburner {
const bladeburnersFactionName = FactionNames.Bladeburners;
if (factionExists(bladeburnersFactionName)) {
const bladeburnerFac = Factions[bladeburnersFactionName];
if (!(bladeburnerFac instanceof Faction)) {
if (!bladeburnerFac) {
throw new Error(
`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.actionTimeToComplete <= 0) {
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");
}
@ -1670,31 +1651,31 @@ export class Bladeburner implements IBladeburner {
if (this.actionTimeCurrent >= this.actionTimeToComplete) {
this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete;
const action = this.getActionObject(this.action);
const retValue = this.completeAction(player, player, this.action);
player.gainMoney(retValue.money, "bladeburner");
player.gainStats(retValue);
const retValue = this.completeAction(Player, this.action);
Player.gainMoney(retValue.money, "bladeburner");
Player.gainStats(retValue);
// Operation Daedalus
if (action == null) {
throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
} 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 {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi;
calculateStaminaGainPerSecond(): number {
const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor;
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 {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi;
calculateMaxStamina(): void {
const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStamina =
(Math.pow(effAgility, 0.8) + this.staminaBonus) *
this.skillMultipliers.stamina *
player.mults.bladeburner_max_stamina;
Player.mults.bladeburner_max_stamina;
if (this.maxStamina !== maxStamina) {
const oldMax = this.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.
if (!router.isInitialized) return;
if (!Router.isInitialized) return;
// 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"]) {
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) {
@ -2006,8 +1987,8 @@ export class Bladeburner implements IBladeburner {
this.storedCycles -= seconds * BladeburnerConstants.CyclesPerSecond;
// Stamina
this.calculateMaxStamina(player);
this.stamina += this.calculateStaminaGainPerSecond(player) * seconds;
this.calculateMaxStamina();
this.stamina += this.calculateStaminaGainPerSecond() * seconds;
this.stamina = Math.min(this.maxStamina, this.stamina);
// Count increase for contracts/operations
@ -2027,9 +2008,7 @@ export class Bladeburner implements IBladeburner {
// Chaos goes down very slowly
for (const cityName of BladeburnerConstants.CityNames) {
const city = this.cities[cityName];
if (!(city instanceof City)) {
throw new Error("Invalid City object when processing passive chaos reduction in Bladeburner.process");
}
if (!city) throw new Error("Invalid city when processing passive chaos reduction in Bladeburner.process");
city.chaos -= 0.0001 * seconds;
city.chaos = Math.max(0, city.chaos);
}
@ -2042,7 +2021,7 @@ export class Bladeburner implements IBladeburner {
this.randomEventCounter += getRandomInt(240, 600);
}
this.processAction(router, player, seconds);
this.processAction(seconds);
// Automation
if (this.automateEnabled) {
@ -2053,7 +2032,7 @@ export class Bladeburner implements IBladeburner {
type: this.automateActionLow.type,
name: this.automateActionLow.name,
});
this.startAction(player, this.action);
this.startAction(this.action);
}
} else if (this.stamina >= this.automateThreshHigh) {
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,
name: this.automateActionHigh.name,
});
this.startAction(player, this.action);
this.startAction(this.action);
}
}
}
}
}
getTypeAndNameFromActionId(actionId: IActionIdentifier): {
getTypeAndNameFromActionId(actionId: ActionIdentifier): {
type: string;
name: string;
} {
@ -2121,7 +2100,7 @@ export class Bladeburner implements IBladeburner {
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 actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) {
@ -2139,7 +2118,7 @@ export class Bladeburner implements IBladeburner {
}
try {
this.startAction(player, actionId);
this.startAction(actionId);
workerScript.log(
"bladeburner.startAction",
() => `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);
if (actionId == null) {
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);
if (actionId == null) {
return "bladeburner.getActionEstimatedSuccessChance";

@ -1,4 +1,4 @@
import { IBladeburner } from "./IBladeburner";
import { Bladeburner } from "./Bladeburner";
import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
@ -7,7 +7,7 @@ export class Contract extends Action {
super(params);
}
getActionTypeSkillSuccessBonus(inst: IBladeburner): number {
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
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 { Action, IActionParams } from "./Action";
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.
getTeamSuccessBonus(inst: IBladeburner): number {
getTeamSuccessBonus(inst: Bladeburner): number {
if (this.teamCount && this.teamCount > 0) {
this.teamCount = Math.min(this.teamCount, inst.teamSize);
const teamMultiplier = Math.pow(this.teamCount, 0.05);
@ -29,11 +29,11 @@ export class Operation extends Action {
return 1;
}
getActionTypeSkillSuccessBonus(inst: IBladeburner): number {
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
return inst.skillMultipliers.successChanceOperation;
}
getChaosDifficultyBonus(inst: IBladeburner /*, params: ISuccessChanceParams*/): number {
getChaosDifficultyBonus(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
const city = inst.getCurrentCity();
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);

@ -1,33 +1,5 @@
import { CityName } from "./../../Locations/data/CityNames";
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;
} = {
export const BladeburnerConstants = {
CityNames: [
CityName.Aevum,
CityName.Chongqing,

@ -1,19 +1,4 @@
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;
} = {
export const SkillNames = {
BladesIntuition: "Blade's Intuition",
Cloak: "Cloak",
Marksman: "Marksman",

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

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

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

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

@ -2,12 +2,10 @@ import React from "react";
import { BlackOperations } from "../BlackOperations";
import { BlackOperation } from "../BlackOperation";
import { BlackOpElem } from "./BlackOpElem";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
export function BlackOpList(props: IProps): React.ReactElement {
@ -35,7 +33,7 @@ export function BlackOpList(props: IProps): React.ReactElement {
return (
<>
{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 { BlackOpList } from "./BlackOpList";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
import Typography from "@mui/material/Typography";
import { FactionNames } from "../../Faction/data/FactionNames";
import { use } from "../../ui/Context";
import { Router } from "../../ui/GameRoot";
import { BlackOperationNames } from "../data/BlackOperationNames";
import { Button } from "@mui/material";
import { CorruptableText } from "../../ui/React/CorruptableText";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
export function BlackOpPage(props: IProps): React.ReactElement {
const router = use.Router();
return (
<>
<Typography>
@ -33,11 +30,11 @@ export function BlackOpPage(props: IProps): React.ReactElement {
losses.
</Typography>
{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>
</Button>
) : (
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
<BlackOpList bladeburner={props.bladeburner} />
)}
</>
);

@ -1,11 +1,10 @@
import React from "react";
import { FactionNames } from "../../Faction/data/FactionNames";
import { use } from "../../ui/Context";
import { Router } from "../../ui/GameRoot";
import { CinematicText } from "../../ui/React/CinematicText";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
export function BladeburnerCinematic(): React.ReactElement {
const router = use.Router();
return (
<CinematicText
lines={[
@ -32,7 +31,7 @@ export function BladeburnerCinematic(): React.ReactElement {
"investigating and dealing with Synthoid threats.",
]}
onDone={() => {
router.toTerminal();
Router.toTerminal();
dialogBoxCreate(
`Visit the National Security Agency (NSA) to apply for their ${FactionNames.Bladeburners} ` +
"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 { AllPages } from "./AllPages";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import Box from "@mui/material/Box";
export function BladeburnerRoot(): React.ReactElement {
const player = use.Player();
const router = use.Router();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
@ -19,16 +17,16 @@ export function BladeburnerRoot(): React.ReactElement {
return () => clearInterval(id);
}, []);
const bladeburner = player.bladeburner;
if (bladeburner === null) return <></>;
const bladeburner = Player.bladeburner;
if (!bladeburner) return <></>;
return (
<Box display="flex" flexDirection="column">
<Box sx={{ display: "grid", gridTemplateColumns: "4fr 8fr", p: 1 }}>
<Stats bladeburner={bladeburner} player={player} router={router} />
<Console bladeburner={bladeburner} player={player} />
<Stats bladeburner={bladeburner} />
<Console bladeburner={bladeburner} />
</Box>
<AllPages bladeburner={bladeburner} player={player} />
<AllPages bladeburner={bladeburner} />
</Box>
);
}

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

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

@ -1,11 +1,9 @@
import React from "react";
import { ContractElem } from "./ContractElem";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
export function ContractList(props: IProps): React.ReactElement {
@ -14,7 +12,7 @@ export function ContractList(props: IProps): React.ReactElement {
return (
<>
{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 { ContractList } from "./ContractList";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
import Typography from "@mui/material/Typography";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
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
difficult, but grant more rank, experience, and money.
</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 { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { IBladeburner } from "../IBladeburner";
import { IAction } from "../IAction";
import { Bladeburner } from "../Bladeburner";
import { Action } from "../Action";
import { GeneralActions } from "../data/GeneralActions";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Player } from "../../Player";
import { CopyableText } from "../../ui/React/CopyableText";
import { StartButton } from "./StartButton";
@ -15,9 +15,8 @@ import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
action: IAction;
bladeburner: Bladeburner;
action: Action;
}
export function GeneralActionElem(props: IProps): React.ReactElement {
@ -40,13 +39,13 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
case "Incite Violence":
return 60;
case "Recruitment":
return props.bladeburner.getRecruitmentTime(props.player);
return props.bladeburner.getRecruitmentTime(Player);
}
return -1; // dead code
})();
const successChance =
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;
const actionData = GeneralActions[props.action.name];

@ -2,12 +2,10 @@ import React from "react";
import { GeneralActionElem } from "./GeneralActionElem";
import { Action } from "../Action";
import { GeneralActions } from "../GeneralActions";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
export function GeneralActionList(props: IProps): React.ReactElement {
@ -20,7 +18,7 @@ export function GeneralActionList(props: IProps): React.ReactElement {
return (
<>
{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 { GeneralActionList } from "./GeneralActionList";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
import Typography from "@mui/material/Typography";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
export function GeneralActionPage(props: IProps): React.ReactElement {
return (
<>
<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 { StartButton } from "./StartButton";
import { TeamSizeButton } from "./TeamSizeButton";
import { IBladeburner } from "../IBladeburner";
import { Bladeburner } from "../Bladeburner";
import { Operation } from "../Operation";
import { Operations } from "../data/Operations";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Player } from "../../Player";
import { CopyableText } from "../../ui/React/CopyableText";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
action: Operation;
}
@ -33,7 +32,7 @@ export function OperationElem(props: IProps): React.ReactElement {
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
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];
if (actionData === undefined) {

@ -1,11 +1,9 @@
import React from "react";
import { OperationElem } from "./OperationElem";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
export function OperationList(props: IProps): React.ReactElement {
@ -14,7 +12,7 @@ export function OperationList(props: IProps): React.ReactElement {
return (
<>
{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 { OperationList } from "./OperationList";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Bladeburner } from "../Bladeburner";
import Typography from "@mui/material/Typography";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
bladeburner: Bladeburner;
}
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
difficult, but grant more rank and experience.
</Typography>
<OperationList bladeburner={props.bladeburner} player={props.player} />
<OperationList bladeburner={props.bladeburner} />
</>
);
}

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

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

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

@ -1,20 +1,20 @@
import React from "react";
import { IBladeburner } from "../IBladeburner";
import { Bladeburner } from "../Bladeburner";
import { BlackOperation } from "../BlackOperation";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import Button from "@mui/material/Button";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { ActionIdentifier } from "../ActionIdentifier";
interface IProps {
bladeburner: IBladeburner;
bladeburner: Bladeburner;
type: number;
name: string;
rerender: () => void;
}
export function StartButton(props: IProps): React.ReactElement {
const player = use.Player();
const action = props.bladeburner.getActionObject({ name: props.name, type: props.type });
const action = props.bladeburner.getActionObject(new ActionIdentifier({ name: props.name, type: props.type }));
if (action == null) {
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;
props.bladeburner.action.type = props.type;
props.bladeburner.action.name = props.name;
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) player.finishWork(true);
props.bladeburner.startAction(player, props.bladeburner.action);
if (!Player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) Player.finishWork(true);
props.bladeburner.startAction(props.bladeburner.action);
props.rerender();
}

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

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

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

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

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

@ -1,8 +1,8 @@
import * as React from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Player } from "../Player";
import { Money } from "../ui/React/Money";
import { Game, reachedLimit } from "./Game";
import { win, reachedLimit } from "./Game";
import { Deck } from "./CardDeck/Deck";
import { Hand } from "./CardDeck/Hand";
import { InputAdornment } from "@mui/material";
@ -24,10 +24,6 @@ enum Result {
Tie = "Push! (Tie)",
}
type Props = {
p: IPlayer;
};
type State = {
playerHand: Hand;
dealerHand: Hand;
@ -40,11 +36,11 @@ type State = {
wagerInvalidHelperText: string;
};
export class Blackjack extends Game<Props, State> {
export class Blackjack extends React.Component<Record<string, never>, State> {
deck: Deck;
constructor(props: Props) {
super(props);
constructor() {
super({});
this.deck = new Deck(DECK_COUNT);
@ -64,20 +60,19 @@ export class Blackjack extends Game<Props, State> {
}
canStartGame = (): boolean => {
const { p } = this.props;
const { bet } = this.state;
return p.canAfford(bet);
return Player.canAfford(bet);
};
startGame = (): void => {
if (!this.canStartGame() || reachedLimit(this.props.p)) {
if (!this.canStartGame() || reachedLimit()) {
return;
}
// 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.
this.props.p.loseMoney(this.state.bet, "casino");
win(-this.state.bet);
const playerHand = 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}`);
})(); // This can't happen, right?
this.win(this.props.p, gains);
win(gains);
this.setState({
gameInProgress: false,
result,
@ -239,7 +234,6 @@ export class Blackjack extends Game<Props, State> {
};
wagerOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { p } = this.props;
const betInput = event.target.value;
const wager = Math.round(parseFloat(betInput));
if (isNaN(wager)) {
@ -263,7 +257,7 @@ export class Blackjack extends Game<Props, State> {
wagerInvalid: true,
wagerInvalidHelperText: "Exceeds max bet",
});
} else if (!p.canAfford(wager)) {
} else if (!Player.canAfford(wager)) {
this.setState({
bet: 0,
betInput,

@ -5,7 +5,6 @@
*/
import React, { useState } from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { BadRNG } from "./RNG";
import { win, reachedLimit } from "./Game";
import { trusted } from "./utils";
@ -15,14 +14,10 @@ import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
type IProps = {
p: IPlayer;
};
const minPlay = 0;
const maxPlay = 10e3;
export function CoinFlip(props: IProps): React.ReactElement {
export function CoinFlip(): React.ReactElement {
const [investment, setInvestment] = useState(1000);
const [result, setResult] = useState(<span> </span>);
const [status, setStatus] = useState("");
@ -43,7 +38,7 @@ export function CoinFlip(props: IProps): React.ReactElement {
}
function play(guess: string): void {
if (reachedLimit(props.p)) return;
if (reachedLimit()) return;
const v = BadRNG.random();
let letter: string;
if (v < 0.5) {
@ -65,11 +60,11 @@ export function CoinFlip(props: IProps): React.ReactElement {
setTimeout(() => setPlayLock(false), 250);
if (correct) {
win(props.p, investment);
win(investment);
} else {
win(props.p, -investment);
win(-investment);
}
if (reachedLimit(props.p)) return;
if (reachedLimit()) 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 { IPlayer } from "../PersonObjects/IPlayer";
import { Money } from "../ui/React/Money";
import { win, reachedLimit } from "./Game";
import { WHRNG } from "./RNG";
@ -9,10 +8,6 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
type IProps = {
p: IPlayer;
};
const minPlay = 0;
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 [investment, setInvestment] = useState(1000);
const [canPlay, setCanPlay] = useState(true);
@ -151,7 +146,7 @@ export function Roulette(props: IProps): React.ReactElement {
}
function play(strategy: Strategy): void {
if (reachedLimit(props.p)) return;
if (reachedLimit()) return;
setCanPlay(false);
setLock(false);
@ -184,14 +179,14 @@ export function Roulette(props: IProps): React.ReactElement {
</>
);
}
win(props.p, gain);
win(gain);
setCanPlay(true);
setLock(true);
setStatus(status);
setN(n);
reachedLimit(props.p);
reachedLimit();
}, 1600);
}

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

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

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

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

@ -1,9 +1,6 @@
import { Player } from "../Player";
import { IPlayer } from "src/PersonObjects/IPlayer";
import { MaterialSizes } from "./MaterialSizes";
import { ICorporation } from "./ICorporation";
import { Corporation } from "./Corporation";
import { IIndustry } from "./IIndustry";
import { IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { Industry } from "./Industry";
import { CorporationConstants } from "./data/Constants";
@ -18,7 +15,7 @@ import { EmployeePositions } from "./EmployeePositions";
import { ResearchMap } from "./ResearchMap";
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))
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) {
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) {
throw new Error("Insufficient funds");
}
@ -72,7 +69,7 @@ export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnl
corporation.unlock(upgrade);
}
export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgrade): void {
export function LevelUpgrade(corporation: Corporation, upgrade: CorporationUpgrade): void {
const baseCost = upgrade.basePrice;
const priceMult = upgrade.priceMult;
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) {
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;
}
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 maxAmount = (warehouse.size - warehouse.sizeUsed) / matSize;
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 (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!");
@ -287,20 +284,20 @@ export function SellShares(corporation: ICorporation, player: IPlayer, numShares
corporation.sharePrice = newSharePrice;
corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
corporation.shareSaleCooldown = CorporationConstants.SellSharesCooldown;
player.gainMoney(profit, "corporation");
Player.gainMoney(profit, "corporation");
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 (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 (!corporation.public) throw new Error("You haven't gone public!");
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.issuedShares -= numShares;
player.loseMoney(numShares * buybackPrice, "corporation");
Player.loseMoney(numShares * buybackPrice, "corporation");
return true;
}
@ -319,7 +316,7 @@ export function AutoAssignJob(office: OfficeSpace, job: string, count: number):
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 costMultiplier = 1.09;
// 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;
}
export function BuyCoffee(corp: ICorporation, office: OfficeSpace): boolean {
export function BuyCoffee(corp: Corporation, office: OfficeSpace): boolean {
const cost = office.getCoffeeCost();
if (corp.funds < cost) {
return false;
@ -347,7 +344,7 @@ export function BuyCoffee(corp: ICorporation, office: OfficeSpace): boolean {
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 cost = costPerEmployee * office.employees.length;
if (corp.funds < cost) {
@ -362,9 +359,9 @@ export function ThrowParty(corp: ICorporation, office: OfficeSpace, costPerEmplo
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 (division.warehouses[city] instanceof Warehouse) return;
if (division.warehouses[city]) return;
division.warehouses[city] = new Warehouse({
corp: corp,
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);
if (corp.funds < sizeUpgradeCost) return;
warehouse.level += amt;
@ -389,7 +386,7 @@ export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, wareho
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();
if (corp.funds < cost) return;
corp.funds = corp.funds - cost;
@ -397,8 +394,8 @@ export function HireAdVert(corp: ICorporation, division: IIndustry): void {
}
export function MakeProduct(
corp: ICorporation,
division: IIndustry,
corp: Corporation,
division: Industry,
city: string,
productName: string,
designInvest: number,
@ -442,7 +439,7 @@ export function MakeProduct(
designCost: designInvest,
advCost: marketingInvest,
});
if (products[product.name] instanceof Product) {
if (products[product.name]) {
throw new Error(`You already have a product with this name!`);
}
@ -450,7 +447,7 @@ export function MakeProduct(
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];
if (researchTree === undefined) throw new Error(`No research tree for industry '${division.type}'`);
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) {
const city = CorporationConstants.Cities[i];
const warehouse = division.warehouses[city];
if (!(warehouse instanceof Warehouse)) {
if (!warehouse) {
continue;
}
if (Player.corporation instanceof Corporation) {
if (Player.corporation) {
// Stores cycles in a "buffer". Processed separately using Engine Counters
warehouse.updateSize(Player.corporation, division);
}

@ -1,14 +1,13 @@
import { CorporationState } from "./CorporationState";
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "./data/CorporationUnlockUpgrades";
import { CorporationUpgrade, CorporationUpgrades } from "./data/CorporationUpgrades";
import { Warehouse } from "./Warehouse";
import { CorporationConstants } from "./data/Constants";
import { Industry } from "./Industry";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { showLiterature } from "../Literature/LiteratureHelpers";
import { LiteratureNames } from "../Literature/data/LiteratureNames";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Player } from "../Player";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
@ -76,7 +75,7 @@ export class Corporation {
this.storedCycles += numCycles;
}
process(player: IPlayer): void {
process(): void {
if (this.storedCycles >= CorporationConstants.CyclesPerIndustryStateCycle) {
const state = this.getState();
const marketCycles = 1;
@ -120,7 +119,7 @@ export class Corporation {
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
dialogBoxCreate(
"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)",
);
this.funds = 150e9;
@ -139,7 +138,7 @@ export class Corporation {
} else {
const totalDividends = this.dividendRate * cycleProfit;
const retainedEarnings = cycleProfit - totalDividends;
player.gainMoney(this.getCycleDividends(), "corporation");
Player.gainMoney(this.getCycleDividends(), "corporation");
this.addFunds(retainedEarnings);
}
} else {
@ -331,7 +330,7 @@ export class Corporation {
for (const city of Object.keys(industry.warehouses)) {
const warehouse = industry.warehouses[city];
if (warehouse === 0) continue;
if (industry.warehouses.hasOwnProperty(city) && warehouse instanceof Warehouse) {
if (industry.warehouses.hasOwnProperty(city) && warehouse) {
warehouse.updateSize(this, industry);
}
}
@ -428,9 +427,9 @@ export class Corporation {
// 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 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
const homeComp = player.getHomeComputer();
const homeComp = Player.getHomeComputer();
let hasHandbook = false;
const handbookFn = LiteratureNames.CorporationManagementHandbook;
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 { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { EmployeePositions } from "./EmployeePositions";
import { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry";
import { Corporation } from "./Corporation";
import { Industry } from "./Industry";
interface IParams {
name?: string;
@ -77,7 +77,7 @@ export class Employee {
return salary;
}
calculateProductivity(corporation: ICorporation, industry: IIndustry): number {
calculateProductivity(corporation: Corporation, industry: Industry): number {
const effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(),
effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(),
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 { MaterialSizes } from "./MaterialSizes";
import { Warehouse } from "./Warehouse";
import { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry";
import { Corporation } from "./Corporation";
interface IParams {
name?: string;
corp?: ICorporation;
corp?: Corporation;
type?: string;
}
export class Industry implements IIndustry {
export class Industry {
name = "";
type = Industries.Agriculture;
sciResearch = new Material({ name: "Scientific Research" });
@ -356,9 +355,7 @@ export class Industry implements IIndustry {
for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
const city = CorporationConstants.Cities[i];
const warehouse = this.warehouses[city];
if (!(warehouse instanceof Warehouse)) {
continue;
}
if (!warehouse) continue;
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;
//At the start of a cycle, store and reset revenue/expenses
@ -414,10 +411,7 @@ export class Industry implements IIndustry {
let employeeSalary = 0;
for (const officeLoc of Object.keys(this.offices)) {
const office = this.offices[officeLoc];
if (office === 0) continue;
if (office instanceof OfficeSpace) {
employeeSalary += office.process(marketCycles, corporation, this);
}
if (office) employeeSalary += office.process(marketCycles, corporation, this);
}
this.thisCycleExpenses = this.thisCycleExpenses + employeeSalary;
@ -468,7 +462,7 @@ export class Industry implements IIndustry {
for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
//If this industry has a warehouse in this city, process the market
//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]];
if (wh === 0) continue;
for (const name of Object.keys(reqMats)) {
@ -518,7 +512,7 @@ export class Industry implements IIndustry {
}
//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,
expenses = 0;
this.calculateProductionFactors();
@ -528,7 +522,7 @@ export class Industry implements IIndustry {
const office = this.offices[city];
if (office === 0) continue;
if (this.warehouses[city] instanceof Warehouse) {
if (this.warehouses[city]) {
const warehouse = this.warehouses[city];
if (warehouse === 0) continue;
@ -825,14 +819,7 @@ export class Industry implements IIndustry {
sellAmt = eval(tmp);
} catch (e) {
dialogBoxCreate(
"Error evaluating your sell amount for material " +
mat.name +
" in " +
this.name +
"'s " +
city +
" office. The sell amount " +
"is being set to zero",
`Error evaluating your sell amount for material ${mat.name} in ${this.name}'s ${city} office. The sell amount is being set to zero`,
);
sellAmt = 0;
}
@ -879,27 +866,13 @@ export class Industry implements IIndustry {
amt = eval(amtStr);
} catch (e) {
dialogBoxCreate(
"Calculating export for " +
mat.name +
" in " +
this.name +
"'s " +
city +
" division failed with " +
"error: " +
e,
`Calculating export for ${mat.name} in ${this.name}'s ${city} division failed with error: ${e}`,
);
continue;
}
if (isNaN(amt)) {
dialogBoxCreate(
"Error calculating export amount for " +
mat.name +
" in " +
this.name +
"'s " +
city +
" division.",
`Error calculating export amount for ${mat.name} in ${this.name}'s ${city} division.`,
);
continue;
}
@ -915,7 +888,7 @@ export class Industry implements IIndustry {
if (corporation.divisions[foo].name === exp.ind) {
const expIndustry = corporation.divisions[foo];
const expWarehouse = expIndustry.warehouses[exp.city];
if (!(expWarehouse instanceof Warehouse)) {
if (!expWarehouse) {
console.error(`Invalid export! ${expIndustry.name} ${exp.city}`);
break;
}
@ -958,7 +931,7 @@ export class Industry implements IIndustry {
//Produce Scientific Research based on R&D employees
//Scientific Research can be produced without a warehouse
if (office instanceof OfficeSpace) {
if (office) {
this.sciResearch.qty +=
0.004 *
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)
processProducts(marketCycles = 1, corporation: ICorporation): [number, number] {
processProducts(marketCycles = 1, corporation: Corporation): [number, number] {
let revenue = 0;
const expenses = 0;
@ -997,7 +970,7 @@ export class Industry implements IIndustry {
for (const prodName of Object.keys(this.products)) {
if (this.products.hasOwnProperty(prodName)) {
const prod = this.products[prodName];
if (prod instanceof Product && prod.fin) {
if (prod && prod.fin) {
revenue += this.processProduct(marketCycles, prod, corporation);
}
}
@ -1006,14 +979,14 @@ export class Industry implements IIndustry {
}
//Processes FINISHED products
processProduct(marketCycles = 1, product: Product, corporation: ICorporation): number {
processProduct(marketCycles = 1, product: Product, corporation: Corporation): number {
let totalProfit = 0;
for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
const city = CorporationConstants.Cities[i];
const office = this.offices[city];
if (office === 0) continue;
const warehouse = this.warehouses[city];
if (warehouse instanceof Warehouse) {
if (warehouse) {
switch (this.state) {
case "PRODUCTION": {
//Calculate the maximum production of this material based
@ -1172,13 +1145,7 @@ export class Industry implements IIndustry {
tmp = eval(tmp);
} catch (e) {
dialogBoxCreate(
"Error evaluating your sell price expression for " +
product.name +
" in " +
this.name +
"'s " +
city +
" office. Sell price is being set to MAX",
`Error evaluating your sell price expression for ${product.name} in ${this.name}'s ${city} office. Sell price is being set to MAX`,
);
tmp = product.maxsll;
}
@ -1223,7 +1190,7 @@ export class Industry implements IIndustry {
if (state === "EXPORT") {
for (let i = 0; i < CorporationConstants.Cities.length; ++i) {
const city = CorporationConstants.Cities[i];
if (!(this.warehouses[city] instanceof Warehouse)) {
if (!this.warehouses[city]) {
continue;
}
const warehouse = this.warehouses[city];
@ -1252,7 +1219,7 @@ export class Industry implements IIndustry {
return 1e9 * Math.pow(1.06, this.numAdVerts);
}
applyAdVert(corporation: ICorporation): void {
applyAdVert(corporation: Corporation): void {
const advMult = corporation.getAdvertisingMultiplier() * this.getAdvertisingMultiplier();
const awareness = (this.awareness + 3 * advMult) * (1.01 * advMult);
this.awareness = Math.min(awareness, Number.MAX_VALUE);

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

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

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

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

@ -1,39 +1,7 @@
import { CityName } from "./../../Locations/data/CityNames";
const CyclesPerMarketCycle = 50;
const AllCorporationStates = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
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;
} = {
export const CorporationConstants = {
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
IssueNewSharesCooldown: 216e3, // 12 Hour in terms of game cycles

@ -1,14 +1,11 @@
import React, { useContext } from "react";
import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry";
import { Corporation } from "../Corporation";
import { Industry } from "../Industry";
export const Context: {
Corporation: React.Context<ICorporation>;
Division: React.Context<IIndustry>;
} = {
Corporation: React.createContext<ICorporation>({} as ICorporation),
Division: React.createContext<IIndustry>({} as IIndustry),
export const Context = {
Corporation: React.createContext<Corporation>({} as Corporation),
Division: React.createContext<Industry>({} as Industry),
};
export const useCorporation = (): ICorporation => useContext(Context.Corporation);
export const useDivision = (): IIndustry => useContext(Context.Division);
export const useCorporation = (): Corporation => useContext(Context.Corporation);
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
// divisions, see an overview of your corporation, or create a new industry
import React, { useState, useEffect } from "react";
import { IIndustry } from "../IIndustry";
import { Industry } from "../Industry";
import { MainPanel } from "./MainPanel";
import { Industries } from "../IndustryData";
import { ExpandIndustryTab } from "./ExpandIndustryTab";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { Context } from "./Context";
import { Overview } from "./Overview";
@ -14,8 +14,7 @@ import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
export function CorporationRoot(): React.ReactElement {
const player = use.Player();
const corporation = player.corporation;
const corporation = Player.corporation;
if (corporation === null) return <></>;
const setRerender = useState(false)[1];
function rerender(): void {
@ -33,7 +32,7 @@ export function CorporationRoot(): React.ReactElement {
const canExpand =
Object.keys(Industries).filter(
(industryType: string) =>
corporation.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
corporation.divisions.find((division: Industry) => division.type === industryType) === undefined,
).length > 0;
return (

@ -2,7 +2,7 @@ import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { IndustryStartingCosts, Industries, IndustryDescriptions } from "../IndustryData";
import { useCorporation } from "./Context";
import { IIndustry } from "../IIndustry";
import { Industry } from "../Industry";
import { NewIndustry } from "../Actions";
import Typography from "@mui/material/Typography";
@ -23,7 +23,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
const possibleIndustries = allIndustries
.filter(
(industryType: string) =>
corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
corp.divisions.find((division: Industry) => division.type === industryType) === undefined,
)
.sort();
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
// current industry.
export function isRelevantMaterial(matName: string, division: IIndustry): boolean {
export function isRelevantMaterial(matName: string, division: Industry): boolean {
// Materials that affect Production multiplier
const prodMultiplierMats = ["Hardware", "Robots", "AICores", "RealEstate", "AI Cores", "Real Estate"];

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

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

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

@ -4,7 +4,7 @@
import React from "react";
import { CityTabs } from "./CityTabs";
import { IIndustry } from "../IIndustry";
import { Industry } from "../Industry";
import { Context, useCorporation } from "./Context";
import { CityName } from "../../Locations/data/CityNames";
@ -18,7 +18,7 @@ export function MainPanel(props: IProps): React.ReactElement {
const corp = useCorporation();
const division =
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
if (division === undefined) throw new Error("Cannot find division");

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

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

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

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

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

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

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

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

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

@ -2,9 +2,8 @@ import React, { useState } from "react";
import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal";
import { use } from "../../../ui/Context";
import { useCorporation } from "../Context";
import { ICorporation } from "../../ICorporation";
import { Corporation } from "../../Corporation";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { Money } from "../../../ui/React/Money";
@ -20,13 +19,12 @@ interface IProps {
// 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
export function SellSharesModal(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const [shares, setShares] = useState<number>(NaN);
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 <></>;
let text = "";
if (isNaN(props.shares) || props.shares <= 0) {
@ -49,7 +47,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
function sell(): void {
if (disabled) return;
try {
const profit = SellShares(corp, player, shares);
const profit = SellShares(corp, shares);
props.onClose();
dialogBoxCreate(
<>

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

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

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

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