This commit is contained in:
Snarling 2022-09-06 09:07:12 -04:00
parent cc2246213f
commit 83d357e758
203 changed files with 2263 additions and 3018 deletions

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

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

@ -20,7 +20,7 @@ import Paper from "@mui/material/Paper";
import Container from "@mui/material/Container"; import Container from "@mui/material/Container";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { ConfirmationModal } from "../../ui/React/ConfirmationModal"; import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { AugmentationNames } from "../data/AugmentationNames"; import { AugmentationNames } from "../data/AugmentationNames";
import { StaticAugmentations } from "../StaticAugmentations"; import { StaticAugmentations } from "../StaticAugmentations";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
@ -29,12 +29,8 @@ import { Info } from "@mui/icons-material";
import { Link } from "@mui/material"; import { Link } from "@mui/material";
import { AlertEvents } from "../../ui/React/AlertManager"; import { AlertEvents } from "../../ui/React/AlertManager";
interface NFGDisplayProps { const NeuroFluxDisplay = (): React.ReactElement => {
player: IPlayer; const level = Player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
}
const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
const level = player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
const openBloodDonation = () => { const openBloodDonation = () => {
AlertEvents.emit( AlertEvents.emit(
@ -67,18 +63,14 @@ const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
); );
}; };
interface EntropyDisplayProps { const EntropyDisplay = (): React.ReactElement => {
player: IPlayer; return Player.entropy > 0 ? (
}
const EntropyDisplay = ({ player }: EntropyDisplayProps): React.ReactElement => {
return player.entropy > 0 ? (
<Paper sx={{ p: 1 }}> <Paper sx={{ p: 1 }}>
<Typography variant="h5" color={Settings.theme.error}> <Typography variant="h5" color={Settings.theme.error}>
Entropy Virus - Level {player.entropy} Entropy Virus - Level {Player.entropy}
</Typography> </Typography>
<Typography color={Settings.theme.error}> <Typography color={Settings.theme.error}>
<b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}% <b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** Player.entropy) * 100, 3)}%
(multiplicative) (multiplicative)
</Typography> </Typography>
</Paper> </Paper>
@ -222,8 +214,8 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
gap: 1, gap: 1,
}} }}
> >
<NeuroFluxDisplay player={player} /> <NeuroFluxDisplay />
<EntropyDisplay player={player} /> <EntropyDisplay />
</Box> </Box>
<Box> <Box>

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

@ -44,7 +44,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
<br /> <br />
<br /> <br />
Would you like to purchase the {props.aug.name} Augmentation for&nbsp; Would you like to purchase the {props.aug.name} Augmentation for&nbsp;
<Money money={props.aug.getCost(player).moneyCost} />? <Money money={props.aug.getCost().moneyCost} />?
<br /> <br />
<br /> <br />
</Typography> </Typography>

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

@ -14,10 +14,10 @@ import { Skills } from "./Skills";
import { Skill } from "./Skill"; import { Skill } from "./Skill";
import { City } from "./City"; import { City } from "./City";
import { IAction } from "./IAction"; import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker"; import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IPerson } from "../PersonObjects/IPerson"; import { IPerson } from "../PersonObjects/IPerson";
import { IRouter } from "../ui/Router"; import { Router } from "../ui/GameRoot";
import { ConsoleHelpText } from "./data/Help"; import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
@ -39,7 +39,7 @@ import { KEY } from "../utils/helpers/keyCodes";
import { isSleeveInfiltrateWork } from "../PersonObjects/Sleeve/Work/SleeveInfiltrateWork"; import { isSleeveInfiltrateWork } from "../PersonObjects/Sleeve/Work/SleeveInfiltrateWork";
import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportWork"; import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportWork";
interface BlackOpsAttempt { export interface BlackOpsAttempt {
error?: string; error?: string;
isAvailable?: boolean; isAvailable?: boolean;
action?: BlackOperation; action?: BlackOperation;
@ -100,7 +100,7 @@ export class Bladeburner implements IBladeburner {
consoleHistory: string[] = []; consoleHistory: string[] = [];
consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"]; consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"];
constructor(player?: IPlayer) { constructor() {
for (let i = 0; i < BladeburnerConstants.CityNames.length; ++i) { for (let i = 0; i < BladeburnerConstants.CityNames.length; ++i) {
this.cities[BladeburnerConstants.CityNames[i]] = new City(BladeburnerConstants.CityNames[i]); this.cities[BladeburnerConstants.CityNames[i]] = new City(BladeburnerConstants.CityNames[i]);
} }
@ -108,7 +108,7 @@ export class Bladeburner implements IBladeburner {
this.updateSkillMultipliers(); // Calls resetSkillMultipliers() this.updateSkillMultipliers(); // Calls resetSkillMultipliers()
// Max Stamina is based on stats and Bladeburner-specific bonuses // Max Stamina is based on stats and Bladeburner-specific bonuses
if (player) this.calculateMaxStamina(player); this.calculateMaxStamina();
this.stamina = this.maxStamina; this.stamina = this.maxStamina;
this.create(); this.create();
} }
@ -162,7 +162,8 @@ export class Bladeburner implements IBladeburner {
return { isAvailable: true, action }; return { isAvailable: true, action };
} }
startAction(person: IPerson, actionId: IActionIdentifier): void { /** This function is only for the player. Sleeves use their own functions to perform blade work. */
startAction(actionId: IActionIdentifier): void {
if (actionId == null) return; if (actionId == null) return;
this.action = actionId; this.action = actionId;
this.actionTimeCurrent = 0; this.actionTimeCurrent = 0;
@ -179,7 +180,7 @@ export class Bladeburner implements IBladeburner {
if (action.count < 1) { if (action.count < 1) {
return this.resetAction(); return this.resetAction();
} }
this.actionTimeToComplete = action.getActionTime(this, person); this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
@ -196,7 +197,7 @@ export class Bladeburner implements IBladeburner {
if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) { if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
return this.resetAction(); return this.resetAction();
} }
this.actionTimeToComplete = action.getActionTime(this, person); this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
@ -214,14 +215,14 @@ export class Bladeburner implements IBladeburner {
if (testBlackOp.action === undefined) { if (testBlackOp.action === undefined) {
throw new Error("action should not be null"); throw new Error("action should not be null");
} }
this.actionTimeToComplete = testBlackOp.action.getActionTime(this, person); this.actionTimeToComplete = testBlackOp.action.getActionTime(this, Player);
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
break; break;
} }
case ActionTypes["Recruitment"]: case ActionTypes["Recruitment"]:
this.actionTimeToComplete = this.getRecruitmentTime(person); this.actionTimeToComplete = this.getRecruitmentTime(Player);
break; break;
case ActionTypes["Training"]: case ActionTypes["Training"]:
case ActionTypes["FieldAnalysis"]: case ActionTypes["FieldAnalysis"]:
@ -234,7 +235,7 @@ export class Bladeburner implements IBladeburner {
this.actionTimeToComplete = 60; this.actionTimeToComplete = 60;
break; break;
default: default:
throw new Error("Invalid Action Type in startAction(Bladeburner,player, ): " + actionId.type); throw new Error("Invalid Action Type in bladeburner.startAction(): " + actionId.type);
} }
} }
@ -252,7 +253,7 @@ export class Bladeburner implements IBladeburner {
this.updateSkillMultipliers(); this.updateSkillMultipliers();
} }
executeConsoleCommands(player: IPlayer, commands: string): void { executeConsoleCommands(commands: string): void {
try { try {
// Console History // Console History
if (this.consoleHistory[this.consoleHistory.length - 1] != commands) { if (this.consoleHistory[this.consoleHistory.length - 1] != commands) {
@ -264,7 +265,7 @@ export class Bladeburner implements IBladeburner {
const arrayOfCommands = commands.split(";"); const arrayOfCommands = commands.split(";");
for (let i = 0; i < arrayOfCommands.length; ++i) { for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]); this.executeConsoleCommand(arrayOfCommands[i]);
} }
} catch (e: unknown) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
@ -394,7 +395,7 @@ export class Bladeburner implements IBladeburner {
return null; return null;
} }
executeStartConsoleCommand(player: IPlayer, args: string[]): void { executeStartConsoleCommand(args: string[]): void {
if (args.length !== 3) { if (args.length !== 3) {
this.postToConsole("Invalid usage of 'start' console command: start [type] [name]"); this.postToConsole("Invalid usage of 'start' console command: start [type] [name]");
this.postToConsole("Use 'help start' for more info"); this.postToConsole("Use 'help start' for more info");
@ -407,7 +408,7 @@ export class Bladeburner implements IBladeburner {
if (GeneralActions[name] != null) { if (GeneralActions[name] != null) {
this.action.type = ActionTypes[name]; this.action.type = ActionTypes[name];
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid action name specified: " + args[2]); this.postToConsole("Invalid action name specified: " + args[2]);
} }
@ -417,7 +418,7 @@ export class Bladeburner implements IBladeburner {
if (this.contracts[name] != null) { if (this.contracts[name] != null) {
this.action.type = ActionTypes.Contract; this.action.type = ActionTypes.Contract;
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid contract name specified: " + args[2]); this.postToConsole("Invalid contract name specified: " + args[2]);
} }
@ -429,7 +430,7 @@ export class Bladeburner implements IBladeburner {
if (this.operations[name] != null) { if (this.operations[name] != null) {
this.action.type = ActionTypes.Operation; this.action.type = ActionTypes.Operation;
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid Operation name specified: " + args[2]); this.postToConsole("Invalid Operation name specified: " + args[2]);
} }
@ -441,7 +442,7 @@ export class Bladeburner implements IBladeburner {
if (BlackOperations[name] != null) { if (BlackOperations[name] != null) {
this.action.type = ActionTypes.BlackOperation; this.action.type = ActionTypes.BlackOperation;
this.action.name = name; this.action.name = name;
this.startAction(player, this.action); this.startAction(this.action);
} else { } else {
this.postToConsole("Invalid BlackOp name specified: " + args[2]); this.postToConsole("Invalid BlackOp name specified: " + args[2]);
} }
@ -820,7 +821,7 @@ export class Bladeburner implements IBladeburner {
return args; return args;
} }
executeConsoleCommand(player: IPlayer, command: string): void { executeConsoleCommand(command: string): void {
command = command.trim(); command = command.trim();
command = command.replace(/\s\s+/g, " "); // Replace all whitespace w/ a single space command = command.replace(/\s\s+/g, " "); // Replace all whitespace w/ a single space
@ -845,7 +846,7 @@ export class Bladeburner implements IBladeburner {
this.executeSkillConsoleCommand(args); this.executeSkillConsoleCommand(args);
break; break;
case "start": case "start":
this.executeStartConsoleCommand(player, args); this.executeStartConsoleCommand(args);
break; break;
case "stop": case "stop":
this.resetAction(); this.resetAction();
@ -1105,7 +1106,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
completeOperation(success: boolean, player: IPlayer): void { completeOperation(success: boolean): void {
if (this.action.type !== ActionTypes.Operation) { if (this.action.type !== ActionTypes.Operation) {
throw new Error("completeOperation() called even though current action is not an Operation"); throw new Error("completeOperation() called even though current action is not an Operation");
} }
@ -1126,7 +1127,7 @@ export class Bladeburner implements IBladeburner {
const losses = getRandomInt(0, max); const losses = getRandomInt(0, max);
this.teamSize -= losses; this.teamSize -= losses;
if (this.teamSize < this.sleeveSize) { if (this.teamSize < this.sleeveSize) {
const sup = player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork)); const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) { for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
const r = Math.floor(Math.random() * sup.length); const r = Math.floor(Math.random() * sup.length);
sup[r].takeDamage(sup[r].hp.max); sup[r].takeDamage(sup[r].hp.max);
@ -1256,7 +1257,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer = true): ITaskTracker { completeAction(person: IPerson, actionIdent: IActionIdentifier, isPlayer = true): ITaskTracker {
let retValue = createTaskTracker(); let retValue = createTaskTracker();
switch (actionIdent.type) { switch (actionIdent.type) {
case ActionTypes["Contract"]: case ActionTypes["Contract"]:
@ -1304,24 +1305,16 @@ export class Bladeburner implements IBladeburner {
this.changeRank(person, gain); this.changeRank(person, gain);
if (isOperation && this.logging.ops) { if (isOperation && this.logging.ops) {
this.log( this.log(
`${person.whoAmI()}: ` + `${person.whoAmI()}: ${action.name} successfully completed! Gained ${formatNumber(gain, 3)} rank`,
action.name +
" successfully completed! Gained " +
formatNumber(gain, 3) +
" rank",
); );
} else if (!isOperation && this.logging.contracts) { } else if (!isOperation && this.logging.contracts) {
this.log( this.log(
`${person.whoAmI()}: ` + `${person.whoAmI()}: ${action.name} contract successfully completed! Gained ` +
action.name + `${formatNumber(gain, 3)} rank and ${numeralWrapper.formatMoney(moneyGain)}`,
" contract successfully completed! Gained " +
formatNumber(gain, 3) +
" rank and " +
numeralWrapper.formatMoney(moneyGain),
); );
} }
} }
isOperation ? this.completeOperation(true, player) : this.completeContract(true, actionIdent); isOperation ? this.completeOperation(true) : this.completeContract(true, actionIdent);
} else { } else {
retValue = this.getActionStats(action, person, false); retValue = this.getActionStats(action, person, false);
++action.failures; ++action.failures;
@ -1335,7 +1328,7 @@ export class Bladeburner implements IBladeburner {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
this.hpLost += damage; this.hpLost += damage;
const cost = calculateHospitalizationCost(player, damage); const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) { if (person.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += cost; this.moneyLost += cost;
@ -1353,7 +1346,7 @@ export class Bladeburner implements IBladeburner {
} else if (!isOperation && this.logging.contracts) { } else if (!isOperation && this.logging.contracts) {
this.log(`${person.whoAmI()}: ` + action.name + " contract failed! " + logLossText); this.log(`${person.whoAmI()}: ` + action.name + " contract failed! " + logLossText);
} }
isOperation ? this.completeOperation(false, player) : this.completeContract(false, actionIdent); isOperation ? this.completeOperation(false) : this.completeContract(false, actionIdent);
} }
if (action.autoLevel) { if (action.autoLevel) {
action.level = action.maxLevel; action.level = action.maxLevel;
@ -1412,7 +1405,7 @@ export class Bladeburner implements IBladeburner {
if (action.hpLoss) { if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier; damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10)); damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(player, damage); const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) { if (person.takeDamage(damage)) {
++this.numHosp; ++this.numHosp;
this.moneyLost += cost; this.moneyLost += cost;
@ -1440,7 +1433,7 @@ export class Bladeburner implements IBladeburner {
const losses = getRandomInt(1, teamLossMax); const losses = getRandomInt(1, teamLossMax);
this.teamSize -= losses; this.teamSize -= losses;
if (this.teamSize < this.sleeveSize) { if (this.teamSize < this.sleeveSize) {
const sup = player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork)); const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) { for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
const r = Math.floor(Math.random() * sup.length); const r = Math.floor(Math.random() * sup.length);
sup[r].takeDamage(sup[r].hp.max); sup[r].takeDamage(sup[r].hp.max);
@ -1603,8 +1596,8 @@ export class Bladeburner implements IBladeburner {
return retValue; return retValue;
} }
infiltrateSynthoidCommunities(p: IPlayer): void { infiltrateSynthoidCommunities(): void {
const infilSleeves = p.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length; const infilSleeves = Player.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length;
const amt = Math.pow(infilSleeves, -0.5) / 2; const amt = Math.pow(infilSleeves, -0.5) / 2;
for (const contract of Object.keys(this.contracts)) { for (const contract of Object.keys(this.contracts)) {
this.contracts[contract].count += amt; this.contracts[contract].count += amt;
@ -1654,7 +1647,7 @@ export class Bladeburner implements IBladeburner {
} }
} }
processAction(router: IRouter, player: IPlayer, seconds: number): void { processAction(seconds: number): void {
if (this.action.type === ActionTypes["Idle"]) return; if (this.action.type === ActionTypes["Idle"]) return;
if (this.actionTimeToComplete <= 0) { if (this.actionTimeToComplete <= 0) {
throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`); throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`);
@ -1670,31 +1663,31 @@ export class Bladeburner implements IBladeburner {
if (this.actionTimeCurrent >= this.actionTimeToComplete) { if (this.actionTimeCurrent >= this.actionTimeToComplete) {
this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete; this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete;
const action = this.getActionObject(this.action); const action = this.getActionObject(this.action);
const retValue = this.completeAction(player, player, this.action); const retValue = this.completeAction(Player, this.action);
player.gainMoney(retValue.money, "bladeburner"); Player.gainMoney(retValue.money, "bladeburner");
player.gainStats(retValue); Player.gainStats(retValue);
// Operation Daedalus // Operation Daedalus
if (action == null) { if (action == null) {
throw new Error("Failed to get BlackOperation Object for: " + this.action.name); throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
} else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) { } else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) {
this.startAction(player, this.action); // Repeat action this.startAction(this.action); // Repeat action
} }
} }
} }
calculateStaminaGainPerSecond(player: IPlayer): number { calculateStaminaGainPerSecond(): number {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi; const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor; const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor;
const gain = (BladeburnerConstants.StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17); const gain = (BladeburnerConstants.StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17);
return gain * (this.skillMultipliers.stamina * player.mults.bladeburner_stamina_gain); return gain * (this.skillMultipliers.stamina * Player.mults.bladeburner_stamina_gain);
} }
calculateMaxStamina(player: IPlayer): void { calculateMaxStamina(): void {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi; const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStamina = const maxStamina =
(Math.pow(effAgility, 0.8) + this.staminaBonus) * (Math.pow(effAgility, 0.8) + this.staminaBonus) *
this.skillMultipliers.stamina * this.skillMultipliers.stamina *
player.mults.bladeburner_max_stamina; Player.mults.bladeburner_max_stamina;
if (this.maxStamina !== maxStamina) { if (this.maxStamina !== maxStamina) {
const oldMax = this.maxStamina; const oldMax = this.maxStamina;
this.maxStamina = maxStamina; this.maxStamina = maxStamina;
@ -1974,12 +1967,12 @@ export class Bladeburner implements IBladeburner {
}); });
} }
process(router: IRouter, player: IPlayer): void { process(): void {
// Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized. // Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized.
if (!router.isInitialized) return; if (!Router.isInitialized) return;
// If the Player starts doing some other actions, set action to idle and alert // If the Player starts doing some other actions, set action to idle and alert
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.currentWork) { if (!Player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && Player.currentWork) {
if (this.action.type !== ActionTypes["Idle"]) { if (this.action.type !== ActionTypes["Idle"]) {
let msg = "Your Bladeburner action was cancelled because you started doing something else."; let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) { if (this.automateEnabled) {
@ -2006,8 +1999,8 @@ export class Bladeburner implements IBladeburner {
this.storedCycles -= seconds * BladeburnerConstants.CyclesPerSecond; this.storedCycles -= seconds * BladeburnerConstants.CyclesPerSecond;
// Stamina // Stamina
this.calculateMaxStamina(player); this.calculateMaxStamina();
this.stamina += this.calculateStaminaGainPerSecond(player) * seconds; this.stamina += this.calculateStaminaGainPerSecond() * seconds;
this.stamina = Math.min(this.maxStamina, this.stamina); this.stamina = Math.min(this.maxStamina, this.stamina);
// Count increase for contracts/operations // Count increase for contracts/operations
@ -2042,7 +2035,7 @@ export class Bladeburner implements IBladeburner {
this.randomEventCounter += getRandomInt(240, 600); this.randomEventCounter += getRandomInt(240, 600);
} }
this.processAction(router, player, seconds); this.processAction(seconds);
// Automation // Automation
if (this.automateEnabled) { if (this.automateEnabled) {
@ -2053,7 +2046,7 @@ export class Bladeburner implements IBladeburner {
type: this.automateActionLow.type, type: this.automateActionLow.type,
name: this.automateActionLow.name, name: this.automateActionLow.name,
}); });
this.startAction(player, this.action); this.startAction(this.action);
} }
} else if (this.stamina >= this.automateThreshHigh) { } else if (this.stamina >= this.automateThreshHigh) {
if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) { if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) {
@ -2061,7 +2054,7 @@ export class Bladeburner implements IBladeburner {
type: this.automateActionHigh.type, type: this.automateActionHigh.type,
name: this.automateActionHigh.name, name: this.automateActionHigh.name,
}); });
this.startAction(player, this.action); this.startAction(this.action);
} }
} }
} }
@ -2121,7 +2114,7 @@ export class Bladeburner implements IBladeburner {
return Object.keys(Skills); return Object.keys(Skills);
} }
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean { startActionNetscriptFn(type: string, name: string, workerScript: WorkerScript): boolean {
const errorLogText = `Invalid action: type='${type}' name='${name}'`; const errorLogText = `Invalid action: type='${type}' name='${name}'`;
const actionId = this.getActionIdFromTypeAndName(type, name); const actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) { if (actionId == null) {
@ -2139,7 +2132,7 @@ export class Bladeburner implements IBladeburner {
} }
try { try {
this.startAction(player, actionId); this.startAction(actionId);
workerScript.log( workerScript.log(
"bladeburner.startAction", "bladeburner.startAction",
() => `Starting bladeburner action with type '${type}' and name '${name}'`, () => `Starting bladeburner action with type '${type}' and name '${name}'`,

@ -2,13 +2,13 @@ import { IActionIdentifier } from "./IActionIdentifier";
import { City } from "./City"; import { City } from "./City";
import { Skill } from "./Skill"; import { Skill } from "./Skill";
import { IAction } from "./IAction"; import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IPerson } from "../PersonObjects/IPerson"; import { IPerson } from "../PersonObjects/IPerson";
import { ITaskTracker } from "../PersonObjects/ITaskTracker"; import { ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { Contract } from "./Contract"; import { Contract } from "./Contract";
import { Operation } from "./Operation"; import { Operation } from "./Operation";
import { IReviverValue } from "../utils/JSONReviver";
import { BlackOpsAttempt } from "./Bladeburner";
export interface IBladeburner { export interface IBladeburner {
numHosp: number; numHosp: number;
@ -20,6 +20,7 @@ export interface IBladeburner {
totalSkillPoints: number; totalSkillPoints: number;
teamSize: number; teamSize: number;
sleeveSize: number;
teamLost: number; teamLost: number;
hpLost: number; hpLost: number;
@ -60,9 +61,10 @@ export interface IBladeburner {
getCurrentCity(): City; getCurrentCity(): City;
calculateStaminaPenalty(): number; calculateStaminaPenalty(): number;
startAction(player: IPlayer, action: IActionIdentifier): void; canAttemptBlackOp(actionId: IActionIdentifier): BlackOpsAttempt;
startAction(action: IActionIdentifier): void;
upgradeSkill(skill: Skill): void; upgradeSkill(skill: Skill): void;
executeConsoleCommands(player: IPlayer, command: string): void; executeConsoleCommands(command: string): void;
postToConsole(input: string, saveToLogs?: boolean): void; postToConsole(input: string, saveToLogs?: boolean): void;
log(input: string): void; log(input: string): void;
resetAction(): void; resetAction(): void;
@ -79,7 +81,7 @@ export interface IBladeburner {
getBlackOpNamesNetscriptFn(): string[]; getBlackOpNamesNetscriptFn(): string[];
getGeneralActionNamesNetscriptFn(): string[]; getGeneralActionNamesNetscriptFn(): string[];
getSkillNamesNetscriptFn(): string[]; getSkillNamesNetscriptFn(): string[];
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean; startActionNetscriptFn(type: string, name: string, workerScript: WorkerScript): boolean;
getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string; getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string;
getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string; getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string;
getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number; getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
@ -90,32 +92,33 @@ export interface IBladeburner {
setTeamSizeNetscriptFn(type: string, name: string, size: number, workerScript: WorkerScript): number; setTeamSizeNetscriptFn(type: string, name: string, size: number, workerScript: WorkerScript): number;
joinBladeburnerFactionNetscriptFn(workerScript: WorkerScript): boolean; joinBladeburnerFactionNetscriptFn(workerScript: WorkerScript): boolean;
getActionIdFromTypeAndName(type: string, name: string): IActionIdentifier | null; getActionIdFromTypeAndName(type: string, name: string): IActionIdentifier | null;
executeStartConsoleCommand(player: IPlayer, args: string[]): void; executeStartConsoleCommand(args: string[]): void;
executeSkillConsoleCommand(args: string[]): void; executeSkillConsoleCommand(args: string[]): void;
executeLogConsoleCommand(args: string[]): void; executeLogConsoleCommand(args: string[]): void;
executeHelpConsoleCommand(args: string[]): void; executeHelpConsoleCommand(args: string[]): void;
executeAutomateConsoleCommand(args: string[]): void; executeAutomateConsoleCommand(args: string[]): void;
parseCommandArguments(command: string): string[]; parseCommandArguments(command: string): string[];
executeConsoleCommand(player: IPlayer, command: string): void; executeConsoleCommand(command: string): void;
triggerMigration(sourceCityName: string): void; triggerMigration(sourceCityName: string): void;
triggerPotentialMigration(sourceCityName: string, chance: number): void; triggerPotentialMigration(sourceCityName: string, chance: number): void;
randomEvent(): void; randomEvent(): void;
getDiplomacyEffectiveness(player: IPlayer): number; getDiplomacyEffectiveness(person: IPerson): number;
getRecruitmentSuccessChance(player: IPerson): number; getRecruitmentSuccessChance(person: IPerson): number;
getRecruitmentTime(player: IPerson): number; getRecruitmentTime(person: IPerson): number;
resetSkillMultipliers(): void; resetSkillMultipliers(): void;
updateSkillMultipliers(): void; updateSkillMultipliers(): void;
completeOperation(success: boolean, player: IPlayer): void; completeOperation(success: boolean): void;
getActionObject(actionId: IActionIdentifier): IAction | null; getActionObject(actionId: IActionIdentifier): IAction | null;
completeContract(success: boolean, actionIdent: IActionIdentifier): void; completeContract(success: boolean, actionIdent: IActionIdentifier): void;
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer?: boolean): ITaskTracker; completeAction(person: IPerson, actionIdent: IActionIdentifier, isPlayer?: boolean): ITaskTracker;
infiltrateSynthoidCommunities(p: IPlayer): void; infiltrateSynthoidCommunities(): void;
changeRank(player: IPlayer, change: number): void; changeRank(person: IPerson, change: number): void;
processAction(router: IRouter, player: IPlayer, seconds: number): void; processAction(seconds: number): void;
calculateStaminaGainPerSecond(player: IPlayer): number; calculateStaminaGainPerSecond(): number;
calculateMaxStamina(player: IPlayer): void; calculateMaxStamina(): void;
create(): void; create(): void;
process(router: IRouter, player: IPlayer): void; process(): void;
getActionStats(action: IAction, person: IPerson, success: boolean): ITaskTracker; getActionStats(action: IAction, person: IPerson, success: boolean): ITaskTracker;
sleeveSupport(joining: boolean): void; sleeveSupport(joining: boolean): void;
toJSON():IReviverValue;
} }

@ -27,14 +27,14 @@ export function ActionLevel({ action, isActive, bladeburner, rerender }: IProps)
function increaseLevel(): void { function increaseLevel(): void {
if (!canIncrease) return; if (!canIncrease) return;
++action.level; ++action.level;
if (isActive) bladeburner.startAction(player, bladeburner.action); if (isActive) bladeburner.startAction(bladeburner.action);
rerender(); rerender();
} }
function decreaseLevel(): void { function decreaseLevel(): void {
if (!canDecrease) return; if (!canDecrease) return;
--action.level; --action.level;
if (isActive) bladeburner.startAction(player, bladeburner.action); if (isActive) bladeburner.startAction(bladeburner.action);
rerender(); rerender();
} }

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

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

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

@ -1,7 +1,6 @@
import * as React from "react"; import * as React from "react";
import { BlackOpList } from "./BlackOpList"; import { BlackOpList } from "./BlackOpList";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
@ -11,7 +10,6 @@ import { CorruptableText } from "../../ui/React/CorruptableText";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
} }
export function BlackOpPage(props: IProps): React.ReactElement { export function BlackOpPage(props: IProps): React.ReactElement {
@ -37,7 +35,7 @@ export function BlackOpPage(props: IProps): React.ReactElement {
<CorruptableText content="Destroy w0rld_d34mon"></CorruptableText> <CorruptableText content="Destroy w0rld_d34mon"></CorruptableText>
</Button> </Button>
) : ( ) : (
<BlackOpList bladeburner={props.bladeburner} player={props.player} /> <BlackOpList bladeburner={props.bladeburner} />
)} )}
</> </>
); );

@ -24,11 +24,11 @@ export function BladeburnerRoot(): React.ReactElement {
return ( return (
<Box display="flex" flexDirection="column"> <Box display="flex" flexDirection="column">
<Box sx={{ display: "grid", gridTemplateColumns: "4fr 8fr", p: 1 }}> <Box sx={{ display: "grid", gridTemplateColumns: "4fr 8fr", p: 1 }}>
<Stats bladeburner={bladeburner} player={player} router={router} /> <Stats bladeburner={bladeburner} />
<Console bladeburner={bladeburner} player={player} /> <Console bladeburner={bladeburner} />
</Box> </Box>
<AllPages bladeburner={bladeburner} player={player} /> <AllPages bladeburner={bladeburner} />
</Box> </Box>
); );
} }

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

@ -5,7 +5,7 @@ import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/Stri
import { Contracts } from "../data/Contracts"; import { Contracts } from "../data/Contracts";
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IAction } from "../IAction"; import { IAction } from "../IAction";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { SuccessChance } from "./SuccessChance"; import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import { ActionLevel } from "./ActionLevel"; import { ActionLevel } from "./ActionLevel";
@ -17,7 +17,6 @@ import Paper from "@mui/material/Paper";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
action: IAction; action: IAction;
} }
@ -32,7 +31,7 @@ export function ContractElem(props: IProps): React.ReactElement {
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
props.bladeburner.actionTimeToComplete, props.bladeburner.actionTimeToComplete,
); );
const actionTime = props.action.getActionTime(props.bladeburner, props.player); const actionTime = props.action.getActionTime(props.bladeburner, Player);
const actionData = Contracts[props.action.name]; const actionData = Contracts[props.action.name];
if (actionData === undefined) { if (actionData === undefined) {

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

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

@ -5,7 +5,7 @@ import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/Stri
import { IBladeburner } from "../IBladeburner"; import { IBladeburner } from "../IBladeburner";
import { IAction } from "../IAction"; import { IAction } from "../IAction";
import { GeneralActions } from "../data/GeneralActions"; import { GeneralActions } from "../data/GeneralActions";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import { StartButton } from "./StartButton"; import { StartButton } from "./StartButton";
@ -16,7 +16,6 @@ import Paper from "@mui/material/Paper";
interface IProps { interface IProps {
bladeburner: IBladeburner; bladeburner: IBladeburner;
player: IPlayer;
action: IAction; action: IAction;
} }
@ -40,13 +39,13 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
case "Incite Violence": case "Incite Violence":
return 60; return 60;
case "Recruitment": case "Recruitment":
return props.bladeburner.getRecruitmentTime(props.player); return props.bladeburner.getRecruitmentTime(Player);
} }
return -1; // dead code return -1; // dead code
})(); })();
const successChance = const successChance =
props.action.name === "Recruitment" props.action.name === "Recruitment"
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1)) ? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(Player), 1))
: -1; : -1;
const actionData = GeneralActions[props.action.name]; const actionData = GeneralActions[props.action.name];

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

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

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

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

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

@ -34,7 +34,7 @@ export function StartButton(props: IProps): React.ReactElement {
props.bladeburner.action.type = props.type; props.bladeburner.action.type = props.type;
props.bladeburner.action.name = props.name; props.bladeburner.action.name = props.name;
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) player.finishWork(true); if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) player.finishWork(true);
props.bladeburner.startAction(player, props.bladeburner.action); props.bladeburner.startAction(props.bladeburner.action);
props.rerender(); props.rerender();
} }

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

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

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

16
src/Casino/Game.ts Normal file

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

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

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

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

@ -1,5 +1,4 @@
import { Player } from "../Player"; import { Player } from "../Player";
import { IPlayer } from "src/PersonObjects/IPlayer";
import { MaterialSizes } from "./MaterialSizes"; import { MaterialSizes } from "./MaterialSizes";
import { ICorporation } from "./ICorporation"; import { ICorporation } from "./ICorporation";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
@ -271,7 +270,7 @@ export function BulkPurchase(corp: ICorporation, warehouse: Warehouse, material:
} }
} }
export function SellShares(corporation: ICorporation, player: IPlayer, numShares: number): number { export function SellShares(corporation: ICorporation, numShares: number): number {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); if (isNaN(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) throw new Error("Invalid value for number of shares"); if (numShares < 0) throw new Error("Invalid value for number of shares");
if (numShares > corporation.numShares) throw new Error("You don't have that many shares to sell!"); if (numShares > corporation.numShares) throw new Error("You don't have that many shares to sell!");
@ -287,20 +286,20 @@ export function SellShares(corporation: ICorporation, player: IPlayer, numShares
corporation.sharePrice = newSharePrice; corporation.sharePrice = newSharePrice;
corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate; corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
corporation.shareSaleCooldown = CorporationConstants.SellSharesCooldown; corporation.shareSaleCooldown = CorporationConstants.SellSharesCooldown;
player.gainMoney(profit, "corporation"); Player.gainMoney(profit, "corporation");
return profit; return profit;
} }
export function BuyBackShares(corporation: ICorporation, player: IPlayer, numShares: number): boolean { export function BuyBackShares(corporation: ICorporation, numShares: number): boolean {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); if (isNaN(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) throw new Error("Invalid value for number of shares"); if (numShares < 0) throw new Error("Invalid value for number of shares");
if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!"); if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!");
if (!corporation.public) throw new Error("You haven't gone public!"); if (!corporation.public) throw new Error("You haven't gone public!");
const buybackPrice = corporation.sharePrice * 1.1; const buybackPrice = corporation.sharePrice * 1.1;
if (player.money < numShares * buybackPrice) throw new Error("You cant afford that many shares!"); if (Player.money < numShares * buybackPrice) throw new Error("You cant afford that many shares!");
corporation.numShares += numShares; corporation.numShares += numShares;
corporation.issuedShares -= numShares; corporation.issuedShares -= numShares;
player.loseMoney(numShares * buybackPrice, "corporation"); Player.loseMoney(numShares * buybackPrice, "corporation");
return true; return true;
} }

@ -8,7 +8,7 @@ import { Industry } from "./Industry";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { showLiterature } from "../Literature/LiteratureHelpers"; import { showLiterature } from "../Literature/LiteratureHelpers";
import { LiteratureNames } from "../Literature/data/LiteratureNames"; import { LiteratureNames } from "../Literature/data/LiteratureNames";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
@ -76,7 +76,7 @@ export class Corporation {
this.storedCycles += numCycles; this.storedCycles += numCycles;
} }
process(player: IPlayer): void { process(): void {
if (this.storedCycles >= CorporationConstants.CyclesPerIndustryStateCycle) { if (this.storedCycles >= CorporationConstants.CyclesPerIndustryStateCycle) {
const state = this.getState(); const state = this.getState();
const marketCycles = 1; const marketCycles = 1;
@ -139,7 +139,7 @@ export class Corporation {
} else { } else {
const totalDividends = this.dividendRate * cycleProfit; const totalDividends = this.dividendRate * cycleProfit;
const retainedEarnings = cycleProfit - totalDividends; const retainedEarnings = cycleProfit - totalDividends;
player.gainMoney(this.getCycleDividends(), "corporation"); Player.gainMoney(this.getCycleDividends(), "corporation");
this.addFunds(retainedEarnings); this.addFunds(retainedEarnings);
} }
} else { } else {
@ -428,9 +428,9 @@ export class Corporation {
// Adds the Corporation Handbook (Starter Guide) to the player's home computer. // Adds the Corporation Handbook (Starter Guide) to the player's home computer.
// This is a lit file that gives introductory info to the player // This is a lit file that gives introductory info to the player
// This occurs when the player clicks the "Getting Started Guide" button on the overview panel // This occurs when the player clicks the "Getting Started Guide" button on the overview panel
getStarterGuide(player: IPlayer): void { getStarterGuide(): void {
// Check if player already has Corporation Handbook // Check if player already has Corporation Handbook
const homeComp = player.getHomeComputer(); const homeComp = Player.getHomeComputer();
let hasHandbook = false; let hasHandbook = false;
const handbookFn = LiteratureNames.CorporationManagementHandbook; const handbookFn = LiteratureNames.CorporationManagementHandbook;
for (let i = 0; i < homeComp.messages.length; ++i) { for (let i = 0; i < homeComp.messages.length; ++i) {

@ -31,7 +31,6 @@ export function Industry(props: IProps): React.ReactElement {
<Box sx={{ width: "50%" }}> <Box sx={{ width: "50%" }}>
<IndustryWarehouse <IndustryWarehouse
rerender={props.rerender} rerender={props.rerender}
player={player}
corp={corp} corp={corp}
currentCity={props.city} currentCity={props.city}
division={division} division={division}

@ -15,7 +15,6 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { isRelevantMaterial } from "./Helpers"; import { isRelevantMaterial } from "./Helpers";
import { IndustryProductEquation } from "./IndustryProductEquation"; import { IndustryProductEquation } from "./IndustryProductEquation";
@ -35,7 +34,6 @@ interface IProps {
division: IIndustry; division: IIndustry;
warehouse: Warehouse | 0; warehouse: Warehouse | 0;
currentCity: string; currentCity: string;
player: IPlayer;
rerender: () => void; rerender: () => void;
} }

@ -35,7 +35,7 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
function buy(): void { function buy(): void {
if (disabled) return; if (disabled) return;
try { try {
BuyBackShares(corp, player, shares); BuyBackShares(corp, shares);
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }

@ -72,7 +72,7 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
</Button> </Button>
)} )}
<Button onClick={selfFund} disabled={name == "" || !canSelfFund}> <Button onClick={selfFund} disabled={name == "" || !canSelfFund}>
Self-Fund (<Money money={150e9} player={player} />) Self-Fund (<Money money={150e9} forPurchase={true} />)
</Button> </Button>
</Modal> </Modal>
); );

@ -49,7 +49,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
function sell(): void { function sell(): void {
if (disabled) return; if (disabled) return;
try { try {
const profit = SellShares(corp, player, shares); const profit = SellShares(corp, shares);
props.onClose(); props.onClose();
dialogBoxCreate( dialogBoxCreate(
<> <>

@ -1,16 +1,16 @@
import { Crimes } from "./Crimes"; import { Crimes } from "./Crimes";
import { Crime } from "./Crime"; import { Crime } from "./Crime";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
export function determineCrimeSuccess(p: IPlayer, type: string): boolean { export function determineCrimeSuccess(type: string): boolean {
let chance = 0; let chance = 0;
let found = false; let found = false;
for (const i of Object.keys(Crimes)) { for (const i of Object.keys(Crimes)) {
const crime = Crimes[i]; const crime = Crimes[i];
if (crime.type === type) { if (crime.type === type) {
chance = crime.successRate(p); chance = crime.successRate(Player);
found = true; found = true;
break; break;
} }

@ -1,7 +1,5 @@
import { IPlayer } from "./PersonObjects/IPlayer"; import { Player } from "./Player";
import { Bladeburner } from "./Bladeburner/Bladeburner"; import { Bladeburner } from "./Bladeburner/Bladeburner";
import { IEngine } from "./IEngine";
import { IRouter } from "./ui/Router";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
@ -28,44 +26,38 @@ import { Entropy } from "./DevMenu/ui/Entropy";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { Exploit } from "./Exploits/Exploit"; import { Exploit } from "./Exploits/Exploit";
interface IProps { export function DevMenuRoot(): React.ReactElement {
player: IPlayer;
engine: IEngine;
router: IRouter;
}
export function DevMenuRoot(props: IProps): React.ReactElement {
useEffect(() => { useEffect(() => {
props.player.giveExploit(Exploit.YoureNotMeantToAccessThis); Player.giveExploit(Exploit.YoureNotMeantToAccessThis);
}, []); }, []);
return ( return (
<> <>
<Typography>Development Menu - Only meant to be used for testing/debugging</Typography> <Typography>Development Menu - Only meant to be used for testing/debugging</Typography>
<General player={props.player} router={props.router} /> <General />
<Stats player={props.player} /> <Stats />
<Factions player={props.player} /> <Factions />
<Augmentations player={props.player} /> <Augmentations />
<SourceFiles player={props.player} /> <SourceFiles />
<Programs player={props.player} /> <Programs />
<Servers /> <Servers />
<Companies /> <Companies />
{props.player.bladeburner instanceof Bladeburner && <BladeburnerElem player={props.player} />} {Player.bladeburner instanceof Bladeburner && <BladeburnerElem />}
{props.player.inGang() && <Gang player={props.player} />} {Player.inGang() && <Gang />}
{props.player.hasCorporation() && <Corporation player={props.player} />} {Player.hasCorporation() && <Corporation />}
<CodingContracts /> <CodingContracts />
{props.player.hasWseAccount && <StockMarket />} {Player.hasWseAccount && <StockMarket />}
{props.player.sleeves.length > 0 && <Sleeves player={props.player} />} {Player.sleeves.length > 0 && <Sleeves />}
{props.player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1) && <Stanek />} {Player.augmentations.some((aug) => aug.name === AugmentationNames.StaneksGift1) && <Stanek />}
<TimeSkip player={props.player} engine={props.engine} /> <TimeSkip />
<Achievements player={props.player} engine={props.engine} /> <Achievements />
<Entropy player={props.player} engine={props.engine} /> <Entropy />
<SaveFile /> <SaveFile />
</> </>
); );

@ -11,44 +11,39 @@ import { Tooltip } from "@mui/material";
import LockIcon from "@mui/icons-material/Lock"; import LockIcon from "@mui/icons-material/Lock";
import LockOpenIcon from "@mui/icons-material/LockOpen"; import LockOpenIcon from "@mui/icons-material/LockOpen";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { achievements } from "../../Achievements/Achievements"; import { achievements } from "../../Achievements/Achievements";
import { IEngine } from "../../IEngine"; import { Engine } from "../../engine";
interface IProps { export function Achievements(): React.ReactElement {
player: IPlayer; const [playerAchievement, setPlayerAchievements] = useState(Player.achievements.map((m) => m.ID));
engine: IEngine;
}
export function Achievements(props: IProps): React.ReactElement {
const [playerAchievement, setPlayerAchievements] = useState(props.player.achievements.map((m) => m.ID));
function grantAchievement(id: string): void { function grantAchievement(id: string): void {
props.player.giveAchievement(id); Player.giveAchievement(id);
setPlayerAchievements(props.player.achievements.map((m) => m.ID)); setPlayerAchievements(Player.achievements.map((m) => m.ID));
} }
function grantAllAchievements(): void { function grantAllAchievements(): void {
Object.values(achievements).forEach((a) => props.player.giveAchievement(a.ID)); Object.values(achievements).forEach((a) => Player.giveAchievement(a.ID));
setPlayerAchievements(props.player.achievements.map((m) => m.ID)); setPlayerAchievements(Player.achievements.map((m) => m.ID));
} }
function removeAchievement(id: string): void { function removeAchievement(id: string): void {
props.player.achievements = props.player.achievements.filter((a) => a.ID !== id); Player.achievements = Player.achievements.filter((a) => a.ID !== id);
setPlayerAchievements(props.player.achievements.map((m) => m.ID)); setPlayerAchievements(Player.achievements.map((m) => m.ID));
} }
function clearAchievements(): void { function clearAchievements(): void {
props.player.achievements = []; Player.achievements = [];
setPlayerAchievements(props.player.achievements.map((m) => m.ID)); setPlayerAchievements(Player.achievements.map((m) => m.ID));
} }
function disableEngine(): void { function disableEngine(): void {
props.engine.Counters.achievementsCounter = Number.MAX_VALUE; Engine.Counters.achievementsCounter = Number.MAX_VALUE;
} }
function enableEngine(): void { function enableEngine(): void {
props.engine.Counters.achievementsCounter = 0; Engine.Counters.achievementsCounter = 0;
} }
return ( return (

@ -12,34 +12,30 @@ import {
} from "@mui/material"; } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
interface IProps { export function Augmentations(): React.ReactElement {
player: IPlayer;
}
export function Augmentations(props: IProps): React.ReactElement {
const [augmentation, setAugmentation] = useState("Augmented Targeting I"); const [augmentation, setAugmentation] = useState("Augmented Targeting I");
function setAugmentationDropdown(event: SelectChangeEvent<string>): void { function setAugmentationDropdown(event: SelectChangeEvent<string>): void {
setAugmentation(event.target.value); setAugmentation(event.target.value);
} }
function queueAug(): void { function queueAug(): void {
props.player.queueAugmentation(augmentation); Player.queueAugmentation(augmentation);
} }
function queueAllAugs(): void { function queueAllAugs(): void {
for (const augName of Object.values(AugmentationNames)) { for (const augName of Object.values(AugmentationNames)) {
props.player.queueAugmentation(augName); Player.queueAugmentation(augName);
} }
} }
function clearAugs(): void { function clearAugs(): void {
props.player.augmentations = []; Player.augmentations = [];
} }
function clearQueuedAugs(): void { function clearQueuedAugs(): void {
props.player.queuedAugmentations = []; Player.queuedAugmentations = [];
} }
return ( return (

@ -7,21 +7,17 @@ import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { Adjuster } from "./Adjuster"; import { Adjuster } from "./Adjuster";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
const bigNumber = 1e27; const bigNumber = 1e27;
interface IProps { export function Bladeburner(): React.ReactElement {
player: IPlayer; const bladeburner = Player.bladeburner;
}
export function Bladeburner(props: IProps): React.ReactElement {
const bladeburner = props.player.bladeburner;
if (bladeburner === null) return <></>; if (bladeburner === null) return <></>;
function modifyBladeburnerRank(modify: number): (x: number) => void { function modifyBladeburnerRank(modify: number): (x: number) => void {
return function (rank: number): void { return function (rank: number): void {
if (!bladeburner) return; if (!bladeburner) return;
bladeburner.changeRank(props.player, rank * modify); bladeburner.changeRank(Player, rank * modify);
}; };
} }
@ -34,7 +30,7 @@ export function Bladeburner(props: IProps): React.ReactElement {
function addTonsBladeburnerRank(): void { function addTonsBladeburnerRank(): void {
if (!bladeburner) return; if (!bladeburner) return;
bladeburner.changeRank(props.player, bigNumber); bladeburner.changeRank(Player, bigNumber);
} }
function modifyBladeburnerCycles(modify: number): (x: number) => void { function modifyBladeburnerCycles(modify: number): (x: number) => void {

@ -8,58 +8,54 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Adjuster } from "./Adjuster"; import { Adjuster } from "./Adjuster";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
const bigNumber = 1e27; const bigNumber = 1e27;
interface IProps { export function Corporation(): React.ReactElement {
player: IPlayer;
}
export function Corporation(props: IProps): React.ReactElement {
function addTonsCorporationFunds(): void { function addTonsCorporationFunds(): void {
if (props.player.corporation) { if (Player.corporation) {
props.player.corporation.funds = props.player.corporation.funds + bigNumber; Player.corporation.funds = Player.corporation.funds + bigNumber;
} }
} }
function modifyCorporationFunds(modify: number): (x: number) => void { function modifyCorporationFunds(modify: number): (x: number) => void {
return function (funds: number): void { return function (funds: number): void {
if (props.player.corporation) { if (Player.corporation) {
props.player.corporation.funds += funds * modify; Player.corporation.funds += funds * modify;
} }
}; };
} }
function resetCorporationFunds(): void { function resetCorporationFunds(): void {
if (props.player.corporation) { if (Player.corporation) {
props.player.corporation.funds = props.player.corporation.funds - props.player.corporation.funds; Player.corporation.funds = Player.corporation.funds - Player.corporation.funds;
} }
} }
function addTonsCorporationCycles(): void { function addTonsCorporationCycles(): void {
if (props.player.corporation) { if (Player.corporation) {
props.player.corporation.storedCycles = bigNumber; Player.corporation.storedCycles = bigNumber;
} }
} }
function modifyCorporationCycles(modify: number): (x: number) => void { function modifyCorporationCycles(modify: number): (x: number) => void {
return function (cycles: number): void { return function (cycles: number): void {
if (props.player.corporation) { if (Player.corporation) {
props.player.corporation.storedCycles += cycles * modify; Player.corporation.storedCycles += cycles * modify;
} }
}; };
} }
function resetCorporationCycles(): void { function resetCorporationCycles(): void {
if (props.player.corporation) { if (Player.corporation) {
props.player.corporation.storedCycles = 0; Player.corporation.storedCycles = 0;
} }
} }
function finishCorporationProducts(): void { function finishCorporationProducts(): void {
if (!props.player.corporation) return; if (!Player.corporation) return;
props.player.corporation.divisions.forEach((div) => { Player.corporation.divisions.forEach((div) => {
Object.keys(div.products).forEach((prod) => { Object.keys(div.products).forEach((prod) => {
const product = div.products[prod]; const product = div.products[prod];
if (product === undefined) throw new Error("Impossible product undefined"); if (product === undefined) throw new Error("Impossible product undefined");
@ -69,8 +65,8 @@ export function Corporation(props: IProps): React.ReactElement {
} }
function addCorporationResearch(): void { function addCorporationResearch(): void {
if (!props.player.corporation) return; if (!Player.corporation) return;
props.player.corporation.divisions.forEach((div) => { Player.corporation.divisions.forEach((div) => {
div.sciResearch.qty += 1e10; div.sciResearch.qty += 1e10;
}); });
} }

@ -6,18 +6,12 @@ import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Adjuster } from "./Adjuster"; import { Adjuster } from "./Adjuster";
import { IEngine } from "../../IEngine";
// Update as additional BitNodes get implemented // Update as additional BitNodes get implemented
interface IProps { export function Entropy(): React.ReactElement {
player: IPlayer;
engine: IEngine;
}
export function Entropy(props: IProps): React.ReactElement {
return ( return (
<Accordion TransitionProps={{ unmountOnExit: true }}> <Accordion TransitionProps={{ unmountOnExit: true }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}> <AccordionSummary expandIcon={<ExpandMoreIcon />}>
@ -28,20 +22,20 @@ export function Entropy(props: IProps): React.ReactElement {
label="Set entropy" label="Set entropy"
placeholder="entropy" placeholder="entropy"
add={(num) => { add={(num) => {
props.player.entropy += num; Player.entropy += num;
props.player.applyEntropy(props.player.entropy); Player.applyEntropy(Player.entropy);
}} }}
subtract={(num) => { subtract={(num) => {
props.player.entropy -= num; Player.entropy -= num;
props.player.applyEntropy(props.player.entropy); Player.applyEntropy(Player.entropy);
}} }}
tons={() => { tons={() => {
props.player.entropy += 1e12; Player.entropy += 1e12;
props.player.applyEntropy(props.player.entropy); Player.applyEntropy(Player.entropy);
}} }}
reset={() => { reset={() => {
props.player.entropy = 0; Player.entropy = 0;
props.player.applyEntropy(props.player.entropy); Player.applyEntropy(Player.entropy);
}} }}
/> />
</AccordionDetails> </AccordionDetails>

@ -9,7 +9,7 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import { Adjuster } from "./Adjuster"; import { Adjuster } from "./Adjuster";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Factions as AllFaction } from "../../Faction/Factions"; import { Factions as AllFaction } from "../../Faction/Factions";
import FormControl from "@mui/material/FormControl"; import FormControl from "@mui/material/FormControl";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
@ -21,11 +21,7 @@ import { FactionNames } from "../../Faction/data/FactionNames";
const bigNumber = 1e12; const bigNumber = 1e12;
interface IProps { export function Factions(): React.ReactElement {
player: IPlayer;
}
export function Factions(props: IProps): React.ReactElement {
const [faction, setFaction] = useState(FactionNames.Illuminati as string); const [faction, setFaction] = useState(FactionNames.Illuminati as string);
function setFactionDropdown(event: SelectChangeEvent<string>): void { function setFactionDropdown(event: SelectChangeEvent<string>): void {
@ -33,11 +29,11 @@ export function Factions(props: IProps): React.ReactElement {
} }
function receiveInvite(): void { function receiveInvite(): void {
props.player.receiveInvite(faction); Player.receiveInvite(faction);
} }
function receiveAllInvites(): void { function receiveAllInvites(): void {
Object.values(FactionNames).forEach((faction) => props.player.receiveInvite(faction)); Object.values(FactionNames).forEach((faction) => Player.receiveInvite(faction));
} }
function modifyFactionRep(modifier: number): (x: number) => void { function modifyFactionRep(modifier: number): (x: number) => void {

@ -7,32 +7,28 @@ import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { Adjuster } from "./Adjuster"; import { Adjuster } from "./Adjuster";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
const bigNumber = 1e27; const bigNumber = 1e27;
interface IProps { export function Gang(): React.ReactElement {
player: IPlayer;
}
export function Gang(props: IProps): React.ReactElement {
function addTonsGangCycles(): void { function addTonsGangCycles(): void {
if (props.player.gang) { if (Player.gang) {
props.player.gang.storedCycles = bigNumber; Player.gang.storedCycles = bigNumber;
} }
} }
function modifyGangCycles(modify: number): (x: number) => void { function modifyGangCycles(modify: number): (x: number) => void {
return function (cycles: number): void { return function (cycles: number): void {
if (props.player.gang) { if (Player.gang) {
props.player.gang.storedCycles += cycles * modify; Player.gang.storedCycles += cycles * modify;
} }
}; };
} }
function resetGangCycles(): void { function resetGangCycles(): void {
if (props.player.gang) { if (Player.gang) {
props.player.gang.storedCycles = 0; Player.gang.storedCycles = 0;
} }
} }

@ -8,61 +8,56 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { IRouter } from "../../ui/Router"; import { Router } from "../../ui/GameRoot";
import { MenuItem, SelectChangeEvent, TextField, Select } from "@mui/material"; import { MenuItem, SelectChangeEvent, TextField, Select } from "@mui/material";
import { Bladeburner } from "../../Bladeburner/Bladeburner"; import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { GangConstants } from "../../Gang/data/Constants"; import { GangConstants } from "../../Gang/data/Constants";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { checkForMessagesToSend } from "../../Message/MessageHelpers"; import { checkForMessagesToSend } from "../../Message/MessageHelpers";
interface IProps { export function General(): React.ReactElement {
player: IPlayer;
router: IRouter;
}
export function General(props: IProps): React.ReactElement {
const [error, setError] = useState(false); const [error, setError] = useState(false);
const [corporationName, setCorporationName] = useState(""); const [corporationName, setCorporationName] = useState("");
const [gangFaction, setGangFaction] = useState(""); const [gangFaction, setGangFaction] = useState("");
function addMoney(n: number) { function addMoney(n: number) {
return function () { return function () {
props.player.gainMoney(n, "other"); Player.gainMoney(n, "other");
}; };
} }
function upgradeRam(): void { function upgradeRam(): void {
props.player.getHomeComputer().maxRam *= 2; Player.getHomeComputer().maxRam *= 2;
} }
function quickB1tFlum3(): void { function quickB1tFlum3(): void {
props.router.toBitVerse(true, true); Router.toBitVerse(true, true);
} }
function b1tflum3(): void { function b1tflum3(): void {
props.router.toBitVerse(true, false); Router.toBitVerse(true, false);
} }
function quickHackW0r1dD43m0n(): void { function quickHackW0r1dD43m0n(): void {
props.router.toBitVerse(false, true); Router.toBitVerse(false, true);
} }
function hackW0r1dD43m0n(): void { function hackW0r1dD43m0n(): void {
props.router.toBitVerse(false, false); Router.toBitVerse(false, false);
} }
function createCorporation(): void { function createCorporation(): void {
props.player.startCorporation(corporationName); Player.startCorporation(corporationName);
} }
function joinBladeburner(): void { function joinBladeburner(): void {
props.player.bladeburner = new Bladeburner(props.player); Player.bladeburner = new Bladeburner();
} }
function startGang(): void { function startGang(): void {
const isHacking = gangFaction === FactionNames.NiteSec || gangFaction === FactionNames.TheBlackHand; const isHacking = gangFaction === FactionNames.NiteSec || gangFaction === FactionNames.TheBlackHand;
props.player.startGang(gangFaction, isHacking); Player.startGang(gangFaction, isHacking);
} }
function setGangFactionDropdown(event: SelectChangeEvent<string>): void { function setGangFactionDropdown(event: SelectChangeEvent<string>): void {

@ -8,29 +8,25 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Programs as AllPrograms } from "../../Programs/Programs"; import { Programs as AllPrograms } from "../../Programs/Programs";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
interface IProps { export function Programs(): React.ReactElement {
player: IPlayer;
}
export function Programs(props: IProps): React.ReactElement {
const [program, setProgram] = useState("NUKE.exe"); const [program, setProgram] = useState("NUKE.exe");
function setProgramDropdown(event: SelectChangeEvent<string>): void { function setProgramDropdown(event: SelectChangeEvent<string>): void {
setProgram(event.target.value); setProgram(event.target.value);
} }
function addProgram(): void { function addProgram(): void {
if (!props.player.hasProgram(program)) { if (!Player.hasProgram(program)) {
props.player.getHomeComputer().programs.push(program); Player.getHomeComputer().programs.push(program);
} }
} }
function addAllPrograms(): void { function addAllPrograms(): void {
for (const i of Object.keys(AllPrograms)) { for (const i of Object.keys(AllPrograms)) {
if (!props.player.hasProgram(AllPrograms[i].name)) { if (!Player.hasProgram(AllPrograms[i].name)) {
props.player.getHomeComputer().programs.push(AllPrograms[i].name); Player.getHomeComputer().programs.push(AllPrograms[i].name);
} }
} }
} }

@ -7,41 +7,37 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Adjuster } from "./Adjuster"; import { Adjuster } from "./Adjuster";
interface IProps { export function Sleeves(): React.ReactElement {
player: IPlayer;
}
export function Sleeves(props: IProps): React.ReactElement {
function sleeveMaxAllShock(): void { function sleeveMaxAllShock(): void {
for (let i = 0; i < props.player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
props.player.sleeves[i].shock = 0; Player.sleeves[i].shock = 0;
} }
} }
function sleeveClearAllShock(): void { function sleeveClearAllShock(): void {
for (let i = 0; i < props.player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
props.player.sleeves[i].shock = 100; Player.sleeves[i].shock = 100;
} }
} }
function sleeveSyncMaxAll(): void { function sleeveSyncMaxAll(): void {
for (let i = 0; i < props.player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
props.player.sleeves[i].sync = 100; Player.sleeves[i].sync = 100;
} }
} }
function sleeveSyncClearAll(): void { function sleeveSyncClearAll(): void {
for (let i = 0; i < props.player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
props.player.sleeves[i].sync = 0; Player.sleeves[i].sync = 0;
} }
} }
function sleeveSetStoredCycles(cycles: number): void { function sleeveSetStoredCycles(cycles: number): void {
for (let i = 0; i < props.player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
props.player.sleeves[i].storedCycles = cycles; Player.sleeves[i].storedCycles = cycles;
} }
} }

@ -8,35 +8,31 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { PlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile"; import { PlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import ButtonGroup from "@mui/material/ButtonGroup"; import ButtonGroup from "@mui/material/ButtonGroup";
// Update as additional BitNodes get implemented // Update as additional BitNodes get implemented
const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; const validSFN = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
interface IProps { export function SourceFiles(): React.ReactElement {
player: IPlayer;
}
export function SourceFiles(props: IProps): React.ReactElement {
function setSF(sfN: number, sfLvl: number) { function setSF(sfN: number, sfLvl: number) {
return function () { return function () {
if (sfN === 9) { if (sfN === 9) {
props.player.hacknetNodes = []; Player.hacknetNodes = [];
} }
if (sfLvl === 0) { if (sfLvl === 0) {
props.player.sourceFiles = props.player.sourceFiles.filter((sf) => sf.n !== sfN); Player.sourceFiles = Player.sourceFiles.filter((sf) => sf.n !== sfN);
return; return;
} }
if (!props.player.sourceFiles.some((sf) => sf.n === sfN)) { if (!Player.sourceFiles.some((sf) => sf.n === sfN)) {
props.player.sourceFiles.push(new PlayerOwnedSourceFile(sfN, sfLvl)); Player.sourceFiles.push(new PlayerOwnedSourceFile(sfN, sfLvl));
return; return;
} }
for (let i = 0; i < props.player.sourceFiles.length; i++) { for (let i = 0; i < Player.sourceFiles.length; i++) {
if (props.player.sourceFiles[i].n === sfN) { if (Player.sourceFiles[i].n === sfN) {
props.player.sourceFiles[i].lvl = sfLvl; Player.sourceFiles[i].lvl = sfLvl;
} }
} }
}; };
@ -51,7 +47,7 @@ export function SourceFiles(props: IProps): React.ReactElement {
} }
function clearExploits(): void { function clearExploits(): void {
props.player.exploits = []; Player.exploits = [];
} }
return ( return (

@ -8,132 +8,128 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Adjuster } from "./Adjuster"; import { Adjuster } from "./Adjuster";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
const bigNumber = 1e27; const bigNumber = 1e27;
interface IProps { export function Stats(): React.ReactElement {
player: IPlayer;
}
export function Stats(props: IProps): React.ReactElement {
function modifyExp(stat: string, modifier: number) { function modifyExp(stat: string, modifier: number) {
return function (exp: number) { return function (exp: number) {
switch (stat) { switch (stat) {
case "hacking": case "hacking":
if (exp) { if (exp) {
props.player.gainHackingExp(exp * modifier); Player.gainHackingExp(exp * modifier);
} }
break; break;
case "strength": case "strength":
if (exp) { if (exp) {
props.player.gainStrengthExp(exp * modifier); Player.gainStrengthExp(exp * modifier);
} }
break; break;
case "defense": case "defense":
if (exp) { if (exp) {
props.player.gainDefenseExp(exp * modifier); Player.gainDefenseExp(exp * modifier);
} }
break; break;
case "dexterity": case "dexterity":
if (exp) { if (exp) {
props.player.gainDexterityExp(exp * modifier); Player.gainDexterityExp(exp * modifier);
} }
break; break;
case "agility": case "agility":
if (exp) { if (exp) {
props.player.gainAgilityExp(exp * modifier); Player.gainAgilityExp(exp * modifier);
} }
break; break;
case "charisma": case "charisma":
if (exp) { if (exp) {
props.player.gainCharismaExp(exp * modifier); Player.gainCharismaExp(exp * modifier);
} }
break; break;
case "intelligence": case "intelligence":
if (exp) { if (exp) {
props.player.gainIntelligenceExp(exp * modifier); Player.gainIntelligenceExp(exp * modifier);
} }
break; break;
} }
props.player.updateSkillLevels(); Player.updateSkillLevels();
}; };
} }
function modifyKarma(modifier: number) { function modifyKarma(modifier: number) {
return function (amt: number) { return function (amt: number) {
props.player.karma += amt * modifier; Player.karma += amt * modifier;
}; };
} }
function tonsOfExp(): void { function tonsOfExp(): void {
props.player.gainHackingExp(bigNumber); Player.gainHackingExp(bigNumber);
props.player.gainStrengthExp(bigNumber); Player.gainStrengthExp(bigNumber);
props.player.gainDefenseExp(bigNumber); Player.gainDefenseExp(bigNumber);
props.player.gainDexterityExp(bigNumber); Player.gainDexterityExp(bigNumber);
props.player.gainAgilityExp(bigNumber); Player.gainAgilityExp(bigNumber);
props.player.gainCharismaExp(bigNumber); Player.gainCharismaExp(bigNumber);
props.player.gainIntelligenceExp(bigNumber); Player.gainIntelligenceExp(bigNumber);
props.player.updateSkillLevels(); Player.updateSkillLevels();
} }
function resetAllExp(): void { function resetAllExp(): void {
props.player.exp.hacking = 0; Player.exp.hacking = 0;
props.player.exp.strength = 0; Player.exp.strength = 0;
props.player.exp.defense = 0; Player.exp.defense = 0;
props.player.exp.dexterity = 0; Player.exp.dexterity = 0;
props.player.exp.agility = 0; Player.exp.agility = 0;
props.player.exp.charisma = 0; Player.exp.charisma = 0;
props.player.exp.intelligence = 0; Player.exp.intelligence = 0;
props.player.updateSkillLevels(); Player.updateSkillLevels();
} }
function resetExperience(stat: string): () => void { function resetExperience(stat: string): () => void {
return function () { return function () {
switch (stat) { switch (stat) {
case "hacking": case "hacking":
props.player.exp.hacking = 0; Player.exp.hacking = 0;
break; break;
case "strength": case "strength":
props.player.exp.strength = 0; Player.exp.strength = 0;
break; break;
case "defense": case "defense":
props.player.exp.defense = 0; Player.exp.defense = 0;
break; break;
case "dexterity": case "dexterity":
props.player.exp.dexterity = 0; Player.exp.dexterity = 0;
break; break;
case "agility": case "agility":
props.player.exp.agility = 0; Player.exp.agility = 0;
break; break;
case "charisma": case "charisma":
props.player.exp.charisma = 0; Player.exp.charisma = 0;
break; break;
case "intelligence": case "intelligence":
props.player.exp.intelligence = 0; Player.exp.intelligence = 0;
break; break;
} }
props.player.updateSkillLevels(); Player.updateSkillLevels();
}; };
} }
function resetKarma(): () => void { function resetKarma(): () => void {
return function () { return function () {
props.player.karma = 0; Player.karma = 0;
}; };
} }
function enableIntelligence(): void { function enableIntelligence(): void {
if (props.player.skills.intelligence === 0) { if (Player.skills.intelligence === 0) {
props.player.skills.intelligence = 1; Player.skills.intelligence = 1;
props.player.updateSkillLevels(); Player.updateSkillLevels();
} }
} }
function disableIntelligence(): void { function disableIntelligence(): void {
props.player.exp.intelligence = 0; Player.exp.intelligence = 0;
props.player.skills.intelligence = 0; Player.skills.intelligence = 0;
props.player.updateSkillLevels(); Player.updateSkillLevels();
} }
return ( return (

@ -7,22 +7,17 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { saveObject } from "../../SaveObject"; import { saveObject } from "../../SaveObject";
import { IEngine } from "../../IEngine"; import { Engine } from "../../engine";
// Update as additional BitNodes get implemented // Update as additional BitNodes get implemented
interface IProps { export function TimeSkip(): React.ReactElement {
player: IPlayer;
engine: IEngine;
}
export function TimeSkip(props: IProps): React.ReactElement {
function timeskip(time: number) { function timeskip(time: number) {
return () => { return () => {
props.player.lastUpdate -= time; Player.lastUpdate -= time;
props.engine._lastUpdate -= time; Engine._lastUpdate -= time;
saveObject.saveGame(); saveObject.saveGame();
setTimeout(() => location.reload(), 1000); setTimeout(() => location.reload(), 1000);
}; };

@ -1,5 +1,5 @@
import { Factions } from "./Faction/Factions"; import { Factions } from "./Faction/Factions";
import { IPlayer } from "./PersonObjects/IPlayer"; import { Player } from "./Player";
export let LastExportBonus = 0; export let LastExportBonus = 0;
@ -9,9 +9,9 @@ export function canGetBonus(): boolean {
return now - LastExportBonus > bonusTimer; return now - LastExportBonus > bonusTimer;
} }
export function onExport(p: IPlayer): void { export function onExport(): void {
if (!canGetBonus()) return; if (!canGetBonus()) return;
for (const facName of p.factions) { for (const facName of Player.factions) {
Factions[facName].favor++; Factions[facName].favor++;
} }
LastExportBonus = new Date().getTime(); LastExportBonus = new Date().getTime();

@ -7,7 +7,6 @@ import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Faction } from "./Faction"; import { Faction } from "./Faction";
import { Factions } from "./Factions"; import { Factions } from "./Factions";
import { Player } from "../Player"; import { Player } from "../Player";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { import {
getHackingWorkRepGain, getHackingWorkRepGain,
@ -59,7 +58,7 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string { export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
const hasPrereqs = hasAugmentationPrereqs(aug); const hasPrereqs = hasAugmentationPrereqs(aug);
const augCosts = aug.getCost(Player); const augCosts = aug.getCost();
if (!hasPrereqs) { if (!hasPrereqs) {
const txt = `You must first purchase or install ${aug.prereqs const txt = `You must first purchase or install ${aug.prereqs
.filter((req) => !Player.hasAugmentation(req)) .filter((req) => !Player.hasAugmentation(req))
@ -84,7 +83,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
} else if (augCosts.moneyCost === 0 || Player.money >= augCosts.moneyCost) { } else if (augCosts.moneyCost === 0 || Player.money >= augCosts.moneyCost) {
const queuedAugmentation = new PlayerOwnedAugmentation(aug.name); const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
if (aug.name == AugmentationNames.NeuroFluxGovernor) { if (aug.name == AugmentationNames.NeuroFluxGovernor) {
queuedAugmentation.level = aug.getLevel(Player); queuedAugmentation.level = aug.getLevel();
} }
Player.queuedAugmentations.push(queuedAugmentation); Player.queuedAugmentations.push(queuedAugmentation);
@ -134,20 +133,20 @@ export function processPassiveFactionRepGain(numCycles: number): void {
} }
} }
export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Faction): string[] => { export const getFactionAugmentationsFiltered = (faction: Faction): string[] => {
// If player has a gang with this faction, return (almost) all augmentations // If player has a gang with this faction, return (almost) all augmentations
if (player.hasGangWith(faction.name)) { if (Player.hasGangWith(faction.name)) {
let augs = Object.values(StaticAugmentations); let augs = Object.values(StaticAugmentations);
// Remove special augs // Remove special augs
augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationNames.CongruityImplant); augs = augs.filter((a) => !a.isSpecial && a.name !== AugmentationNames.CongruityImplant);
if (player.bitNodeN === 2) { if (Player.bitNodeN === 2) {
// TRP is not available outside of BN2 for Gangs // TRP is not available outside of BN2 for Gangs
augs.push(StaticAugmentations[AugmentationNames.TheRedPill]); augs.push(StaticAugmentations[AugmentationNames.TheRedPill]);
} }
const rng = SFC32RNG(`BN${player.bitNodeN}.${player.sourceFileLvl(player.bitNodeN)}`); const rng = SFC32RNG(`BN${Player.bitNodeN}.${Player.sourceFileLvl(Player.bitNodeN)}`);
// Remove faction-unique augs that don't belong to this faction // Remove faction-unique augs that don't belong to this faction
const uniqueFilter = (a: Augmentation): boolean => { const uniqueFilter = (a: Augmentation): boolean => {
// Keep all the non-unique one // Keep all the non-unique one

@ -1,6 +1,6 @@
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPerson } from "../../PersonObjects/IPerson";
export function repFromDonation(amt: number, player: IPlayer): number { export function repFromDonation(amt: number, person: IPerson): number {
return (amt / CONSTANTS.DonateMoneyToRepDivisor) * player.mults.faction_rep; return (amt / CONSTANTS.DonateMoneyToRepDivisor) * person.mults.faction_rep;
} }

@ -10,7 +10,7 @@ import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations"; import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums"; import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Favor } from "../../ui/React/Favor"; import { Favor } from "../../ui/React/Favor";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
@ -24,8 +24,6 @@ type IProps = {
}; };
export function AugmentationsPage(props: IProps): React.ReactElement { export function AugmentationsPage(props: IProps): React.ReactElement {
const player = use.Player();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -33,7 +31,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
} }
function getAugs(): string[] { function getAugs(): string[] {
return getFactionAugmentationsFiltered(player, props.faction); return getFactionAugmentationsFiltered(props.faction);
} }
function getAugsSorted(): string[] { function getAugsSorted(): string[] {
@ -61,7 +59,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost(player).moneyCost - aug2.getCost(player).moneyCost; return aug1.getCost().moneyCost - aug2.getCost().moneyCost;
}); });
return augs; return augs;
@ -71,11 +69,11 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
const augs = getAugs(); const augs = getAugs();
function canBuy(augName: string): boolean { function canBuy(augName: string): boolean {
const aug = StaticAugmentations[augName]; const aug = StaticAugmentations[augName];
const augCosts = aug.getCost(player); const augCosts = aug.getCost();
const repCost = augCosts.repCost; const repCost = augCosts.repCost;
const hasReq = props.faction.playerReputation >= repCost; const hasReq = props.faction.playerReputation >= repCost;
const hasRep = hasAugmentationPrereqs(aug); const hasRep = hasAugmentationPrereqs(aug);
const hasCost = augCosts.moneyCost !== 0 && player.money > augCosts.moneyCost; const hasCost = augCosts.moneyCost !== 0 && Player.money > augCosts.moneyCost;
return hasCost && hasReq && hasRep; return hasCost && hasReq && hasRep;
} }
const buy = augs.filter(canBuy).sort((augName1, augName2) => { const buy = augs.filter(canBuy).sort((augName1, augName2) => {
@ -85,7 +83,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost(player).moneyCost - aug2.getCost(player).moneyCost; return aug1.getCost().moneyCost - aug2.getCost().moneyCost;
}); });
const cantBuy = augs const cantBuy = augs
.filter((aug) => !canBuy(aug)) .filter((aug) => !canBuy(aug))
@ -95,7 +93,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
if (aug1 == null || aug2 == null) { if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost(player).repCost - aug2.getCost(player).repCost; return aug1.getCost().repCost - aug2.getCost().repCost;
}); });
return buy.concat(cantBuy); return buy.concat(cantBuy);
@ -109,7 +107,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
if (aug1 == null || aug2 == null) { if (aug1 == null || aug2 == null) {
throw new Error("Invalid Augmentation Names"); throw new Error("Invalid Augmentation Names");
} }
return aug1.getCost(player).repCost - aug2.getCost(player).repCost; return aug1.getCost().repCost - aug2.getCost().repCost;
}); });
return augs; return augs;
@ -128,7 +126,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
const purchasable = augs.filter( const purchasable = augs.filter(
(aug: string) => (aug: string) =>
aug === AugmentationNames.NeuroFluxGovernor || aug === AugmentationNames.NeuroFluxGovernor ||
(!player.augmentations.some((a) => a.name === aug) && !player.queuedAugmentations.some((a) => a.name === aug)), (!Player.augmentations.some((a) => a.name === aug) && !Player.queuedAugmentations.some((a) => a.name === aug)),
); );
const owned = augs.filter((aug: string) => !purchasable.includes(aug)); const owned = augs.filter((aug: string) => !purchasable.includes(aug));
@ -195,16 +193,15 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
<PurchasableAugmentations <PurchasableAugmentations
augNames={purchasable} augNames={purchasable}
ownedAugNames={owned} ownedAugNames={owned}
player={player} canPurchase={(aug) => {
canPurchase={(player, aug) => { const costs = aug.getCost();
const costs = aug.getCost(player);
return ( return (
hasAugmentationPrereqs(aug) && hasAugmentationPrereqs(aug) &&
props.faction.playerReputation >= costs.repCost && props.faction.playerReputation >= costs.repCost &&
(costs.moneyCost === 0 || player.money > costs.moneyCost) (costs.moneyCost === 0 || Player.money > costs.moneyCost)
); );
}} }}
purchaseAugmentation={(player, aug, showModal) => { purchaseAugmentation={(aug, showModal) => {
if (!Settings.SuppressBuyAugmentationConfirmation) { if (!Settings.SuppressBuyAugmentationConfirmation) {
showModal(true); showModal(true);
} else { } else {

@ -5,7 +5,7 @@ import React, { useState } from "react";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { Faction } from "../Faction"; import { Faction } from "../Faction";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { repFromDonation } from "../formulas/donation"; import { repFromDonation } from "../formulas/donation";
import { Favor } from "../../ui/React/Favor"; import { Favor } from "../../ui/React/Favor";
@ -24,7 +24,6 @@ type IProps = {
faction: Faction; faction: Faction;
disabled: boolean; disabled: boolean;
favorToDonate: number; favorToDonate: number;
p: IPlayer;
rerender: () => void; rerender: () => void;
}; };
@ -35,7 +34,7 @@ export function DonateOption(props: IProps): React.ReactElement {
function canDonate(): boolean { function canDonate(): boolean {
if (isNaN(donateAmt)) return false; if (isNaN(donateAmt)) return false;
if (isNaN(donateAmt) || donateAmt <= 0) return false; if (isNaN(donateAmt) || donateAmt <= 0) return false;
if (props.p.money < donateAmt) return false; if (Player.money < donateAmt) return false;
return true; return true;
} }
@ -44,8 +43,8 @@ export function DonateOption(props: IProps): React.ReactElement {
const amt = donateAmt; const amt = donateAmt;
if (isNaN(amt)) return; if (isNaN(amt)) return;
if (!canDonate()) return; if (!canDonate()) return;
props.p.loseMoney(amt, "other"); Player.loseMoney(amt, "other");
const repGain = repFromDonation(amt, props.p); const repGain = repFromDonation(amt, Player);
props.faction.playerReputation += repGain; props.faction.playerReputation += repGain;
dialogBoxCreate( dialogBoxCreate(
<> <>
@ -58,12 +57,12 @@ export function DonateOption(props: IProps): React.ReactElement {
function Status(): React.ReactElement { function Status(): React.ReactElement {
if (isNaN(donateAmt)) return <></>; if (isNaN(donateAmt)) return <></>;
if (!canDonate()) { if (!canDonate()) {
if (props.p.money < donateAmt) return <Typography>Insufficient funds</Typography>; if (Player.money < donateAmt) return <Typography>Insufficient funds</Typography>;
return <Typography>Invalid donate amount entered!</Typography>; return <Typography>Invalid donate amount entered!</Typography>;
} }
return ( return (
<Typography> <Typography>
This donation will result in <Reputation reputation={repFromDonation(donateAmt, props.p)} /> reputation gain This donation will result in <Reputation reputation={repFromDonation(donateAmt, Player)} /> reputation gain
</Typography> </Typography>
); );
} }

@ -15,7 +15,8 @@ import { CONSTANTS } from "../../Constants";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Faction } from "../Faction"; import { Faction } from "../Faction";
import { use } from "../../ui/Context"; import { Router } from "../../ui/GameRoot";
import { Player } from "../../Player";
import { Typography, Button } from "@mui/material"; import { Typography, Button } from "@mui/material";
import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot"; import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
@ -58,18 +59,16 @@ interface IMainProps {
} }
function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.ReactElement { function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [sleevesOpen, setSleevesOpen] = useState(false); const [sleevesOpen, setSleevesOpen] = useState(false);
const factionInfo = faction.getInfo(); const factionInfo = faction.getInfo();
function startWork(): void { function startWork(): void {
player.startFocusing(); Player.startFocusing();
router.toWork(); Router.toWork();
} }
function startFieldWork(faction: Faction): void { function startFieldWork(faction: Faction): void {
player.startWork( Player.startWork(
new FactionWork({ new FactionWork({
singularity: false, singularity: false,
faction: faction.name, faction: faction.name,
@ -80,7 +79,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
} }
function startHackingContracts(faction: Faction): void { function startHackingContracts(faction: Faction): void {
player.startWork( Player.startWork(
new FactionWork({ new FactionWork({
singularity: false, singularity: false,
faction: faction.name, faction: faction.name,
@ -91,7 +90,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
} }
function startSecurityWork(faction: Faction): void { function startSecurityWork(faction: Faction): void {
player.startWork( Player.startWork(
new FactionWork({ new FactionWork({
singularity: false, singularity: false,
faction: faction.name, faction: faction.name,
@ -103,18 +102,18 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
// We have a special flag for whether the player this faction is the player's // We have a special flag for whether the player this faction is the player's
// gang faction because if the player has a gang, they cannot do any other action // gang faction because if the player has a gang, they cannot do any other action
const isPlayersGang = player.inGang() && player.getGangName() === faction.name; const isPlayersGang = Player.inGang() && Player.getGangName() === faction.name;
// Flags for whether special options (gang, sleeve purchases, donate, etc.) // Flags for whether special options (gang, sleeve purchases, donate, etc.)
// should be shown // should be shown
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
const canDonate = faction.favor >= favorToDonate; const canDonate = faction.favor >= favorToDonate;
const canPurchaseSleeves = faction.name === FactionNames.TheCovenant && player.bitNodeN === 10; const canPurchaseSleeves = faction.name === FactionNames.TheCovenant && Player.bitNodeN === 10;
return ( return (
<> <>
<Button onClick={() => router.toFactions()}>Back</Button> <Button onClick={() => Router.toFactions()}>Back</Button>
<Typography variant="h4" color="primary"> <Typography variant="h4" color="primary">
{faction.name} {faction.name}
</Typography> </Typography>
@ -134,13 +133,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
<Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={() => startSecurityWork(faction)} /> <Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={() => startSecurityWork(faction)} />
)} )}
{!isPlayersGang && factionInfo.offersWork() && ( {!isPlayersGang && factionInfo.offersWork() && (
<DonateOption <DonateOption faction={faction} rerender={rerender} favorToDonate={favorToDonate} disabled={!canDonate} />
faction={faction}
p={player}
rerender={rerender}
favorToDonate={favorToDonate}
disabled={!canDonate}
/>
)} )}
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={onAugmentations} /> <Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={onAugmentations} />
{canPurchaseSleeves && ( {canPurchaseSleeves && (
@ -159,8 +152,6 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
export function FactionRoot(props: IProps): React.ReactElement { export function FactionRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
const player = use.Player();
const router = use.Router();
const [purchasingAugs, setPurchasingAugs] = useState(props.augPage); const [purchasingAugs, setPurchasingAugs] = useState(props.augPage);
function rerender(): void { function rerender(): void {
@ -174,13 +165,13 @@ export function FactionRoot(props: IProps): React.ReactElement {
const faction = props.faction; const faction = props.faction;
if (player && !player.factions.includes(faction.name)) { if (!Player.factions.includes(faction.name)) {
return ( return (
<> <>
<Typography variant="h4" color="primary"> <Typography variant="h4" color="primary">
You have not joined {faction.name} yet! You have not joined {faction.name} yet!
</Typography> </Typography>
<Button onClick={() => router.toFactions()}>Back to Factions</Button> <Button onClick={() => Router.toFactions()}>Back to Factions</Button>
</> </>
); );
} }

@ -1,10 +1,10 @@
import { Explore, Info, LastPage, LocalPolice, NewReleases, Report, SportsMma } from "@mui/icons-material"; import { Explore, Info, LastPage, LocalPolice, NewReleases, Report, SportsMma } from "@mui/icons-material";
import { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material"; import { Box, Button, Container, Paper, Tooltip, Typography, useTheme } from "@mui/material";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { IRouter } from "../../ui/Router"; import { Router } from "../../ui/GameRoot";
import { FactionNames } from "../data/FactionNames"; import { FactionNames } from "../data/FactionNames";
import { Faction } from "../Faction"; import { Faction } from "../Faction";
import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers"; import { getFactionAugmentationsFiltered, joinFaction } from "../FactionHelpers";
@ -12,10 +12,10 @@ import { Factions } from "../Factions";
export const InvitationsSeen: string[] = []; export const InvitationsSeen: string[] = [];
const getAugsLeft = (faction: Faction, player: IPlayer): number => { const getAugsLeft = (faction: Faction): number => {
const augs = getFactionAugmentationsFiltered(player, faction); const augs = getFactionAugmentationsFiltered(faction);
return augs.filter((augmentation: string) => !player.hasAugmentation(augmentation)).length; return augs.filter((augmentation: string) => !Player.hasAugmentation(augmentation)).length;
}; };
interface IWorkTypeProps { interface IWorkTypeProps {
@ -50,8 +50,6 @@ const WorkTypesOffered = (props: IWorkTypeProps): React.ReactElement => {
}; };
interface IFactionProps { interface IFactionProps {
player: IPlayer;
router: IRouter;
faction: Faction; faction: Faction;
joined: boolean; joined: boolean;
@ -63,11 +61,11 @@ const FactionElement = (props: IFactionProps): React.ReactElement => {
const facInfo = props.faction.getInfo(); const facInfo = props.faction.getInfo();
function openFaction(faction: Faction): void { function openFaction(faction: Faction): void {
props.router.toFaction(faction); Router.toFaction(faction);
} }
function openFactionAugPage(faction: Faction): void { function openFactionAugPage(faction: Faction): void {
props.router.toFaction(faction, true); Router.toFaction(faction, true);
} }
function acceptInvitation(event: React.MouseEvent<HTMLButtonElement, MouseEvent>, faction: string): void { function acceptInvitation(event: React.MouseEvent<HTMLButtonElement, MouseEvent>, faction: string): void {
@ -124,7 +122,7 @@ const FactionElement = (props: IFactionProps): React.ReactElement => {
</span> </span>
<span style={{ display: "flex", alignItems: "center" }}> <span style={{ display: "flex", alignItems: "center" }}>
{props.player.hasGangWith(props.faction.name) && ( {Player.hasGangWith(props.faction.name) && (
<Tooltip title="You have a gang with this Faction"> <Tooltip title="You have a gang with this Faction">
<SportsMma sx={{ color: Settings.theme.hp, ml: 1 }} /> <SportsMma sx={{ color: Settings.theme.hp, ml: 1 }} />
</Tooltip> </Tooltip>
@ -157,11 +155,11 @@ const FactionElement = (props: IFactionProps): React.ReactElement => {
</Typography> </Typography>
<span style={{ display: "flex", alignItems: "center" }}> <span style={{ display: "flex", alignItems: "center" }}>
{!props.player.hasGangWith(props.faction.name) && <WorkTypesOffered faction={props.faction} />} {!Player.hasGangWith(props.faction.name) && <WorkTypesOffered faction={props.faction} />}
{props.joined && ( {props.joined && (
<Typography variant="body2" sx={{ display: "flex" }}> <Typography variant="body2" sx={{ display: "flex" }}>
{getAugsLeft(props.faction, props.player)} Augmentations left {getAugsLeft(props.faction)} Augmentations left
</Typography> </Typography>
)} )}
</span> </span>
@ -182,12 +180,7 @@ const FactionElement = (props: IFactionProps): React.ReactElement => {
); );
}; };
interface IProps { export function FactionsRoot(): React.ReactElement {
player: IPlayer;
router: IRouter;
}
export function FactionsRoot(props: IProps): React.ReactElement {
const theme = useTheme(); const theme = useTheme();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -199,16 +192,16 @@ export function FactionsRoot(props: IProps): React.ReactElement {
}, []); }, []);
useEffect(() => { useEffect(() => {
props.player.factionInvitations.forEach((faction) => { Player.factionInvitations.forEach((faction) => {
if (InvitationsSeen.includes(faction)) return; if (InvitationsSeen.includes(faction)) return;
InvitationsSeen.push(faction); InvitationsSeen.push(faction);
}); });
}, []); }, []);
const allFactions = Object.values(FactionNames).map((faction) => faction as string); const allFactions = Object.values(FactionNames).map((faction) => faction as string);
const allJoinedFactions = [...props.player.factions]; const allJoinedFactions = [...Player.factions];
allJoinedFactions.sort((a, b) => allFactions.indexOf(a) - allFactions.indexOf(b)); allJoinedFactions.sort((a, b) => allFactions.indexOf(a) - allFactions.indexOf(b));
const invitations = props.player.factionInvitations; const invitations = Player.factionInvitations;
return ( return (
<Container disableGutters maxWidth="lg" sx={{ mx: 0, mb: 10 }}> <Container disableGutters maxWidth="lg" sx={{ mx: 0, mb: 10 }}>
@ -249,16 +242,7 @@ export function FactionsRoot(props: IProps): React.ReactElement {
<Box> <Box>
{invitations.map((facName) => { {invitations.map((facName) => {
if (!Factions.hasOwnProperty(facName)) return null; if (!Factions.hasOwnProperty(facName)) return null;
return ( return <FactionElement key={facName} faction={Factions[facName]} joined={false} rerender={rerender} />;
<FactionElement
key={facName}
faction={Factions[facName]}
player={props.player}
router={props.router}
joined={false}
rerender={rerender}
/>
);
})} })}
</Box> </Box>
</span> </span>
@ -272,16 +256,7 @@ export function FactionsRoot(props: IProps): React.ReactElement {
{allJoinedFactions.length > 0 ? ( {allJoinedFactions.length > 0 ? (
allJoinedFactions.map((facName) => { allJoinedFactions.map((facName) => {
if (!Factions.hasOwnProperty(facName)) return null; if (!Factions.hasOwnProperty(facName)) return null;
return ( return <FactionElement key={facName} faction={Factions[facName]} joined={true} rerender={rerender} />;
<FactionElement
key={facName}
faction={Factions[facName]}
player={props.player}
router={props.router}
joined={true}
rerender={rerender}
/>
);
}) })
) : ( ) : (
<Typography>You have not yet joined any Factions.</Typography> <Typography>You have not yet joined any Factions.</Typography>

@ -1,7 +1,5 @@
import { Box, Container, Typography } from "@mui/material"; import { Box, Container, Typography } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router";
import { GameOptionsTab } from "../GameOptionsTab"; import { GameOptionsTab } from "../GameOptionsTab";
import { GameOptionsSidebar } from "./GameOptionsSidebar"; import { GameOptionsSidebar } from "./GameOptionsSidebar";
import { GameplayPage } from "./GameplayPage"; import { GameplayPage } from "./GameplayPage";
@ -11,8 +9,6 @@ import { RemoteAPIPage } from "./RemoteAPIPage";
import { SystemPage } from "./SystemPage"; import { SystemPage } from "./SystemPage";
interface IProps { interface IProps {
player: IPlayer;
router: IRouter;
save: () => void; save: () => void;
export: () => void; export: () => void;
forceKill: () => void; forceKill: () => void;
@ -29,8 +25,6 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
<GameOptionsSidebar <GameOptionsSidebar
tab={currentTab} tab={currentTab}
setTab={setCurrentTab} setTab={setCurrentTab}
player={props.player}
router={props.router}
save={props.save} save={props.save}
export={props.export} export={props.export}
forceKill={props.forceKill} forceKill={props.forceKill}

@ -13,7 +13,6 @@ import {
import { Box, Button, List, ListItemButton, Paper, Tooltip, Typography } from "@mui/material"; import { Box, Button, List, ListItemButton, Paper, Tooltip, Typography } from "@mui/material";
import { default as React, useRef, useState } from "react"; import { default as React, useRef, useState } from "react";
import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal"; import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { ImportData, saveObject } from "../../SaveObject"; import { ImportData, saveObject } from "../../SaveObject";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { StyleEditorButton } from "../../Themes/ui/StyleEditorButton"; import { StyleEditorButton } from "../../Themes/ui/StyleEditorButton";
@ -22,15 +21,13 @@ import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
import { DeleteGameButton } from "../../ui/React/DeleteGameButton"; import { DeleteGameButton } from "../../ui/React/DeleteGameButton";
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar"; import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
import { SoftResetButton } from "../../ui/React/SoftResetButton"; import { SoftResetButton } from "../../ui/React/SoftResetButton";
import { IRouter } from "../../ui/Router"; import { Router } from "../../ui/GameRoot";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { GameOptionsTab } from "../GameOptionsTab"; import { GameOptionsTab } from "../GameOptionsTab";
interface IProps { interface IProps {
tab: GameOptionsTab; tab: GameOptionsTab;
setTab: (tab: GameOptionsTab) => void; setTab: (tab: GameOptionsTab) => void;
player: IPlayer;
router: IRouter;
save: () => void; save: () => void;
export: () => void; export: () => void;
forceKill: () => void; forceKill: () => void;
@ -94,7 +91,7 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
function compareSaveGame(): void { function compareSaveGame(): void {
if (!importData) return; if (!importData) return;
props.router.toImportSave(importData.base64); Router.toImportSave(importData.base64);
setImportSaveOpen(false); setImportSaveOpen(false);
setImportData(null); setImportData(null);
} }
@ -219,12 +216,12 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title="Head to the theme browser to see a collection of prebuilt themes."> <Tooltip title="Head to the theme browser to see a collection of prebuilt themes.">
<Button startIcon={<Palette />} onClick={() => props.router.toThemeBrowser()} sx={{ gridArea: "browse" }}> <Button startIcon={<Palette />} onClick={() => Router.toThemeBrowser()} sx={{ gridArea: "browse" }}>
Theme Browser Theme Browser
</Button> </Button>
</Tooltip> </Tooltip>
<Box sx={{ gridArea: "theme", "& .MuiButton-root": { height: "100%", width: "100%" } }}> <Box sx={{ gridArea: "theme", "& .MuiButton-root": { height: "100%", width: "100%" } }}>
<ThemeEditorButton router={props.router} /> <ThemeEditorButton />
</Box> </Box>
<Box sx={{ gridArea: "style", "& .MuiButton-root": { height: "100%", width: "100%" } }}> <Box sx={{ gridArea: "style", "& .MuiButton-root": { height: "100%", width: "100%" } }}>
<StyleEditorButton /> <StyleEditorButton />

@ -1,21 +1,21 @@
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
import { IPlayer } from "./PersonObjects/IPlayer"; import { IPerson } from "./PersonObjects/IPerson";
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence"; import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
import { Server } from "./Server/Server"; import { Server } from "./Server/Server";
/** /**
* Returns the chance the player has to successfully hack a server * Returns the chance the person has to successfully hack a server
*/ */
export function calculateHackingChance(server: Server, player: IPlayer): number { export function calculateHackingChance(server: Server, person: IPerson): number {
const hackFactor = 1.75; const hackFactor = 1.75;
const difficultyMult = (100 - server.hackDifficulty) / 100; const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = hackFactor * player.skills.hacking; const skillMult = hackFactor * person.skills.hacking;
const skillChance = (skillMult - server.requiredHackingSkill) / skillMult; const skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
const chance = const chance =
skillChance * skillChance *
difficultyMult * difficultyMult *
player.mults.hacking_chance * person.mults.hacking_chance *
calculateIntelligenceBonus(player.skills.intelligence, 1); calculateIntelligenceBonus(person.skills.intelligence, 1);
if (chance > 1) { if (chance > 1) {
return 1; return 1;
} }
@ -27,10 +27,10 @@ export function calculateHackingChance(server: Server, player: IPlayer): number
} }
/** /**
* Returns the amount of hacking experience the player will gain upon * Returns the amount of hacking experience the person will gain upon
* successfully hacking a server * successfully hacking a server
*/ */
export function calculateHackingExpGain(server: Server, player: IPlayer): number { export function calculateHackingExpGain(server: Server, person: IPerson): number {
const baseExpGain = 3; const baseExpGain = 3;
const diffFactor = 0.3; const diffFactor = 0.3;
if (server.baseDifficulty == null) { if (server.baseDifficulty == null) {
@ -39,21 +39,21 @@ export function calculateHackingExpGain(server: Server, player: IPlayer): number
let expGain = baseExpGain; let expGain = baseExpGain;
expGain += server.baseDifficulty * diffFactor; expGain += server.baseDifficulty * diffFactor;
return expGain * player.mults.hacking_exp * BitNodeMultipliers.HackExpGain; return expGain * person.mults.hacking_exp * BitNodeMultipliers.HackExpGain;
} }
/** /**
* Returns the percentage of money that will be stolen from a server if * Returns the percentage of money that will be stolen from a server if
* it is successfully hacked (returns the decimal form, not the actual percent value) * it is successfully hacked (returns the decimal form, not the actual percent value)
*/ */
export function calculatePercentMoneyHacked(server: Server, player: IPlayer): number { export function calculatePercentMoneyHacked(server: Server, person: IPerson): number {
// Adjust if needed for balancing. This is the divisor for the final calculation // Adjust if needed for balancing. This is the divisor for the final calculation
const balanceFactor = 240; const balanceFactor = 240;
const difficultyMult = (100 - server.hackDifficulty) / 100; const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = (player.skills.hacking - (server.requiredHackingSkill - 1)) / player.skills.hacking; const skillMult = (person.skills.hacking - (server.requiredHackingSkill - 1)) / person.skills.hacking;
const percentMoneyHacked = const percentMoneyHacked =
(difficultyMult * skillMult * player.mults.hacking_money * BitNodeMultipliers.ScriptHackMoney) / balanceFactor; (difficultyMult * skillMult * person.mults.hacking_money * BitNodeMultipliers.ScriptHackMoney) / balanceFactor;
if (percentMoneyHacked < 0) { if (percentMoneyHacked < 0) {
return 0; return 0;
} }
@ -67,7 +67,7 @@ export function calculatePercentMoneyHacked(server: Server, player: IPlayer): nu
/** /**
* Returns time it takes to complete a hack on a server, in seconds * Returns time it takes to complete a hack on a server, in seconds
*/ */
export function calculateHackingTime(server: Server, player: IPlayer): number { export function calculateHackingTime(server: Server, person: IPerson): number {
const difficultyMult = server.requiredHackingSkill * server.hackDifficulty; const difficultyMult = server.requiredHackingSkill * server.hackDifficulty;
const baseDiff = 500; const baseDiff = 500;
@ -75,12 +75,12 @@ export function calculateHackingTime(server: Server, player: IPlayer): number {
const diffFactor = 2.5; const diffFactor = 2.5;
let skillFactor = diffFactor * difficultyMult + baseDiff; let skillFactor = diffFactor * difficultyMult + baseDiff;
// tslint:disable-next-line // tslint:disable-next-line
skillFactor /= player.skills.hacking + baseSkill; skillFactor /= person.skills.hacking + baseSkill;
const hackTimeMultiplier = 5; const hackTimeMultiplier = 5;
const hackingTime = const hackingTime =
(hackTimeMultiplier * skillFactor) / (hackTimeMultiplier * skillFactor) /
(player.mults.hacking_speed * calculateIntelligenceBonus(player.skills.intelligence, 1)); (person.mults.hacking_speed * calculateIntelligenceBonus(person.skills.intelligence, 1));
return hackingTime; return hackingTime;
} }
@ -88,17 +88,17 @@ export function calculateHackingTime(server: Server, player: IPlayer): number {
/** /**
* Returns time it takes to complete a grow operation on a server, in seconds * Returns time it takes to complete a grow operation on a server, in seconds
*/ */
export function calculateGrowTime(server: Server, player: IPlayer): number { export function calculateGrowTime(server: Server, person: IPerson): number {
const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2 const growTimeMultiplier = 3.2; // Relative to hacking time. 16/5 = 3.2
return growTimeMultiplier * calculateHackingTime(server, player); return growTimeMultiplier * calculateHackingTime(server, person);
} }
/** /**
* Returns time it takes to complete a weaken operation on a server, in seconds * Returns time it takes to complete a weaken operation on a server, in seconds
*/ */
export function calculateWeakenTime(server: Server, player: IPlayer): number { export function calculateWeakenTime(server: Server, person: IPerson): number {
const weakenTimeMultiplier = 4; // Relative to hacking time const weakenTimeMultiplier = 4; // Relative to hacking time
return weakenTimeMultiplier * calculateHackingTime(server, player); return weakenTimeMultiplier * calculateHackingTime(server, person);
} }

@ -6,7 +6,7 @@
*/ */
import { IReturnStatus } from "../types"; import { IReturnStatus } from "../types";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { Server } from "../Server/Server"; import { Server } from "../Server/Server";
function baseCheck(server: Server, fnName: string): IReturnStatus { function baseCheck(server: Server, fnName: string): IReturnStatus {
@ -29,14 +29,14 @@ function baseCheck(server: Server, fnName: string): IReturnStatus {
return { res: true }; return { res: true };
} }
export function netscriptCanHack(server: Server, p: IPlayer): IReturnStatus { export function netscriptCanHack(server: Server): IReturnStatus {
const initialCheck = baseCheck(server, "hack"); const initialCheck = baseCheck(server, "hack");
if (!initialCheck.res) { if (!initialCheck.res) {
return initialCheck; return initialCheck;
} }
const s = server; const s = server;
if (s.requiredHackingSkill > p.skills.hacking) { if (s.requiredHackingSkill > Player.skills.hacking) {
return { return {
res: false, res: false,
msg: `Cannot hack ${server.hostname} server because your hacking skill is not high enough`, msg: `Cannot hack ${server.hostname} server because your hacking skill is not high enough`,

@ -18,17 +18,17 @@ import { HashUpgrades } from "./HashUpgrades";
import { generateRandomContract } from "../CodingContractGenerator"; import { generateRandomContract } from "../CodingContractGenerator";
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../InteractiveTutorial"; import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../InteractiveTutorial";
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { Server } from "../Server/Server"; import { Server } from "../Server/Server";
// Returns a boolean indicating whether the player has Hacknet Servers // Returns a boolean indicating whether the player has Hacknet Servers
// (the upgraded form of Hacknet Nodes) // (the upgraded form of Hacknet Nodes)
export function hasHacknetServers(player: IPlayer): boolean { export function hasHacknetServers(): boolean {
return player.bitNodeN === 9 || player.sourceFileLvl(9) > 0; return Player.bitNodeN === 9 || Player.sourceFileLvl(9) > 0;
} }
export function purchaseHacknet(player: IPlayer): number { export function purchaseHacknet(): number {
/* INTERACTIVE TUTORIAL */ /* INTERACTIVE TUTORIAL */
if (ITutorial.isRunning) { if (ITutorial.isRunning) {
if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) { if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) {
@ -39,72 +39,68 @@ export function purchaseHacknet(player: IPlayer): number {
} }
/* END INTERACTIVE TUTORIAL */ /* END INTERACTIVE TUTORIAL */
const numOwned = player.hacknetNodes.length; const numOwned = Player.hacknetNodes.length;
if (hasHacknetServers(player)) { if (hasHacknetServers()) {
const cost = getCostOfNextHacknetServer(player); const cost = getCostOfNextHacknetServer();
if (isNaN(cost)) { if (isNaN(cost)) {
throw new Error(`Calculated cost of purchasing HacknetServer is NaN`); throw new Error(`Calculated cost of purchasing HacknetServer is NaN`);
} }
if (!player.canAfford(cost) || numOwned >= HacknetServerConstants.MaxServers) { if (!Player.canAfford(cost) || numOwned >= HacknetServerConstants.MaxServers) {
return -1; return -1;
} }
player.loseMoney(cost, "hacknet_expenses"); Player.loseMoney(cost, "hacknet_expenses");
player.createHacknetServer(); Player.createHacknetServer();
updateHashManagerCapacity(player); updateHashManagerCapacity();
return numOwned; return numOwned;
} else { } else {
const cost = getCostOfNextHacknetNode(player); const cost = getCostOfNextHacknetNode();
if (isNaN(cost)) { if (isNaN(cost)) {
throw new Error(`Calculated cost of purchasing HacknetNode is NaN`); throw new Error(`Calculated cost of purchasing HacknetNode is NaN`);
} }
if (!player.canAfford(cost)) { if (!Player.canAfford(cost)) {
return -1; return -1;
} }
// Auto generate a name for the Node // Auto generate a name for the Node
const name = "hacknet-node-" + numOwned; const name = "hacknet-node-" + numOwned;
const node = new HacknetNode(name, player.mults.hacknet_node_money); const node = new HacknetNode(name, Player.mults.hacknet_node_money);
player.loseMoney(cost, "hacknet_expenses"); Player.loseMoney(cost, "hacknet_expenses");
player.hacknetNodes.push(node); Player.hacknetNodes.push(node);
return numOwned; return numOwned;
} }
} }
export function hasMaxNumberHacknetServers(player: IPlayer): boolean { export function hasMaxNumberHacknetServers(): boolean {
return hasHacknetServers(player) && player.hacknetNodes.length >= HacknetServerConstants.MaxServers; return hasHacknetServers() && Player.hacknetNodes.length >= HacknetServerConstants.MaxServers;
} }
export function getCostOfNextHacknetNode(player: IPlayer): number { export function getCostOfNextHacknetNode(): number {
return calculateNodeCost(player.hacknetNodes.length + 1, player.mults.hacknet_node_purchase_cost); return calculateNodeCost(Player.hacknetNodes.length + 1, Player.mults.hacknet_node_purchase_cost);
} }
export function getCostOfNextHacknetServer(player: IPlayer): number { export function getCostOfNextHacknetServer(): number {
return calculateServerCost(player.hacknetNodes.length + 1, player.mults.hacknet_node_purchase_cost); return calculateServerCost(Player.hacknetNodes.length + 1, Player.mults.hacknet_node_purchase_cost);
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level
export function getMaxNumberLevelUpgrades( export function getMaxNumberLevelUpgrades(nodeObj: HacknetNode | HacknetServer, maxLevel: number): number {
player: IPlayer,
nodeObj: HacknetNode | HacknetServer,
maxLevel: number,
): number {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
} }
if (player.money < nodeObj.calculateLevelUpgradeCost(1, player.mults.hacknet_node_level_cost)) { if (Player.money < nodeObj.calculateLevelUpgradeCost(1, Player.mults.hacknet_node_level_cost)) {
return 0; return 0;
} }
let min = 1; let min = 1;
let max = maxLevel - 1; let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.level; const levelsToMax = maxLevel - nodeObj.level;
if (player.money > nodeObj.calculateLevelUpgradeCost(levelsToMax, player.mults.hacknet_node_level_cost)) { if (Player.money > nodeObj.calculateLevelUpgradeCost(levelsToMax, Player.mults.hacknet_node_level_cost)) {
return levelsToMax; return levelsToMax;
} }
@ -112,13 +108,13 @@ export function getMaxNumberLevelUpgrades(
const curr = ((min + max) / 2) | 0; const curr = ((min + max) / 2) | 0;
if ( if (
curr !== maxLevel && curr !== maxLevel &&
player.money > nodeObj.calculateLevelUpgradeCost(curr, player.mults.hacknet_node_level_cost) && Player.money > nodeObj.calculateLevelUpgradeCost(curr, Player.mults.hacknet_node_level_cost) &&
player.money < nodeObj.calculateLevelUpgradeCost(curr + 1, player.mults.hacknet_node_level_cost) Player.money < nodeObj.calculateLevelUpgradeCost(curr + 1, Player.mults.hacknet_node_level_cost)
) { ) {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
} else if (player.money < nodeObj.calculateLevelUpgradeCost(curr, player.mults.hacknet_node_level_cost)) { } else if (Player.money < nodeObj.calculateLevelUpgradeCost(curr, Player.mults.hacknet_node_level_cost)) {
max = curr - 1; max = curr - 1;
} else if (player.money > nodeObj.calculateLevelUpgradeCost(curr, player.mults.hacknet_node_level_cost)) { } else if (Player.money > nodeObj.calculateLevelUpgradeCost(curr, Player.mults.hacknet_node_level_cost)) {
min = curr + 1; min = curr + 1;
} else { } else {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
@ -128,16 +124,12 @@ export function getMaxNumberLevelUpgrades(
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's RAM // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's RAM
export function getMaxNumberRamUpgrades( export function getMaxNumberRamUpgrades(nodeObj: HacknetNode | HacknetServer, maxLevel: number): number {
player: IPlayer,
nodeObj: HacknetNode | HacknetServer,
maxLevel: number,
): number {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
} }
if (player.money < nodeObj.calculateRamUpgradeCost(1, player.mults.hacknet_node_ram_cost)) { if (Player.money < nodeObj.calculateRamUpgradeCost(1, Player.mults.hacknet_node_ram_cost)) {
return 0; return 0;
} }
@ -147,13 +139,13 @@ export function getMaxNumberRamUpgrades(
} else { } else {
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram)); levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
} }
if (player.money > nodeObj.calculateRamUpgradeCost(levelsToMax, player.mults.hacknet_node_ram_cost)) { if (Player.money > nodeObj.calculateRamUpgradeCost(levelsToMax, Player.mults.hacknet_node_ram_cost)) {
return levelsToMax; return levelsToMax;
} }
//We'll just loop until we find the max //We'll just loop until we find the max
for (let i = levelsToMax - 1; i >= 0; --i) { for (let i = levelsToMax - 1; i >= 0; --i) {
if (player.money > nodeObj.calculateRamUpgradeCost(i, player.mults.hacknet_node_ram_cost)) { if (Player.money > nodeObj.calculateRamUpgradeCost(i, Player.mults.hacknet_node_ram_cost)) {
return i; return i;
} }
} }
@ -162,7 +154,6 @@ export function getMaxNumberRamUpgrades(
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cores // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cores
export function getMaxNumberCoreUpgrades( export function getMaxNumberCoreUpgrades(
player: IPlayer,
nodeObj: HacknetNode | HacknetServer, nodeObj: HacknetNode | HacknetServer,
maxLevel: number, maxLevel: number,
): number { ): number {
@ -170,14 +161,14 @@ export function getMaxNumberCoreUpgrades(
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
} }
if (player.money < nodeObj.calculateCoreUpgradeCost(1, player.mults.hacknet_node_core_cost)) { if (Player.money < nodeObj.calculateCoreUpgradeCost(1, Player.mults.hacknet_node_core_cost)) {
return 0; return 0;
} }
let min = 1; let min = 1;
let max = maxLevel - 1; let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.cores; const levelsToMax = maxLevel - nodeObj.cores;
if (player.money > nodeObj.calculateCoreUpgradeCost(levelsToMax, player.mults.hacknet_node_core_cost)) { if (Player.money > nodeObj.calculateCoreUpgradeCost(levelsToMax, Player.mults.hacknet_node_core_cost)) {
return levelsToMax; return levelsToMax;
} }
@ -186,13 +177,13 @@ export function getMaxNumberCoreUpgrades(
const curr = ((min + max) / 2) | 0; const curr = ((min + max) / 2) | 0;
if ( if (
curr != maxLevel && curr != maxLevel &&
player.money > nodeObj.calculateCoreUpgradeCost(curr, player.mults.hacknet_node_core_cost) && Player.money > nodeObj.calculateCoreUpgradeCost(curr, Player.mults.hacknet_node_core_cost) &&
player.money < nodeObj.calculateCoreUpgradeCost(curr + 1, player.mults.hacknet_node_core_cost) Player.money < nodeObj.calculateCoreUpgradeCost(curr + 1, Player.mults.hacknet_node_core_cost)
) { ) {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
} else if (player.money < nodeObj.calculateCoreUpgradeCost(curr, player.mults.hacknet_node_core_cost)) { } else if (Player.money < nodeObj.calculateCoreUpgradeCost(curr, Player.mults.hacknet_node_core_cost)) {
max = curr - 1; max = curr - 1;
} else if (player.money > nodeObj.calculateCoreUpgradeCost(curr, player.mults.hacknet_node_core_cost)) { } else if (Player.money > nodeObj.calculateCoreUpgradeCost(curr, Player.mults.hacknet_node_core_cost)) {
min = curr + 1; min = curr + 1;
} else { } else {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
@ -203,19 +194,19 @@ export function getMaxNumberCoreUpgrades(
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cache // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cache
export function getMaxNumberCacheUpgrades(player: IPlayer, nodeObj: HacknetServer, maxLevel: number): number { export function getMaxNumberCacheUpgrades(nodeObj: HacknetServer, maxLevel: number): number {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);
} }
if (!player.canAfford(nodeObj.calculateCacheUpgradeCost(1))) { if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(1))) {
return 0; return 0;
} }
let min = 1; let min = 1;
let max = maxLevel - 1; let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.cache; const levelsToMax = maxLevel - nodeObj.cache;
if (player.canAfford(nodeObj.calculateCacheUpgradeCost(levelsToMax))) { if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(levelsToMax))) {
return levelsToMax; return levelsToMax;
} }
@ -224,13 +215,13 @@ export function getMaxNumberCacheUpgrades(player: IPlayer, nodeObj: HacknetServe
const curr = ((min + max) / 2) | 0; const curr = ((min + max) / 2) | 0;
if ( if (
curr != maxLevel && curr != maxLevel &&
player.canAfford(nodeObj.calculateCacheUpgradeCost(curr)) && Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr)) &&
!player.canAfford(nodeObj.calculateCacheUpgradeCost(curr + 1)) !Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr + 1))
) { ) {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
} else if (!player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) { } else if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
max = curr - 1; max = curr - 1;
} else if (player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) { } else if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) {
min = curr + 1; min = curr + 1;
} else { } else {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
@ -240,9 +231,9 @@ export function getMaxNumberCacheUpgrades(player: IPlayer, nodeObj: HacknetServe
return 0; return 0;
} }
export function purchaseLevelUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { export function purchaseLevelUpgrade(node: HacknetNode | HacknetServer, levels = 1): boolean {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
const cost = node.calculateLevelUpgradeCost(sanitizedLevels, player.mults.hacknet_node_level_cost); const cost = node.calculateLevelUpgradeCost(sanitizedLevels, Player.mults.hacknet_node_level_cost);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false; return false;
} }
@ -258,22 +249,22 @@ export function purchaseLevelUpgrade(player: IPlayer, node: HacknetNode | Hackne
// the maximum number of upgrades and use that // the maximum number of upgrades and use that
if (node.level + sanitizedLevels > (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel)) { if (node.level + sanitizedLevels > (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel)) {
const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel) - node.level); const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel) - node.level);
return purchaseLevelUpgrade(player, node, diff); return purchaseLevelUpgrade(node, diff);
} }
if (!player.canAfford(cost)) { if (!Player.canAfford(cost)) {
return false; return false;
} }
player.loseMoney(cost, "hacknet_expenses"); Player.loseMoney(cost, "hacknet_expenses");
node.upgradeLevel(sanitizedLevels, player.mults.hacknet_node_money); node.upgradeLevel(sanitizedLevels, Player.mults.hacknet_node_money);
return true; return true;
} }
export function purchaseRamUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { export function purchaseRamUpgrade(node: HacknetNode | HacknetServer, levels = 1): boolean {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
const cost = node.calculateRamUpgradeCost(sanitizedLevels, player.mults.hacknet_node_ram_cost); const cost = node.calculateRamUpgradeCost(sanitizedLevels, Player.mults.hacknet_node_ram_cost);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false; return false;
} }
@ -291,28 +282,28 @@ export function purchaseRamUpgrade(player: IPlayer, node: HacknetNode | HacknetS
if (node instanceof HacknetServer) { if (node instanceof HacknetServer) {
if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerConstants.MaxRam) { if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerConstants.MaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetServerConstants.MaxRam / node.maxRam))); const diff = Math.max(0, Math.log2(Math.round(HacknetServerConstants.MaxRam / node.maxRam)));
return purchaseRamUpgrade(player, node, diff); return purchaseRamUpgrade(node, diff);
} }
} else if (node instanceof HacknetNode) { } else if (node instanceof HacknetNode) {
if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeConstants.MaxRam) { if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeConstants.MaxRam) {
const diff = Math.max(0, Math.log2(Math.round(HacknetNodeConstants.MaxRam / node.ram))); const diff = Math.max(0, Math.log2(Math.round(HacknetNodeConstants.MaxRam / node.ram)));
return purchaseRamUpgrade(player, node, diff); return purchaseRamUpgrade(node, diff);
} }
} }
if (!player.canAfford(cost)) { if (!Player.canAfford(cost)) {
return false; return false;
} }
player.loseMoney(cost, "hacknet_expenses"); Player.loseMoney(cost, "hacknet_expenses");
node.upgradeRam(sanitizedLevels, player.mults.hacknet_node_money); node.upgradeRam(sanitizedLevels, Player.mults.hacknet_node_money);
return true; return true;
} }
export function purchaseCoreUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { export function purchaseCoreUpgrade(node: HacknetNode | HacknetServer, levels = 1): boolean {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
const cost = node.calculateCoreUpgradeCost(sanitizedLevels, player.mults.hacknet_node_core_cost); const cost = node.calculateCoreUpgradeCost(sanitizedLevels, Player.mults.hacknet_node_core_cost);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false; return false;
} }
@ -328,20 +319,20 @@ export function purchaseCoreUpgrade(player: IPlayer, node: HacknetNode | Hacknet
// the max possible number of upgrades and use that // the max possible number of upgrades and use that
if (node.cores + sanitizedLevels > (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores)) { if (node.cores + sanitizedLevels > (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores)) {
const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores) - node.cores); const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores) - node.cores);
return purchaseCoreUpgrade(player, node, diff); return purchaseCoreUpgrade(node, diff);
} }
if (!player.canAfford(cost)) { if (!Player.canAfford(cost)) {
return false; return false;
} }
player.loseMoney(cost, "hacknet_expenses"); Player.loseMoney(cost, "hacknet_expenses");
node.upgradeCore(sanitizedLevels, player.mults.hacknet_node_money); node.upgradeCore(sanitizedLevels, Player.mults.hacknet_node_money);
return true; return true;
} }
export function purchaseCacheUpgrade(player: IPlayer, node: HacknetServer, levels = 1): boolean { export function purchaseCacheUpgrade(node: HacknetServer, levels = 1): boolean {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
const cost = node.calculateCacheUpgradeCost(sanitizedLevels); const cost = node.calculateCacheUpgradeCost(sanitizedLevels);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
@ -356,71 +347,71 @@ export function purchaseCacheUpgrade(player: IPlayer, node: HacknetServer, level
// Fail if we're already at max // Fail if we're already at max
if (node.cache + sanitizedLevels > HacknetServerConstants.MaxCache) { if (node.cache + sanitizedLevels > HacknetServerConstants.MaxCache) {
const diff = Math.max(0, HacknetServerConstants.MaxCache - node.cache); const diff = Math.max(0, HacknetServerConstants.MaxCache - node.cache);
return purchaseCacheUpgrade(player, node, diff); return purchaseCacheUpgrade(node, diff);
} }
if (!player.canAfford(cost)) { if (!Player.canAfford(cost)) {
return false; return false;
} }
player.loseMoney(cost, "hacknet_expenses"); Player.loseMoney(cost, "hacknet_expenses");
node.upgradeCache(sanitizedLevels); node.upgradeCache(sanitizedLevels);
return true; return true;
} }
export function processHacknetEarnings(player: IPlayer, numCycles: number): number { export function processHacknetEarnings(numCycles: number): number {
// Determine if player has Hacknet Nodes or Hacknet Servers, then // Determine if player has Hacknet Nodes or Hacknet Servers, then
// call the appropriate function // call the appropriate function
if (player.hacknetNodes.length === 0) { if (Player.hacknetNodes.length === 0) {
return 0; return 0;
} }
if (hasHacknetServers(player)) { if (hasHacknetServers()) {
return processAllHacknetServerEarnings(player, numCycles); return processAllHacknetServerEarnings(numCycles);
} else if (player.hacknetNodes[0] instanceof HacknetNode) { } else if (Player.hacknetNodes[0] instanceof HacknetNode) {
return processAllHacknetNodeEarnings(player, numCycles); return processAllHacknetNodeEarnings(numCycles);
} else { } else {
return 0; return 0;
} }
} }
function processAllHacknetNodeEarnings(player: IPlayer, numCycles: number): number { function processAllHacknetNodeEarnings(numCycles: number): number {
let total = 0; let total = 0;
for (let i = 0; i < player.hacknetNodes.length; ++i) { for (let i = 0; i < Player.hacknetNodes.length; ++i) {
const node = player.hacknetNodes[i]; const node = Player.hacknetNodes[i];
if (typeof node === "string") throw new Error("player node should not be ip string"); if (typeof node === "string") throw new Error("player node should not be ip string");
total += processSingleHacknetNodeEarnings(player, numCycles, node); total += processSingleHacknetNodeEarnings(numCycles, node);
} }
return total; return total;
} }
function processSingleHacknetNodeEarnings(player: IPlayer, numCycles: number, nodeObj: HacknetNode): number { function processSingleHacknetNodeEarnings(numCycles: number, nodeObj: HacknetNode): number {
const totalEarnings = nodeObj.process(numCycles); const totalEarnings = nodeObj.process(numCycles);
player.gainMoney(totalEarnings, "hacknet"); Player.gainMoney(totalEarnings, "hacknet");
return totalEarnings; return totalEarnings;
} }
function processAllHacknetServerEarnings(player: IPlayer, numCycles: number): number { function processAllHacknetServerEarnings(numCycles: number): number {
if (!(player.hashManager instanceof HashManager)) { if (!(Player.hashManager instanceof HashManager)) {
throw new Error(`Player does not have a HashManager (should be in 'hashManager' prop)`); throw new Error(`Player does not have a HashManager (should be in 'hashManager' prop)`);
} }
let hashes = 0; let hashes = 0;
for (let i = 0; i < player.hacknetNodes.length; ++i) { for (let i = 0; i < Player.hacknetNodes.length; ++i) {
// hacknetNodes array only contains the IP addresses of the servers. // hacknetNodes array only contains the IP addresses of the servers.
// Also, update the hash rate before processing // Also, update the hash rate before processing
const ip = player.hacknetNodes[i]; const ip = Player.hacknetNodes[i];
if (ip instanceof HacknetNode) throw new Error(`player nodes should not be HacketNode`); if (ip instanceof HacknetNode) throw new Error(`player nodes should not be HacketNode`);
const hserver = GetServer(ip); const hserver = GetServer(ip);
if (!(hserver instanceof HacknetServer)) throw new Error(`player nodes shoud not be Server`); if (!(hserver instanceof HacknetServer)) throw new Error(`player nodes shoud not be Server`);
hserver.updateHashRate(player.mults.hacknet_node_money); hserver.updateHashRate(Player.mults.hacknet_node_money);
const h = hserver.process(numCycles); const h = hserver.process(numCycles);
hashes += h; hashes += h;
} }
const wastedHashes = player.hashManager.storeHashes(hashes); const wastedHashes = Player.hashManager.storeHashes(hashes);
if (wastedHashes > 0) { if (wastedHashes > 0) {
const upgrade = HashUpgrades["Sell for Money"]; const upgrade = HashUpgrades["Sell for Money"];
if (upgrade === null) throw new Error("Could not get the hash upgrade"); if (upgrade === null) throw new Error("Could not get the hash upgrade");
@ -428,65 +419,65 @@ function processAllHacknetServerEarnings(player: IPlayer, numCycles: number): nu
const multiplier = wastedHashes / upgrade.cost; const multiplier = wastedHashes / upgrade.cost;
if (multiplier > 0) { if (multiplier > 0) {
player.gainMoney(upgrade.value * multiplier, "hacknet"); Player.gainMoney(upgrade.value * multiplier, "hacknet");
} }
} }
return hashes; return hashes;
} }
export function updateHashManagerCapacity(player: IPlayer): void { export function updateHashManagerCapacity(): void {
if (!(player.hashManager instanceof HashManager)) { if (!(Player.hashManager instanceof HashManager)) {
console.error(`Player does not have a HashManager`); console.error(`Player does not have a HashManager`);
return; return;
} }
const nodes = player.hacknetNodes; const nodes = Player.hacknetNodes;
if (nodes.length === 0) { if (nodes.length === 0) {
player.hashManager.updateCapacity(0); Player.hashManager.updateCapacity(0);
return; return;
} }
let total = 0; let total = 0;
for (let i = 0; i < nodes.length; ++i) { for (let i = 0; i < nodes.length; ++i) {
if (typeof nodes[i] !== "string") { if (typeof nodes[i] !== "string") {
player.hashManager.updateCapacity(0); Player.hashManager.updateCapacity(0);
return; return;
} }
const ip = nodes[i]; const ip = nodes[i];
if (ip instanceof HacknetNode) throw new Error(`player nodes should be string but isn't`); if (ip instanceof HacknetNode) throw new Error(`player nodes should be string but isn't`);
const h = GetServer(ip); const h = GetServer(ip);
if (!(h instanceof HacknetServer)) { if (!(h instanceof HacknetServer)) {
player.hashManager.updateCapacity(0); Player.hashManager.updateCapacity(0);
return; return;
} }
total += h.hashCapacity; total += h.hashCapacity;
} }
player.hashManager.updateCapacity(total); Player.hashManager.updateCapacity(total);
} }
export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget: string, count = 1): boolean { export function purchaseHashUpgrade(upgName: string, upgTarget: string, count = 1): boolean {
if (!(player.hashManager instanceof HashManager)) { if (!(Player.hashManager instanceof HashManager)) {
console.error(`Player does not have a HashManager`); console.error(`Player does not have a HashManager`);
return false; return false;
} }
// HashManager handles the transaction. This just needs to actually implement // HashManager handles the transaction. This just needs to actually implement
// the effects of the upgrade // the effects of the upgrade
if (player.hashManager.upgrade(upgName, count)) { if (Player.hashManager.upgrade(upgName, count)) {
const upg = HashUpgrades[upgName]; const upg = HashUpgrades[upgName];
switch (upgName) { switch (upgName) {
case "Sell for Money": { case "Sell for Money": {
player.gainMoney(upg.value * count, "hacknet"); Player.gainMoney(upg.value * count, "hacknet");
break; break;
} }
case "Sell for Corporation Funds": { case "Sell for Corporation Funds": {
const corp = player.corporation; const corp = Player.corporation;
if (corp === null) { if (corp === null) {
player.hashManager.refundUpgrade(upgName, count); Player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
corp.funds = corp.funds + upg.value * count; corp.funds = corp.funds + upg.value * count;
@ -503,7 +494,7 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
target.changeMinimumSecurity(upg.value ** count, true); target.changeMinimumSecurity(upg.value ** count, true);
} catch (e) { } catch (e) {
player.hashManager.refundUpgrade(upgName, count); Player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
break; break;
@ -522,7 +513,7 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
target.changeMaximumMoney(upg.value); target.changeMaximumMoney(upg.value);
} }
} catch (e) { } catch (e) {
player.hashManager.refundUpgrade(upgName, count); Player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
break; break;
@ -537,9 +528,9 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
} }
case "Exchange for Corporation Research": { case "Exchange for Corporation Research": {
// This will throw if player doesn't have a corporation // This will throw if player doesn't have a corporation
const corp = player.corporation; const corp = Player.corporation;
if (corp === null) { if (corp === null) {
player.hashManager.refundUpgrade(upgName, count); Player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
for (const division of corp.divisions) { for (const division of corp.divisions) {
@ -549,19 +540,19 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
} }
case "Exchange for Bladeburner Rank": { case "Exchange for Bladeburner Rank": {
// This will throw if player isnt in Bladeburner // This will throw if player isnt in Bladeburner
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) { if (bladeburner === null) {
player.hashManager.refundUpgrade(upgName, count); Player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
bladeburner.changeRank(player, upg.value * count); bladeburner.changeRank(Player, upg.value * count);
break; break;
} }
case "Exchange for Bladeburner SP": { case "Exchange for Bladeburner SP": {
// This will throw if player isnt in Bladeburner // This will throw if player isnt in Bladeburner
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) { if (bladeburner === null) {
player.hashManager.refundUpgrade(upgName, count); Player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }

@ -14,7 +14,7 @@ import {
purchaseCoreUpgrade, purchaseCoreUpgrade,
} from "../HacknetHelpers"; } from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { HacknetNode } from "../HacknetNode"; import { HacknetNode } from "../HacknetNode";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
@ -36,7 +36,6 @@ interface IProps {
node: HacknetNode; node: HacknetNode;
purchaseMultiplier: number | "MAX"; purchaseMultiplier: number | "MAX";
rerender: () => void; rerender: () => void;
player: IPlayer;
} }
export function HacknetNodeElem(props: IProps): React.ReactElement { export function HacknetNodeElem(props: IProps): React.ReactElement {
@ -51,16 +50,16 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(props.player, node, HacknetNodeConstants.MaxLevel); multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel);
} else { } else {
const levelsToMax = HacknetNodeConstants.MaxLevel - node.level; const levelsToMax = HacknetNodeConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const increase = const increase =
calculateMoneyGainRate(node.level + multiplier, node.ram, node.cores, props.player.mults.hacknet_node_money) - calculateMoneyGainRate(node.level + multiplier, node.ram, node.cores, Player.mults.hacknet_node_money) -
node.moneyGainRatePerSecond; node.moneyGainRatePerSecond;
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.mults.hacknet_node_level_cost); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.mults.hacknet_node_level_cost);
upgradeLevelButton = ( upgradeLevelButton = (
<Tooltip <Tooltip
title={ title={
@ -71,7 +70,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
> >
<Button onClick={upgradeLevelOnClick}> <Button onClick={upgradeLevelOnClick}>
+{multiplier} -&nbsp; +{multiplier} -&nbsp;
<Money money={upgradeLevelCost} player={props.player} /> <Money money={upgradeLevelCost} forPurchase={true} />
</Button> </Button>
</Tooltip> </Tooltip>
); );
@ -79,9 +78,9 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
function upgradeLevelOnClick(): void { function upgradeLevelOnClick(): void {
const numUpgrades = const numUpgrades =
purchaseMult === "MAX" purchaseMult === "MAX"
? getMaxNumberLevelUpgrades(props.player, node, HacknetNodeConstants.MaxLevel) ? getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel)
: purchaseMult; : purchaseMult;
purchaseLevelUpgrade(props.player, node, numUpgrades); purchaseLevelUpgrade(node, numUpgrades);
rerender(); rerender();
} }
@ -91,7 +90,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(props.player, node, HacknetNodeConstants.MaxRam); multiplier = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam);
} else { } else {
const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram)); const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram));
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
@ -102,9 +101,9 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
node.level, node.level,
node.ram * Math.pow(2, multiplier), node.ram * Math.pow(2, multiplier),
node.cores, node.cores,
props.player.mults.hacknet_node_money, Player.mults.hacknet_node_money,
) - node.moneyGainRatePerSecond; ) - node.moneyGainRatePerSecond;
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.mults.hacknet_node_ram_cost); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.mults.hacknet_node_ram_cost);
upgradeRAMButton = ( upgradeRAMButton = (
<Tooltip <Tooltip
title={ title={
@ -115,24 +114,24 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
> >
<Button onClick={upgradeRamOnClick}> <Button onClick={upgradeRamOnClick}>
+{multiplier} -&nbsp; +{multiplier} -&nbsp;
<Money money={upgradeRamCost} player={props.player} /> <Money money={upgradeRamCost} forPurchase={true} />
</Button> </Button>
</Tooltip> </Tooltip>
); );
} }
function upgradeRamOnClick(): void { function upgradeRamOnClick(): void {
const numUpgrades = const numUpgrades =
purchaseMult === "MAX" ? getMaxNumberRamUpgrades(props.player, node, HacknetNodeConstants.MaxRam) : purchaseMult; purchaseMult === "MAX" ? getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam) : purchaseMult;
purchaseRamUpgrade(props.player, node, numUpgrades); purchaseRamUpgrade(node, numUpgrades);
rerender(); rerender();
} }
function upgradeCoresOnClick(): void { function upgradeCoresOnClick(): void {
const numUpgrades = const numUpgrades =
purchaseMult === "MAX" purchaseMult === "MAX"
? getMaxNumberCoreUpgrades(props.player, node, HacknetNodeConstants.MaxCores) ? getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores)
: purchaseMult; : purchaseMult;
purchaseCoreUpgrade(props.player, node, numUpgrades); purchaseCoreUpgrade(node, numUpgrades);
rerender(); rerender();
} }
let upgradeCoresButton; let upgradeCoresButton;
@ -141,16 +140,16 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(props.player, node, HacknetNodeConstants.MaxCores); multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores);
} else { } else {
const levelsToMax = HacknetNodeConstants.MaxCores - node.cores; const levelsToMax = HacknetNodeConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult); multiplier = Math.min(levelsToMax, purchaseMult);
} }
const increase = const increase =
calculateMoneyGainRate(node.level, node.ram, node.cores + multiplier, props.player.mults.hacknet_node_money) - calculateMoneyGainRate(node.level, node.ram, node.cores + multiplier, Player.mults.hacknet_node_money) -
node.moneyGainRatePerSecond; node.moneyGainRatePerSecond;
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.mults.hacknet_node_core_cost); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.mults.hacknet_node_core_cost);
upgradeCoresButton = ( upgradeCoresButton = (
<Tooltip <Tooltip
title={ title={
@ -161,7 +160,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
> >
<Button onClick={upgradeCoresOnClick}> <Button onClick={upgradeCoresOnClick}>
+{multiplier} -&nbsp; +{multiplier} -&nbsp;
<Money money={upgradeCoreCost} player={props.player} /> <Money money={upgradeCoreCost} forPurchase={true} />
</Button> </Button>
</Tooltip> </Tooltip>
); );

@ -21,7 +21,7 @@ import {
purchaseHacknet, purchaseHacknet,
} from "../HacknetHelpers"; } from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -29,11 +29,7 @@ import Grid from "@mui/material/Grid";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
interface IProps { export function HacknetRoot(): React.ReactElement {
player: IPlayer;
}
export function HacknetRoot(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -47,9 +43,9 @@ export function HacknetRoot(props: IProps): React.ReactElement {
}, []); }, []);
let totalProduction = 0; let totalProduction = 0;
for (let i = 0; i < props.player.hacknetNodes.length; ++i) { for (let i = 0; i < Player.hacknetNodes.length; ++i) {
const node = props.player.hacknetNodes[i]; const node = Player.hacknetNodes[i];
if (hasHacknetServers(props.player)) { if (hasHacknetServers()) {
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
const hserver = GetServer(node); const hserver = GetServer(node);
if (!(hserver instanceof HacknetServer)) throw new Error("node was not hacknet server"); // should never happen if (!(hserver instanceof HacknetServer)) throw new Error("node was not hacknet server"); // should never happen
@ -65,16 +61,16 @@ export function HacknetRoot(props: IProps): React.ReactElement {
} }
function handlePurchaseButtonClick(): void { function handlePurchaseButtonClick(): void {
purchaseHacknet(props.player); purchaseHacknet();
rerender(); rerender();
} }
// Cost to purchase a new Hacknet Node // Cost to purchase a new Hacknet Node
let purchaseCost; let purchaseCost;
if (hasHacknetServers(props.player)) { if (hasHacknetServers()) {
purchaseCost = getCostOfNextHacknetServer(props.player); purchaseCost = getCostOfNextHacknetServer();
} else { } else {
purchaseCost = getCostOfNextHacknetNode(props.player); purchaseCost = getCostOfNextHacknetNode();
} }
// onClick event handlers for purchase multiplier buttons // onClick event handlers for purchase multiplier buttons
@ -86,8 +82,8 @@ export function HacknetRoot(props: IProps): React.ReactElement {
]; ];
// HacknetNode components // HacknetNode components
const nodes = props.player.hacknetNodes.map((node: string | HacknetNode) => { const nodes = Player.hacknetNodes.map((node: string | HacknetNode) => {
if (hasHacknetServers(props.player)) { if (hasHacknetServers()) {
if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen
const hserver = GetServer(node); const hserver = GetServer(node);
if (hserver == null) { if (hserver == null) {
@ -96,7 +92,6 @@ export function HacknetRoot(props: IProps): React.ReactElement {
if (!(hserver instanceof HacknetServer)) throw new Error("node was not hacknet server"); // should never happen if (!(hserver instanceof HacknetServer)) throw new Error("node was not hacknet server"); // should never happen
return ( return (
<HacknetServerElem <HacknetServerElem
player={props.player}
key={hserver.hostname} key={hserver.hostname}
node={hserver} node={hserver}
purchaseMultiplier={purchaseMultiplier} purchaseMultiplier={purchaseMultiplier}
@ -106,21 +101,15 @@ export function HacknetRoot(props: IProps): React.ReactElement {
} else { } else {
if (typeof node === "string") throw new Error("node was ip string"); // should never happen if (typeof node === "string") throw new Error("node was ip string"); // should never happen
return ( return (
<HacknetNodeElem <HacknetNodeElem key={node.name} node={node} purchaseMultiplier={purchaseMultiplier} rerender={rerender} />
player={props.player}
key={node.name}
node={node}
purchaseMultiplier={purchaseMultiplier}
rerender={rerender}
/>
); );
} }
}); });
return ( return (
<> <>
<Typography variant="h4">Hacknet {hasHacknetServers(props.player) ? "Servers" : "Nodes"}</Typography> <Typography variant="h4">Hacknet {hasHacknetServers() ? "Servers" : "Nodes"}</Typography>
<GeneralInfo hasHacknetServers={hasHacknetServers(props.player)} /> <GeneralInfo hasHacknetServers={hasHacknetServers()} />
<PurchaseButton cost={purchaseCost} multiplier={purchaseMultiplier} onClick={handlePurchaseButtonClick} /> <PurchaseButton cost={purchaseCost} multiplier={purchaseMultiplier} onClick={handlePurchaseButtonClick} />
@ -128,14 +117,14 @@ export function HacknetRoot(props: IProps): React.ReactElement {
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={6}> <Grid item xs={6}>
<PlayerInfo totalProduction={totalProduction} player={props.player} /> <PlayerInfo totalProduction={totalProduction} />
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={purchaseMultiplier} /> <MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={purchaseMultiplier} />
</Grid> </Grid>
</Grid> </Grid>
{hasHacknetServers(props.player) && <Button onClick={() => setOpen(true)}>Spend Hashes on Upgrades</Button>} {hasHacknetServers() && <Button onClick={() => setOpen(true)}>Spend Hashes on Upgrades</Button>}
<Box sx={{ display: "grid", width: "fit-content", gridTemplateColumns: "repeat(3, 1fr)" }}>{nodes}</Box> <Box sx={{ display: "grid", width: "fit-content", gridTemplateColumns: "repeat(3, 1fr)" }}>{nodes}</Box>
<HashUpgradeModal open={open} onClose={() => setOpen(false)} /> <HashUpgradeModal open={open} onClose={() => setOpen(false)} />

@ -17,7 +17,7 @@ import {
updateHashManagerCapacity, updateHashManagerCapacity,
} from "../HacknetHelpers"; } from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { HacknetServer } from "../HacknetServer"; import { HacknetServer } from "../HacknetServer";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
@ -39,7 +39,6 @@ interface IProps {
node: HacknetServer; node: HacknetServer;
purchaseMultiplier: number | string; purchaseMultiplier: number | string;
rerender: () => void; rerender: () => void;
player: IPlayer;
} }
export function HacknetServerElem(props: IProps): React.ReactElement { export function HacknetServerElem(props: IProps): React.ReactElement {
@ -54,7 +53,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberLevelUpgrades(props.player, node, HacknetServerConstants.MaxLevel); multiplier = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
} else { } else {
const levelsToMax = HacknetServerConstants.MaxLevel - node.level; const levelsToMax = HacknetServerConstants.MaxLevel - node.level;
multiplier = Math.min(levelsToMax, purchaseMult as number); multiplier = Math.min(levelsToMax, purchaseMult as number);
@ -66,11 +65,11 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
0, 0,
node.maxRam, node.maxRam,
node.cores, node.cores,
props.player.mults.hacknet_node_money, Player.mults.hacknet_node_money,
) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.mults.hacknet_node_money); ) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, Player.mults.hacknet_node_money);
const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam; const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam;
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.mults.hacknet_node_level_cost); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.mults.hacknet_node_level_cost);
upgradeLevelButton = ( upgradeLevelButton = (
<Tooltip <Tooltip
title={ title={
@ -86,7 +85,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
> >
<Button onClick={upgradeLevelOnClick}> <Button onClick={upgradeLevelOnClick}>
+{multiplier}&nbsp;-&nbsp; +{multiplier}&nbsp;-&nbsp;
<Money money={upgradeLevelCost} player={props.player} /> <Money money={upgradeLevelCost} forPurchase={true} />
</Button> </Button>
</Tooltip> </Tooltip>
); );
@ -94,18 +93,18 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
function upgradeLevelOnClick(): void { function upgradeLevelOnClick(): void {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberLevelUpgrades(props.player, node, HacknetServerConstants.MaxLevel); numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel);
} }
purchaseLevelUpgrade(props.player, node, numUpgrades as number); purchaseLevelUpgrade(node, numUpgrades as number);
rerender(); rerender();
} }
function upgradeRamOnClick(): void { function upgradeRamOnClick(): void {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberRamUpgrades(props.player, node, HacknetServerConstants.MaxRam); numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
} }
purchaseRamUpgrade(props.player, node, numUpgrades as number); purchaseRamUpgrade(node, numUpgrades as number);
rerender(); rerender();
} }
// Upgrade RAM Button // Upgrade RAM Button
@ -115,7 +114,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberRamUpgrades(props.player, node, HacknetServerConstants.MaxRam); multiplier = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam);
} else { } else {
const levelsToMax = Math.round(Math.log2(HacknetServerConstants.MaxRam / node.maxRam)); const levelsToMax = Math.round(Math.log2(HacknetServerConstants.MaxRam / node.maxRam));
multiplier = Math.min(levelsToMax, purchaseMult as number); multiplier = Math.min(levelsToMax, purchaseMult as number);
@ -127,8 +126,8 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
0, 0,
node.maxRam * Math.pow(2, multiplier), node.maxRam * Math.pow(2, multiplier),
node.cores, node.cores,
props.player.mults.hacknet_node_money, Player.mults.hacknet_node_money,
) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.mults.hacknet_node_money); ) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, Player.mults.hacknet_node_money);
const modded_increase = const modded_increase =
calculateHashGainRate( calculateHashGainRate(
@ -136,11 +135,11 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
node.ramUsed, node.ramUsed,
node.maxRam * Math.pow(2, multiplier), node.maxRam * Math.pow(2, multiplier),
node.cores, node.cores,
props.player.mults.hacknet_node_money, Player.mults.hacknet_node_money,
) - ) -
calculateHashGainRate(node.level, node.ramUsed, node.maxRam, node.cores, props.player.mults.hacknet_node_money); calculateHashGainRate(node.level, node.ramUsed, node.maxRam, node.cores, Player.mults.hacknet_node_money);
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.mults.hacknet_node_ram_cost); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.mults.hacknet_node_ram_cost);
upgradeRamButton = ( upgradeRamButton = (
<Tooltip <Tooltip
title={ title={
@ -156,7 +155,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
> >
<Button onClick={upgradeRamOnClick}> <Button onClick={upgradeRamOnClick}>
+{multiplier}&nbsp;-&nbsp; +{multiplier}&nbsp;-&nbsp;
<Money money={upgradeRamCost} player={props.player} /> <Money money={upgradeRamCost} forPurchase={true} />
</Button> </Button>
</Tooltip> </Tooltip>
); );
@ -165,9 +164,9 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
function upgradeCoresOnClick(): void { function upgradeCoresOnClick(): void {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCoreUpgrades(props.player, node, HacknetServerConstants.MaxCores); numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
} }
purchaseCoreUpgrade(props.player, node, numUpgrades as number); purchaseCoreUpgrade(node, numUpgrades as number);
rerender(); rerender();
} }
// Upgrade Cores Button // Upgrade Cores Button
@ -177,7 +176,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberCoreUpgrades(props.player, node, HacknetServerConstants.MaxCores); multiplier = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores);
} else { } else {
const levelsToMax = HacknetServerConstants.MaxCores - node.cores; const levelsToMax = HacknetServerConstants.MaxCores - node.cores;
multiplier = Math.min(levelsToMax, purchaseMult as number); multiplier = Math.min(levelsToMax, purchaseMult as number);
@ -189,11 +188,11 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
0, 0,
node.maxRam, node.maxRam,
node.cores + multiplier, node.cores + multiplier,
props.player.mults.hacknet_node_money, Player.mults.hacknet_node_money,
) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.mults.hacknet_node_money); ) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, Player.mults.hacknet_node_money);
const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam; const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam;
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.mults.hacknet_node_core_cost); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.mults.hacknet_node_core_cost);
upgradeCoresButton = ( upgradeCoresButton = (
<Tooltip <Tooltip
title={ title={
@ -209,7 +208,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
> >
<Button onClick={upgradeCoresOnClick}> <Button onClick={upgradeCoresOnClick}>
+{multiplier}&nbsp;-&nbsp; +{multiplier}&nbsp;-&nbsp;
<Money money={upgradeCoreCost} player={props.player} /> <Money money={upgradeCoreCost} forPurchase={true} />
</Button> </Button>
</Tooltip> </Tooltip>
); );
@ -222,7 +221,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
} else { } else {
let multiplier = 0; let multiplier = 0;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
multiplier = getMaxNumberCacheUpgrades(props.player, node, HacknetServerConstants.MaxCache); multiplier = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
} else { } else {
const levelsToMax = HacknetServerConstants.MaxCache - node.cache; const levelsToMax = HacknetServerConstants.MaxCache - node.cache;
multiplier = Math.min(levelsToMax, purchaseMult as number); multiplier = Math.min(levelsToMax, purchaseMult as number);
@ -240,22 +239,22 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
> >
<Button onClick={upgradeCacheOnClick}> <Button onClick={upgradeCacheOnClick}>
+{multiplier}&nbsp;-&nbsp; +{multiplier}&nbsp;-&nbsp;
<Money money={upgradeCacheCost} player={props.player} /> <Money money={upgradeCacheCost} forPurchase={true} />
</Button> </Button>
</Tooltip> </Tooltip>
); );
if (props.player.money < upgradeCacheCost) { if (Player.money < upgradeCacheCost) {
} else { } else {
} }
} }
function upgradeCacheOnClick(): void { function upgradeCacheOnClick(): void {
let numUpgrades = purchaseMult; let numUpgrades = purchaseMult;
if (purchaseMult === "MAX") { if (purchaseMult === "MAX") {
numUpgrades = getMaxNumberCacheUpgrades(props.player, node, HacknetServerConstants.MaxCache); numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache);
} }
purchaseCacheUpgrade(props.player, node, numUpgrades as number); purchaseCacheUpgrade(node, numUpgrades as number);
rerender(); rerender();
updateHashManagerCapacity(props.player); updateHashManagerCapacity();
} }
return ( return (

@ -4,8 +4,6 @@ import { purchaseHashUpgrade } from "../HacknetHelpers";
import { HashManager } from "../HashManager"; import { HashManager } from "../HashManager";
import { HashUpgrade } from "../HashUpgrade"; import { HashUpgrade } from "../HashUpgrade";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { ServerDropdown, ServerType } from "../../ui/React/ServerDropdown"; import { ServerDropdown, ServerType } from "../../ui/React/ServerDropdown";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
@ -19,7 +17,6 @@ import { SelectChangeEvent } from "@mui/material/Select";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
interface IProps { interface IProps {
player: IPlayer;
hashManager: HashManager; hashManager: HashManager;
upg: HashUpgrade; upg: HashUpgrade;
rerender: () => void; rerender: () => void;
@ -39,7 +36,7 @@ export function HacknetUpgradeElem(props: IProps): React.ReactElement {
function purchase(): void { function purchase(): void {
const canPurchase = props.hashManager.hashes >= props.hashManager.getUpgradeCost(props.upg.name); const canPurchase = props.hashManager.hashes >= props.hashManager.getUpgradeCost(props.upg.name);
if (canPurchase) { if (canPurchase) {
const res = purchaseHashUpgrade(props.player, props.upg.name, selectedServer); const res = purchaseHashUpgrade(props.upg.name, selectedServer);
if (!res) { if (!res) {
dialogBoxCreate( dialogBoxCreate(
"Failed to purchase upgrade. This may be because you do not have enough hashes, " + "Failed to purchase upgrade. This may be because you do not have enough hashes, " +

@ -9,7 +9,7 @@ import { HashUpgrades } from "../HashUpgrades";
import { Hashes } from "../../ui/React/Hashes"; import { Hashes } from "../../ui/React/Hashes";
import { HacknetUpgradeElem } from "./HacknetUpgradeElem"; import { HacknetUpgradeElem } from "./HacknetUpgradeElem";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
@ -18,7 +18,6 @@ interface IProps {
} }
export function HashUpgradeModal(props: IProps): React.ReactElement { export function HashUpgradeModal(props: IProps): React.ReactElement {
const player = use.Player();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
@ -29,7 +28,7 @@ export function HashUpgradeModal(props: IProps): React.ReactElement {
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
const hashManager = player.hashManager; const hashManager = Player.hashManager;
if (!(hashManager instanceof HashManager)) { if (!(hashManager instanceof HashManager)) {
throw new Error(`Player does not have a HashManager)`); throw new Error(`Player does not have a HashManager)`);
} }
@ -39,19 +38,11 @@ export function HashUpgradeModal(props: IProps): React.ReactElement {
<> <>
<Typography>Spend your hashes on a variety of different upgrades</Typography> <Typography>Spend your hashes on a variety of different upgrades</Typography>
<Typography> <Typography>
Hashes: <Hashes hashes={player.hashManager.hashes} /> Hashes: <Hashes hashes={Player.hashManager.hashes} />
</Typography> </Typography>
{Object.keys(HashUpgrades).map((upgName) => { {Object.keys(HashUpgrades).map((upgName) => {
const upg = HashUpgrades[upgName]; const upg = HashUpgrades[upgName];
return ( return <HacknetUpgradeElem upg={upg} hashManager={hashManager} key={upg.name} rerender={rerender} />;
<HacknetUpgradeElem
player={player}
upg={upg}
hashManager={hashManager}
key={upg.name}
rerender={rerender}
/>
);
})} })}
</> </>
</Modal> </Modal>

@ -7,7 +7,7 @@
import React from "react"; import React from "react";
import { hasHacknetServers } from "../HacknetHelpers"; import { hasHacknetServers } from "../HacknetHelpers";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate"; import { MoneyRate } from "../../ui/React/MoneyRate";
import { HashRate } from "../../ui/React/HashRate"; import { HashRate } from "../../ui/React/HashRate";
@ -16,11 +16,10 @@ import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
totalProduction: number; totalProduction: number;
player: IPlayer;
} }
export function PlayerInfo(props: IProps): React.ReactElement { export function PlayerInfo(props: IProps): React.ReactElement {
const hasServers = hasHacknetServers(props.player); const hasServers = hasHacknetServers();
let prod; let prod;
if (hasServers) { if (hasServers) {
@ -33,14 +32,13 @@ export function PlayerInfo(props: IProps): React.ReactElement {
<> <>
<Typography> <Typography>
Money: Money:
<Money money={props.player.money} /> <Money money={Player.money} />
</Typography> </Typography>
{hasServers && ( {hasServers && (
<> <>
<Typography> <Typography>
Hashes: <Hashes hashes={props.player.hashManager.hashes} /> /{" "} Hashes: <Hashes hashes={Player.hashManager.hashes} /> / <Hashes hashes={Player.hashManager.capacity} />
<Hashes hashes={props.player.hashManager.capacity} />
</Typography> </Typography>
</> </>
)} )}

@ -18,14 +18,14 @@ interface IProps {
export function PurchaseButton(props: IProps): React.ReactElement { export function PurchaseButton(props: IProps): React.ReactElement {
const cost = props.cost; const cost = props.cost;
let text; let text;
if (hasHacknetServers(Player)) { if (hasHacknetServers()) {
if (hasMaxNumberHacknetServers(Player)) { if (hasMaxNumberHacknetServers()) {
text = <>Hacknet Server limit reached</>; text = <>Hacknet Server limit reached</>;
} else { } else {
text = ( text = (
<> <>
Purchase Hacknet Server -&nbsp; Purchase Hacknet Server -&nbsp;
<Money money={cost} player={Player} /> <Money money={cost} forPurchase={true} />
</> </>
); );
} }
@ -33,7 +33,7 @@ export function PurchaseButton(props: IProps): React.ReactElement {
text = ( text = (
<> <>
Purchase Hacknet Node -&nbsp; Purchase Hacknet Node -&nbsp;
<Money money={cost} player={Player} /> <Money money={cost} forPurchase={true} />
</> </>
); );
} }

@ -1,18 +1,18 @@
import { IPlayer } from "../PersonObjects/IPlayer"; import { Player } from "../Player";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
export function getHospitalizationCost(p: IPlayer): number { export function getHospitalizationCost(): number {
if (p.money < 0) { if (Player.money < 0) {
return 0; return 0;
} }
return Math.min(p.money * 0.1, (p.hp.max - p.hp.current) * CONSTANTS.HospitalCostPerHp); return Math.min(Player.money * 0.1, (Player.hp.max - Player.hp.current) * CONSTANTS.HospitalCostPerHp);
} }
export function calculateHospitalizationCost(p: IPlayer, damage: number): number { export function calculateHospitalizationCost(damage: number): number {
const oldhp = p.hp.current; const oldhp = Player.hp.current;
p.hp.current -= damage; Player.hp.current -= damage;
const cost = getHospitalizationCost(p); const cost = getHospitalizationCost();
p.hp.current = oldhp; Player.hp.current = oldhp;
return cost; return cost;
} }

@ -1,40 +0,0 @@
/**
* Location and traveling-related helper functions.
* Mostly used for UI
*/
import { SpecialServers } from "../Server/data/SpecialServers";
import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer";
import { GetServer } from "../Server/AllServers";
import { dialogBoxCreate } from "../ui/React/DialogBox";
/**
* Attempt to purchase a TOR router
* @param {IPlayer} p - Player object
*/
export function purchaseTorRouter(p: IPlayer): void {
if (p.hasTorRouter()) {
dialogBoxCreate(`You already have a TOR Router!`);
return;
}
if (!p.canAfford(CONSTANTS.TorRouterCost)) {
dialogBoxCreate("You cannot afford to purchase the TOR router!");
return;
}
p.loseMoney(CONSTANTS.TorRouterCost, "other");
const darkweb = GetServer(SpecialServers.DarkWeb);
if (!darkweb) {
throw new Error("Dark web is not a server.");
}
p.getHomeComputer().serversOnNetwork.push(darkweb.hostname);
darkweb.serversOnNetwork.push(p.getHomeComputer().hostname);
dialogBoxCreate(
"You have purchased a TOR router!\n" +
"You now have access to the dark web from your home computer.\n" +
"Use the scan/scan-analyze commands to search for the dark web connection.",
);
}

@ -9,7 +9,6 @@ import { Blackjack, DECK_COUNT } from "../../Casino/Blackjack";
import { CoinFlip } from "../../Casino/CoinFlip"; import { CoinFlip } from "../../Casino/CoinFlip";
import { Roulette } from "../../Casino/Roulette"; import { Roulette } from "../../Casino/Roulette";
import { SlotMachine } from "../../Casino/SlotMachine"; import { SlotMachine } from "../../Casino/SlotMachine";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
enum GameType { enum GameType {
@ -20,11 +19,7 @@ enum GameType {
Blackjack = "blackjack", Blackjack = "blackjack",
} }
type IProps = { export function CasinoLocation(): React.ReactElement {
p: IPlayer;
};
export function CasinoLocation(props: IProps): React.ReactElement {
const [game, setGame] = useState(GameType.None); const [game, setGame] = useState(GameType.None);
function updateGame(game: GameType): void { function updateGame(game: GameType): void {
@ -44,10 +39,10 @@ export function CasinoLocation(props: IProps): React.ReactElement {
{game !== GameType.None && ( {game !== GameType.None && (
<> <>
<Button onClick={() => updateGame(GameType.None)}>Stop playing</Button> <Button onClick={() => updateGame(GameType.None)}>Stop playing</Button>
{game === GameType.Coin && <CoinFlip p={props.p} />} {game === GameType.Coin && <CoinFlip />}
{game === GameType.Slots && <SlotMachine p={props.p} />} {game === GameType.Slots && <SlotMachine />}
{game === GameType.Roulette && <Roulette p={props.p} />} {game === GameType.Roulette && <Roulette />}
{game === GameType.Blackjack && <Blackjack p={props.p} />} {game === GameType.Blackjack && <Blackjack />}
</> </>
)} )}
</> </>

@ -3,29 +3,28 @@ import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { MathJaxWrapper } from "../../MathJaxWrapper"; import { MathJaxWrapper } from "../../MathJaxWrapper";
type IProps = { type IProps = {
p: IPlayer;
rerender: () => void; rerender: () => void;
}; };
export function CoresButton(props: IProps): React.ReactElement { export function CoresButton(props: IProps): React.ReactElement {
const homeComputer = props.p.getHomeComputer(); const homeComputer = Player.getHomeComputer();
const maxCores = homeComputer.cpuCores >= 8; const maxCores = homeComputer.cpuCores >= 8;
if (maxCores) { if (maxCores) {
return <Button>Upgrade 'home' cores - MAX</Button>; return <Button>Upgrade 'home' cores - MAX</Button>;
} }
const cost = props.p.getUpgradeHomeCoresCost(); const cost = Player.getUpgradeHomeCoresCost();
function buy(): void { function buy(): void {
if (maxCores) return; if (maxCores) return;
if (!props.p.canAfford(cost)) return; if (!Player.canAfford(cost)) return;
props.p.loseMoney(cost, "servers"); Player.loseMoney(cost, "servers");
homeComputer.cpuCores++; homeComputer.cpuCores++;
props.rerender(); props.rerender();
} }
@ -38,9 +37,9 @@ export function CoresButton(props: IProps): React.ReactElement {
<i>"Cores increase the effectiveness of grow() and weaken() on 'home'"</i> <i>"Cores increase the effectiveness of grow() and weaken() on 'home'"</i>
</Typography> </Typography>
<br /> <br />
<Button disabled={!props.p.canAfford(cost)} onClick={buy}> <Button disabled={!Player.canAfford(cost)} onClick={buy}>
Upgrade 'home' cores ({homeComputer.cpuCores} -&gt; {homeComputer.cpuCores + 1}) -&nbsp; Upgrade 'home' cores ({homeComputer.cpuCores} -&gt; {homeComputer.cpuCores + 1}) -&nbsp;
<Money money={cost} player={props.p} /> <Money money={cost} forPurchase={true} />
</Button> </Button>
</span> </span>
</Tooltip> </Tooltip>

@ -27,7 +27,7 @@ import { isBackdoorInstalled } from "../../Server/ServerHelpers";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import { CorruptableText } from "../../ui/React/CorruptableText"; import { CorruptableText } from "../../ui/React/CorruptableText";
import { use } from "../../ui/Context"; import { Router } from "../../ui/GameRoot";
import { serverMetadata } from "../../Server/data/servers"; import { serverMetadata } from "../../Server/data/servers";
import { Tooltip } from "@mui/material"; import { Tooltip } from "@mui/material";
@ -36,8 +36,6 @@ type IProps = {
}; };
export function GenericLocation({ loc }: IProps): React.ReactElement { export function GenericLocation({ loc }: IProps): React.ReactElement {
const router = use.Router();
const player = use.Player();
/** /**
* Determine what needs to be rendered for this location based on the locations * Determine what needs to be rendered for this location based on the locations
* type. Returns an array of React components that should be rendered * type. Returns an array of React components that should be rendered
@ -50,11 +48,11 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
} }
if (loc.types.includes(LocationType.Gym)) { if (loc.types.includes(LocationType.Gym)) {
content.push(<GymLocation key={"gymlocation"} router={router} loc={loc} p={player} />); content.push(<GymLocation key={"gymlocation"} loc={loc} />);
} }
if (loc.types.includes(LocationType.Hospital)) { if (loc.types.includes(LocationType.Hospital)) {
content.push(<HospitalLocation key={"hospitallocation"} p={player} />); content.push(<HospitalLocation key={"hospitallocation"} />);
} }
if (loc.types.includes(LocationType.Slums)) { if (loc.types.includes(LocationType.Slums)) {
@ -70,7 +68,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
} }
if (loc.types.includes(LocationType.TravelAgency)) { if (loc.types.includes(LocationType.TravelAgency)) {
content.push(<TravelAgencyRoot key={"travelagencylocation"} p={player} router={router} />); content.push(<TravelAgencyRoot key={"travelagencylocation"} />);
} }
if (loc.types.includes(LocationType.University)) { if (loc.types.includes(LocationType.University)) {
@ -78,7 +76,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
} }
if (loc.types.includes(LocationType.Casino)) { if (loc.types.includes(LocationType.Casino)) {
content.push(<CasinoLocation key={"casinoLocation"} p={player} />); content.push(<CasinoLocation key={"casinoLocation"} />);
} }
return content; return content;
@ -92,7 +90,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
return ( return (
<> <>
<Button onClick={() => router.toCity()}>Return to World</Button> <Button onClick={() => Router.toCity()}>Return to World</Button>
<Typography variant="h4" sx={{ mt: 1 }}> <Typography variant="h4" sx={{ mt: 1 }}>
{backdoorInstalled && !Settings.DisableTextEffects ? ( {backdoorInstalled && !Settings.DisableTextEffects ? (
<Tooltip title={`Backdoor installed on ${loc.name}.`}> <Tooltip title={`Backdoor installed on ${loc.name}.`}>

@ -8,31 +8,29 @@ import Button from "@mui/material/Button";
import { Location } from "../Location"; import { Location } from "../Location";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { IRouter } from "../../ui/Router"; import { Router } from "../../ui/GameRoot";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { ClassWork, ClassType, Classes } from "../../Work/ClassWork"; import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/formulas/Class"; import { calculateCost } from "../../Work/formulas/Class";
type IProps = { type IProps = {
loc: Location; loc: Location;
p: IPlayer;
router: IRouter;
}; };
export function GymLocation(props: IProps): React.ReactElement { export function GymLocation(props: IProps): React.ReactElement {
function train(stat: ClassType): void { function train(stat: ClassType): void {
props.p.startWork( Player.startWork(
new ClassWork({ new ClassWork({
classType: stat, classType: stat,
location: props.loc.name, location: props.loc.name,
singularity: false, singularity: false,
}), }),
); );
props.p.startFocusing(); Player.startFocusing();
props.router.toWork(); Router.toWork();
} }
const cost = calculateCost(Classes[ClassType.GymStrength], props.loc); const cost = calculateCost(Classes[ClassType.GymStrength], props.loc);
@ -40,16 +38,16 @@ export function GymLocation(props: IProps): React.ReactElement {
return ( return (
<Box sx={{ display: "grid", width: "fit-content" }}> <Box sx={{ display: "grid", width: "fit-content" }}>
<Button onClick={() => train(ClassType.GymStrength)}> <Button onClick={() => train(ClassType.GymStrength)}>
Train Strength (<Money money={cost} player={props.p} /> / sec) Train Strength (<Money money={cost} forPurchase={true} /> / sec)
</Button> </Button>
<Button onClick={() => train(ClassType.GymDefense)}> <Button onClick={() => train(ClassType.GymDefense)}>
Train Defense (<Money money={cost} player={props.p} /> / sec) Train Defense (<Money money={cost} forPurchase={true} /> / sec)
</Button> </Button>
<Button onClick={() => train(ClassType.GymDexterity)}> <Button onClick={() => train(ClassType.GymDexterity)}>
Train Dexterity (<Money money={cost} player={props.p} /> / sec) Train Dexterity (<Money money={cost} forPurchase={true} /> / sec)
</Button> </Button>
<Button onClick={() => train(ClassType.GymAgility)}> <Button onClick={() => train(ClassType.GymAgility)}>
Train Agility (<Money money={cost} player={props.p} /> / sec) Train Agility (<Money money={cost} forPurchase={true} /> / sec)
</Button> </Button>
</Box> </Box>
); );

@ -6,16 +6,14 @@
import * as React from "react"; import * as React from "react";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { getHospitalizationCost } from "../../Hospital/Hospital"; import { getHospitalizationCost } from "../../Hospital/Hospital";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
type IProps = { type IProps = {};
p: IPlayer;
};
type IState = { type IState = {
currHp: number; currHp: number;
@ -34,12 +32,12 @@ export class HospitalLocation extends React.Component<IProps, IState> {
this.getHealed = this.getHealed.bind(this); this.getHealed = this.getHealed.bind(this);
this.state = { this.state = {
currHp: this.props.p.hp.current, currHp: Player.hp.current,
}; };
} }
getCost(): number { getCost(): number {
return getHospitalizationCost(this.props.p); return getHospitalizationCost();
} }
getHealed(e: React.MouseEvent<HTMLElement>): void { getHealed(e: React.MouseEvent<HTMLElement>): void {
@ -47,20 +45,20 @@ export class HospitalLocation extends React.Component<IProps, IState> {
return; return;
} }
if (this.props.p.hp.current < 0) { if (Player.hp.current < 0) {
this.props.p.hp.current = 0; Player.hp.current = 0;
} }
if (this.props.p.hp.current >= this.props.p.hp.max) { if (Player.hp.current >= Player.hp.max) {
return; return;
} }
const cost = this.getCost(); const cost = this.getCost();
this.props.p.loseMoney(cost, "hospitalization"); Player.loseMoney(cost, "hospitalization");
this.props.p.hp.current = this.props.p.hp.max; Player.hp.current = Player.hp.max;
// This just forces a re-render to update the cost // This just forces a re-render to update the cost
this.setState({ this.setState({
currHp: this.props.p.hp.current, currHp: Player.hp.current,
}); });
dialogBoxCreate( dialogBoxCreate(
@ -75,7 +73,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
return ( return (
<Button onClick={this.getHealed} style={this.btnStyle}> <Button onClick={this.getHealed} style={this.btnStyle}>
Get treatment for wounds - <Money money={cost} player={this.props.p} /> Get treatment for wounds - <Money money={cost} forPurchase={true} />
</Button> </Button>
); );
} }

@ -25,7 +25,7 @@ export function PurchaseServerModal(props: IProps): React.ReactElement {
const [hostname, setHostname] = useState(""); const [hostname, setHostname] = useState("");
function tryToPurchaseServer(): void { function tryToPurchaseServer(): void {
purchaseServer(hostname, props.ram, props.cost, player); purchaseServer(hostname, props.ram, props.cost);
props.onClose(); props.onClose();
} }
@ -41,7 +41,7 @@ export function PurchaseServerModal(props: IProps): React.ReactElement {
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography> <Typography>
Would you like to purchase a new server with {numeralWrapper.formatRAM(props.ram)} of RAM for{" "} Would you like to purchase a new server with {numeralWrapper.formatRAM(props.ram)} of RAM for{" "}
<Money money={props.cost} player={player} />? <Money money={props.cost} forPurchase={true} />?
</Typography> </Typography>
<br /> <br />
<br /> <br />

@ -4,7 +4,7 @@ import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases"; import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
@ -14,20 +14,19 @@ import { MathJaxWrapper } from "../../MathJaxWrapper";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
type IProps = { type IProps = {
p: IPlayer;
rerender: () => void; rerender: () => void;
}; };
export function RamButton(props: IProps): React.ReactElement { export function RamButton(props: IProps): React.ReactElement {
const homeComputer = props.p.getHomeComputer(); const homeComputer = Player.getHomeComputer();
if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) { if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) {
return <Button>Upgrade 'home' RAM - MAX</Button>; return <Button>Upgrade 'home' RAM - MAX</Button>;
} }
const cost = props.p.getUpgradeHomeRamCost(); const cost = Player.getUpgradeHomeRamCost();
function buy(): void { function buy(): void {
purchaseRamForHomeComputer(props.p); purchaseRamForHomeComputer();
props.rerender(); props.rerender();
} }
@ -45,10 +44,10 @@ export function RamButton(props: IProps): React.ReactElement {
<i>"More RAM means more scripts on 'home'"</i> <i>"More RAM means more scripts on 'home'"</i>
</Typography> </Typography>
<br /> <br />
<Button disabled={!props.p.canAfford(cost)} onClick={buy}> <Button disabled={!Player.canAfford(cost)} onClick={buy}>
Upgrade 'home' RAM ({numeralWrapper.formatRAM(homeComputer.maxRam)} -&gt;&nbsp; Upgrade 'home' RAM ({numeralWrapper.formatRAM(homeComputer.maxRam)} -&gt;&nbsp;
{numeralWrapper.formatRAM(homeComputer.maxRam * 2)}) -&nbsp; {numeralWrapper.formatRAM(homeComputer.maxRam * 2)}) -&nbsp;
<Money money={cost} player={props.p} /> <Money money={cost} forPurchase={true} />
</Button> </Button>
</span> </span>
</Tooltip> </Tooltip>

@ -15,7 +15,7 @@ import { CoresButton } from "./CoresButton";
import { getPurchaseServerCost } from "../../Server/ServerPurchases"; import { getPurchaseServerCost } from "../../Server/ServerPurchases";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { use } from "../../ui/Context"; import { Player } from "../../Player";
import { PurchaseServerModal } from "./PurchaseServerModal"; import { PurchaseServerModal } from "./PurchaseServerModal";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
@ -27,13 +27,12 @@ interface IServerProps {
function ServerButton(props: IServerProps): React.ReactElement { function ServerButton(props: IServerProps): React.ReactElement {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const player = use.Player();
const cost = getPurchaseServerCost(props.ram); const cost = getPurchaseServerCost(props.ram);
return ( return (
<> <>
<Button onClick={() => setOpen(true)} disabled={!player.canAfford(cost)}> <Button onClick={() => setOpen(true)} disabled={!Player.canAfford(cost)}>
Purchase {numeralWrapper.formatRAM(props.ram)} Server&nbsp;-&nbsp; Purchase {numeralWrapper.formatRAM(props.ram)} Server&nbsp;-&nbsp;
<Money money={cost} player={player} /> <Money money={cost} forPurchase={true} />
</Button> </Button>
<PurchaseServerModal <PurchaseServerModal
open={open} open={open}
@ -51,7 +50,6 @@ type IProps = {
}; };
export function TechVendorLocation(props: IProps): React.ReactElement { export function TechVendorLocation(props: IProps): React.ReactElement {
const player = use.Player();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
@ -76,11 +74,11 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
<i>"You can order bigger servers via scripts. We don't take custom orders in person."</i> <i>"You can order bigger servers via scripts. We don't take custom orders in person."</i>
</Typography> </Typography>
<br /> <br />
<TorButton p={player} rerender={rerender} /> <TorButton rerender={rerender} />
<br /> <br />
<RamButton p={player} rerender={rerender} /> <RamButton rerender={rerender} />
<br /> <br />
<CoresButton p={player} rerender={rerender} /> <CoresButton rerender={rerender} />
</> </>
); );
} }

@ -1,32 +1,62 @@
import React from "react"; import React from "react";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { purchaseTorRouter } from "../LocationsHelpers"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { GetServer } from "../../Server/AllServers";
import { SpecialServers } from "../../Server/data/SpecialServers";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
/**
* Attempt to purchase a TOR router using the button.
*/
export function purchaseTorRouter(): void {
if (Player.hasTorRouter()) {
dialogBoxCreate(`You already have a TOR Router!`);
return;
}
if (!Player.canAfford(CONSTANTS.TorRouterCost)) {
dialogBoxCreate("You cannot afford to purchase the TOR router!");
return;
}
Player.loseMoney(CONSTANTS.TorRouterCost, "other");
const darkweb = GetServer(SpecialServers.DarkWeb);
if (!darkweb) {
throw new Error("Dark web is not a server.");
}
Player.getHomeComputer().serversOnNetwork.push(darkweb.hostname);
darkweb.serversOnNetwork.push(Player.getHomeComputer().hostname);
dialogBoxCreate(
"You have purchased a TOR router!\n" +
"You now have access to the dark web from your home computer.\n" +
"Use the scan/scan-analyze commands to search for the dark web connection.",
);
}
type IProps = { type IProps = {
p: IPlayer;
rerender: () => void; rerender: () => void;
}; };
export function TorButton(props: IProps): React.ReactElement { export function TorButton(props: IProps): React.ReactElement {
function buy(): void { function buy(): void {
purchaseTorRouter(props.p); purchaseTorRouter();
props.rerender(); props.rerender();
} }
if (props.p.hasTorRouter()) { if (Player.hasTorRouter()) {
return <Button>TOR Router - Purchased</Button>; return <Button>TOR Router - Purchased</Button>;
} }
return ( return (
<Button disabled={!props.p.canAfford(CONSTANTS.TorRouterCost)} onClick={buy}> <Button disabled={!Player.canAfford(CONSTANTS.TorRouterCost)} onClick={buy}>
Purchase TOR router -&nbsp; Purchase TOR router -&nbsp;
<Money money={CONSTANTS.TorRouterCost} player={props.p} /> <Money money={CONSTANTS.TorRouterCost} forPurchase={true} />
</Button> </Button>
); );
} }

@ -9,11 +9,10 @@ import { CityName } from "../data/CityNames";
import { TravelConfirmationModal } from "./TravelConfirmationModal"; import { TravelConfirmationModal } from "./TravelConfirmationModal";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { IRouter } from "../../ui/Router"; import { Router } from "../../ui/GameRoot";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { WorldMap } from "../../ui/React/WorldMap"; import { WorldMap } from "../../ui/React/WorldMap";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
@ -22,26 +21,19 @@ import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
type IProps = { function travel(to: CityName): void {
p: IPlayer;
router: IRouter;
};
function travel(p: IPlayer, router: IRouter, to: CityName): void {
const cost = CONSTANTS.TravelCost; const cost = CONSTANTS.TravelCost;
if (!p.canAfford(cost)) { if (!Player.canAfford(cost)) {
return; return;
} }
p.loseMoney(cost, "other"); Player.loseMoney(cost, "other");
p.travel(to); Player.travel(to);
dialogBoxCreate(`You are now in ${to}!`); dialogBoxCreate(`You are now in ${to}!`);
router.toCity(); Router.toCity();
} }
export function TravelAgencyRoot(props: IProps): React.ReactElement { export function TravelAgencyRoot(): React.ReactElement {
const player = use.Player();
const router = use.Router();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [destination, setDestination] = useState(CityName.Sector12); const [destination, setDestination] = useState(CityName.Sector12);
@ -56,11 +48,11 @@ export function TravelAgencyRoot(props: IProps): React.ReactElement {
function startTravel(city: CityName): void { function startTravel(city: CityName): void {
const cost = CONSTANTS.TravelCost; const cost = CONSTANTS.TravelCost;
if (!player.canAfford(cost)) { if (!Player.canAfford(cost)) {
return; return;
} }
if (Settings.SuppressTravelConfirmation) { if (Settings.SuppressTravelConfirmation) {
travel(player, router, city); travel(city);
return; return;
} }
setOpen(true); setOpen(true);
@ -73,12 +65,12 @@ export function TravelAgencyRoot(props: IProps): React.ReactElement {
<Box mx={2}> <Box mx={2}>
<Typography> <Typography>
From here, you can travel to any other city! A ticket costs{" "} From here, you can travel to any other city! A ticket costs{" "}
<Money money={CONSTANTS.TravelCost} player={props.p} />. <Money money={CONSTANTS.TravelCost} forPurchase={true} />.
</Typography> </Typography>
{Settings.DisableASCIIArt ? ( {Settings.DisableASCIIArt ? (
<> <>
{Object.values(CityName) {Object.values(CityName)
.filter((city: string) => city != props.p.city) .filter((city: string) => city != Player.city)
.map((city: string) => { .map((city: string) => {
const match = Object.entries(CityName).find((entry) => entry[1] === city); const match = Object.entries(CityName).find((entry) => entry[1] === city);
if (match === undefined) throw new Error(`could not find key for city '${city}'`); if (match === undefined) throw new Error(`could not find key for city '${city}'`);
@ -93,12 +85,12 @@ export function TravelAgencyRoot(props: IProps): React.ReactElement {
})} })}
</> </>
) : ( ) : (
<WorldMap currentCity={props.p.city} onTravel={(city: CityName) => startTravel(city)} /> <WorldMap currentCity={Player.city} onTravel={(city: CityName) => startTravel(city)} />
)} )}
</Box> </Box>
<TravelConfirmationModal <TravelConfirmationModal
city={destination} city={destination}
travel={() => travel(player, router, destination)} travel={() => travel(destination)}
open={open} open={open}
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
/> />

@ -24,7 +24,7 @@ export function TravelConfirmationModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography> <Typography>
Would you like to travel to {props.city}? The trip will cost <Money money={cost} player={player} />. Would you like to travel to {props.city}? The trip will cost <Money money={cost} forPurchase={true} />.
</Typography> </Typography>
<br /> <br />
<br /> <br />

@ -53,31 +53,31 @@ export function UniversityLocation(props: IProps): React.ReactElement {
<Tooltip title={earnHackingExpTooltip}> <Tooltip title={earnHackingExpTooltip}>
<Button onClick={() => take(ClassType.DataStructures)}> <Button onClick={() => take(ClassType.DataStructures)}>
Take Data Structures course ( Take Data Structures course (
<Money money={dataStructuresCost} player={player} /> / sec) <Money money={dataStructuresCost} forPurchase={true} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnHackingExpTooltip}> <Tooltip title={earnHackingExpTooltip}>
<Button onClick={() => take(ClassType.Networks)}> <Button onClick={() => take(ClassType.Networks)}>
Take Networks course ( Take Networks course (
<Money money={networksCost} player={player} /> / sec) <Money money={networksCost} forPurchase={true} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnHackingExpTooltip}> <Tooltip title={earnHackingExpTooltip}>
<Button onClick={() => take(ClassType.Algorithms)}> <Button onClick={() => take(ClassType.Algorithms)}>
Take Algorithms course ( Take Algorithms course (
<Money money={algorithmsCost} player={player} /> / sec) <Money money={algorithmsCost} forPurchase={true} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnCharismaExpTooltip}> <Tooltip title={earnCharismaExpTooltip}>
<Button onClick={() => take(ClassType.Management)}> <Button onClick={() => take(ClassType.Management)}>
Take Management course ( Take Management course (
<Money money={managementCost} player={player} /> / sec) <Money money={managementCost} forPurchase={true} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnCharismaExpTooltip}> <Tooltip title={earnCharismaExpTooltip}>
<Button onClick={() => take(ClassType.Leadership)}> <Button onClick={() => take(ClassType.Leadership)}>
Take Leadership course ( Take Leadership course (
<Money money={leadershipCost} player={player} /> / sec) <Money money={leadershipCost} forPurchase={true} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
</Box> </Box>

@ -1,4 +1,4 @@
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Player } from "../../Player";
import { Milestones } from "../Milestones"; import { Milestones } from "../Milestones";
import { Milestone } from "../Milestone"; import { Milestone } from "../Milestone";
import * as React from "react"; import * as React from "react";
@ -6,26 +6,22 @@ import * as React from "react";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
interface IProps { function highestMilestone(milestones: Milestone[]): number {
player: IPlayer;
}
function highestMilestone(p: IPlayer, milestones: Milestone[]): number {
let n = -1; let n = -1;
for (let i = 0; i < milestones.length; i++) { for (let i = 0; i < milestones.length; i++) {
if (milestones[i].fulfilled(p)) n = i; if (milestones[i].fulfilled(Player)) n = i;
} }
return n; return n;
} }
export function MilestonesRoot(props: IProps): JSX.Element { export function MilestonesRoot(): JSX.Element {
const n = highestMilestone(props.player, Milestones); const n = highestMilestone(Milestones);
const milestones = Milestones.map((milestone: Milestone, i: number) => { const milestones = Milestones.map((milestone: Milestone, i: number) => {
if (i <= n + 1) { if (i <= n + 1) {
return ( return (
<Typography key={i}> <Typography key={i}>
[{milestone.fulfilled(props.player) ? "x" : " "}] {milestone.title} [{milestone.fulfilled(Player) ? "x" : " "}] {milestone.title}
</Typography> </Typography>
); );
} }

@ -390,7 +390,7 @@ function hack(
const hackingTime = calculateHackingTime(server, Player); // This is in seconds const hackingTime = calculateHackingTime(server, Player); // This is in seconds
// No root access or skill level too low // No root access or skill level too low
const canHack = netscriptCanHack(server, Player); const canHack = netscriptCanHack(server);
if (!canHack.res) { if (!canHack.res) {
throw makeRuntimeErrorMsg(ctx, canHack.msg || ""); throw makeRuntimeErrorMsg(ctx, canHack.msg || "");
} }

@ -1,4 +1,4 @@
import { Player as player } from "../Player"; import { Player } from "../Player";
import { Bladeburner } from "../Bladeburner/Bladeburner"; import { Bladeburner } from "../Bladeburner/Bladeburner";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Bladeburner as INetscriptBladeburner, BladeburnerCurAction } from "../ScriptEditor/NetscriptDefinitions"; import { Bladeburner as INetscriptBladeburner, BladeburnerCurAction } from "../ScriptEditor/NetscriptDefinitions";
@ -8,29 +8,23 @@ import { BlackOperation } from "../Bladeburner/BlackOperation";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> { export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
const checkBladeburnerAccess = function (ctx: NetscriptContext, skipjoined = false): void { const checkBladeburnerAccess = function (ctx: NetscriptContext): void {
const bladeburner = player.bladeburner; getBladeburner(ctx);
if (bladeburner === null) throw new Error("Must have joined bladeburner"); return;
const apiAccess = };
player.bitNodeN === 7 || const getBladeburner = function (ctx: NetscriptContext): Bladeburner {
player.sourceFiles.some((a) => { const apiAccess = Player.bitNodeN === 7 || Player.sourceFiles.some((a) => a.n === 7);
return a.n === 7;
});
if (!apiAccess) { if (!apiAccess) {
const apiDenied = `You do not currently have access to the Bladeburner API. You must either be in BitNode-7 or have Source-File 7.`; throw helpers.makeRuntimeErrorMsg(ctx, "You have not unlocked the bladeburner API.", "API ACCESS");
throw helpers.makeRuntimeErrorMsg(ctx, apiDenied);
}
if (!skipjoined) {
const bladeburnerAccess = bladeburner instanceof Bladeburner;
if (!bladeburnerAccess) {
const bladeburnerDenied = `You must be a member of the Bladeburner division to use this API.`;
throw helpers.makeRuntimeErrorMsg(ctx, bladeburnerDenied);
}
} }
const bladeburner = Player.bladeburner;
if (!bladeburner)
throw helpers.makeRuntimeErrorMsg(ctx, "You must be a member of the Bladeburner division to use this API.");
return bladeburner;
}; };
const checkBladeburnerCity = function (ctx: NetscriptContext, city: string): void { const checkBladeburnerCity = function (ctx: NetscriptContext, city: string): void {
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner"); if (bladeburner === null) throw new Error("Must have joined bladeburner");
if (!bladeburner.cities.hasOwnProperty(city)) { if (!bladeburner.cities.hasOwnProperty(city)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city: ${city}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city: ${city}`);
@ -38,7 +32,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
}; };
const getBladeburnerActionObject = function (ctx: NetscriptContext, type: string, name: string): IAction { const getBladeburnerActionObject = function (ctx: NetscriptContext, type: string, name: string): IAction {
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner"); if (bladeburner === null) throw new Error("Must have joined bladeburner");
const actionId = bladeburner.getActionIdFromTypeAndName(type, name); const actionId = bladeburner.getActionIdFromTypeAndName(type, name);
if (!actionId) { if (!actionId) {
@ -54,21 +48,15 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
return { return {
getContractNames: (ctx: NetscriptContext) => (): string[] => { getContractNames: (ctx: NetscriptContext) => (): string[] => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getContractNamesNetscriptFn(); return bladeburner.getContractNamesNetscriptFn();
}, },
getOperationNames: (ctx: NetscriptContext) => (): string[] => { getOperationNames: (ctx: NetscriptContext) => (): string[] => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getOperationNamesNetscriptFn(); return bladeburner.getOperationNamesNetscriptFn();
}, },
getBlackOpNames: (ctx: NetscriptContext) => (): string[] => { getBlackOpNames: (ctx: NetscriptContext) => (): string[] => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getBlackOpNamesNetscriptFn(); return bladeburner.getBlackOpNamesNetscriptFn();
}, },
getBlackOpRank: getBlackOpRank:
@ -81,15 +69,11 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
return action.reqdRank; return action.reqdRank;
}, },
getGeneralActionNames: (ctx: NetscriptContext) => (): string[] => { getGeneralActionNames: (ctx: NetscriptContext) => (): string[] => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getGeneralActionNamesNetscriptFn(); return bladeburner.getGeneralActionNamesNetscriptFn();
}, },
getSkillNames: (ctx: NetscriptContext) => (): string[] => { getSkillNames: (ctx: NetscriptContext) => (): string[] => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getSkillNamesNetscriptFn(); return bladeburner.getSkillNamesNetscriptFn();
}, },
startAction: startAction:
@ -97,25 +81,19 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
(_type: unknown, _name: unknown): boolean => { (_type: unknown, _name: unknown): boolean => {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.startActionNetscriptFn(player, type, name, ctx.workerScript); return bladeburner.startActionNetscriptFn(type, name, ctx.workerScript);
} catch (e: unknown) { } catch (e: unknown) {
throw helpers.makeRuntimeErrorMsg(ctx, String(e)); throw helpers.makeRuntimeErrorMsg(ctx, String(e));
} }
}, },
stopBladeburnerAction: (ctx: NetscriptContext) => (): void => { stopBladeburnerAction: (ctx: NetscriptContext) => (): void => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.resetAction(); return bladeburner.resetAction();
}, },
getCurrentAction: (ctx: NetscriptContext) => (): BladeburnerCurAction => { getCurrentAction: (ctx: NetscriptContext) => (): BladeburnerCurAction => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.getTypeAndNameFromActionId(bladeburner.action); return bladeburner.getTypeAndNameFromActionId(bladeburner.action);
}, },
getActionTime: getActionTime:
@ -123,11 +101,9 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
(_type: unknown, _name: unknown): number => { (_type: unknown, _name: unknown): number => {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
const time = bladeburner.getActionTimeNetscriptFn(player, type, name); const time = bladeburner.getActionTimeNetscriptFn(Player, type, name);
if (typeof time === "string") { if (typeof time === "string") {
const errorLogText = `Invalid action: type='${type}' name='${name}'`; const errorLogText = `Invalid action: type='${type}' name='${name}'`;
helpers.log(ctx, () => errorLogText); helpers.log(ctx, () => errorLogText);
@ -140,9 +116,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
} }
}, },
getActionCurrentTime: (ctx: NetscriptContext) => (): number => { getActionCurrentTime: (ctx: NetscriptContext) => (): number => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
const timecomputed = const timecomputed =
Math.min(bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow, bladeburner.actionTimeToComplete) * Math.min(bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow, bladeburner.actionTimeToComplete) *
@ -157,11 +131,9 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
(_type: unknown, _name: unknown): [number, number] => { (_type: unknown, _name: unknown): [number, number] => {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
const chance = bladeburner.getActionEstimatedSuccessChanceNetscriptFn(player, type, name); const chance = bladeburner.getActionEstimatedSuccessChanceNetscriptFn(Player, type, name);
if (typeof chance === "string") { if (typeof chance === "string") {
const errorLogText = `Invalid action: type='${type}' name='${name}'`; const errorLogText = `Invalid action: type='${type}' name='${name}'`;
helpers.log(ctx, () => errorLogText); helpers.log(ctx, () => errorLogText);
@ -195,9 +167,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
(_type: unknown, _name: unknown): number => { (_type: unknown, _name: unknown): number => {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getActionCountRemainingNetscriptFn(type, name, ctx.workerScript); return bladeburner.getActionCountRemainingNetscriptFn(type, name, ctx.workerScript);
} catch (e: unknown) { } catch (e: unknown) {
@ -255,24 +225,18 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
action.level = level; action.level = level;
}, },
getRank: (ctx: NetscriptContext) => (): number => { getRank: (ctx: NetscriptContext) => (): number => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.rank; return bladeburner.rank;
}, },
getSkillPoints: (ctx: NetscriptContext) => (): number => { getSkillPoints: (ctx: NetscriptContext) => (): number => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.skillPoints; return bladeburner.skillPoints;
}, },
getSkillLevel: getSkillLevel:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
(_skillName: unknown): number => { (_skillName: unknown): number => {
const skillName = helpers.string(ctx, "skillName", _skillName); const skillName = helpers.string(ctx, "skillName", _skillName);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getSkillLevelNetscriptFn(skillName, ctx.workerScript); return bladeburner.getSkillLevelNetscriptFn(skillName, ctx.workerScript);
} catch (e: unknown) { } catch (e: unknown) {
@ -284,9 +248,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
(_skillName: unknown, _count: unknown = 1): number => { (_skillName: unknown, _count: unknown = 1): number => {
const skillName = helpers.string(ctx, "skillName", _skillName); const skillName = helpers.string(ctx, "skillName", _skillName);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, count, ctx.workerScript); return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, count, ctx.workerScript);
} catch (e: unknown) { } catch (e: unknown) {
@ -298,9 +260,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
(_skillName: unknown, _count: unknown = 1): boolean => { (_skillName: unknown, _count: unknown = 1): boolean => {
const skillName = helpers.string(ctx, "skillName", _skillName); const skillName = helpers.string(ctx, "skillName", _skillName);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.upgradeSkillNetscriptFn(skillName, count, ctx.workerScript); return bladeburner.upgradeSkillNetscriptFn(skillName, count, ctx.workerScript);
} catch (e: unknown) { } catch (e: unknown) {
@ -312,9 +272,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
(_type: unknown, _name: unknown): number => { (_type: unknown, _name: unknown): number => {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.getTeamSizeNetscriptFn(type, name, ctx.workerScript); return bladeburner.getTeamSizeNetscriptFn(type, name, ctx.workerScript);
} catch (e: unknown) { } catch (e: unknown) {
@ -327,9 +285,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
const type = helpers.string(ctx, "type", _type); const type = helpers.string(ctx, "type", _type);
const name = helpers.string(ctx, "name", _name); const name = helpers.string(ctx, "name", _name);
const size = helpers.number(ctx, "size", _size); const size = helpers.number(ctx, "size", _size);
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try { try {
return bladeburner.setTeamSizeNetscriptFn(type, name, size, ctx.workerScript); return bladeburner.setTeamSizeNetscriptFn(type, name, size, ctx.workerScript);
} catch (e: unknown) { } catch (e: unknown) {
@ -342,7 +298,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
const cityName = helpers.string(ctx, "cityName", _cityName); const cityName = helpers.string(ctx, "cityName", _cityName);
checkBladeburnerAccess(ctx); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName); checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].popEst; return bladeburner.cities[cityName].popEst;
}, },
@ -352,7 +308,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
const cityName = helpers.string(ctx, "cityName", _cityName); const cityName = helpers.string(ctx, "cityName", _cityName);
checkBladeburnerAccess(ctx); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName); checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].comms; return bladeburner.cities[cityName].comms;
}, },
@ -362,14 +318,12 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
const cityName = helpers.string(ctx, "cityName", _cityName); const cityName = helpers.string(ctx, "cityName", _cityName);
checkBladeburnerAccess(ctx); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName); checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.cities[cityName].chaos; return bladeburner.cities[cityName].chaos;
}, },
getCity: (ctx: NetscriptContext) => (): string => { getCity: (ctx: NetscriptContext) => (): string => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.city; return bladeburner.city;
}, },
switchCity: switchCity:
@ -378,37 +332,33 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
const cityName = helpers.string(ctx, "cityName", _cityName); const cityName = helpers.string(ctx, "cityName", _cityName);
checkBladeburnerAccess(ctx); checkBladeburnerAccess(ctx);
checkBladeburnerCity(ctx, cityName); checkBladeburnerCity(ctx, cityName);
const bladeburner = player.bladeburner; const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
bladeburner.city = cityName; bladeburner.city = cityName;
return true; return true;
}, },
getStamina: (ctx: NetscriptContext) => (): [number, number] => { getStamina: (ctx: NetscriptContext) => (): [number, number] => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return [bladeburner.stamina, bladeburner.maxStamina]; return [bladeburner.stamina, bladeburner.maxStamina];
}, },
joinBladeburnerFaction: (ctx: NetscriptContext) => (): boolean => { joinBladeburnerFaction: (ctx: NetscriptContext) => (): boolean => {
checkBladeburnerAccess(ctx, true); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return bladeburner.joinBladeburnerFactionNetscriptFn(ctx.workerScript); return bladeburner.joinBladeburnerFactionNetscriptFn(ctx.workerScript);
}, },
joinBladeburnerDivision: (ctx: NetscriptContext) => (): boolean => { joinBladeburnerDivision: (ctx: NetscriptContext) => (): boolean => {
if (player.bitNodeN === 7 || player.sourceFileLvl(7) > 0) { if (Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0) {
if (BitNodeMultipliers.BladeburnerRank === 0) { if (BitNodeMultipliers.BladeburnerRank === 0) {
return false; // Disabled in this bitnode return false; // Disabled in this bitnode
} }
if (player.bladeburner instanceof Bladeburner) { if (Player.bladeburner instanceof Bladeburner) {
return true; // Already member return true; // Already member
} else if ( } else if (
player.skills.strength >= 100 && Player.skills.strength >= 100 &&
player.skills.defense >= 100 && Player.skills.defense >= 100 &&
player.skills.dexterity >= 100 && Player.skills.dexterity >= 100 &&
player.skills.agility >= 100 Player.skills.agility >= 100
) { ) {
player.bladeburner = new Bladeburner(player); Player.bladeburner = new Bladeburner();
helpers.log(ctx, () => "You have been accepted into the Bladeburner division"); helpers.log(ctx, () => "You have been accepted into the Bladeburner division");
return true; return true;
@ -420,9 +370,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
return false; return false;
}, },
getBonusTime: (ctx: NetscriptContext) => (): number => { getBonusTime: (ctx: NetscriptContext) => (): number => {
checkBladeburnerAccess(ctx); const bladeburner = getBladeburner(ctx);
const bladeburner = player.bladeburner;
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
return Math.round(bladeburner.storedCycles / 5) * 1000; return Math.round(bladeburner.storedCycles / 5) * 1000;
}, },
}; };

@ -1047,14 +1047,14 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
(_numShares: unknown): number => { (_numShares: unknown): number => {
checkAccess(ctx); checkAccess(ctx);
const numShares = helpers.number(ctx, "numShares", _numShares); const numShares = helpers.number(ctx, "numShares", _numShares);
return SellShares(getCorporation(), player, numShares); return SellShares(getCorporation(), numShares);
}, },
buyBackShares: buyBackShares:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
(_numShares: unknown): boolean => { (_numShares: unknown): boolean => {
checkAccess(ctx); checkAccess(ctx);
const numShares = helpers.number(ctx, "numShares", _numShares); const numShares = helpers.number(ctx, "numShares", _numShares);
return BuyBackShares(getCorporation(), player, numShares); return BuyBackShares(getCorporation(), numShares);
}, },
bribe: bribe:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>

@ -410,11 +410,11 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
}, },
classGains: classGains:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
(_player: unknown, _classType: unknown, _locationName: unknown): WorkStats => { (_person: unknown, _classType: unknown, _locationName: unknown): WorkStats => {
const target = helpers.player(ctx, _player); const person = helpers.player(ctx, _person);
const classType = helpers.string(ctx, "classType", _classType); const classType = helpers.string(ctx, "classType", _classType);
const locationName = helpers.string(ctx, "locationName", _locationName); const locationName = helpers.string(ctx, "locationName", _locationName);
return calculateClassEarnings(player, target, classType as ClassType, locationName as LocationName); return calculateClassEarnings(person, classType as ClassType, locationName as LocationName);
}, },
factionGains: factionGains:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>

@ -26,7 +26,7 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
(_augName: unknown): number => { (_augName: unknown): number => {
const augName = helpers.string(ctx, "augName", _augName); const augName = helpers.string(ctx, "augName", _augName);
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { if (!getGraftingAvailableAugs().includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
} }
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
@ -38,16 +38,16 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
(_augName: string): number => { (_augName: string): number => {
const augName = helpers.string(ctx, "augName", _augName); const augName = helpers.string(ctx, "augName", _augName);
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { if (!getGraftingAvailableAugs().includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
} }
const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]); const graftableAug = new GraftableAugmentation(StaticAugmentations[augName]);
return calculateGraftingTimeWithBonus(player, graftableAug); return calculateGraftingTimeWithBonus(graftableAug);
}, },
getGraftableAugmentations: (ctx: NetscriptContext) => (): string[] => { getGraftableAugmentations: (ctx: NetscriptContext) => (): string[] => {
checkGraftingAPIAccess(ctx); checkGraftingAPIAccess(ctx);
const graftableAugs = getGraftingAvailableAugs(player); const graftableAugs = getGraftingAvailableAugs();
return graftableAugs; return graftableAugs;
}, },
@ -60,7 +60,7 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
if (player.city !== CityName.NewTokyo) { if (player.city !== CityName.NewTokyo) {
throw helpers.makeRuntimeErrorMsg(ctx, "You must be in New Tokyo to begin grafting an Augmentation."); throw helpers.makeRuntimeErrorMsg(ctx, "You must be in New Tokyo to begin grafting an Augmentation.");
} }
if (!getGraftingAvailableAugs(player).includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) { if (!getGraftingAvailableAugs().includes(augName) || !StaticAugmentations.hasOwnProperty(augName)) {
helpers.log(ctx, () => `Invalid aug: ${augName}`); helpers.log(ctx, () => `Invalid aug: ${augName}`);
return false; return false;
} }
@ -82,7 +82,6 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
new GraftingWork({ new GraftingWork({
singularity: true, singularity: true,
augmentation: augName, augmentation: augName,
player: player,
}), }),
); );

@ -29,7 +29,7 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
throw helpers.makeRuntimeErrorMsg(ctx, "Index specified for Hacknet Node is out-of-bounds: " + i); throw helpers.makeRuntimeErrorMsg(ctx, "Index specified for Hacknet Node is out-of-bounds: " + i);
} }
if (hasHacknetServers(player)) { if (hasHacknetServers()) {
const hi = player.hacknetNodes[i]; const hi = player.hacknetNodes[i];
if (typeof hi !== "string") throw new Error("hacknet node was not a string"); if (typeof hi !== "string") throw new Error("hacknet node was not a string");
const hserver = GetServer(hi); const hserver = GetServer(hi);
@ -54,19 +54,19 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
return player.hacknetNodes.length; return player.hacknetNodes.length;
}, },
maxNumNodes: () => (): number => { maxNumNodes: () => (): number => {
if (hasHacknetServers(player)) { if (hasHacknetServers()) {
return HacknetServerConstants.MaxServers; return HacknetServerConstants.MaxServers;
} }
return Infinity; return Infinity;
}, },
purchaseNode: () => (): number => { purchaseNode: () => (): number => {
return purchaseHacknet(player); return purchaseHacknet();
}, },
getPurchaseNodeCost: () => (): number => { getPurchaseNodeCost: () => (): number => {
if (hasHacknetServers(player)) { if (hasHacknetServers()) {
return getCostOfNextHacknetServer(player); return getCostOfNextHacknetServer();
} else { } else {
return getCostOfNextHacknetNode(player); return getCostOfNextHacknetNode();
} }
}, },
getNodeStats: getNodeStats:
@ -74,7 +74,7 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
(_i: unknown): NodeStats => { (_i: unknown): NodeStats => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
const hasUpgraded = hasHacknetServers(player); const hasUpgraded = hasHacknetServers();
const res: NodeStats = { const res: NodeStats = {
name: node instanceof HacknetServer ? node.hostname : node.name, name: node instanceof HacknetServer ? node.hostname : node.name,
level: node.level, level: node.level,
@ -99,7 +99,7 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return purchaseLevelUpgrade(player, node, n); return purchaseLevelUpgrade(node, n);
}, },
upgradeRam: upgradeRam:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -107,7 +107,7 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return purchaseRamUpgrade(player, node, n); return purchaseRamUpgrade(node, n);
}, },
upgradeCore: upgradeCore:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -115,14 +115,14 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
return purchaseCoreUpgrade(player, node, n); return purchaseCoreUpgrade(node, n);
}, },
upgradeCache: upgradeCache:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
(_i: unknown, _n: unknown = 1): boolean => { (_i: unknown, _n: unknown = 1): boolean => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return false; return false;
} }
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
@ -130,9 +130,9 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
helpers.log(ctx, () => "Can only be called on hacknet servers"); helpers.log(ctx, () => "Can only be called on hacknet servers");
return false; return false;
} }
const res = purchaseCacheUpgrade(player, node, n); const res = purchaseCacheUpgrade(node, n);
if (res) { if (res) {
updateHashManagerCapacity(player); updateHashManagerCapacity();
} }
return res; return res;
}, },
@ -165,7 +165,7 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
(_i: unknown, _n: unknown = 1): number => { (_i: unknown, _n: unknown = 1): number => {
const i = helpers.number(ctx, "i", _i); const i = helpers.number(ctx, "i", _i);
const n = helpers.number(ctx, "n", _n); const n = helpers.number(ctx, "n", _n);
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return Infinity; return Infinity;
} }
const node = getHacknetNode(ctx, i); const node = getHacknetNode(ctx, i);
@ -176,13 +176,13 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
return node.calculateCacheUpgradeCost(n); return node.calculateCacheUpgradeCost(n);
}, },
numHashes: () => (): number => { numHashes: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return 0; return 0;
} }
return player.hashManager.hashes; return player.hashManager.hashes;
}, },
hashCapacity: () => (): number => { hashCapacity: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return 0; return 0;
} }
return player.hashManager.capacity; return player.hashManager.capacity;
@ -192,7 +192,7 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
(_upgName: unknown, _count: unknown = 1): number => { (_upgName: unknown, _count: unknown = 1): number => {
const upgName = helpers.string(ctx, "upgName", _upgName); const upgName = helpers.string(ctx, "upgName", _upgName);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return Infinity; return Infinity;
} }
@ -204,13 +204,13 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
const upgName = helpers.string(ctx, "upgName", _upgName); const upgName = helpers.string(ctx, "upgName", _upgName);
const upgTarget = helpers.string(ctx, "upgTarget", _upgTarget); const upgTarget = helpers.string(ctx, "upgTarget", _upgTarget);
const count = helpers.number(ctx, "count", _count); const count = helpers.number(ctx, "count", _count);
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return false; return false;
} }
return purchaseHashUpgrade(player, upgName, upgTarget, count); return purchaseHashUpgrade(upgName, upgTarget, count);
}, },
getHashUpgrades: () => (): string[] => { getHashUpgrades: () => (): string[] => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return []; return [];
} }
return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name); return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name);
@ -226,13 +226,13 @@ export function NetscriptHacknet(): InternalAPI<IHacknet> {
return level; return level;
}, },
getStudyMult: () => (): number => { getStudyMult: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return 1; return 1;
} }
return player.hashManager.getStudyMult(); return player.hashManager.getStudyMult();
}, },
getTrainingMult: () => (): number => { getTrainingMult: () => (): number => {
if (!hasHacknetServers(player)) { if (!hasHacknetServers()) {
return 1; return 1;
} }
return player.hashManager.getTrainingMult(); return player.hashManager.getTrainingMult();

File diff suppressed because it is too large Load Diff

@ -1,4 +1,4 @@
import { Player as player } from "../Player"; import { Player } from "../Player";
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers"; import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
@ -21,7 +21,7 @@ import { helpers } from "../Netscript/NetscriptHelpers";
export function NetscriptSleeve(): InternalAPI<ISleeve> { export function NetscriptSleeve(): InternalAPI<ISleeve> {
const checkSleeveAPIAccess = function (ctx: NetscriptContext): void { const checkSleeveAPIAccess = function (ctx: NetscriptContext): void {
if (player.bitNodeN !== 10 && !player.sourceFileLvl(10)) { if (Player.bitNodeN !== 10 && !Player.sourceFileLvl(10)) {
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
"You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10", "You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10",
@ -30,7 +30,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
}; };
const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number): void { const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number): void {
if (sleeveNumber >= player.sleeves.length || sleeveNumber < 0) { if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) {
const msg = `Invalid sleeve number: ${sleeveNumber}`; const msg = `Invalid sleeve number: ${sleeveNumber}`;
helpers.log(ctx, () => msg); helpers.log(ctx, () => msg);
throw helpers.makeRuntimeErrorMsg(ctx, msg); throw helpers.makeRuntimeErrorMsg(ctx, msg);
@ -38,7 +38,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
}; };
const getSleeveStats = function (sleeveNumber: number): SleeveSkills { const getSleeveStats = function (sleeveNumber: number): SleeveSkills {
const sl = player.sleeves[sleeveNumber]; const sl = Player.sleeves[sleeveNumber];
return { return {
shock: 100 - sl.shock, shock: 100 - sl.shock,
sync: sl.sync, sync: sl.sync,
@ -55,7 +55,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
return { return {
getNumSleeves: (ctx: NetscriptContext) => (): number => { getNumSleeves: (ctx: NetscriptContext) => (): number => {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
return player.sleeves.length; return Player.sleeves.length;
}, },
setToShockRecovery: setToShockRecovery:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -63,7 +63,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].shockRecovery(player); return Player.sleeves[sleeveNumber].shockRecovery();
}, },
setToSynchronize: setToSynchronize:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -71,7 +71,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].synchronize(player); return Player.sleeves[sleeveNumber].synchronize();
}, },
setToCommitCrime: setToCommitCrime:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -84,7 +84,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
if (crime === null) { if (crime === null) {
return false; return false;
} }
return player.sleeves[sleeveNumber].commitCrime(player, crime.name); return Player.sleeves[sleeveNumber].commitCrime(crime.name);
}, },
setToUniversityCourse: setToUniversityCourse:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -94,7 +94,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
const className = helpers.string(ctx, "className", _className); const className = helpers.string(ctx, "className", _className);
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].takeUniversityCourse(player, universityName, className); return Player.sleeves[sleeveNumber].takeUniversityCourse(universityName, className);
}, },
travel: travel:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -104,7 +104,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
if (checkEnum(CityName, cityName)) { if (checkEnum(CityName, cityName)) {
return player.sleeves[sleeveNumber].travel(player, cityName); return Player.sleeves[sleeveNumber].travel(cityName);
} else { } else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city name: '${cityName}'.`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city name: '${cityName}'.`);
} }
@ -118,11 +118,11 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
// Cannot work at the same company that another sleeve is working at // Cannot work at the same company that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) { if (i === sleeveNumber) {
continue; continue;
} }
const other = player.sleeves[i]; const other = Player.sleeves[i];
if (isSleeveCompanyWork(other.currentWork) && other.currentWork.companyName === companyName) { if (isSleeveCompanyWork(other.currentWork) && other.currentWork.companyName === companyName) {
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
@ -131,7 +131,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
} }
} }
return player.sleeves[sleeveNumber].workForCompany(player, companyName); return Player.sleeves[sleeveNumber].workForCompany(companyName);
}, },
setToFactionWork: setToFactionWork:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -143,11 +143,11 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
// Cannot work at the same faction that another sleeve is working at // Cannot work at the same faction that another sleeve is working at
for (let i = 0; i < player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) { if (i === sleeveNumber) {
continue; continue;
} }
const other = player.sleeves[i]; const other = Player.sleeves[i];
if (isSleeveFactionWork(other.currentWork) && other.currentWork.factionName === factionName) { if (isSleeveFactionWork(other.currentWork) && other.currentWork.factionName === factionName) {
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
@ -156,14 +156,14 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
} }
} }
if (player.gang && player.gang.facName == factionName) { if (Player.gang && Player.gang.facName == factionName) {
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
`Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`, `Sleeve ${sleeveNumber} cannot work for faction ${factionName} because you have started a gang with them.`,
); );
} }
return player.sleeves[sleeveNumber].workForFaction(player, factionName, workType); return Player.sleeves[sleeveNumber].workForFaction(factionName, workType);
}, },
setToGymWorkout: setToGymWorkout:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -174,7 +174,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
return player.sleeves[sleeveNumber].workoutAtGym(player, gymName, stat); return Player.sleeves[sleeveNumber].workoutAtGym(gymName, stat);
}, },
getSleeveStats: getSleeveStats:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -191,7 +191,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
const sl = player.sleeves[sleeveNumber]; const sl = Player.sleeves[sleeveNumber];
if (sl.currentWork === null) return null; if (sl.currentWork === null) return null;
return sl.currentWork.APICopy(); return sl.currentWork.APICopy();
}, },
@ -202,13 +202,13 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
const sl = player.sleeves[sleeveNumber]; const sl = Player.sleeves[sleeveNumber];
return { return {
tor: false, tor: false,
city: sl.city, city: sl.city,
hp: sl.hp, hp: sl.hp,
jobs: Object.keys(player.jobs), // technically sleeves have the same jobs as the player. jobs: Object.keys(Player.jobs), // technically sleeves have the same jobs as the player.
jobTitle: Object.values(player.jobs), jobTitle: Object.values(Player.jobs),
mult: { mult: {
agility: sl.mults.agility, agility: sl.mults.agility,
@ -239,8 +239,8 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
const augs = []; const augs = [];
for (let i = 0; i < player.sleeves[sleeveNumber].augmentations.length; i++) { for (let i = 0; i < Player.sleeves[sleeveNumber].augmentations.length; i++) {
augs.push(player.sleeves[sleeveNumber].augmentations[i].name); augs.push(Player.sleeves[sleeveNumber].augmentations[i].name);
} }
return augs; return augs;
}, },
@ -251,7 +251,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber); checkSleeveNumber(ctx, sleeveNumber);
const purchasableAugs = findSleevePurchasableAugs(player.sleeves[sleeveNumber], player); const purchasableAugs = findSleevePurchasableAugs(Player.sleeves[sleeveNumber]);
const augs = []; const augs = [];
for (let i = 0; i < purchasableAugs.length; i++) { for (let i = 0; i < purchasableAugs.length; i++) {
const aug = purchasableAugs[i]; const aug = purchasableAugs[i];
@ -280,7 +280,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid aug: ${augName}`);
} }
return player.sleeves[sleeveNumber].tryBuyAugmentation(player, aug); return Player.sleeves[sleeveNumber].tryBuyAugmentation(aug);
}, },
getSleeveAugmentationPrice: getSleeveAugmentationPrice:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -296,7 +296,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveAPIAccess(ctx); checkSleeveAPIAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName); const augName = helpers.string(ctx, "augName", _augName);
const aug: Augmentation = StaticAugmentations[augName]; const aug: Augmentation = StaticAugmentations[augName];
return aug.getCost(player).repCost; return aug.getCost().repCost;
}, },
setToBladeburnerAction: setToBladeburnerAction:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -314,11 +314,11 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
// Cannot Take on Contracts if another sleeve is performing that action // Cannot Take on Contracts if another sleeve is performing that action
if (action === "Take on contracts") { if (action === "Take on contracts") {
for (let i = 0; i < player.sleeves.length; ++i) { for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) { if (i === sleeveNumber) {
continue; continue;
} }
const other = player.sleeves[i]; const other = Player.sleeves[i];
if (isSleeveBladeburnerWork(other.currentWork) && other.currentWork.actionName === contract) { if (isSleeveBladeburnerWork(other.currentWork) && other.currentWork.actionName === contract) {
throw helpers.makeRuntimeErrorMsg( throw helpers.makeRuntimeErrorMsg(
ctx, ctx,
@ -328,7 +328,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
} }
} }
return player.sleeves[sleeveNumber].bladeburner(player, action, contract); return Player.sleeves[sleeveNumber].bladeburner(action, contract);
}, },
}; };
} }

@ -1,47 +1,47 @@
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../IPlayer"; import { Player } from "../../Player";
import { Multipliers } from "../Multipliers"; import { Multipliers } from "../Multipliers";
export const calculateEntropy = (player: IPlayer, stacks = 1): Multipliers => { export const calculateEntropy = (stacks = 1): Multipliers => {
const nerf = CONSTANTS.EntropyEffect ** stacks; const nerf = CONSTANTS.EntropyEffect ** stacks;
return { return {
hacking_chance: player.mults.hacking_chance * nerf, hacking_chance: Player.mults.hacking_chance * nerf,
hacking_speed: player.mults.hacking_speed * nerf, hacking_speed: Player.mults.hacking_speed * nerf,
hacking_money: player.mults.hacking_money * nerf, hacking_money: Player.mults.hacking_money * nerf,
hacking_grow: player.mults.hacking_grow * nerf, hacking_grow: Player.mults.hacking_grow * nerf,
hacking: player.mults.hacking * nerf, hacking: Player.mults.hacking * nerf,
strength: player.mults.strength * nerf, strength: Player.mults.strength * nerf,
defense: player.mults.defense * nerf, defense: Player.mults.defense * nerf,
dexterity: player.mults.dexterity * nerf, dexterity: Player.mults.dexterity * nerf,
agility: player.mults.agility * nerf, agility: Player.mults.agility * nerf,
charisma: player.mults.charisma * nerf, charisma: Player.mults.charisma * nerf,
hacking_exp: player.mults.hacking_exp * nerf, hacking_exp: Player.mults.hacking_exp * nerf,
strength_exp: player.mults.strength_exp * nerf, strength_exp: Player.mults.strength_exp * nerf,
defense_exp: player.mults.defense_exp * nerf, defense_exp: Player.mults.defense_exp * nerf,
dexterity_exp: player.mults.dexterity_exp * nerf, dexterity_exp: Player.mults.dexterity_exp * nerf,
agility_exp: player.mults.agility_exp * nerf, agility_exp: Player.mults.agility_exp * nerf,
charisma_exp: player.mults.charisma_exp * nerf, charisma_exp: Player.mults.charisma_exp * nerf,
company_rep: player.mults.company_rep * nerf, company_rep: Player.mults.company_rep * nerf,
faction_rep: player.mults.faction_rep * nerf, faction_rep: Player.mults.faction_rep * nerf,
crime_money: player.mults.crime_money * nerf, crime_money: Player.mults.crime_money * nerf,
crime_success: player.mults.crime_success * nerf, crime_success: Player.mults.crime_success * nerf,
hacknet_node_money: player.mults.hacknet_node_money * nerf, hacknet_node_money: Player.mults.hacknet_node_money * nerf,
hacknet_node_purchase_cost: player.mults.hacknet_node_purchase_cost * nerf, hacknet_node_purchase_cost: Player.mults.hacknet_node_purchase_cost * nerf,
hacknet_node_ram_cost: player.mults.hacknet_node_ram_cost * nerf, hacknet_node_ram_cost: Player.mults.hacknet_node_ram_cost * nerf,
hacknet_node_core_cost: player.mults.hacknet_node_core_cost * nerf, hacknet_node_core_cost: Player.mults.hacknet_node_core_cost * nerf,
hacknet_node_level_cost: player.mults.hacknet_node_level_cost * nerf, hacknet_node_level_cost: Player.mults.hacknet_node_level_cost * nerf,
work_money: player.mults.work_money * nerf, work_money: Player.mults.work_money * nerf,
bladeburner_max_stamina: player.mults.bladeburner_max_stamina * nerf, bladeburner_max_stamina: Player.mults.bladeburner_max_stamina * nerf,
bladeburner_stamina_gain: player.mults.bladeburner_stamina_gain * nerf, bladeburner_stamina_gain: Player.mults.bladeburner_stamina_gain * nerf,
bladeburner_analysis: player.mults.bladeburner_analysis * nerf, bladeburner_analysis: Player.mults.bladeburner_analysis * nerf,
bladeburner_success_chance: player.mults.bladeburner_success_chance * nerf, bladeburner_success_chance: Player.mults.bladeburner_success_chance * nerf,
}; };
}; };

@ -1,8 +1,8 @@
import { StaticAugmentations } from "../../Augmentation/StaticAugmentations"; import { StaticAugmentations } from "../../Augmentation/StaticAugmentations";
import { GraftableAugmentation } from "./GraftableAugmentation"; import { GraftableAugmentation } from "./GraftableAugmentation";
import { IPlayer } from "../IPlayer"; import { Player } from "../../Player";
export const getGraftingAvailableAugs = (player: IPlayer): string[] => { export const getGraftingAvailableAugs = (): string[] => {
const augs: string[] = []; const augs: string[] = [];
for (const [augName, aug] of Object.entries(StaticAugmentations)) { for (const [augName, aug] of Object.entries(StaticAugmentations)) {
@ -10,14 +10,14 @@ export const getGraftingAvailableAugs = (player: IPlayer): string[] => {
augs.push(augName); augs.push(augName);
} }
return augs.filter((augmentation: string) => !player.hasAugmentation(augmentation)); return augs.filter((augmentation: string) => !Player.hasAugmentation(augmentation));
}; };
export const graftingIntBonus = (player: IPlayer): number => { export const graftingIntBonus = (): number => {
return 1 + (player.getIntelligenceBonus(3) - 1) / 3; return 1 + (Player.getIntelligenceBonus(3) - 1) / 3;
}; };
export const calculateGraftingTimeWithBonus = (player: IPlayer, aug: GraftableAugmentation): number => { export const calculateGraftingTimeWithBonus = (aug: GraftableAugmentation): number => {
const baseTime = aug.time; const baseTime = aug.time;
return baseTime / graftingIntBonus(player); return baseTime / graftingIntBonus();
}; };

@ -11,11 +11,11 @@ import { LocationName } from "../../../Locations/data/LocationNames";
import { Locations } from "../../../Locations/Locations"; import { Locations } from "../../../Locations/Locations";
import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums"; import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums";
import { Settings } from "../../../Settings/Settings"; import { Settings } from "../../../Settings/Settings";
import { use } from "../../../ui/Context"; import { Router } from "../../../ui/GameRoot";
import { ConfirmationModal } from "../../../ui/React/ConfirmationModal"; import { ConfirmationModal } from "../../../ui/React/ConfirmationModal";
import { Money } from "../../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { convertTimeMsToTimeElapsedString, formatNumber } from "../../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString, formatNumber } from "../../../utils/StringHelperFunctions";
import { IPlayer } from "../../IPlayer"; import { Player } from "../../../Player";
import { GraftableAugmentation } from "../GraftableAugmentation"; import { GraftableAugmentation } from "../GraftableAugmentation";
import { calculateGraftingTimeWithBonus, getGraftingAvailableAugs } from "../GraftingHelpers"; import { calculateGraftingTimeWithBonus, getGraftingAvailableAugs } from "../GraftingHelpers";
@ -29,21 +29,19 @@ export const GraftableAugmentations = (): Record<string, GraftableAugmentation>
return gAugs; return gAugs;
}; };
const canGraft = (player: IPlayer, aug: GraftableAugmentation): boolean => { const canGraft = (aug: GraftableAugmentation): boolean => {
if (player.money < aug.cost) { if (Player.money < aug.cost) {
return false; return false;
} }
return hasAugmentationPrereqs(aug.augmentation); return hasAugmentationPrereqs(aug.augmentation);
}; };
interface IProps { interface IProps {
player: IPlayer;
aug: Augmentation; aug: Augmentation;
} }
const AugPreReqsChecklist = (props: IProps): React.ReactElement => { const AugPreReqsChecklist = (props: IProps): React.ReactElement => {
const aug = props.aug, const aug = props.aug;
player = props.player;
return ( return (
<Typography color={Settings.theme.money}> <Typography color={Settings.theme.money}>
@ -51,7 +49,7 @@ const AugPreReqsChecklist = (props: IProps): React.ReactElement => {
<br /> <br />
{aug.prereqs.map((preAug) => ( {aug.prereqs.map((preAug) => (
<span style={{ display: "flex", alignItems: "center" }}> <span style={{ display: "flex", alignItems: "center" }}>
{player.hasAugmentation(preAug) ? <CheckBox sx={{ mr: 1 }} /> : <CheckBoxOutlineBlank sx={{ mr: 1 }} />} {Player.hasAugmentation(preAug) ? <CheckBox sx={{ mr: 1 }} /> : <CheckBoxOutlineBlank sx={{ mr: 1 }} />}
{preAug} {preAug}
</span> </span>
))} ))}
@ -60,12 +58,9 @@ const AugPreReqsChecklist = (props: IProps): React.ReactElement => {
}; };
export const GraftingRoot = (): React.ReactElement => { export const GraftingRoot = (): React.ReactElement => {
const player = use.Player();
const router = use.Router();
const graftableAugmentations = useState(GraftableAugmentations())[0]; const graftableAugmentations = useState(GraftableAugmentations())[0];
const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs(player)[0]); const [selectedAug, setSelectedAug] = useState(getGraftingAvailableAugs()[0]);
const [graftOpen, setGraftOpen] = useState(false); const [graftOpen, setGraftOpen] = useState(false);
const selectedAugmentation = StaticAugmentations[selectedAug]; const selectedAugmentation = StaticAugmentations[selectedAug];
@ -75,7 +70,7 @@ export const GraftingRoot = (): React.ReactElement => {
} }
const getAugsSorted = (): string[] => { const getAugsSorted = (): string[] => {
const augs = getGraftingAvailableAugs(player); const augs = getGraftingAvailableAugs();
switch (Settings.PurchaseAugmentationsOrder) { switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost: case PurchaseAugmentationsOrderSetting.Cost:
return augs.sort((a, b) => graftableAugmentations[a].cost - graftableAugmentations[b].cost); return augs.sort((a, b) => graftableAugmentations[a].cost - graftableAugmentations[b].cost);
@ -96,7 +91,7 @@ export const GraftingRoot = (): React.ReactElement => {
return ( return (
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}> <Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
<Button onClick={() => router.toLocation(Locations[LocationName.NewTokyoVitaLife])}>Back</Button> <Button onClick={() => Router.toLocation(Locations[LocationName.NewTokyoVitaLife])}>Back</Button>
<Typography variant="h4">Grafting Laboratory</Typography> <Typography variant="h4">Grafting Laboratory</Typography>
<Typography> <Typography>
You find yourself in a secret laboratory, owned by a mysterious researcher. You find yourself in a secret laboratory, owned by a mysterious researcher.
@ -122,14 +117,14 @@ export const GraftingRoot = (): React.ReactElement => {
</Button> </Button>
</Box> </Box>
</Paper> </Paper>
{getGraftingAvailableAugs(player).length > 0 ? ( {getGraftingAvailableAugs().length > 0 ? (
<Paper sx={{ mb: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}> <Paper sx={{ mb: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
<List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}> <List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
{getAugsSorted().map((k, i) => ( {getAugsSorted().map((k, i) => (
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}> <ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
<Typography <Typography
sx={{ sx={{
color: canGraft(player, graftableAugmentations[k]) color: canGraft(graftableAugmentations[k])
? Settings.theme.primary ? Settings.theme.primary
: Settings.theme.disabled, : Settings.theme.disabled,
}} }}
@ -146,11 +141,11 @@ export const GraftingRoot = (): React.ReactElement => {
<Button <Button
onClick={() => setGraftOpen(true)} onClick={() => setGraftOpen(true)}
sx={{ width: "100%" }} sx={{ width: "100%" }}
disabled={!canGraft(player, graftableAugmentations[selectedAug])} disabled={!canGraft(graftableAugmentations[selectedAug])}
> >
Graft Augmentation ( Graft Augmentation (
<Typography> <Typography>
<Money money={graftableAugmentations[selectedAug].cost} player={player} /> <Money money={graftableAugmentations[selectedAug].cost} forPurchase={true} />
</Typography> </Typography>
) )
</Button> </Button>
@ -158,21 +153,20 @@ export const GraftingRoot = (): React.ReactElement => {
open={graftOpen} open={graftOpen}
onClose={() => setGraftOpen(false)} onClose={() => setGraftOpen(false)}
onConfirm={() => { onConfirm={() => {
player.startWork( Player.startWork(
new GraftingWork({ new GraftingWork({
augmentation: selectedAug, augmentation: selectedAug,
singularity: false, singularity: false,
player: player,
}), }),
); );
player.startFocusing(); Player.startFocusing();
router.toWork(); Router.toWork();
}} }}
confirmationText={ confirmationText={
<> <>
Cancelling grafting will <b>not</b> save grafting progress, and the money you spend will <b>not</b>{" "} Cancelling grafting will <b>not</b> save grafting progress, and the money you spend will <b>not</b>{" "}
be returned. be returned.
{!player.hasAugmentation(AugmentationNames.CongruityImplant) && ( {!Player.hasAugmentation(AugmentationNames.CongruityImplant) && (
<> <>
<br /> <br />
<br /> <br />
@ -186,14 +180,12 @@ export const GraftingRoot = (): React.ReactElement => {
<Typography color={Settings.theme.info}> <Typography color={Settings.theme.info}>
<b>Time to Graft:</b>{" "} <b>Time to Graft:</b>{" "}
{convertTimeMsToTimeElapsedString( {convertTimeMsToTimeElapsedString(
calculateGraftingTimeWithBonus(player, graftableAugmentations[selectedAug]), calculateGraftingTimeWithBonus(graftableAugmentations[selectedAug]),
)} )}
{/* Use formula so the displayed creation time is accurate to player bonus */} {/* Use formula so the displayed creation time is accurate to player bonus */}
</Typography> </Typography>
{selectedAugmentation.prereqs.length > 0 && ( {selectedAugmentation.prereqs.length > 0 && <AugPreReqsChecklist aug={selectedAugmentation} />}
<AugPreReqsChecklist player={player} aug={selectedAugmentation} />
)}
<br /> <br />
@ -229,10 +221,10 @@ export const GraftingRoot = (): React.ReactElement => {
<Paper sx={{ my: 1, p: 1, width: "fit-content" }}> <Paper sx={{ my: 1, p: 1, width: "fit-content" }}>
<Typography> <Typography>
<b>Entropy strength:</b> {player.entropy} <b>Entropy strength:</b> {Player.entropy}
<br /> <br />
<b>All multipliers decreased by:</b>{" "} <b>All multipliers decreased by:</b>{" "}
{formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}% (multiplicative) {formatNumber((1 - CONSTANTS.EntropyEffect ** Player.entropy) * 100, 3)}% (multiplicative)
</Typography> </Typography>
</Paper> </Paper>

@ -32,5 +32,5 @@ export function applyEntropy(this: IPlayer, stacks = 1): void {
this.reapplyAllAugmentations(); this.reapplyAllAugmentations();
this.reapplyAllSourceFiles(); this.reapplyAllSourceFiles();
this.mults = calculateEntropy(this, stacks); this.mults = calculateEntropy(stacks);
} }

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