Merge branch 'dev' into wrap-api

This commit is contained in:
hydroflame 2022-05-19 01:37:06 -04:00 committed by GitHub
commit b80e2d5fd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1101 additions and 1036 deletions

4
dist/main.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

42
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11
doc/NEW_BN_GUIDELINE.md Normal file

@ -0,0 +1,11 @@
Promote:
- New mechanic
- Coding problems based on NP problems. This makes solution that are easy to implement inefficient and solutions that are hard to implement efficent. (eg. Stanek)
- inter-mechanic synergy
- Simplicity (eg. Stanek, Hashnet. bad example: Corp)
Avoid:
- Failure conditions, it's very frustrating to revert several days worth of progress.
- Making existing mechanic harder. This makes it hard to port the content to other BNs.

1
doc/POTENTIAL_BN_1.md Normal file

@ -0,0 +1 @@
Sleeves meet Screeps (That's all I got)

3
doc/POTENTIAL_BN_2.md Normal file

@ -0,0 +1,3 @@
A game of risk from the point of view of a politician.
You allocate resources on a world map, trying to win elections.

@ -1 +0,0 @@
I want the wiki here https://bitburner.fandom.com/wiki/Bitburner_Wiki taken down please.

@ -631,6 +631,7 @@ export class Augmentation {
augmentationReference.baseCost *
getGenericAugmentationPriceMultiplier() *
BitNodeMultipliers.AugmentationMoneyCost;
repCost = augmentationReference.baseRepRequirement * BitNodeMultipliers.AugmentationRepCost;
}
return { moneyCost, repCost };
}

@ -45,13 +45,13 @@ function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactEle
);
}
type MultiplierListItemData = [
multiplier: string,
currentValue: number,
augmentedValue: number,
bitNodeMultiplier: number,
color: string,
];
interface MultiplierListItemData {
mult: string;
current: number;
augmented: number;
bnMult?: number;
color?: string;
}
interface IMultiplierListProps {
rows: MultiplierListItemData[];
@ -60,23 +60,23 @@ interface IMultiplierListProps {
function MultiplierList(props: IMultiplierListProps): React.ReactElement {
const listItems = props.rows
.map((data) => {
const [multiplier, currentValue, augmentedValue, bitNodeMultiplier, color] = data;
const { mult, current, augmented, bnMult = 1, color = Settings.theme.primary } = data;
if (!isNaN(augmentedValue)) {
if (!isNaN(augmented)) {
return (
<ListItem key={multiplier} disableGutters sx={{ py: 0 }}>
<ListItem key={mult} disableGutters sx={{ py: 0 }}>
<ListItemText
sx={{ my: 0.1 }}
primary={
<Typography color={color}>
<b>{multiplier}</b>
<b>{mult}</b>
</Typography>
}
secondary={
<span style={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
<BitNodeModifiedStats base={currentValue} mult={bitNodeMultiplier} color={color} />
<BitNodeModifiedStats base={current} mult={bnMult} color={color} />
<DoubleArrow fontSize="small" color="success" sx={{ mb: 0.5, mx: 1 }} />
<BitNodeModifiedStats base={augmentedValue} mult={bitNodeMultiplier} color={Settings.theme.success} />
<BitNodeModifiedStats base={augmented} mult={bnMult} color={Settings.theme.success} />
</span>
}
disableTypography
@ -94,177 +94,205 @@ function MultiplierList(props: IMultiplierListProps): React.ReactElement {
export function PlayerMultipliers(): React.ReactElement {
const mults = calculateAugmentedStats();
// 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])),
{
mult: "Hacking Chance",
current: Player.hacking_chance_mult,
augmented: Player.hacking_chance_mult * mults.hacking_chance_mult,
},
{
mult: "Hacking Speed",
current: Player.hacking_speed_mult,
augmented: Player.hacking_speed_mult * mults.hacking_speed_mult,
},
{
mult: "Hacking Money",
current: Player.hacking_money_mult,
augmented: Player.hacking_money_mult * mults.hacking_money_mult,
bnMult: BitNodeMultipliers.ScriptHackMoney,
},
{
mult: "Hacking Growth",
current: Player.hacking_grow_mult,
augmented: Player.hacking_grow_mult * mults.hacking_grow_mult,
},
{
mult: "Hacking Level",
current: Player.hacking_mult,
augmented: Player.hacking_mult * mults.hacking_mult,
bnMult: BitNodeMultipliers.HackingLevelMultiplier,
},
{
mult: "Hacking Experience",
current: Player.hacking_exp_mult,
augmented: Player.hacking_exp_mult * mults.hacking_exp_mult,
bnMult: BitNodeMultipliers.HackExpGain,
},
].map((data: MultiplierListItemData) =>
Object.defineProperty(data, "color", {
value: 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,
],
{
mult: "Strength Level",
current: Player.strength_mult,
augmented: Player.strength_mult * mults.strength_mult,
bnMult: BitNodeMultipliers.StrengthLevelMultiplier,
},
{
mult: "Strength Experience",
current: Player.strength_exp_mult,
augmented: Player.strength_exp_mult * mults.strength_exp_mult,
},
{
mult: "Defense Level",
current: Player.defense_mult,
augmented: Player.defense_mult * mults.defense_mult,
bnMult: BitNodeMultipliers.DefenseLevelMultiplier,
},
{
mult: "Defense Experience",
current: Player.defense_exp_mult,
augmented: Player.defense_exp_mult * mults.defense_exp_mult,
},
{
mult: "Dexterity Level",
current: Player.dexterity_mult,
augmented: Player.dexterity_mult * mults.dexterity_mult,
bnMult: BitNodeMultipliers.DexterityLevelMultiplier,
},
{
mult: "Dexterity Experience",
current: Player.dexterity_exp_mult,
augmented: Player.dexterity_exp_mult * mults.dexterity_exp_mult,
},
{
mult: "Agility Level",
current: Player.agility_mult,
augmented: Player.agility_mult * mults.agility_mult,
bnMult: BitNodeMultipliers.AgilityLevelMultiplier,
},
{
mult: "Agility Experience",
current: Player.agility_exp_mult,
augmented: Player.agility_exp_mult * mults.agility_exp_mult,
},
].map((data: MultiplierListItemData) =>
Object.defineProperty(data, "color", {
value: Settings.theme.combat,
}),
),
{
mult: "Charisma Level",
current: Player.charisma_mult,
augmented: Player.charisma_mult * mults.charisma_mult,
bnMult: BitNodeMultipliers.CharismaLevelMultiplier,
color: Settings.theme.cha,
},
{
mult: "Charisma Experience",
current: Player.charisma_exp_mult,
augmented: Player.charisma_exp_mult * mults.charisma_exp_mult,
color: 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,
],
{
mult: "Hacknet Node Production",
current: Player.hacknet_node_money_mult,
augmented: Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
bnMult: BitNodeMultipliers.HacknetNodeMoney,
},
{
mult: "Hacknet Node Purchase Cost",
current: Player.hacknet_node_purchase_cost_mult,
augmented: Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
},
{
mult: "Hacknet Node RAM Upgrade Cost",
current: Player.hacknet_node_ram_cost_mult,
augmented: Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
},
{
mult: "Hacknet Node Core Purchase Cost",
current: Player.hacknet_node_core_cost_mult,
augmented: Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
},
{
mult: "Hacknet Node Level Upgrade Cost",
current: Player.hacknet_node_level_cost_mult,
augmented: Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
},
{
mult: "Company Reputation Gain",
current: Player.company_rep_mult,
augmented: Player.company_rep_mult * mults.company_rep_mult,
},
{
mult: "Faction Reputation Gain",
current: Player.faction_rep_mult,
augmented: Player.faction_rep_mult * mults.faction_rep_mult,
bnMult: BitNodeMultipliers.FactionWorkRepGain,
},
{
mult: "Salary",
current: Player.work_money_mult,
augmented: Player.work_money_mult * mults.work_money_mult,
bnMult: BitNodeMultipliers.CompanyWorkMoney,
color: Settings.theme.money,
},
{
mult: "Crime Success Chance",
current: Player.crime_success_mult,
augmented: Player.crime_success_mult * mults.crime_success_mult,
color: Settings.theme.combat,
},
{
mult: "Crime Money",
current: Player.crime_money_mult,
augmented: Player.crime_money_mult * mults.crime_money_mult,
bnMult: BitNodeMultipliers.CrimeMoney,
color: 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])),
{
mult: "Bladeburner Success Chance",
current: Player.bladeburner_success_chance_mult,
augmented: Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
},
{
mult: "Bladeburner Max Stamina",
current: Player.bladeburner_max_stamina_mult,
augmented: Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
},
{
mult: "Bladeburner Stamina Gain",
current: Player.bladeburner_stamina_gain_mult,
augmented: Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
},
{
mult: "Bladeburner Field Analysis",
current: Player.bladeburner_analysis_mult,
augmented: Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
},
);
}
const hasLeftImprovements = +!!(leftColData.filter((item) => item[2] !== 0).length > 0),
hasRightImprovements = +!!(rightColData.filter((item) => item[2] !== 0).length > 0);
return (
<Paper
sx={{
p: 1,
maxHeight: 400,
overflowY: "scroll",
display: "grid",
gridTemplateColumns: `repeat(${hasLeftImprovements + hasRightImprovements}, 1fr)`,
display: "flex",
flexDirection: "column",
flexWrap: "wrap",
gap: 1,
}}
>
<MultiplierList rows={leftColData} />

@ -488,7 +488,7 @@ export const defaultMultipliers: IBitNodeMultipliers = {
FourSigmaMarketDataApiCost: 1,
CorporationValuation: 1,
CorporationSoftCap: 1,
CorporationSoftcap: 1,
BladeburnerRank: 1,
BladeburnerSkillCost: 1,
@ -504,6 +504,8 @@ export const defaultMultipliers: IBitNodeMultipliers = {
WorldDaemonDifficulty: 1,
};
Object.freeze(defaultMultipliers);
export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultipliers {
const mults = Object.assign({}, defaultMultipliers);
switch (n) {
@ -523,7 +525,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftPowerMultiplier: 2,
StaneksGiftExtraSize: -6,
PurchasedServerSoftcap: 1.3,
CorporationSoftCap: 0.9,
CorporationSoftcap: 0.9,
WorldDaemonDifficulty: 5,
});
}
@ -609,7 +611,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftPowerMultiplier: 0.5,
StaneksGiftExtraSize: 2,
GangSoftcap: 0.7,
CorporationSoftCap: 0.9,
CorporationSoftcap: 0.9,
WorldDaemonDifficulty: 2,
GangUniqueAugs: 0.2,
});
@ -637,7 +639,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftPowerMultiplier: 0.9,
StaneksGiftExtraSize: -1,
GangSoftcap: 0.7,
CorporationSoftCap: 0.9,
CorporationSoftcap: 0.9,
WorldDaemonDifficulty: 2,
GangUniqueAugs: 0.2,
});
@ -657,7 +659,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftExtraSize: -99,
PurchasedServerSoftcap: 4,
GangSoftcap: 0,
CorporationSoftCap: 0,
CorporationSoftcap: 0,
GangUniqueAugs: 0,
});
}
@ -685,7 +687,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftPowerMultiplier: 0.5,
StaneksGiftExtraSize: 2,
GangSoftcap: 0.8,
CorporationSoftCap: 0.7,
CorporationSoftcap: 0.7,
WorldDaemonDifficulty: 2,
GangUniqueAugs: 0.25,
});
@ -717,7 +719,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftExtraSize: -3,
PurchasedServerSoftcap: 1.1,
GangSoftcap: 0.9,
CorporationSoftCap: 0.9,
CorporationSoftcap: 0.9,
WorldDaemonDifficulty: 2,
GangUniqueAugs: 0.25,
});
@ -741,7 +743,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
FourSigmaMarketDataCost: 4,
FourSigmaMarketDataApiCost: 4,
PurchasedServerSoftcap: 2,
CorporationSoftCap: 0.9,
CorporationSoftcap: 0.9,
WorldDaemonDifficulty: 1.5,
GangUniqueAugs: 0.75,
});
@ -809,7 +811,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftPowerMultiplier: inc,
StaneksGiftExtraSize: inc,
GangSoftcap: 0.8,
CorporationSoftCap: 0.8,
CorporationSoftcap: 0.8,
WorldDaemonDifficulty: inc,
GangUniqueAugs: dec,
@ -854,7 +856,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
StaneksGiftPowerMultiplier: 2,
StaneksGiftExtraSize: 1,
GangSoftcap: 0.3,
CorporationSoftCap: 0.3,
CorporationSoftcap: 0.3,
WorldDaemonDifficulty: 3,
GangUniqueAugs: 0.1,
});

@ -242,7 +242,7 @@ export interface IBitNodeMultipliers {
/**
* Influences corporation dividends.
*/
CorporationSoftCap: number;
CorporationSoftcap: number;
// Index signature
[key: string]: number;
@ -252,4 +252,4 @@ export interface IBitNodeMultipliers {
* The multipliers that are influenced by current Bitnode progression.
*/
// tslint:disable-next-line:variable-name
export const BitNodeMultipliers = defaultMultipliers;
export const BitNodeMultipliers = Object.assign({}, defaultMultipliers);

@ -1,554 +1,335 @@
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
import { Box, Collapse, ListItemButton, ListItemText, Paper, Typography } from "@mui/material";
import ExpandMore from "@mui/icons-material/ExpandMore";
import { Box, Collapse, ListItemButton, ListItemText, Paper, Table, TableBody, Typography } from "@mui/material";
import { uniqueId } from "lodash";
import React from "react";
import { SpecialServers } from "../../Server/data/SpecialServers";
import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { StatsRow } from "../../ui/React/StatsRow";
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
import { IBitNodeMultipliers } from "../BitNodeMultipliers";
import { SpecialServers } from "../../Server/data/SpecialServers";
interface IProps {
n: number;
level?: number;
}
export function BitnodeMultiplierDescription({ n }: IProps): React.ReactElement {
const player = use.Player();
export function BitnodeMultiplierDescription({ n, level }: IProps): React.ReactElement {
const [open, setOpen] = React.useState(false);
const mults = getBitNodeMultipliers(n, player.sourceFileLvl(n));
if (n === 1) return <></>;
return (
<>
<br />
<Box component={Paper}>
<ListItemButton onClick={() => setOpen((old) => !old)}>
<ListItemText primary={<Typography>Bitnode multipliers:</Typography>} />
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Box mx={2}>
<Collapse in={open}>
<GeneralMults n={n} mults={mults} />
<FactionMults n={n} mults={mults} />
<AugmentationMults n={n} mults={mults} />
<StockMults n={n} mults={mults} />
<SkillMults n={n} mults={mults} />
<HackingMults n={n} mults={mults} />
<PurchasedServersMults n={n} mults={mults} />
<CrimeMults n={n} mults={mults} />
<InfiltrationMults n={n} mults={mults} />
<CompanyMults n={n} mults={mults} />
<GangMults n={n} mults={mults} />
<CorporationMults n={n} mults={mults} />
<BladeburnerMults n={n} mults={mults} />
<StanekMults n={n} mults={mults} />
<br />
</Collapse>
</Box>
</Box>
</>
<Box component={Paper} sx={{ mt: 1, p: 1 }}>
<ListItemButton disableGutters onClick={() => setOpen((old) => !old)}>
<ListItemText primary={<Typography variant="h6">Bitnode Multipliers</Typography>} />
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open}>
<BitNodeMultipliersDisplay n={n} level={level} />
</Collapse>
</Box>
);
}
export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElement => {
const player = use.Player();
// If a level argument has been provided, use that as the multiplier level
// If not, then we have to assume that we want the next level up from the
// current node's source file, so we get the min of that, the SF's max level,
// or if it's BN12, ∞
const maxSfLevel = n === 12 ? Infinity : 3;
const mults = getBitNodeMultipliers(n, level ?? Math.min(player.sourceFileLvl(n) + 1, maxSfLevel));
return (
<Box sx={{ columnCount: 2, columnGap: 1, mb: -2 }}>
<GeneralMults n={n} mults={mults} />
<SkillMults n={n} mults={mults} />
<FactionMults n={n} mults={mults} />
<AugmentationMults n={n} mults={mults} />
<HackingMults n={n} mults={mults} />
<PurchasedServersMults n={n} mults={mults} />
<StockMults n={n} mults={mults} />
<CrimeMults n={n} mults={mults} />
<InfiltrationMults n={n} mults={mults} />
<CompanyMults n={n} mults={mults} />
<GangMults n={n} mults={mults} />
<CorporationMults n={n} mults={mults} />
<BladeburnerMults n={n} mults={mults} />
<StanekMults n={n} mults={mults} />
</Box>
);
};
interface IBNMultRows {
[mult: string]: {
name: string;
content?: string;
color?: string;
};
}
interface IBNMultTableProps {
sectionName: string;
rowData: IBNMultRows;
mults: IBitNodeMultipliers;
}
const BNMultTable = (props: IBNMultTableProps): React.ReactElement => {
const rowsArray = Object.entries(props.rowData)
.filter(([key, _value]) => props.mults[key] !== defaultMultipliers[key])
.map(([key, value]) => (
<StatsRow
key={uniqueId()}
name={value.name}
data={{ content: value.content ?? `${(props.mults[key] * 100).toFixed(3)}%` }}
color={value.color ?? Settings.theme.primary}
/>
));
return rowsArray.length > 0 ? (
<span style={{ display: "inline-block", width: "100%", marginBottom: "16px" }}>
<Typography variant="h6">{props.sectionName}</Typography>
<Table>
<TableBody>{rowsArray}</TableBody>
</Table>
</span>
) : (
<></>
);
};
interface IMultsProps {
n: number;
mults: IBitNodeMultipliers;
}
function GeneralMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.ClassGymExpGain === defaultMultipliers.ClassGymExpGain &&
mults.CodingContractMoney === defaultMultipliers.CodingContractMoney &&
mults.DaedalusAugsRequirement === defaultMultipliers.DaedalusAugsRequirement &&
mults.WorldDaemonDifficulty === defaultMultipliers.WorldDaemonDifficulty &&
mults.HacknetNodeMoney === defaultMultipliers.HacknetNodeMoney
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>General:</Typography>
<Box mx={1}>
{mults.WorldDaemonDifficulty !== defaultMultipliers.WorldDaemonDifficulty ? (
<Typography>
{SpecialServers.WorldDaemon} difficulty: x{mults.WorldDaemonDifficulty.toFixed(3)}
</Typography>
) : (
<></>
)}
{mults.DaedalusAugsRequirement !== defaultMultipliers.DaedalusAugsRequirement ? (
<Typography>Daedalus aug req.: {mults.DaedalusAugsRequirement}</Typography>
) : (
<></>
)}
{mults.HacknetNodeMoney !== defaultMultipliers.HacknetNodeMoney ? (
<Typography>Hacknet production: x{mults.HacknetNodeMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.CodingContractMoney !== defaultMultipliers.CodingContractMoney ? (
<Typography>Coding contract reward: x{mults.CodingContractMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ClassGymExpGain !== defaultMultipliers.ClassGymExpGain ? (
<Typography>Class/Gym exp: x{mults.ClassGymExpGain.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
WorldDaemonDifficulty: { name: `${SpecialServers.WorldDaemon} Difficulty` },
DaedalusAugsRequirement: {
name: "Daedalus Augs Requirement",
content: String(mults.DaedalusAugsRequirement),
},
HacknetNodeMoney: { name: "Hacknet Production" },
CodingContractMoney: { name: "Coding Contract Reward" },
ClassGymExpGain: { name: "Class/Gym Exp" },
};
return <BNMultTable sectionName="General" rowData={rows} mults={mults} />;
}
function AugmentationMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.AugmentationMoneyCost === defaultMultipliers.AugmentationMoneyCost &&
mults.AugmentationRepCost === defaultMultipliers.AugmentationRepCost
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Augmentations:</Typography>
<Box mx={1}>
{mults.AugmentationMoneyCost !== defaultMultipliers.AugmentationMoneyCost ? (
<Typography>Cost: x{mults.AugmentationMoneyCost.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.AugmentationRepCost !== defaultMultipliers.AugmentationRepCost ? (
<Typography>Reputation: x{mults.AugmentationRepCost.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
AugmentationMoneyCost: { name: "Money Cost" },
AugmentationRepCost: {
name: "Reputation Cost",
color: Settings.theme.rep,
},
};
return <BNMultTable sectionName="Augmentations" rowData={rows} mults={mults} />;
}
function CompanyMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.CompanyWorkExpGain === defaultMultipliers.CompanyWorkExpGain &&
mults.CompanyWorkMoney === defaultMultipliers.CompanyWorkMoney
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Company:</Typography>
<Box mx={1}>
{mults.CompanyWorkMoney !== defaultMultipliers.CompanyWorkMoney ? (
<Typography>Money: x{mults.CompanyWorkMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.CompanyWorkExpGain !== defaultMultipliers.CompanyWorkExpGain ? (
<Typography>Exp: x{mults.CompanyWorkExpGain.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
CompanyWorkMoney: {
name: "Work Money",
color: Settings.theme.money,
},
CompanyWorkExpGain: { name: "Work Exp" },
};
return <BNMultTable sectionName="Company" rowData={rows} mults={mults} />;
}
function StockMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.FourSigmaMarketDataApiCost === defaultMultipliers.FourSigmaMarketDataApiCost &&
mults.FourSigmaMarketDataCost === defaultMultipliers.FourSigmaMarketDataCost
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Stock market:</Typography>
<Box mx={1}>
{mults.FourSigmaMarketDataCost !== defaultMultipliers.FourSigmaMarketDataCost ? (
<Typography>Market data cost: x{mults.FourSigmaMarketDataCost.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.FourSigmaMarketDataApiCost !== defaultMultipliers.FourSigmaMarketDataApiCost ? (
<Typography>Market data API cost: x{mults.FourSigmaMarketDataApiCost.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
FourSigmaMarketDataCost: { name: "Market Data Cost" },
FourSigmaMarketDataApiCost: { name: "Market Data API Cost" },
};
return <BNMultTable sectionName="Stock Market" rowData={rows} mults={mults} />;
}
function FactionMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.FactionPassiveRepGain === defaultMultipliers.FactionPassiveRepGain &&
mults.FactionWorkExpGain === defaultMultipliers.FactionWorkExpGain &&
mults.FactionWorkRepGain === defaultMultipliers.FactionWorkRepGain &&
mults.RepToDonateToFaction === defaultMultipliers.RepToDonateToFaction
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Faction:</Typography>
<Box mx={1}>
{mults.RepToDonateToFaction !== defaultMultipliers.RepToDonateToFaction ? (
<Typography>Favor to donate: x{mults.RepToDonateToFaction.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.FactionWorkRepGain !== defaultMultipliers.FactionWorkRepGain ? (
<Typography>Work rep: x{mults.FactionWorkRepGain.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.FactionWorkExpGain !== defaultMultipliers.FactionWorkExpGain ? (
<Typography>Work exp: x{mults.FactionWorkExpGain.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.FactionPassiveRepGain !== defaultMultipliers.FactionPassiveRepGain ? (
<Typography>Passive rep: x{mults.FactionPassiveRepGain.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
RepToDonateToFaction: { name: "Favor to Donate" },
FactionWorkRepGain: {
name: "Work Reputation",
color: Settings.theme.rep,
},
FactionWorkExpGain: { name: "Work Exp" },
FactionPassiveRepGain: {
name: "Passive Rep",
color: Settings.theme.rep,
},
};
return <BNMultTable sectionName="Faction" rowData={rows} mults={mults} />;
}
function CrimeMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (mults.CrimeExpGain === defaultMultipliers.CrimeExpGain && mults.CrimeMoney === defaultMultipliers.CrimeMoney)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Crime:</Typography>
<Box mx={1}>
{mults.CrimeExpGain !== defaultMultipliers.CrimeExpGain ? (
<Typography>Exp: x{mults.CrimeExpGain.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.CrimeMoney !== defaultMultipliers.CrimeMoney ? (
<Typography>Money: x{mults.CrimeMoney.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
CrimeExpGain: {
name: "Crime Exp",
color: Settings.theme.combat,
},
CrimeMoney: {
name: "Crime Money",
color: Settings.theme.combat,
},
};
return <BNMultTable sectionName="Crime" rowData={rows} mults={mults} />;
}
function SkillMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.HackingLevelMultiplier === defaultMultipliers.HackingLevelMultiplier &&
mults.AgilityLevelMultiplier === defaultMultipliers.AgilityLevelMultiplier &&
mults.DefenseLevelMultiplier === defaultMultipliers.DefenseLevelMultiplier &&
mults.DexterityLevelMultiplier === defaultMultipliers.DexterityLevelMultiplier &&
mults.StrengthLevelMultiplier === defaultMultipliers.StrengthLevelMultiplier &&
mults.CharismaLevelMultiplier === defaultMultipliers.CharismaLevelMultiplier
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Skills:</Typography>
<Box mx={1}>
{mults.HackingLevelMultiplier !== defaultMultipliers.HackingLevelMultiplier ? (
<Typography>Hacking: x{mults.HackingLevelMultiplier.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.AgilityLevelMultiplier !== defaultMultipliers.AgilityLevelMultiplier ? (
<Typography>Agility: x{mults.AgilityLevelMultiplier.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.DefenseLevelMultiplier !== defaultMultipliers.DefenseLevelMultiplier ? (
<Typography>Defense: x{mults.DefenseLevelMultiplier.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.DexterityLevelMultiplier !== defaultMultipliers.DexterityLevelMultiplier ? (
<Typography>Dexterity: x{mults.DexterityLevelMultiplier.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.StrengthLevelMultiplier !== defaultMultipliers.StrengthLevelMultiplier ? (
<Typography>Strength: x{mults.StrengthLevelMultiplier.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.CharismaLevelMultiplier !== defaultMultipliers.CharismaLevelMultiplier ? (
<Typography>Charisma: x{mults.CharismaLevelMultiplier.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
HackingLevelMultiplier: {
name: "Hacking Level",
color: Settings.theme.hack,
},
StrengthLevelMultiplier: {
name: "Strength Level",
color: Settings.theme.combat,
},
DefenseLevelMultiplier: {
name: "Defense Level",
color: Settings.theme.combat,
},
DexterityLevelMultiplier: {
name: "Dexterity Level",
color: Settings.theme.combat,
},
AgilityLevelMultiplier: {
name: "Agility Level",
color: Settings.theme.combat,
},
CharismaLevelMultiplier: {
name: "Charisma Level",
color: Settings.theme.cha,
},
};
return <BNMultTable sectionName="Skills" rowData={rows} mults={mults} />;
}
function HackingMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.ServerGrowthRate === defaultMultipliers.ServerGrowthRate &&
mults.ServerMaxMoney === defaultMultipliers.ServerMaxMoney &&
mults.ServerStartingMoney === defaultMultipliers.ServerStartingMoney &&
mults.ServerStartingSecurity === defaultMultipliers.ServerStartingSecurity &&
mults.ServerWeakenRate === defaultMultipliers.ServerWeakenRate &&
mults.ManualHackMoney === defaultMultipliers.ManualHackMoney &&
mults.ScriptHackMoney === defaultMultipliers.ScriptHackMoney &&
mults.ScriptHackMoneyGain === defaultMultipliers.ScriptHackMoneyGain &&
mults.HackExpGain === defaultMultipliers.HackExpGain
)
return <></>;
const rows: IBNMultRows = {
HackExpGain: {
name: "Hacking Exp",
color: Settings.theme.hack,
},
ServerGrowthRate: { name: "Server Growth Rate" },
ServerMaxMoney: { name: "Server Max Money" },
ServerStartingMoney: { name: "Server Starting Money" },
ServerStartingSecurity: { name: "Server Starting Security" },
ServerWeakenRate: { name: "Server Weaken Rate" },
ManualHackMoney: {
name: "Manual Hack Money",
color: Settings.theme.money,
},
ScriptHackMoney: {
name: "Script Hack Money",
color: Settings.theme.money,
},
ScriptHackMoneyGain: {
name: "Money Gained From Hack",
color: Settings.theme.money,
},
};
return (
<>
<br />
<Typography variant={"h5"}>Hacking:</Typography>
<Box mx={1}>
{mults.HackExpGain !== defaultMultipliers.HackExpGain ? (
<Typography>Exp: x{mults.HackExpGain.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ServerGrowthRate !== defaultMultipliers.ServerGrowthRate ? (
<Typography>Growth rate: x{mults.ServerGrowthRate.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ServerMaxMoney !== defaultMultipliers.ServerMaxMoney ? (
<Typography>Max money: x{mults.ServerMaxMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ServerStartingMoney !== defaultMultipliers.ServerStartingMoney ? (
<Typography>Starting money: x{mults.ServerStartingMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ServerStartingSecurity !== defaultMultipliers.ServerStartingSecurity ? (
<Typography>Starting security: x{mults.ServerStartingSecurity.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ServerWeakenRate !== defaultMultipliers.ServerWeakenRate ? (
<Typography>Weaken rate: x{mults.ServerWeakenRate.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ManualHackMoney !== defaultMultipliers.ManualHackMoney ? (
<Typography>Manual hack money: x{mults.ManualHackMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ScriptHackMoney !== defaultMultipliers.ScriptHackMoney ? (
<Typography>Hack money stolen: x{mults.ScriptHackMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.ScriptHackMoneyGain !== defaultMultipliers.ScriptHackMoneyGain ? (
<Typography>Money gained from hack: x{mults.ScriptHackMoneyGain.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
return <BNMultTable sectionName="Hacking" rowData={rows} mults={mults} />;
}
function PurchasedServersMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.PurchasedServerCost === defaultMultipliers.PurchasedServerCost &&
mults.PurchasedServerSoftcap === defaultMultipliers.PurchasedServerSoftcap &&
mults.PurchasedServerLimit === defaultMultipliers.PurchasedServerLimit &&
mults.PurchasedServerMaxRam === defaultMultipliers.PurchasedServerMaxRam &&
mults.HomeComputerRamCost === defaultMultipliers.HomeComputerRamCost
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Purchased servers:</Typography>
<Box mx={1}>
{mults.PurchasedServerCost !== defaultMultipliers.PurchasedServerCost ? (
<Typography>Base cost: {mults.PurchasedServerCost.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.PurchasedServerSoftcap !== defaultMultipliers.PurchasedServerSoftcap ? (
<Typography>Softcap cost: {mults.PurchasedServerSoftcap.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.PurchasedServerLimit !== defaultMultipliers.PurchasedServerLimit ? (
<Typography>Limit: x{mults.PurchasedServerLimit.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.PurchasedServerMaxRam !== defaultMultipliers.PurchasedServerMaxRam ? (
<Typography>Max ram: x{mults.PurchasedServerMaxRam.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.HomeComputerRamCost !== defaultMultipliers.HomeComputerRamCost ? (
<Typography>Home ram cost: x{mults.HomeComputerRamCost.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
PurchasedServerCost: {
name: "Base Cost",
content: mults.PurchasedServerCost.toFixed(3),
},
PurchasedServerSoftcap: {
name: "Softcap Cost",
content: mults.PurchasedServerSoftcap.toFixed(3),
},
PurchasedServerLimit: { name: "Server Limit" },
PurchasedServerMaxRam: { name: "Max RAM" },
HomeComputerRamCost: { name: "Home RAM Cost" },
};
return <BNMultTable sectionName="Purchased Servers" rowData={rows} mults={mults} />;
}
function InfiltrationMults({ mults }: IMultsProps): React.ReactElement {
// is it empty check
if (
mults.InfiltrationMoney === defaultMultipliers.InfiltrationMoney &&
mults.InfiltrationRep === defaultMultipliers.InfiltrationRep
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Infiltration:</Typography>
<Box mx={1}>
{mults.InfiltrationMoney !== defaultMultipliers.InfiltrationMoney ? (
<Typography>Money: {mults.InfiltrationMoney.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.InfiltrationRep !== defaultMultipliers.InfiltrationRep ? (
<Typography>Reputation: x{mults.InfiltrationRep.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
InfiltrationMoney: {
name: "Infiltration Money",
color: Settings.theme.money,
},
InfiltrationRep: {
name: "Infiltration Reputation",
color: Settings.theme.rep,
},
};
return <BNMultTable sectionName="Infiltration" rowData={rows} mults={mults} />;
}
function BladeburnerMults({ n, mults }: IMultsProps): React.ReactElement {
function BladeburnerMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
// access check
if (n !== 6 && n !== 7 && player.sourceFileLvl(6) === 0) return <></>;
//default mults check
if (mults.BladeburnerRank === 1 && mults.BladeburnerSkillCost === 1) return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Bladeburner:</Typography>
<Box mx={1}>
{mults.BladeburnerRank !== 1 ? <Typography>Rank gain: x{mults.BladeburnerRank.toFixed(3)}</Typography> : <></>}
{mults.BladeburnerSkillCost !== 1 ? (
<Typography>Skill cost: x{mults.BladeburnerSkillCost.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
if (!player.canAccessBladeburner()) return <></>;
const rows: IBNMultRows = {
BladeburnerRank: { name: "Rank Gain" },
BladeburnerSkillCost: { name: "Skill Cost" },
};
return <BNMultTable sectionName="Bladeburner" rowData={rows} mults={mults} />;
}
function StanekMults({ n, mults }: IMultsProps): React.ReactElement {
function StanekMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
// access check
if (n !== 13 && player.sourceFileLvl(13) === 0) return <></>;
//default mults check
if (
mults.StaneksGiftExtraSize === defaultMultipliers.StaneksGiftExtraSize &&
mults.StaneksGiftPowerMultiplier === defaultMultipliers.StaneksGiftPowerMultiplier
)
return <></>;
if (!player.canAccessCotMG()) return <></>;
const s = mults.StaneksGiftExtraSize;
return (
<>
<br />
<Typography variant={"h5"}>Stanek's Gift:</Typography>
<Box mx={1}>
{mults.StaneksGiftPowerMultiplier !== defaultMultipliers.StaneksGiftPowerMultiplier ? (
<Typography>Gift power: x{mults.StaneksGiftPowerMultiplier.toFixed(3)}</Typography>
) : (
<></>
)}
{s !== defaultMultipliers.StaneksGiftExtraSize ? (
<Typography>Base size modifier: {s > defaultMultipliers.StaneksGiftExtraSize ? `+${s}` : s}</Typography>
) : (
<></>
)}
</Box>
</>
);
const extraSize = mults.StaneksGiftExtraSize.toFixed(3);
const rows: IBNMultRows = {
StnakesGiftPowerMultiplier: { name: "Gift Power" },
StaneksGiftExtraSize: {
name: "Base Size Modifier",
content: `${mults.StaneksGiftExtraSize > defaultMultipliers.StaneksGiftExtraSize ? `+${extraSize}` : extraSize}`,
},
};
return <BNMultTable sectionName="Stanek's Gift" rowData={rows} mults={mults} />;
}
function GangMults({ n, mults }: IMultsProps): React.ReactElement {
function GangMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
// access check
if (n !== 2 && player.sourceFileLvl(2) === 0) return <></>;
// is it empty check
if (
mults.GangSoftcap === defaultMultipliers.GangSoftcap &&
mults.GangUniqueAugs === defaultMultipliers.GangUniqueAugs
)
return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Gang:</Typography>
<Box mx={1}>
{mults.GangSoftcap !== defaultMultipliers.GangSoftcap ? (
<Typography>Softcap: {mults.GangSoftcap.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.GangUniqueAugs !== defaultMultipliers.GangUniqueAugs ? (
<Typography>Unique augs: x{mults.GangUniqueAugs.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
if (player.bitNodeN !== 2 && player.sourceFileLvl(2) <= 0) return <></>;
const rows: IBNMultRows = {
GangSoftcap: {
name: "Gang Softcap",
content: mults.GangSoftcap.toFixed(3),
},
GangUniqueAugs: { name: "Unique Augmentations" },
};
return <BNMultTable sectionName="Gang" rowData={rows} mults={mults} />;
}
function CorporationMults({ n, mults }: IMultsProps): React.ReactElement {
function CorporationMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
// access check
if (n !== 3 && player.sourceFileLvl(3) === 0) return <></>;
// is it empty check
if (
mults.CorporationSoftCap === defaultMultipliers.CorporationSoftCap &&
mults.CorporationValuation === defaultMultipliers.CorporationValuation
)
return <></>;
if (!player.canAccessCorporation()) return <></>;
return (
<>
<br />
<Typography variant={"h5"}>Corporation:</Typography>
<Box mx={1}>
{mults.CorporationSoftCap !== defaultMultipliers.CorporationSoftCap ? (
<Typography>Softcap: {mults.CorporationSoftCap.toFixed(3)}</Typography>
) : (
<></>
)}
{mults.CorporationValuation !== defaultMultipliers.CorporationValuation ? (
<Typography>Valuation: x{mults.CorporationValuation.toFixed(3)}</Typography>
) : (
<></>
)}
</Box>
</>
);
const rows: IBNMultRows = {
CorporationSoftcap: {
name: "Corporation Softcap",
content: mults.CorporationSoftcap.toFixed(3),
},
CorporationValuation: { name: "Valuation" },
};
return <BNMultTable sectionName="Corporation" rowData={rows} mults={mults} />;
}

@ -41,7 +41,7 @@ export function PortalModal(props: IProps): React.ReactElement {
<br />
<br />
<Typography>{bitNode.info}</Typography>
<BitnodeMultiplierDescription n={props.n} />
<BitnodeMultiplierDescription n={props.n} level={newLevel} />
<br />
<br />
<Button

@ -697,7 +697,7 @@ export class Bladeburner implements IBladeburner {
// Set variables
if (args.length === 4) {
const variable = args[1];
const variable = args[1].toLowerCase(); // allows Action Type to be with or without capitalisation.
const val = args[2];
let highLow = false; // True for high, false for low
@ -1919,7 +1919,7 @@ export class Bladeburner implements IBladeburner {
}
// If the Player starts doing some other actions, set action to idle and alert
if (player.hasAugmentation(AugmentationNames.BladesSimulacrum) === false && player.isWorking) {
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.isWorking) {
if (this.action.type !== ActionTypes["Idle"]) {
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) {

@ -84,6 +84,7 @@ export const CONSTANTS: {
SoARepMult: number;
EntropyEffect: number;
TotalNumBitNodes: number;
InfiniteLoopLimit: number;
Donations: number; // number of blood/plasma/palette donation the dev have verified., boosts NFG
LatestUpdate: string;
} = {
@ -226,6 +227,8 @@ export const CONSTANTS: {
// BitNode/Source-File related stuff
TotalNumBitNodes: 24,
InfiniteLoopLimit: 1000,
Donations: 7,
LatestUpdate: `

@ -159,7 +159,7 @@ export class Corporation {
if (this.unlockUpgrades[6] === 1) {
upgrades += 0.1;
}
return Math.pow(dividends, BitNodeMultipliers.CorporationSoftCap + upgrades);
return Math.pow(dividends, BitNodeMultipliers.CorporationSoftcap + upgrades);
}
determineValuation(): number {

@ -68,13 +68,7 @@ export class ActiveFragment {
}
copy(): ActiveFragment {
// We have to do a round trip because the constructor.
const fragment = FragmentById(this.id);
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
const c = new ActiveFragment({ x: this.x, y: this.y, rotation: this.rotation, fragment: fragment });
c.highestCharge = this.highestCharge;
c.numCharge = this.numCharge;
return c;
return Object.assign({}, this);
}
/**

@ -76,13 +76,7 @@ export class Fragment {
}
copy(): Fragment {
return new Fragment(
this.id,
this.shape.map((a) => a.slice()),
this.type,
this.power,
this.limit,
);
return Object.assign({}, this);
}
}

@ -136,8 +136,9 @@ export class StaneksGift implements IStaneksGift {
}
updateMults(p: IPlayer): void {
p.reapplyAllAugmentations(true);
p.reapplyAllSourceFiles();
// applyEntropy also reapplies all augmentations and source files
// This wraps up the reset nicely
p.applyEntropy(p.entropy);
for (const aFrag of this.fragments) {
const fragment = aFrag.fragment();

@ -70,7 +70,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
<>
<OptionsSlider
label=".script exec time (ms)"
value={execTime}
initialValue={execTime}
callback={handleExecTimeChange}
step={1}
min={5}
@ -84,7 +84,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
/>
<OptionsSlider
label="Recently killed scripts size"
value={recentScriptsSize}
initialValue={recentScriptsSize}
callback={handleRecentScriptsSizeChange}
step={25}
min={0}
@ -98,7 +98,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
/>
<OptionsSlider
label="Netscript log size"
value={logSize}
initialValue={logSize}
callback={handleLogSizeChange}
step={20}
min={20}
@ -112,7 +112,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
/>
<OptionsSlider
label="Netscript port size"
value={portSize}
initialValue={portSize}
callback={handlePortSizeChange}
step={1}
min={20}
@ -126,7 +126,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
/>
<OptionsSlider
label="Terminal capacity"
value={terminalSize}
initialValue={terminalSize}
callback={handleTerminalSizeChange}
step={50}
min={50}
@ -141,7 +141,7 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
/>
<OptionsSlider
label="Autosave interval (s)"
value={autosaveInterval}
initialValue={autosaveInterval}
callback={handleAutosaveIntervalChange}
step={30}
min={0}
@ -179,6 +179,12 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
</>
}
/>
<OptionSwitch
checked={Settings.InfinityLoopSafety}
onChange={(newValue) => (Settings.InfinityLoopSafety = newValue)}
text="Script infinite loop safety net"
tooltip={<>If this is set the game will attempt to automatically kill scripts stuck in infinite loops.</>}
/>
</GameOptionsPage>
),
[GameOptionsTab.INTERFACE]: (

@ -1,8 +1,8 @@
import { Slider, Tooltip, Typography, Box } from "@mui/material";
import React from "react";
import React, { useState } from "react";
interface IProps {
value: any;
initialValue: any;
callback: (event: any, newValue: number | number[]) => void;
step: number;
min: number;
@ -13,14 +13,21 @@ interface IProps {
}
export const OptionsSlider = (props: IProps): React.ReactElement => {
const [value, setValue] = useState(props.initialValue);
const onChange = (_evt: Event, newValue: number | Array<number>): void => {
setValue(newValue);
};
return (
<Box>
<Tooltip title={<Typography>{props.tooltip}</Typography>}>
<Typography>{props.label}</Typography>
</Tooltip>
<Slider
value={props.value}
onChange={props.callback}
value={value}
onChange={onChange}
onChangeCommitted={props.callback}
step={props.step}
min={props.min}
max={props.max}

@ -85,9 +85,15 @@ export function TerritorySubpage(): React.ReactElement {
</Typography>
</Box>
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)" }}>
{gangNames.map((name) => (
<OtherGangTerritory key={name} name={name} />
))}
{gangNames
.sort((a, b) => {
if (AllGangs[a].territory <= 0 && AllGangs[b].territory > 0) return 1;
if (AllGangs[a].territory > 0 && AllGangs[b].territory <= 0) return -1;
return 0;
})
.map((name) => (
<OtherGangTerritory key={name} name={name} />
))}
</Box>
<TerritoryInfoModal open={infoOpen} onClose={() => setInfoOpen(false)} />
</Container>
@ -114,14 +120,16 @@ function OtherGangTerritory(props: ITerritoryProps): React.ReactElement {
const playerPower = AllGangs[gang.facName].power;
const power = AllGangs[props.name].power;
const clashVictoryChance = playerPower / (power + playerPower);
const territory = AllGangs[props.name].territory;
const opacity = territory ? 1 : 0.75;
return (
<Box component={Paper} sx={{ p: 1 }}>
<Box component={Paper} sx={{ p: 1, opacity }}>
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
{props.name}
</Typography>
<Typography>
<b>Power:</b> {formatNumber(power, 3)} <br />
<b>Territory:</b> {formatTerritory(AllGangs[props.name].territory)}% <br />
<b>Territory:</b> {formatTerritory(territory)}% <br />
<b>Clash Win Chance:</b> {numeralWrapper.formatPercentage(clashVictoryChance, 3)}
</Typography>
</Box>

@ -36,7 +36,7 @@ export function calculateTradeInformationRepReward(
30 *
levelBonus *
(player.hasAugmentation(AugmentationNames.WKSharmonizer, true) ? 1.5 : 1) *
BitNodeMultipliers.InfiltrationMoney
BitNodeMultipliers.InfiltrationRep
);
}

@ -37,9 +37,13 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
const [guess, setGuess] = useState("");
const hasAugment = Player.hasAugmentation(AugmentationNames.ChaosOfDionysus, true);
function ignorableKeyboardEvent(event: KeyboardEvent): boolean {
return event.key === KEY.BACKSPACE || (event.shiftKey && event.key === "Shift") || event.ctrlKey || event.altKey;
}
function press(this: Document, event: KeyboardEvent): void {
event.preventDefault();
if (event.key === KEY.BACKSPACE) return;
if (ignorableKeyboardEvent(event)) return;
const nextGuess = guess + event.key.toUpperCase();
if (!answer.startsWith(nextGuess)) props.onFailure();
else if (answer === nextGuess) props.onSuccess();

@ -317,7 +317,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
return renderGrafting();
}
case LocationName.Sector12CityHall: {
return (BitNodeMultipliers.CorporationSoftCap < 0.15 && <></>) || <CreateCorporation />;
return (BitNodeMultipliers.CorporationSoftcap < 0.15 && <></>) || <CreateCorporation />;
}
case LocationName.Sector12NSA: {
return renderBladeburner();

@ -5,6 +5,8 @@ import type { WorkerScript } from "./WorkerScript";
import { makeRuntimeRejectMsg } from "../NetscriptEvaluator";
import { Player } from "../Player";
import { CityName } from "src/Locations/data/CityNames";
import { Settings } from "../Settings/Settings";
import { CONSTANTS } from "../Constants";
type ExternalFunction = (...args: any[]) => any;
type ExternalAPI = {
@ -91,8 +93,14 @@ function wrapFunction(
getValidPort: (port: any) => helpers.getValidPort(functionPath, port),
},
};
const safetyEnabled = Settings.InfinityLoopSafety;
function wrappedFunction(...args: unknown[]): unknown {
helpers.updateDynamicRam(ctx.function, getRamCost(Player, ...tree, ctx.function));
if (safetyEnabled) workerScript.infiniteLoopSafetyCounter++;
if (workerScript.infiniteLoopSafetyCounter > CONSTANTS.InfiniteLoopLimit)
throw new Error(
`Infinite loop without sleep detected. ${CONSTANTS.InfiniteLoopLimit} ns functions were called without sleep. This will cause your UI to hang.`,
);
return func(ctx)(...args);
}
const parent = getNestedProperty(wrappedAPI, ...tree);

@ -51,6 +51,7 @@ export const RamCostConstants: IMap<number> = {
ScriptCodingContractBaseRamCost: 10,
ScriptSleeveBaseRamCost: 4,
ScriptGetOwnedSourceFiles: 5,
ScriptClearTerminalCost: 0.2,
ScriptSingularityFn1RamCost: 2,
ScriptSingularityFn2RamCost: 3,
@ -358,6 +359,7 @@ export const RamCosts: IMap<any> = {
enableLog: 0,
isLogEnabled: 0,
getScriptLogs: 0,
clearTerminal: RamCostConstants.ScriptClearTerminalCost,
nuke: RamCostConstants.ScriptPortProgramRamCost,
brutessh: RamCostConstants.ScriptPortProgramRamCost,
ftpcrack: RamCostConstants.ScriptPortProgramRamCost,

@ -111,6 +111,11 @@ export class WorkerScript {
*/
atExit: any;
/**
* Once this counter reaches it's limit the script crashes. It is reset when a promise completes.
*/
infiniteLoopSafetyCounter = 0;
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => any) {
this.name = runningScriptObj.filename;
this.hostname = runningScriptObj.server;

@ -14,6 +14,7 @@ export function netscriptDelay(time: number, workerScript: WorkerScript): Promis
workerScript.delay = null;
workerScript.delayReject = undefined;
workerScript.infiniteLoopSafetyCounter = 0;
if (workerScript.env.stopFlag) reject(new ScriptDeath(workerScript));
else resolve();
}, time);

@ -55,7 +55,7 @@ import { makeRuntimeRejectMsg, netscriptDelay, resolveNetscriptRequestedThreads
import { numeralWrapper } from "./ui/numeralFormat";
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
import { LogBoxEvents } from "./ui/React/LogBoxManager";
import { LogBoxEvents, LogBoxCloserEvents } from "./ui/React/LogBoxManager";
import { arrayToString } from "./utils/helpers/arrayToString";
import { isString } from "./utils/helpers/isString";
@ -1007,6 +1007,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
LogBoxEvents.emit(runningScriptObj);
},
closeTail: function (_pid: unknown = workerScript.scriptRef.pid): void {
updateDynamicRam("closeTail", getRamCost(Player, "closeTail"));
const pid = helper.number("closeTail", "pid", _pid);
//Emit an event to tell the game to close the tail window if it exists
LogBoxCloserEvents.emit(pid);
},
nuke: function (_hostname: unknown): boolean {
updateDynamicRam("nuke", getRamCost(Player, "nuke"));
const hostname = helper.string("tail", "hostname", _hostname);

@ -74,7 +74,7 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript
if (!player.canAccessCorporation() || player.hasCorporation()) return false;
if (!corporationName) return false;
if (player.bitNodeN !== 3 && !selfFund) throw new Error("cannot use seed funds outside of BitNode 3");
if (BitNodeMultipliers.CorporationSoftCap < 0.15)
if (BitNodeMultipliers.CorporationSoftcap < 0.15)
throw new Error(`You cannot create a corporation in Bitnode ${player.bitNodeN}`);
if (selfFund) {

@ -84,7 +84,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
if (script.filename === cbScript) {
const ramUsage = script.ramUsage;
const ramAvailable = home.maxRam - home.ramUsed;
if (ramUsage > ramAvailable) {
if (ramUsage > ramAvailable + 0.001) {
return; // Not enough RAM
}
const runningScriptObj = new RunningScript(script, []); // No args
@ -123,7 +123,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
_ctx.helper.checkSingularityAccess();
const augName = _ctx.helper.string("augName", _augName);
const aug = getAugmentation(_ctx, augName);
return [aug.getCost(player).moneyCost, aug.getCost(player).repCost];
const costs = aug.getCost(player);
return [costs.repCost, costs.moneyCost];
},
getAugmentationPrereq: (_ctx: NetscriptContext) =>
function (_augName: unknown): string[] {

@ -5,6 +5,7 @@ import { netscriptDelay } from "../NetscriptEvaluator";
import { staneksGift } from "../CotMG/Helper";
import { Fragments, FragmentById } from "../CotMG/Fragment";
import { FragmentType } from "../CotMG/FragmentType";
import {
Fragment as IFragment,
@ -42,11 +43,19 @@ export function NetscriptStanek(
},
chargeFragment: (_ctx: NetscriptContext) =>
function (_rootX: unknown, _rootY: unknown): Promise<void> {
//Get the fragment object using the given coordinates
const rootX = _ctx.helper.number("rootX", _rootX);
const rootY = _ctx.helper.number("rootY", _rootY);
checkStanekAPIAccess("chargeFragment");
const fragment = staneksGift.findFragment(rootX, rootY);
//Check whether the selected fragment can ge charged
if (!fragment) throw _ctx.makeRuntimeErrorMsg(`No fragment with root (${rootX}, ${rootY}).`);
if (fragment.fragment().type == FragmentType.Booster) {
throw _ctx.makeRuntimeErrorMsg(
`The fragment with root (${rootX}, ${rootY}) is a Booster Fragment and thus cannot be charged.`,
);
}
//Charge the fragment
const time = staneksGift.inBonus() ? 200 : 1000;
return netscriptDelay(time, workerScript).then(function () {
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);

@ -11,6 +11,7 @@ import { defaultStyles } from "../Themes/Styles";
import { CONSTANTS } from "../Constants";
import { hash } from "../hash/hash";
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
import { Terminal } from "../../src/Terminal";
export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
return {
@ -96,5 +97,11 @@ export function NetscriptUserInterface(): InternalAPI<IUserInterface> {
return gameInfo;
},
clearTerminal: function (): void {
updateRam("clearTerminal");
workerScript.log("ui.clearTerminal", () => `Clearing terminal`);
Terminal.clear();
},
};
}

@ -532,7 +532,7 @@ function createAndAddWorkerScript(
const oneRamUsage = getRamUsageFromRunningScript(runningScriptObj);
const ramUsage = roundToTwo(oneRamUsage * threads);
const ramAvailable = server.maxRam - server.ramUsed;
if (ramUsage > ramAvailable) {
if (ramUsage > ramAvailable + 0.001) {
dialogBoxCreate(
`Not enough RAM to run script ${runningScriptObj.filename} with args ` +
`${arrayToString(runningScriptObj.args)}. This likely occurred because you re-loaded ` +
@ -750,7 +750,7 @@ export function runScriptFromScript(
if (server.hasAdminRights == false) {
workerScript.log(caller, () => `You do not have root access on '${server.hostname}'`);
return 0;
} else if (ramUsage > ramAvailable) {
} else if (ramUsage > ramAvailable + 0.001) {
workerScript.log(
caller,
() =>

@ -1,13 +1,14 @@
import { Construction, CheckBox, CheckBoxOutlineBlank } from "@mui/icons-material";
import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material";
import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material";
import React, { useState, useEffect } from "react";
import React, { useEffect, useState } from "react";
import { Augmentation } from "../../../Augmentation/Augmentation";
import { StaticAugmentations } from "../../../Augmentation/StaticAugmentations";
import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames";
import { StaticAugmentations } from "../../../Augmentation/StaticAugmentations";
import { CONSTANTS } from "../../../Constants";
import { hasAugmentationPrereqs } from "../../../Faction/FactionHelpers";
import { LocationName } from "../../../Locations/data/LocationNames";
import { Locations } from "../../../Locations/Locations";
import { PurchaseAugmentationsOrderSetting } from "../../../Settings/SettingEnums";
import { Settings } from "../../../Settings/Settings";
import { IMap } from "../../../types";
import { use } from "../../../ui/Context";
@ -15,8 +16,8 @@ import { ConfirmationModal } from "../../../ui/React/ConfirmationModal";
import { Money } from "../../../ui/React/Money";
import { convertTimeMsToTimeElapsedString, formatNumber } from "../../../utils/StringHelperFunctions";
import { IPlayer } from "../../IPlayer";
import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../GraftingHelpers";
import { GraftableAugmentation } from "../GraftableAugmentation";
import { calculateGraftingTimeWithBonus, getGraftingAvailableAugs } from "../GraftingHelpers";
const GraftableAugmentations: IMap<GraftableAugmentation> = {};
@ -69,6 +70,21 @@ export const GraftingRoot = (): React.ReactElement => {
setRerender((old) => !old);
}
const getAugsSorted = (): string[] => {
const augs = getGraftingAvailableAugs(player);
switch (Settings.PurchaseAugmentationsOrder) {
case PurchaseAugmentationsOrderSetting.Cost:
return augs.sort((a, b) => GraftableAugmentations[a].cost - GraftableAugmentations[b].cost);
default:
return augs;
}
};
const switchSortOrder = (newOrder: PurchaseAugmentationsOrderSetting): void => {
Settings.PurchaseAugmentationsOrder = newOrder;
rerender();
};
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
@ -91,13 +107,31 @@ export const GraftingRoot = (): React.ReactElement => {
</Typography>
<Box sx={{ my: 3 }}>
<Typography variant="h5">Graft Augmentations</Typography>
<Paper sx={{ p: 1 }}>
<Typography variant="h5">Graft Augmentations</Typography>
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
<Button sx={{ width: "100%" }} onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>
Sort by Cost
</Button>
<Button sx={{ width: "100%" }} onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>
Sort by Default Order
</Button>
</Box>
</Paper>
{getGraftingAvailableAugs(player).length > 0 ? (
<Paper sx={{ my: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
<Paper sx={{ mb: 1, width: "fit-content", display: "grid", gridTemplateColumns: "1fr 3fr" }}>
<List sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}>
{getGraftingAvailableAugs(player).map((k, i) => (
{getAugsSorted().map((k, i) => (
<ListItemButton key={i + 1} onClick={() => setSelectedAug(k)} selected={selectedAug === k}>
<Typography>{k}</Typography>
<Typography
sx={{
color: canGraft(player, GraftableAugmentations[k])
? Settings.theme.primary
: Settings.theme.disabled,
}}
>
{k}
</Typography>
</ListItemButton>
))}
</List>

@ -560,7 +560,7 @@ export interface BitNodeMultipliers {
/** Influences how much money the player earns when completing working their job. */
CompanyWorkMoney: number;
/** Influences the money gain from dividends of corporations created by the player. */
CorporationSoftCap: number;
CorporationSoftcap: number;
/** Influences the valuation of corporations created by the player. */
CorporationValuation: number;
/** Influences the base experience gained for each ability when the player commits a crime. */
@ -4391,6 +4391,13 @@ interface UserInterface {
* RAM cost: 0 GB
*/
getGameInfo(): GameInfo;
/**
* Clear the Terminal window, as if the player ran `clear` in the terminal
* @remarks
* RAM cost: 0.2 GB
*/
clearTerminal(): void;
}
/**
@ -4974,6 +4981,21 @@ export interface NS {
*/
tail(fn?: FilenameOrPID, host?: string, ...args: any[]): void;
/**
* Close the tail window of a script.
* @remarks
* RAM cost: 0 GB
*
* Closes a scripts logs. This is functionally the same pressing the "Close" button on the tail window.
*
* If the function is called with no arguments, it will close the current scripts logs.
*
* Otherwise, the pid argument can be used to close the logs from another script.
*
* @param pid - Optional. PID of the script having its tail closed. If omitted, the current script is used.
*/
closeTail(pid?: number): void;
/**
* Get the list of servers connected to a server.
* @remarks

@ -54,6 +54,11 @@ interface IDefaultSettings {
*/
EnableBashHotkeys: boolean;
/**
* Infinite loop safety net
*/
InfinityLoopSafety: boolean;
/**
* Timestamps format
*/
@ -201,6 +206,7 @@ export const defaultSettings: IDefaultSettings = {
DisableOverviewProgressBars: false,
EnableBashHotkeys: false,
TimestampsFormat: "",
InfinityLoopSafety: true,
Locale: "en",
MaxRecentScriptsCapacity: 50,
MaxLogCapacity: 50,
@ -241,6 +247,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
DisableOverviewProgressBars: defaultSettings.DisableOverviewProgressBars,
EnableBashHotkeys: defaultSettings.EnableBashHotkeys,
TimestampsFormat: defaultSettings.TimestampsFormat,
InfinityLoopSafety: defaultSettings.InfinityLoopSafety,
Locale: "en",
MaxRecentScriptsCapacity: defaultSettings.MaxRecentScriptsCapacity,
MaxLogCapacity: defaultSettings.MaxLogCapacity,

@ -67,8 +67,18 @@ SourceFiles["SourceFile5"] = new SourceFile(
This Source-File grants a special new stat called Intelligence. Intelligence is unique because it is permanent and
persistent (it never gets reset back to 1). However, gaining Intelligence experience is much slower than other
stats. Higher Intelligence levels will boost your production for many actions in the game. In addition, this
Source-File will unlock the getBitNodeMultipliers() Netscript function and let you start with Formulas.exe, and
will raise all of your hacking-related multipliers by:
Source-File will unlock:
<br />
<ul>
<li>
The <code>getBitNodeMultipliers()</code> Netscript function
</li>
<li>Permanent access to Formulas.exe</li>
<li>
Access to BitNode multiplier information on the <b>Stats</b> page
</li>
</ul>
It will also raise all of your hacking-related multipliers by:
<br />
<br />
Level 1: 8%

@ -463,6 +463,12 @@ export class Terminal implements ITerminal {
this.contractOpen = true;
const res = await contract.prompt();
//Check if the contract still exists by the time the promise is fullfilled
if (serv.getContract(contractName) == null) {
this.contractOpen = false;
return this.error("Contract no longer exists (Was it solved by a script?)");
}
switch (res) {
case CodingContractResult.Success:
if (contract.reward !== null) {

@ -63,13 +63,11 @@ export function runScript(
return;
}
if (ramUsage > ramAvailable) {
if (ramUsage > ramAvailable + 0.001) {
terminal.error(
"This machine does not have enough RAM to run this script with " +
numThreads +
" threads. Script requires " +
numeralWrapper.formatRAM(ramUsage) +
" of RAM",
"This machine does not have enough RAM to run this script" +
(numThreads === 1 ? "" : ` with ${numThreads} threads`) +
`. Script requires ${numeralWrapper.formatRAM(ramUsage)} of RAM`,
);
return;
}

@ -1382,7 +1382,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
return [n + m, edges];
},
solver: (data: [number, [number, number][]], ans: string): boolean => {
//Case where the player believes there is no solution
//Case where the player believes there is no solution.
//Attempt to construct one to check if this is correct.
if (ans == "[]") {
//Helper function to get neighbourhood of a vertex
function neighbourhood(vertex: number): number[] {

@ -1,8 +1,9 @@
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 { BitNodes, defaultMultipliers, getBitNodeMultipliers } from "../BitNode/BitNode";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { BitNodeMultipliersDisplay } from "../BitNode/ui/BitnodeMultipliersDescription";
import { HacknetServerConstants } from "../Hacknet/data/Constants";
import { getPurchaseServerLimit } from "../Server/ServerPurchases";
import { Settings } from "../Settings/Settings";
@ -14,6 +15,7 @@ import { Modal } from "./React/Modal";
import { Money } from "./React/Money";
import { StatsRow } from "./React/StatsRow";
import { StatsTable } from "./React/StatsTable";
import { isEqual } from "lodash";
interface EmployersModalProps {
open: boolean;
@ -36,8 +38,22 @@ const EmployersModal = ({ open, onClose }: EmployersModalProps): React.ReactElem
);
};
interface IMultRow {
// The name of the multiplier
mult: string;
// The player's raw multiplier value
value: number;
// The player's effective multiplier value, affected by BitNode mults
effValue?: number;
// The text color for the row
color?: string;
}
interface MultTableProps {
rows: (string | number)[][];
rows: IMultRow[];
color: string;
noMargin?: boolean;
}
@ -48,29 +64,22 @@ function MultiplierTable(props: MultTableProps): React.ReactElement {
<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;
const { mult, value, effValue = null, color = props.color } = data;
if (modded && modded !== value && player.sourceFileLvl(5) > 0) {
if (effValue !== null && effValue !== value && player.sourceFileLvl(5) > 0) {
return (
<StatsRow key={mult} name={mult} color={props.color} data={{}}>
<StatsRow key={mult} name={mult} color={color} data={{}}>
<>
<Typography color={props.color}>
<Typography color={color}>
<span style={{ opacity: 0.5 }}>{numeralWrapper.formatPercentage(value)}</span>{" "}
{numeralWrapper.formatPercentage(modded)}
{numeralWrapper.formatPercentage(effValue)}
</Typography>
</>
</StatsRow>
);
}
return (
<StatsRow
key={mult}
name={mult}
color={props.color}
data={{ content: numeralWrapper.formatPercentage(value) }}
/>
<StatsRow key={mult} name={mult} color={color} data={{ content: numeralWrapper.formatPercentage(value) }} />
);
})}
</TableBody>
@ -82,16 +91,14 @@ function CurrentBitNode(): React.ReactElement {
const player = use.Player();
if (player.sourceFiles.length > 0) {
const index = "BitNode" + player.bitNodeN;
const lvl = player.sourceFileLvl(player.bitNodeN) + 1;
const lvl = Math.min(player.sourceFileLvl(player.bitNodeN) + 1, player.bitNodeN === 12 ? Infinity : 3);
return (
<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>
<Paper sx={{ mb: 1, 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>
);
}
@ -218,6 +225,14 @@ export function CharacterStats(): React.ReactElement {
}
timeRows.push(["Total", convertTimeMsToTimeElapsedString(player.totalPlaytime)]);
let showBitNodeMults = false;
if (player.sourceFileLvl(5) > 0) {
const n = player.bitNodeN;
const maxSfLevel = n === 12 ? Infinity : 3;
const mults = getBitNodeMultipliers(n, Math.min(player.sourceFileLvl(n) + 1, maxSfLevel));
showBitNodeMults = !isEqual(mults, defaultMultipliers);
}
return (
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
<Typography variant="h4">Stats</Typography>
@ -332,186 +347,255 @@ export function CharacterStats(): React.ReactElement {
</Table>
</Paper>
</Box>
<Box sx={{ mb: 1 }}>
<Paper sx={{ p: 1 }}>
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
Multipliers
{player.sourceFileLvl(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>
<Box>
<Paper sx={{ p: 1, mb: 1 }}>
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
Multipliers
{player.sourceFileLvl(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={[
{
mult: "Hacking Chance",
value: player.hacking_chance_mult,
},
{
mult: "Hacking Speed",
value: player.hacking_speed_mult,
},
{
mult: "Hacking Money",
value: player.hacking_money_mult,
effValue: player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney,
},
{
mult: "Hacking Growth",
value: player.hacking_grow_mult,
effValue: player.hacking_grow_mult * BitNodeMultipliers.ServerGrowthRate,
},
]}
color={Settings.theme.hack}
/>
<MultiplierTable
rows={[
{
mult: "Hacking Level",
value: player.hacking_mult,
effValue: player.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier,
},
{
mult: "Hacking Experience",
value: player.hacking_exp_mult,
effValue: player.hacking_exp_mult * BitNodeMultipliers.HackExpGain,
},
]}
color={Settings.theme.hack}
/>
<MultiplierTable
rows={[
{
mult: "Strength Level",
value: player.strength_mult,
effValue: player.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier,
},
{
mult: "Strength Experience",
value: player.strength_exp_mult,
},
]}
color={Settings.theme.combat}
/>
<MultiplierTable
rows={[
{
mult: "Defense Level",
value: player.defense_mult,
effValue: player.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier,
},
{
mult: "Defense Experience",
value: player.defense_exp_mult,
},
]}
color={Settings.theme.combat}
/>
<MultiplierTable
rows={[
{
mult: "Dexterity Level",
value: player.dexterity_mult,
effValue: player.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier,
},
{
mult: "Dexterity Experience",
value: player.dexterity_exp_mult,
},
]}
color={Settings.theme.combat}
/>
<MultiplierTable
rows={[
{
mult: "Agility Level",
value: player.agility_mult,
effValue: player.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier,
},
{
mult: "Agility Experience",
value: player.agility_exp_mult,
},
]}
color={Settings.theme.combat}
/>
<MultiplierTable
rows={[
{
mult: "Charisma Level",
value: player.charisma_mult,
effValue: player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
},
{
mult: "Charisma Experience",
value: player.charisma_exp_mult,
},
]}
color={Settings.theme.cha}
noMargin
/>
</Box>
<Box>
<MultiplierTable
rows={[
{
mult: "Hacknet Node Production",
value: player.hacknet_node_money_mult,
effValue: player.hacknet_node_money_mult * BitNodeMultipliers.HacknetNodeMoney,
},
{
mult: "Hacknet Node Purchase Cost",
value: player.hacknet_node_purchase_cost_mult,
},
{
mult: "Hacknet Node RAM Upgrade Cost",
value: player.hacknet_node_ram_cost_mult,
},
{
mult: "Hacknet Node Core Purchase Cost",
value: player.hacknet_node_core_cost_mult,
},
{
mult: "Hacknet Node Level Upgrade Cost",
value: player.hacknet_node_level_cost_mult,
},
]}
color={Settings.theme.primary}
/>
<MultiplierTable
rows={[
{
mult: "Company Reputation Gain",
value: player.company_rep_mult,
color: Settings.theme.rep,
},
{
mult: "Faction Reputation Gain",
value: player.faction_rep_mult,
effValue: player.faction_rep_mult * BitNodeMultipliers.FactionWorkRepGain,
color: Settings.theme.rep,
},
{
mult: "Salary",
value: player.work_money_mult,
effValue: player.work_money_mult * BitNodeMultipliers.CompanyWorkMoney,
color: Settings.theme.money,
},
]}
color={Settings.theme.money}
/>
<MultiplierTable
rows={[
{
mult: "Crime Success Chance",
value: player.crime_success_mult,
},
{
mult: "Crime Money",
value: player.crime_money_mult,
effValue: player.crime_money_mult * BitNodeMultipliers.CrimeMoney,
color: Settings.theme.money,
},
]}
color={Settings.theme.combat}
/>
{player.canAccessBladeburner() && (
<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],
{
mult: "Bladeburner Success Chance",
value: player.bladeburner_success_chance_mult,
},
{
mult: "Bladeburner Max Stamina",
value: player.bladeburner_max_stamina_mult,
},
{
mult: "Bladeburner Stamina Gain",
value: player.bladeburner_stamina_gain_mult,
},
{
mult: "Bladeburner Field Analysis",
value: player.bladeburner_analysis_mult,
},
]}
color={Settings.theme.primary}
noMargin
/>
<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>
</Box>
</Paper>
<Paper sx={{ p: 1, mb: 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 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>
<CurrentBitNode />
{showBitNodeMults && (
<Paper sx={{ p: 1, mb: 1 }}>
<Typography variant="h5">BitNode Multipliers</Typography>
<BitNodeMultipliersDisplay n={player.bitNodeN} />
</Paper>
)}
<MoneyModal open={moneyOpen} onClose={() => setMoneyOpen(false)} />
<EmployersModal open={employersOpen} onClose={() => setEmployersOpen(false)} />
</Container>

@ -35,7 +35,7 @@ export function NSSelection(props: IProps): React.ReactElement {
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Modal open={props.open} onClose={props.onClose} sx={{ zIndex: 999999 }}>
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
<Tab label="NS1" />
<Tab label="NS2" />

@ -18,10 +18,12 @@ import { Theme } from "@mui/material";
import { findRunningScript } from "../../Script/ScriptHelpers";
import { Player } from "../../Player";
import { debounce } from "lodash";
import { Settings } from "../../Settings/Settings";
let layerCounter = 0;
export const LogBoxEvents = new EventEmitter<[RunningScript]>();
export const LogBoxCloserEvents = new EventEmitter<[number]>();
export const LogBoxClearEvents = new EventEmitter<[]>();
interface Log {
@ -50,6 +52,15 @@ export function LogBoxManager(): React.ReactElement {
[],
);
//Event used by ns.closeTail to close tail windows
useEffect(
() =>
LogBoxCloserEvents.subscribe((pid: number) => {
closePid(pid);
}),
[],
);
useEffect(() =>
LogBoxClearEvents.subscribe(() => {
logs = [];
@ -57,11 +68,18 @@ export function LogBoxManager(): React.ReactElement {
}),
);
//Close tail windows by their id
function close(id: string): void {
logs = logs.filter((l) => l.id !== id);
rerender();
}
//Close tail windows by their pid
function closePid(pid: number): void {
logs = logs.filter((log) => log.script.pid != pid);
rerender();
}
return (
<>
{logs.map((log) => (
@ -77,42 +95,18 @@ interface IProps {
onClose: () => void;
}
const useStyles = makeStyles((theme: Theme) =>
const useStyles = makeStyles((_theme: Theme) =>
createStyles({
title: {
"&.is-minimized + *": {
border: "none",
margin: 0,
"max-height": 0,
padding: 0,
"pointer-events": "none",
visibility: "hidden",
},
},
logs: {
overflowY: "scroll",
overflowX: "hidden",
scrollbarWidth: "auto",
display: "flex",
flexDirection: "column-reverse",
whiteSpace: "pre-wrap",
},
titleButton: {
padding: "1px 6px",
},
success: {
color: theme.colors.success,
},
error: {
color: theme.palette.error.main,
},
primary: {
color: theme.palette.primary.main,
},
info: {
color: theme.palette.info.main,
},
warning: {
color: theme.palette.warning.main,
padding: "1px 0",
height: "100%",
},
}),
);
@ -190,20 +184,20 @@ function LogWindow(props: IProps): React.ReactElement {
setMinimized(!minimized);
}
function lineClass(s: string): string {
function lineColor(s: string): string {
if (s.match(/(^\[[^\]]+\] )?ERROR/) || s.match(/(^\[[^\]]+\] )?FAIL/)) {
return classes.error;
return Settings.theme.error;
}
if (s.match(/(^\[[^\]]+\] )?SUCCESS/)) {
return classes.success;
return Settings.theme.success;
}
if (s.match(/(^\[[^\]]+\] )?WARN/)) {
return classes.warning;
return Settings.theme.warning;
}
if (s.match(/(^\[[^\]]+\] )?INFO/)) {
return classes.info;
return Settings.theme.info;
}
return classes.primary;
return Settings.theme.primary;
}
// And trigger fakeDrag when the window is resized
@ -242,74 +236,99 @@ function LogWindow(props: IProps): React.ReactElement {
if (e.clientX < 0 || e.clientY < 0 || e.clientX > innerWidth || e.clientY > innerHeight) return false;
};
// Max [width, height]
const minConstraints: [number, number] = [250, 33];
return (
<Draggable handle=".drag" onDrag={boundToBody} ref={rootRef}>
<Paper
style={{
display: "flex",
<Draggable handle=".drag" onDrag={boundToBody} ref={rootRef} onMouseDown={updateLayer}>
<Box
display="flex"
sx={{
flexFlow: "column",
position: "fixed",
left: "40%",
top: "30%",
zIndex: 1400,
minWidth: `${minConstraints[0]}px`,
minHeight: `${minConstraints[1]}px`,
...(minimized
? {
border: "none",
margin: 0,
maxHeight: 0,
padding: 0,
}
: {
border: `1px solid ${Settings.theme.welllight}`,
}),
}}
ref={container}
>
<div onMouseDown={updateLayer}>
<Paper
className={classes.title + " " + (minimized ? "is-minimized" : "")}
style={{
cursor: "grab",
}}
>
<Box className="drag" display="flex" alignItems="center" ref={draggableRef}>
<Typography color="primary" variant="h6" sx={{ marginRight: "auto" }} title={title(true)}>
{title()}
<ResizableBox
height={500}
width={500}
minConstraints={minConstraints}
handle={
<span
style={{
position: "absolute",
right: "-10px",
bottom: "-16px",
cursor: "nw-resize",
display: minimized ? "none" : "inline-block",
}}
>
<ArrowForwardIosIcon color="primary" style={{ transform: "rotate(45deg)", fontSize: "1.75rem" }} />
</span>
}
>
<>
<Paper className="drag" sx={{ display: "flex", alignItems: "center", cursor: "grab" }} ref={draggableRef}>
<Typography
variant="h6"
sx={{ marginRight: "auto", textOverflow: "ellipsis", whiteSpace: "nowrap", overflow: "hidden" }}
title={title(true)}
>
{title(true)}
</Typography>
{!workerScripts.has(script.pid) ? (
<Button className={classes.titleButton} onClick={run} onTouchEnd={run}>
Run
<span style={{ minWidth: "fit-content", height: `${minConstraints[1]}px` }}>
{!workerScripts.has(script.pid) ? (
<Button className={classes.titleButton} onClick={run} onTouchEnd={run}>
Run
</Button>
) : (
<Button className={classes.titleButton} onClick={kill} onTouchEnd={kill}>
Kill
</Button>
)}
<Button className={classes.titleButton} onClick={minimize} onTouchEnd={minimize}>
{minimized ? "\u{1F5D6}" : "\u{1F5D5}"}
</Button>
) : (
<Button className={classes.titleButton} onClick={kill} onTouchEnd={kill}>
Kill
<Button className={classes.titleButton} onClick={props.onClose} onTouchEnd={props.onClose}>
Close
</Button>
)}
<Button className={classes.titleButton} onClick={minimize} onTouchEnd={minimize}>
{minimized ? "\u{1F5D6}" : "\u{1F5D5}"}
</Button>
<Button className={classes.titleButton} onClick={props.onClose} onTouchEnd={props.onClose}>
Close
</Button>
</Box>
</Paper>
<Paper sx={{ overflow: "scroll", overflowWrap: "break-word", whiteSpace: "pre-wrap" }}>
<ResizableBox
</span>
</Paper>
<Paper
className={classes.logs}
height={500}
width={500}
minConstraints={[250, 30]}
handle={
<span style={{ position: "absolute", right: "-10px", bottom: "-13px", cursor: "nw-resize" }}>
<ArrowForwardIosIcon color="primary" style={{ transform: "rotate(45deg)" }} />
</span>
}
sx={{ height: `calc(100% - ${minConstraints[1]}px)`, display: minimized ? "none" : "flex" }}
>
<Box>
<span style={{ display: "flex", flexDirection: "column" }}>
{script.logs.map(
(line: string, i: number): JSX.Element => (
<Typography key={i} className={lineClass(line)}>
<Typography key={i} sx={{ color: lineColor(line) }}>
{line}
<br />
</Typography>
),
)}
</Box>
</ResizableBox>
</Paper>
</div>
</Paper>
</span>
</Paper>
</>
</ResizableBox>
</Box>
</Draggable>
);
}

@ -1,10 +1,11 @@
import React from "react";
import { Theme } from "@mui/material";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import M from "@mui/material/Modal";
import Fade from "@mui/material/Fade";
import Box from "@mui/material/Box";
import Fade from "@mui/material/Fade";
import M from "@mui/material/Modal";
import createStyles from "@mui/styles/createStyles";
import makeStyles from "@mui/styles/makeStyles";
import { SxProps } from "@mui/system";
import React from "react";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@ -12,7 +13,6 @@ const useStyles = makeStyles((theme: Theme) =>
display: "flex",
alignItems: "center",
justifyContent: "center",
zIndex: 999999,
},
paper: {
backgroundColor: theme.palette.background.default,
@ -35,6 +35,7 @@ interface IProps {
open: boolean;
onClose: () => void;
children: React.ReactNode;
sx?: SxProps<Theme>;
}
export const Modal = (props: IProps): React.ReactElement => {
@ -49,6 +50,7 @@ export const Modal = (props: IProps): React.ReactElement => {
onClose={props.onClose}
closeAfterTransition
className={classes.modal}
sx={props.sx}
>
<Fade in={props.open}>
<div className={classes.paper}>