mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-02 05:47:14 +02:00
Merge branch 'dev' into bugfix/corp-updates
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
import * as React from "react";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
@@ -134,7 +133,7 @@ function generateStatsDescription(mults: IMap<number>, programs?: string[], star
|
||||
desc = (
|
||||
<>
|
||||
{desc}
|
||||
<br />+{f(mults.charisma_mult - 1)} Charisma skill
|
||||
<br />+{f(mults.charisma_mult - 1)} charisma skill
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -410,8 +409,8 @@ export class Augmentation {
|
||||
this.info = params.info;
|
||||
this.prereqs = params.prereqs ? params.prereqs : [];
|
||||
|
||||
this.baseRepRequirement = params.repCost * BitNodeMultipliers.AugmentationRepCost;
|
||||
this.baseCost = params.moneyCost * BitNodeMultipliers.AugmentationMoneyCost;
|
||||
this.baseRepRequirement = params.repCost;
|
||||
this.baseCost = params.moneyCost;
|
||||
this.startingCost = this.baseCost;
|
||||
this.factions = params.factions;
|
||||
|
||||
|
||||
@@ -1484,6 +1484,25 @@ export const initGeneralAugmentations = (): Augmentation[] => [
|
||||
),
|
||||
factions: [FactionNames.TianDiHui],
|
||||
}),
|
||||
|
||||
// Grafting-exclusive Augmentation
|
||||
new Augmentation({
|
||||
name: AugmentationNames.CongruityImplant,
|
||||
repCost: Infinity,
|
||||
moneyCost: 50e12,
|
||||
info: (
|
||||
<>
|
||||
Developed by a pioneer in Grafting research, this implant generates pulses of stability which seem to have a
|
||||
nullifying effect versus the Entropy virus.
|
||||
<br />
|
||||
<br />
|
||||
<b>Note:</b> For unknown reasons, the lowercase <code>n</code> appears to be an integral component to its
|
||||
functionality.
|
||||
</>
|
||||
),
|
||||
stats: <>This Augmentation removes the Entropy virus, and prevents it from affecting you again.</>,
|
||||
factions: [],
|
||||
}),
|
||||
];
|
||||
|
||||
export const initBladeburnerAugmentations = (): Augmentation[] => [
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
initNeuroFluxGovernor,
|
||||
initUnstableCircadianModulator,
|
||||
} from "./AugmentationCreator";
|
||||
import { Router } from "../ui/GameRoot";
|
||||
|
||||
export function AddToAugmentations(aug: Augmentation): void {
|
||||
const name = aug.name;
|
||||
@@ -141,6 +142,12 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
|
||||
}
|
||||
}
|
||||
|
||||
// Special logic for Congruity Implant
|
||||
if (aug.name === AugmentationNames.CongruityImplant && !reapply) {
|
||||
Player.entropy = 0;
|
||||
Player.applyEntropy(Player.entropy);
|
||||
}
|
||||
|
||||
// Push onto Player's Augmentation list
|
||||
if (!reapply) {
|
||||
const ownedAug = new PlayerOwnedAugmentation(aug.name);
|
||||
@@ -148,8 +155,8 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
|
||||
}
|
||||
}
|
||||
|
||||
function installAugmentations(): boolean {
|
||||
if (Player.queuedAugmentations.length == 0) {
|
||||
function installAugmentations(force?: boolean): boolean {
|
||||
if (Player.queuedAugmentations.length == 0 && !force) {
|
||||
dialogBoxCreate("You have not purchased any Augmentations to install!");
|
||||
return false;
|
||||
}
|
||||
@@ -179,13 +186,16 @@ function installAugmentations(): boolean {
|
||||
augmentationList += aug.name + level + "<br>";
|
||||
}
|
||||
Player.queuedAugmentations = [];
|
||||
dialogBoxCreate(
|
||||
"You slowly drift to sleep as scientists put you under in order " +
|
||||
"to install the following Augmentations:<br>" +
|
||||
augmentationList +
|
||||
"<br>You wake up in your home...you feel different...",
|
||||
);
|
||||
if (!force) {
|
||||
dialogBoxCreate(
|
||||
"You slowly drift to sleep as scientists put you under in order " +
|
||||
"to install the following Augmentations:<br>" +
|
||||
augmentationList +
|
||||
"<br>You wake up in your home...you feel different...",
|
||||
);
|
||||
}
|
||||
prestigeAugmentation();
|
||||
Router.toTerminal();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ export enum AugmentationNames {
|
||||
BionicArms = "Bionic Arms",
|
||||
SNA = "Social Negotiation Assistant (S.N.A)",
|
||||
HydroflameLeftArm = "Hydroflame Left Arm",
|
||||
CongruityImplant = "nickofolas Congruity Implant",
|
||||
EsperEyewear = "EsperTech Bladeburner Eyewear",
|
||||
EMS4Recombination = "EMS-4 Recombination",
|
||||
OrionShoulder = "ORION-MKIV Shoulder",
|
||||
|
||||
@@ -7,7 +7,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { InstalledAugmentations } from "./InstalledAugmentations";
|
||||
import { PlayerMultipliers } from "./PlayerMultipliers";
|
||||
import { PurchasedAugmentations } from "./PurchasedAugmentations";
|
||||
import { SourceFiles } from "./SourceFiles";
|
||||
import { SourceFilesElement } from "./SourceFiles";
|
||||
|
||||
import { canGetBonus } from "../../ExportBonus";
|
||||
import { use } from "../../ui/Context";
|
||||
@@ -16,8 +16,55 @@ import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Container from "@mui/material/Container";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { Info } from "@mui/icons-material";
|
||||
|
||||
interface NFGDisplayProps {
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
|
||||
const level = player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
|
||||
|
||||
return level > 0 ? (
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5" color={Settings.theme.info}>
|
||||
NeuroFlux Governor - Level {level}
|
||||
</Typography>
|
||||
<Typography color={Settings.theme.info}>{Augmentations[AugmentationNames.NeuroFluxGovernor].stats}</Typography>
|
||||
</Paper>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
interface EntropyDisplayProps {
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
const EntropyDisplay = ({ player }: EntropyDisplayProps): React.ReactElement => {
|
||||
return player.entropy > 0 ? (
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5" color={Settings.theme.error}>
|
||||
Entropy Virus - Level {player.entropy}
|
||||
</Typography>
|
||||
<Typography color={Settings.theme.error}>
|
||||
<b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}%
|
||||
(multiplicative)
|
||||
</Typography>
|
||||
</Paper>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
exportGameFn: () => void;
|
||||
@@ -55,81 +102,113 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
|
||||
<Typography variant="h4">Augmentations</Typography>
|
||||
<Box mx={2}>
|
||||
<Typography>
|
||||
Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to
|
||||
install them.
|
||||
</Typography>
|
||||
<Typography>WARNING: Installing your Augmentations resets most of your progress, including:</Typography>
|
||||
<br />
|
||||
<Typography>- Stats/Skill levels and Experience</Typography>
|
||||
<Typography>- Money</Typography>
|
||||
<Typography>- Scripts on every computer but your home computer</Typography>
|
||||
<Typography>- Purchased servers</Typography>
|
||||
<Typography>- Hacknet Nodes</Typography>
|
||||
<Typography>- Faction/Company reputation</Typography>
|
||||
<Typography>- Stocks</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Installing Augmentations lets you start over with the perks and benefits granted by all of the Augmentations
|
||||
you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your home computer (but you
|
||||
will lose all programs besides NUKE.exe)
|
||||
</Typography>
|
||||
<Box sx={{ mb: 1 }}>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||
Purchased Augmentations
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography>
|
||||
Below is a list of all Augmentations you have purchased but not yet installed. Click the button
|
||||
below to install them.
|
||||
</Typography>
|
||||
<Typography>
|
||||
WARNING: Installing your Augmentations resets most of your progress, including:
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>- Stats/Skill levels and Experience</Typography>
|
||||
<Typography>- Money</Typography>
|
||||
<Typography>- Scripts on every computer but your home computer</Typography>
|
||||
<Typography>- Purchased servers</Typography>
|
||||
<Typography>- Hacknet Nodes</Typography>
|
||||
<Typography>- Faction/Company reputation</Typography>
|
||||
<Typography>- Stocks</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Installing Augmentations lets you start over with the perks and benefits granted by all of the
|
||||
Augmentations you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your
|
||||
home computer (but you will lose all programs besides NUKE.exe)
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Info sx={{ ml: 1, mb: 0.5 }} color="info" />
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
<ConfirmationModal
|
||||
open={installOpen}
|
||||
onClose={() => setInstallOpen(false)}
|
||||
onConfirm={props.installAugmentationsFn}
|
||||
confirmationText={
|
||||
<>
|
||||
Installing will reset
|
||||
<br />
|
||||
<br />- money
|
||||
<br />- skill / experience
|
||||
<br />- every server except home
|
||||
<br />- factions and reputation
|
||||
<br />
|
||||
<br />
|
||||
You will keep:
|
||||
<br />
|
||||
<br />- All scripts on home
|
||||
<br />- home ram and cores
|
||||
<br />
|
||||
<br />
|
||||
It is recommended to install several Augmentations at once. Preferably everything from any faction of
|
||||
your choosing.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Box sx={{ display: "grid", width: "100%", gridTemplateColumns: "1fr 1fr" }}>
|
||||
<Tooltip title={<Typography>'I never asked for this'</Typography>}>
|
||||
<span>
|
||||
<Button sx={{ width: "100%" }} disabled={player.queuedAugmentations.length === 0} onClick={doInstall}>
|
||||
Install Augmentations
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Tooltip title={<Typography>It's always a good idea to backup/export your save!</Typography>}>
|
||||
<Button sx={{ width: "100%" }} onClick={doExport} color="error">
|
||||
Backup Save {exportBonusStr()}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Paper>
|
||||
{player.queuedAugmentations.length > 0 ? (
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||
<PurchasedAugmentations />
|
||||
<PlayerMultipliers />
|
||||
</Box>
|
||||
) : (
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography>No Augmentations have been purchased yet</Typography>
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
<Typography variant="h4" color="primary">
|
||||
Purchased Augmentations
|
||||
</Typography>
|
||||
<Box mx={2}>
|
||||
<Tooltip title={<Typography>'I never asked for this'</Typography>}>
|
||||
<span>
|
||||
<Button disabled={player.queuedAugmentations.length === 0} onClick={doInstall}>
|
||||
Install Augmentations
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<ConfirmationModal
|
||||
open={installOpen}
|
||||
onClose={() => setInstallOpen(false)}
|
||||
onConfirm={props.installAugmentationsFn}
|
||||
confirmationText={
|
||||
<>
|
||||
Installing will reset
|
||||
<br />
|
||||
<br />- money
|
||||
<br />- skill / experience
|
||||
<br />- every server except home
|
||||
<br />- factions and reputation
|
||||
<br />
|
||||
<br />
|
||||
You will keep:
|
||||
<br />
|
||||
<br />- All scripts on home
|
||||
<br />- home ram and cores
|
||||
<br />
|
||||
<br />
|
||||
It is recommended to install several Augmentations at once. Preferably everything from any faction of your
|
||||
choosing.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Tooltip title={<Typography>It's always a good idea to backup/export your save!</Typography>}>
|
||||
<Button sx={{ mx: 2 }} onClick={doExport} color="error">
|
||||
Backup Save {exportBonusStr()}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<PurchasedAugmentations />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
my: 1,
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${
|
||||
+!!((player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0) > 0) +
|
||||
+!!(player.entropy > 0)
|
||||
}, 1fr)`,
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<NeuroFluxDisplay player={player} />
|
||||
<EntropyDisplay player={player} />
|
||||
</Box>
|
||||
<Typography variant="h4">Installed Augmentations</Typography>
|
||||
<Box mx={2}>
|
||||
<Typography>
|
||||
List of all Augmentations that have been installed. You have gained the effects of these.
|
||||
</Typography>
|
||||
|
||||
<Box>
|
||||
<InstalledAugmentations />
|
||||
</Box>
|
||||
<PlayerMultipliers />
|
||||
<SourceFiles />
|
||||
</>
|
||||
<SourceFilesElement />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,28 +5,23 @@
|
||||
* It also contains 'configuration' buttons that allow you to change how the
|
||||
* Augs/SF's are displayed
|
||||
*/
|
||||
import { Box, ListItemButton, Paper, Typography } from "@mui/material";
|
||||
import Button from "@mui/material/Button";
|
||||
import List from "@mui/material/List";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { use } from "../../ui/Context";
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import Button from "@mui/material/Button";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import List from "@mui/material/List";
|
||||
import { ExpandLess, ExpandMore } from "@mui/icons-material";
|
||||
import { Box, Paper, ListItemButton, ListItemText, Typography, Collapse } from "@mui/material";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
|
||||
export function InstalledAugmentations(): React.ReactElement {
|
||||
const setRerender = useState(true)[1];
|
||||
const player = use.Player();
|
||||
const sourceAugs = player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor);
|
||||
|
||||
const sourceAugs = player.augmentations.slice();
|
||||
const [selectedAug, setSelectedAug] = useState(sourceAugs[0]);
|
||||
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sourceAugs.sort((aug1, aug2) => {
|
||||
@@ -49,59 +44,60 @@ export function InstalledAugmentations(): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title={"Sorts the Augmentations alphabetically in numeral order"}>
|
||||
<Button onClick={sortInOrder}>Sort in Order</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={"Sorts the Augmentations based on when you acquired them (same as default)"}>
|
||||
<Button sx={{ mx: 2 }} onClick={sortByAcquirementTime}>
|
||||
Sort by Acquirement Time
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<List dense>
|
||||
{player.entropy > 0 &&
|
||||
(() => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Box component={Paper}>
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography color={Settings.theme.hp} style={{ whiteSpace: "pre-wrap" }}>
|
||||
Entropy Virus - Level {player.entropy}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{open ? (
|
||||
<ExpandLess sx={{ color: Settings.theme.hp }} />
|
||||
) : (
|
||||
<ExpandMore sx={{ color: Settings.theme.hp }} />
|
||||
)}
|
||||
<Box sx={{ width: "100%" }}>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">Installed Augmentations</Typography>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
|
||||
<Tooltip title={"Sorts the Augmentations alphabetically in numeral order"}>
|
||||
<Button sx={{ width: "100%" }} onClick={sortInOrder}>
|
||||
Sort in Order
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={"Sorts the Augmentations based on when you acquired them (same as default)"}>
|
||||
<Button sx={{ width: "100%" }} onClick={sortByAcquirementTime}>
|
||||
Sort by Time of Acquirement
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Paper>
|
||||
{sourceAugs.length > 0 ? (
|
||||
<Paper sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||
<Box>
|
||||
<List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
|
||||
{sourceAugs.map((k, i) => (
|
||||
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
|
||||
<Typography>{k.name}</Typography>
|
||||
</ListItemButton>
|
||||
<Collapse in={open} unmountOnExit>
|
||||
<Box m={4}>
|
||||
<Typography color={Settings.theme.hp}>
|
||||
<b>All multipliers decreased by:</b>{" "}
|
||||
{formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}% (multiplicative)
|
||||
</Typography>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
})()}
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
<Box sx={{ m: 1 }}>
|
||||
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||
{selectedAug.name}
|
||||
</Typography>
|
||||
<Typography sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
||||
{(() => {
|
||||
const aug = Augmentations[selectedAug.name];
|
||||
|
||||
{sourceAugs.map((e) => {
|
||||
const aug = Augmentations[e.name];
|
||||
|
||||
let level = null;
|
||||
if (e.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
level = e.level;
|
||||
}
|
||||
|
||||
return <AugmentationAccordion key={aug.name} aug={aug} level={level} />;
|
||||
})}
|
||||
</List>
|
||||
</>
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const tooltip = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
return tooltip;
|
||||
})()}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Paper>
|
||||
) : (
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography>No Augmentations have been installed yet</Typography>
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
/**
|
||||
* React component for displaying the player's multipliers on the Augmentation UI page
|
||||
*/
|
||||
import { DoubleArrow } from "@mui/icons-material";
|
||||
import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material";
|
||||
import * as React from "react";
|
||||
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { Player } from "../../Player";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { Table, TableCell } from "../../ui/React/Table";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
function calculateAugmentedStats(): any {
|
||||
const augP: any = {};
|
||||
interface IAugmentedStats {
|
||||
[index: string]: number;
|
||||
}
|
||||
|
||||
function calculateAugmentedStats(): IAugmentedStats {
|
||||
const augP: IAugmentedStats = {};
|
||||
for (const aug of Player.queuedAugmentations) {
|
||||
const augObj = Augmentations[aug.name];
|
||||
for (const mult of Object.keys(augObj.mults)) {
|
||||
@@ -25,268 +27,249 @@ function calculateAugmentedStats(): any {
|
||||
return augP;
|
||||
}
|
||||
|
||||
function Improvements({ r, m }: { r: number; m: number }): React.ReactElement {
|
||||
if (r) {
|
||||
return (
|
||||
<>
|
||||
<TableCell key="2">
|
||||
<Typography> {"=>"} </Typography>
|
||||
</TableCell>
|
||||
<TableCell key="3">
|
||||
<Typography>
|
||||
{numeralWrapper.formatPercentage(r)} <BN5Stat base={r} mult={m} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
|
||||
interface IBN5StatsProps {
|
||||
interface IBitNodeModifiedStatsProps {
|
||||
base: number;
|
||||
mult: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
function BN5Stat(props: IBN5StatsProps): React.ReactElement {
|
||||
if (props.mult === 1) return <></>;
|
||||
return <>({numeralWrapper.formatPercentage(props.base * props.mult)})</>;
|
||||
}
|
||||
function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement {
|
||||
// If player doesn't have SF5 or if the property isn't affected by BitNode mults
|
||||
if (props.mult === 1 || SourceFileFlags[5] === 0)
|
||||
return <Typography color={props.color}>{numeralWrapper.formatPercentage(props.base)}</Typography>;
|
||||
|
||||
function MultiplierTable({ rows }: { rows: [string, number, number, number][] }): React.ReactElement {
|
||||
return (
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>
|
||||
{rows.map((r: any) => (
|
||||
<TableRow key={r[0]}>
|
||||
<TableCell key="0">
|
||||
<Typography noWrap>{r[0]} multiplier: </Typography>
|
||||
</TableCell>
|
||||
<TableCell key="1" style={{ textAlign: "right" }}>
|
||||
<Typography noWrap>
|
||||
{numeralWrapper.formatPercentage(r[1])} <BN5Stat base={r[1]} mult={r[3]} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<Improvements r={r[2]} m={r[3]} />
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<Typography color={props.color}>
|
||||
<span style={{ opacity: 0.5 }}>{numeralWrapper.formatPercentage(props.base)}</span>{" "}
|
||||
{numeralWrapper.formatPercentage(props.base * props.mult)}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
type MultiplierListItemData = [
|
||||
multiplier: string,
|
||||
currentValue: number,
|
||||
augmentedValue: number,
|
||||
bitNodeMultiplier: number,
|
||||
color: string,
|
||||
];
|
||||
|
||||
interface IMultiplierListProps {
|
||||
rows: MultiplierListItemData[];
|
||||
}
|
||||
|
||||
function MultiplierList(props: IMultiplierListProps): React.ReactElement {
|
||||
const listItems = props.rows
|
||||
.map((data) => {
|
||||
const [multiplier, currentValue, augmentedValue, bitNodeMultiplier, color] = data;
|
||||
|
||||
if (!isNaN(augmentedValue)) {
|
||||
return (
|
||||
<ListItem key={multiplier} disableGutters sx={{ py: 0 }}>
|
||||
<ListItemText
|
||||
sx={{ my: 0.1 }}
|
||||
primary={
|
||||
<Typography color={color}>
|
||||
<b>{multiplier}</b>
|
||||
</Typography>
|
||||
}
|
||||
secondary={
|
||||
<span style={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||
<BitNodeModifiedStats base={currentValue} mult={bitNodeMultiplier} color={color} />
|
||||
<DoubleArrow fontSize="small" color="success" sx={{ mb: 0.5, mx: 1 }} />
|
||||
<BitNodeModifiedStats base={augmentedValue} mult={bitNodeMultiplier} color={Settings.theme.success} />
|
||||
</span>
|
||||
}
|
||||
disableTypography
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
return;
|
||||
})
|
||||
.filter((i) => i !== undefined);
|
||||
|
||||
return listItems.length > 0 ? <List disablePadding>{listItems}</List> : <></>;
|
||||
}
|
||||
|
||||
export function PlayerMultipliers(): React.ReactElement {
|
||||
const mults = calculateAugmentedStats();
|
||||
|
||||
function BladeburnerMults(): React.ReactElement {
|
||||
if (!Player.canAccessBladeburner()) return <></>;
|
||||
return (
|
||||
<>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Bladeburner Success Chance",
|
||||
Player.bladeburner_success_chance_mult,
|
||||
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Max Stamina",
|
||||
Player.bladeburner_max_stamina_mult,
|
||||
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Stamina Gain",
|
||||
Player.bladeburner_stamina_gain_mult,
|
||||
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Field Analysis",
|
||||
Player.bladeburner_analysis_mult,
|
||||
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
|
||||
1,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
</>
|
||||
// Column data is a bit janky, so it's set up here to allow for
|
||||
// easier logic in setting up the layout
|
||||
const leftColData: MultiplierListItemData[] = [
|
||||
...[
|
||||
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult, 1],
|
||||
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult, 1],
|
||||
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult, 1],
|
||||
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult, 1],
|
||||
[
|
||||
"Hacking Level ",
|
||||
Player.hacking_mult,
|
||||
Player.hacking_mult * mults.hacking_mult,
|
||||
BitNodeMultipliers.HackingLevelMultiplier,
|
||||
],
|
||||
[
|
||||
"Hacking Experience ",
|
||||
Player.hacking_exp_mult,
|
||||
Player.hacking_exp_mult * mults.hacking_exp_mult,
|
||||
BitNodeMultipliers.HackExpGain,
|
||||
],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.hack])),
|
||||
...[
|
||||
[
|
||||
"Strength Level ",
|
||||
Player.strength_mult,
|
||||
Player.strength_mult * mults.strength_mult,
|
||||
BitNodeMultipliers.StrengthLevelMultiplier,
|
||||
],
|
||||
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult, 1],
|
||||
[
|
||||
"Defense Level ",
|
||||
Player.defense_mult,
|
||||
Player.defense_mult * mults.defense_mult,
|
||||
BitNodeMultipliers.DefenseLevelMultiplier,
|
||||
],
|
||||
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult, 1],
|
||||
[
|
||||
"Dexterity Level ",
|
||||
Player.dexterity_mult,
|
||||
Player.dexterity_mult * mults.dexterity_mult,
|
||||
BitNodeMultipliers.DexterityLevelMultiplier,
|
||||
],
|
||||
["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult, 1],
|
||||
[
|
||||
"Agility Level ",
|
||||
Player.agility_mult,
|
||||
Player.agility_mult * mults.agility_mult,
|
||||
BitNodeMultipliers.AgilityLevelMultiplier,
|
||||
],
|
||||
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult, 1],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.combat])),
|
||||
[
|
||||
"Charisma Level ",
|
||||
Player.charisma_mult,
|
||||
Player.charisma_mult * mults.charisma_mult,
|
||||
BitNodeMultipliers.CharismaLevelMultiplier,
|
||||
Settings.theme.cha,
|
||||
],
|
||||
[
|
||||
"Charisma Experience ",
|
||||
Player.charisma_exp_mult,
|
||||
Player.charisma_exp_mult * mults.charisma_exp_mult,
|
||||
1,
|
||||
Settings.theme.cha,
|
||||
],
|
||||
];
|
||||
const rightColData: MultiplierListItemData[] = [
|
||||
...[
|
||||
[
|
||||
"Hacknet Node production ",
|
||||
Player.hacknet_node_money_mult,
|
||||
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
|
||||
BitNodeMultipliers.HacknetNodeMoney,
|
||||
],
|
||||
[
|
||||
"Hacknet Node purchase cost ",
|
||||
Player.hacknet_node_purchase_cost_mult,
|
||||
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node RAM upgrade cost ",
|
||||
Player.hacknet_node_ram_cost_mult,
|
||||
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node Core purchase cost ",
|
||||
Player.hacknet_node_core_cost_mult,
|
||||
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node level upgrade cost ",
|
||||
Player.hacknet_node_level_cost_mult,
|
||||
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
|
||||
1,
|
||||
],
|
||||
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult, 1],
|
||||
[
|
||||
"Faction reputation gain ",
|
||||
Player.faction_rep_mult,
|
||||
Player.faction_rep_mult * mults.faction_rep_mult,
|
||||
BitNodeMultipliers.FactionWorkRepGain,
|
||||
],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])),
|
||||
[
|
||||
"Salary ",
|
||||
Player.work_money_mult,
|
||||
Player.work_money_mult * mults.work_money_mult,
|
||||
BitNodeMultipliers.CompanyWorkMoney,
|
||||
Settings.theme.money,
|
||||
],
|
||||
[
|
||||
"Crime success ",
|
||||
Player.crime_success_mult,
|
||||
Player.crime_success_mult * mults.crime_success_mult,
|
||||
1,
|
||||
Settings.theme.combat,
|
||||
],
|
||||
[
|
||||
"Crime money ",
|
||||
Player.crime_money_mult,
|
||||
Player.crime_money_mult * mults.crime_money_mult,
|
||||
BitNodeMultipliers.CrimeMoney,
|
||||
Settings.theme.money,
|
||||
],
|
||||
];
|
||||
|
||||
if (Player.canAccessBladeburner()) {
|
||||
rightColData.push(
|
||||
...[
|
||||
[
|
||||
"Bladeburner Success Chance",
|
||||
Player.bladeburner_success_chance_mult,
|
||||
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Max Stamina",
|
||||
Player.bladeburner_max_stamina_mult,
|
||||
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Stamina Gain",
|
||||
Player.bladeburner_stamina_gain_mult,
|
||||
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Bladeburner Field Analysis",
|
||||
Player.bladeburner_analysis_mult,
|
||||
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
|
||||
1,
|
||||
],
|
||||
].map((data): MultiplierListItemData => (data as any).concat([Settings.theme.primary])),
|
||||
);
|
||||
}
|
||||
|
||||
const hasLeftImprovements = +!!(leftColData.filter((item) => item[2] !== 0).length > 0),
|
||||
hasRightImprovements = +!!(rightColData.filter((item) => item[2] !== 0).length > 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">Multipliers</Typography>
|
||||
<Box mx={2}>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult, 1],
|
||||
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult, 1],
|
||||
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult, 1],
|
||||
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult, 1],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Hacking Level ",
|
||||
Player.hacking_mult,
|
||||
Player.hacking_mult * mults.hacking_mult,
|
||||
BitNodeMultipliers.HackingLevelMultiplier,
|
||||
],
|
||||
[
|
||||
"Hacking Experience ",
|
||||
Player.hacking_exp_mult,
|
||||
Player.hacking_exp_mult * mults.hacking_exp_mult,
|
||||
BitNodeMultipliers.HackExpGain,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Strength Level ",
|
||||
Player.strength_mult,
|
||||
Player.strength_mult * mults.strength_mult,
|
||||
BitNodeMultipliers.StrengthLevelMultiplier,
|
||||
],
|
||||
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult, 1],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Defense Level ",
|
||||
Player.defense_mult,
|
||||
Player.defense_mult * mults.defense_mult,
|
||||
BitNodeMultipliers.DefenseLevelMultiplier,
|
||||
],
|
||||
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult, 1],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Dexterity Level ",
|
||||
Player.dexterity_mult,
|
||||
Player.dexterity_mult * mults.dexterity_mult,
|
||||
BitNodeMultipliers.DexterityLevelMultiplier,
|
||||
],
|
||||
[
|
||||
"Dexterity Experience ",
|
||||
Player.dexterity_exp_mult,
|
||||
Player.dexterity_exp_mult * mults.dexterity_exp_mult,
|
||||
1,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Agility Level ",
|
||||
Player.agility_mult,
|
||||
Player.agility_mult * mults.agility_mult,
|
||||
BitNodeMultipliers.AgilityLevelMultiplier,
|
||||
],
|
||||
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult, 1],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Charisma Level ",
|
||||
Player.charisma_mult,
|
||||
Player.charisma_mult * mults.charisma_mult,
|
||||
BitNodeMultipliers.CharismaLevelMultiplier,
|
||||
],
|
||||
["Charisma Experience ", Player.charisma_exp_mult, Player.charisma_exp_mult * mults.charisma_exp_mult, 1],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Hacknet Node production ",
|
||||
Player.hacknet_node_money_mult,
|
||||
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
|
||||
BitNodeMultipliers.HacknetNodeMoney,
|
||||
],
|
||||
[
|
||||
"Hacknet Node purchase cost ",
|
||||
Player.hacknet_node_purchase_cost_mult,
|
||||
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node RAM upgrade cost ",
|
||||
Player.hacknet_node_ram_cost_mult,
|
||||
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node Core purchase cost ",
|
||||
Player.hacknet_node_core_cost_mult,
|
||||
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
|
||||
1,
|
||||
],
|
||||
[
|
||||
"Hacknet Node level upgrade cost ",
|
||||
Player.hacknet_node_level_cost_mult,
|
||||
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
|
||||
1,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult, 1],
|
||||
[
|
||||
"Faction reputation gain ",
|
||||
Player.faction_rep_mult,
|
||||
Player.faction_rep_mult * mults.faction_rep_mult,
|
||||
BitNodeMultipliers.FactionWorkRepGain,
|
||||
],
|
||||
[
|
||||
"Salary ",
|
||||
Player.work_money_mult,
|
||||
Player.work_money_mult * mults.work_money_mult,
|
||||
BitNodeMultipliers.CompanyWorkMoney,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Crime success ", Player.crime_success_mult, Player.crime_success_mult * mults.crime_success_mult, 1],
|
||||
[
|
||||
"Crime money ",
|
||||
Player.crime_money_mult,
|
||||
Player.crime_money_mult * mults.crime_money_mult,
|
||||
BitNodeMultipliers.CrimeMoney,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<BladeburnerMults />
|
||||
</Box>
|
||||
</>
|
||||
<Paper
|
||||
sx={{
|
||||
p: 1,
|
||||
maxHeight: 400,
|
||||
overflowY: "scroll",
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${hasLeftImprovements + hasRightImprovements}, 1fr)`,
|
||||
}}
|
||||
>
|
||||
<MultiplierList rows={leftColData} />
|
||||
<MultiplierList rows={rightColData} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
* React component for displaying all of the player's purchased (but not installed)
|
||||
* Augmentations on the Augmentations UI.
|
||||
*/
|
||||
import { List, ListItemText, Paper, Tooltip, Typography } from "@mui/material";
|
||||
import * as React from "react";
|
||||
|
||||
import { Player } from "../../Player";
|
||||
import { Augmentations } from "../Augmentations";
|
||||
import { AugmentationNames } from "../data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
|
||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||
import List from "@mui/material/List";
|
||||
|
||||
export function PurchasedAugmentations(): React.ReactElement {
|
||||
const augs: React.ReactElement[] = [];
|
||||
@@ -23,14 +20,48 @@ export function PurchasedAugmentations(): React.ReactElement {
|
||||
}
|
||||
for (let i = 0; i < Player.queuedAugmentations.length; i++) {
|
||||
const ownedAug = Player.queuedAugmentations[i];
|
||||
let displayName = ownedAug.name;
|
||||
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue;
|
||||
const aug = Augmentations[ownedAug.name];
|
||||
|
||||
let level = null;
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
level = ownedAug.level;
|
||||
displayName += ` - Level ${level}`;
|
||||
}
|
||||
augs.push(<AugmentationAccordion key={aug.name} aug={aug} level={level} />);
|
||||
|
||||
augs.push(
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
{(() => {
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const tooltip = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
return tooltip;
|
||||
})()}
|
||||
</Typography>
|
||||
}
|
||||
enterNextDelay={500}
|
||||
key={displayName}
|
||||
>
|
||||
<ListItemText sx={{ px: 2, py: 1 }} primary={displayName} />
|
||||
</Tooltip>,
|
||||
);
|
||||
}
|
||||
|
||||
return <List dense>{augs}</List>;
|
||||
return (
|
||||
<Paper sx={{ py: 1, maxHeight: 400, overflowY: "scroll" }}>
|
||||
<List sx={{ height: 400, overflowY: "scroll" }} disablePadding>
|
||||
{augs}
|
||||
</List>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,159 @@
|
||||
import React from "react";
|
||||
import { SourceFileMinus1 } from "./SourceFileMinus1";
|
||||
import { OwnedSourceFiles } from "./OwnedSourceFiles";
|
||||
import List from "@mui/material/List";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { ListItemButton, ListItemText, Paper } from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import List from "@mui/material/List";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import React, { useState } from "react";
|
||||
import { Exploit, ExploitName } from "../../Exploits/Exploit";
|
||||
import { Player } from "../../Player";
|
||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { SourceFile } from "../../SourceFile/SourceFile";
|
||||
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||
|
||||
interface SfMinus1 {
|
||||
info: React.ReactElement;
|
||||
n: number;
|
||||
name: string;
|
||||
lvl: number;
|
||||
}
|
||||
|
||||
const safeGetSf = (sfNum: number): SourceFile | SfMinus1 | null => {
|
||||
if (sfNum === -1) {
|
||||
const sfMinus1: SfMinus1 = {
|
||||
info: (
|
||||
<>
|
||||
This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web ecosystem.
|
||||
<br />
|
||||
<br />
|
||||
It increases all of the player's multipliers by 0.1%
|
||||
<br />
|
||||
<br />
|
||||
You have found the following exploits:
|
||||
<br />
|
||||
<br />
|
||||
{Player.exploits.map((c: Exploit) => (
|
||||
<React.Fragment key={c}>
|
||||
* {ExploitName(c)}
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
),
|
||||
lvl: Player.exploits.length,
|
||||
n: -1,
|
||||
name: "Source-File -1: Exploits in the BitNodes",
|
||||
};
|
||||
return sfMinus1;
|
||||
}
|
||||
|
||||
const srcFileKey = "SourceFile" + sfNum;
|
||||
const sfObj = SourceFiles[srcFileKey];
|
||||
if (sfObj == null) {
|
||||
console.error(`Invalid source file number: ${sfNum}`);
|
||||
return null;
|
||||
}
|
||||
return sfObj;
|
||||
};
|
||||
|
||||
const getMaxLevel = (sfObj: SourceFile | SfMinus1): string | number => {
|
||||
let maxLevel;
|
||||
switch (sfObj.n) {
|
||||
case 12:
|
||||
maxLevel = "∞";
|
||||
break;
|
||||
case -1:
|
||||
maxLevel = Object.keys(Exploit).length;
|
||||
break;
|
||||
default:
|
||||
maxLevel = "3";
|
||||
}
|
||||
return maxLevel;
|
||||
};
|
||||
|
||||
export function SourceFilesElement(): React.ReactElement {
|
||||
const sourceSfs = Player.sourceFiles.slice();
|
||||
const exploits = Player.exploits;
|
||||
// Create a fake SF for -1, if "owned"
|
||||
if (exploits.length > 0) {
|
||||
sourceSfs.unshift({
|
||||
n: -1,
|
||||
lvl: exploits.length,
|
||||
});
|
||||
}
|
||||
|
||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||
sourceSfs.sort((sf1, sf2) => {
|
||||
return sf1.n - sf2.n;
|
||||
});
|
||||
}
|
||||
|
||||
if (sourceSfs.length === 0) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const [selectedSf, setSelectedSf] = useState(sourceSfs[0]);
|
||||
|
||||
export function SourceFiles(): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">Source Files</Typography>
|
||||
<Box mx={2}>
|
||||
<List dense>
|
||||
<SourceFileMinus1 />
|
||||
<OwnedSourceFiles />
|
||||
</List>
|
||||
</Box>
|
||||
</>
|
||||
<Box sx={{ width: "100%", mt: 1 }}>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">Source Files</Typography>
|
||||
</Paper>
|
||||
<Paper sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||
<Box>
|
||||
<List
|
||||
sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}
|
||||
disablePadding
|
||||
>
|
||||
{sourceSfs.map((e, i) => {
|
||||
const sfObj = safeGetSf(e.n);
|
||||
if (!sfObj) return;
|
||||
|
||||
const maxLevel = getMaxLevel(sfObj);
|
||||
|
||||
return (
|
||||
<ListItemButton
|
||||
key={i + 1}
|
||||
onClick={() => setSelectedSf(e)}
|
||||
selected={selectedSf.n === e.n}
|
||||
sx={{ py: 0 }}
|
||||
>
|
||||
<ListItemText
|
||||
disableTypography
|
||||
primary={<Typography>{sfObj.name}</Typography>}
|
||||
secondary={
|
||||
<Typography>
|
||||
Level {e.lvl} / {maxLevel}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
</ListItemButton>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
<Box sx={{ m: 1 }}>
|
||||
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||
{safeGetSf(selectedSf.n)?.name}
|
||||
</Typography>
|
||||
<Typography sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
||||
{(() => {
|
||||
const sfObj = safeGetSf(selectedSf.n);
|
||||
if (!sfObj) return;
|
||||
|
||||
const maxLevel = getMaxLevel(sfObj);
|
||||
|
||||
return (
|
||||
<>
|
||||
Level {selectedSf.lvl} / {maxLevel}
|
||||
<br />
|
||||
<br />
|
||||
{sfObj.info}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from "react";
|
||||
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { BitNodes } from "../BitNode";
|
||||
import { enterBitNode, setRedPillFlag } from "../../RedPill";
|
||||
import { enterBitNode } from "../../RedPill";
|
||||
import { PortalModal } from "./PortalModal";
|
||||
import { CinematicText } from "../../ui/React/CinematicText";
|
||||
import { use } from "../../ui/Context";
|
||||
@@ -123,7 +123,6 @@ interface IProps {
|
||||
}
|
||||
|
||||
export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
setRedPillFlag(true);
|
||||
const player = use.Player();
|
||||
const enter = enterBitNode;
|
||||
const destroyed = player.bitNodeN;
|
||||
|
||||
+1
-1
@@ -276,7 +276,7 @@ export const CONSTANTS: {
|
||||
CodingContractBaseCompanyRepGain: 4000,
|
||||
CodingContractBaseMoneyGain: 75e6,
|
||||
|
||||
// Augmentation crafting multipliers
|
||||
// Augmentation grafting multipliers
|
||||
AugmentationGraftingCostMult: 3,
|
||||
AugmentationGraftingTimeBase: 3600000,
|
||||
|
||||
|
||||
@@ -161,9 +161,9 @@ export class Material {
|
||||
break;
|
||||
case "Robots":
|
||||
this.dmd = 90;
|
||||
this.dmdR = [80, 9];
|
||||
this.dmdR = [80, 99];
|
||||
this.cmp = 90;
|
||||
this.cmpR = [80, 9];
|
||||
this.cmpR = [80, 99];
|
||||
this.bCost = 75e3;
|
||||
this.mv = 0.5; //Less mv bc its processed twice
|
||||
this.mku = 1;
|
||||
@@ -172,7 +172,7 @@ export class Material {
|
||||
this.dmd = 90;
|
||||
this.dmdR = [80, 99];
|
||||
this.cmp = 90;
|
||||
this.cmpR = [80, 9];
|
||||
this.cmpR = [80, 99];
|
||||
this.bCost = 15e3;
|
||||
this.mv = 0.8; //Less mv bc its processed twice
|
||||
this.mku = 0.5;
|
||||
|
||||
@@ -29,6 +29,16 @@ export class OfficeSpace {
|
||||
[EmployeePositions.RandD]: 0,
|
||||
total: 0,
|
||||
};
|
||||
employeeJobs: { [key: string]: number } = {
|
||||
[EmployeePositions.Operations]: 0,
|
||||
[EmployeePositions.Engineer]: 0,
|
||||
[EmployeePositions.Business]: 0,
|
||||
[EmployeePositions.Management]: 0,
|
||||
[EmployeePositions.RandD]: 0,
|
||||
[EmployeePositions.Training]: 0,
|
||||
[EmployeePositions.Unassigned]: 0,
|
||||
total: 0,
|
||||
};
|
||||
|
||||
constructor(params: IParams = {}) {
|
||||
this.loc = params.loc ? params.loc : "";
|
||||
@@ -48,6 +58,8 @@ export class OfficeSpace {
|
||||
}
|
||||
}
|
||||
|
||||
this.calculateTotalEmployees();
|
||||
|
||||
// Process Office properties
|
||||
this.maxEne = 100;
|
||||
this.maxHap = 100;
|
||||
@@ -101,6 +113,19 @@ export class OfficeSpace {
|
||||
return salaryPaid;
|
||||
}
|
||||
|
||||
calculateTotalEmployees(): void {
|
||||
//Reset
|
||||
for (const name of Object.keys(this.employeeJobs)) {
|
||||
this.employeeJobs[name] = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.employees.length; ++i) {
|
||||
const employee = this.employees[i];
|
||||
this.employeeJobs[employee.pos]++;
|
||||
}
|
||||
this.employeeJobs.total = this.employees.length;
|
||||
}
|
||||
|
||||
calculateEmployeeProductivity(corporation: ICorporation, industry: IIndustry): void {
|
||||
//Reset
|
||||
for (const name of Object.keys(this.employeeProd)) {
|
||||
|
||||
@@ -30,7 +30,7 @@ export function CityTabs(props: IProps): React.ReactElement {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: "65%" }}>
|
||||
<Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: "65vw" }}>
|
||||
{Object.values(division.offices).map(
|
||||
(office: OfficeSpace | 0) => office !== 0 && <Tab key={office.loc} label={office.loc} value={office.loc} />,
|
||||
)}
|
||||
|
||||
@@ -38,7 +38,7 @@ export function CorporationRoot(): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Context.Corporation.Provider value={corporation}>
|
||||
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65%" }} scrollButtons>
|
||||
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65vw" }} scrollButtons>
|
||||
<Tab label={corporation.name} value={"Overview"} />
|
||||
{corporation.divisions.map((div) => (
|
||||
<Tab key={div.name} label={div.name} value={div.name} />
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { ActiveFragment } from "./ActiveFragment";
|
||||
import { IStaneksGift } from "./IStaneksGift";
|
||||
|
||||
export class DummyGift implements IStaneksGift {
|
||||
storedCycles = 0;
|
||||
fragments: ActiveFragment[] = [];
|
||||
_width: number;
|
||||
_height: number;
|
||||
|
||||
constructor(width: number, height: number, fragments: ActiveFragment[]) {
|
||||
this.fragments = fragments;
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
}
|
||||
|
||||
width(): number {
|
||||
return this._width;
|
||||
}
|
||||
height(): number {
|
||||
return this._height;
|
||||
}
|
||||
charge(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
process(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
effect(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
canPlace(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
place(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
findFragment(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
fragmentAt(worldX: number, worldY: number): ActiveFragment | undefined {
|
||||
for (const aFrag of this.fragments) {
|
||||
if (aFrag.fullAt(worldX, worldY)) {
|
||||
return aFrag;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
delete(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
clear(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
count(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
inBonus(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
prestigeAugmentation(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
prestigeSourceFile(): any {
|
||||
throw new Error("unimplemented for dummy gift");
|
||||
}
|
||||
}
|
||||
@@ -12,3 +12,26 @@ export function loadStaneksGift(saveString: string): void {
|
||||
staneksGift = new StaneksGift();
|
||||
}
|
||||
}
|
||||
|
||||
export function zeros(dimensions: number[]): any {
|
||||
const array = [];
|
||||
|
||||
for (let i = 0; i < dimensions[0]; ++i) {
|
||||
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
export function calculateGrid(gift: IStaneksGift): number[][] {
|
||||
const newgrid = zeros([gift.width(), gift.height()]) as unknown as number[][];
|
||||
for (let i = 0; i < gift.width(); i++) {
|
||||
for (let j = 0; j < gift.height(); j++) {
|
||||
const fragment = gift.fragmentAt(i, j);
|
||||
if (!fragment) continue;
|
||||
newgrid[i][j] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return newgrid;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Box, Table } from "@mui/material";
|
||||
import * as React from "react";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { DummyGift } from "../DummyGift";
|
||||
import { Grid } from "./Grid";
|
||||
import { calculateGrid, zeros } from "../Helper";
|
||||
|
||||
interface IProps {
|
||||
width: number;
|
||||
height: number;
|
||||
fragments: ActiveFragment[];
|
||||
}
|
||||
|
||||
export function DummyGrid(props: IProps): React.ReactElement {
|
||||
const gift = new DummyGift(props.width, props.height, props.fragments);
|
||||
const activeGrid = calculateGrid(gift);
|
||||
const ghostGrid = zeros([props.width, props.height]);
|
||||
return (
|
||||
<Box>
|
||||
<Table>
|
||||
<Grid
|
||||
width={props.width}
|
||||
height={props.height}
|
||||
activeGrid={activeGrid}
|
||||
ghostGrid={ghostGrid}
|
||||
gift={gift}
|
||||
enter={() => undefined}
|
||||
click={() => undefined}
|
||||
/>
|
||||
</Table>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { TableBody, TableRow } from "@mui/material";
|
||||
import * as React from "react";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import { Cell } from "./Cell";
|
||||
|
||||
interface IProps {
|
||||
width: number;
|
||||
height: number;
|
||||
activeGrid: number[][];
|
||||
ghostGrid: number[][];
|
||||
gift: IStaneksGift;
|
||||
enter(i: number, j: number): void;
|
||||
click(i: number, j: number): void;
|
||||
}
|
||||
|
||||
function randomColor(fragment: ActiveFragment): string {
|
||||
// Can't set Math.random seed so copy casino. TODO refactor both RNG later.
|
||||
let s1 = Math.pow((fragment.x + 1) * (fragment.y + 1), 10);
|
||||
let s2 = s1;
|
||||
let s3 = s1;
|
||||
|
||||
const colors = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
s1 = (171 * s1) % 30269;
|
||||
s2 = (172 * s2) % 30307;
|
||||
s3 = (170 * s3) % 30323;
|
||||
colors.push((s1 / 30269.0 + s2 / 30307.0 + s3 / 30323.0) % 1.0);
|
||||
}
|
||||
|
||||
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
|
||||
}
|
||||
|
||||
export function Grid(props: IProps): React.ReactElement {
|
||||
function color(worldX: number, worldY: number): string {
|
||||
if (props.ghostGrid[worldX][worldY] && props.activeGrid[worldX][worldY]) return "red";
|
||||
if (props.ghostGrid[worldX][worldY]) return "white";
|
||||
|
||||
if (props.activeGrid[worldX][worldY]) {
|
||||
const fragment = props.gift.fragmentAt(worldX, worldY);
|
||||
if (!fragment) throw new Error("ActiveFragment should not be null");
|
||||
return randomColor(fragment);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// switch the width/length to make axis consistent.
|
||||
const elems = [];
|
||||
for (let j = 0; j < props.height; j++) {
|
||||
const cells = [];
|
||||
for (let i = 0; i < props.width; i++) {
|
||||
cells.push(
|
||||
<Cell key={i} onMouseEnter={() => props.enter(i, j)} onClick={() => props.click(i, j)} color={color(i, j)} />,
|
||||
);
|
||||
}
|
||||
elems.push(<TableRow key={j}>{cells}</TableRow>);
|
||||
}
|
||||
|
||||
return <TableBody>{elems}</TableBody>;
|
||||
}
|
||||
+11
-78
@@ -1,62 +1,20 @@
|
||||
import * as React from "react";
|
||||
import { Fragment, NoneFragment } from "../Fragment";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { FragmentType } from "../FragmentType";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import { Cell } from "./Cell";
|
||||
import { FragmentInspector } from "./FragmentInspector";
|
||||
import { FragmentSelector } from "./FragmentSelector";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import { Table } from "../../ui/React/Table";
|
||||
|
||||
function zeros(dimensions: number[]): any {
|
||||
const array = [];
|
||||
|
||||
for (let i = 0; i < dimensions[0]; ++i) {
|
||||
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
function randomColor(fragment: ActiveFragment): string {
|
||||
// Can't set Math.random seed so copy casino. TODO refactor both RNG later.
|
||||
let s1 = Math.pow((fragment.x + 1) * (fragment.y + 1), 10);
|
||||
let s2 = s1;
|
||||
let s3 = s1;
|
||||
|
||||
const colors = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
s1 = (171 * s1) % 30269;
|
||||
s2 = (172 * s2) % 30307;
|
||||
s3 = (170 * s3) % 30323;
|
||||
colors.push((s1 / 30269.0 + s2 / 30307.0 + s3 / 30323.0) % 1.0);
|
||||
}
|
||||
|
||||
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
|
||||
}
|
||||
import { Grid } from "./Grid";
|
||||
import { zeros, calculateGrid } from "../Helper";
|
||||
|
||||
interface IProps {
|
||||
gift: IStaneksGift;
|
||||
}
|
||||
|
||||
export function MainBoard(props: IProps): React.ReactElement {
|
||||
function calculateGrid(gift: IStaneksGift): any {
|
||||
const newgrid = zeros([gift.width(), gift.height()]);
|
||||
for (let i = 0; i < gift.width(); i++) {
|
||||
for (let j = 0; j < gift.height(); j++) {
|
||||
const fragment = gift.fragmentAt(i, j);
|
||||
if (!fragment) continue;
|
||||
newgrid[i][j] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return newgrid;
|
||||
}
|
||||
|
||||
const [grid, setGrid] = React.useState(calculateGrid(props.gift));
|
||||
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
|
||||
const [pos, setPos] = React.useState([0, 0]);
|
||||
@@ -96,44 +54,11 @@ export function MainBoard(props: IProps): React.ReactElement {
|
||||
setGrid(calculateGrid(props.gift));
|
||||
}
|
||||
|
||||
function color(worldX: number, worldY: number): string {
|
||||
if (ghostGrid[worldX][worldY] && grid[worldX][worldY]) return "red";
|
||||
if (ghostGrid[worldX][worldY]) return "white";
|
||||
|
||||
if (grid[worldX][worldY]) {
|
||||
const fragment = props.gift.fragmentAt(worldX, worldY);
|
||||
if (!fragment) throw new Error("ActiveFragment should not be null");
|
||||
return randomColor(fragment);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function clear(): void {
|
||||
props.gift.clear();
|
||||
setGrid(zeros([props.gift.width(), props.gift.height()]));
|
||||
}
|
||||
|
||||
// switch the width/length to make axis consistent.
|
||||
const elems = [];
|
||||
for (let j = 0; j < props.gift.height(); j++) {
|
||||
const cells = [];
|
||||
for (let i = 0; i < props.gift.width(); i++) {
|
||||
cells.push(
|
||||
<Cell
|
||||
key={i}
|
||||
onMouseEnter={() => moveGhost(i, j, rotation)}
|
||||
onClick={() => clickAt(i, j)}
|
||||
color={color(i, j)}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
elems.push(
|
||||
<TableRow key={j} className="staneksgift_row">
|
||||
{cells}
|
||||
</TableRow>,
|
||||
);
|
||||
}
|
||||
|
||||
function updateSelectedFragment(fragment: Fragment): void {
|
||||
setSelectedFragment(fragment);
|
||||
const newgrid = zeros([props.gift.width(), props.gift.height()]);
|
||||
@@ -162,7 +87,15 @@ export function MainBoard(props: IProps): React.ReactElement {
|
||||
<Button onClick={clear}>Clear</Button>
|
||||
<Box display="flex">
|
||||
<Table>
|
||||
<TableBody>{elems}</TableBody>
|
||||
<Grid
|
||||
width={props.gift.width()}
|
||||
height={props.gift.height()}
|
||||
activeGrid={grid}
|
||||
ghostGrid={ghostGrid}
|
||||
gift={props.gift}
|
||||
enter={(i, j) => moveGhost(i, j, rotation)}
|
||||
click={(i, j) => clickAt(i, j)}
|
||||
/>
|
||||
</Table>
|
||||
<FragmentInspector gift={props.gift} x={pos[0]} y={pos[1]} fragment={props.gift.fragmentAt(pos[0], pos[1])} />
|
||||
</Box>
|
||||
|
||||
@@ -4,7 +4,12 @@ import { CONSTANTS } from "../../Constants";
|
||||
import { StaneksGiftEvents } from "../StaneksGiftEvents";
|
||||
import { MainBoard } from "./MainBoard";
|
||||
import { IStaneksGift } from "../IStaneksGift";
|
||||
import { Info } from "@mui/icons-material";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { ActiveFragment } from "../ActiveFragment";
|
||||
import { Fragments } from "../Fragment";
|
||||
import { DummyGrid } from "./DummyGrid";
|
||||
|
||||
type IProps = {
|
||||
staneksGift: IStaneksGift;
|
||||
@@ -18,7 +23,162 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
|
||||
useEffect(() => StaneksGiftEvents.subscribe(rerender), []);
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">Stanek's Gift</Typography>
|
||||
<Typography variant="h4">
|
||||
Stanek's Gift
|
||||
<Info
|
||||
sx={{ ml: 1, mb: 0 }}
|
||||
color="info"
|
||||
onClick={() =>
|
||||
dialogBoxCreate(
|
||||
<>
|
||||
<Typography>
|
||||
Stanek's Gift is a powerful, unique augmentation obtained by joining the Church of the Machine God,
|
||||
which can be found in Chongqing. It is only possible to join the church if you are in BitNode 13 or
|
||||
possess at least one level of Source File 13, and you will be turned away if you have purchased or
|
||||
installed any augmentations beside NeuroFlux Governors. You are, however, permitted to install
|
||||
augmentations as normal once you have joined the church.
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Initially, those bearing the gift will find that its overwhelming power worsens all of their stats by
|
||||
10%. This penalty can be overcome in time by receiving free upgrades from the Church of the Machine
|
||||
God faction, but the reputation requirements for these upgrades are steep, and you can only obtain
|
||||
reputation with the church by charging the gift.
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
In order to charge Stanek's Gift, the user must first arrange stat-modifying fragments within the grid
|
||||
located on the device. This may be done manually or by scripts that utilize the Stanek's Gift
|
||||
Netscript API. Not every tile of the grid must house a fragment, but fragments cannot overlap or
|
||||
otherwise share tiles. Fragments can be rotated, but their design does not allow them to be flipped
|
||||
around to mirror their original shape. Note that the size of the grid is determined by the BitNode you
|
||||
are currently in and the level of your Source File 13, if applicable.
|
||||
</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
There exist two kinds of fragments. The first are Stat Fragments, which take up 4 tiles of the grid.
|
||||
Each Stat Fragment is unique, and there is only one of each. There is no way to obtain more of these
|
||||
fragments. Each Stat Fragment has an associated stat that it will improve, as well as a multiplier on
|
||||
its effectiveness known as power. When initially placed, a Stat Fragment will have no effect. In order
|
||||
for the fragment to gain stat boosts, it must be charged. The other kind of fragments are known as
|
||||
Booster Fragments, which take up 5 tiles of the grid. There is no shortage of Booster Fragments, and
|
||||
it is virtually impossible to run out of them. While not providing any direct stat increases to their
|
||||
user, Stat Fragments increase the efficacy of adjacent Stat Fragments by 10%, and do not need to be
|
||||
charged.
|
||||
</Typography>
|
||||
|
||||
<DummyGrid
|
||||
width={4}
|
||||
height={4}
|
||||
fragments={[
|
||||
new ActiveFragment({
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: 0,
|
||||
fragment: Fragments.find((f) => f.id === 5) ?? Fragments[0],
|
||||
}),
|
||||
new ActiveFragment({
|
||||
x: 0,
|
||||
y: 2,
|
||||
rotation: 0,
|
||||
fragment: Fragments.find((f) => f.id === 101) ?? Fragments[0],
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
<Typography sx={{ fontStyle: "italic" }}>
|
||||
This boost provides a bonus to the touching fragment
|
||||
</Typography>
|
||||
|
||||
<DummyGrid
|
||||
width={4}
|
||||
height={4}
|
||||
fragments={[
|
||||
new ActiveFragment({
|
||||
x: 0,
|
||||
y: 1,
|
||||
rotation: 3,
|
||||
fragment: Fragments.find((f) => f.id === 100) ?? Fragments[0],
|
||||
}),
|
||||
new ActiveFragment({
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: 2,
|
||||
fragment: Fragments.find((f) => f.id === 1) ?? Fragments[0],
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
<Typography sx={{ fontStyle: "italic" }}>
|
||||
Even though the booster touches many tiles, the bonus is only applied once.
|
||||
</Typography>
|
||||
|
||||
<DummyGrid
|
||||
width={4}
|
||||
height={4}
|
||||
fragments={[
|
||||
new ActiveFragment({
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: 0,
|
||||
fragment: Fragments.find((f) => f.id === 5) ?? Fragments[0],
|
||||
}),
|
||||
new ActiveFragment({
|
||||
x: 2,
|
||||
y: 0,
|
||||
rotation: 0,
|
||||
fragment: Fragments.find((f) => f.id === 105) ?? Fragments[0],
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
<Typography sx={{ fontStyle: "italic" }}>
|
||||
Even though the booster touches many tiles, the bonus is only applied once.
|
||||
</Typography>
|
||||
|
||||
<DummyGrid
|
||||
width={4}
|
||||
height={4}
|
||||
fragments={[
|
||||
new ActiveFragment({
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: 1,
|
||||
fragment: Fragments.find((f) => f.id === 27) ?? Fragments[0],
|
||||
}),
|
||||
new ActiveFragment({
|
||||
x: 0,
|
||||
y: 1,
|
||||
rotation: 2,
|
||||
fragment: Fragments.find((f) => f.id === 100) ?? Fragments[0],
|
||||
}),
|
||||
new ActiveFragment({
|
||||
x: 2,
|
||||
y: 0,
|
||||
rotation: 1,
|
||||
fragment: Fragments.find((f) => f.id === 30) ?? Fragments[0],
|
||||
}),
|
||||
]}
|
||||
/>
|
||||
<Typography sx={{ fontStyle: "italic" }}>
|
||||
This booster provides bonus to all fragment it touches.
|
||||
</Typography>
|
||||
|
||||
<br />
|
||||
<Typography>
|
||||
Stat Fragments are charged using the stanek.chargeFragment(rootX, rootY) NetScript API function. The
|
||||
charging process ordinarily takes 1000ms to complete, but only takes 200ms during bonus time. When the
|
||||
function finishes executing, the fragment's charge levels will be raised by an amount corresponding to
|
||||
the number of threads that were used. Note that it is no more effective to charge a fragment many
|
||||
times with few threads than to charge few times with many threads, so there is no need to distribute
|
||||
charging jobs across multiple scripts. As a Stat Fragment's charge level is increased, its bonuses
|
||||
will increase, but there will be diminishing returns. As such, it is generally most efficient to
|
||||
charge all of the placed fragments equally. The charge level of a fragment will not decrease over
|
||||
time, but it will be reset to 0 upon removing it from the board or installing augmentations.
|
||||
</Typography>
|
||||
</>,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Typography>
|
||||
|
||||
<Typography>
|
||||
The gift is a grid on which you can place upgrades called fragments. The main type of fragment increases a stat,
|
||||
like your hacking skill or agility exp. Once a stat fragment is placed it then needs to be charged via scripts
|
||||
|
||||
+5
-10
@@ -72,17 +72,12 @@ export function buyDarkwebItem(itemName: string): void {
|
||||
// buy and push
|
||||
Player.loseMoney(item.price, "other");
|
||||
|
||||
const programsRef = Player.getHomeComputer().programs;
|
||||
// Remove partially created program if there is one
|
||||
const existingPartialExeIndex = programsRef.findIndex(
|
||||
(program) => item?.program && program.startsWith(item?.program),
|
||||
);
|
||||
// findIndex returns -1 if there is no match, we only want to splice on a match
|
||||
if (existingPartialExeIndex > -1) {
|
||||
programsRef.splice(existingPartialExeIndex, 1);
|
||||
Player.getHomeComputer().pushProgram(item.program);
|
||||
// Cancel if the program is in progress of writing
|
||||
if (Player.createProgramName === item.program) {
|
||||
Player.isWorking = false;
|
||||
Player.resetWorkStatus();
|
||||
}
|
||||
// Add the newly bought, full .exe
|
||||
Player.getHomeComputer().programs.push(item.program);
|
||||
|
||||
Terminal.print(
|
||||
"You have purchased the " + item.program + " program. The new program can be found on your home computer.",
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { Clear, ExpandMore, Reply, ReplyAll } from "@mui/icons-material";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Button,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import Accordion from "@mui/material/Accordion";
|
||||
import AccordionSummary from "@mui/material/AccordionSummary";
|
||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import ReplyAllIcon from "@mui/icons-material/ReplyAll";
|
||||
import ReplyIcon from "@mui/icons-material/Reply";
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
@@ -39,50 +38,46 @@ export function Augmentations(props: IProps): React.ReactElement {
|
||||
props.player.augmentations = [];
|
||||
}
|
||||
|
||||
function clearQueuedAugs(): void {
|
||||
props.player.queuedAugmentations = [];
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion TransitionProps={{ unmountOnExit: true }}>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<AccordionSummary expandIcon={<ExpandMore />}>
|
||||
<Typography>Augmentations</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<Typography>Aug:</Typography>
|
||||
</td>
|
||||
<td>
|
||||
<Select
|
||||
onChange={setAugmentationDropdown}
|
||||
value={augmentation}
|
||||
startAdornment={
|
||||
<>
|
||||
<IconButton onClick={queueAllAugs} size="large">
|
||||
<ReplyAllIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={queueAug} size="large">
|
||||
<ReplyIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
endAdornment={
|
||||
<>
|
||||
<IconButton onClick={clearAugs} size="large">
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
{Object.values(AugmentationNames).map((aug) => (
|
||||
<MenuItem key={aug} value={aug}>
|
||||
{aug}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Select
|
||||
onChange={setAugmentationDropdown}
|
||||
value={augmentation}
|
||||
startAdornment={
|
||||
<>
|
||||
<IconButton onClick={queueAllAugs} size="large">
|
||||
<ReplyAll />
|
||||
</IconButton>
|
||||
<IconButton onClick={queueAug} size="large">
|
||||
<Reply />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
endAdornment={
|
||||
<>
|
||||
<IconButton onClick={clearAugs} size="large">
|
||||
<Clear />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
{Object.values(AugmentationNames).map((aug) => (
|
||||
<MenuItem key={aug} value={aug}>
|
||||
{aug}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<Button sx={{ display: "block" }} onClick={clearQueuedAugs}>
|
||||
Clear Queued Augmentations
|
||||
</Button>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
+6
-7
@@ -2,7 +2,7 @@ import { Player } from "./Player";
|
||||
import { Router } from "./ui/GameRoot";
|
||||
import { removeLeadingSlash } from "./Terminal/DirectoryHelpers";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "./ui/React/Snackbar";
|
||||
import { IMap, IReturnStatus } from "./types";
|
||||
import { GetServer } from "./Server/AllServers";
|
||||
import { ImportPlayerData, SaveData, saveObject } from "./SaveObject";
|
||||
@@ -111,8 +111,7 @@ function initAppNotifier(): void {
|
||||
if (!fn) fn = Terminal.print;
|
||||
fn.bind(Terminal)(message);
|
||||
},
|
||||
toast: (message: string, type: "info" | "success" | "warning" | "error", duration = 2000) =>
|
||||
SnackbarEvents.emit(message, type, duration),
|
||||
toast: (message: string, type: ToastVariant, duration = 2000) => SnackbarEvents.emit(message, type, duration),
|
||||
};
|
||||
|
||||
// Will be consumud by the electron wrapper.
|
||||
@@ -127,7 +126,7 @@ function initSaveFunctions(): void {
|
||||
saveObject.exportGame();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export game.", "error", 2000);
|
||||
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
|
||||
}
|
||||
},
|
||||
triggerScriptsExport: (): void => exportScripts("*", Player.getHomeComputer()),
|
||||
@@ -176,7 +175,7 @@ function initElectronBridge(): void {
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not save game.", "error", 2000);
|
||||
SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000);
|
||||
});
|
||||
});
|
||||
bridge.receive("trigger-game-export", () => {
|
||||
@@ -184,7 +183,7 @@ function initElectronBridge(): void {
|
||||
(window as any).appSaveFns.triggerGameExport();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export game.", "error", 2000);
|
||||
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
|
||||
}
|
||||
});
|
||||
bridge.receive("trigger-scripts-export", () => {
|
||||
@@ -192,7 +191,7 @@ function initElectronBridge(): void {
|
||||
(window as any).appSaveFns.triggerScriptsExport();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
SnackbarEvents.emit("Could not export scripts.", "error", 2000);
|
||||
SnackbarEvents.emit("Could not export scripts.", ToastVariant.ERROR, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,7 +81,6 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
|
||||
}
|
||||
|
||||
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
|
||||
const factionInfo = fac.getInfo();
|
||||
const hasPrereqs = hasAugmentationPrereqs(aug);
|
||||
if (!hasPrereqs) {
|
||||
const txt = `You must first purchase or install ${aug.prereqs.join(",")} before you can purchase this one.`;
|
||||
@@ -90,7 +89,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
|
||||
} else {
|
||||
dialogBoxCreate(txt);
|
||||
}
|
||||
} else if (aug.baseCost !== 0 && Player.money < aug.baseCost * factionInfo.augmentationPriceMult) {
|
||||
} else if (aug.baseCost !== 0 && Player.money < aug.baseCost) {
|
||||
const txt = "You don't have enough money to purchase " + aug.name;
|
||||
if (sing) {
|
||||
return txt;
|
||||
@@ -102,14 +101,14 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
|
||||
return txt;
|
||||
}
|
||||
dialogBoxCreate(txt);
|
||||
} else if (aug.baseCost === 0 || Player.money >= aug.baseCost * factionInfo.augmentationPriceMult) {
|
||||
} else if (aug.baseCost === 0 || Player.money >= aug.baseCost) {
|
||||
const queuedAugmentation = new PlayerOwnedAugmentation(aug.name);
|
||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||
queuedAugmentation.level = getNextNeurofluxLevel();
|
||||
}
|
||||
Player.queuedAugmentations.push(queuedAugmentation);
|
||||
|
||||
Player.loseMoney(aug.baseCost * factionInfo.augmentationPriceMult, "augmentations");
|
||||
Player.loseMoney(aug.baseCost, "augmentations");
|
||||
|
||||
// If you just purchased Neuroflux Governor, recalculate the cost
|
||||
if (aug.name == AugmentationNames.NeuroFluxGovernor) {
|
||||
@@ -203,7 +202,7 @@ export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Factio
|
||||
// Remove special augs
|
||||
augs = augs.filter((a) => !a.isSpecial);
|
||||
|
||||
const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor];
|
||||
const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor, AugmentationNames.CongruityImplant];
|
||||
|
||||
if (player.bitNodeN !== 2) {
|
||||
// TRP is not available outside of BN2 for Gangs
|
||||
|
||||
+233
-361
@@ -2,20 +2,20 @@ import React from "react";
|
||||
import { IMap } from "../types";
|
||||
import { FactionNames } from "./data/FactionNames";
|
||||
|
||||
interface FactionInfoParams {
|
||||
infoText?: JSX.Element;
|
||||
enemies?: string[];
|
||||
offerHackingWork?: boolean;
|
||||
offerFieldWork?: boolean;
|
||||
offerSecurityWork?: boolean;
|
||||
special?: boolean;
|
||||
keepOnInstall?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the "information" property for all the Factions, which is just a description of each faction
|
||||
*/
|
||||
export class FactionInfo {
|
||||
/**
|
||||
* The multiplier to apply to augmentation base purchase price.
|
||||
*/
|
||||
augmentationPriceMult: number;
|
||||
|
||||
/**
|
||||
* The multiplier to apply to augmentation reputation base requirement.
|
||||
*/
|
||||
augmentationRepRequirementMult: number;
|
||||
|
||||
/**
|
||||
* The names of all other factions considered to be enemies to this faction.
|
||||
*/
|
||||
@@ -31,11 +31,6 @@ export class FactionInfo {
|
||||
*/
|
||||
offerFieldWork: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports hacking missions to earn reputation.
|
||||
*/
|
||||
offerHackingMission: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports hacking work to earn reputation.
|
||||
*/
|
||||
@@ -56,32 +51,19 @@ export class FactionInfo {
|
||||
*/
|
||||
special: boolean;
|
||||
|
||||
constructor(
|
||||
infoText: JSX.Element,
|
||||
enemies: string[],
|
||||
offerHackingMission: boolean,
|
||||
offerHackingWork: boolean,
|
||||
offerFieldWork: boolean,
|
||||
offerSecurityWork: boolean,
|
||||
special: boolean,
|
||||
keep: boolean,
|
||||
) {
|
||||
this.infoText = infoText;
|
||||
this.enemies = enemies;
|
||||
this.offerHackingMission = offerHackingMission;
|
||||
this.offerHackingWork = offerHackingWork;
|
||||
this.offerFieldWork = offerFieldWork;
|
||||
this.offerSecurityWork = offerSecurityWork;
|
||||
constructor(params: FactionInfoParams) {
|
||||
this.infoText = params.infoText ?? <></>;
|
||||
this.enemies = params.enemies ?? [];
|
||||
this.offerHackingWork = params.offerHackingWork ?? false;
|
||||
this.offerFieldWork = params.offerFieldWork ?? false;
|
||||
this.offerSecurityWork = params.offerSecurityWork ?? false;
|
||||
|
||||
// These are always all 1 for now.
|
||||
this.augmentationPriceMult = 1;
|
||||
this.augmentationRepRequirementMult = 1;
|
||||
this.keep = keep;
|
||||
this.special = special;
|
||||
this.keep = params.keepOnInstall ?? false;
|
||||
this.special = params.special ?? false;
|
||||
}
|
||||
|
||||
offersWork(): boolean {
|
||||
return this.offerFieldWork || this.offerHackingMission || this.offerHackingWork || this.offerSecurityWork;
|
||||
return this.offerFieldWork || this.offerHackingWork || this.offerSecurityWork;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,35 +73,25 @@ export class FactionInfo {
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const FactionInfos: IMap<FactionInfo> = {
|
||||
// Endgame
|
||||
[FactionNames.Illuminati]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.Illuminati]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
Humanity never changes. No matter how civilized society becomes, it will eventually fall back into chaos. And
|
||||
from this chaos, we are the invisible hand that guides them to order.{" "}
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.Daedalus]: new FactionInfo(
|
||||
<>Yesterday we obeyed kings and bent our necks to emperors. Today we kneel only to truth.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Daedalus]: new FactionInfo({
|
||||
infoText: <>Yesterday we obeyed kings and bent our necks to emperors. Today we kneel only to truth.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.TheCovenant]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.TheCovenant]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
Surrender yourself. Give up your empty individuality to become part of something great, something eternal.
|
||||
Become a slave. Submit your mind, body, and soul. Only then can you set yourself free.
|
||||
@@ -128,35 +100,27 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
Only then can you discover immortality.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
}),
|
||||
|
||||
// Megacorporations, each forms its own faction
|
||||
[FactionNames.ECorp]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.ECorp]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
{FactionNames.ECorp}'s mission is simple: to connect the world of today with the technology of tomorrow. With
|
||||
our wide range of Internet-related software and commercial hardware, {FactionNames.ECorp} makes the world's
|
||||
information universally accessible.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.MegaCorp]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.MegaCorp]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
{FactionNames.MegaCorp} does what no other dares to do. We imagine. We create. We invent. We create what others
|
||||
have never even dreamed of. Our work fills the world's needs for food, water, power, and transportation on an
|
||||
@@ -167,17 +131,14 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
the world.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.BachmanAssociates]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.BachmanAssociates]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
Where Law and Business meet - thats where we are.
|
||||
<br />
|
||||
@@ -185,112 +146,87 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
Legal Insight - Business Instinct - Innovative Experience.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.BladeIndustries]: new FactionInfo(
|
||||
<>Augmentation is Salvation.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
[FactionNames.BladeIndustries]: new FactionInfo({
|
||||
infoText: <>Augmentation is Salvation.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.NWO]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.NWO]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
Humans don't truly desire freedom. They want to be observed, understood, and judged. They want to be given
|
||||
purpose and direction in life. That is why they created God. And that is why they created civilization - not
|
||||
because of willingness, but because of a need to be incorporated into higher orders of structure and meaning.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.ClarkeIncorporated]: new FactionInfo(
|
||||
<>The Power of the Genome - Unlocked.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
[FactionNames.ClarkeIncorporated]: new FactionInfo({
|
||||
infoText: <>The Power of the Genome - Unlocked.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.OmniTekIncorporated]: new FactionInfo(
|
||||
<>Simply put, our mission is to design and build robots that make a difference.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
[FactionNames.OmniTekIncorporated]: new FactionInfo({
|
||||
infoText: <>Simply put, our mission is to design and build robots that make a difference.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.FourSigma]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.FourSigma]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
The scientific method is the best way to approach investing. Big strategies backed up with big data. Driven by
|
||||
deep learning and innovative ideas. And improved by iteration. That's {FactionNames.FourSigma}.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
[FactionNames.KuaiGongInternational]: new FactionInfo(
|
||||
<>Dream big. Work hard. Make history.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
[FactionNames.KuaiGongInternational]: new FactionInfo({
|
||||
infoText: <>Dream big. Work hard. Make history.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
// Other Corporations
|
||||
[FactionNames.FulcrumSecretTechnologies]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.FulcrumSecretTechnologies]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
The human organism has an innate desire to worship. That is why they created gods. If there were no gods, it
|
||||
would be necessary to create them. And now we can.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerSecurityWork: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
|
||||
// Hacker groups
|
||||
[FactionNames.BitRunners]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.BitRunners]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
Our entire lives are controlled by bits. All of our actions, our thoughts, our personal information. It's all
|
||||
transformed into bits, stored in bits, communicated through bits. It’s impossible for any person to move, to
|
||||
@@ -302,17 +238,11 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
Those who run the bits, run the world.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.TheBlackHand]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.TheBlackHand]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
The world, so afraid of strong government, now has no government. Only power - Digital power. Financial power.
|
||||
Technological power. And those at the top rule with an invisible hand. They built a society where the rich get
|
||||
@@ -322,17 +252,13 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
So much pain. So many lives. Their darkness must end.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
}),
|
||||
|
||||
// prettier-ignore
|
||||
[FactionNames.NiteSec]: new FactionInfo(<>
|
||||
[FactionNames.NiteSec]: new FactionInfo({
|
||||
infoText:(<>
|
||||
{" __..__ "}<br />
|
||||
{" _.nITESECNIt. "}<br />
|
||||
{" .-'NITESECNITESEc. "}<br />
|
||||
@@ -367,105 +293,87 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
{" / .d/$/$; , ; "}<br />
|
||||
{" d .dNITESEC $ | "}<br />
|
||||
{" :bp.__.gNITESEC/$ :$ ; "}<br />
|
||||
{" NITESECNITESECNIT /$b : "}<br /></>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
{" NITESECNITESECNIT /$b : "}<br /></>),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: false,
|
||||
offerSecurityWork: false,
|
||||
special: false,
|
||||
keepOnInstall: false,
|
||||
}),
|
||||
|
||||
// City factions, essentially governments
|
||||
[FactionNames.Aevum]: new FactionInfo(
|
||||
<>The Silicon City.</>,
|
||||
[FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Chongqing]: new FactionInfo(
|
||||
<>Serve the People.</>,
|
||||
[FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Ishima]: new FactionInfo(
|
||||
<>The East Asian Order of the Future.</>,
|
||||
[FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.NewTokyo]: new FactionInfo(
|
||||
<>Asia's World City.</>,
|
||||
[FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Sector12]: new FactionInfo(
|
||||
<>The City of the Future.</>,
|
||||
[FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Volhaven]: new FactionInfo(
|
||||
<>Benefit, Honor, and Glory.</>,
|
||||
[FactionNames.Chongqing, FactionNames.Sector12, FactionNames.NewTokyo, FactionNames.Aevum, FactionNames.Ishima],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Aevum]: new FactionInfo({
|
||||
infoText: <>The Silicon City.</>,
|
||||
enemies: [FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
[FactionNames.Chongqing]: new FactionInfo({
|
||||
infoText: <>Serve the People.</>,
|
||||
enemies: [FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
[FactionNames.Ishima]: new FactionInfo({
|
||||
infoText: <>The East Asian Order of the Future.</>,
|
||||
enemies: [FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
[FactionNames.NewTokyo]: new FactionInfo({
|
||||
infoText: <>Asia's World City.</>,
|
||||
enemies: [FactionNames.Sector12, FactionNames.Aevum, FactionNames.Volhaven],
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
[FactionNames.Sector12]: new FactionInfo({
|
||||
infoText: <>The City of the Future.</>,
|
||||
enemies: [FactionNames.Chongqing, FactionNames.NewTokyo, FactionNames.Ishima, FactionNames.Volhaven],
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
[FactionNames.Volhaven]: new FactionInfo({
|
||||
infoText: <>Benefit, Honor, and Glory.</>,
|
||||
enemies: [
|
||||
FactionNames.Chongqing,
|
||||
FactionNames.Sector12,
|
||||
FactionNames.NewTokyo,
|
||||
FactionNames.Aevum,
|
||||
FactionNames.Ishima,
|
||||
],
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
|
||||
// Criminal Organizations/Gangs
|
||||
[FactionNames.SpeakersForTheDead]: new FactionInfo(
|
||||
<>It is better to reign in Hell than to serve in Heaven.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.SpeakersForTheDead]: new FactionInfo({
|
||||
infoText: <>It is better to reign in Hell than to serve in Heaven.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.TheDarkArmy]: new FactionInfo(
|
||||
<>The World doesn't care about right or wrong. It only cares about power.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.TheDarkArmy]: new FactionInfo({
|
||||
infoText: <>The World doesn't care about right or wrong. It only cares about power.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.TheSyndicate]: new FactionInfo(<>Honor holds you back.</>, [], true, true, true, true, false, false),
|
||||
[FactionNames.TheSyndicate]: new FactionInfo({
|
||||
infoText: <>Honor holds you back.</>,
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.Silhouette]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.Silhouette]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
Corporations have filled the void of power left behind by the collapse of Western government. The issue is
|
||||
they've become so big that you don't know who they're working for. And if you're employed at one of these
|
||||
@@ -475,100 +383,66 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
That's terror. Terror, fear, and corruption. All born into the system, all propagated by the system.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
offerFieldWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.Tetrads]: new FactionInfo(
|
||||
<>Following the mandate of Heaven and carrying out the way.</>,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Tetrads]: new FactionInfo({
|
||||
infoText: <>Following the mandate of Heaven and carrying out the way.</>,
|
||||
|
||||
[FactionNames.SlumSnakes]: new FactionInfo(
|
||||
<>{FactionNames.SlumSnakes} rule!</>,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.SlumSnakes]: new FactionInfo({
|
||||
infoText: <>{FactionNames.SlumSnakes} rule!</>,
|
||||
|
||||
offerFieldWork: true,
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
|
||||
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
|
||||
[FactionNames.Netburners]: new FactionInfo(
|
||||
<>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.Netburners]: new FactionInfo({
|
||||
infoText: <>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>,
|
||||
offerHackingWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.TianDiHui]: new FactionInfo(
|
||||
<>Obey Heaven and work righteously.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
[FactionNames.TianDiHui]: new FactionInfo({
|
||||
infoText: <>Obey Heaven and work righteously.</>,
|
||||
offerHackingWork: true,
|
||||
|
||||
[FactionNames.CyberSec]: new FactionInfo(
|
||||
(
|
||||
offerSecurityWork: true,
|
||||
}),
|
||||
|
||||
[FactionNames.CyberSec]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
The Internet is the first thing that was built that we don't fully understand, the largest experiment in anarchy
|
||||
that we have ever had. And as the world becomes increasingly dominated by it, society approaches the brink of
|
||||
total chaos. We serve only to protect society, to protect humanity, to protect the world from imminent collapse.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
offerHackingWork: true,
|
||||
}),
|
||||
|
||||
// Special Factions
|
||||
[FactionNames.Bladeburners]: new FactionInfo(
|
||||
(
|
||||
[FactionNames.Bladeburners]: new FactionInfo({
|
||||
infoText: (
|
||||
<>
|
||||
It's too bad they won't live. But then again, who does?
|
||||
<br />
|
||||
<br />
|
||||
Note that for this faction, reputation can only be gained through {FactionNames.Bladeburners} actions.
|
||||
Completing {FactionNames.Bladeburners}
|
||||
contracts/operations will increase your reputation.
|
||||
Note that for this faction, reputation can only be gained through {FactionNames.Bladeburners} actions.{" "}
|
||||
Completing {FactionNames.Bladeburners} contracts/operations will increase your reputation.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
special: true,
|
||||
}),
|
||||
|
||||
// prettier-ignore
|
||||
[FactionNames.ChurchOfTheMachineGod]: new FactionInfo(<>
|
||||
[FactionNames.ChurchOfTheMachineGod]: new FactionInfo({
|
||||
infoText:(<>
|
||||
{" `` "}<br />
|
||||
{" -odmmNmds: "}<br />
|
||||
{" `hNmo:..-omNh. "}<br />
|
||||
@@ -600,13 +474,11 @@ export const FactionInfos: IMap<FactionInfo> = {
|
||||
Many cultures predict an end to humanity in the near future, a final
|
||||
Armageddon that will end the world; but we disagree.
|
||||
<br /><br />Note that for this faction, reputation can
|
||||
only be gained by charging Stanek's gift.</>,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
only be gained by charging Stanek's gift.</>),
|
||||
offerHackingWork: false,
|
||||
offerFieldWork: false,
|
||||
offerSecurityWork: false,
|
||||
special: true,
|
||||
keepOnInstall: true,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -78,10 +78,10 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
const augs = getAugs();
|
||||
function canBuy(augName: string): boolean {
|
||||
const aug = Augmentations[augName];
|
||||
const repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
|
||||
const repCost = aug.baseRepRequirement;
|
||||
const hasReq = props.faction.playerReputation >= repCost;
|
||||
const hasRep = hasAugmentationPrereqs(aug);
|
||||
const hasCost = aug.baseCost !== 0 && player.money > aug.baseCost * props.faction.getInfo().augmentationPriceMult;
|
||||
const hasCost = aug.baseCost !== 0 && player.money > aug.baseCost;
|
||||
return hasCost && hasReq && hasRep;
|
||||
}
|
||||
const buy = augs.filter(canBuy).sort((augName1, augName2) => {
|
||||
|
||||
@@ -20,7 +20,6 @@ interface IProps {
|
||||
|
||||
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const factionInfo = props.faction.getInfo();
|
||||
|
||||
function buy(): void {
|
||||
if (!isRepeatableAug(props.aug) && player.hasAugmentation(props.aug)) {
|
||||
@@ -43,7 +42,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
<br />
|
||||
Would you like to purchase the {props.aug.name} Augmentation for
|
||||
<Money money={props.aug.baseCost * factionInfo.augmentationPriceMult} />?
|
||||
<Money money={props.aug.baseCost} />?
|
||||
<br />
|
||||
<br />
|
||||
</Typography>
|
||||
|
||||
@@ -84,11 +84,11 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const moneyCost = aug.baseCost * props.faction.getInfo().augmentationPriceMult;
|
||||
const repCost = aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
|
||||
const moneyCost = aug.baseCost;
|
||||
const repCost = aug.baseRepRequirement;
|
||||
const hasReq = hasAugmentationPrereqs(aug);
|
||||
const hasRep = props.faction.playerReputation >= repCost;
|
||||
const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost * props.faction.getInfo().augmentationPriceMult;
|
||||
const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost;
|
||||
|
||||
// Determine UI properties
|
||||
const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";
|
||||
|
||||
+5
-3
@@ -225,7 +225,6 @@ export class Gang implements IGang {
|
||||
if (AllGangs[otherGang].territory <= 0) return;
|
||||
const territoryGain = calculateTerritoryGain(thisGang, otherGang);
|
||||
AllGangs[thisGang].territory += territoryGain;
|
||||
if (AllGangs[thisGang].territory > 0.999) AllGangs[thisGang].territory = 1;
|
||||
AllGangs[otherGang].territory -= territoryGain;
|
||||
if (thisGang === gangName) {
|
||||
this.clash(true); // Player won
|
||||
@@ -239,9 +238,7 @@ export class Gang implements IGang {
|
||||
if (AllGangs[thisGang].territory <= 0) return;
|
||||
const territoryGain = calculateTerritoryGain(otherGang, thisGang);
|
||||
AllGangs[thisGang].territory -= territoryGain;
|
||||
if (AllGangs[otherGang].territory < 0.001) AllGangs[otherGang].territory = 0;
|
||||
AllGangs[otherGang].territory += territoryGain;
|
||||
if (AllGangs[otherGang].territory > 0.999) AllGangs[otherGang].territory = 1;
|
||||
if (thisGang === gangName) {
|
||||
this.clash(false); // Player lost
|
||||
} else if (otherGang === gangName) {
|
||||
@@ -251,6 +248,11 @@ export class Gang implements IGang {
|
||||
AllGangs[thisGang].power *= 1 / 1.01;
|
||||
}
|
||||
}
|
||||
|
||||
const total = Object.values(AllGangs)
|
||||
.map((g) => g.territory)
|
||||
.reduce((p, c) => p + c, 0);
|
||||
Object.values(AllGangs).forEach((g) => (g.territory /= total));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Money } from "../../ui/React/Money";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { MathJaxWrapper } from "../../MathJaxWrapper";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
@@ -30,9 +31,13 @@ export function RamButton(props: IProps): React.ReactElement {
|
||||
props.rerender();
|
||||
}
|
||||
|
||||
const bnMult = BitNodeMultipliers.HomeComputerRamCost === 1 ? "" : `\\cdot ${BitNodeMultipliers.HomeComputerRamCost}`;
|
||||
console.log(BitNodeMultipliers.HomeComputerRamCost);
|
||||
return (
|
||||
<Tooltip
|
||||
title={<MathJaxWrapper>{`\\(\\large{cost = 3.2 \\cdot 10^3 \\cdot 1.58^{log_2{(ram)}}}\\)`}</MathJaxWrapper>}
|
||||
title={
|
||||
<MathJaxWrapper>{`\\(\\large{cost = ram \\cdot 3.2 \\cdot 10^4 \\cdot 1.58^{log_2{(ram)}}} ${bnMult}\\)`}</MathJaxWrapper>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<br />
|
||||
|
||||
@@ -24,7 +24,7 @@ import { joinFaction } from "../../Faction/FactionHelpers";
|
||||
import { use } from "../../ui/Context";
|
||||
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
|
||||
import { N00dles } from "../../utils/helpers/N00dles";
|
||||
import { Exploit } from "../../Exploits/Exploit";
|
||||
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
|
||||
@@ -92,7 +92,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
|
||||
function renderNoodleBar(): React.ReactElement {
|
||||
function EatNoodles(): void {
|
||||
SnackbarEvents.emit("You ate some delicious noodles and feel refreshed", "success", 2000);
|
||||
SnackbarEvents.emit("You ate some delicious noodles and feel refreshed", ToastVariant.SUCCESS, 2000);
|
||||
N00dles(); // This is the true power of the noodles.
|
||||
if (player.sourceFiles.length > 0) player.giveExploit(Exploit.N00dles);
|
||||
if (player.sourceFileLvl(5) > 0 || player.bitNodeN === 5) {
|
||||
@@ -175,10 +175,11 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
|
||||
}
|
||||
|
||||
router.toFaction(faction);
|
||||
router.toStaneksGift();
|
||||
}
|
||||
|
||||
function renderCotMG(): React.ReactElement {
|
||||
const toStanek = <Button onClick={() => router.toStaneksGift()}>Open Stanek's Gift</Button>;
|
||||
// prettier-ignore
|
||||
const symbol = <Typography sx={{ lineHeight: '1em', whiteSpace: 'pre' }}>
|
||||
{" `` "}<br />
|
||||
@@ -219,6 +220,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
seems. Curious, Just how much of a machine's soul do you house in that body?
|
||||
</i>
|
||||
</Typography>
|
||||
<br />
|
||||
{toStanek}
|
||||
<br />
|
||||
{symbol}
|
||||
</>
|
||||
);
|
||||
@@ -233,6 +237,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
mastery of the gift clearly demonstrates that. My hopes are climbing by the day for you.
|
||||
</i>
|
||||
</Typography>
|
||||
<br />
|
||||
{toStanek}
|
||||
<br />
|
||||
{symbol}
|
||||
</>
|
||||
);
|
||||
@@ -243,6 +250,9 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
<Typography>
|
||||
<i>Allison "Mother" Stanek: Welcome back my child!</i>
|
||||
</Typography>
|
||||
<br />
|
||||
{toStanek}
|
||||
<br />
|
||||
{symbol}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Message } from "./Message";
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { Router } from "../ui/GameRoot";
|
||||
import { Programs } from "../Programs/Programs";
|
||||
import { Player } from "../Player";
|
||||
import { redPillFlag } from "../RedPill";
|
||||
import { Page } from "../ui/Router";
|
||||
import { GetServer } from "../Server/AllServers";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
@@ -51,7 +51,7 @@ function addMessageToServer(msg: Message, serverHostname: string): void {
|
||||
|
||||
//Checks if any of the 'timed' messages should be sent
|
||||
function checkForMessagesToSend(): void {
|
||||
if (redPillFlag) return;
|
||||
if (Router.page() === Page.BitVerse) return;
|
||||
const jumper0 = Messages[MessageFilenames.Jumper0];
|
||||
const jumper1 = Messages[MessageFilenames.Jumper1];
|
||||
const jumper2 = Messages[MessageFilenames.Jumper2];
|
||||
@@ -62,38 +62,29 @@ function checkForMessagesToSend(): void {
|
||||
const bitrunnersTest = Messages[MessageFilenames.BitRunnersTest];
|
||||
const redpill = Messages[MessageFilenames.RedPill];
|
||||
|
||||
let redpillOwned = false;
|
||||
if (Augmentations[AugmentationNames.TheRedPill].owned) {
|
||||
redpillOwned = true;
|
||||
}
|
||||
|
||||
if (redpill && redpillOwned && Player.sourceFiles.length === 0 && !redPillFlag) {
|
||||
sendMessage(redpill, true);
|
||||
} else if (redpill && redpillOwned) {
|
||||
//If player has already destroyed a BitNode, message is not forced
|
||||
if (!redPillFlag) {
|
||||
sendMessage(redpill);
|
||||
}
|
||||
} else if (jumper0 && !jumper0.recvd && Player.hacking >= 25) {
|
||||
if (Player.hasAugmentation(AugmentationNames.TheRedPill)) {
|
||||
//Force the message if the player has never destroyed a BitNode
|
||||
sendMessage(redpill, Player.sourceFiles.length === 0);
|
||||
} else if (!jumper0.recvd && Player.hacking >= 25) {
|
||||
sendMessage(jumper0);
|
||||
const flightName = Programs.Flight.name;
|
||||
const homeComp = Player.getHomeComputer();
|
||||
if (!homeComp.programs.includes(flightName)) {
|
||||
homeComp.programs.push(flightName);
|
||||
}
|
||||
} else if (jumper1 && !jumper1.recvd && Player.hacking >= 40) {
|
||||
} else if (!jumper1.recvd && Player.hacking >= 40) {
|
||||
sendMessage(jumper1);
|
||||
} else if (cybersecTest && !cybersecTest.recvd && Player.hacking >= 50) {
|
||||
} else if (!cybersecTest.recvd && Player.hacking >= 50) {
|
||||
sendMessage(cybersecTest);
|
||||
} else if (jumper2 && !jumper2.recvd && Player.hacking >= 175) {
|
||||
} else if (!jumper2.recvd && Player.hacking >= 175) {
|
||||
sendMessage(jumper2);
|
||||
} else if (nitesecTest && !nitesecTest.recvd && Player.hacking >= 200) {
|
||||
} else if (!nitesecTest.recvd && Player.hacking >= 200) {
|
||||
sendMessage(nitesecTest);
|
||||
} else if (jumper3 && !jumper3.recvd && Player.hacking >= 350) {
|
||||
} else if (!jumper3.recvd && Player.hacking >= 350) {
|
||||
sendMessage(jumper3);
|
||||
} else if (jumper4 && !jumper4.recvd && Player.hacking >= 490) {
|
||||
} else if (!jumper4.recvd && Player.hacking >= 490) {
|
||||
sendMessage(jumper4);
|
||||
} else if (bitrunnersTest && !bitrunnersTest.recvd && Player.hacking >= 500) {
|
||||
} else if (!bitrunnersTest.recvd && Player.hacking >= 500) {
|
||||
sendMessage(bitrunnersTest);
|
||||
}
|
||||
}
|
||||
@@ -102,23 +93,23 @@ function AddToAllMessages(msg: Message): void {
|
||||
Messages[msg.filename] = msg;
|
||||
}
|
||||
|
||||
let Messages: { [key: string]: Message | undefined } = {};
|
||||
let Messages: { [key: string]: Message } = {};
|
||||
|
||||
function loadMessages(saveString: string): void {
|
||||
Messages = JSON.parse(saveString, Reviver);
|
||||
}
|
||||
|
||||
const MessageFilenames = {
|
||||
Jumper0: "j0.msg",
|
||||
Jumper1: "j1.msg",
|
||||
Jumper2: "j2.msg",
|
||||
Jumper3: "j3.msg",
|
||||
Jumper4: "j4.msg",
|
||||
CyberSecTest: "csec-test.msg",
|
||||
NiteSecTest: "nitesec-test.msg",
|
||||
BitRunnersTest: "19dfj3l1nd.msg",
|
||||
RedPill: "icarus.msg",
|
||||
};
|
||||
enum MessageFilenames {
|
||||
Jumper0 = "j0.msg",
|
||||
Jumper1 = "j1.msg",
|
||||
Jumper2 = "j2.msg",
|
||||
Jumper3 = "j3.msg",
|
||||
Jumper4 = "j4.msg",
|
||||
CyberSecTest = "csec-test.msg",
|
||||
NiteSecTest = "nitesec-test.msg",
|
||||
BitRunnersTest = "19dfj3l1nd.msg",
|
||||
RedPill = "icarus.msg",
|
||||
}
|
||||
|
||||
function initMessages(): void {
|
||||
//Reset
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import { getRamCost } from "./RamCostGenerator";
|
||||
import type { IPort } from "../NetscriptPort";
|
||||
import type { BaseServer } from "../Server/BaseServer";
|
||||
import type { WorkerScript } from "./WorkerScript";
|
||||
import { makeRuntimeRejectMsg } from "../NetscriptEvaluator";
|
||||
import { Player } from "../Player";
|
||||
import { CityName } from "src/Locations/data/CityNames";
|
||||
|
||||
type ExternalFunction = (...args: any[]) => any;
|
||||
type ExternalAPI = {
|
||||
[string: string]: ExternalAPI | ExternalFunction;
|
||||
};
|
||||
|
||||
type InternalFunction<F extends (...args: unknown[]) => unknown> = (ctx: NetscriptContext) => F;
|
||||
export type InternalAPI<API> = {
|
||||
[Property in keyof API]: API[Property] extends ExternalFunction
|
||||
? InternalFunction<API[Property]>
|
||||
: API[Property] extends ExternalAPI
|
||||
? InternalAPI<API[Property]>
|
||||
: never;
|
||||
};
|
||||
|
||||
type WrappedNetscriptFunction = (...args: unknown[]) => unknown;
|
||||
type WrappedNetscriptAPI = {
|
||||
readonly [string: string]: WrappedNetscriptAPI | WrappedNetscriptFunction;
|
||||
};
|
||||
|
||||
export type NetscriptContext = {
|
||||
makeRuntimeErrorMsg: (message: string) => string;
|
||||
log: (message: () => string) => void;
|
||||
workerScript: WorkerScript;
|
||||
function: string;
|
||||
helper: WrappedNetscriptHelpers;
|
||||
};
|
||||
|
||||
type NetscriptHelpers = {
|
||||
updateDynamicRam: (fnName: string, ramCost: number) => void;
|
||||
makeRuntimeErrorMsg: (caller: string, msg: string) => string;
|
||||
string: (funcName: string, argName: string, v: unknown) => string;
|
||||
number: (funcName: string, argName: string, v: unknown) => number;
|
||||
city: (funcName: string, argName: string, v: unknown) => CityName;
|
||||
boolean: (v: unknown) => boolean;
|
||||
getServer: (hostname: string, callingFnName: string) => BaseServer;
|
||||
checkSingularityAccess: (func: string) => void;
|
||||
hack: (hostname: any, manual: any, { threads: requestedThreads, stock }?: any) => Promise<number>;
|
||||
getValidPort: (funcName: string, port: any) => IPort;
|
||||
};
|
||||
|
||||
type WrappedNetscriptHelpers = {
|
||||
makeRuntimeErrorMsg: (msg: string) => string;
|
||||
string: (argName: string, v: unknown) => string;
|
||||
number: (argName: string, v: unknown) => number;
|
||||
city: (argName: string, v: unknown) => CityName;
|
||||
boolean: (v: unknown) => boolean;
|
||||
getServer: (hostname: string) => BaseServer;
|
||||
checkSingularityAccess: () => void;
|
||||
hack: (hostname: any, manual: any, { threads: requestedThreads, stock }?: any) => Promise<number>;
|
||||
getValidPort: (port: any) => IPort;
|
||||
};
|
||||
|
||||
function wrapFunction(
|
||||
helpers: NetscriptHelpers,
|
||||
wrappedAPI: any,
|
||||
workerScript: WorkerScript,
|
||||
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
|
||||
...tree: string[]
|
||||
): void {
|
||||
const functionPath = tree.join(".");
|
||||
const functionName = tree.pop();
|
||||
if (typeof functionName !== "string") {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Failure occured while wrapping netscript api");
|
||||
}
|
||||
const ctx = {
|
||||
makeRuntimeErrorMsg: (message: string) => {
|
||||
return helpers.makeRuntimeErrorMsg(functionPath, message);
|
||||
},
|
||||
log: (message: () => string) => {
|
||||
workerScript.log(functionPath, message);
|
||||
},
|
||||
workerScript,
|
||||
function: functionName,
|
||||
helper: {
|
||||
makeRuntimeErrorMsg: (msg: string) => helpers.makeRuntimeErrorMsg(functionPath, msg),
|
||||
string: (argName: string, v: unknown) => helpers.string(functionPath, argName, v),
|
||||
number: (argName: string, v: unknown) => helpers.number(functionPath, argName, v),
|
||||
city: (argName: string, v: unknown) => helpers.city(functionPath, argName, v),
|
||||
boolean: helpers.boolean,
|
||||
getServer: (hostname: string) => helpers.getServer(hostname, functionPath),
|
||||
checkSingularityAccess: () => helpers.checkSingularityAccess(functionName),
|
||||
hack: helpers.hack,
|
||||
getValidPort: (port: any) => helpers.getValidPort(functionPath, port),
|
||||
},
|
||||
};
|
||||
function wrappedFunction(...args: unknown[]): unknown {
|
||||
helpers.updateDynamicRam(ctx.function, getRamCost(Player, ...tree, ctx.function));
|
||||
return func(ctx)(...args);
|
||||
}
|
||||
const parent = getNestedProperty(wrappedAPI, ...tree);
|
||||
Object.defineProperty(parent, functionName, {
|
||||
value: wrappedFunction,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function wrapAPI(
|
||||
helpers: NetscriptHelpers,
|
||||
wrappedAPI: ExternalAPI,
|
||||
workerScript: WorkerScript,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
namespace: any,
|
||||
...tree: string[]
|
||||
): WrappedNetscriptAPI {
|
||||
if (typeof namespace !== "object") throw new Error("Invalid namespace?");
|
||||
for (const property of Object.getOwnPropertyNames(namespace)) {
|
||||
switch (typeof namespace[property]) {
|
||||
case "function": {
|
||||
wrapFunction(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property);
|
||||
break;
|
||||
}
|
||||
case "object": {
|
||||
wrapAPI(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setNestedProperty(wrappedAPI, namespace[property], ...tree, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return wrappedAPI;
|
||||
}
|
||||
|
||||
function setNestedProperty(root: any, value: any, ...tree: string[]): any {
|
||||
let target = root;
|
||||
const key = tree.pop();
|
||||
if (typeof key !== "string") {
|
||||
throw new Error("Failure occured while wrapping netscript api (setNestedProperty)");
|
||||
}
|
||||
for (const branch of tree) {
|
||||
if (target[branch] === undefined) {
|
||||
target[branch] = {};
|
||||
}
|
||||
target = target[branch];
|
||||
}
|
||||
target[key] = value;
|
||||
}
|
||||
|
||||
function getNestedProperty(root: any, ...tree: string[]): any {
|
||||
let target = root;
|
||||
for (const branch of tree) {
|
||||
if (target[branch] === undefined) {
|
||||
target[branch] = {};
|
||||
}
|
||||
target = target[branch];
|
||||
}
|
||||
return target;
|
||||
}
|
||||
+276
-254
@@ -15,6 +15,7 @@ export const RamCostConstants: IMap<number> = {
|
||||
ScriptWeakenRamCost: 0.15,
|
||||
ScriptWeakenAnalyzeRamCost: 1,
|
||||
ScriptScanRamCost: 0.2,
|
||||
ScriptRecentScriptsRamCost: 0.2,
|
||||
ScriptPortProgramRamCost: 0.05,
|
||||
ScriptRunRamCost: 1.0,
|
||||
ScriptExecRamCost: 1.3,
|
||||
@@ -81,148 +82,52 @@ function SF4Cost(cost: number): (player: IPlayer) => number {
|
||||
};
|
||||
}
|
||||
|
||||
export const RamCosts: IMap<any> = {
|
||||
hacknet: {
|
||||
numNodes: 0,
|
||||
purchaseNode: 0,
|
||||
getPurchaseNodeCost: 0,
|
||||
getNodeStats: 0,
|
||||
upgradeLevel: 0,
|
||||
upgradeRam: 0,
|
||||
upgradeCore: 0,
|
||||
upgradeCache: 0,
|
||||
getLevelUpgradeCost: 0,
|
||||
getRamUpgradeCost: 0,
|
||||
getCoreUpgradeCost: 0,
|
||||
getCacheUpgradeCost: 0,
|
||||
numHashes: 0,
|
||||
hashCost: 0,
|
||||
spendHashes: 0,
|
||||
},
|
||||
sprintf: 0,
|
||||
vsprintf: 0,
|
||||
scan: RamCostConstants.ScriptScanRamCost,
|
||||
hack: RamCostConstants.ScriptHackRamCost,
|
||||
hackAnalyzeThreads: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
hackAnalyze: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
hackAnalyzeSecurity: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
hackAnalyzeChance: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
sleep: 0,
|
||||
asleep: 0,
|
||||
share: 2.4,
|
||||
getSharePower: 0.2,
|
||||
grow: RamCostConstants.ScriptGrowRamCost,
|
||||
growthAnalyze: RamCostConstants.ScriptGrowthAnalyzeRamCost,
|
||||
growthAnalyzeSecurity: RamCostConstants.ScriptGrowthAnalyzeRamCost,
|
||||
weaken: RamCostConstants.ScriptWeakenRamCost,
|
||||
weakenAnalyze: RamCostConstants.ScriptWeakenAnalyzeRamCost,
|
||||
print: 0,
|
||||
printf: 0,
|
||||
tprint: 0,
|
||||
clearLog: 0,
|
||||
disableLog: 0,
|
||||
enableLog: 0,
|
||||
isLogEnabled: 0,
|
||||
getScriptLogs: 0,
|
||||
nuke: RamCostConstants.ScriptPortProgramRamCost,
|
||||
brutessh: RamCostConstants.ScriptPortProgramRamCost,
|
||||
ftpcrack: RamCostConstants.ScriptPortProgramRamCost,
|
||||
relaysmtp: RamCostConstants.ScriptPortProgramRamCost,
|
||||
httpworm: RamCostConstants.ScriptPortProgramRamCost,
|
||||
sqlinject: RamCostConstants.ScriptPortProgramRamCost,
|
||||
run: RamCostConstants.ScriptRunRamCost,
|
||||
exec: RamCostConstants.ScriptExecRamCost,
|
||||
spawn: RamCostConstants.ScriptSpawnRamCost,
|
||||
kill: RamCostConstants.ScriptKillRamCost,
|
||||
killall: RamCostConstants.ScriptKillRamCost,
|
||||
exit: 0,
|
||||
atExit: 0,
|
||||
scp: RamCostConstants.ScriptScpRamCost,
|
||||
ls: RamCostConstants.ScriptScanRamCost,
|
||||
ps: RamCostConstants.ScriptScanRamCost,
|
||||
hasRootAccess: RamCostConstants.ScriptHasRootAccessRamCost,
|
||||
getIp: RamCostConstants.ScriptGetHostnameRamCost,
|
||||
getHostname: RamCostConstants.ScriptGetHostnameRamCost,
|
||||
getHackingLevel: RamCostConstants.ScriptGetHackingLevelRamCost,
|
||||
getHackingMultipliers: RamCostConstants.ScriptGetMultipliersRamCost,
|
||||
getHacknetMultipliers: RamCostConstants.ScriptGetMultipliersRamCost,
|
||||
getBitNodeMultipliers: RamCostConstants.ScriptGetMultipliersRamCost,
|
||||
getServer: RamCostConstants.ScriptGetMultipliersRamCost / 2,
|
||||
getServerMoneyAvailable: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerSecurityLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerBaseSecurityLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerMinSecurityLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerRequiredHackingLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerMaxMoney: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerGrowth: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerNumPortsRequired: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerRam: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerMaxRam: RamCostConstants.ScriptGetServerMaxRam,
|
||||
getServerUsedRam: RamCostConstants.ScriptGetServerUsedRam,
|
||||
serverExists: RamCostConstants.ScriptGetServerRamCost,
|
||||
fileExists: RamCostConstants.ScriptFileExistsRamCost,
|
||||
isRunning: RamCostConstants.ScriptIsRunningRamCost,
|
||||
stock: {
|
||||
getSymbols: RamCostConstants.ScriptGetStockRamCost,
|
||||
getPrice: RamCostConstants.ScriptGetStockRamCost,
|
||||
getAskPrice: RamCostConstants.ScriptGetStockRamCost,
|
||||
getBidPrice: RamCostConstants.ScriptGetStockRamCost,
|
||||
getPosition: RamCostConstants.ScriptGetStockRamCost,
|
||||
getMaxShares: RamCostConstants.ScriptGetStockRamCost,
|
||||
getPurchaseCost: RamCostConstants.ScriptGetStockRamCost,
|
||||
getSaleGain: RamCostConstants.ScriptGetStockRamCost,
|
||||
buy: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
sell: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
short: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
sellShort: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
placeOrder: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
cancelOrder: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
getOrders: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
getVolatility: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
getForecast: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchase4SMarketData: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchase4SMarketDataTixApi: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchaseWseAccount: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchaseTixApi: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
},
|
||||
getPurchasedServerLimit: RamCostConstants.ScriptGetPurchasedServerLimit,
|
||||
getPurchasedServerMaxRam: RamCostConstants.ScriptGetPurchasedServerMaxRam,
|
||||
getPurchasedServerCost: RamCostConstants.ScriptGetPurchaseServerRamCost,
|
||||
purchaseServer: RamCostConstants.ScriptPurchaseServerRamCost,
|
||||
deleteServer: RamCostConstants.ScriptPurchaseServerRamCost,
|
||||
getPurchasedServers: RamCostConstants.ScriptPurchaseServerRamCost,
|
||||
write: 0,
|
||||
tryWritePort: 0,
|
||||
read: 0,
|
||||
peek: 0,
|
||||
clear: 0,
|
||||
writePort: 0,
|
||||
readPort: 0,
|
||||
getPortHandle: 0,
|
||||
rm: RamCostConstants.ScriptReadWriteRamCost,
|
||||
scriptRunning: RamCostConstants.ScriptArbScriptRamCost,
|
||||
scriptKill: RamCostConstants.ScriptArbScriptRamCost,
|
||||
getScriptName: 0,
|
||||
getScriptRam: RamCostConstants.ScriptGetScriptRamCost,
|
||||
getHackTime: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
getGrowTime: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
getWeakenTime: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
getScriptIncome: RamCostConstants.ScriptGetScriptRamCost,
|
||||
getScriptExpGain: RamCostConstants.ScriptGetScriptRamCost,
|
||||
getRunningScript: RamCostConstants.ScriptGetRunningScriptRamCost,
|
||||
nFormat: 0,
|
||||
tFormat: 0,
|
||||
getTimeSinceLastAug: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
prompt: 0,
|
||||
wget: 0,
|
||||
getFavorToDonate: RamCostConstants.ScriptGetFavorToDonate,
|
||||
getPlayer: RamCostConstants.ScriptSingularityFn1RamCost / 4,
|
||||
mv: 0,
|
||||
getOwnedSourceFiles: RamCostConstants.ScriptGetOwnedSourceFiles,
|
||||
tail: 0,
|
||||
toast: 0,
|
||||
// Hacknet API
|
||||
const hacknet: IMap<any> = {
|
||||
numNodes: 0,
|
||||
purchaseNode: 0,
|
||||
getPurchaseNodeCost: 0,
|
||||
getNodeStats: 0,
|
||||
upgradeLevel: 0,
|
||||
upgradeRam: 0,
|
||||
upgradeCore: 0,
|
||||
upgradeCache: 0,
|
||||
getLevelUpgradeCost: 0,
|
||||
getRamUpgradeCost: 0,
|
||||
getCoreUpgradeCost: 0,
|
||||
getCacheUpgradeCost: 0,
|
||||
numHashes: 0,
|
||||
hashCost: 0,
|
||||
spendHashes: 0,
|
||||
};
|
||||
|
||||
// Singularity Functions
|
||||
// Stock API
|
||||
const stock: IMap<any> = {
|
||||
getSymbols: RamCostConstants.ScriptGetStockRamCost,
|
||||
getPrice: RamCostConstants.ScriptGetStockRamCost,
|
||||
getAskPrice: RamCostConstants.ScriptGetStockRamCost,
|
||||
getBidPrice: RamCostConstants.ScriptGetStockRamCost,
|
||||
getPosition: RamCostConstants.ScriptGetStockRamCost,
|
||||
getMaxShares: RamCostConstants.ScriptGetStockRamCost,
|
||||
getPurchaseCost: RamCostConstants.ScriptGetStockRamCost,
|
||||
getSaleGain: RamCostConstants.ScriptGetStockRamCost,
|
||||
buy: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
sell: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
short: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
sellShort: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
placeOrder: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
cancelOrder: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
getOrders: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
getVolatility: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
getForecast: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchase4SMarketData: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchase4SMarketDataTixApi: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchaseWseAccount: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
purchaseTixApi: RamCostConstants.ScriptBuySellStockRamCost,
|
||||
};
|
||||
|
||||
// Singularity API
|
||||
const singularity: IMap<any> = {
|
||||
universityCourse: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
||||
gymWorkout: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
||||
travelToCity: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
||||
@@ -272,127 +177,244 @@ export const RamCosts: IMap<any> = {
|
||||
installAugmentations: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
|
||||
isFocused: SF4Cost(0.1),
|
||||
setFocus: SF4Cost(0.1),
|
||||
};
|
||||
|
||||
// Gang API
|
||||
gang: {
|
||||
createGang: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
inGang: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getMemberNames: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getGangInformation: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getOtherGangInformation: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getMemberInformation: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
canRecruitMember: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
recruitMember: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getTaskNames: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getTaskStats: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
setMemberTask: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getEquipmentNames: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getEquipmentCost: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getEquipmentType: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getEquipmentStats: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
purchaseEquipment: RamCostConstants.ScriptGangApiBaseRamCost,
|
||||
ascendMember: RamCostConstants.ScriptGangApiBaseRamCost,
|
||||
getAscensionResult: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
setTerritoryWarfare: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getChanceToWinClash: RamCostConstants.ScriptGangApiBaseRamCost,
|
||||
getBonusTime: 0,
|
||||
},
|
||||
// Gang API
|
||||
const gang: IMap<any> = {
|
||||
createGang: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
inGang: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getMemberNames: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getGangInformation: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getOtherGangInformation: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getMemberInformation: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
canRecruitMember: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
recruitMember: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getTaskNames: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getTaskStats: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
setMemberTask: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getEquipmentNames: RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getEquipmentCost: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getEquipmentType: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getEquipmentStats: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
purchaseEquipment: RamCostConstants.ScriptGangApiBaseRamCost,
|
||||
ascendMember: RamCostConstants.ScriptGangApiBaseRamCost,
|
||||
getAscensionResult: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
setTerritoryWarfare: RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getChanceToWinClash: RamCostConstants.ScriptGangApiBaseRamCost,
|
||||
getBonusTime: 0,
|
||||
};
|
||||
|
||||
// Bladeburner API
|
||||
bladeburner: {
|
||||
getContractNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getOperationNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getBlackOpNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getBlackOpRank: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
|
||||
getGeneralActionNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getSkillNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
startAction: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
stopBladeburnerAction: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
|
||||
getCurrentAction: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 4,
|
||||
getActionTime: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionEstimatedSuccessChance: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionRepGain: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionCountRemaining: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionMaxLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionCurrentLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionAutolevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
setActionAutolevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
setActionLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getRank: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getSkillPoints: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getSkillLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getSkillUpgradeCost: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
upgradeSkill: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getTeamSize: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
setTeamSize: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCityEstimatedPopulation: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCityCommunities: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCityChaos: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCity: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
switchCity: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getStamina: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
joinBladeburnerFaction: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
joinBladeburnerDivision: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getBonusTime: 0,
|
||||
},
|
||||
// Bladeburner API
|
||||
const bladeburner: IMap<any> = {
|
||||
getContractNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getOperationNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getBlackOpNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getBlackOpRank: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
|
||||
getGeneralActionNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
getSkillNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10,
|
||||
startAction: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
stopBladeburnerAction: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 2,
|
||||
getCurrentAction: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 4,
|
||||
getActionTime: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionEstimatedSuccessChance: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionRepGain: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionCountRemaining: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionMaxLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionCurrentLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getActionAutolevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
setActionAutolevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
setActionLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getRank: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getSkillPoints: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getSkillLevel: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getSkillUpgradeCost: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
upgradeSkill: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getTeamSize: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
setTeamSize: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCityEstimatedPopulation: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCityCommunities: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCityChaos: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getCity: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
switchCity: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getStamina: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
joinBladeburnerFaction: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
joinBladeburnerDivision: RamCostConstants.ScriptBladeburnerApiBaseRamCost,
|
||||
getBonusTime: 0,
|
||||
};
|
||||
|
||||
// Coding Contract API
|
||||
codingcontract: {
|
||||
attempt: RamCostConstants.ScriptCodingContractBaseRamCost,
|
||||
getContractType: RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||
getData: RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||
getDescription: RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||
getNumTriesRemaining: RamCostConstants.ScriptCodingContractBaseRamCost / 5,
|
||||
},
|
||||
// Coding Contract API
|
||||
const codingcontract: IMap<any> = {
|
||||
attempt: RamCostConstants.ScriptCodingContractBaseRamCost,
|
||||
getContractType: RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||
getData: RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||
getDescription: RamCostConstants.ScriptCodingContractBaseRamCost / 2,
|
||||
getNumTriesRemaining: RamCostConstants.ScriptCodingContractBaseRamCost / 5,
|
||||
};
|
||||
|
||||
// Duplicate Sleeve API
|
||||
sleeve: {
|
||||
getNumSleeves: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToShockRecovery: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToSynchronize: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToCommitCrime: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToUniversityCourse: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
travel: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToCompanyWork: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToFactionWork: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToGymWorkout: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getSleeveStats: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getTask: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getInformation: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getSleeveAugmentations: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getSleevePurchasableAugs: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
purchaseSleeveAug: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
},
|
||||
// Duplicate Sleeve API
|
||||
const sleeve: IMap<any> = {
|
||||
getNumSleeves: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToShockRecovery: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToSynchronize: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToCommitCrime: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToUniversityCourse: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
travel: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToCompanyWork: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToFactionWork: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
setToGymWorkout: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getSleeveStats: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getTask: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getInformation: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getSleeveAugmentations: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
getSleevePurchasableAugs: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
purchaseSleeveAug: RamCostConstants.ScriptSleeveBaseRamCost,
|
||||
};
|
||||
|
||||
stanek: {
|
||||
giftWidth: RamCostConstants.ScriptStanekWidth,
|
||||
giftHeight: RamCostConstants.ScriptStanekHeight,
|
||||
chargeFragment: RamCostConstants.ScriptStanekCharge,
|
||||
fragmentDefinitions: RamCostConstants.ScriptStanekFragmentDefinitions,
|
||||
activeFragments: RamCostConstants.ScriptStanekPlacedFragments,
|
||||
clearGift: RamCostConstants.ScriptStanekClear,
|
||||
canPlaceFragment: RamCostConstants.ScriptStanekCanPlace,
|
||||
placeFragment: RamCostConstants.ScriptStanekPlace,
|
||||
getFragment: RamCostConstants.ScriptStanekFragmentAt,
|
||||
removeFragment: RamCostConstants.ScriptStanekDeleteAt,
|
||||
},
|
||||
// Stanek API
|
||||
const stanek: IMap<any> = {
|
||||
giftWidth: RamCostConstants.ScriptStanekWidth,
|
||||
giftHeight: RamCostConstants.ScriptStanekHeight,
|
||||
chargeFragment: RamCostConstants.ScriptStanekCharge,
|
||||
fragmentDefinitions: RamCostConstants.ScriptStanekFragmentDefinitions,
|
||||
activeFragments: RamCostConstants.ScriptStanekPlacedFragments,
|
||||
clearGift: RamCostConstants.ScriptStanekClear,
|
||||
canPlaceFragment: RamCostConstants.ScriptStanekCanPlace,
|
||||
placeFragment: RamCostConstants.ScriptStanekPlace,
|
||||
getFragment: RamCostConstants.ScriptStanekFragmentAt,
|
||||
removeFragment: RamCostConstants.ScriptStanekDeleteAt,
|
||||
};
|
||||
|
||||
ui: {
|
||||
getTheme: 0,
|
||||
setTheme: 0,
|
||||
resetTheme: 0,
|
||||
getStyles: 0,
|
||||
setStyles: 0,
|
||||
resetStyles: 0,
|
||||
getGameInfo: 0,
|
||||
},
|
||||
// UI API
|
||||
const ui: IMap<any> = {
|
||||
getTheme: 0,
|
||||
setTheme: 0,
|
||||
resetTheme: 0,
|
||||
getStyles: 0,
|
||||
setStyles: 0,
|
||||
resetStyles: 0,
|
||||
getGameInfo: 0,
|
||||
};
|
||||
|
||||
grafting: {
|
||||
getAugmentationGraftPrice: 3.75,
|
||||
getAugmentationGraftTime: 3.75,
|
||||
graftAugmentation: 7.5,
|
||||
},
|
||||
// Grafting API
|
||||
const grafting: IMap<any> = {
|
||||
getAugmentationGraftPrice: 3.75,
|
||||
getAugmentationGraftTime: 3.75,
|
||||
graftAugmentation: 7.5,
|
||||
};
|
||||
|
||||
export const RamCosts: IMap<any> = {
|
||||
hacknet,
|
||||
stock,
|
||||
singularity,
|
||||
...singularity, // singularity is in namespace & toplevel
|
||||
gang,
|
||||
bladeburner,
|
||||
codingcontract,
|
||||
sleeve,
|
||||
stanek,
|
||||
ui,
|
||||
grafting,
|
||||
|
||||
sprintf: 0,
|
||||
vsprintf: 0,
|
||||
scan: RamCostConstants.ScriptScanRamCost,
|
||||
hack: RamCostConstants.ScriptHackRamCost,
|
||||
hackAnalyzeThreads: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
hackAnalyze: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
hackAnalyzeSecurity: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
hackAnalyzeChance: RamCostConstants.ScriptHackAnalyzeRamCost,
|
||||
sleep: 0,
|
||||
asleep: 0,
|
||||
share: 2.4,
|
||||
getSharePower: 0.2,
|
||||
grow: RamCostConstants.ScriptGrowRamCost,
|
||||
growthAnalyze: RamCostConstants.ScriptGrowthAnalyzeRamCost,
|
||||
growthAnalyzeSecurity: RamCostConstants.ScriptGrowthAnalyzeRamCost,
|
||||
weaken: RamCostConstants.ScriptWeakenRamCost,
|
||||
weakenAnalyze: RamCostConstants.ScriptWeakenAnalyzeRamCost,
|
||||
print: 0,
|
||||
printf: 0,
|
||||
tprint: 0,
|
||||
clearLog: 0,
|
||||
disableLog: 0,
|
||||
enableLog: 0,
|
||||
isLogEnabled: 0,
|
||||
getScriptLogs: 0,
|
||||
nuke: RamCostConstants.ScriptPortProgramRamCost,
|
||||
brutessh: RamCostConstants.ScriptPortProgramRamCost,
|
||||
ftpcrack: RamCostConstants.ScriptPortProgramRamCost,
|
||||
relaysmtp: RamCostConstants.ScriptPortProgramRamCost,
|
||||
httpworm: RamCostConstants.ScriptPortProgramRamCost,
|
||||
sqlinject: RamCostConstants.ScriptPortProgramRamCost,
|
||||
run: RamCostConstants.ScriptRunRamCost,
|
||||
exec: RamCostConstants.ScriptExecRamCost,
|
||||
spawn: RamCostConstants.ScriptSpawnRamCost,
|
||||
kill: RamCostConstants.ScriptKillRamCost,
|
||||
killall: RamCostConstants.ScriptKillRamCost,
|
||||
exit: 0,
|
||||
atExit: 0,
|
||||
scp: RamCostConstants.ScriptScpRamCost,
|
||||
ls: RamCostConstants.ScriptScanRamCost,
|
||||
ps: RamCostConstants.ScriptScanRamCost,
|
||||
getRecentScripts: RamCostConstants.ScriptRecentScriptsRamCost,
|
||||
hasRootAccess: RamCostConstants.ScriptHasRootAccessRamCost,
|
||||
getIp: RamCostConstants.ScriptGetHostnameRamCost,
|
||||
getHostname: RamCostConstants.ScriptGetHostnameRamCost,
|
||||
getHackingLevel: RamCostConstants.ScriptGetHackingLevelRamCost,
|
||||
getHackingMultipliers: RamCostConstants.ScriptGetMultipliersRamCost,
|
||||
getHacknetMultipliers: RamCostConstants.ScriptGetMultipliersRamCost,
|
||||
getBitNodeMultipliers: RamCostConstants.ScriptGetMultipliersRamCost,
|
||||
getServer: RamCostConstants.ScriptGetMultipliersRamCost / 2,
|
||||
getServerMoneyAvailable: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerSecurityLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerBaseSecurityLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerMinSecurityLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerRequiredHackingLevel: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerMaxMoney: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerGrowth: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerNumPortsRequired: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerRam: RamCostConstants.ScriptGetServerRamCost,
|
||||
getServerMaxRam: RamCostConstants.ScriptGetServerMaxRam,
|
||||
getServerUsedRam: RamCostConstants.ScriptGetServerUsedRam,
|
||||
serverExists: RamCostConstants.ScriptGetServerRamCost,
|
||||
fileExists: RamCostConstants.ScriptFileExistsRamCost,
|
||||
isRunning: RamCostConstants.ScriptIsRunningRamCost,
|
||||
getPurchasedServerLimit: RamCostConstants.ScriptGetPurchasedServerLimit,
|
||||
getPurchasedServerMaxRam: RamCostConstants.ScriptGetPurchasedServerMaxRam,
|
||||
getPurchasedServerCost: RamCostConstants.ScriptGetPurchaseServerRamCost,
|
||||
purchaseServer: RamCostConstants.ScriptPurchaseServerRamCost,
|
||||
deleteServer: RamCostConstants.ScriptPurchaseServerRamCost,
|
||||
getPurchasedServers: RamCostConstants.ScriptPurchaseServerRamCost,
|
||||
write: 0,
|
||||
tryWritePort: 0,
|
||||
read: 0,
|
||||
peek: 0,
|
||||
clear: 0,
|
||||
writePort: 0,
|
||||
readPort: 0,
|
||||
getPortHandle: 0,
|
||||
rm: RamCostConstants.ScriptReadWriteRamCost,
|
||||
scriptRunning: RamCostConstants.ScriptArbScriptRamCost,
|
||||
scriptKill: RamCostConstants.ScriptArbScriptRamCost,
|
||||
getScriptName: 0,
|
||||
getScriptRam: RamCostConstants.ScriptGetScriptRamCost,
|
||||
getHackTime: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
getGrowTime: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
getWeakenTime: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
getScriptIncome: RamCostConstants.ScriptGetScriptRamCost,
|
||||
getScriptExpGain: RamCostConstants.ScriptGetScriptRamCost,
|
||||
getRunningScript: RamCostConstants.ScriptGetRunningScriptRamCost,
|
||||
nFormat: 0,
|
||||
tFormat: 0,
|
||||
getTimeSinceLastAug: RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
prompt: 0,
|
||||
wget: 0,
|
||||
getFavorToDonate: RamCostConstants.ScriptGetFavorToDonate,
|
||||
getPlayer: RamCostConstants.ScriptSingularityFn1RamCost / 4,
|
||||
mv: 0,
|
||||
getOwnedSourceFiles: RamCostConstants.ScriptGetOwnedSourceFiles,
|
||||
tail: 0,
|
||||
toast: 0,
|
||||
heart: {
|
||||
// Easter egg function
|
||||
break: 0,
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
import { RunningScript } from "src/Script/RunningScript";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
|
||||
export const recentScripts: RecentScript[] = [];
|
||||
|
||||
export function AddRecentScript(workerScript: WorkerScript): void {
|
||||
if (recentScripts.find((r) => r.pid === workerScript.pid)) return;
|
||||
recentScripts.unshift({
|
||||
filename: workerScript.name,
|
||||
args: workerScript.args,
|
||||
pid: workerScript.pid,
|
||||
timestamp: new Date(),
|
||||
if (recentScripts.find((r) => r.runningScript.pid === workerScript.pid)) return;
|
||||
|
||||
const killedTime = new Date();
|
||||
recentScripts.unshift({
|
||||
timeOfDeath: killedTime,
|
||||
runningScript: workerScript.scriptRef,
|
||||
});
|
||||
while (recentScripts.length > 50) {
|
||||
|
||||
while (recentScripts.length > Settings.MaxRecentScriptsCapacity) {
|
||||
recentScripts.pop();
|
||||
}
|
||||
}
|
||||
|
||||
export interface RecentScript {
|
||||
filename: string;
|
||||
args: string[];
|
||||
pid: number;
|
||||
timestamp: Date;
|
||||
timeOfDeath: Date;
|
||||
runningScript: RunningScript;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
|
||||
/**
|
||||
* Script death marker.
|
||||
*
|
||||
* IMPORTANT: the game engine should not base any of it's decisions on the data
|
||||
* carried in a ScriptDeath instance.
|
||||
*
|
||||
* This is because ScriptDeath instances are thrown through player code when a
|
||||
* script is killed. Which grants the player access to the class and the ability
|
||||
* to construct new instances with arbitrary data.
|
||||
*/
|
||||
export class ScriptDeath {
|
||||
/** Process ID number. */
|
||||
pid: number;
|
||||
|
||||
/** Filename of the script. */
|
||||
name: string;
|
||||
|
||||
/** IP Address on which the script was running */
|
||||
hostname: string;
|
||||
|
||||
/** Status message in case of script error. */
|
||||
errorMessage = "";
|
||||
|
||||
constructor(ws: WorkerScript) {
|
||||
this.pid = ws.pid;
|
||||
this.name = ws.name;
|
||||
this.hostname = ws.hostname;
|
||||
this.errorMessage = ws.errorMessage;
|
||||
|
||||
Object.freeze(this);
|
||||
}
|
||||
}
|
||||
|
||||
Object.freeze(ScriptDeath);
|
||||
Object.freeze(ScriptDeath.prototype);
|
||||
@@ -60,7 +60,7 @@ export class WorkerScript {
|
||||
env: Environment;
|
||||
|
||||
/**
|
||||
* Status message in case of script error. Currently unused I think
|
||||
* Status message in case of script error.
|
||||
*/
|
||||
errorMessage = "";
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Stops an actively-running script (represented by a WorkerScript object)
|
||||
* and removes it from the global pool of active scripts.
|
||||
*/
|
||||
import { ScriptDeath } from "./ScriptDeath";
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
import { workerScripts } from "./WorkerScripts";
|
||||
import { WorkerScriptStartStopEventEmitter } from "./WorkerScriptStartStopEventEmitter";
|
||||
@@ -139,7 +140,7 @@ function killNetscriptDelay(workerScript: WorkerScript): void {
|
||||
if (workerScript.delay) {
|
||||
clearTimeout(workerScript.delay);
|
||||
if (workerScript.delayReject) {
|
||||
workerScript.delayReject(workerScript);
|
||||
workerScript.delayReject(new ScriptDeath(workerScript));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { isString } from "./utils/helpers/isString";
|
||||
import { GetServer } from "./Server/AllServers";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
|
||||
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
|
||||
// Cancel any pre-existing netscriptDelay'ed function call
|
||||
// TODO: the rejection almost certainly ends up in the uncaught rejection handler.
|
||||
// Maybe reject with a stack-trace'd error message?
|
||||
if (workerScript.delayReject) workerScript.delayReject();
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
workerScript.delay = window.setTimeout(() => {
|
||||
workerScript.delay = null;
|
||||
workerScript.delayReject = undefined;
|
||||
|
||||
if (workerScript.env.stopFlag) reject(workerScript);
|
||||
if (workerScript.env.stopFlag) reject(new ScriptDeath(workerScript));
|
||||
else resolve();
|
||||
}, time);
|
||||
workerScript.delayReject = reject;
|
||||
|
||||
+71
-24
@@ -75,10 +75,13 @@ import { IPort } from "./NetscriptPort";
|
||||
|
||||
import {
|
||||
NS as INS,
|
||||
Singularity as ISingularity,
|
||||
Player as INetscriptPlayer,
|
||||
Gang as IGang,
|
||||
Bladeburner as IBladeburner,
|
||||
Stanek as IStanek,
|
||||
RunningScript as IRunningScript,
|
||||
RecentScript as IRecentScript,
|
||||
SourceFileLvl,
|
||||
BasicHGWOptions,
|
||||
ProcessInfo,
|
||||
@@ -87,18 +90,22 @@ import {
|
||||
BitNodeMultipliers as IBNMults,
|
||||
Server as IServerDef,
|
||||
RunningScript as IRunningScriptDef,
|
||||
// ToastVariant,
|
||||
} from "./ScriptEditor/NetscriptDefinitions";
|
||||
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
|
||||
|
||||
import { toNative } from "./NetscriptFunctions/toNative";
|
||||
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "./ui/React/Snackbar";
|
||||
import { checkEnum } from "./utils/helpers/checkEnum";
|
||||
|
||||
import { Flags } from "./NetscriptFunctions/Flags";
|
||||
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
|
||||
import { CalculateShareMult, StartSharing } from "./NetworkShare/Share";
|
||||
import { recentScripts } from "./Netscript/RecentScripts";
|
||||
import { CityName } from "./Locations/data/CityNames";
|
||||
import { wrapAPI } from "./Netscript/APIWrapper";
|
||||
|
||||
interface NS extends INS {
|
||||
[key: string]: any;
|
||||
@@ -212,6 +219,32 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sanitizes a `RunningScript` to remove sensitive information, making it suitable for
|
||||
* return through an NS function.
|
||||
* @see NS.getRecentScripts
|
||||
* @see NS.getRunningScript
|
||||
* @param runningScript Existing, internal RunningScript
|
||||
* @returns A sanitized, NS-facing copy of the RunningScript
|
||||
*/
|
||||
const createPublicRunningScript = function (runningScript: RunningScript): IRunningScript {
|
||||
return {
|
||||
args: runningScript.args.slice(),
|
||||
filename: runningScript.filename,
|
||||
logs: runningScript.logs.slice(),
|
||||
offlineExpGained: runningScript.offlineExpGained,
|
||||
offlineMoneyMade: runningScript.offlineMoneyMade,
|
||||
offlineRunningTime: runningScript.offlineRunningTime,
|
||||
onlineExpGained: runningScript.onlineExpGained,
|
||||
onlineMoneyMade: runningScript.onlineMoneyMade,
|
||||
onlineRunningTime: runningScript.onlineRunningTime,
|
||||
pid: runningScript.pid,
|
||||
ramUsage: runningScript.ramUsage,
|
||||
server: runningScript.server,
|
||||
threads: runningScript.threads,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function for getting the error log message when the user specifies
|
||||
* a nonexistent running script
|
||||
@@ -491,12 +524,14 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
const sleeve = NetscriptSleeve(Player, workerScript, helper);
|
||||
const extra = NetscriptExtra(Player, workerScript, helper);
|
||||
const hacknet = NetscriptHacknet(Player, workerScript, helper);
|
||||
const stanek = NetscriptStanek(Player, workerScript, helper);
|
||||
const stanek = wrapAPI(helper, {}, workerScript, NetscriptStanek(Player, workerScript, helper), "stanek")
|
||||
.stanek as unknown as IStanek;
|
||||
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
|
||||
const codingcontract = NetscriptCodingContract(Player, workerScript, helper);
|
||||
const corporation = NetscriptCorporation(Player, workerScript, helper);
|
||||
const formulas = NetscriptFormulas(Player, workerScript, helper);
|
||||
const singularity = NetscriptSingularity(Player, workerScript, helper);
|
||||
const singularity = wrapAPI(helper, {}, workerScript, NetscriptSingularity(Player, workerScript), "singularity")
|
||||
.singularity as unknown as ISingularity;
|
||||
const stockmarket = NetscriptStockMarket(Player, workerScript, helper);
|
||||
const ui = NetscriptUserInterface(Player, workerScript, helper);
|
||||
const grafting = NetscriptGrafting(Player, workerScript, helper);
|
||||
@@ -583,9 +618,25 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
|
||||
return calculatePercentMoneyHacked(server, Player);
|
||||
},
|
||||
hackAnalyzeSecurity: function (_threads: unknown): number {
|
||||
hackAnalyzeSecurity: function (_threads: unknown, _hostname?: unknown): number {
|
||||
updateDynamicRam("hackAnalyzeSecurity", getRamCost(Player, "hackAnalyzeSecurity"));
|
||||
const threads = helper.number("hackAnalyzeSecurity", "threads", _threads);
|
||||
let threads = helper.number("hackAnalyzeSecurity", "threads", _threads);
|
||||
if (_hostname) {
|
||||
const hostname = helper.string("hackAnalyzeSecurity", "hostname", _hostname);
|
||||
const server = safeGetServer(hostname, "hackAnalyze");
|
||||
if (!(server instanceof Server)) {
|
||||
workerScript.log("hackAnalyzeSecurity", () => "Cannot be executed on this server.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const percentHacked = calculatePercentMoneyHacked(server, Player);
|
||||
|
||||
if (percentHacked > 0) {
|
||||
// thread count is limited to the maximum number of threads needed
|
||||
threads = Math.ceil(1 / percentHacked);
|
||||
}
|
||||
}
|
||||
|
||||
return CONSTANTS.ServerFortifyAmount * threads;
|
||||
},
|
||||
hackAnalyzeChance: function (_hostname: unknown): number {
|
||||
@@ -1413,6 +1464,13 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
allFiles.sort();
|
||||
return allFiles;
|
||||
},
|
||||
getRecentScripts: function (): IRecentScript[] {
|
||||
updateDynamicRam("getRecentScripts", getRamCost(Player, "getRecentScripts"));
|
||||
return recentScripts.map((rs) => ({
|
||||
timeOfDeath: rs.timeOfDeath,
|
||||
...createPublicRunningScript(rs.runningScript),
|
||||
}));
|
||||
},
|
||||
ps: function (_hostname: unknown = workerScript.hostname): ProcessInfo[] {
|
||||
updateDynamicRam("ps", getRamCost(Player, "ps"));
|
||||
const hostname = helper.string("ps", "hostname", _hostname);
|
||||
@@ -2128,21 +2186,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
runningScript = getRunningScript(fn, hostname, "getRunningScript", args);
|
||||
}
|
||||
if (runningScript === null) return null;
|
||||
return {
|
||||
args: runningScript.args.slice(),
|
||||
filename: runningScript.filename,
|
||||
logs: runningScript.logs.slice(),
|
||||
offlineExpGained: runningScript.offlineExpGained,
|
||||
offlineMoneyMade: runningScript.offlineMoneyMade,
|
||||
offlineRunningTime: runningScript.offlineRunningTime,
|
||||
onlineExpGained: runningScript.onlineExpGained,
|
||||
onlineMoneyMade: runningScript.onlineMoneyMade,
|
||||
onlineRunningTime: runningScript.onlineRunningTime,
|
||||
pid: runningScript.pid,
|
||||
ramUsage: runningScript.ramUsage,
|
||||
server: runningScript.server,
|
||||
threads: runningScript.threads,
|
||||
};
|
||||
return createPublicRunningScript(runningScript);
|
||||
},
|
||||
getHackTime: function (_hostname: unknown = workerScript.hostname): number {
|
||||
updateDynamicRam("getHackTime", getRamCost(Player, "getHackTime"));
|
||||
@@ -2262,13 +2306,13 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
const message = helper.string("alert", "message", _message);
|
||||
dialogBoxCreate(message);
|
||||
},
|
||||
toast: function (_message: unknown, _variant: unknown = "success", duration: any = 2000): void {
|
||||
toast: function (_message: unknown, _variant: unknown = ToastVariant.SUCCESS, duration: any = 2000): void {
|
||||
updateDynamicRam("toast", getRamCost(Player, "toast"));
|
||||
const message = helper.string("toast", "message", _message);
|
||||
const variant = helper.string("toast", "variant", _variant);
|
||||
if (!["success", "info", "warning", "error"].includes(variant))
|
||||
throw new Error(`variant must be one of "success", "info", "warning", or "error"`);
|
||||
SnackbarEvents.emit(message, variant as any, duration);
|
||||
if (!checkEnum(ToastVariant, variant))
|
||||
throw new Error(`variant must be one of ${Object.values(ToastVariant).join(", ")}`);
|
||||
SnackbarEvents.emit(message, variant, duration);
|
||||
},
|
||||
prompt: function (_txt: unknown, options?: { type?: string; options?: string[] }): Promise<boolean | string> {
|
||||
updateDynamicRam("prompt", getRamCost(Player, "prompt"));
|
||||
@@ -2511,6 +2555,9 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
||||
}
|
||||
},
|
||||
flags: Flags(workerScript.args),
|
||||
enums: {
|
||||
toast: ToastVariant,
|
||||
},
|
||||
};
|
||||
|
||||
// add undocumented functions
|
||||
|
||||
@@ -356,11 +356,14 @@ export function NetscriptCorporation(
|
||||
const cityName = helper.city("getMaterial", "cityName", _cityName);
|
||||
const materialName = helper.string("getMaterial", "materialName", _materialName);
|
||||
const material = getMaterial(divisionName, cityName, materialName);
|
||||
const corporation = getCorporation();
|
||||
return {
|
||||
cost: material.bCost,
|
||||
name: material.name,
|
||||
qty: material.qty,
|
||||
qlt: material.qlt,
|
||||
dmd: corporation.unlockUpgrades[2] ? material.dmd : undefined,
|
||||
cmp: corporation.unlockUpgrades[3] ? material.cmp : undefined,
|
||||
prod: material.prd,
|
||||
sell: material.sll,
|
||||
};
|
||||
@@ -370,10 +373,20 @@ export function NetscriptCorporation(
|
||||
const divisionName = helper.string("getProduct", "divisionName", _divisionName);
|
||||
const productName = helper.string("getProduct", "productName", _productName);
|
||||
const product = getProduct(divisionName, productName);
|
||||
const corporation = getCorporation();
|
||||
return {
|
||||
name: product.name,
|
||||
dmd: product.dmd,
|
||||
cmp: product.cmp,
|
||||
dmd: corporation.unlockUpgrades[2] ? product.dmd : undefined,
|
||||
cmp: corporation.unlockUpgrades[3] ? product.cmp : undefined,
|
||||
rat: product.rat,
|
||||
properties: {
|
||||
qlt: product.qlt,
|
||||
per: product.per,
|
||||
dur: product.dur,
|
||||
rel: product.rel,
|
||||
aes: product.aes,
|
||||
fea: product.fea,
|
||||
},
|
||||
pCost: product.pCost,
|
||||
sCost: product.sCost,
|
||||
cityData: product.data,
|
||||
@@ -678,9 +691,6 @@ export function NetscriptCorporation(
|
||||
const office = getOffice(divisionName, cityName);
|
||||
if (!Object.values(EmployeePositions).includes(job)) throw new Error(`'${job}' is not a valid job.`);
|
||||
return netscriptDelay(1000, workerScript).then(function () {
|
||||
if (workerScript.env.stopFlag) {
|
||||
return Promise.reject(workerScript);
|
||||
}
|
||||
return Promise.resolve(office.setEmployeeToJob(job, amount));
|
||||
});
|
||||
},
|
||||
@@ -794,6 +804,15 @@ export function NetscriptCorporation(
|
||||
"Research & Development": office.employeeProd[EmployeePositions.RandD],
|
||||
Training: office.employeeProd[EmployeePositions.Training],
|
||||
},
|
||||
employeeJobs: {
|
||||
Operations: office.employeeJobs[EmployeePositions.Operations],
|
||||
Engineer: office.employeeJobs[EmployeePositions.Engineer],
|
||||
Business: office.employeeJobs[EmployeePositions.Business],
|
||||
Management: office.employeeJobs[EmployeePositions.Management],
|
||||
"Research & Development": office.employeeJobs[EmployeePositions.RandD],
|
||||
Training: office.employeeJobs[EmployeePositions.Training],
|
||||
Unassigned: office.employeeJobs[EmployeePositions.Unassigned],
|
||||
},
|
||||
};
|
||||
},
|
||||
getEmployee: function (_divisionName: unknown, _cityName: unknown, _employeeName: unknown): NSEmployee {
|
||||
|
||||
@@ -54,7 +54,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
||||
if (player.city !== CityName.NewTokyo) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"grafting.graftAugmentation",
|
||||
"You must be in New Tokyo to begin crafting an Augmentation.",
|
||||
"You must be in New Tokyo to begin grafting an Augmentation.",
|
||||
);
|
||||
}
|
||||
if (!getAvailableAugs(player).includes(augName)) {
|
||||
@@ -90,7 +90,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h
|
||||
Router.toTerminal();
|
||||
}
|
||||
|
||||
workerScript.log("grafting.graftAugmentation", () => `Began crafting Augmentation ${augName}.`);
|
||||
workerScript.log("grafting.graftAugmentation", () => `Began grafting Augmentation ${augName}.`);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
+1172
-1168
File diff suppressed because it is too large
Load Diff
@@ -97,7 +97,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
travel: function (_sleeveNumber: unknown, _cityName: unknown): boolean {
|
||||
updateRam("travel");
|
||||
const sleeveNumber = helper.number("travel", "sleeveNumber", _sleeveNumber);
|
||||
const cityName = helper.string("setToUniversityCourse", "cityName", _cityName);
|
||||
const cityName = helper.string("travel", "cityName", _cityName);
|
||||
checkSleeveAPIAccess("travel");
|
||||
checkSleeveNumber("travel", sleeveNumber);
|
||||
return player.sleeves[sleeveNumber].travel(player, cityName as CityName);
|
||||
@@ -105,7 +105,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
setToCompanyWork: function (_sleeveNumber: unknown, acompanyName: unknown): boolean {
|
||||
updateRam("setToCompanyWork");
|
||||
const sleeveNumber = helper.number("setToCompanyWork", "sleeveNumber", _sleeveNumber);
|
||||
const companyName = helper.string("setToUniversityCourse", "companyName", acompanyName);
|
||||
const companyName = helper.string("setToCompanyWork", "companyName", acompanyName);
|
||||
checkSleeveAPIAccess("setToCompanyWork");
|
||||
checkSleeveNumber("setToCompanyWork", sleeveNumber);
|
||||
|
||||
@@ -117,7 +117,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
const other = player.sleeves[i];
|
||||
if (other.currentTask === SleeveTaskType.Company && other.currentTaskLocation === companyName) {
|
||||
throw helper.makeRuntimeErrorMsg(
|
||||
"sleeve.setToFactionWork",
|
||||
"sleeve.setToCompanyWork",
|
||||
`Sleeve ${sleeveNumber} cannot work for company ${companyName} because Sleeve ${i} is already working for them.`,
|
||||
);
|
||||
}
|
||||
@@ -132,8 +132,8 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
): boolean | undefined {
|
||||
updateRam("setToFactionWork");
|
||||
const sleeveNumber = helper.number("setToFactionWork", "sleeveNumber", _sleeveNumber);
|
||||
const factionName = helper.string("setToUniversityCourse", "factionName", _factionName);
|
||||
const workType = helper.string("setToUniversityCourse", "workType", _workType);
|
||||
const factionName = helper.string("setToFactionWork", "factionName", _factionName);
|
||||
const workType = helper.string("setToFactionWork", "workType", _workType);
|
||||
checkSleeveAPIAccess("setToFactionWork");
|
||||
checkSleeveNumber("setToFactionWork", sleeveNumber);
|
||||
|
||||
@@ -163,8 +163,8 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
||||
setToGymWorkout: function (_sleeveNumber: unknown, _gymName: unknown, _stat: unknown): boolean {
|
||||
updateRam("setToGymWorkout");
|
||||
const sleeveNumber = helper.number("setToGymWorkout", "sleeveNumber", _sleeveNumber);
|
||||
const gymName = helper.string("setToUniversityCourse", "gymName", _gymName);
|
||||
const stat = helper.string("setToUniversityCourse", "stat", _stat);
|
||||
const gymName = helper.string("setToGymWorkout", "gymName", _gymName);
|
||||
const stat = helper.string("setToGymWorkout", "stat", _stat);
|
||||
checkSleeveAPIAccess("setToGymWorkout");
|
||||
checkSleeveNumber("setToGymWorkout", sleeveNumber);
|
||||
|
||||
|
||||
@@ -2,112 +2,112 @@ import { INetscriptHelper } from "./INetscriptHelper";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { netscriptDelay } from "../NetscriptEvaluator";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
|
||||
import { staneksGift } from "../CotMG/Helper";
|
||||
import { Fragments, FragmentById } from "../CotMG/Fragment";
|
||||
|
||||
import {
|
||||
Stanek as IStanek,
|
||||
Fragment as IFragment,
|
||||
ActiveFragment as IActiveFragment,
|
||||
Stanek as IStanek,
|
||||
} from "../ScriptEditor/NetscriptDefinitions";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { NetscriptContext, InternalAPI } from "../Netscript/APIWrapper";
|
||||
|
||||
export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IStanek {
|
||||
export function NetscriptStanek(
|
||||
player: IPlayer,
|
||||
workerScript: WorkerScript,
|
||||
helper: INetscriptHelper,
|
||||
): InternalAPI<IStanek> {
|
||||
function checkStanekAPIAccess(func: string): void {
|
||||
if (!player.hasAugmentation(AugmentationNames.StaneksGift1, true)) {
|
||||
helper.makeRuntimeErrorMsg(func, "Requires Stanek's Gift installed.");
|
||||
}
|
||||
}
|
||||
|
||||
const updateRam = (funcName: string): void =>
|
||||
helper.updateDynamicRam(funcName, getRamCost(player, "stanek", funcName));
|
||||
|
||||
return {
|
||||
giftWidth: function (): number {
|
||||
updateRam("giftWidth");
|
||||
checkStanekAPIAccess("giftWidth");
|
||||
return staneksGift.width();
|
||||
},
|
||||
giftHeight: function (): number {
|
||||
updateRam("giftHeight");
|
||||
checkStanekAPIAccess("giftHeight");
|
||||
return staneksGift.height();
|
||||
},
|
||||
chargeFragment: function (_rootX: unknown, _rootY: unknown): Promise<void> {
|
||||
updateRam("chargeFragment");
|
||||
const rootX = helper.number("stanek.chargeFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.chargeFragment", "rootY", _rootY);
|
||||
checkStanekAPIAccess("chargeFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (!fragment)
|
||||
throw helper.makeRuntimeErrorMsg("stanek.chargeFragment", `No fragment with root (${rootX}, ${rootY}).`);
|
||||
const time = staneksGift.inBonus() ? 200 : 1000;
|
||||
return netscriptDelay(time, workerScript).then(function () {
|
||||
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||
workerScript.log("stanek.chargeFragment", () => `Charged fragment for ${charge} charge.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
fragmentDefinitions: function (): IFragment[] {
|
||||
updateRam("fragmentDefinitions");
|
||||
checkStanekAPIAccess("fragmentDefinitions");
|
||||
workerScript.log("stanek.fragmentDefinitions", () => `Returned ${Fragments.length} fragments`);
|
||||
return Fragments.map((f) => f.copy());
|
||||
},
|
||||
activeFragments: function (): IActiveFragment[] {
|
||||
updateRam("activeFragments");
|
||||
checkStanekAPIAccess("activeFragments");
|
||||
workerScript.log("stanek.activeFragments", () => `Returned ${staneksGift.fragments.length} fragments`);
|
||||
return staneksGift.fragments.map((af) => {
|
||||
return { ...af.copy(), ...af.fragment().copy() };
|
||||
});
|
||||
},
|
||||
clearGift: function (): void {
|
||||
updateRam("clearGift");
|
||||
checkStanekAPIAccess("clearGift");
|
||||
workerScript.log("stanek.clearGift", () => `Cleared Stanek's Gift.`);
|
||||
staneksGift.clear();
|
||||
},
|
||||
canPlaceFragment: function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
updateRam("canPlaceFragment");
|
||||
const rootX = helper.number("stanek.canPlaceFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.canPlaceFragment", "rootY", _rootY);
|
||||
const rotation = helper.number("stanek.canPlaceFragment", "rotation", _rotation);
|
||||
const fragmentId = helper.number("stanek.canPlaceFragment", "fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("canPlaceFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.canPlaceFragment", `Invalid fragment id: ${fragmentId}`);
|
||||
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
|
||||
return can;
|
||||
},
|
||||
placeFragment: function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
updateRam("placeFragment");
|
||||
const rootX = helper.number("stanek.placeFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.placeFragment", "rootY", _rootY);
|
||||
const rotation = helper.number("stanek.placeFragment", "rotation", _rotation);
|
||||
const fragmentId = helper.number("stanek.placeFragment", "fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("placeFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.placeFragment", `Invalid fragment id: ${fragmentId}`);
|
||||
return staneksGift.place(rootX, rootY, rotation, fragment);
|
||||
},
|
||||
getFragment: function (_rootX: unknown, _rootY: unknown): IActiveFragment | undefined {
|
||||
updateRam("getFragment");
|
||||
const rootX = helper.number("stanek.getFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.getFragment", "rootY", _rootY);
|
||||
checkStanekAPIAccess("getFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (fragment !== undefined) return fragment.copy();
|
||||
return undefined;
|
||||
},
|
||||
removeFragment: function (_rootX: unknown, _rootY: unknown): boolean {
|
||||
updateRam("removeFragment");
|
||||
const rootX = helper.number("stanek.removeFragment", "rootX", _rootX);
|
||||
const rootY = helper.number("stanek.removeFragment", "rootY", _rootY);
|
||||
checkStanekAPIAccess("removeFragment");
|
||||
return staneksGift.delete(rootX, rootY);
|
||||
},
|
||||
giftWidth: () =>
|
||||
function (): number {
|
||||
checkStanekAPIAccess("giftWidth");
|
||||
return staneksGift.width();
|
||||
},
|
||||
giftHeight: () =>
|
||||
function (): number {
|
||||
checkStanekAPIAccess("giftHeight");
|
||||
return staneksGift.height();
|
||||
},
|
||||
chargeFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown): Promise<void> {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
checkStanekAPIAccess("chargeFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`No fragment with root (${rootX}, ${rootY}).`);
|
||||
const time = staneksGift.inBonus() ? 200 : 1000;
|
||||
return netscriptDelay(time, workerScript).then(function () {
|
||||
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||
_ctx.log(() => `Charged fragment for ${charge} charge.`);
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
fragmentDefinitions: (_ctx: NetscriptContext) =>
|
||||
function (): IFragment[] {
|
||||
checkStanekAPIAccess("fragmentDefinitions");
|
||||
_ctx.log(() => `Returned ${Fragments.length} fragments`);
|
||||
return Fragments.map((f) => f.copy());
|
||||
},
|
||||
activeFragments: (_ctx: NetscriptContext) =>
|
||||
function (): IActiveFragment[] {
|
||||
checkStanekAPIAccess("activeFragments");
|
||||
_ctx.log(() => `Returned ${staneksGift.fragments.length} fragments`);
|
||||
return staneksGift.fragments.map((af) => {
|
||||
return { ...af.copy(), ...af.fragment().copy() };
|
||||
});
|
||||
},
|
||||
clearGift: (_ctx: NetscriptContext) =>
|
||||
function (): void {
|
||||
checkStanekAPIAccess("clearGift");
|
||||
_ctx.log(() => `Cleared Stanek's Gift.`);
|
||||
staneksGift.clear();
|
||||
},
|
||||
canPlaceFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
const rotation = _ctx.helper.number("rotation", _rotation);
|
||||
const fragmentId = _ctx.helper.number("fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("canPlaceFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`Invalid fragment id: ${fragmentId}`);
|
||||
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
|
||||
return can;
|
||||
},
|
||||
placeFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
const rotation = _ctx.helper.number("rotation", _rotation);
|
||||
const fragmentId = _ctx.helper.number("fragmentId", _fragmentId);
|
||||
checkStanekAPIAccess("placeFragment");
|
||||
const fragment = FragmentById(fragmentId);
|
||||
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`Invalid fragment id: ${fragmentId}`);
|
||||
return staneksGift.place(rootX, rootY, rotation, fragment);
|
||||
},
|
||||
getFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown): IActiveFragment | undefined {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
checkStanekAPIAccess("getFragment");
|
||||
const fragment = staneksGift.findFragment(rootX, rootY);
|
||||
if (fragment !== undefined) return fragment.copy();
|
||||
return undefined;
|
||||
},
|
||||
removeFragment: (_ctx: NetscriptContext) =>
|
||||
function (_rootX: unknown, _rootY: unknown): boolean {
|
||||
const rootX = _ctx.helper.number("rootX", _rootX);
|
||||
const rootY = _ctx.helper.number("rootY", _rootY);
|
||||
checkStanekAPIAccess("removeFragment");
|
||||
return staneksGift.delete(rootX, rootY);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||
import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling";
|
||||
import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder } from "../StockMarket/StockMarket";
|
||||
import { StockMarket, SymbolToStockMap, placeOrder, cancelOrder, initStockMarketFn } from "../StockMarket/StockMarket";
|
||||
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
|
||||
import { OrderTypes } from "../StockMarket/data/OrderTypes";
|
||||
import { PositionTypes } from "../StockMarket/data/PositionTypes";
|
||||
@@ -411,6 +411,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
|
||||
}
|
||||
|
||||
player.hasWseAccount = true;
|
||||
initStockMarketFn();
|
||||
player.loseMoney(getStockMarketWseCost(), "stock");
|
||||
workerScript.log("stock.purchaseWseAccount", () => "Purchased WSE Account Access");
|
||||
return true;
|
||||
|
||||
@@ -202,7 +202,7 @@ function _getScriptUrls(script: Script, scripts: Script[], seen: Script[]): Scri
|
||||
|
||||
// We automatically define a print function() in the NetscriptJS module so that
|
||||
// accidental calls to window.print() do not bring up the "print screen" dialog
|
||||
transformedCode += `\n\nfunction print() {throw new Error("Invalid call to window.print(). Did you mean to use Netscript's print()?");}`;
|
||||
transformedCode += `\n\nfunction print() {throw new Error("Invalid call to window.print(). Did you mean to use Netscript's print()?");}\n//# sourceURL=${script.server}/${script.filename}`;
|
||||
|
||||
const blob = URL.createObjectURL(makeScriptBlob(transformedCode));
|
||||
// Push the blob URL onto the top of the stack.
|
||||
|
||||
+100
-84
@@ -3,6 +3,7 @@
|
||||
* that allows for scripts to run
|
||||
*/
|
||||
import { killWorkerScript } from "./Netscript/killWorkerScript";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||
import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter";
|
||||
@@ -59,7 +60,7 @@ export function prestigeWorkerScripts(): void {
|
||||
// JS script promises need a little massaging to have the same guarantees as netscript
|
||||
// promises. This does said massaging and kicks the script off. It returns a promise
|
||||
// that resolves or rejects when the corresponding worker script is done.
|
||||
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<WorkerScript> {
|
||||
function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Promise<void> {
|
||||
workerScript.running = true;
|
||||
|
||||
// The name of the currently running netscript function, to prevent concurrent
|
||||
@@ -79,7 +80,7 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
// This is not a problem for legacy Netscript because it also checks the
|
||||
// stop flag in the evaluator.
|
||||
if (workerScript.env.stopFlag) {
|
||||
throw workerScript;
|
||||
throw new ScriptDeath(workerScript);
|
||||
}
|
||||
|
||||
if (propName === "asleep") return f(...args); // OK for multiple simultaneous calls to sleep.
|
||||
@@ -90,7 +91,7 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
"promise-returning function? (Currently running: %s tried to run: %s)";
|
||||
if (runningFn) {
|
||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, sprintf(msg, runningFn, propName));
|
||||
throw workerScript;
|
||||
throw new ScriptDeath(workerScript);
|
||||
}
|
||||
runningFn = propName;
|
||||
|
||||
@@ -116,18 +117,29 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
};
|
||||
}
|
||||
|
||||
for (const prop of Object.keys(workerScript.env.vars)) {
|
||||
if (typeof workerScript.env.vars[prop] !== "function") continue;
|
||||
workerScript.env.vars[prop] = wrap(prop, workerScript.env.vars[prop]);
|
||||
function wrapObject(vars: any, ...tree: string[]): void {
|
||||
for (const prop of Object.keys(vars)) {
|
||||
switch (typeof vars[prop]) {
|
||||
case "function": {
|
||||
vars[prop] = wrap([...tree, prop].join("."), vars[prop]);
|
||||
break;
|
||||
}
|
||||
case "object": {
|
||||
if (Array.isArray(vars[prop])) continue;
|
||||
wrapObject(vars[prop], ...tree, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
workerScript.env.vars.stanek.charge = wrap("stanek.charge", workerScript.env.vars.stanek.charge);
|
||||
wrapObject(workerScript.env.vars);
|
||||
|
||||
// Note: the environment that we pass to the JS script only needs to contain the functions visible
|
||||
// to that script, which env.vars does at this point.
|
||||
return new Promise<WorkerScript>((resolve, reject) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
executeJSScript(player, workerScript.getServer().scripts, workerScript)
|
||||
.then(() => {
|
||||
resolve(workerScript);
|
||||
resolve();
|
||||
})
|
||||
.catch((e) => reject(e));
|
||||
}).catch((e) => {
|
||||
@@ -140,20 +152,21 @@ function startNetscript2Script(player: IPlayer, workerScript: WorkerScript): Pro
|
||||
e.message + ((e.stack && "\nstack:\n" + e.stack.toString()) || ""),
|
||||
);
|
||||
}
|
||||
throw workerScript;
|
||||
throw new ScriptDeath(workerScript);
|
||||
} else if (isScriptErrorMessage(e)) {
|
||||
workerScript.errorMessage = e;
|
||||
throw workerScript;
|
||||
} else if (e instanceof WorkerScript) {
|
||||
throw new ScriptDeath(workerScript);
|
||||
} else if (e instanceof ScriptDeath) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, e);
|
||||
throw workerScript; // Don't know what to do with it, let's rethrow.
|
||||
// Don't know what to do with it, let's try making an error message out of it
|
||||
workerScript.errorMessage = makeRuntimeRejectMsg(workerScript, "" + e);
|
||||
throw new ScriptDeath(workerScript);
|
||||
});
|
||||
}
|
||||
|
||||
function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript> {
|
||||
function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
const code = workerScript.code;
|
||||
workerScript.running = true;
|
||||
|
||||
@@ -168,7 +181,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const interpreterInitialization = function (int: any, scope: any): void {
|
||||
@@ -201,7 +214,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
})
|
||||
.catch(function (err: any) {
|
||||
// workerscript is when you cancel a delay
|
||||
if (!(err instanceof WorkerScript)) {
|
||||
if (!(err instanceof ScriptDeath)) {
|
||||
console.error(err);
|
||||
const errorTextArray = err.split("|DELIMITER|");
|
||||
const hostname = errorTextArray[1];
|
||||
@@ -214,7 +227,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -277,14 +290,14 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve(workerScript);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
function runInterpreter(): void {
|
||||
try {
|
||||
if (workerScript.env.stopFlag) {
|
||||
return reject(workerScript);
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
}
|
||||
|
||||
let more = true;
|
||||
@@ -297,7 +310,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
if (more) {
|
||||
setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
|
||||
} else {
|
||||
resolve(workerScript);
|
||||
resolve();
|
||||
}
|
||||
} catch (e: any) {
|
||||
e = e.toString();
|
||||
@@ -305,7 +318,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
e = makeRuntimeRejectMsg(workerScript, e);
|
||||
}
|
||||
workerScript.errorMessage = e;
|
||||
return reject(workerScript);
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,11 +327,12 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
||||
} catch (e: any) {
|
||||
if (isString(e)) {
|
||||
workerScript.errorMessage = e;
|
||||
return reject(workerScript);
|
||||
} else if (e instanceof WorkerScript) {
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
} else if (e instanceof ScriptDeath) {
|
||||
return reject(e);
|
||||
} else {
|
||||
return reject(workerScript);
|
||||
console.error(e);
|
||||
return reject(new ScriptDeath(workerScript));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -541,83 +555,85 @@ function createAndAddWorkerScript(
|
||||
|
||||
// Create the WorkerScript. NOTE: WorkerScript ctor will set the underlying
|
||||
// RunningScript's PID as well
|
||||
const s = new WorkerScript(runningScriptObj, pid, NetscriptFunctions);
|
||||
s.ramUsage = oneRamUsage;
|
||||
const workerScript = new WorkerScript(runningScriptObj, pid, NetscriptFunctions);
|
||||
workerScript.ramUsage = oneRamUsage;
|
||||
|
||||
// Add the WorkerScript to the global pool
|
||||
workerScripts.set(pid, s);
|
||||
workerScripts.set(pid, workerScript);
|
||||
WorkerScriptStartStopEventEmitter.emit();
|
||||
|
||||
// Start the script's execution
|
||||
let p: Promise<WorkerScript> | null = null; // Script's resulting promise
|
||||
if (s.name.endsWith(".js") || s.name.endsWith(".ns")) {
|
||||
p = startNetscript2Script(player, s);
|
||||
let scriptExecution: Promise<void> | null = null; // Script's resulting promise
|
||||
if (workerScript.name.endsWith(".js") || workerScript.name.endsWith(".ns")) {
|
||||
scriptExecution = startNetscript2Script(player, workerScript);
|
||||
} else {
|
||||
p = startNetscript1Script(s);
|
||||
if (!(p instanceof Promise)) {
|
||||
scriptExecution = startNetscript1Script(workerScript);
|
||||
if (!(scriptExecution instanceof Promise)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
// running status to false
|
||||
p.then(function (w: WorkerScript) {
|
||||
w.running = false;
|
||||
w.env.stopFlag = true;
|
||||
// On natural death, the earnings are transfered to the parent if it still exists.
|
||||
if (parent !== undefined) {
|
||||
if (parent.running) {
|
||||
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
|
||||
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
|
||||
scriptExecution
|
||||
.then(function () {
|
||||
workerScript.running = false;
|
||||
workerScript.env.stopFlag = true;
|
||||
// On natural death, the earnings are transfered to the parent if it still exists.
|
||||
if (parent !== undefined) {
|
||||
if (parent.running) {
|
||||
parent.scriptRef.onlineExpGained += runningScriptObj.onlineExpGained;
|
||||
parent.scriptRef.onlineMoneyMade += runningScriptObj.onlineMoneyMade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
killWorkerScript(s);
|
||||
w.log("", () => "Script finished running");
|
||||
}).catch(function (w) {
|
||||
if (w instanceof Error) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
return;
|
||||
} else if (w instanceof WorkerScript) {
|
||||
if (isScriptErrorMessage(w.errorMessage)) {
|
||||
const errorTextArray = w.errorMessage.split("|DELIMITER|");
|
||||
if (errorTextArray.length != 4) {
|
||||
console.error("ERROR: Something wrong with Error text in evaluator...");
|
||||
console.error("Error text: " + w.errorMessage);
|
||||
return;
|
||||
killWorkerScript(workerScript);
|
||||
workerScript.log("", () => "Script finished running");
|
||||
})
|
||||
.catch(function (e) {
|
||||
if (e instanceof Error) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + e.toString());
|
||||
return;
|
||||
} else if (e instanceof ScriptDeath) {
|
||||
if (isScriptErrorMessage(workerScript.errorMessage)) {
|
||||
const errorTextArray = workerScript.errorMessage.split("|DELIMITER|");
|
||||
if (errorTextArray.length != 4) {
|
||||
console.error("ERROR: Something wrong with Error text in evaluator...");
|
||||
console.error("Error text: " + workerScript.errorMessage);
|
||||
return;
|
||||
}
|
||||
const hostname = errorTextArray[1];
|
||||
const scriptName = errorTextArray[2];
|
||||
const errorMsg = errorTextArray[3];
|
||||
|
||||
let msg = `RUNTIME ERROR<br>${scriptName}@${hostname} (PID - ${workerScript.pid})<br>`;
|
||||
if (workerScript.args.length > 0) {
|
||||
msg += `Args: ${arrayToString(workerScript.args)}<br>`;
|
||||
}
|
||||
msg += "<br>";
|
||||
msg += errorMsg;
|
||||
|
||||
dialogBoxCreate(msg);
|
||||
workerScript.log("", () => "Script crashed with runtime error");
|
||||
} else {
|
||||
workerScript.log("", () => "Script killed");
|
||||
return; // Already killed, so stop here
|
||||
}
|
||||
const hostname = errorTextArray[1];
|
||||
const scriptName = errorTextArray[2];
|
||||
const errorMsg = errorTextArray[3];
|
||||
|
||||
let msg = `RUNTIME ERROR<br>${scriptName}@${hostname}<br>`;
|
||||
if (w.args.length > 0) {
|
||||
msg += `Args: ${arrayToString(w.args)}<br>`;
|
||||
}
|
||||
msg += "<br>";
|
||||
msg += errorMsg;
|
||||
|
||||
dialogBoxCreate(msg);
|
||||
w.log("", () => "Script crashed with runtime error");
|
||||
} else if (isScriptErrorMessage(e)) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error(
|
||||
"ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " +
|
||||
e.toString(),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
w.log("", () => "Script killed");
|
||||
return; // Already killed, so stop here
|
||||
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
||||
console.error(e);
|
||||
}
|
||||
} else if (isScriptErrorMessage(w)) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.error(
|
||||
"ERROR: Evaluating workerscript returns only error message rather than WorkerScript object. THIS SHOULDN'T HAPPEN: " +
|
||||
w.toString(),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
dialogBoxCreate("An unknown script died for an unknown reason. This is a bug please contact game dev");
|
||||
console.error(w);
|
||||
}
|
||||
|
||||
killWorkerScript(s);
|
||||
});
|
||||
killWorkerScript(workerScript);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
<Typography variant="h5">Graft Augmentations</Typography>
|
||||
{getAvailableAugs(player).length > 0 ? (
|
||||
<Paper sx={{ my: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
|
||||
<List sx={{ maxHeight: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
|
||||
<List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
|
||||
{getAvailableAugs(player).map((k, i) => (
|
||||
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
|
||||
<Typography>{k}</Typography>
|
||||
@@ -130,9 +130,13 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
<>
|
||||
Cancelling grafting will <b>not</b> save grafting progress, and the money you spend will <b>not</b>{" "}
|
||||
be returned.
|
||||
<br />
|
||||
<br />
|
||||
Additionally, grafting an Augmentation will increase the potency of the Entropy virus.
|
||||
{!player.hasAugmentation(AugmentationNames.CongruityImplant) && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
Additionally, grafting an Augmentation will increase the potency of the Entropy virus.
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -62,7 +62,7 @@ import { Money } from "../../ui/React/Money";
|
||||
|
||||
import React from "react";
|
||||
import { serverMetadata } from "../../Server/data/servers";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
|
||||
import { calculateClassEarnings } from "../formulas/work";
|
||||
import { achievements } from "../../Achievements/Achievements";
|
||||
import { FactionNames } from "../../Faction/data/FactionNames";
|
||||
@@ -1366,15 +1366,19 @@ export function craftAugmentationWork(this: IPlayer, numCycles: number): boolean
|
||||
export function finishGraftAugmentationWork(this: IPlayer, cancelled: boolean): string {
|
||||
const augName = this.graftAugmentationName;
|
||||
if (cancelled === false) {
|
||||
dialogBoxCreate(
|
||||
`You've finished crafting ${augName}.<br>The augmentation has been grafted to your body, but you feel a bit off.`,
|
||||
);
|
||||
|
||||
applyAugmentation(Augmentations[augName]);
|
||||
this.entropy += 1;
|
||||
this.applyEntropy(this.entropy);
|
||||
|
||||
if (!this.hasAugmentation(AugmentationNames.CongruityImplant)) {
|
||||
this.entropy += 1;
|
||||
this.applyEntropy(this.entropy);
|
||||
}
|
||||
|
||||
dialogBoxCreate(
|
||||
`You've finished grafting ${augName}.<br>The augmentation has been applied to your body` +
|
||||
(this.hasAugmentation(AugmentationNames.CongruityImplant) ? "." : ", but you feel a bit off."),
|
||||
);
|
||||
} else {
|
||||
dialogBoxCreate(`You cancelled the crafting of ${augName}.<br>Your money was not returned to you.`);
|
||||
dialogBoxCreate(`You cancelled the grafting of ${augName}.<br>Your money was not returned to you.`);
|
||||
}
|
||||
|
||||
// Intelligence gain
|
||||
@@ -1731,7 +1735,11 @@ export function regenerateHp(this: IPlayer, amt: number): void {
|
||||
|
||||
export function hospitalize(this: IPlayer): number {
|
||||
const cost = getHospitalizationCost(this);
|
||||
SnackbarEvents.emit(`You've been Hospitalized for ${numeralWrapper.formatMoney(cost)}`, "warning", 2000);
|
||||
SnackbarEvents.emit(
|
||||
`You've been Hospitalized for ${numeralWrapper.formatMoney(cost)}`,
|
||||
ToastVariant.WARNING,
|
||||
2000,
|
||||
);
|
||||
|
||||
this.loseMoney(cost, "hospitalization");
|
||||
this.hp = this.max_hp;
|
||||
@@ -2706,7 +2714,7 @@ export function canAccessGrafting(this: IPlayer): boolean {
|
||||
export function giveExploit(this: IPlayer, exploit: Exploit): void {
|
||||
if (!this.exploits.includes(exploit)) {
|
||||
this.exploits.push(exploit);
|
||||
SnackbarEvents.emit("SF -1 acquired!", "success", 2000);
|
||||
SnackbarEvents.emit("SF -1 acquired!", ToastVariant.SUCCESS, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2715,7 +2723,7 @@ export function giveAchievement(this: IPlayer, achievementId: string): void {
|
||||
if (!achievement) return;
|
||||
if (!this.achievements.map((a) => a.ID).includes(achievementId)) {
|
||||
this.achievements.push({ ID: achievementId, unlockedOn: new Date().getTime() });
|
||||
SnackbarEvents.emit(`Unlocked Achievement: "${achievement.Name}"`, "success", 2000);
|
||||
SnackbarEvents.emit(`Unlocked Achievement: "${achievement.Name}"`, ToastVariant.SUCCESS, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,6 @@ import { SourceFiles } from "./SourceFile/SourceFiles";
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
import { IRouter } from "./ui/Router";
|
||||
|
||||
export let redPillFlag = false;
|
||||
|
||||
export function setRedPillFlag(b: boolean): void {
|
||||
redPillFlag = b;
|
||||
}
|
||||
|
||||
function giveSourceFile(bitNodeNumber: number): void {
|
||||
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
|
||||
const sourceFile = SourceFiles[sourceFileKey];
|
||||
@@ -82,7 +76,6 @@ export function enterBitNode(router: IRouter, flume: boolean, destroyedBitNode:
|
||||
if (newBitNode === 5 && Player.intelligence === 0) {
|
||||
Player.intelligence = 1;
|
||||
}
|
||||
redPillFlag = false;
|
||||
// Set new Bit Node
|
||||
Player.bitNodeN = newBitNode;
|
||||
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
|
||||
import { staneksGift, loadStaneksGift } from "./CotMG/Helper";
|
||||
|
||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "./ui/React/Snackbar";
|
||||
|
||||
import * as ExportBonus from "./ExportBonus";
|
||||
|
||||
@@ -114,7 +114,7 @@ class BitburnerSaveObject {
|
||||
pushGameSaved(saveData);
|
||||
|
||||
if (emitToastEvent) {
|
||||
SnackbarEvents.emit("Game Saved!", "info", 2000);
|
||||
SnackbarEvents.emit("Game Saved!", ToastVariant.INFO, 2000);
|
||||
}
|
||||
return resolve();
|
||||
})
|
||||
|
||||
@@ -230,6 +230,9 @@ async function parseOnlyRamCalculate(
|
||||
} else if (ref in workerScript.env.vars.grafting) {
|
||||
func = workerScript.env.vars.grafting[ref];
|
||||
refDetail = `grafting.${ref}`;
|
||||
} else if (ref in workerScript.env.vars.singularity) {
|
||||
func = workerScript.env.vars.singularity[ref];
|
||||
refDetail = `singularity.${ref}`;
|
||||
} else {
|
||||
func = workerScript.env.vars[ref];
|
||||
refDetail = `${ref}`;
|
||||
|
||||
+86
-14
@@ -101,24 +101,46 @@ interface Player {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
interface RunningScript {
|
||||
export interface RunningScript {
|
||||
/** Arguments the script was called with */
|
||||
args: string[];
|
||||
/** Filename of the script */
|
||||
filename: string;
|
||||
/**
|
||||
* Script logs as an array. The newest log entries are at the bottom.
|
||||
* Timestamps, if enabled, are placed inside `[brackets]` at the start of each line.
|
||||
**/
|
||||
logs: string[];
|
||||
/** Total amount of hacking experience earned from this script when offline */
|
||||
offlineExpGained: number;
|
||||
/** Total amount of money made by this script when offline */
|
||||
offlineMoneyMade: number;
|
||||
/** Offline running time of the script, in seconds **/
|
||||
/** Number of seconds that the script has been running offline */
|
||||
offlineRunningTime: number;
|
||||
/** Total amount of hacking experience earned from this script when online */
|
||||
onlineExpGained: number;
|
||||
/** Total amount of money made by this script when online */
|
||||
onlineMoneyMade: number;
|
||||
/** Online running time of the script, in seconds **/
|
||||
/** Number of seconds that this script has been running online */
|
||||
onlineRunningTime: number;
|
||||
/** Process ID. Must be an integer */
|
||||
pid: number;
|
||||
/** How much RAM this script uses for ONE thread */
|
||||
ramUsage: number;
|
||||
/** Hostname of the server on which this script runs */
|
||||
server: string;
|
||||
/** Number of threads that this script runs with */
|
||||
threads: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface RecentScript extends RunningScript {
|
||||
/** Timestamp of when the script was killed */
|
||||
timeOfDeath: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data representing the internal values of a crime.
|
||||
* @public
|
||||
@@ -2618,7 +2640,7 @@ export interface Hacknet {
|
||||
* // NS1:
|
||||
* var upgradeName = "Sell for Corporation Funds";
|
||||
* if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
||||
* hacknet.spendHashes(upgName);
|
||||
* hacknet.spendHashes(upgradeName);
|
||||
* }
|
||||
* ```
|
||||
* @example
|
||||
@@ -2626,7 +2648,7 @@ export interface Hacknet {
|
||||
* // NS2:
|
||||
* const upgradeName = "Sell for Corporation Funds";
|
||||
* if (ns.hacknet.numHashes() > ns.hacknet.hashCost(upgradeName)) {
|
||||
* ns.hacknet.spendHashes(upgName);
|
||||
* ns.hacknet.spendHashes(upgradeName);
|
||||
* }
|
||||
* ```
|
||||
* @param upgName - Name of the upgrade of Hacknet Node.
|
||||
@@ -4298,7 +4320,7 @@ interface UserInterface {
|
||||
* {@link https://bitburner.readthedocs.io/en/latest/netscript/netscriptjs.html| ns2 in-game docs}
|
||||
* <hr>
|
||||
*/
|
||||
export interface NS extends Singularity {
|
||||
export interface NS {
|
||||
/**
|
||||
* Namespace for hacknet functions.
|
||||
* @remarks RAM cost: 4 GB
|
||||
@@ -4555,9 +4577,10 @@ export interface NS extends Singularity {
|
||||
* Returns the security increase that would occur if a hack with this many threads happened.
|
||||
*
|
||||
* @param threads - Amount of threads that will be used.
|
||||
* @param hostname - Hostname of the target server. The number of threads is limited to the number needed to hack the servers maximum amount of money.
|
||||
* @returns The security increase.
|
||||
*/
|
||||
hackAnalyzeSecurity(threads: number): number;
|
||||
hackAnalyzeSecurity(threads: number, hostname?: string): number;
|
||||
|
||||
/**
|
||||
* Get the chance of successfully hacking a server.
|
||||
@@ -4781,6 +4804,27 @@ export interface NS extends Singularity {
|
||||
*/
|
||||
getScriptLogs(fn?: string, host?: string, ...args: any[]): string[];
|
||||
|
||||
/**
|
||||
* Get an array of recently killed scripts across all servers.
|
||||
* @remarks
|
||||
* RAM cost: 0.2 GB
|
||||
*
|
||||
* The most recently killed script is the first element in the array.
|
||||
* Note that there is a maximum number of recently killed scripts which are tracked.
|
||||
* This is configurable in the game's options as `Recently killed scripts size`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* let recentScripts = ns.getRecentScripts();
|
||||
* let mostRecent = recentScripts.shift()
|
||||
* if (mostRecent)
|
||||
* ns.tprint(mostRecent.logs.join('\n'))
|
||||
* ```
|
||||
*
|
||||
* @returns Array with information about previously killed scripts.
|
||||
*/
|
||||
getRecentScripts(): RecentScript[];
|
||||
|
||||
/**
|
||||
* Open the tail window of a script.
|
||||
* @remarks
|
||||
@@ -6183,7 +6227,7 @@ export interface NS extends Singularity {
|
||||
* @param variant - Type of toast, must be one of success, info, warning, error. Defaults to success.
|
||||
* @param duration - Duration of toast in ms. Can also be `null` to create a persistent toast. Defaults to 2000
|
||||
*/
|
||||
toast(msg: any, variant?: string, duration?: number | null): void;
|
||||
toast(msg: any, variant?: ToastVariantValues, duration?: number | null): void;
|
||||
|
||||
/**
|
||||
* Download a file from the internet.
|
||||
@@ -6378,6 +6422,24 @@ export interface NS extends Singularity {
|
||||
* RAM cost: 0.2 GB
|
||||
*/
|
||||
getSharePower(): number;
|
||||
|
||||
enums: NSEnums;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export enum ToastVariant {
|
||||
SUCCESS = "success",
|
||||
WARNING = "warning",
|
||||
ERROR = "error",
|
||||
INFO = "info",
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type ToastVariantValues = `${ToastVariant}`;
|
||||
|
||||
/** @public */
|
||||
export interface NSEnums {
|
||||
toast: typeof ToastVariant;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6914,10 +6976,14 @@ interface Employee {
|
||||
interface Product {
|
||||
/** Name of the product */
|
||||
name: string;
|
||||
/** Demand for the product */
|
||||
dmd: number;
|
||||
/** Competition for the product */
|
||||
cmp: number;
|
||||
/** Demand for the product, only present if "Market Research - Demand" unlocked */
|
||||
dmd: number | undefined;
|
||||
/** Competition for the product, only present if "Market Research - Competition" unlocked */
|
||||
cmp: number | undefined;
|
||||
/** Product Rating */
|
||||
rat: number;
|
||||
/** Product Properties. The data is \{qlt, per, dur, rel, aes, fea\} */
|
||||
properties: { [key: string]: number };
|
||||
/** Production cost */
|
||||
pCost: number;
|
||||
/** Sell cost, can be "MP+5" */
|
||||
@@ -6941,7 +7007,11 @@ interface Material {
|
||||
qty: number;
|
||||
/** Quality of the material */
|
||||
qlt: number;
|
||||
/** Amount of material produced */
|
||||
/** Demand for the material, only present if "Market Research - Demand" unlocked */
|
||||
dmd: number | undefined;
|
||||
/** Competition for the material, only present if "Market Research - Competition" unlocked */
|
||||
cmp: number | undefined;
|
||||
/** Amount of material produced */
|
||||
prod: number;
|
||||
/** Amount of material sold */
|
||||
sell: number;
|
||||
@@ -6987,8 +7057,10 @@ interface Office {
|
||||
maxMor: number;
|
||||
/** Name of all the employees */
|
||||
employees: string[];
|
||||
/** Positions of the employees */
|
||||
/** Production of the employees */
|
||||
employeeProd: EmployeeJobs;
|
||||
/** Positions of the employees */
|
||||
employeeJobs: EmployeeJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -981,11 +981,11 @@ export function Root(props: IProps): React.ReactElement {
|
||||
</Button>
|
||||
<Typography>
|
||||
{" "}
|
||||
Documentation:{" "}
|
||||
<strong>Documentation:</strong>{" "}
|
||||
<Link target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
|
||||
Basic
|
||||
</Link>{" "}
|
||||
|
|
||||
</Link>
|
||||
{" | "}
|
||||
<Link target="_blank" href="https://github.com/danielyxie/bitburner/blob/dev/markdown/bitburner.ns.md">
|
||||
Full
|
||||
</Link>
|
||||
|
||||
@@ -248,6 +248,19 @@ export class BaseServer {
|
||||
this.ramUsed = ram;
|
||||
}
|
||||
|
||||
pushProgram(program: string): void {
|
||||
if (this.programs.includes(program)) return;
|
||||
|
||||
// Remove partially created program if there is one
|
||||
const existingPartialExeIndex = this.programs.findIndex((p) => p.startsWith(program));
|
||||
// findIndex returns -1 if there is no match, we only want to splice on a match
|
||||
if (existingPartialExeIndex > -1) {
|
||||
this.programs.splice(existingPartialExeIndex, 1);
|
||||
}
|
||||
|
||||
this.programs.push(program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to a script file
|
||||
* Overwrites existing files. Creates new files if the script does not eixst
|
||||
|
||||
@@ -63,6 +63,11 @@ interface IDefaultSettings {
|
||||
*/
|
||||
Locale: string;
|
||||
|
||||
/**
|
||||
* Limit the number of recently killed script entries being tracked.
|
||||
*/
|
||||
MaxRecentScriptsCapacity: number;
|
||||
|
||||
/**
|
||||
* Limit the number of log entries for each script being executed on each server.
|
||||
*/
|
||||
@@ -191,6 +196,7 @@ export const defaultSettings: IDefaultSettings = {
|
||||
EnableBashHotkeys: false,
|
||||
TimestampsFormat: "",
|
||||
Locale: "en",
|
||||
MaxRecentScriptsCapacity: 50,
|
||||
MaxLogCapacity: 50,
|
||||
MaxPortCapacity: 50,
|
||||
MaxTerminalCapacity: 500,
|
||||
@@ -228,6 +234,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
EnableBashHotkeys: defaultSettings.EnableBashHotkeys,
|
||||
TimestampsFormat: defaultSettings.TimestampsFormat,
|
||||
Locale: "en",
|
||||
MaxRecentScriptsCapacity: defaultSettings.MaxRecentScriptsCapacity,
|
||||
MaxLogCapacity: defaultSettings.MaxLogCapacity,
|
||||
MaxPortCapacity: defaultSettings.MaxPortCapacity,
|
||||
MaxTerminalCapacity: defaultSettings.MaxTerminalCapacity,
|
||||
|
||||
@@ -51,7 +51,6 @@ import { CONSTANTS } from "../../Constants";
|
||||
import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../../InteractiveTutorial";
|
||||
import { getAvailableCreatePrograms } from "../../Programs/ProgramHelpers";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { redPillFlag } from "../../RedPill";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
|
||||
@@ -275,7 +274,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
// Alt-o - Options
|
||||
function handleShortcuts(this: Document, event: KeyboardEvent): any {
|
||||
if (Settings.DisableHotkeys) return;
|
||||
if ((props.player.isWorking && props.player.focus) || redPillFlag) return;
|
||||
if ((props.player.isWorking && props.player.focus) || props.router.page() === Page.BitVerse) return;
|
||||
if (event.key === KEY.T && event.altKey) {
|
||||
event.preventDefault();
|
||||
clickTerminal();
|
||||
|
||||
@@ -306,7 +306,7 @@ export function processStockPrices(numCycles = 1): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function initStockMarketFnForReact(): void {
|
||||
export function initStockMarketFn(): void {
|
||||
initStockMarket();
|
||||
initSymbolToStockMap();
|
||||
}
|
||||
|
||||
@@ -191,8 +191,8 @@ export const HelpTexts: IMap<string[]> = {
|
||||
"Usage: connect [hostname]",
|
||||
" ",
|
||||
"Connect to a remote server. The hostname of the remote server must be given as the argument ",
|
||||
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
|
||||
"see which servers can be connected to, use the 'scan' command.",
|
||||
"to this command. Note that only servers that are immediately adjacent to the current server in the network and the ones that have",
|
||||
"a backdoor installed can be connected to. To see which servers can be connected to, use the 'scan' command.",
|
||||
" ",
|
||||
],
|
||||
cp: ["Usage: cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp.", " "],
|
||||
|
||||
@@ -4,7 +4,8 @@ import { getSubdirectories } from "./DirectoryServerHelpers";
|
||||
import { Aliases, GlobalAliases, substituteAliases } from "../Alias";
|
||||
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { GetServer, GetAllServers } from "../Server/AllServers";
|
||||
import { GetAllServers } from "../Server/AllServers";
|
||||
import { Server } from "../Server/Server";
|
||||
import { ParseCommand, ParseCommands } from "./Parser";
|
||||
import { HelpTexts } from "./HelpText";
|
||||
import { isScriptFilename } from "../Script/isScriptFilename";
|
||||
@@ -238,16 +239,14 @@ export async function determineAllPossibilitiesForTabCompletion(
|
||||
}
|
||||
|
||||
if (isCommand("connect")) {
|
||||
// All network connections
|
||||
for (let i = 0; i < currServ.serversOnNetwork.length; ++i) {
|
||||
const serv = GetServer(currServ.serversOnNetwork[i]);
|
||||
if (serv == null) {
|
||||
continue;
|
||||
}
|
||||
allPos.push(serv.hostname);
|
||||
}
|
||||
|
||||
return allPos;
|
||||
// All directly connected and backdoored servers are reachable
|
||||
console.log(GetAllServers());
|
||||
return GetAllServers()
|
||||
.filter(
|
||||
(server) =>
|
||||
currServ.serversOnNetwork.includes(server.hostname) || (server instanceof Server && server.backdoorInstalled),
|
||||
)
|
||||
.map((server) => server.hostname);
|
||||
}
|
||||
|
||||
if (isCommand("nano") || isCommand("vim")) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import { StyleEditorButton } from "./StyleEditorButton";
|
||||
import { ThemeEntry } from "./ThemeEntry";
|
||||
import { ThemeCollaborate } from "./ThemeCollaborate";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
|
||||
|
||||
interface IProps {
|
||||
router: IRouter;
|
||||
@@ -54,7 +54,7 @@ export function ThemeBrowser({ router }: IProps): React.ReactElement {
|
||||
UNDO
|
||||
</Button>
|
||||
</>,
|
||||
"info",
|
||||
ToastVariant.INFO,
|
||||
30000,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { ScriptDeath } from "./Netscript/ScriptDeath";
|
||||
import { isScriptErrorMessage } from "./NetscriptEvaluator";
|
||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||
|
||||
@@ -14,9 +14,9 @@ export function setupUncaughtPromiseHandler(): void {
|
||||
msg += "<br>";
|
||||
msg += errorMsg;
|
||||
dialogBoxCreate(msg);
|
||||
} else if (e.reason instanceof WorkerScript) {
|
||||
} else if (e.reason instanceof ScriptDeath) {
|
||||
const msg =
|
||||
`UNCAUGHT PROMISE ERROR<br>You forgot to await a promise<br>${e.reason.name}@${e.reason.hostname}<br>` +
|
||||
`UNCAUGHT PROMISE ERROR<br>You forgot to await a promise<br>${e.reason.name}@${e.reason.hostname} (PID - ${e.reason.pid})<br>` +
|
||||
`Maybe hack / grow / weaken ?`;
|
||||
dialogBoxCreate(msg);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { MinHeap } from "../utils/Heap";
|
||||
|
||||
import { HammingEncode, HammingDecode } from "../utils/HammingCodeTools";
|
||||
/* tslint:disable:completed-docs no-magic-numbers arrow-return-shorthand */
|
||||
|
||||
/* Function that generates a valid 'data' for a contract type */
|
||||
@@ -122,7 +124,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
" 2 + 2\n",
|
||||
" 2 + 1 + 1\n",
|
||||
" 1 + 1 + 1 + 1\n\n",
|
||||
`How many different ways can the number ${n} be written as a sum of at least`,
|
||||
`How many different distinct ways can the number ${n} be written as a sum of at least`,
|
||||
"two positive integers?",
|
||||
].join(" ");
|
||||
},
|
||||
@@ -145,6 +147,51 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return ways[data] === parseInt(ans, 10);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: [number, number[]]): string => {
|
||||
const n: number = data[0];
|
||||
const s: number[] = data[1];
|
||||
return [
|
||||
`How many different distinct ways can the number ${n} be written`,
|
||||
"as a sum of integers contained in the set:\n\n",
|
||||
`[${s}]?\n\n`,
|
||||
"You may use each integer in the set zero or more times.",
|
||||
].join(" ");
|
||||
},
|
||||
difficulty: 2,
|
||||
gen: (): [number, number[]] => {
|
||||
const n: number = getRandomInt(12, 200);
|
||||
const maxLen: number = getRandomInt(8, 12);
|
||||
const s: number[] = [];
|
||||
// Bias towards small numbers is intentional to have much bigger answers in general
|
||||
// to force people better optimize their solutions
|
||||
for (let i = 1; i <= n; i++) {
|
||||
if (s.length == maxLen) {
|
||||
break;
|
||||
}
|
||||
if (Math.random() < 0.6 || n - i < maxLen - s.length) {
|
||||
s.push(i);
|
||||
}
|
||||
}
|
||||
return [n, s];
|
||||
},
|
||||
name: "Total Ways to Sum II",
|
||||
numTries: 10,
|
||||
solver: (data: [number, number[]], ans: string): boolean => {
|
||||
// https://www.geeksforgeeks.org/coin-change-dp-7/?ref=lbp
|
||||
const n = data[0];
|
||||
const s = data[1];
|
||||
const ways: number[] = [1];
|
||||
ways.length = n + 1;
|
||||
ways.fill(0, 1);
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
for (let j = s[i]; j <= n; j++) {
|
||||
ways[j] += ways[j - s[i]];
|
||||
}
|
||||
}
|
||||
return ways[n] === parseInt(ans, 10);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (n: number[][]): string => {
|
||||
let d: string = [
|
||||
@@ -310,6 +357,62 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return (ans === "1" && solution) || (ans === "0" && !solution);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (arr: number[]): string => {
|
||||
return [
|
||||
"You are given the following array of integers:\n\n",
|
||||
`${arr}\n\n`,
|
||||
"Each element in the array represents your MAXIMUM jump length",
|
||||
"at that position. This means that if you are at position i and your",
|
||||
"maximum jump length is n, you can jump to any position from",
|
||||
"i to i+n.",
|
||||
"\n\nAssuming you are initially positioned",
|
||||
"at the start of the array, determine the minimum number of",
|
||||
"jumps to reach the end of the array.\n\n",
|
||||
"If it's impossible to reach the end, then the answer should be 0.",
|
||||
].join(" ");
|
||||
},
|
||||
difficulty: 3,
|
||||
gen: (): number[] => {
|
||||
const len: number = getRandomInt(3, 25);
|
||||
const arr: number[] = [];
|
||||
arr.length = len;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
for (let j = 0; j < 10; j++) {
|
||||
if (Math.random() <= j / 10 + 0.1) {
|
||||
arr[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
},
|
||||
name: "Array Jumping Game II",
|
||||
numTries: 3,
|
||||
solver: (data: number[], ans: string): boolean => {
|
||||
const n: number = data.length;
|
||||
let reach = 0;
|
||||
let jumps = 0;
|
||||
let lastJump = -1;
|
||||
while (reach < n - 1) {
|
||||
let jumpedFrom = -1;
|
||||
for (let i = reach; i > lastJump; i--) {
|
||||
if (i + data[i] > reach) {
|
||||
reach = i + data[i];
|
||||
jumpedFrom = i;
|
||||
}
|
||||
}
|
||||
if (jumpedFrom === -1) {
|
||||
jumps = 0;
|
||||
break;
|
||||
}
|
||||
lastJump = jumpedFrom;
|
||||
jumps++;
|
||||
}
|
||||
return jumps === parseInt(ans, 10);
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (arr: number[][]): string => {
|
||||
return [
|
||||
@@ -794,6 +897,140 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return obstacleGrid[obstacleGrid.length - 1][obstacleGrid[0].length - 1] === parseInt(ans);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Shortest Path in a Grid",
|
||||
desc: (data: number[][]): string => {
|
||||
return [
|
||||
"You are located in the top-left corner of the following grid:\n\n",
|
||||
` [${data.map((line) => "[" + line + "]").join(",\n ")}]\n\n`,
|
||||
"You are trying to find the shortest path to the bottom-right corner of the grid,",
|
||||
"but there are obstacles on the grid that you cannot move onto.",
|
||||
"These obstacles are denoted by '1', while empty spaces are denoted by 0.\n\n",
|
||||
"Determine the shortest path from start to finish, if one exists.",
|
||||
"The answer should be given as a string of UDLR characters, indicating the moves along the path\n\n",
|
||||
"NOTE: If there are multiple equally short paths, any of them is accepted as answer.",
|
||||
"If there is no path, the answer should be an empty string.\n",
|
||||
"NOTE: The data returned for this contract is an 2D array of numbers representing the grid.\n\n",
|
||||
"Examples:\n\n",
|
||||
" [[0,1,0,0,0],\n",
|
||||
" [0,0,0,1,0]]\n",
|
||||
"\n",
|
||||
"Answer: 'DRRURRD'\n\n",
|
||||
" [[0,1],\n",
|
||||
" [1,0]]\n",
|
||||
"\n",
|
||||
"Answer: ''\n\n",
|
||||
].join(" ");
|
||||
},
|
||||
difficulty: 7,
|
||||
numTries: 10,
|
||||
gen: (): number[][] => {
|
||||
const height = getRandomInt(6, 12);
|
||||
const width = getRandomInt(6, 12);
|
||||
const dstY = height - 1;
|
||||
const dstX = width - 1;
|
||||
const minPathLength = dstY + dstX; // Math.abs(dstY - srcY) + Math.abs(dstX - srcX)
|
||||
|
||||
const grid: number[][] = new Array(height);
|
||||
for (let y = 0; y < height; y++) grid[y] = new Array(width).fill(0);
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
for (let x = 0; x < width; x++) {
|
||||
if (y == 0 && x == 0) continue; // Don't block start
|
||||
if (y == dstY && x == dstX) continue; // Don't block destination
|
||||
|
||||
// Generate more obstacles the farther a position is from start and destination.
|
||||
// Raw distance factor peaks at 50% at half-way mark. Rescale to 40% max.
|
||||
// Obstacle chance range of [15%, 40%] produces ~78% solvable puzzles
|
||||
const distanceFactor = (Math.min(y + x, dstY - y + dstX - x) / minPathLength) * 0.8;
|
||||
if (Math.random() < Math.max(0.15, distanceFactor)) grid[y][x] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
},
|
||||
solver: (data: number[][], ans: string): boolean => {
|
||||
const width = data[0].length;
|
||||
const height = data.length;
|
||||
const dstY = height - 1;
|
||||
const dstX = width - 1;
|
||||
|
||||
const distance: [number][] = new Array(height);
|
||||
//const prev: [[number, number] | undefined][] = new Array(height);
|
||||
const queue = new MinHeap<[number, number]>();
|
||||
|
||||
for (let y = 0; y < height; y++) {
|
||||
distance[y] = new Array(width).fill(Infinity) as [number];
|
||||
//prev[y] = new Array(width).fill(undefined) as [undefined];
|
||||
}
|
||||
|
||||
function validPosition(y: number, x: number): boolean {
|
||||
return y >= 0 && y < height && x >= 0 && x < width && data[y][x] == 0;
|
||||
}
|
||||
|
||||
// List in-bounds and passable neighbors
|
||||
function* neighbors(y: number, x: number): Generator<[number, number]> {
|
||||
if (validPosition(y - 1, x)) yield [y - 1, x]; // Up
|
||||
if (validPosition(y + 1, x)) yield [y + 1, x]; // Down
|
||||
if (validPosition(y, x - 1)) yield [y, x - 1]; // Left
|
||||
if (validPosition(y, x + 1)) yield [y, x + 1]; // Right
|
||||
}
|
||||
|
||||
// Prepare starting point
|
||||
distance[0][0] = 0;
|
||||
queue.push([0, 0], 0);
|
||||
|
||||
// Take next-nearest position and expand potential paths from there
|
||||
while (queue.size > 0) {
|
||||
const [y, x] = queue.pop() as [number, number];
|
||||
for (const [yN, xN] of neighbors(y, x)) {
|
||||
const d = distance[y][x] + 1;
|
||||
if (d < distance[yN][xN]) {
|
||||
if (distance[yN][xN] == Infinity)
|
||||
// Not reached previously
|
||||
queue.push([yN, xN], d);
|
||||
// Found a shorter path
|
||||
else queue.changeWeight(([yQ, xQ]) => yQ == yN && xQ == xN, d);
|
||||
//prev[yN][xN] = [y, x];
|
||||
distance[yN][xN] = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No path at all?
|
||||
if (distance[dstY][dstX] == Infinity) return ans == "";
|
||||
|
||||
// There is a solution, require that the answer path is as short as the shortest
|
||||
// path we found
|
||||
if (ans.length > distance[dstY][dstX]) return false;
|
||||
|
||||
// Further verify that the answer path is a valid path
|
||||
let ansX = 0;
|
||||
let ansY = 0;
|
||||
for (const direction of ans) {
|
||||
switch (direction) {
|
||||
case "U":
|
||||
ansY -= 1;
|
||||
break;
|
||||
case "D":
|
||||
ansY += 1;
|
||||
break;
|
||||
case "L":
|
||||
ansX -= 1;
|
||||
break;
|
||||
case "R":
|
||||
ansX += 1;
|
||||
break;
|
||||
default:
|
||||
return false; // Invalid character
|
||||
}
|
||||
if (!validPosition(ansY, ansX)) return false;
|
||||
}
|
||||
|
||||
// Path was valid, finally verify that the answer path brought us to the end coordinates
|
||||
return ansY == dstY && ansX == dstX;
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: (data: string): string => {
|
||||
return [
|
||||
@@ -1008,4 +1245,62 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "HammingCodes: Integer to encoded Binary",
|
||||
numTries: 10,
|
||||
difficulty: 5,
|
||||
desc: (n: number): string => {
|
||||
return [
|
||||
"You are given the following decimal Value: \n",
|
||||
`${n} \n`,
|
||||
"Convert it into a binary string and encode it as a 'Hamming-Code'. eg:\n ",
|
||||
"Value 8 will result into binary '1000', which will be encoded",
|
||||
"with the pattern 'pppdpddd', where p is a paritybit and d a databit,\n",
|
||||
"or '10101' (Value 21) will result into (pppdpdddpd) '1001101011'.\n\n",
|
||||
"NOTE: You need an parity Bit on Index 0 as an 'overall'-paritybit. \n",
|
||||
"NOTE 2: You should watch the HammingCode-video from 3Blue1Brown, which explains the 'rule' of encoding,",
|
||||
"including the first Index parity-bit mentioned on the first note.\n\n",
|
||||
"Now the only one rule for this encoding:\n",
|
||||
" It's not allowed to add additional leading '0's to the binary value\n",
|
||||
"That means, the binary value has to be encoded as it is",
|
||||
].join(" ");
|
||||
},
|
||||
gen: (): number => {
|
||||
return getRandomInt(Math.pow(2, 4), Math.pow(2, getRandomInt(1, 57)));
|
||||
},
|
||||
solver: (data: number, ans: string): boolean => {
|
||||
return ans === HammingEncode(data);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "HammingCodes: Encoded Binary to Integer",
|
||||
difficulty: 8,
|
||||
numTries: 10,
|
||||
desc: (n: string): string => {
|
||||
return [
|
||||
"You are given the following encoded binary String: \n",
|
||||
`'${n}' \n`,
|
||||
"Treat it as a Hammingcode with 1 'possible' error on an random Index.\n",
|
||||
"Find the 'possible' wrong bit, fix it and extract the decimal value, which is hidden inside the string.\n\n",
|
||||
"Note: The length of the binary string is dynamic, but it's encoding/decoding is following Hammings 'rule'\n",
|
||||
"Note 2: Index 0 is an 'overall' parity bit. Watch the Hammingcode-video from 3Blue1Brown for more information\n",
|
||||
"Note 3: There's a ~55% chance for an altered Bit. So... MAYBE there is an altered Bit 😉\n",
|
||||
"Extranote for automation: return the decimal value as a string",
|
||||
].join(" ");
|
||||
},
|
||||
gen: (): string => {
|
||||
const _alteredBit = Math.round(Math.random());
|
||||
const _buildArray: Array<string> = HammingEncode(
|
||||
getRandomInt(Math.pow(2, 4), Math.pow(2, getRandomInt(1, 57))),
|
||||
).split("");
|
||||
if (_alteredBit) {
|
||||
const _randomIndex: number = getRandomInt(0, _buildArray.length - 1);
|
||||
_buildArray[_randomIndex] = _buildArray[_randomIndex] == "0" ? "1" : "0";
|
||||
}
|
||||
return _buildArray.join("");
|
||||
},
|
||||
solver: (data: string, ans: string): boolean => {
|
||||
return parseInt(ans, 10) === HammingDecode(data);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ import { calculateAchievements } from "./Achievements/Achievements";
|
||||
import React from "react";
|
||||
import { setupUncaughtPromiseHandler } from "./UncaughtPromiseHandler";
|
||||
import { Button, Typography } from "@mui/material";
|
||||
import { SnackbarEvents } from "./ui/React/Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "./ui/React/Snackbar";
|
||||
|
||||
const Engine: {
|
||||
_lastUpdate: number;
|
||||
@@ -495,7 +495,7 @@ function warnAutosaveDisabled(): void {
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
SnackbarEvents.emit(warningToast, "warning", 5000);
|
||||
SnackbarEvents.emit(warningToast, ToastVariant.WARNING, 5000);
|
||||
}
|
||||
|
||||
export { Engine };
|
||||
|
||||
@@ -57,8 +57,8 @@ export function RecentScriptAccordion(props: IProps): React.ReactElement {
|
||||
<ListItemText
|
||||
primary={
|
||||
<Typography>
|
||||
└ {recentScript.filename} (died{" "}
|
||||
{convertTimeMsToTimeElapsedString(new Date().getTime() - recentScript.timestamp.getTime())} ago)
|
||||
└ {recentScript.runningScript.filename} (died{" "}
|
||||
{convertTimeMsToTimeElapsedString(new Date().getTime() - recentScript.timeOfDeath.getTime())} ago)
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
@@ -78,7 +78,7 @@ export function RecentScriptAccordion(props: IProps): React.ReactElement {
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell className={classes.noborder} colSpan={2}>
|
||||
<Typography>└ Args: {arrayToString(recentScript.args)}</Typography>
|
||||
<Typography>└ Args: {arrayToString(recentScript.runningScript.args)}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
|
||||
@@ -13,7 +13,7 @@ export function RecentScriptsPage(): React.ReactElement {
|
||||
<>
|
||||
<Typography>List of all recently killed scripts.</Typography>
|
||||
{recentScripts.map((r) => (
|
||||
<RecentScriptAccordion key={r.pid} recentScript={r} />
|
||||
<RecentScriptAccordion key={r.runningScript.pid} recentScript={r} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
+361
-344
@@ -1,144 +1,80 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { numeralWrapper } from "./numeralFormat";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
import { getPurchaseServerLimit } from "../Server/ServerPurchases";
|
||||
import { HacknetServerConstants } from "../Hacknet/data/Constants";
|
||||
import { StatsTable } from "./React/StatsTable";
|
||||
import { Money } from "./React/Money";
|
||||
import { use } from "./Context";
|
||||
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
|
||||
import { Paper, Table, TableBody, Box, IconButton, Typography, Container, Tooltip } from "@mui/material";
|
||||
import { MoreHoriz, Info } from "@mui/icons-material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { BitNodes } from "../BitNode/BitNode";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { HacknetServerConstants } from "../Hacknet/data/Constants";
|
||||
import { getPurchaseServerLimit } from "../Server/ServerPurchases";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
import { use } from "./Context";
|
||||
import { numeralWrapper } from "./numeralFormat";
|
||||
import { Modal } from "./React/Modal";
|
||||
import { Money } from "./React/Money";
|
||||
import { StatsRow } from "./React/StatsRow";
|
||||
import { StatsTable } from "./React/StatsTable";
|
||||
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import { Table, TableCell } from "./React/Table";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
|
||||
function LastEmployer(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
if (player.companyName) {
|
||||
return <Typography>Employer at which you last worked: {player.companyName}</Typography>;
|
||||
}
|
||||
return <></>;
|
||||
interface EmployersModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function LastJob(): React.ReactElement {
|
||||
const EmployersModal = ({ open, onClose }: EmployersModalProps): React.ReactElement => {
|
||||
const player = use.Player();
|
||||
if (player.companyName !== "") {
|
||||
return <Typography>Job you last worked: {player.jobs[player.companyName]}</Typography>;
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
|
||||
function Employers(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
if (player.jobs && Object.keys(player.jobs).length !== 0)
|
||||
return (
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<>
|
||||
<Typography>All Employers:</Typography>
|
||||
|
||||
<Typography variant="h5">All Employers</Typography>
|
||||
<ul>
|
||||
{Object.keys(player.jobs).map((j) => (
|
||||
<Typography key={j}> * {j}</Typography>
|
||||
<Typography key={j}>* {j}</Typography>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
|
||||
function Hacknet(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
// Can't import HacknetHelpers for some reason.
|
||||
if (!(player.bitNodeN === 9 || SourceFileFlags[9] > 0)) {
|
||||
return (
|
||||
<>
|
||||
<Typography>{`Hacknet Nodes owned: ${player.hacknetNodes.length}`}</Typography>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Typography>{`Hacknet Servers owned: ${player.hacknetNodes.length} / ${HacknetServerConstants.MaxServers}`}</Typography>
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function Intelligence(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
if (player.intelligence > 0 && (player.bitNodeN === 5 || SourceFileFlags[5] > 0)) {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography>Intelligence: </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography>{numeralWrapper.formatSkill(player.intelligence)} </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatExp(player.intelligence_exp)} exp)</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
|
||||
function MultiplierTable(props: any): React.ReactElement {
|
||||
function bn5Stat(r: any): JSX.Element {
|
||||
if (SourceFileFlags[5] > 0 && r.length > 2 && r[1] != r[2]) {
|
||||
return (
|
||||
<TableCell key="2" align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatPercentage(r[2])})</Typography>
|
||||
</TableCell>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>
|
||||
{props.rows.map((r: any) => (
|
||||
<TableRow key={r[0]}>
|
||||
<TableCell key="0">
|
||||
<Typography noWrap>{`${r[0]} multiplier:`} </Typography>
|
||||
</TableCell>
|
||||
<TableCell key="1" align="right">
|
||||
<Typography noWrap>{numeralWrapper.formatPercentage(r[1])}</Typography>
|
||||
</TableCell>
|
||||
{bn5Stat(r)}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
interface MultTableProps {
|
||||
rows: (string | number)[][];
|
||||
color: string;
|
||||
noMargin?: boolean;
|
||||
}
|
||||
|
||||
function BladeburnerMults(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
if (!player.canAccessBladeburner()) return <></>;
|
||||
function MultiplierTable(props: MultTableProps): React.ReactElement {
|
||||
return (
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Bladeburner Success Chance", player.bladeburner_success_chance_mult],
|
||||
["Bladeburner Max Stamina", player.bladeburner_max_stamina_mult],
|
||||
["Bladeburner Stamina Gain", player.bladeburner_stamina_gain_mult],
|
||||
["Bladeburner Field Analysis", player.bladeburner_analysis_mult],
|
||||
]}
|
||||
/>
|
||||
<Table sx={{ display: "table", width: "100%", mb: (props.noMargin ?? false) === true ? 0 : 2 }}>
|
||||
<TableBody>
|
||||
{props.rows.map((data) => {
|
||||
const mult = data[0] as string,
|
||||
value = data[1] as number,
|
||||
modded = data[2] as number | null;
|
||||
|
||||
if (modded && modded !== value && SourceFileFlags[5] > 0) {
|
||||
return (
|
||||
<StatsRow key={mult} name={mult} color={props.color} data={{}}>
|
||||
<>
|
||||
<Typography color={props.color}>
|
||||
<span style={{ opacity: 0.5 }}>{numeralWrapper.formatPercentage(value)}</span>{" "}
|
||||
{numeralWrapper.formatPercentage(modded)}
|
||||
</Typography>
|
||||
</>
|
||||
</StatsRow>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<StatsRow
|
||||
key={mult}
|
||||
name={mult}
|
||||
color={props.color}
|
||||
data={{ content: numeralWrapper.formatPercentage(value) }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -146,15 +82,17 @@ function CurrentBitNode(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
if (player.sourceFiles.length > 0) {
|
||||
const index = "BitNode" + player.bitNodeN;
|
||||
const currentSourceFile = player.sourceFiles.find((sourceFile) => sourceFile.n == player.bitNodeN);
|
||||
const lvl = currentSourceFile ? currentSourceFile.lvl : 0;
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">
|
||||
BitNode {player.bitNodeN}: {BitNodes[index].name}
|
||||
</Typography>
|
||||
<Typography sx={{ mx: 2 }} style={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>
|
||||
{BitNodes[index].info}
|
||||
</Typography>
|
||||
</>
|
||||
<Box>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">
|
||||
BitNode {player.bitNodeN}: {BitNodes[index].name} (Level {lvl})
|
||||
</Typography>
|
||||
<Typography sx={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>{BitNodes[index].info}</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -262,6 +200,7 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
|
||||
export function CharacterStats(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const [moneyOpen, setMoneyOpen] = useState(false);
|
||||
const [employersOpen, setEmployersOpen] = useState(false);
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
@@ -273,229 +212,307 @@ export function CharacterStats(): React.ReactElement {
|
||||
}, []);
|
||||
|
||||
const timeRows = [
|
||||
["Time played since last Augmentation:", convertTimeMsToTimeElapsedString(player.playtimeSinceLastAug)],
|
||||
["Since last Augmentation installation", convertTimeMsToTimeElapsedString(player.playtimeSinceLastAug)],
|
||||
];
|
||||
if (player.sourceFiles.length > 0) {
|
||||
timeRows.push([
|
||||
"Time played since last Bitnode destroyed:",
|
||||
convertTimeMsToTimeElapsedString(player.playtimeSinceLastBitnode),
|
||||
]);
|
||||
timeRows.push(["Since last Bitnode destroyed", convertTimeMsToTimeElapsedString(player.playtimeSinceLastBitnode)]);
|
||||
}
|
||||
timeRows.push(["Total Time played:", convertTimeMsToTimeElapsedString(player.totalPlaytime)]);
|
||||
timeRows.push(["Total", convertTimeMsToTimeElapsedString(player.totalPlaytime)]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">General</Typography>
|
||||
<Box sx={{ mx: 2 }}>
|
||||
<Typography>Current City: {player.city}</Typography>
|
||||
<LastEmployer />
|
||||
<LastJob />
|
||||
<Employers />
|
||||
|
||||
<Typography>
|
||||
Money: <Money money={player.money} />
|
||||
<IconButton onClick={() => setMoneyOpen(true)}>
|
||||
<MoreHorizIcon color="info" />
|
||||
</IconButton>
|
||||
</Typography>
|
||||
</Box>
|
||||
<br />
|
||||
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
|
||||
<Typography variant="h4">Stats</Typography>
|
||||
<Box sx={{ mx: 2 }}>
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Hacking: </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>{numeralWrapper.formatSkill(player.hacking)} </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatExp(player.hacking_exp)} exp)</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Strength: </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>{numeralWrapper.formatSkill(player.strength)} </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatExp(player.strength_exp)} exp)</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Defense: </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>{numeralWrapper.formatSkill(player.defense)} </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatExp(player.defense_exp)} exp)</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Dexterity: </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>{numeralWrapper.formatSkill(player.dexterity)} </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatExp(player.dexterity_exp)} exp)</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Agility: </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>{numeralWrapper.formatSkill(player.agility)} </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatExp(player.agility_exp)} exp)</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Typography noWrap>Charisma: </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>{numeralWrapper.formatSkill(player.charisma)} </Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Typography noWrap>({numeralWrapper.formatExp(player.charisma_exp)} exp)</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<Intelligence />
|
||||
</TableBody>
|
||||
</Table>
|
||||
<br />
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", minWidth: "fit-content", mb: 1, gap: 1 }}>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">General</Typography>
|
||||
<Table>
|
||||
<TableBody>
|
||||
<StatsRow name="Current City" color={Settings.theme.primary} data={{ content: player.city }} />
|
||||
<StatsRow name="Money" color={Settings.theme.money} data={{}}>
|
||||
<>
|
||||
<Money money={player.money} />
|
||||
<IconButton onClick={() => setMoneyOpen(true)} sx={{ p: 0 }}>
|
||||
<MoreHoriz color="info" />
|
||||
</IconButton>
|
||||
</>
|
||||
</StatsRow>
|
||||
{player.companyName ? (
|
||||
<>
|
||||
<StatsRow
|
||||
name="Last Employer"
|
||||
color={Settings.theme.primary}
|
||||
data={{ content: player.companyName }}
|
||||
/>
|
||||
<StatsRow
|
||||
name="Last Job"
|
||||
color={Settings.theme.primary}
|
||||
data={{ content: player.jobs[player.companyName] }}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{player.jobs && Object.keys(player.jobs).length !== 0 ? (
|
||||
<StatsRow name="All Employers" color={Settings.theme.primary} data={{}}>
|
||||
<>
|
||||
<span style={{ color: Settings.theme.primary }}>{Object.keys(player.jobs).length} total</span>
|
||||
<IconButton onClick={() => setEmployersOpen(true)} sx={{ p: 0 }}>
|
||||
<MoreHoriz color="info" />
|
||||
</IconButton>
|
||||
</>
|
||||
</StatsRow>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<StatsRow
|
||||
name="Servers Owned"
|
||||
color={Settings.theme.primary}
|
||||
data={{ content: `${player.purchasedServers.length} / ${getPurchaseServerLimit()}` }}
|
||||
/>
|
||||
<StatsRow
|
||||
name={`Hacknet ${player.bitNodeN === 9 || SourceFileFlags[9] > 0 ? "Servers" : "Nodes"} owned`}
|
||||
color={Settings.theme.primary}
|
||||
data={{
|
||||
content: `${player.hacknetNodes.length}${
|
||||
player.bitNodeN === 9 || SourceFileFlags[9] > 0 ? ` / ${HacknetServerConstants.MaxServers}` : ""
|
||||
}`,
|
||||
}}
|
||||
/>
|
||||
<StatsRow
|
||||
name="Augmentations Installed"
|
||||
color={Settings.theme.primary}
|
||||
data={{ content: String(player.augmentations.length) }}
|
||||
/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">Skills</Typography>
|
||||
<Table>
|
||||
<TableBody>
|
||||
<StatsRow
|
||||
name="Hacking"
|
||||
color={Settings.theme.hack}
|
||||
data={{ level: player.hacking, exp: player.hacking_exp }}
|
||||
/>
|
||||
<StatsRow
|
||||
name="Strength"
|
||||
color={Settings.theme.combat}
|
||||
data={{ level: player.strength, exp: player.strength_exp }}
|
||||
/>
|
||||
<StatsRow
|
||||
name="Defense"
|
||||
color={Settings.theme.combat}
|
||||
data={{ level: player.defense, exp: player.defense_exp }}
|
||||
/>
|
||||
<StatsRow
|
||||
name="Dexterity"
|
||||
color={Settings.theme.combat}
|
||||
data={{ level: player.dexterity, exp: player.dexterity_exp }}
|
||||
/>
|
||||
<StatsRow
|
||||
name="Agility"
|
||||
color={Settings.theme.combat}
|
||||
data={{ level: player.agility, exp: player.agility_exp }}
|
||||
/>
|
||||
<StatsRow
|
||||
name="Charisma"
|
||||
color={Settings.theme.cha}
|
||||
data={{ level: player.charisma, exp: player.charisma_exp }}
|
||||
/>
|
||||
{player.intelligence > 0 && (player.bitNodeN === 5 || SourceFileFlags[5] > 0) && (
|
||||
<StatsRow
|
||||
name="Intelligence"
|
||||
color={Settings.theme.int}
|
||||
data={{ level: player.intelligence, exp: player.intelligence_exp }}
|
||||
/>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
</Box>
|
||||
<br />
|
||||
<Typography variant="h4">Multipliers</Typography>
|
||||
<Box sx={{ mx: 2 }}>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Hacking Chance", player.hacking_chance_mult],
|
||||
["Hacking Speed", player.hacking_speed_mult],
|
||||
[
|
||||
"Hacking Money",
|
||||
player.hacking_money_mult,
|
||||
player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney,
|
||||
],
|
||||
[
|
||||
"Hacking Growth",
|
||||
player.hacking_grow_mult,
|
||||
player.hacking_grow_mult * BitNodeMultipliers.ServerGrowthRate,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Hacking Level", player.hacking_mult, player.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier],
|
||||
["Hacking Experience", player.hacking_exp_mult, player.hacking_exp_mult * BitNodeMultipliers.HackExpGain],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
<Box sx={{ mb: 1 }}>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||
Multipliers
|
||||
{SourceFileFlags[5] > 0 && (
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Displays your current multipliers.
|
||||
<br />
|
||||
<br />
|
||||
When there is a dim number next to a multiplier, that means that the multiplier in question is being
|
||||
affected by BitNode multipliers.
|
||||
<br />
|
||||
<br />
|
||||
The dim number is the raw multiplier, and the undimmed number is the effective multiplier, as
|
||||
dictated by the BitNode.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Info sx={{ ml: 1, mb: 0.5 }} color="info" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 1 }}>
|
||||
<Box>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Hacking Chance", player.hacking_chance_mult],
|
||||
["Hacking Speed", player.hacking_speed_mult],
|
||||
[
|
||||
"Hacking Money",
|
||||
player.hacking_money_mult,
|
||||
player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney,
|
||||
],
|
||||
[
|
||||
"Hacking Growth",
|
||||
player.hacking_grow_mult,
|
||||
player.hacking_grow_mult * BitNodeMultipliers.ServerGrowthRate,
|
||||
],
|
||||
]}
|
||||
color={Settings.theme.hack}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Hacking Level",
|
||||
player.hacking_mult,
|
||||
player.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier,
|
||||
],
|
||||
[
|
||||
"Hacking Experience",
|
||||
player.hacking_exp_mult,
|
||||
player.hacking_exp_mult * BitNodeMultipliers.HackExpGain,
|
||||
],
|
||||
]}
|
||||
color={Settings.theme.hack}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Strength Level",
|
||||
player.strength_mult,
|
||||
player.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier,
|
||||
],
|
||||
["Strength Experience", player.strength_exp_mult],
|
||||
]}
|
||||
color={Settings.theme.combat}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Defense Level",
|
||||
player.defense_mult,
|
||||
player.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier,
|
||||
],
|
||||
["Defense Experience", player.defense_exp_mult],
|
||||
]}
|
||||
color={Settings.theme.combat}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Dexterity Level",
|
||||
player.dexterity_mult,
|
||||
player.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier,
|
||||
],
|
||||
["Dexterity Experience", player.dexterity_exp_mult],
|
||||
]}
|
||||
color={Settings.theme.combat}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Agility Level",
|
||||
player.agility_mult,
|
||||
player.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier,
|
||||
],
|
||||
["Agility Experience", player.agility_exp_mult],
|
||||
]}
|
||||
color={Settings.theme.combat}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Charisma Level",
|
||||
player.charisma_mult,
|
||||
player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
|
||||
],
|
||||
["Charisma Experience", player.charisma_exp_mult],
|
||||
]}
|
||||
color={Settings.theme.cha}
|
||||
noMargin
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Strength Level", player.strength_mult, player.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier],
|
||||
["Strength Experience", player.strength_exp_mult],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Defense Level", player.defense_mult, player.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier],
|
||||
["Defense Experience", player.defense_exp_mult],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Dexterity Level",
|
||||
player.dexterity_mult,
|
||||
player.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier,
|
||||
],
|
||||
["Dexterity Experience", player.dexterity_exp_mult],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Agility Level", player.agility_mult, player.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier],
|
||||
["Agility Experience", player.agility_exp_mult],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Charisma Level", player.charisma_mult, player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier],
|
||||
["Charisma Experience", player.charisma_exp_mult],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Hacknet Node production",
|
||||
player.hacknet_node_money_mult,
|
||||
player.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney,
|
||||
],
|
||||
["Hacknet Node purchase cost", player.hacknet_node_purchase_cost_mult],
|
||||
["Hacknet Node RAM upgrade cost", player.hacknet_node_ram_cost_mult],
|
||||
["Hacknet Node Core purchase cost", player.hacknet_node_core_cost_mult],
|
||||
["Hacknet Node level upgrade cost", player.hacknet_node_level_cost_mult],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Company reputation gain", player.company_rep_mult],
|
||||
[
|
||||
"Faction reputation gain",
|
||||
player.faction_rep_mult,
|
||||
player.faction_rep_mult * BitNodeMultipliers.FactionWorkRepGain,
|
||||
],
|
||||
["Salary", player.work_money_mult, player.work_money_mult * BitNodeMultipliers.CompanyWorkMoney],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Crime success", player.crime_success_mult],
|
||||
["Crime money", player.crime_money_mult, player.crime_money_mult * BitNodeMultipliers.CrimeMoney],
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
<BladeburnerMults />
|
||||
<Box>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
[
|
||||
"Hacknet Node production",
|
||||
player.hacknet_node_money_mult,
|
||||
player.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney,
|
||||
],
|
||||
["Hacknet Node purchase cost", player.hacknet_node_purchase_cost_mult],
|
||||
["Hacknet Node RAM upgrade cost", player.hacknet_node_ram_cost_mult],
|
||||
["Hacknet Node Core purchase cost", player.hacknet_node_core_cost_mult],
|
||||
["Hacknet Node level upgrade cost", player.hacknet_node_level_cost_mult],
|
||||
]}
|
||||
color={Settings.theme.primary}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Company reputation gain", player.company_rep_mult],
|
||||
[
|
||||
"Faction reputation gain",
|
||||
player.faction_rep_mult,
|
||||
player.faction_rep_mult * BitNodeMultipliers.FactionWorkRepGain,
|
||||
],
|
||||
["Salary", player.work_money_mult, player.work_money_mult * BitNodeMultipliers.CompanyWorkMoney],
|
||||
]}
|
||||
color={Settings.theme.money}
|
||||
/>
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Crime success", player.crime_success_mult],
|
||||
["Crime money", player.crime_money_mult, player.crime_money_mult * BitNodeMultipliers.CrimeMoney],
|
||||
]}
|
||||
color={Settings.theme.combat}
|
||||
/>
|
||||
{player.canAccessBladeburner() && (
|
||||
<MultiplierTable
|
||||
rows={[
|
||||
["Bladeburner Success Chance", player.bladeburner_success_chance_mult],
|
||||
["Bladeburner Max Stamina", player.bladeburner_max_stamina_mult],
|
||||
["Bladeburner Stamina Gain", player.bladeburner_stamina_gain_mult],
|
||||
["Bladeburner Field Analysis", player.bladeburner_analysis_mult],
|
||||
]}
|
||||
color={Settings.theme.primary}
|
||||
noMargin
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
<br />
|
||||
|
||||
<Typography variant="h4">Misc</Typography>
|
||||
<Box sx={{ mx: 2 }}>
|
||||
<Typography>{`Servers owned: ${player.purchasedServers.length} / ${getPurchaseServerLimit()}`}</Typography>
|
||||
<Hacknet />
|
||||
<Typography>{`Augmentations installed: ${player.augmentations.length}`}</Typography>
|
||||
<StatsTable rows={timeRows} />
|
||||
<Box sx={{ mb: 1 }}>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography variant="h5">Time Played</Typography>
|
||||
<Table>
|
||||
<TableBody>
|
||||
{timeRows.map(([name, content]) => (
|
||||
<StatsRow key={name} name={name} color={Settings.theme.primary} data={{ content: content }} />
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
</Box>
|
||||
<br />
|
||||
<CurrentBitNode />
|
||||
<MoneyModal open={moneyOpen} onClose={() => setMoneyOpen(false)} />
|
||||
</>
|
||||
<EmployersModal open={employersOpen} onClose={() => setEmployersOpen(false)} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
+3
-4
@@ -22,7 +22,7 @@ import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/Buyin
|
||||
import {
|
||||
cancelOrder,
|
||||
eventEmitterForUiReset,
|
||||
initStockMarketFnForReact,
|
||||
initStockMarketFn,
|
||||
placeOrder,
|
||||
StockMarket,
|
||||
} from "../StockMarket/StockMarket";
|
||||
@@ -310,7 +310,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
|
||||
function softReset(): void {
|
||||
dialogBoxCreate("Soft Reset!");
|
||||
prestigeAugmentation();
|
||||
installAugmentations(true);
|
||||
resetErrorBoundary();
|
||||
Router.toTerminal();
|
||||
}
|
||||
@@ -445,7 +445,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
buyStockShort={shortStock}
|
||||
cancelOrder={cancelOrder}
|
||||
eventEmitterForReset={eventEmitterForUiReset}
|
||||
initStockMarket={initStockMarketFnForReact}
|
||||
initStockMarket={initStockMarketFn}
|
||||
p={player}
|
||||
placeOrder={placeOrder}
|
||||
sellStockLong={sellStock}
|
||||
@@ -491,7 +491,6 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
}}
|
||||
installAugmentationsFn={() => {
|
||||
installAugmentations();
|
||||
Router.toTerminal();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ import PaletteIcon from "@mui/icons-material/Palette";
|
||||
import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal";
|
||||
import { ConfirmationModal } from "./ConfirmationModal";
|
||||
|
||||
import { SnackbarEvents } from "./Snackbar";
|
||||
import { SnackbarEvents, ToastVariant } from "./Snackbar";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { DeleteGameButton } from "./DeleteGameButton";
|
||||
@@ -64,6 +64,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
const importInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [execTime, setExecTime] = useState(Settings.CodeInstructionRunTime);
|
||||
const [recentScriptsSize, setRecentScriptsSize] = useState(Settings.MaxRecentScriptsCapacity);
|
||||
const [logSize, setLogSize] = useState(Settings.MaxLogCapacity);
|
||||
const [portSize, setPortSize] = useState(Settings.MaxPortCapacity);
|
||||
const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity);
|
||||
@@ -79,6 +80,11 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
Settings.CodeInstructionRunTime = newValue as number;
|
||||
}
|
||||
|
||||
function handleRecentScriptsSizeChange(event: any, newValue: number | number[]): void {
|
||||
setRecentScriptsSize(newValue as number);
|
||||
Settings.MaxRecentScriptsCapacity = newValue as number;
|
||||
}
|
||||
|
||||
function handleLogSizeChange(event: any, newValue: number | number[]): void {
|
||||
setLogSize(newValue as number);
|
||||
Settings.MaxLogCapacity = newValue as number;
|
||||
@@ -123,7 +129,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
setImportData(data);
|
||||
setImportSaveOpen(true);
|
||||
} catch (ex: any) {
|
||||
SnackbarEvents.emit(ex.toString(), "error", 5000);
|
||||
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +139,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
try {
|
||||
await saveObject.importGame(importData.base64);
|
||||
} catch (ex: any) {
|
||||
SnackbarEvents.emit(ex.toString(), "error", 5000);
|
||||
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000);
|
||||
}
|
||||
|
||||
setImportSaveOpen(false);
|
||||
@@ -176,6 +182,24 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
max={100}
|
||||
valueLabelDisplay="auto"
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The maximum number of recently killed script entries being tracked. Setting this too high can
|
||||
cause the game to use a lot of memory.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Recently killed scripts size</Typography>
|
||||
</Tooltip>
|
||||
<Slider
|
||||
value={recentScriptsSize}
|
||||
onChange={handleRecentScriptsSizeChange}
|
||||
step={25}
|
||||
min={0}
|
||||
max={500}
|
||||
valueLabelDisplay="auto"
|
||||
/>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
|
||||
@@ -10,6 +10,13 @@ interface IProps {
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
}
|
||||
|
||||
export enum ToastVariant {
|
||||
SUCCESS = "success",
|
||||
WARNING = "warning",
|
||||
ERROR = "error",
|
||||
INFO = "info",
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
snackbar: {
|
||||
// Log popup z-index increments, so let's add a padding to be well above them.
|
||||
@@ -36,9 +43,7 @@ export function SnackbarProvider(props: IProps): React.ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
export const SnackbarEvents = new EventEmitter<
|
||||
[string | React.ReactNode, "success" | "warning" | "error" | "info", number]
|
||||
>();
|
||||
export const SnackbarEvents = new EventEmitter<[string | React.ReactNode, ToastVariant, number]>();
|
||||
|
||||
export function Snackbar(): React.ReactElement {
|
||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
||||
|
||||
@@ -17,9 +17,10 @@ interface IProps {
|
||||
color: string;
|
||||
classes?: any;
|
||||
data: ITableRowData;
|
||||
children?: React.ReactElement;
|
||||
}
|
||||
|
||||
export const StatsRow = ({ name, color, classes = useStyles(), data }: IProps): React.ReactElement => {
|
||||
export const StatsRow = ({ name, color, classes = useStyles(), children, data }: IProps): React.ReactElement => {
|
||||
let content;
|
||||
|
||||
if (data.content !== undefined) {
|
||||
@@ -36,7 +37,8 @@ export const StatsRow = ({ name, color, classes = useStyles(), data }: IProps):
|
||||
<Typography style={{ color: color }}>{name}</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right" classes={{ root: classes.cellNone }}>
|
||||
<Typography style={{ color: color }}>{content}</Typography>
|
||||
{content ? <Typography style={{ color: color }}>{content}</Typography> : <></>}
|
||||
{children}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@@ -504,7 +504,7 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
||||
<Grid item>
|
||||
<Typography>
|
||||
You are currently working on crafting {player.graftAugmentationName}.
|
||||
You are currently working on grafting {player.graftAugmentationName}.
|
||||
<br />
|
||||
<br />
|
||||
You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}
|
||||
@@ -519,7 +519,7 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button sx={{ mx: 2 }} onClick={cancel}>
|
||||
Cancel work on crafting Augmentation
|
||||
Cancel work on grafting Augmentation
|
||||
</Button>
|
||||
<Button onClick={unfocus}>Do something else simultaneously</Button>
|
||||
</Grid>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// by Discord: H3draut3r#6722, feel free to ask me any questions. i probably don't know the answer 🤣
|
||||
export function HammingEncode(value: number): string {
|
||||
// encoding following Hammings rule
|
||||
function HammingSumOfParity(_lengthOfDBits: number): number {
|
||||
// will calculate the needed amount of parityBits 'without' the "overall"-Parity (that math took me 4 Days to get it working)
|
||||
return _lengthOfDBits < 3 || _lengthOfDBits == 0 // oh and of course using ternary operators, it's a pretty neat function
|
||||
? _lengthOfDBits == 0
|
||||
? 0
|
||||
: _lengthOfDBits + 1
|
||||
: // the following math will only work, if the length is greater equal 3, otherwise it's "kind of" broken :D
|
||||
Math.ceil(Math.log2(_lengthOfDBits * 2)) <=
|
||||
Math.ceil(Math.log2(1 + _lengthOfDBits + Math.ceil(Math.log2(_lengthOfDBits))))
|
||||
? Math.ceil(Math.log2(_lengthOfDBits) + 1)
|
||||
: Math.ceil(Math.log2(_lengthOfDBits));
|
||||
}
|
||||
const _data = value.toString(2).split(""); // first, change into binary string, then create array with 1 bit per index
|
||||
const _sumParity: number = HammingSumOfParity(_data.length); // get the sum of needed parity bits (for later use in encoding)
|
||||
const count = (arr: Array<string>, val: string): number =>
|
||||
arr.reduce((a: number, v: string) => (v === val ? a + 1 : a), 0);
|
||||
// function count for specific entries in the array, for later use
|
||||
|
||||
const _build = ["x", "x", ..._data.splice(0, 1)]; // init the "pre-build"
|
||||
for (let i = 2; i < _sumParity; i++) {
|
||||
// add new paritybits and the corresponding data bits (pre-building array)
|
||||
_build.push("x", ..._data.splice(0, Math.pow(2, i) - 1));
|
||||
}
|
||||
// now the "calculation"... get the paritybits ('x') working
|
||||
for (const index of _build.reduce(function (a: Array<number>, e: string, i: number) {
|
||||
if (e == "x") a.push(i);
|
||||
return a;
|
||||
}, [])) {
|
||||
// that reduce will result in an array of index numbers where the "x" is placed
|
||||
const _tempcount = index + 1; // set the "stepsize" for the parityBit
|
||||
const _temparray = []; // temporary array to store the extracted bits
|
||||
const _tempdata = [..._build]; // only work with a copy of the _build
|
||||
while (_tempdata[index] !== undefined) {
|
||||
// as long as there are bits on the starting index, do "cut"
|
||||
const _temp: Array<string> = _tempdata.splice(index, _tempcount * 2); // cut stepsize*2 bits, then...
|
||||
_temparray.push(..._temp.splice(0, _tempcount)); // ... cut the result again and keep the first half
|
||||
}
|
||||
_temparray.splice(0, 1); // remove first bit, which is the parity one
|
||||
_build[index] = (count(_temparray, "1") % 2).toString(); // count with remainder of 2 and"toString" to store the parityBit
|
||||
} // parity done, now the "overall"-parity is set
|
||||
_build.unshift((count(_build, "1") % 2).toString()); // has to be done as last element
|
||||
return _build.join(""); // return the _build as string
|
||||
}
|
||||
|
||||
export function HammingDecode(_data: string): number {
|
||||
//check for altered bit and decode
|
||||
const _build = _data.split(""); // ye, an array for working, again
|
||||
const _testArray = []; //for the "truthtable". if any is false, the data has an altered bit, will check for and fix it
|
||||
const _sumParity = Math.ceil(Math.log2(_data.length)); // sum of parity for later use
|
||||
const count = (arr: Array<string>, val: string): number =>
|
||||
arr.reduce((a: number, v: string) => (v === val ? a + 1 : a), 0);
|
||||
// the count.... again ;)
|
||||
|
||||
let _overallParity = _build.splice(0, 1).join(""); // store first index, for checking in next step and fix the _build properly later on
|
||||
_testArray.push(_overallParity == (count(_build, "1") % 2).toString() ? true : false); // first check with the overall parity bit
|
||||
for (let i = 0; i < _sumParity; i++) {
|
||||
// for the rest of the remaining parity bits we also "check"
|
||||
const _tempIndex = Math.pow(2, i) - 1; // get the parityBits Index
|
||||
const _tempStep = _tempIndex + 1; // set the stepsize
|
||||
const _tempData = [..._build]; // get a "copy" of the build-data for working
|
||||
const _tempArray = []; // init empty array for "testing"
|
||||
while (_tempData[_tempIndex] != undefined) {
|
||||
// extract from the copied data until the "starting" index is undefined
|
||||
const _temp = [..._tempData.splice(_tempIndex, _tempStep * 2)]; // extract 2*stepsize
|
||||
_tempArray.push(..._temp.splice(0, _tempStep)); // and cut again for keeping first half
|
||||
}
|
||||
const _tempParity = _tempArray.shift(); // and again save the first index separated for checking with the rest of the data
|
||||
_testArray.push(_tempParity == (count(_tempArray, "1") % 2).toString() ? true : false);
|
||||
// is the _tempParity the calculated data? push answer into the 'truthtable'
|
||||
}
|
||||
let _fixIndex = 0; // init the "fixing" index and start with 0
|
||||
for (let i = 1; i < _sumParity + 1; i++) {
|
||||
// simple binary adding for every boolean in the _testArray, starting from 2nd index of it
|
||||
_fixIndex += _testArray[i] ? 0 : Math.pow(2, i) / 2;
|
||||
}
|
||||
_build.unshift(_overallParity); // now we need the "overall" parity back in it's place
|
||||
// try fix the actual encoded binary string if there is an error
|
||||
if (_fixIndex > 0 && _testArray[0] == false) {
|
||||
// if the overall is false and the sum of calculated values is greater equal 0, fix the corresponding hamming-bit
|
||||
_build[_fixIndex] = _build[_fixIndex] == "0" ? "1" : "0";
|
||||
} else if (_testArray[0] == false) {
|
||||
// otherwise, if the the overall_parity is the only wrong, fix that one
|
||||
_overallParity = _overallParity == "0" ? "1" : "0";
|
||||
} else if (_testArray[0] == true && _testArray.some((truth) => truth == false)) {
|
||||
return 0; // uhm, there's some strange going on... 2 bits are altered? How? This should not happen 👀
|
||||
}
|
||||
// oof.. halfway through... we fixed an possible altered bit, now "extract" the parity-bits from the _build
|
||||
for (let i = _sumParity; i >= 0; i--) {
|
||||
// start from the last parity down the 2nd index one
|
||||
_build.splice(Math.pow(2, i), 1);
|
||||
}
|
||||
_build.splice(0, 1); // remove the overall parity bit and we have our binary value
|
||||
return parseInt(_build.join(""), 2); // parse the integer with redux 2 and we're done!
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/** Binary heap. */
|
||||
abstract class BinHeap<T> {
|
||||
/**
|
||||
* Heap data array consisting of [weight, payload] pairs, arranged by weight
|
||||
* to satisfy heap condition.
|
||||
*
|
||||
* Encodes the binary tree by storing tree root at index 0 and
|
||||
* left child of element i at `i * 2 + 1` and
|
||||
* right child of element i at `i * 2 + 2`.
|
||||
*/
|
||||
protected data: [number, T][];
|
||||
|
||||
constructor() {
|
||||
this.data = [];
|
||||
}
|
||||
|
||||
/** Get number of elements in the heap. */
|
||||
public get size(): number {
|
||||
return this.data.length;
|
||||
}
|
||||
|
||||
/** Add a new element to the heap. */
|
||||
public push(value: T, weight: number): void {
|
||||
const i = this.data.length;
|
||||
this.data[i] = [weight, value];
|
||||
this.heapifyUp(i);
|
||||
}
|
||||
|
||||
/** Get the value of the root-most element of the heap, without changing the heap. */
|
||||
public peek(): T | undefined {
|
||||
if (this.data.length == 0) return undefined;
|
||||
|
||||
return this.data[0][1];
|
||||
}
|
||||
|
||||
/** Remove the root-most element of the heap and return the removed element's value. */
|
||||
public pop(): T | undefined {
|
||||
if (this.data.length == 0) return undefined;
|
||||
|
||||
const value = this.data[0][1];
|
||||
|
||||
this.data[0] = this.data[this.data.length - 1];
|
||||
this.data.length = this.data.length - 1;
|
||||
|
||||
this.heapifyDown(0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Change the weight of an element in the heap. */
|
||||
public changeWeight(predicate: (value: T) => boolean, weight: number): void {
|
||||
// Find first element with matching value, if any
|
||||
const i = this.data.findIndex((e) => predicate(e[1]));
|
||||
if (i == -1) return;
|
||||
|
||||
// Update that element's weight
|
||||
this.data[i][0] = weight;
|
||||
|
||||
// And re-heapify if needed
|
||||
const p = Math.floor((i - 1) / 2);
|
||||
|
||||
if (!this.heapOrderABeforeB(this.data[p][0], this.data[i][0]))
|
||||
// Needs to shift root-wards?
|
||||
this.heapifyUp(i);
|
||||
// Try shifting deeper
|
||||
else this.heapifyDown(i);
|
||||
}
|
||||
|
||||
/** Restore heap condition, starting at index i and traveling towards root. */
|
||||
protected heapifyUp(i: number): void {
|
||||
// Swap the new element up towards root until it reaches root position or
|
||||
// settles under under a suitable parent
|
||||
while (i > 0) {
|
||||
const p = Math.floor((i - 1) / 2);
|
||||
|
||||
// Reached heap-ordered state already?
|
||||
if (this.heapOrderABeforeB(this.data[p][0], this.data[i][0])) break;
|
||||
|
||||
// Swap
|
||||
const tmp = this.data[p];
|
||||
this.data[p] = this.data[i];
|
||||
this.data[i] = tmp;
|
||||
|
||||
// And repeat at parent index
|
||||
i = p;
|
||||
}
|
||||
}
|
||||
|
||||
/** Restore heap condition, starting at index i and traveling away from root. */
|
||||
protected heapifyDown(i: number): void {
|
||||
// Swap the shifted element down in the heap until it either reaches the
|
||||
// bottom layer or is in correct order relative to it's children
|
||||
while (i < this.data.length) {
|
||||
const l = i * 2 + 1;
|
||||
const r = i * 2 + 2;
|
||||
let toSwap = i;
|
||||
|
||||
// Find which one of element i and it's children should be closest to root
|
||||
if (l < this.data.length && this.heapOrderABeforeB(this.data[l][0], this.data[toSwap][0])) toSwap = l;
|
||||
if (r < this.data.length && this.heapOrderABeforeB(this.data[r][0], this.data[toSwap][0])) toSwap = r;
|
||||
|
||||
// Already in order?
|
||||
if (i == toSwap) break;
|
||||
|
||||
// Not in order. Swap child that should be closest to root up to 'i' and repeat
|
||||
const tmp = this.data[toSwap];
|
||||
this.data[toSwap] = this.data[i];
|
||||
this.data[i] = tmp;
|
||||
|
||||
i = toSwap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should element with weight `weightA` be closer to root than element with
|
||||
* weight `weightB`?
|
||||
*/
|
||||
protected abstract heapOrderABeforeB(weightA: number, weightB: number): boolean;
|
||||
}
|
||||
|
||||
/** Binary max-heap. */
|
||||
export class MaxHeap<T> extends BinHeap<T> {
|
||||
heapOrderABeforeB(weightA: number, weightB: number): boolean {
|
||||
return weightA > weightB;
|
||||
}
|
||||
}
|
||||
|
||||
/** Binary min-heap. */
|
||||
export class MinHeap<T> extends BinHeap<T> {
|
||||
heapOrderABeforeB(weightA: number, weightB: number): boolean {
|
||||
return weightA < weightB;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export function checkEnum<T extends string, TEnumValue extends string>(
|
||||
enumVariable: { [key in T]: TEnumValue },
|
||||
value: string,
|
||||
): value is TEnumValue {
|
||||
return Object.values(enumVariable).includes(value);
|
||||
}
|
||||
Reference in New Issue
Block a user