Merge pull request #3449 from danielyxie/dev

basic doc no longer hacker themed
This commit is contained in:
hydroflame 2022-04-14 02:25:44 -04:00 committed by GitHub
commit a9eaea0568
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 1499 additions and 1020 deletions

1
.gitignore vendored

@ -1,4 +1,5 @@
.DS_Store .DS_Store
.history
.vscode .vscode
Changelog.txt Changelog.txt
Netburner.txt Netburner.txt

@ -1,453 +0,0 @@
:root {
--dark-text-color: #0c0;
--dark-link-color: #090;
}
body {
color: #000;
}
.wy-nav-content-wrap {
background-color: #000;
}
.wy-nav-content {
background-color: #000;
}
.section {
color: var(--dark-text-color);
}
.rst-content .highlighted {
background: #333;
box-shadow: none;
}
.highlight {
background-color: #17181c;
}
.highlight .nn {
color: var(--dark-text-color);
}
.highlight .nb {
color: #8bb8df;
}
.highlight .kn,
.highlight .kc,
.highlight .k {
color: #41c2ea;
}
.highlight .s1,
.highlight .s2 {
color: #b3e87f;
}
.highlight .nt {
color: #ccb350;
}
.highlight .c1 {
color: #686868;
}
.rst-content div[class^="highlight"] {
border-color: #1a1a1a;
}
.icon,
.icon-home {
color: var(--dark-link-color);
}
.wy-nav-content a,
.wy-nav-content a:visited {
color: var(--dark-link-color) !important;
text-decoration: underline;
}
.btn-neutral {
background-color: #17181c !important;
}
.btn-neutral:hover {
background-color: #101114 !important;
}
.btn-neutral:visited {
color: #c1c1c1 !important;
}
.btn {
box-shadow: none;
}
footer {
color: #bdbdbd;
}
.wy-nav-side {
background-color: #000;
border: 1px solid #333;
}
.wy-menu-vertical > a {
color: var(--dark-text-color);
}
.wy-menu-vertical li.current {
background-color: #000;
}
.wy-menu-vertical li.current > a,
.wy-menu-vertical li.on a {
background-color: #000;
color: var(--dark-text-color);
}
.wy-menu-vertical li.toctree-l1.current > a,
.wy-menu-vertical li.current a {
border-color: #000;
}
.wy-menu-vertical header,
.wy-menu-vertical p.caption {
color: var(--dark-text-color);
}
html.writer-html4 .rst-content dl:not(.docutils) > dt,
html.writer-html5
.rst-content
dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)
> dt {
background-color: #333;
}
.wy-menu-vertical li.current a {
color: #090;
}
.wy-menu-vertical a {
color: var(--dark-text-color);
}
.wy-menu-vertical li.current a:hover {
background-color: #222;
}
.wy-menu-vertical a:hover,
.wy-menu-vertical li.current > a:hover,
.wy-menu-vertical li.on a:hover {
background-color: #000;
color: var(--dark-text-color);
}
.wy-menu-vertical li.toctree-l2.current > a,
.wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a {
background-color: #000;
}
.wy-side-nav-search {
background-color: #000;
color: var(--dark-text-color);
}
.wy-side-nav-search .wy-dropdown > a,
.wy-side-nav-search > a {
color: var(--dark-text-color);
}
.wy-side-nav-search input[type="text"] {
border-left: 0px;
border-right: 0px;
border-top: 0px;
border-radius: 0px;
box-shadow: none;
border-bottom-color: #0c0;
background-color: #333;
color: var(--dark-text-color);
}
.theme-switcher {
background-color: #0b0c0d;
color: var(--dark-text-color);
}
writer-html4 .rst-content dl:not(.docutils) > dt,
writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) > dt {
background-color: #0b0b0b;
color: #007dce;
border-color: #282828;
}
.rst-content code,
.rst-content tt {
color: var(--dark-text-color);
}
writer-html4 .rst-content dl:not(.docutils) dl:not(.field-list) > dt,
writer-html5
.rst-content
dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)
dl:not(.field-list)
> dt {
background-color: #0f0f0f;
color: #959595;
border-color: #2b2b2b;
}
.rst-content code,
.rst-content tt,
code {
background-color: #2d2d2d;
border-color: #1c1c1c;
}
.rst-content code.xref,
.rst-content tt.xref,
a .rst-content code,
a .rst-content tt {
color: #cecece;
}
.rst-content .hint,
.rst-content .important,
.rst-content .tip,
.rst-content .wy-alert-success.admonition,
.rst-content .wy-alert-success.admonition-todo,
.rst-content .wy-alert-success.attention,
.rst-content .wy-alert-success.caution,
.rst-content .wy-alert-success.danger,
.rst-content .wy-alert-success.error,
.rst-content .wy-alert-success.note,
.rst-content .wy-alert-success.seealso,
.rst-content .wy-alert-success.warning,
.wy-alert.wy-alert-success {
background-color: #00392e;
}
.rst-content .hint .admonition-title,
.rst-content .hint .wy-alert-title,
.rst-content .important .admonition-title,
.rst-content .important .wy-alert-title,
.rst-content .tip .admonition-title,
.rst-content .tip .wy-alert-title,
.rst-content .wy-alert-success.admonition-todo .admonition-title,
.rst-content .wy-alert-success.admonition-todo .wy-alert-title,
.rst-content .wy-alert-success.admonition .admonition-title,
.rst-content .wy-alert-success.admonition .wy-alert-title,
.rst-content .wy-alert-success.attention .admonition-title,
.rst-content .wy-alert-success.attention .wy-alert-title,
.rst-content .wy-alert-success.caution .admonition-title,
.rst-content .wy-alert-success.caution .wy-alert-title,
.rst-content .wy-alert-success.danger .admonition-title,
.rst-content .wy-alert-success.danger .wy-alert-title,
.rst-content .wy-alert-success.error .admonition-title,
.rst-content .wy-alert-success.error .wy-alert-title,
.rst-content .wy-alert-success.note .admonition-title,
.rst-content .wy-alert-success.note .wy-alert-title,
.rst-content .wy-alert-success.seealso .admonition-title,
.rst-content .wy-alert-success.seealso .wy-alert-title,
.rst-content .wy-alert-success.warning .admonition-title,
.rst-content .wy-alert-success.warning .wy-alert-title,
.rst-content .wy-alert.wy-alert-success .admonition-title,
.wy-alert.wy-alert-success .rst-content .admonition-title,
.wy-alert.wy-alert-success .wy-alert-title {
background-color: #006a56;
}
.rst-content .note,
.rst-content .seealso,
.rst-content .wy-alert-info.admonition,
.rst-content .wy-alert-info.admonition-todo,
.rst-content .wy-alert-info.attention,
.rst-content .wy-alert-info.caution,
.rst-content .wy-alert-info.danger,
.rst-content .wy-alert-info.error,
.rst-content .wy-alert-info.hint,
.rst-content .wy-alert-info.important,
.rst-content .wy-alert-info.tip,
.rst-content .wy-alert-info.warning,
.wy-alert.wy-alert-info {
background-color: #002c4d;
}
.rst-content .note .admonition-title,
.rst-content .note .wy-alert-title,
.rst-content .seealso .admonition-title,
.rst-content .seealso .wy-alert-title,
.rst-content .wy-alert-info.admonition-todo .admonition-title,
.rst-content .wy-alert-info.admonition-todo .wy-alert-title,
.rst-content .wy-alert-info.admonition .admonition-title,
.rst-content .wy-alert-info.admonition .wy-alert-title,
.rst-content .wy-alert-info.attention .admonition-title,
.rst-content .wy-alert-info.attention .wy-alert-title,
.rst-content .wy-alert-info.caution .admonition-title,
.rst-content .wy-alert-info.caution .wy-alert-title,
.rst-content .wy-alert-info.danger .admonition-title,
.rst-content .wy-alert-info.danger .wy-alert-title,
.rst-content .wy-alert-info.error .admonition-title,
.rst-content .wy-alert-info.error .wy-alert-title,
.rst-content .wy-alert-info.hint .admonition-title,
.rst-content .wy-alert-info.hint .wy-alert-title,
.rst-content .wy-alert-info.important .admonition-title,
.rst-content .wy-alert-info.important .wy-alert-title,
.rst-content .wy-alert-info.tip .admonition-title,
.rst-content .wy-alert-info.tip .wy-alert-title,
.rst-content .wy-alert-info.warning .admonition-title,
.rst-content .wy-alert-info.warning .wy-alert-title,
.rst-content .wy-alert.wy-alert-info .admonition-title,
.wy-alert.wy-alert-info .rst-content .admonition-title,
.wy-alert.wy-alert-info .wy-alert-title {
background-color: #004a7b;
}
.rst-content dl:not(.docutils) dt {
background-color: #333;
}
.rst-content {
color: var(--dark-text-color);
}
.rst-content .admonition-todo,
.rst-content .attention,
.rst-content .caution,
.rst-content .warning,
.rst-content .wy-alert-warning.admonition,
.rst-content .wy-alert-warning.danger,
.rst-content .wy-alert-warning.error,
.rst-content .wy-alert-warning.hint,
.rst-content .wy-alert-warning.important,
.rst-content .wy-alert-warning.note,
.rst-content .wy-alert-warning.seealso,
.rst-content .wy-alert-warning.tip,
.wy-alert.wy-alert-warning {
background-color: #533500;
}
.rst-content .admonition-todo .admonition-title,
.rst-content .admonition-todo .wy-alert-title,
.rst-content .attention .admonition-title,
.rst-content .attention .wy-alert-title,
.rst-content .caution .admonition-title,
.rst-content .caution .wy-alert-title,
.rst-content .warning .admonition-title,
.rst-content .warning .wy-alert-title,
.rst-content .wy-alert-warning.admonition .admonition-title,
.rst-content .wy-alert-warning.admonition .wy-alert-title,
.rst-content .wy-alert-warning.danger .admonition-title,
.rst-content .wy-alert-warning.danger .wy-alert-title,
.rst-content .wy-alert-warning.error .admonition-title,
.rst-content .wy-alert-warning.error .wy-alert-title,
.rst-content .wy-alert-warning.hint .admonition-title,
.rst-content .wy-alert-warning.hint .wy-alert-title,
.rst-content .wy-alert-warning.important .admonition-title,
.rst-content .wy-alert-warning.important .wy-alert-title,
.rst-content .wy-alert-warning.note .admonition-title,
.rst-content .wy-alert-warning.note .wy-alert-title,
.rst-content .wy-alert-warning.seealso .admonition-title,
.rst-content .wy-alert-warning.seealso .wy-alert-title,
.rst-content .wy-alert-warning.tip .admonition-title,
.rst-content .wy-alert-warning.tip .wy-alert-title,
.rst-content .wy-alert.wy-alert-warning .admonition-title,
.wy-alert.wy-alert-warning .rst-content .admonition-title,
.wy-alert.wy-alert-warning .wy-alert-title {
background-color: #803b00;
}
.rst-content .danger,
.rst-content .error,
.rst-content .wy-alert-danger.admonition,
.rst-content .wy-alert-danger.admonition-todo,
.rst-content .wy-alert-danger.attention,
.rst-content .wy-alert-danger.caution,
.rst-content .wy-alert-danger.hint,
.rst-content .wy-alert-danger.important,
.rst-content .wy-alert-danger.note,
.rst-content .wy-alert-danger.seealso,
.rst-content .wy-alert-danger.tip,
.rst-content .wy-alert-danger.warning,
.wy-alert.wy-alert-danger {
background-color: #82231a;
}
.rst-content .danger .admonition-title,
.rst-content .danger .wy-alert-title,
.rst-content .error .admonition-title,
.rst-content .error .wy-alert-title,
.rst-content .wy-alert-danger.admonition-todo .admonition-title,
.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,
.rst-content .wy-alert-danger.admonition .admonition-title,
.rst-content .wy-alert-danger.admonition .wy-alert-title,
.rst-content .wy-alert-danger.attention .admonition-title,
.rst-content .wy-alert-danger.attention .wy-alert-title,
.rst-content .wy-alert-danger.caution .admonition-title,
.rst-content .wy-alert-danger.caution .wy-alert-title,
.rst-content .wy-alert-danger.hint .admonition-title,
.rst-content .wy-alert-danger.hint .wy-alert-title,
.rst-content .wy-alert-danger.important .admonition-title,
.rst-content .wy-alert-danger.important .wy-alert-title,
.rst-content .wy-alert-danger.note .admonition-title,
.rst-content .wy-alert-danger.note .wy-alert-title,
.rst-content .wy-alert-danger.seealso .admonition-title,
.rst-content .wy-alert-danger.seealso .wy-alert-title,
.rst-content .wy-alert-danger.tip .admonition-title,
.rst-content .wy-alert-danger.tip .wy-alert-title,
.rst-content .wy-alert-danger.warning .admonition-title,
.rst-content .wy-alert-danger.warning .wy-alert-title,
.rst-content .wy-alert.wy-alert-danger .admonition-title,
.wy-alert.wy-alert-danger .rst-content .admonition-title,
.wy-alert.wy-alert-danger .wy-alert-title {
background-color: #b9372b;
}
.wy-nav-top {
background-color: #0b152d;
}
.rst-content table.docutils thead,
.rst-content table.field-list thead,
.wy-table thead {
color: var(--dark-text-color);
}
.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,
.wy-table-backed,
.wy-table-odd td,
.wy-table-striped tr:nth-child(2n-1) td {
background-color: #333;
}
.rst-content table.docutils:not(.field-list) tr:nth-child(2n) td,
.wy-table-backed,
.wy-table-odd td,
.wy-table-striped tr:nth-child(2n) td {
background-color: #444;
}
.rst-content table.docutils td,
.wy-table-bordered-all td,
writer-html5 .rst-content table.docutils th,
.rst-content table.docutils,
.wy-table-bordered-all {
border-color: #262626;
}
.rst-content table.docutils caption,
.rst-content table.field-list caption,
.wy-table caption {
color: var(--dark-text-color);
}
.wy-menu-vertical li.toctree-l3.current > a,
.wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a {
background-color: #000;
}
.wy-side-nav-search > div.version {
color: var(--dark-text-color);
}

@ -8,7 +8,6 @@ import { CONSTANTS } from "../Constants";
import { Factions, factionExists } from "../Faction/Factions"; import { Factions, factionExists } from "../Faction/Factions";
import { Player } from "../Player"; import { Player } from "../Player";
import { prestigeAugmentation } from "../Prestige"; import { prestigeAugmentation } from "../Prestige";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { clearObject } from "../utils/helpers/clearObject"; import { clearObject } from "../utils/helpers/clearObject";
@ -73,7 +72,7 @@ function initAugmentations(): void {
} }
function getBaseAugmentationPriceMultiplier(): number { function getBaseAugmentationPriceMultiplier(): number {
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]]; return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
} }
export function getGenericAugmentationPriceMultiplier(): number { export function getGenericAugmentationPriceMultiplier(): number {
return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length); return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length);

@ -7,7 +7,6 @@ import * as React from "react";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Augmentations } from "../Augmentations"; import { Augmentations } from "../Augmentations";
@ -35,7 +34,7 @@ interface IBitNodeModifiedStatsProps {
function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement { function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement {
// If player doesn't have SF5 or if the property isn't affected by BitNode mults // If player doesn't have SF5 or if the property isn't affected by BitNode mults
if (props.mult === 1 || SourceFileFlags[5] === 0) if (props.mult === 1 || Player.sourceFileLvl(5) === 0)
return <Typography color={props.color}>{numeralWrapper.formatPercentage(props.base)}</Typography>; return <Typography color={props.color}>{numeralWrapper.formatPercentage(props.base)}</Typography>;
return ( return (

@ -1,5 +1,4 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { BitNodes } from "../BitNode"; import { BitNodes } from "../BitNode";
import { enterBitNode } from "../../RedPill"; import { enterBitNode } from "../../RedPill";
@ -128,12 +127,6 @@ export function BitverseRoot(props: IProps): React.ReactElement {
const destroyed = player.bitNodeN; const destroyed = player.bitNodeN;
const [destroySequence, setDestroySequence] = useState(!props.quick); const [destroySequence, setDestroySequence] = useState(!props.quick);
// Update NextSourceFileFlags
const nextSourceFileFlags = SourceFileFlags.slice();
if (!props.flume) {
if (nextSourceFileFlags[destroyed] < 3) ++nextSourceFileFlags[destroyed];
}
if (destroySequence) { if (destroySequence) {
return ( return (
<CinematicText <CinematicText
@ -164,6 +157,15 @@ export function BitverseRoot(props: IProps): React.ReactElement {
); );
} }
const nextSourceFileLvl = (n: number): number => {
const lvl = player.sourceFileLvl(n);
if (n !== destroyed) {
return lvl;
}
const max = n === 12 ? Infinity : 3;
return Math.min(max, lvl + 1);
};
if (Settings.DisableASCIIArt) { if (Settings.DisableASCIIArt) {
return ( return (
<> <>
@ -177,7 +179,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<BitNodePortal <BitNodePortal
key={node.number} key={node.number}
n={node.number} n={node.number}
level={nextSourceFileFlags[node.number]} level={nextSourceFileLvl(node.number)}
enter={enter} enter={enter}
flume={props.flume} flume={props.flume}
destroyedBitNode={destroyed} destroyedBitNode={destroyed}
@ -216,6 +218,8 @@ export function BitverseRoot(props: IProps): React.ReactElement {
</> </>
); );
} }
const n = nextSourceFileLvl;
return ( return (
// prettier-ignore // prettier-ignore
<> <>
@ -229,19 +233,19 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={n(13)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={n(10)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={n(11)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={n(9)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={n(12)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={n(7)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={n(8)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={n(5)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={n(6)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={n(1)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={n(2)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={n(3)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={n(4)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
<br /> <br />

@ -28,7 +28,6 @@ import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital"; import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { Augmentations } from "../Augmentation/Augmentations";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { getTimestamp } from "../utils/helpers/getTimestamp"; import { getTimestamp } from "../utils/helpers/getTimestamp";
import { joinFaction } from "../Faction/FactionHelpers"; import { joinFaction } from "../Faction/FactionHelpers";

@ -117,7 +117,7 @@ export const CONSTANTS: {
LatestUpdate: string; LatestUpdate: string;
} = { } = {
VersionString: "1.6.4", VersionString: "1.6.4",
VersionNumber: 13, VersionNumber: 14,
// Speed (in ms) at which the main loop is updated // Speed (in ms) at which the main loop is updated
_idleSpeed: 200, _idleSpeed: 200,

@ -51,29 +51,31 @@ export function NewIndustry(corporation: ICorporation, industry: string, name: s
export function NewCity(corporation: ICorporation, division: IIndustry, city: string): void { export function NewCity(corporation: ICorporation, division: IIndustry, city: string): void {
if (corporation.funds < CorporationConstants.OfficeInitialCost) { if (corporation.funds < CorporationConstants.OfficeInitialCost) {
throw new Error("You don't have enough company funds to open a new office!"); throw new Error("You don't have enough company funds to open a new office!");
} else {
corporation.funds = corporation.funds - CorporationConstants.OfficeInitialCost;
division.offices[city] = new OfficeSpace({
loc: city,
size: CorporationConstants.OfficeInitialSize,
});
} }
if (division.offices[city]) {
throw new Error(`You have already expanded into ${city} for ${division.name}`);
}
corporation.funds = corporation.funds - CorporationConstants.OfficeInitialCost;
division.offices[city] = new OfficeSpace({
loc: city,
size: CorporationConstants.OfficeInitialSize,
});
} }
export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void { export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void {
if (corporation.funds < upgrade[1]) { if (corporation.funds < upgrade.price) {
throw new Error("Insufficient funds"); throw new Error("Insufficient funds");
} }
if (corporation.unlockUpgrades[upgrade[0]] === 1) { if (corporation.unlockUpgrades[upgrade.index] === 1) {
throw new Error(`You have already unlocked the ${upgrade[2]} upgrade!`); throw new Error(`You have already unlocked the ${upgrade.name} upgrade!`);
} }
corporation.unlock(upgrade); corporation.unlock(upgrade);
} }
export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgrade): void { export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgrade): void {
const baseCost = upgrade[1]; const baseCost = upgrade.basePrice;
const priceMult = upgrade[2]; const priceMult = upgrade.priceMult;
const level = corporation.upgrades[upgrade[0]]; const level = corporation.upgrades[upgrade.index];
const cost = baseCost * Math.pow(priceMult, level); const cost = baseCost * Math.pow(priceMult, level);
if (corporation.funds < cost) { if (corporation.funds < cost) {
throw new Error("Insufficient funds"); throw new Error("Insufficient funds");
@ -345,10 +347,17 @@ export function PurchaseWarehouse(corp: ICorporation, division: IIndustry, city:
corp.funds = corp.funds - CorporationConstants.WarehouseInitialCost; corp.funds = corp.funds - CorporationConstants.WarehouseInitialCost;
} }
export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse): void { export function UpgradeWarehouseCost(warehouse: Warehouse, amt: number): number {
const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1); return Array.from(Array(amt).keys()).reduce(
(acc, index) => acc + CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1 + index),
0,
);
}
export function UpgradeWarehouse(corp: ICorporation, division: IIndustry, warehouse: Warehouse, amt = 1): void {
const sizeUpgradeCost = UpgradeWarehouseCost(warehouse, amt);
if (corp.funds < sizeUpgradeCost) return; if (corp.funds < sizeUpgradeCost) return;
++warehouse.level; warehouse.level += amt;
warehouse.updateSize(corp, division); warehouse.updateSize(corp, division);
corp.funds = corp.funds - sizeUpgradeCost; corp.funds = corp.funds - sizeUpgradeCost;
} }
@ -416,7 +425,7 @@ export function MakeProduct(
} }
const product = new Product({ const product = new Product({
name: productName.replace(/[<>]/g, ""), //Sanitize for HTMl elements name: productName.replace(/[<>]/g, "").trim(), //Sanitize for HTMl elements
createCity: city, createCity: city,
designCost: designInvest, designCost: designInvest,
advCost: marketingInvest, advCost: marketingInvest,
@ -488,12 +497,23 @@ export function CancelExportMaterial(divisionName: string, cityName: string, mat
export function LimitProductProduction(product: Product, cityName: string, qty: number): void { export function LimitProductProduction(product: Product, cityName: string, qty: number): void {
if (qty < 0 || isNaN(qty)) { if (qty < 0 || isNaN(qty)) {
product.prdman[cityName][0] = false; product.prdman[cityName][0] = false;
product.prdman[cityName][1] = 0;
} else { } else {
product.prdman[cityName][0] = true; product.prdman[cityName][0] = true;
product.prdman[cityName][1] = qty; product.prdman[cityName][1] = qty;
} }
} }
export function LimitMaterialProduction(material: Material, qty: number): void {
if (qty < 0 || isNaN(qty)) {
material.prdman[0] = false;
material.prdman[1] = 0;
} else {
material.prdman[0] = true;
material.prdman[1] = qty;
}
}
export function SetMaterialMarketTA1(material: Material, on: boolean): void { export function SetMaterialMarketTA1(material: Material, on: boolean): void {
material.marketTa1 = on; material.marketTa1 = on;
} }

@ -45,6 +45,8 @@ export class Corporation {
upgrades: number[]; upgrades: number[];
upgradeMultipliers: number[]; upgradeMultipliers: number[];
avgProfit = 0;
state = new CorporationState(); state = new CorporationState();
constructor(params: IParams = {}) { constructor(params: IParams = {}) {
@ -106,6 +108,8 @@ export class Corporation {
this.expenses = this.expenses + ind.lastCycleExpenses; this.expenses = this.expenses + ind.lastCycleExpenses;
}); });
const profit = this.revenue - this.expenses; const profit = this.revenue - this.expenses;
this.avgProfit =
(this.avgProfit * (CorporationConstants.AvgProfitLength - 1) + profit) / CorporationConstants.AvgProfitLength;
const cycleProfit = profit * (marketCycles * CorporationConstants.SecsPerMarketCycle); const cycleProfit = profit * (marketCycles * CorporationConstants.SecsPerMarketCycle);
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) { if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
dialogBoxCreate( dialogBoxCreate(
@ -160,7 +164,7 @@ export class Corporation {
determineValuation(): number { determineValuation(): number {
let val, let val,
profit = this.revenue - this.expenses; profit = this.avgProfit;
if (this.public) { if (this.public) {
// Account for dividends // Account for dividends
if (this.dividendPercentage > 0) { if (this.dividendPercentage > 0) {
@ -260,8 +264,8 @@ export class Corporation {
//One time upgrades that unlock new features //One time upgrades that unlock new features
unlock(upgrade: CorporationUnlockUpgrade): void { unlock(upgrade: CorporationUnlockUpgrade): void {
const upgN = upgrade[0], const upgN = upgrade.index,
price = upgrade[1]; price = upgrade.price;
while (this.unlockUpgrades.length <= upgN) { while (this.unlockUpgrades.length <= upgN) {
this.unlockUpgrades.push(0); this.unlockUpgrades.push(0);
} }
@ -282,10 +286,10 @@ export class Corporation {
//Levelable upgrades //Levelable upgrades
upgrade(upgrade: CorporationUpgrade): void { upgrade(upgrade: CorporationUpgrade): void {
const upgN = upgrade[0], const upgN = upgrade.index,
basePrice = upgrade[1], basePrice = upgrade.basePrice,
priceMult = upgrade[2], priceMult = upgrade.priceMult,
upgradeAmt = upgrade[3]; //Amount by which the upgrade multiplier gets increased (additive) upgradeAmt = upgrade.benefit; //Amount by which the upgrade multiplier gets increased (additive)
while (this.upgrades.length <= upgN) { while (this.upgrades.length <= upgN) {
this.upgrades.push(0); this.upgrades.push(0);
} }

@ -28,6 +28,7 @@ export const CorporationConstants: {
AllMaterials: string[]; AllMaterials: string[];
FundingRoundShares: number[]; FundingRoundShares: number[];
FundingRoundMultiplier: number[]; FundingRoundMultiplier: number[];
AvgProfitLength: number;
} = { } = {
INITIALSHARES: 1e9, //Total number of shares you have at your company INITIALSHARES: 1e9, //Total number of shares you have at your company
SHARESPERPRICEUPDATE: 1e6, //When selling large number of shares, price is dynamically updated for every batch of this amount SHARESPERPRICEUPDATE: 1e6, //When selling large number of shares, price is dynamically updated for every batch of this amount
@ -83,4 +84,6 @@ export const CorporationConstants: {
], ],
FundingRoundShares: [0.1, 0.35, 0.25, 0.2], FundingRoundShares: [0.1, 0.35, 0.25, 0.2],
FundingRoundMultiplier: [4, 3, 3, 2.5], FundingRoundMultiplier: [4, 3, 3, 2.5],
AvgProfitLength: 1,
}; };

@ -1,70 +1,100 @@
import { IMap } from "../../types"; export interface CorporationUnlockUpgrade {
index: number;
price: number;
name: string;
desc: string;
}
export type CorporationUnlockUpgrade = [number, number, string, string]; export enum CorporationUnlockUpgradeIndex {
Export = 0,
SmartSupply = 1,
MarketResearchDemand = 2,
MarketDataCompetition = 3,
VeChain = 4,
ShadyAccounting = 5,
GovernmentPartnership = 6,
WarehouseAPI = 7,
OfficeAPI = 8,
}
// Corporation Unlock Upgrades // Corporation Unlock Upgrades
// Upgrades for entire corporation, unlocks features, either you have it or you dont // Upgrades for entire corporation, unlocks features, either you have it or you dont
// The data structure is an array with the following format: export const CorporationUnlockUpgrades: Record<CorporationUnlockUpgradeIndex, CorporationUnlockUpgrade> = {
// [index in Corporation feature upgrades array, price, name, description]
export const CorporationUnlockUpgrades: IMap<CorporationUnlockUpgrade> = {
//Lets you export goods //Lets you export goods
"0": [ [CorporationUnlockUpgradeIndex.Export]: {
0, index: 0,
20e9, price: 20e9,
"Export", name: "Export",
"Develop infrastructure to export your materials to your other facilities. " + desc:
"Develop infrastructure to export your materials to your other facilities. " +
"This allows you to move materials around between different divisions and cities.", "This allows you to move materials around between different divisions and cities.",
], },
//Lets you buy exactly however many required materials you need for production //Lets you buy exactly however many required materials you need for production
"1": [ [CorporationUnlockUpgradeIndex.SmartSupply]: {
1, index: 1,
25e9, price: 25e9,
"Smart Supply", name: "Smart Supply",
"Use advanced AI to anticipate your supply needs. " + desc:
"Use advanced AI to anticipate your supply needs. " +
"This allows you to purchase exactly however many materials you need for production.", "This allows you to purchase exactly however many materials you need for production.",
], },
//Displays each material/product's demand //Displays each material/product's demand
"2": [ [CorporationUnlockUpgradeIndex.MarketResearchDemand]: {
2, index: 2,
5e9, price: 5e9,
"Market Research - Demand", name: "Market Research - Demand",
"Mine and analyze market data to determine the demand of all resources. " + desc:
"Mine and analyze market data to determine the demand of all resources. " +
"The demand attribute, which affects sales, will be displayed for every material and product.", "The demand attribute, which affects sales, will be displayed for every material and product.",
], },
//Display's each material/product's competition //Display's each material/product's competition
"3": [ [CorporationUnlockUpgradeIndex.MarketDataCompetition]: {
3, index: 3,
5e9, price: 5e9,
"Market Data - Competition", name: "Market Data - Competition",
"Mine and analyze market data to determine how much competition there is on the market " + desc:
"Mine and analyze market data to determine how much competition there is on the market " +
"for all resources. The competition attribute, which affects sales, will be displayed for " + "for all resources. The competition attribute, which affects sales, will be displayed for " +
"every material and product.", "every material and product.",
], },
"4": [ [CorporationUnlockUpgradeIndex.VeChain]: {
4, index: 4,
10e9, price: 10e9,
"VeChain", name: "VeChain",
"Use AI and blockchain technology to identify where you can improve your supply chain systems. " + desc:
"Use AI and blockchain technology to identify where you can improve your supply chain systems. " +
"This upgrade will allow you to view a wide array of useful statistics about your " + "This upgrade will allow you to view a wide array of useful statistics about your " +
"Corporation.", "Corporation.",
], },
"5": [ [CorporationUnlockUpgradeIndex.ShadyAccounting]: {
5, index: 5,
500e12, price: 500e12,
"Shady Accounting", name: "Shady Accounting",
"Utilize unscrupulous accounting practices and pay off government officials to save money " + desc:
"Utilize unscrupulous accounting practices and pay off government officials to save money " +
"on taxes. This reduces the dividend tax rate by 5%.", "on taxes. This reduces the dividend tax rate by 5%.",
], },
"6": [ [CorporationUnlockUpgradeIndex.GovernmentPartnership]: {
6, index: 6,
2e15, price: 2e15,
"Government Partnership", name: "Government Partnership",
"Help national governments further their agendas in exchange for lowered taxes. " + desc:
"Help national governments further their agendas in exchange for lowered taxes. " +
"This reduces the dividend tax rate by 10%", "This reduces the dividend tax rate by 10%",
], },
"7": [7, 50e9, "Warehouse API", "Enables the warehouse API."], [CorporationUnlockUpgradeIndex.WarehouseAPI]: {
"8": [8, 50e9, "Office API", "Enables the office API."], index: 7,
price: 50e9,
name: "Warehouse API",
desc: "Enables the warehouse API.",
},
[CorporationUnlockUpgradeIndex.OfficeAPI]: {
index: 8,
price: 50e9,
name: "Office API",
desc: "Enables the office API.",
},
}; };

@ -1,127 +1,153 @@
import { IMap } from "../../types"; export interface CorporationUpgrade {
index: number;
basePrice: number;
priceMult: number;
benefit: number;
name: string;
desc: string;
}
export type CorporationUpgrade = [number, number, number, number, string, string]; export enum CorporationUpgradeIndex {
SmartFactories = 0,
SmartStorage = 1,
DreamSense = 2,
WilsonAnalytics = 3,
NuoptimalNootropicInjectorImplants = 4,
SpeechProcessorImplants = 5,
NeuralAccelerators = 6,
FocusWires = 7,
ABCSalesBots = 8,
ProjectInsight = 9,
}
// Corporation Upgrades // Corporation Upgrades
// Upgrades for entire corporation, levelable upgrades // Upgrades for entire corporation, levelable upgrades
// The data structure is an array with the following format export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpgrade> = {
// [index in Corporation upgrades array, base price, price mult, benefit mult (additive), name, desc]
export const CorporationUpgrades: IMap<CorporationUpgrade> = {
//Smart factories, increases production //Smart factories, increases production
"0": [ [CorporationUpgradeIndex.SmartFactories]: {
0, index: CorporationUpgradeIndex.SmartFactories,
2e9, basePrice: 2e9,
1.06, priceMult: 1.06,
0.03, benefit: 0.03,
"Smart Factories", name: "Smart Factories",
"Advanced AI automatically optimizes the operation and productivity " + desc:
"Advanced AI automatically optimizes the operation and productivity " +
"of factories. Each level of this upgrade increases your global production by 3% (additive).", "of factories. Each level of this upgrade increases your global production by 3% (additive).",
], },
//Smart warehouses, increases storage size //Smart warehouses, increases storage size
"1": [ [CorporationUpgradeIndex.SmartStorage]: {
1, index: CorporationUpgradeIndex.SmartStorage,
2e9, basePrice: 2e9,
1.06, priceMult: 1.06,
0.1, benefit: 0.1,
"Smart Storage", name: "Smart Storage",
"Advanced AI automatically optimizes your warehouse storage methods. " + desc:
"Advanced AI automatically optimizes your warehouse storage methods. " +
"Each level of this upgrade increases your global warehouse storage size by 10% (additive).", "Each level of this upgrade increases your global warehouse storage size by 10% (additive).",
], },
//Advertise through dreams, passive popularity/ awareness gain //Advertise through dreams, passive popularity/ awareness gain
"2": [ [CorporationUpgradeIndex.DreamSense]: {
2, index: CorporationUpgradeIndex.DreamSense,
4e9, basePrice: 4e9,
1.1, priceMult: 1.1,
0.001, benefit: 0.001,
"DreamSense", name: "DreamSense",
"Use DreamSense LCC Technologies to advertise your corporation " + desc:
"Use DreamSense LCC Technologies to advertise your corporation " +
"to consumers through their dreams. Each level of this upgrade provides a passive " + "to consumers through their dreams. Each level of this upgrade provides a passive " +
"increase in awareness of all of your companies (divisions) by 0.004 / market cycle," + "increase in awareness of all of your companies (divisions) by 0.004 / market cycle," +
"and in popularity by 0.001 / market cycle. A market cycle is approximately " + "and in popularity by 0.001 / market cycle. A market cycle is approximately " +
"15 seconds.", "15 seconds.",
], },
//Makes advertising more effective //Makes advertising more effective
"3": [ [CorporationUpgradeIndex.WilsonAnalytics]: {
3, index: CorporationUpgradeIndex.WilsonAnalytics,
4e9, basePrice: 4e9,
1.5, priceMult: 1.5,
0.005, benefit: 0.005,
"Wilson Analytics", name: "Wilson Analytics",
"Purchase data and analysis from Wilson, a marketing research " + desc:
"Purchase data and analysis from Wilson, a marketing research " +
"firm. Each level of this upgrades increases the effectiveness of your " + "firm. Each level of this upgrades increases the effectiveness of your " +
"advertising by 0.5% (additive).", "advertising by 0.5% (additive).",
], },
//Augmentation for employees, increases cre //Augmentation for employees, increases cre
"4": [ [CorporationUpgradeIndex.NuoptimalNootropicInjectorImplants]: {
4, index: CorporationUpgradeIndex.NuoptimalNootropicInjectorImplants,
1e9, basePrice: 1e9,
1.06, priceMult: 1.06,
0.1, benefit: 0.1,
"Nuoptimal Nootropic Injector Implants", name: "Nuoptimal Nootropic Injector Implants",
"Purchase the Nuoptimal Nootropic " + desc:
"Purchase the Nuoptimal Nootropic " +
"Injector augmentation for your employees. Each level of this upgrade " + "Injector augmentation for your employees. Each level of this upgrade " +
"globally increases the creativity of your employees by 10% (additive).", "globally increases the creativity of your employees by 10% (additive).",
], },
//Augmentation for employees, increases cha //Augmentation for employees, increases cha
"5": [ [CorporationUpgradeIndex.SpeechProcessorImplants]: {
5, index: CorporationUpgradeIndex.SpeechProcessorImplants,
1e9, basePrice: 1e9,
1.06, priceMult: 1.06,
0.1, benefit: 0.1,
"Speech Processor Implants", name: "Speech Processor Implants",
"Purchase the Speech Processor augmentation for your employees. " + desc:
"Purchase the Speech Processor augmentation for your employees. " +
"Each level of this upgrade globally increases the charisma of your employees by 10% (additive).", "Each level of this upgrade globally increases the charisma of your employees by 10% (additive).",
], },
//Augmentation for employees, increases int //Augmentation for employees, increases int
"6": [ [CorporationUpgradeIndex.NeuralAccelerators]: {
6, index: CorporationUpgradeIndex.NeuralAccelerators,
1e9, basePrice: 1e9,
1.06, priceMult: 1.06,
0.1, benefit: 0.1,
"Neural Accelerators", name: "Neural Accelerators",
"Purchase the Neural Accelerator augmentation for your employees. " + desc:
"Purchase the Neural Accelerator augmentation for your employees. " +
"Each level of this upgrade globally increases the intelligence of your employees " + "Each level of this upgrade globally increases the intelligence of your employees " +
"by 10% (additive).", "by 10% (additive).",
], },
//Augmentation for employees, increases eff //Augmentation for employees, increases eff
"7": [ [CorporationUpgradeIndex.FocusWires]: {
7, index: CorporationUpgradeIndex.FocusWires,
1e9, basePrice: 1e9,
1.06, priceMult: 1.06,
0.1, benefit: 0.1,
"FocusWires", name: "FocusWires",
"Purchase the FocusWire augmentation for your employees. Each level " + desc:
"Purchase the FocusWire augmentation for your employees. Each level " +
"of this upgrade globally increases the efficiency of your employees by 10% (additive).", "of this upgrade globally increases the efficiency of your employees by 10% (additive).",
], },
//Improves sales of materials/products //Improves sales of materials/products
"8": [ [CorporationUpgradeIndex.ABCSalesBots]: {
8, index: CorporationUpgradeIndex.ABCSalesBots,
1e9, basePrice: 1e9,
1.07, priceMult: 1.07,
0.01, benefit: 0.01,
"ABC SalesBots", name: "ABC SalesBots",
"Always Be Closing. Purchase these robotic salesmen to increase the amount of " + desc:
"Always Be Closing. Purchase these robotic salesmen to increase the amount of " +
"materials and products you sell. Each level of this upgrade globally increases your sales " + "materials and products you sell. Each level of this upgrade globally increases your sales " +
"by 1% (additive).", "by 1% (additive).",
], },
//Improves scientific research rate //Improves scientific research rate
"9": [ [CorporationUpgradeIndex.ProjectInsight]: {
9, index: CorporationUpgradeIndex.ProjectInsight,
5e9, basePrice: 5e9,
1.07, priceMult: 1.07,
0.05, benefit: 0.05,
"Project Insight", name: "Project Insight",
"Purchase 'Project Insight', a R&D service provided by the secretive " + desc:
"Purchase 'Project Insight', a R&D service provided by the secretive " +
"Fulcrum Technologies. Each level of this upgrade globally increases the amount of " + "Fulcrum Technologies. Each level of this upgrade globally increases the amount of " +
"Scientific Research you produce by 5% (additive).", "Scientific Research you produce by 5% (additive).",
], },
}; };

@ -8,8 +8,8 @@ import { EmployeePositions } from "../EmployeePositions";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { UpgradeOfficeSizeModal } from "./UpgradeOfficeSizeModal"; import { UpgradeOfficeSizeModal } from "./modals/UpgradeOfficeSizeModal";
import { ThrowPartyModal } from "./ThrowPartyModal"; import { ThrowPartyModal } from "./modals/ThrowPartyModal";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "./Context";

@ -7,8 +7,8 @@ import { Industries } from "../IndustryData";
import { IndustryUpgrades } from "../IndustryUpgrades"; import { IndustryUpgrades } from "../IndustryUpgrades";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText"; import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { MakeProductModal } from "./MakeProductModal"; import { MakeProductModal } from "./modals/MakeProductModal";
import { ResearchModal } from "./ResearchModal"; import { ResearchModal } from "./modals/ResearchModal";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate"; import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsTable } from "../../ui/React/StatsTable"; import { StatsTable } from "../../ui/React/StatsTable";

@ -6,7 +6,7 @@ import { CorporationConstants } from "../data/Constants";
import { Material } from "../Material"; import { Material } from "../Material";
import { Product } from "../Product"; import { Product } from "../Product";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { SmartSupplyModal } from "./SmartSupplyModal"; import { SmartSupplyModal } from "./modals/SmartSupplyModal";
import { ProductElem } from "./ProductElem"; import { ProductElem } from "./ProductElem";
import { MaterialElem } from "./MaterialElem"; import { MaterialElem } from "./MaterialElem";
import { MaterialSizes } from "../MaterialSizes"; import { MaterialSizes } from "../MaterialSizes";

@ -20,13 +20,13 @@ interface IProps {
export function LevelableUpgrade(props: IProps): React.ReactElement { export function LevelableUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const data = props.upgrade; const data = props.upgrade;
const level = corp.upgrades[data[0]]; const level = corp.upgrades[data.index];
const baseCost = data[1]; const baseCost = data.basePrice;
const priceMult = data[2]; const priceMult = data.priceMult;
const cost = baseCost * Math.pow(priceMult, level); const cost = baseCost * Math.pow(priceMult, level);
const tooltip = data[5]; const tooltip = data.desc;
function onClick(): void { function onClick(): void {
if (corp.funds < cost) return; if (corp.funds < cost) return;
try { try {
@ -45,7 +45,7 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
</Button> </Button>
<Tooltip title={tooltip}> <Tooltip title={tooltip}>
<Typography> <Typography>
{data[4]} - lvl {level} {data.name} - lvl {level}
</Typography> </Typography>
</Tooltip> </Tooltip>
</Box> </Box>

@ -5,10 +5,10 @@ import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { Material } from "../Material"; import { Material } from "../Material";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { ExportModal } from "./ExportModal"; import { ExportModal } from "./modals/ExportModal";
import { MaterialMarketTaModal } from "./MaterialMarketTaModal"; import { MaterialMarketTaModal } from "./modals/MaterialMarketTaModal";
import { SellMaterialModal } from "./SellMaterialModal"; import { SellMaterialModal } from "./modals/SellMaterialModal";
import { PurchaseMaterialModal } from "./PurchaseMaterialModal"; import { PurchaseMaterialModal } from "./modals/PurchaseMaterialModal";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
@ -21,6 +21,7 @@ import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { LimitMaterialProductionModal } from "./modals/LimitMaterialProductionModal";
interface IMaterialProps { interface IMaterialProps {
warehouse: Warehouse; warehouse: Warehouse;
@ -37,6 +38,8 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
const [exportOpen, setExportOpen] = useState(false); const [exportOpen, setExportOpen] = useState(false);
const [sellMaterialOpen, setSellMaterialOpen] = useState(false); const [sellMaterialOpen, setSellMaterialOpen] = useState(false);
const [materialMarketTaOpen, setMaterialMarketTaOpen] = useState(false); const [materialMarketTaOpen, setMaterialMarketTaOpen] = useState(false);
const [limitProductionOpen, setLimitProductionOpen] = useState(false);
const warehouse = props.warehouse; const warehouse = props.warehouse;
const city = props.city; const city = props.city;
const mat = props.mat; const mat = props.mat;
@ -110,6 +113,12 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
sellButtonText = <>Sell (0.000/0.000)</>; sellButtonText = <>Sell (0.000/0.000)</>;
} }
// Limit Production button
let limitMaterialButtonText = "Limit Material";
if (mat.prdman[0]) {
limitMaterialButtonText += " (" + numeralWrapper.format(mat.prdman[1], nf) + ")";
}
return ( return (
<Paper> <Paper>
<Box sx={{ display: "grid", gridTemplateColumns: "2fr 1fr", m: "5px" }}> <Box sx={{ display: "grid", gridTemplateColumns: "2fr 1fr", m: "5px" }}>
@ -194,6 +203,14 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
/> />
</> </>
)} )}
<Button color={tutorial ? "error" : "primary"} onClick={() => setLimitProductionOpen(true)}>
{limitMaterialButtonText}
</Button>
<LimitMaterialProductionModal
material={mat}
open={limitProductionOpen}
onClose={() => setLimitProductionOpen(false)}
/>
</Box> </Box>
</Box> </Box>
</Paper> </Paper>

@ -2,18 +2,18 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { LevelableUpgrade } from "./LevelableUpgrade"; import { LevelableUpgrade } from "./LevelableUpgrade";
import { UnlockUpgrade } from "./UnlockUpgrade"; import { UnlockUpgrade } from "./UnlockUpgrade";
import { BribeFactionModal } from "./BribeFactionModal"; import { BribeFactionModal } from "./modals/BribeFactionModal";
import { SellSharesModal } from "./SellSharesModal"; import { SellSharesModal } from "./modals/SellSharesModal";
import { BuybackSharesModal } from "./BuybackSharesModal"; import { BuybackSharesModal } from "./modals/BuybackSharesModal";
import { IssueDividendsModal } from "./IssueDividendsModal"; import { IssueDividendsModal } from "./modals/IssueDividendsModal";
import { IssueNewSharesModal } from "./IssueNewSharesModal"; import { IssueNewSharesModal } from "./modals/IssueNewSharesModal";
import { FindInvestorsModal } from "./FindInvestorsModal"; import { FindInvestorsModal } from "./modals/FindInvestorsModal";
import { GoPublicModal } from "./GoPublicModal"; import { GoPublicModal } from "./modals/GoPublicModal";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades"; import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
import { CorporationUpgrade, CorporationUpgrades } from "../data/CorporationUpgrades"; import { CorporationUpgrade, CorporationUpgradeIndex, CorporationUpgrades } from "../data/CorporationUpgrades";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
@ -164,9 +164,9 @@ function Upgrades({ rerender }: IUpgradeProps): React.ReactElement {
<Typography variant="h4">Unlocks</Typography> <Typography variant="h4">Unlocks</Typography>
<Grid container> <Grid container>
{Object.values(CorporationUnlockUpgrades) {Object.values(CorporationUnlockUpgrades)
.filter((upgrade: CorporationUnlockUpgrade) => !corp.unlockUpgrades[upgrade[0]]) .filter((upgrade: CorporationUnlockUpgrade) => !corp.unlockUpgrades[upgrade.index])
.map((upgrade: CorporationUnlockUpgrade) => ( .map((upgrade: CorporationUnlockUpgrade) => (
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade[0]} /> <UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade.index} />
))} ))}
</Grid> </Grid>
</Paper> </Paper>
@ -174,9 +174,9 @@ function Upgrades({ rerender }: IUpgradeProps): React.ReactElement {
<Typography variant="h4">Upgrades</Typography> <Typography variant="h4">Upgrades</Typography>
<Grid container> <Grid container>
{corp.upgrades {corp.upgrades
.map((level: number, i: number) => CorporationUpgrades[i]) .map((level: number, i: number) => CorporationUpgrades[i as CorporationUpgradeIndex])
.map((upgrade: CorporationUpgrade) => ( .map((upgrade: CorporationUpgrade) => (
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade[0]} /> <LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade.index} />
))} ))}
</Grid> </Grid>
</Paper> </Paper>

@ -2,10 +2,11 @@ import React, { useState } from "react";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { Product } from "../Product"; import { Product } from "../Product";
import { DiscontinueProductModal } from "./DiscontinueProductModal"; import { DiscontinueProductModal } from "./modals/DiscontinueProductModal";
import { LimitProductProductionModal } from "./LimitProductProductionModal"; import { LimitProductProductionModal } from "./modals/LimitProductProductionModal";
import { SellProductModal } from "./SellProductModal"; import { SellProductModal } from "./modals/SellProductModal";
import { ProductMarketTaModal } from "./ProductMarketTaModal"; import { ProductMarketTaModal } from "./modals/ProductMarketTaModal";
import { CancelProductModal } from "./modals/CancelProductModal";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
@ -32,6 +33,7 @@ export function ProductElem(props: IProductProps): React.ReactElement {
const [sellOpen, setSellOpen] = useState(false); const [sellOpen, setSellOpen] = useState(false);
const [limitOpen, setLimitOpen] = useState(false); const [limitOpen, setLimitOpen] = useState(false);
const [discontinueOpen, setDiscontinueOpen] = useState(false); const [discontinueOpen, setDiscontinueOpen] = useState(false);
const [cancelOpen, setCancelOpen] = useState(false);
const [marketTaOpen, setMarketTaOpen] = useState(false); const [marketTaOpen, setMarketTaOpen] = useState(false);
const city = props.city; const city = props.city;
const product = props.product; const product = props.product;
@ -111,6 +113,13 @@ export function ProductElem(props: IProductProps): React.ReactElement {
</Typography> </Typography>
<br /> <br />
<Typography>{numeralWrapper.format(product.prog, "0.00")}% complete</Typography> <Typography>{numeralWrapper.format(product.prog, "0.00")}% complete</Typography>
<Button onClick={() => setCancelOpen(true)}>Cancel</Button>
<CancelProductModal
product={product}
rerender={props.rerender}
open={cancelOpen}
onClose={() => setCancelOpen(false)}
/>
</> </>
) : ( ) : (
<> <>
@ -171,6 +180,13 @@ export function ProductElem(props: IProductProps): React.ReactElement {
<Typography>Est. Market Price: {numeralWrapper.formatMoney(product.pCost)}</Typography> <Typography>Est. Market Price: {numeralWrapper.formatMoney(product.pCost)}</Typography>
</Tooltip> </Tooltip>
</Box> </Box>
<Button onClick={() => setDiscontinueOpen(true)}>Discontinue</Button>
<DiscontinueProductModal
product={product}
rerender={props.rerender}
open={discontinueOpen}
onClose={() => setDiscontinueOpen(false)}
/>
</> </>
)} )}
@ -186,14 +202,6 @@ export function ProductElem(props: IProductProps): React.ReactElement {
open={limitOpen} open={limitOpen}
onClose={() => setLimitOpen(false)} onClose={() => setLimitOpen(false)}
/> />
<Button onClick={() => setDiscontinueOpen(true)}>Discontinue</Button>
<DiscontinueProductModal
product={product}
rerender={props.rerender}
open={discontinueOpen}
onClose={() => setDiscontinueOpen(false)}
/>
{division.hasResearch("Market-TA.I") && ( {division.hasResearch("Market-TA.I") && (
<> <>
<Button onClick={() => setMarketTaOpen(true)}>Market-TA</Button> <Button onClick={() => setMarketTaOpen(true)}>Market-TA</Button>

@ -20,9 +20,9 @@ interface IProps {
export function UnlockUpgrade(props: IProps): React.ReactElement { export function UnlockUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const data = props.upgradeData; const data = props.upgradeData;
const tooltip = data[3]; const tooltip = data.desc;
function onClick(): void { function onClick(): void {
if (corp.funds < data[1]) return; if (corp.funds < data.price) return;
try { try {
UU(corp, props.upgradeData); UU(corp, props.upgradeData);
} catch (err) { } catch (err) {
@ -34,11 +34,11 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
return ( return (
<Grid item xs={4}> <Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse"> <Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds < data[1]} sx={{ mx: 1 }} onClick={onClick}> <Button disabled={corp.funds < data.price} sx={{ mx: 1 }} onClick={onClick}>
<MoneyCost money={data[1]} corp={corp} /> <MoneyCost money={data.price} corp={corp} />
</Button> </Button>
<Tooltip title={tooltip}> <Tooltip title={tooltip}>
<Typography>{data[2]}</Typography> <Typography>{data.name}</Typography>
</Tooltip> </Tooltip>
</Box> </Box>
</Grid> </Grid>

@ -1,11 +1,11 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../../Faction/Factions";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { use } from "../../ui/Context"; import { use } from "../../../ui/Context";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";

@ -1,14 +1,14 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { use } from "../../ui/Context"; import { use } from "../../../ui/Context";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import { BuyBackShares } from "../Actions"; import { BuyBackShares } from "../../Actions";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IProps { interface IProps {
open: boolean; open: boolean;

@ -0,0 +1,34 @@
import React from "react";
import { Product } from "../../Product";
import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps {
open: boolean;
onClose: () => void;
product: Product;
rerender: () => void;
}
// Create a popup that lets the player cancel a product
export function CancelProductModal(props: IProps): React.ReactElement {
const division = useDivision();
function cancel(): void {
division.discontinueProduct(props.product);
props.onClose();
props.rerender();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Are you sure you want to do this? Canceling a product removes it completely and permanently. You will receive no
money back by doing so
</Typography>
<Button onClick={cancel}>Cancel</Button>
</Modal>
);
}

@ -1,8 +1,8 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Money } from "../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { use } from "../../ui/Context"; import { use } from "../../../ui/Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";

@ -1,8 +1,8 @@
import React from "react"; import React from "react";
import { Product } from "../Product"; import { Product } from "../../Product";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "./Context"; import { useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";

@ -1,12 +1,12 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Material } from "../Material"; import { Material } from "../../Material";
import { Export } from "../Export"; import { Export } from "../../Export";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../../IIndustry";
import { ExportMaterial } from "../Actions"; import { ExportMaterial } from "../../Actions";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import { isRelevantMaterial } from "./Helpers"; import { isRelevantMaterial } from "../Helpers";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";

@ -1,8 +1,8 @@
import React from "react"; import React from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";

@ -1,13 +1,13 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IProps { interface IProps {
open: boolean; open: boolean;

@ -1,13 +1,13 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { IssueDividends } from "../Actions"; import { IssueDividends } from "../../Actions";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IProps { interface IProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;

@ -1,14 +1,14 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { getRandomInt } from "../../../utils/helpers/getRandomInt";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IEffectTextProps { interface IEffectTextProps {
shares: number | null; shares: number | null;

@ -0,0 +1,53 @@
import React, { useEffect, useState } from "react";
import { LimitMaterialProduction } from "../../Actions";
import { Modal } from "../../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import { KEY } from "../../../utils/helpers/keyCodes";
import { Material } from "../../Material";
interface IProps {
open: boolean;
onClose: () => void;
material: Material;
}
// Create a popup that lets the player limit the production of a product
export function LimitMaterialProductionModal(props: IProps): React.ReactElement {
const [limit, setLimit] = useState<number | null>(null);
// reset modal internal state on modal close
useEffect(() => {
if (!props.open) {
setLimit(null);
}
}, [props.open]);
function limitMaterialProduction(): void {
let qty = limit;
if (qty === null) qty = -1;
LimitMaterialProduction(props.material, qty);
props.onClose();
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.key === KEY.ENTER) limitMaterialProduction();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setLimit(null);
else setLimit(parseFloat(event.target.value));
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Enter a limit to the amount of this material you would like to produce per second. Leave the box empty to set no
limit.
</Typography>
<TextField autoFocus={true} placeholder="Limit" type="number" onChange={onChange} onKeyDown={onKeyDown} />
<Button onClick={limitMaterialProduction}>Limit production</Button>
</Modal>
);
}

@ -1,11 +1,11 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { Product } from "../Product"; import { Product } from "../../Product";
import { LimitProductProduction } from "../Actions"; import { LimitProductProduction } from "../../Actions";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IProps { interface IProps {
open: boolean; open: boolean;
@ -18,6 +18,13 @@ interface IProps {
export function LimitProductProductionModal(props: IProps): React.ReactElement { export function LimitProductProductionModal(props: IProps): React.ReactElement {
const [limit, setLimit] = useState<number | null>(null); const [limit, setLimit] = useState<number | null>(null);
// reset modal internal state on modal close
useEffect(() => {
if (!props.open) {
setLimit(null);
}
}, [props.open]);
function limitProductProduction(): void { function limitProductProduction(): void {
let qty = limit; let qty = limit;
if (qty === null) qty = -1; if (qty === null) qty = -1;

@ -1,15 +1,15 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { Industries } from "../IndustryData"; import { Industries } from "../../IndustryData";
import { MakeProduct } from "../Actions"; import { MakeProduct } from "../../Actions";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IProps { interface IProps {
open: boolean; open: boolean;

@ -1,8 +1,8 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { Material } from "../Material"; import { Material } from "../../Material";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "./Context"; import { useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";

@ -1,8 +1,8 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { Product } from "../Product"; import { Product } from "../../Product";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "./Context"; import { useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";

@ -1,16 +1,16 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { MaterialSizes } from "../MaterialSizes"; import { MaterialSizes } from "../../MaterialSizes";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../../Warehouse";
import { Material } from "../Material"; import { Material } from "../../Material";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { BulkPurchase, BuyMaterial } from "../Actions"; import { BulkPurchase, BuyMaterial } from "../../Actions";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IBulkPurchaseTextProps { interface IBulkPurchaseTextProps {
warehouse: Warehouse; warehouse: Warehouse;
@ -18,37 +18,6 @@ interface IBulkPurchaseTextProps {
amount: string; amount: string;
} }
function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement {
const parsedAmt = parseFloat(props.amount);
const cost = parsedAmt * props.mat.bCost;
const matSize = MaterialSizes[props.mat.name];
const maxAmount = (props.warehouse.size - props.warehouse.sizeUsed) / matSize;
if (parsedAmt * matSize > maxAmount) {
return (
<>
<Typography color={"error"}>Not enough warehouse space to purchase this amount</Typography>
</>
);
} else if (isNaN(cost) || parsedAmt < 0) {
return (
<>
<Typography color={"error"}>Invalid put for Bulk Purchase amount</Typography>
</>
);
} else {
return (
<>
<Typography>
Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
{numeralWrapper.formatMoney(cost)}
</Typography>
</>
);
}
}
interface IBPProps { interface IBPProps {
onClose: () => void; onClose: () => void;
mat: Material; mat: Material;
@ -58,6 +27,41 @@ interface IBPProps {
function BulkPurchaseSection(props: IBPProps): React.ReactElement { function BulkPurchaseSection(props: IBPProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const [buyAmt, setBuyAmt] = useState(""); const [buyAmt, setBuyAmt] = useState("");
const [disabled, setDisabled] = useState(false);
function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement {
const parsedAmt = parseFloat(props.amount);
const cost = parsedAmt * props.mat.bCost;
const matSize = MaterialSizes[props.mat.name];
const maxAmount = (props.warehouse.size - props.warehouse.sizeUsed) / matSize;
if (parsedAmt > maxAmount) {
setDisabled(true);
return (
<>
<Typography color={"error"}>Not enough warehouse space to purchase this amount</Typography>
</>
);
} else if (isNaN(cost) || parsedAmt < 0) {
setDisabled(true);
return (
<>
<Typography color={"error"}>Invalid input for Bulk Purchase amount</Typography>
</>
);
} else {
setDisabled(false);
return (
<>
<Typography>
Purchasing {numeralWrapper.format(parsedAmt, "0,0.00")} of {props.mat.name} will cost{" "}
{numeralWrapper.formatMoney(cost)}
</Typography>
</>
);
}
}
function bulkPurchase(): void { function bulkPurchase(): void {
try { try {
@ -90,7 +94,9 @@ function BulkPurchaseSection(props: IBPProps): React.ReactElement {
placeholder="Bulk Purchase amount" placeholder="Bulk Purchase amount"
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<Button onClick={bulkPurchase}>Confirm Bulk Purchase</Button> <Button disabled={disabled} onClick={bulkPurchase}>
Confirm Bulk Purchase
</Button>
</> </>
); );
} }

@ -1,13 +1,13 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { IndustryResearchTrees } from "../IndustryData"; import { IndustryResearchTrees } from "../../IndustryData";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../../IIndustry";
import { Research } from "../Actions"; import { Research } from "../../Actions";
import { Node } from "../ResearchTree"; import { Node } from "../../ResearchTree";
import { ResearchMap } from "../ResearchMap"; import { ResearchMap } from "../../ResearchMap";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../../Settings/Settings";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";

@ -1,12 +1,12 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Material } from "../Material"; import { Material } from "../../Material";
import { SellMaterial } from "../Actions"; import { SellMaterial } from "../../Actions";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
function initialPrice(mat: Material): string { function initialPrice(mat: Material): string {
let val = mat.sCost ? mat.sCost + "" : ""; let val = mat.sCost ? mat.sCost + "" : "";

@ -1,15 +1,15 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Product } from "../Product"; import { Product } from "../../Product";
import { SellProduct } from "../Actions"; import { SellProduct } from "../../Actions";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
function initialPrice(product: Product): string { function initialPrice(product: Product): string {
let val = product.sCost ? product.sCost + "" : ""; let val = product.sCost ? product.sCost + "" : "";

@ -1,16 +1,16 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { use } from "../../ui/Context"; import { use } from "../../../ui/Context";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../../ICorporation";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Money } from "../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { SellShares } from "../Actions"; import { SellShares } from "../../Actions";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IProps { interface IProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
@ -33,19 +33,22 @@ export function SellSharesModal(props: IProps): React.ReactElement {
function ProfitIndicator(props: { shares: number | null; corp: ICorporation }): React.ReactElement { function ProfitIndicator(props: { shares: number | null; corp: ICorporation }): React.ReactElement {
if (props.shares === null) return <></>; if (props.shares === null) return <></>;
let text = "";
if (isNaN(props.shares) || props.shares <= 0) { if (isNaN(props.shares) || props.shares <= 0) {
return <>ERROR: Invalid value entered for number of shares to sell</>; text = `ERROR: Invalid value entered for number of shares to sell`;
} else if (props.shares > corp.numShares) { } else if (props.shares > corp.numShares) {
return <>You don't have this many shares to sell!</>; text = `You don't have this many shares to sell!`;
} else { } else {
const stockSaleResults = corp.calculateShareSale(props.shares); const stockSaleResults = corp.calculateShareSale(props.shares);
const profit = stockSaleResults[0]; const profit = stockSaleResults[0];
return ( text = `Sell ${props.shares} shares for a total of ${numeralWrapper.formatMoney(profit)}`;
<>
Sell {props.shares} shares for a total of {numeralWrapper.formatMoney(profit)}
</>
);
} }
return (
<Typography>
<small>{text}</small>
</Typography>
);
} }
function sell(): void { function sell(): void {
@ -84,7 +87,6 @@ export function SellSharesModal(props: IProps): React.ReactElement {
<br /> <br />
The current price of your company's stock is {numeralWrapper.formatMoney(corp.sharePrice)} The current price of your company's stock is {numeralWrapper.formatMoney(corp.sharePrice)}
</Typography> </Typography>
<ProfitIndicator shares={shares} corp={corp} />
<br /> <br />
<TextField <TextField
variant="standard" variant="standard"
@ -97,6 +99,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
<Button disabled={disabled} onClick={sell} sx={{ mx: 1 }}> <Button disabled={disabled} onClick={sell} sx={{ mx: 1 }}>
Sell shares Sell shares
</Button> </Button>
<ProfitIndicator shares={shares} corp={corp} />
</Modal> </Modal>
); );
} }

@ -1,11 +1,11 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../../Warehouse";
import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../Actions"; import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../../Actions";
import { Material } from "../Material"; import { Material } from "../../Material";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "./Context"; import { useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";

@ -1,16 +1,16 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../../OfficeSpace";
import { ThrowParty } from "../Actions"; import { ThrowParty } from "../../Actions";
import { Money } from "../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
interface IProps { interface IProps {
open: boolean; open: boolean;

@ -1,11 +1,11 @@
import React from "react"; import React from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../../ui/numeralFormat";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../../OfficeSpace";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../../ICorporation";
import { UpgradeOfficeSize } from "../Actions"; import { UpgradeOfficeSize } from "../../Actions";
import { Modal } from "../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation } from "./Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";

@ -17,7 +17,7 @@ export function DummyGrid(props: IProps): React.ReactElement {
const ghostGrid = zeros([props.width, props.height]); const ghostGrid = zeros([props.width, props.height]);
return ( return (
<Box> <Box>
<Table> <Table sx={{ width: props.width, height: props.height }}>
<Grid <Grid
width={props.width} width={props.width}
height={props.height} height={props.height}

@ -66,6 +66,7 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
user, Stat Fragments increase the efficacy of adjacent Stat Fragments by 10%, and do not need to be user, Stat Fragments increase the efficacy of adjacent Stat Fragments by 10%, and do not need to be
charged. charged.
</Typography> </Typography>
<br />
<DummyGrid <DummyGrid
width={4} width={4}
@ -86,11 +87,12 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
]} ]}
/> />
<Typography sx={{ fontStyle: "italic" }}> <Typography sx={{ fontStyle: "italic" }}>
This boost provides a bonus to the touching fragment This Booster Fragment provides a bonus to the adjacent Stat Fragment.
</Typography> </Typography>
<br />
<DummyGrid <DummyGrid
width={4} width={3}
height={4} height={4}
fragments={[ fragments={[
new ActiveFragment({ new ActiveFragment({
@ -108,8 +110,10 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
]} ]}
/> />
<Typography sx={{ fontStyle: "italic" }}> <Typography sx={{ fontStyle: "italic" }}>
Even though the booster touches many tiles, the bonus is only applied once. Even though the Booster Fragment touches the Stat Fragment in multiple places, the bonus is only
applied once.
</Typography> </Typography>
<br />
<DummyGrid <DummyGrid
width={4} width={4}
@ -122,16 +126,17 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
fragment: Fragments.find((f) => f.id === 5) ?? Fragments[0], fragment: Fragments.find((f) => f.id === 5) ?? Fragments[0],
}), }),
new ActiveFragment({ new ActiveFragment({
x: 2, x: 1,
y: 0, y: 1,
rotation: 0, rotation: 0,
fragment: Fragments.find((f) => f.id === 105) ?? Fragments[0], fragment: Fragments.find((f) => f.id === 105) ?? Fragments[0],
}), }),
]} ]}
/> />
<Typography sx={{ fontStyle: "italic" }}> <Typography sx={{ fontStyle: "italic" }}>
Even though the booster touches many tiles, the bonus is only applied once. This Booster Fragment does nothing, as it is not touching a Stat Fragment.
</Typography> </Typography>
<br />
<DummyGrid <DummyGrid
width={4} width={4}
@ -158,10 +163,10 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
]} ]}
/> />
<Typography sx={{ fontStyle: "italic" }}> <Typography sx={{ fontStyle: "italic" }}>
This booster provides bonus to all fragment it touches. This Booster Fragment provides a bonus to both Stat Fragments it's touching.
</Typography> </Typography>
<br /> <br />
<Typography> <Typography>
Stat Fragments are charged using the stanek.chargeFragment(rootX, rootY) NetScript API function. The 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 charging process ordinarily takes 1000ms to complete, but only takes 200ms during bonus time. When the

@ -10,6 +10,10 @@ import Button from "@mui/material/Button";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { MenuItem, SelectChangeEvent, TextField, Select } from "@mui/material";
import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { GangConstants } from "../../Gang/data/Constants";
import { FactionNames } from "../../Faction/data/FactionNames";
import { checkForMessagesToSend } from "../../Message/MessageHelpers"; import { checkForMessagesToSend } from "../../Message/MessageHelpers";
interface IProps { interface IProps {
@ -19,6 +23,8 @@ interface IProps {
export function General(props: IProps): React.ReactElement { export function General(props: IProps): React.ReactElement {
const [error, setError] = useState(false); const [error, setError] = useState(false);
const [corporationName, setCorporationName] = useState("");
const [gangFaction, setGangFaction] = useState("");
function addMoney(n: number) { function addMoney(n: number) {
return function () { return function () {
@ -46,6 +52,23 @@ export function General(props: IProps): React.ReactElement {
props.router.toBitVerse(false, false); props.router.toBitVerse(false, false);
} }
function createCorporation(): void {
props.player.startCorporation(corporationName);
}
function joinBladeburner(): void {
props.player.bladeburner = new Bladeburner(props.player);
}
function startGang(): void {
const isHacking = gangFaction === FactionNames.NiteSec || gangFaction === FactionNames.TheBlackHand;
props.player.startGang(gangFaction, isHacking);
}
function setGangFactionDropdown(event: SelectChangeEvent<string>): void {
setGangFaction(event.target.value);
}
function checkMessages(): void { function checkMessages(): void {
checkForMessagesToSend(); checkForMessagesToSend();
} }
@ -87,6 +110,22 @@ export function General(props: IProps): React.ReactElement {
</Button> </Button>
<Button onClick={upgradeRam}>+ RAM</Button> <Button onClick={upgradeRam}>+ RAM</Button>
<br /> <br />
<Typography>Corporation Name:</Typography>
<TextField value={corporationName} onChange={(x) => setCorporationName(x.target.value)} />
<Button onClick={createCorporation}>Create Corporation</Button>
<br />
<Typography>Gang Faction:</Typography>
<Select value={gangFaction} onChange={setGangFactionDropdown}>
{GangConstants.Names.map((factionName) => (
<MenuItem key={factionName} value={factionName}>
{factionName}
</MenuItem>
))}
</Select>
<Button onClick={startGang}>Start Gang</Button>
<br />
<Button onClick={joinBladeburner}>Join BladeBurner</Button>
<br />
<Button onClick={quickB1tFlum3}>Quick b1t_flum3.exe</Button> <Button onClick={quickB1tFlum3}>Quick b1t_flum3.exe</Button>
<Button onClick={b1tflum3}>Run b1t_flum3.exe</Button> <Button onClick={b1tflum3}>Run b1t_flum3.exe</Button>

@ -15,7 +15,6 @@ import {
getFactionSecurityWorkRepGain, getFactionSecurityWorkRepGain,
getFactionFieldWorkRepGain, getFactionFieldWorkRepGain,
} from "../PersonObjects/formulas/reputation"; } from "../PersonObjects/formulas/reputation";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal"; import { InvitationEvent } from "./ui/InvitationModal";
@ -120,13 +119,14 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
aug.baseCost = 750e3 * mult * BitNodeMultipliers.AugmentationMoneyCost; aug.baseCost = 750e3 * mult * BitNodeMultipliers.AugmentationMoneyCost;
for (let i = 0; i < Player.queuedAugmentations.length - 1; ++i) { for (let i = 0; i < Player.queuedAugmentations.length - 1; ++i) {
aug.baseCost *= CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]]; aug.baseCost *= CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
} }
} }
for (const name of Object.keys(Augmentations)) { for (const name of Object.keys(Augmentations)) {
if (Augmentations.hasOwnProperty(name)) { if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]]; Augmentations[name].baseCost *=
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
} }
} }

@ -1,6 +1,8 @@
import React from "react"; import React from "react";
import { IMap } from "../types"; import { IMap } from "../types";
import { FactionNames } from "./data/FactionNames"; import { FactionNames } from "./data/FactionNames";
import { use } from "../ui/Context";
import { Option } from "./ui/Option";
interface FactionInfoParams { interface FactionInfoParams {
infoText?: JSX.Element; infoText?: JSX.Element;
@ -10,6 +12,7 @@ interface FactionInfoParams {
offerSecurityWork?: boolean; offerSecurityWork?: boolean;
special?: boolean; special?: boolean;
keepOnInstall?: boolean; keepOnInstall?: boolean;
assignment?: () => React.ReactElement;
} }
/** /**
@ -51,6 +54,11 @@ export class FactionInfo {
*/ */
special: boolean; special: boolean;
/**
* The data to display on the faction screen.
*/
assignment?: () => React.ReactElement;
constructor(params: FactionInfoParams) { constructor(params: FactionInfoParams) {
this.infoText = params.infoText ?? <></>; this.infoText = params.infoText ?? <></>;
this.enemies = params.enemies ?? []; this.enemies = params.enemies ?? [];
@ -60,6 +68,7 @@ export class FactionInfo {
this.keep = params.keepOnInstall ?? false; this.keep = params.keepOnInstall ?? false;
this.special = params.special ?? false; this.special = params.special ?? false;
this.assignment = params.assignment;
} }
offersWork(): boolean { offersWork(): boolean {
@ -438,11 +447,21 @@ export const FactionInfos: IMap<FactionInfo> = {
), ),
special: true, special: true,
assignment: (): React.ReactElement => {
const router = use.Router();
return (
<Option
buttonText={"Open Bladeburner headquarters"}
infoText={"You can gain reputation with bladeburner by completing contracts and operations."}
onClick={() => router.toBladeburner()}
/>
);
},
}), }),
// prettier-ignore
[FactionNames.ChurchOfTheMachineGod]: new FactionInfo({ [FactionNames.ChurchOfTheMachineGod]: new FactionInfo({
infoText:(<> // prettier-ignore
infoText:(<>
{" `` "}<br /> {" `` "}<br />
{" -odmmNmds: "}<br /> {" -odmmNmds: "}<br />
{" `hNmo:..-omNh. "}<br /> {" `hNmo:..-omNh. "}<br />
@ -472,13 +491,24 @@ export const FactionInfos: IMap<FactionInfo> = {
{" -smNNNNmdo- "}<br /> {" -smNNNNmdo- "}<br />
{" `..` "}<br /><br /> {" `..` "}<br /><br />
Many cultures predict an end to humanity in the near future, a final Many cultures predict an end to humanity in the near future, a final
Armageddon that will end the world; but we disagree. Armageddon that will end the world; but we disagree.</>),
<br /><br />Note that for this faction, reputation can offerHackingWork: false,
only be gained by charging Stanek's gift.</>), offerFieldWork: false,
offerHackingWork: false, offerSecurityWork: false,
offerFieldWork: false, special: true,
offerSecurityWork: false, keepOnInstall: true,
special: true, assignment: (): React.ReactElement => {
keepOnInstall: true, const router = use.Router();
return (
<Option
buttonText={"Open Staneks Gift"}
infoText={
"Stanek's Gift is a powerful augmentation that powers up the stat you chose to boost." +
"Gaining reputation with the Church of the Machine God can only be done by charging the gift."
}
onClick={() => router.toStaneksGift()}
/>
);
},
}), }),
}; };

@ -31,6 +31,17 @@ const useStyles = makeStyles(() =>
}), }),
); );
function DefaultAssignment(): React.ReactElement {
return (
<Typography>
Perform work/carry out assignments for your faction to help further its cause! By doing so you will earn
reputation for your faction. You will also gain reputation passively over time, although at a very slow rate.
Earning reputation will allow you to purchase Augmentations through this faction, which are powerful upgrades that
enhance your abilities.
</Typography>
);
}
export function Info(props: IProps): React.ReactElement { export function Info(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -44,6 +55,8 @@ export function Info(props: IProps): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
const Assignment = props.factionInfo.assignment ?? DefaultAssignment;
const favorGain = props.faction.getFavorGain(); const favorGain = props.faction.getFavorGain();
return ( return (
<> <>
@ -94,12 +107,7 @@ export function Info(props: IProps): React.ReactElement {
</Box> </Box>
<Typography>-------------------------</Typography> <Typography>-------------------------</Typography>
<Typography> <Assignment />
Perform work/carry out assignments for your faction to help further its cause! By doing so you will earn
reputation for your faction. You will also gain reputation passively over time, although at a very slow rate.
Earning reputation will allow you to purchase Augmentations through this faction, which are powerful upgrades
that enhance your abilities.
</Typography>
</> </>
); );
} }

@ -252,6 +252,7 @@ export class Gang implements IGang {
const total = Object.values(AllGangs) const total = Object.values(AllGangs)
.map((g) => g.territory) .map((g) => g.territory)
.reduce((p, c) => p + c, 0); .reduce((p, c) => p + c, 0);
console.log(total);
Object.values(AllGangs).forEach((g) => (g.territory /= total)); Object.values(AllGangs).forEach((g) => (g.territory /= total));
} }
} }

@ -21,12 +21,11 @@ import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../InteractiveTuto
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { Server } from "../Server/Server"; import { Server } from "../Server/Server";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
// Returns a boolean indicating whether the player has Hacknet Servers // Returns a boolean indicating whether the player has Hacknet Servers
// (the upgraded form of Hacknet Nodes) // (the upgraded form of Hacknet Nodes)
export function hasHacknetServers(player: IPlayer): boolean { export function hasHacknetServers(player: IPlayer): boolean {
return player.bitNodeN === 9 || SourceFileFlags[9] > 0; return player.bitNodeN === 9 || player.sourceFileLvl(9) > 0;
} }
export function purchaseHacknet(player: IPlayer): number { export function purchaseHacknet(player: IPlayer): number {

@ -15,7 +15,7 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Location } from "../Location"; import { Location } from "../Location";
import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal"; import { CreateCorporationModal } from "../../Corporation/ui/modals/CreateCorporationModal";
import { LocationName } from "../data/LocationNames"; import { LocationName } from "../data/LocationNames";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
@ -34,6 +34,7 @@ import { HacknetServer } from "../../Hacknet/HacknetServer";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot"; import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
type IProps = { type IProps = {
loc: Location; loc: Location;
@ -316,7 +317,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
return renderGrafting(); return renderGrafting();
} }
case LocationName.Sector12CityHall: { case LocationName.Sector12CityHall: {
return <CreateCorporation />; return (BitNodeMultipliers.CorporationSoftCap < 0.15 && <></>) || <CreateCorporation />;
} }
case LocationName.Sector12NSA: { case LocationName.Sector12NSA: {
return renderBladeburner(); return renderBladeburner();

@ -177,6 +177,8 @@ const singularity: IMap<any> = {
installAugmentations: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), installAugmentations: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
isFocused: SF4Cost(0.1), isFocused: SF4Cost(0.1),
setFocus: SF4Cost(0.1), setFocus: SF4Cost(0.1),
b1tflum3: SF4Cost(16),
destroyW0r1dD43m0n: SF4Cost(32),
}; };
// Gang API // Gang API

@ -40,7 +40,6 @@ import {
} from "./Server/ServerHelpers"; } from "./Server/ServerHelpers";
import { getPurchaseServerCost, getPurchaseServerLimit, getPurchaseServerMaxRam } from "./Server/ServerPurchases"; import { getPurchaseServerCost, getPurchaseServerLimit, getPurchaseServerMaxRam } from "./Server/ServerPurchases";
import { Server } from "./Server/Server"; import { Server } from "./Server/Server";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { influenceStockThroughServerHack, influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing"; import { influenceStockThroughServerHack, influenceStockThroughServerGrow } from "./StockMarket/PlayerInfluencing";
import { isValidFilePath, removeLeadingSlash } from "./Terminal/DirectoryHelpers"; import { isValidFilePath, removeLeadingSlash } from "./Terminal/DirectoryHelpers";
@ -1548,7 +1547,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
}, },
getBitNodeMultipliers: function (): IBNMults { getBitNodeMultipliers: function (): IBNMults {
updateDynamicRam("getBitNodeMultipliers", getRamCost(Player, "getBitNodeMultipliers")); updateDynamicRam("getBitNodeMultipliers", getRamCost(Player, "getBitNodeMultipliers"));
if (SourceFileFlags[5] <= 0 && Player.bitNodeN !== 5) { if (Player.sourceFileLvl(5) <= 0 && Player.bitNodeN !== 5) {
throw makeRuntimeErrorMsg("getBitNodeMultipliers", "Requires Source-File 5 to run."); throw makeRuntimeErrorMsg("getBitNodeMultipliers", "Requires Source-File 5 to run.");
} }
const copy = Object.assign({}, BitNodeMultipliers); const copy = Object.assign({}, BitNodeMultipliers);

@ -53,6 +53,9 @@ import {
SellShares, SellShares,
BuyBackShares, BuyBackShares,
SetSmartSupplyUseLeftovers, SetSmartSupplyUseLeftovers,
LimitMaterialProduction,
LimitProductProduction,
UpgradeWarehouseCost,
} from "../Corporation/Actions"; } from "../Corporation/Actions";
import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades"; import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades";
import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades"; import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades";
@ -64,6 +67,7 @@ import { CorporationConstants } from "../Corporation/data/Constants";
import { IndustryUpgrades } from "../Corporation/IndustryUpgrades"; import { IndustryUpgrades } from "../Corporation/IndustryUpgrades";
import { ResearchMap } from "../Corporation/ResearchMap"; import { ResearchMap } from "../Corporation/ResearchMap";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
export function NetscriptCorporation( export function NetscriptCorporation(
player: IPlayer, player: IPlayer,
@ -74,6 +78,8 @@ export function NetscriptCorporation(
if (!player.canAccessCorporation() || player.hasCorporation()) return false; if (!player.canAccessCorporation() || player.hasCorporation()) return false;
if (!corporationName) return false; if (!corporationName) return false;
if (player.bitNodeN !== 3 && !selfFund) throw new Error("cannot use seed funds outside of BitNode 3"); if (player.bitNodeN !== 3 && !selfFund) throw new Error("cannot use seed funds outside of BitNode 3");
if (BitNodeMultipliers.CorporationSoftCap < 0.15)
throw new Error(`You cannot create a corporation in Bitnode ${player.bitNodeN}`);
if (selfFund) { if (selfFund) {
if (!player.canAfford(150e9)) return false; if (!player.canAfford(150e9)) return false;
@ -88,35 +94,35 @@ export function NetscriptCorporation(
function hasUnlockUpgrade(upgradeName: string): boolean { function hasUnlockUpgrade(upgradeName: string): boolean {
const corporation = getCorporation(); const corporation = getCorporation();
const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName); const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade.name === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
const upgN = upgrade[0]; const upgN = upgrade.index;
return corporation.unlockUpgrades[upgN] === 1; return corporation.unlockUpgrades[upgN] === 1;
} }
function getUnlockUpgradeCost(upgradeName: string): number { function getUnlockUpgradeCost(upgradeName: string): number {
const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName); const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade.name === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
return upgrade[1]; return upgrade.price;
} }
function getUpgradeLevel(_upgradeName: string): number { function getUpgradeLevel(_upgradeName: string): number {
const upgradeName = helper.string("levelUpgrade", "upgradeName", _upgradeName); const upgradeName = helper.string("levelUpgrade", "upgradeName", _upgradeName);
const corporation = getCorporation(); const corporation = getCorporation();
const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName); const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade.name === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
const upgN = upgrade[0]; const upgN = upgrade.index;
return corporation.upgrades[upgN]; return corporation.upgrades[upgN];
} }
function getUpgradeLevelCost(_upgradeName: string): number { function getUpgradeLevelCost(_upgradeName: string): number {
const upgradeName = helper.string("levelUpgrade", "upgradeName", _upgradeName); const upgradeName = helper.string("levelUpgrade", "upgradeName", _upgradeName);
const corporation = getCorporation(); const corporation = getCorporation();
const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName); const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade.name === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
const upgN = upgrade[0]; const upgN = upgrade.index;
const baseCost = upgrade[1]; const baseCost = upgrade.basePrice;
const priceMult = upgrade[2]; const priceMult = upgrade.priceMult;
const level = corporation.upgrades[upgN]; const level = corporation.upgrades[upgN];
return baseCost * Math.pow(priceMult, level); return baseCost * Math.pow(priceMult, level);
} }
@ -311,12 +317,16 @@ export function NetscriptCorporation(
checkAccess("getPurchaseWarehouseCost", 7); checkAccess("getPurchaseWarehouseCost", 7);
return CorporationConstants.WarehouseInitialCost; return CorporationConstants.WarehouseInitialCost;
}, },
getUpgradeWarehouseCost: function (_divisionName: unknown, _cityName: unknown): number { getUpgradeWarehouseCost: function (_divisionName: unknown, _cityName: unknown, _amt: unknown = 1): number {
checkAccess("upgradeWarehouse", 7); checkAccess("upgradeWarehouse", 7);
const divisionName = helper.string("getUpgradeWarehouseCost", "divisionName", _divisionName); const divisionName = helper.string("getUpgradeWarehouseCost", "divisionName", _divisionName);
const cityName = helper.city("getUpgradeWarehouseCost", "cityName", _cityName); const cityName = helper.city("getUpgradeWarehouseCost", "cityName", _cityName);
const amt = helper.number("getUpgradeWarehouseCost", "amount", _amt);
if (amt < 1) {
throw helper.makeRuntimeErrorMsg(`corporation.getUpgradeWarehouseCost`, "You must provide a positive number");
}
const warehouse = getWarehouse(divisionName, cityName); const warehouse = getWarehouse(divisionName, cityName);
return CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, warehouse.level + 1); return UpgradeWarehouseCost(warehouse, amt);
}, },
hasWarehouse: function (_divisionName: unknown, _cityName: unknown): boolean { hasWarehouse: function (_divisionName: unknown, _cityName: unknown): boolean {
checkAccess("hasWarehouse", 7); checkAccess("hasWarehouse", 7);
@ -348,6 +358,7 @@ export function NetscriptCorporation(
const material = getMaterial(divisionName, cityName, materialName); const material = getMaterial(divisionName, cityName, materialName);
const corporation = getCorporation(); const corporation = getCorporation();
return { return {
cost: material.bCost,
name: material.name, name: material.name,
qty: material.qty, qty: material.qty,
qlt: material.qlt, qlt: material.qlt,
@ -389,12 +400,16 @@ export function NetscriptCorporation(
const corporation = getCorporation(); const corporation = getCorporation();
PurchaseWarehouse(corporation, getDivision(divisionName), cityName); PurchaseWarehouse(corporation, getDivision(divisionName), cityName);
}, },
upgradeWarehouse: function (_divisionName: unknown, _cityName: unknown): void { upgradeWarehouse: function (_divisionName: unknown, _cityName: unknown, _amt: unknown = 1): void {
checkAccess("upgradeWarehouse", 7); checkAccess("upgradeWarehouse", 7);
const divisionName = helper.string("upgradeWarehouse", "divisionName", _divisionName); const divisionName = helper.string("upgradeWarehouse", "divisionName", _divisionName);
const cityName = helper.city("upgradeWarehouse", "cityName", _cityName); const cityName = helper.city("upgradeWarehouse", "cityName", _cityName);
const amt = helper.number("upgradeWarehouse", "amount", _amt);
const corporation = getCorporation(); const corporation = getCorporation();
UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName)); if (amt < 1) {
throw helper.makeRuntimeErrorMsg(`corporation.upgradeWarehouse`, "You must provide a positive number");
}
UpgradeWarehouse(corporation, getDivision(divisionName), getWarehouse(divisionName, cityName), amt);
}, },
sellMaterial: function ( sellMaterial: function (
_divisionName: unknown, _divisionName: unknown,
@ -508,6 +523,19 @@ export function NetscriptCorporation(
const corporation = getCorporation(); const corporation = getCorporation();
MakeProduct(corporation, getDivision(divisionName), cityName, productName, designInvest, marketingInvest); MakeProduct(corporation, getDivision(divisionName), cityName, productName, designInvest, marketingInvest);
}, },
limitProductProduction: function (
_divisionName: unknown,
_productName: unknown,
_cityName: unknown,
_qty: unknown,
) {
checkAccess("limitProductProduction", 7);
const divisionName = helper.string("limitProductProduction", "divisionName", _divisionName);
const cityName = helper.city("limitMaterialProduction", "cityName", _cityName);
const productName = helper.string("limitProductProduction", "productName", _productName);
const qty = helper.number("limitMaterialProduction", "qty", _qty);
LimitProductProduction(getProduct(divisionName, productName), cityName, qty);
},
exportMaterial: function ( exportMaterial: function (
_sourceDivision: unknown, _sourceDivision: unknown,
_sourceCity: unknown, _sourceCity: unknown,
@ -548,6 +576,19 @@ export function NetscriptCorporation(
const amt = helper.string("cancelExportMaterial", "amt", _amt); const amt = helper.string("cancelExportMaterial", "amt", _amt);
CancelExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + ""); CancelExportMaterial(targetDivision, targetCity, getMaterial(sourceDivision, sourceCity, materialName), amt + "");
}, },
limitMaterialProduction: function (
_divisionName: unknown,
_cityName: unknown,
_materialName: unknown,
_qty: unknown,
) {
checkAccess("limitMaterialProduction", 7);
const divisionName = helper.string("limitMaterialProduction", "divisionName", _divisionName);
const cityName = helper.city("limitMaterialProduction", "cityName", _cityName);
const materialName = helper.string("limitMaterialProduction", "materialName", _materialName);
const qty = helper.number("limitMaterialProduction", "qty", _qty);
LimitMaterialProduction(getMaterial(divisionName, cityName, materialName), qty);
},
setMaterialMarketTA1: function ( setMaterialMarketTA1: function (
_divisionName: unknown, _divisionName: unknown,
_cityName: unknown, _cityName: unknown,
@ -820,7 +861,7 @@ export function NetscriptCorporation(
checkAccess("unlockUpgrade"); checkAccess("unlockUpgrade");
const upgradeName = helper.string("unlockUpgrade", "upgradeName", _upgradeName); const upgradeName = helper.string("unlockUpgrade", "upgradeName", _upgradeName);
const corporation = getCorporation(); const corporation = getCorporation();
const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade[2] === upgradeName); const upgrade = Object.values(CorporationUnlockUpgrades).find((upgrade) => upgrade.name === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
UnlockUpgrade(corporation, upgrade); UnlockUpgrade(corporation, upgrade);
}, },
@ -828,7 +869,7 @@ export function NetscriptCorporation(
checkAccess("levelUpgrade"); checkAccess("levelUpgrade");
const upgradeName = helper.string("levelUpgrade", "upgradeName", _upgradeName); const upgradeName = helper.string("levelUpgrade", "upgradeName", _upgradeName);
const corporation = getCorporation(); const corporation = getCorporation();
const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade[4] === upgradeName); const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade.name === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
LevelUpgrade(corporation, upgrade); LevelUpgrade(corporation, upgrade);
}, },

@ -3,11 +3,13 @@ import { IPlayer } from "../PersonObjects/IPlayer";
import { Exploit } from "../Exploits/Exploit"; import { Exploit } from "../Exploits/Exploit";
import * as bcrypt from "bcryptjs"; import * as bcrypt from "bcryptjs";
import { INetscriptHelper } from "./INetscriptHelper"; import { INetscriptHelper } from "./INetscriptHelper";
import { Apr1Events as devMenu } from "../ui/Apr1";
export interface INetscriptExtra { export interface INetscriptExtra {
heart: { heart: {
break(): number; break(): number;
}; };
openDevMenu(): void;
exploit(): void; exploit(): void;
bypass(doc: Document): void; bypass(doc: Document): void;
alterReality(): void; alterReality(): void;
@ -22,6 +24,9 @@ export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, help
return player.karma; return player.karma;
}, },
}, },
openDevMenu: function (): void {
devMenu.emit();
},
exploit: function (): void { exploit: function (): void {
player.giveExploit(Exploit.UndocumentedFunctionCall); player.giveExploit(Exploit.UndocumentedFunctionCall);
}, },

@ -47,6 +47,8 @@ import { Server } from "../Server/Server";
import { netscriptCanHack } from "../Hacking/netscriptCanHack"; import { netscriptCanHack } from "../Hacking/netscriptCanHack";
import { FactionInfos } from "../Faction/FactionInfo"; import { FactionInfos } from "../Faction/FactionInfo";
import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
import { enterBitNode } from "../RedPill";
export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI<ISingularity> { export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI<ISingularity> {
const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation { const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation {
@ -94,8 +96,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
return { return {
getOwnedAugmentations: (_ctx: NetscriptContext) => getOwnedAugmentations: (_ctx: NetscriptContext) =>
function (_purchased: unknown = false): string[] { function (_purchased: unknown = false): string[] {
const purchased = _ctx.helper.boolean(_purchased);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const purchased = _ctx.helper.boolean(_purchased);
const res = []; const res = [];
for (let i = 0; i < player.augmentations.length; ++i) { for (let i = 0; i < player.augmentations.length; ++i) {
res.push(player.augmentations[i].name); res.push(player.augmentations[i].name);
@ -109,52 +111,52 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
getAugmentationsFromFaction: (_ctx: NetscriptContext) => getAugmentationsFromFaction: (_ctx: NetscriptContext) =>
function (_facName: unknown): string[] { function (_facName: unknown): string[] {
const facName = _ctx.helper.string("facName", _facName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName);
const faction = getFaction(_ctx, facName); const faction = getFaction(_ctx, facName);
return getFactionAugmentationsFiltered(player, faction); return getFactionAugmentationsFiltered(player, faction);
}, },
getAugmentationCost: (_ctx: NetscriptContext) => getAugmentationCost: (_ctx: NetscriptContext) =>
function (_augName: unknown): [number, number] { function (_augName: unknown): [number, number] {
const augName = _ctx.helper.string("augName", _augName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const augName = _ctx.helper.string("augName", _augName);
const aug = getAugmentation(_ctx, augName); const aug = getAugmentation(_ctx, augName);
return [aug.baseRepRequirement, aug.baseCost]; return [aug.baseRepRequirement, aug.baseCost];
}, },
getAugmentationPrereq: (_ctx: NetscriptContext) => getAugmentationPrereq: (_ctx: NetscriptContext) =>
function (_augName: unknown): string[] { function (_augName: unknown): string[] {
const augName = _ctx.helper.string("augName", _augName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const augName = _ctx.helper.string("augName", _augName);
const aug = getAugmentation(_ctx, augName); const aug = getAugmentation(_ctx, augName);
return aug.prereqs.slice(); return aug.prereqs.slice();
}, },
getAugmentationPrice: (_ctx: NetscriptContext) => getAugmentationPrice: (_ctx: NetscriptContext) =>
function (_augName: unknown): number { function (_augName: unknown): number {
const augName = _ctx.helper.string("augName", _augName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const augName = _ctx.helper.string("augName", _augName);
const aug = getAugmentation(_ctx, augName); const aug = getAugmentation(_ctx, augName);
return aug.baseCost; return aug.baseCost;
}, },
getAugmentationRepReq: (_ctx: NetscriptContext) => getAugmentationRepReq: (_ctx: NetscriptContext) =>
function (_augName: unknown): number { function (_augName: unknown): number {
const augName = _ctx.helper.string("augName", _augName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const augName = _ctx.helper.string("augName", _augName);
const aug = getAugmentation(_ctx, augName); const aug = getAugmentation(_ctx, augName);
return aug.baseRepRequirement; return aug.baseRepRequirement;
}, },
getAugmentationStats: (_ctx: NetscriptContext) => getAugmentationStats: (_ctx: NetscriptContext) =>
function (_augName: unknown): AugmentationStats { function (_augName: unknown): AugmentationStats {
const augName = _ctx.helper.string("augName", _augName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const augName = _ctx.helper.string("augName", _augName);
const aug = getAugmentation(_ctx, augName); const aug = getAugmentation(_ctx, augName);
return Object.assign({}, aug.mults); return Object.assign({}, aug.mults);
}, },
purchaseAugmentation: (_ctx: NetscriptContext) => purchaseAugmentation: (_ctx: NetscriptContext) =>
function (_facName: unknown, _augName: unknown): boolean { function (_facName: unknown, _augName: unknown): boolean {
_ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName); const facName = _ctx.helper.string("facName", _facName);
const augName = _ctx.helper.string("augName", _augName); const augName = _ctx.helper.string("augName", _augName);
_ctx.helper.checkSingularityAccess();
const fac = getFaction(_ctx, facName); const fac = getFaction(_ctx, facName);
const aug = getAugmentation(_ctx, augName); const aug = getAugmentation(_ctx, augName);
@ -200,8 +202,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
softReset: (_ctx: NetscriptContext) => softReset: (_ctx: NetscriptContext) =>
function (_cbScript: unknown = ""): void { function (_cbScript: unknown = ""): void {
const cbScript = _ctx.helper.string("cbScript", _cbScript);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const cbScript = _ctx.helper.string("cbScript", _cbScript);
workerScript.log("softReset", () => "Soft resetting. This will cause this script to be killed"); workerScript.log("softReset", () => "Soft resetting. This will cause this script to be killed");
setTimeout(() => { setTimeout(() => {
@ -215,8 +217,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
installAugmentations: (_ctx: NetscriptContext) => installAugmentations: (_ctx: NetscriptContext) =>
function (_cbScript: unknown = ""): boolean { function (_cbScript: unknown = ""): boolean {
const cbScript = _ctx.helper.string("cbScript", _cbScript);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const cbScript = _ctx.helper.string("cbScript", _cbScript);
if (player.queuedAugmentations.length === 0) { if (player.queuedAugmentations.length === 0) {
workerScript.log("installAugmentations", () => "You do not have any Augmentations to be installed."); workerScript.log("installAugmentations", () => "You do not have any Augmentations to be installed.");
@ -239,8 +241,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
goToLocation: (_ctx: NetscriptContext) => goToLocation: (_ctx: NetscriptContext) =>
function (_locationName: unknown): boolean { function (_locationName: unknown): boolean {
const locationName = _ctx.helper.string("locationName", _locationName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const locationName = _ctx.helper.string("locationName", _locationName);
const location = Object.values(Locations).find((l) => l.name === locationName); const location = Object.values(Locations).find((l) => l.name === locationName);
if (!location) { if (!location) {
workerScript.log("goToLocation", () => `No location named ${locationName}`); workerScript.log("goToLocation", () => `No location named ${locationName}`);
@ -256,10 +258,10 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
universityCourse: (_ctx: NetscriptContext) => universityCourse: (_ctx: NetscriptContext) =>
function (_universityName: unknown, _className: unknown, _focus: unknown = true): boolean { function (_universityName: unknown, _className: unknown, _focus: unknown = true): boolean {
_ctx.helper.checkSingularityAccess();
const universityName = _ctx.helper.string("universityName", _universityName); const universityName = _ctx.helper.string("universityName", _universityName);
const className = _ctx.helper.string("className", _className); const className = _ctx.helper.string("className", _className);
const focus = _ctx.helper.boolean(_focus); const focus = _ctx.helper.boolean(_focus);
_ctx.helper.checkSingularityAccess();
const wasFocusing = player.focus; const wasFocusing = player.focus;
if (player.isWorking) { if (player.isWorking) {
const txt = player.singularityStopWork(); const txt = player.singularityStopWork();
@ -347,10 +349,10 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
gymWorkout: (_ctx: NetscriptContext) => gymWorkout: (_ctx: NetscriptContext) =>
function (_gymName: unknown, _stat: unknown, _focus: unknown = true): boolean { function (_gymName: unknown, _stat: unknown, _focus: unknown = true): boolean {
_ctx.helper.checkSingularityAccess();
const gymName = _ctx.helper.string("gymName", _gymName); const gymName = _ctx.helper.string("gymName", _gymName);
const stat = _ctx.helper.string("stat", _stat); const stat = _ctx.helper.string("stat", _stat);
const focus = _ctx.helper.boolean(_focus); const focus = _ctx.helper.boolean(_focus);
_ctx.helper.checkSingularityAccess();
const wasFocusing = player.focus; const wasFocusing = player.focus;
if (player.isWorking) { if (player.isWorking) {
const txt = player.singularityStopWork(); const txt = player.singularityStopWork();
@ -462,8 +464,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
travelToCity: (_ctx: NetscriptContext) => travelToCity: (_ctx: NetscriptContext) =>
function (_cityName: unknown): boolean { function (_cityName: unknown): boolean {
const cityName = _ctx.helper.city("cityName", _cityName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const cityName = _ctx.helper.city("cityName", _cityName);
switch (cityName) { switch (cityName) {
case CityName.Aevum: case CityName.Aevum:
@ -520,8 +522,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
purchaseProgram: (_ctx: NetscriptContext) => purchaseProgram: (_ctx: NetscriptContext) =>
function (_programName: unknown): boolean { function (_programName: unknown): boolean {
const programName = _ctx.helper.string("programName", _programName).toLowerCase();
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const programName = _ctx.helper.string("programName", _programName).toLowerCase();
if (!player.hasTorRouter()) { if (!player.hasTorRouter()) {
workerScript.log("purchaseProgram", () => "You do not have the TOR router."); workerScript.log("purchaseProgram", () => "You do not have the TOR router.");
@ -569,8 +571,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
connect: (_ctx: NetscriptContext) => connect: (_ctx: NetscriptContext) =>
function (_hostname: unknown): boolean { function (_hostname: unknown): boolean {
const hostname = _ctx.helper.string("hostname", _hostname);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const hostname = _ctx.helper.string("hostname", _hostname);
if (!hostname) { if (!hostname) {
throw _ctx.helper.makeRuntimeErrorMsg(`Invalid hostname: '${hostname}'`); throw _ctx.helper.makeRuntimeErrorMsg(`Invalid hostname: '${hostname}'`);
} }
@ -662,8 +664,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
setFocus: (_ctx: NetscriptContext) => setFocus: (_ctx: NetscriptContext) =>
function (_focus: unknown): boolean { function (_focus: unknown): boolean {
const focus = _ctx.helper.boolean(_focus);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const focus = _ctx.helper.boolean(_focus);
if (!player.isWorking) { if (!player.isWorking) {
throw _ctx.helper.makeRuntimeErrorMsg("Not currently working"); throw _ctx.helper.makeRuntimeErrorMsg("Not currently working");
} }
@ -862,9 +864,9 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
workForCompany: (_ctx: NetscriptContext) => workForCompany: (_ctx: NetscriptContext) =>
function (_companyName: unknown, _focus: unknown = true): boolean { function (_companyName: unknown, _focus: unknown = true): boolean {
_ctx.helper.checkSingularityAccess();
let companyName = _ctx.helper.string("companyName", _companyName); let companyName = _ctx.helper.string("companyName", _companyName);
const focus = _ctx.helper.boolean(_focus); const focus = _ctx.helper.boolean(_focus);
_ctx.helper.checkSingularityAccess();
// Sanitize input // Sanitize input
if (companyName == null) { if (companyName == null) {
@ -918,9 +920,9 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
applyToCompany: (_ctx: NetscriptContext) => applyToCompany: (_ctx: NetscriptContext) =>
function (_companyName: unknown, _field: unknown): boolean { function (_companyName: unknown, _field: unknown): boolean {
_ctx.helper.checkSingularityAccess();
const companyName = _ctx.helper.string("companyName", _companyName); const companyName = _ctx.helper.string("companyName", _companyName);
const field = _ctx.helper.string("field", _field); const field = _ctx.helper.string("field", _field);
_ctx.helper.checkSingularityAccess();
getCompany(_ctx, companyName); getCompany(_ctx, companyName);
player.location = companyName as LocationName; player.location = companyName as LocationName;
@ -990,22 +992,22 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
getCompanyRep: (_ctx: NetscriptContext) => getCompanyRep: (_ctx: NetscriptContext) =>
function (_companyName: unknown): number { function (_companyName: unknown): number {
const companyName = _ctx.helper.string("companyName", _companyName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const companyName = _ctx.helper.string("companyName", _companyName);
const company = getCompany(_ctx, companyName); const company = getCompany(_ctx, companyName);
return company.playerReputation; return company.playerReputation;
}, },
getCompanyFavor: (_ctx: NetscriptContext) => getCompanyFavor: (_ctx: NetscriptContext) =>
function (_companyName: unknown): number { function (_companyName: unknown): number {
const companyName = _ctx.helper.string("companyName", _companyName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const companyName = _ctx.helper.string("companyName", _companyName);
const company = getCompany(_ctx, companyName); const company = getCompany(_ctx, companyName);
return company.favor; return company.favor;
}, },
getCompanyFavorGain: (_ctx: NetscriptContext) => getCompanyFavorGain: (_ctx: NetscriptContext) =>
function (_companyName: unknown): number { function (_companyName: unknown): number {
const companyName = _ctx.helper.string("companyName", _companyName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const companyName = _ctx.helper.string("companyName", _companyName);
const company = getCompany(_ctx, companyName); const company = getCompany(_ctx, companyName);
return company.getFavorGain(); return company.getFavorGain();
}, },
@ -1017,8 +1019,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
joinFaction: (_ctx: NetscriptContext) => joinFaction: (_ctx: NetscriptContext) =>
function (_facName: unknown): boolean { function (_facName: unknown): boolean {
const facName = _ctx.helper.string("facName", _facName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName);
getFaction(_ctx, facName); getFaction(_ctx, facName);
if (!player.factionInvitations.includes(facName)) { if (!player.factionInvitations.includes(facName)) {
@ -1041,10 +1043,10 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
workForFaction: (_ctx: NetscriptContext) => workForFaction: (_ctx: NetscriptContext) =>
function (_facName: unknown, _type: unknown, _focus: unknown = true): boolean { function (_facName: unknown, _type: unknown, _focus: unknown = true): boolean {
_ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName); const facName = _ctx.helper.string("facName", _facName);
const type = _ctx.helper.string("type", _type); const type = _ctx.helper.string("type", _type);
const focus = _ctx.helper.boolean(_focus); const focus = _ctx.helper.boolean(_focus);
_ctx.helper.checkSingularityAccess();
getFaction(_ctx, facName); getFaction(_ctx, facName);
// if the player is in a gang and the target faction is any of the gang faction, fail // if the player is in a gang and the target faction is any of the gang faction, fail
@ -1130,30 +1132,30 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
getFactionRep: (_ctx: NetscriptContext) => getFactionRep: (_ctx: NetscriptContext) =>
function (_facName: unknown): number { function (_facName: unknown): number {
const facName = _ctx.helper.string("facName", _facName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName);
const faction = getFaction(_ctx, facName); const faction = getFaction(_ctx, facName);
return faction.playerReputation; return faction.playerReputation;
}, },
getFactionFavor: (_ctx: NetscriptContext) => getFactionFavor: (_ctx: NetscriptContext) =>
function (_facName: unknown): number { function (_facName: unknown): number {
const facName = _ctx.helper.string("facName", _facName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName);
const faction = getFaction(_ctx, facName); const faction = getFaction(_ctx, facName);
return faction.favor; return faction.favor;
}, },
getFactionFavorGain: (_ctx: NetscriptContext) => getFactionFavorGain: (_ctx: NetscriptContext) =>
function (_facName: unknown): number { function (_facName: unknown): number {
const facName = _ctx.helper.string("facName", _facName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName);
const faction = getFaction(_ctx, facName); const faction = getFaction(_ctx, facName);
return faction.getFavorGain(); return faction.getFavorGain();
}, },
donateToFaction: (_ctx: NetscriptContext) => donateToFaction: (_ctx: NetscriptContext) =>
function (_facName: unknown, _amt: unknown): boolean { function (_facName: unknown, _amt: unknown): boolean {
_ctx.helper.checkSingularityAccess();
const facName = _ctx.helper.string("facName", _facName); const facName = _ctx.helper.string("facName", _facName);
const amt = _ctx.helper.number("amt", _amt); const amt = _ctx.helper.number("amt", _amt);
_ctx.helper.checkSingularityAccess();
const faction = getFaction(_ctx, facName); const faction = getFaction(_ctx, facName);
if (!player.factions.includes(faction.name)) { if (!player.factions.includes(faction.name)) {
workerScript.log("donateToFaction", () => `You can't donate to '${facName}' because you aren't a member`); workerScript.log("donateToFaction", () => `You can't donate to '${facName}' because you aren't a member`);
@ -1200,9 +1202,9 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
createProgram: (_ctx: NetscriptContext) => createProgram: (_ctx: NetscriptContext) =>
function (_programName: unknown, _focus: unknown = true): boolean { function (_programName: unknown, _focus: unknown = true): boolean {
_ctx.helper.checkSingularityAccess();
const programName = _ctx.helper.string("programName", _programName).toLowerCase(); const programName = _ctx.helper.string("programName", _programName).toLowerCase();
const focus = _ctx.helper.boolean(_focus); const focus = _ctx.helper.boolean(_focus);
_ctx.helper.checkSingularityAccess();
const wasFocusing = player.focus; const wasFocusing = player.focus;
if (player.isWorking) { if (player.isWorking) {
@ -1249,8 +1251,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
commitCrime: (_ctx: NetscriptContext) => commitCrime: (_ctx: NetscriptContext) =>
function (_crimeRoughName: unknown): number { function (_crimeRoughName: unknown): number {
const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName);
if (player.isWorking) { if (player.isWorking) {
const txt = player.singularityStopWork(); const txt = player.singularityStopWork();
@ -1270,8 +1272,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
getCrimeChance: (_ctx: NetscriptContext) => getCrimeChance: (_ctx: NetscriptContext) =>
function (_crimeRoughName: unknown): number { function (_crimeRoughName: unknown): number {
const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName);
const crime = findCrime(crimeRoughName.toLowerCase()); const crime = findCrime(crimeRoughName.toLowerCase());
if (crime == null) { if (crime == null) {
@ -1282,8 +1284,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
getCrimeStats: (_ctx: NetscriptContext) => getCrimeStats: (_ctx: NetscriptContext) =>
function (_crimeRoughName: unknown): CrimeStats { function (_crimeRoughName: unknown): CrimeStats {
const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName);
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const crimeRoughName = _ctx.helper.string("crimeRoughName", _crimeRoughName);
const crime = findCrime(crimeRoughName.toLowerCase()); const crime = findCrime(crimeRoughName.toLowerCase());
if (crime == null) { if (crime == null) {
@ -1305,8 +1307,8 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
}, },
getDarkwebProgramCost: (_ctx: NetscriptContext) => getDarkwebProgramCost: (_ctx: NetscriptContext) =>
function (_programName: unknown): number { function (_programName: unknown): number {
const programName = _ctx.helper.string("programName", _programName).toLowerCase();
_ctx.helper.checkSingularityAccess(); _ctx.helper.checkSingularityAccess();
const programName = _ctx.helper.string("programName", _programName).toLowerCase();
// If we don't have Tor, log it and return -1 // If we don't have Tor, log it and return -1
if (!player.hasTorRouter()) { if (!player.hasTorRouter()) {
@ -1335,5 +1337,51 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
} }
return item.price; return item.price;
}, },
b1tflum3:
(_ctx: NetscriptContext) =>
(_nextBN: unknown, _callbackScript: unknown = ""): void => {
_ctx.helper.checkSingularityAccess();
const nextBN = _ctx.helper.number("nextBN", _nextBN);
const callbackScript = _ctx.helper.string("callbackScript", _callbackScript);
_ctx.helper.checkSingularityAccess();
enterBitNode(Router, true, player.bitNodeN, nextBN);
if (callbackScript)
setTimeout(() => {
runAfterReset(callbackScript);
}, 0);
},
destroyW0r1dD43m0n:
(_ctx: NetscriptContext) =>
(_nextBN: unknown, _callbackScript: unknown = ""): void => {
_ctx.helper.checkSingularityAccess();
const nextBN = _ctx.helper.number("nextBN", _nextBN);
const callbackScript = _ctx.helper.string("callbackScript", _callbackScript);
_ctx.helper.checkSingularityAccess();
const hackingRequirements = (): boolean => {
const wd = GetServer(SpecialServers.WorldDaemon);
if (!(wd instanceof Server))
throw new Error("WorldDaemon was not a normal server. This is a bug contact dev.");
if (player.hacking < wd.requiredHackingSkill) return false;
if (!wd.hasAdminRights) return false;
return true;
};
const bladeburnerRequirements = (): boolean => {
if (!player.inBladeburner()) return false;
if (!player.bladeburner) return false;
return player.bladeburner.blackops[BlackOperationNames.OperationDaedalus];
};
if (!hackingRequirements() && !bladeburnerRequirements()) {
_ctx.log(() => "Requirements not met to destroy the world daemon");
return;
}
enterBitNode(Router, false, player.bitNodeN, nextBN);
if (callbackScript)
setTimeout(() => {
runAfterReset(callbackScript);
}, 0);
},
}; };
} }

@ -2,7 +2,6 @@ import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { getRamCost } from "../Netscript/RamCostGenerator"; import { getRamCost } from "../Netscript/RamCostGenerator";
import { FactionWorkType } from "../Faction/FactionWorkTypeEnum"; import { FactionWorkType } from "../Faction/FactionWorkTypeEnum";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum"; import { SleeveTaskType } from "../PersonObjects/Sleeve/SleeveTaskTypesEnum";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers"; import { findSleevePurchasableAugs } from "../PersonObjects/Sleeve/SleeveHelpers";
@ -20,7 +19,7 @@ import {
export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): ISleeve { export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): ISleeve {
const checkSleeveAPIAccess = function (func: string): void { const checkSleeveAPIAccess = function (func: string): void {
if (player.bitNodeN !== 10 && !SourceFileFlags[10]) { if (player.bitNodeN !== 10 && !player.sourceFileLvl(10)) {
throw helper.makeRuntimeErrorMsg( throw helper.makeRuntimeErrorMsg(
`sleeve.${func}`, `sleeve.${func}`,
"You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10", "You do not currently have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10",

@ -1,5 +1,4 @@
import { Bladeburner } from "../../Bladeburner/Bladeburner"; import { Bladeburner } from "../../Bladeburner/Bladeburner";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IPlayer } from "../IPlayer"; import { IPlayer } from "../IPlayer";
export function canAccessBladeburner(this: IPlayer): boolean { export function canAccessBladeburner(this: IPlayer): boolean {
@ -7,7 +6,7 @@ export function canAccessBladeburner(this: IPlayer): boolean {
return false; return false;
} }
return this.bitNodeN === 6 || this.bitNodeN === 7 || SourceFileFlags[6] > 0 || SourceFileFlags[7] > 0; return this.bitNodeN === 6 || this.bitNodeN === 7 || this.sourceFileLvl(6) > 0 || this.sourceFileLvl(7) > 0;
} }
export function inBladeburner(this: IPlayer): boolean { export function inBladeburner(this: IPlayer): boolean {

@ -1,10 +1,12 @@
import { Corporation } from "../../Corporation/Corporation"; import { Corporation } from "../../Corporation/Corporation";
import { CorporationUnlockUpgrades } from "../../Corporation/data/CorporationUnlockUpgrades"; import {
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags"; CorporationUnlockUpgradeIndex,
CorporationUnlockUpgrades,
} from "../../Corporation/data/CorporationUnlockUpgrades";
import { IPlayer } from "../IPlayer"; import { IPlayer } from "../IPlayer";
export function canAccessCorporation(this: IPlayer): boolean { export function canAccessCorporation(this: IPlayer): boolean {
return this.bitNodeN === 3 || SourceFileFlags[3] > 0; return this.bitNodeN === 3 || this.sourceFileLvl(3) > 0;
} }
export function hasCorporation(this: IPlayer): boolean { export function hasCorporation(this: IPlayer): boolean {
@ -19,9 +21,9 @@ export function startCorporation(this: IPlayer, corpName: string, additionalShar
name: corpName, name: corpName,
}); });
if (SourceFileFlags[3] === 3) { if (this.sourceFileLvl(3) === 3) {
const warehouseApi = CorporationUnlockUpgrades["7"][0]; const warehouseApi = CorporationUnlockUpgrades[CorporationUnlockUpgradeIndex.WarehouseAPI].index;
const OfficeApi = CorporationUnlockUpgrades["8"][0]; const OfficeApi = CorporationUnlockUpgrades[CorporationUnlockUpgradeIndex.OfficeAPI].index;
this.corporation.unlockUpgrades[warehouseApi] = 1; this.corporation.unlockUpgrades[warehouseApi] = 1;
this.corporation.unlockUpgrades[OfficeApi] = 1; this.corporation.unlockUpgrades[OfficeApi] = 1;

@ -1,7 +1,6 @@
import { Factions } from "../../Faction/Factions"; import { Factions } from "../../Faction/Factions";
import { Faction } from "../../Faction/Faction"; import { Faction } from "../../Faction/Faction";
import { Gang } from "../../Gang/Gang"; import { Gang } from "../../Gang/Gang";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IPlayer } from "../IPlayer"; import { IPlayer } from "../IPlayer";
// Amount of negative karma needed to manage a gang in BitNodes other than 2 // Amount of negative karma needed to manage a gang in BitNodes other than 2
@ -11,7 +10,7 @@ export function canAccessGang(this: IPlayer): boolean {
if (this.bitNodeN === 2) { if (this.bitNodeN === 2) {
return true; return true;
} }
if (SourceFileFlags[2] <= 0) { if (this.sourceFileLvl(2) <= 0) {
return false; return false;
} }

@ -45,7 +45,6 @@ import { SpecialServers } from "../../Server/data/SpecialServers";
import { applySourceFile } from "../../SourceFile/applySourceFile"; import { applySourceFile } from "../../SourceFile/applySourceFile";
import { applyExploit } from "../../Exploits/applyExploits"; import { applyExploit } from "../../Exploits/applyExploits";
import { SourceFiles } from "../../SourceFile/SourceFiles"; import { SourceFiles } from "../../SourceFile/SourceFiles";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { influenceStockThroughCompanyWork } from "../../StockMarket/PlayerInfluencing"; import { influenceStockThroughCompanyWork } from "../../StockMarket/PlayerInfluencing";
import { getHospitalizationCost } from "../../Hospital/Hospital"; import { getHospitalizationCost } from "../../Hospital/Hospital";
import { WorkerScript } from "../../Netscript/WorkerScript"; import { WorkerScript } from "../../Netscript/WorkerScript";
@ -121,7 +120,7 @@ export function prestigeAugmentation(this: PlayerObject): void {
this.queuedAugmentations = []; this.queuedAugmentations = [];
const numSleeves = Math.min(3, SourceFileFlags[10] + (this.bitNodeN === 10 ? 1 : 0)) + this.sleevesFromCovenant; const numSleeves = Math.min(3, this.sourceFileLvl(10) + (this.bitNodeN === 10 ? 1 : 0)) + this.sleevesFromCovenant;
if (this.sleeves.length > numSleeves) this.sleeves.length = numSleeves; if (this.sleeves.length > numSleeves) this.sleeves.length = numSleeves;
for (let i = this.sleeves.length; i < numSleeves; i++) { for (let i = this.sleeves.length; i < numSleeves; i++) {
this.sleeves.push(new Sleeve(this)); this.sleeves.push(new Sleeve(this));
@ -467,7 +466,7 @@ export function gainIntelligenceExp(this: IPlayer, exp: number): void {
console.error("ERROR: NaN passed into Player.gainIntelligenceExp()"); console.error("ERROR: NaN passed into Player.gainIntelligenceExp()");
return; return;
} }
if (SourceFileFlags[5] > 0 || this.intelligence > 0) { if (this.sourceFileLvl(5) > 0 || this.intelligence > 0) {
this.intelligence_exp += exp; this.intelligence_exp += exp;
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp)); this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
} }
@ -1037,7 +1036,7 @@ export function getWorkMoneyGain(this: IPlayer): number {
// If player has SF-11, calculate salary multiplier from favor // If player has SF-11, calculate salary multiplier from favor
let bn11Mult = 1; let bn11Mult = 1;
const company = Companies[this.companyName]; const company = Companies[this.companyName];
if (SourceFileFlags[11] > 0) { if (this.sourceFileLvl(11) > 0) {
bn11Mult = 1 + company.favor / 100; bn11Mult = 1 + company.favor / 100;
} }
@ -1315,20 +1314,21 @@ export function createProgramWork(this: IPlayer, numCycles: number): boolean {
export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string { export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string {
const programName = this.createProgramName; const programName = this.createProgramName;
if (cancelled === false) { if (!cancelled) {
//Complete case
this.gainIntelligenceExp((CONSTANTS.IntelligenceProgramBaseExpGain * this.timeWorked) / 1000);
dialogBoxCreate(`You've finished creating ${programName}!<br>The new program can be found on your home computer.`); dialogBoxCreate(`You've finished creating ${programName}!<br>The new program can be found on your home computer.`);
this.getHomeComputer().programs.push(programName); if (!this.getHomeComputer().programs.includes(programName)) {
} else { this.getHomeComputer().programs.push(programName);
}
} else if (!this.getHomeComputer().programs.includes(programName)) {
//Incomplete case
const perc = (Math.floor((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 10000) / 100).toString(); const perc = (Math.floor((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 10000) / 100).toString();
const incompleteName = programName + "-" + perc + "%-INC"; const incompleteName = programName + "-" + perc + "%-INC";
this.getHomeComputer().programs.push(incompleteName); this.getHomeComputer().programs.push(incompleteName);
} }
if (!cancelled) {
this.gainIntelligenceExp((CONSTANTS.IntelligenceProgramBaseExpGain * this.timeWorked) / 1000);
}
this.isWorking = false; this.isWorking = false;
this.resetWorkStatus(); this.resetWorkStatus();
@ -2699,7 +2699,7 @@ export function gotoLocation(this: IPlayer, to: LocationName): boolean {
} }
export function canAccessGrafting(this: IPlayer): boolean { export function canAccessGrafting(this: IPlayer): boolean {
return this.bitNodeN === 10 || SourceFileFlags[10] > 0; return this.bitNodeN === 10 || this.sourceFileLvl(10) > 0;
} }
export function giveExploit(this: IPlayer, exploit: Exploit): void { export function giveExploit(this: IPlayer, exploit: Exploit): void {
@ -2737,7 +2737,7 @@ export function setMult(this: IPlayer, name: string, mult: number): void {
} }
export function canAccessCotMG(this: IPlayer): boolean { export function canAccessCotMG(this: IPlayer): boolean {
return this.bitNodeN === 13 || SourceFileFlags[13] > 0; return this.bitNodeN === 13 || this.sourceFileLvl(13) > 0;
} }
export function sourceFileLvl(this: IPlayer, n: number): number { export function sourceFileLvl(this: IPlayer, n: number): number {

@ -20,7 +20,6 @@ import { LiteratureNames } from "./Literature/data/LiteratureNames";
import { GetServer, AddToAllServers, initForeignServers, prestigeAllServers } from "./Server/AllServers"; import { GetServer, AddToAllServers, initForeignServers, prestigeAllServers } from "./Server/AllServers";
import { prestigeHomeComputer } from "./Server/ServerHelpers"; import { prestigeHomeComputer } from "./Server/ServerHelpers";
import { SourceFileFlags, updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { SpecialServers } from "./Server/data/SpecialServers"; import { SpecialServers } from "./Server/data/SpecialServers";
import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket"; import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
@ -139,7 +138,7 @@ export function prestigeAugmentation(): void {
if (Player.bitNodeN === 8) { if (Player.bitNodeN === 8) {
Player.money = BitNode8StartingMoney; Player.money = BitNode8StartingMoney;
} }
if (Player.bitNodeN === 8 || SourceFileFlags[8] > 0) { if (Player.bitNodeN === 8 || Player.sourceFileLvl(8) > 0) {
Player.hasWseAccount = true; Player.hasWseAccount = true;
Player.hasTixApiAccess = true; Player.hasTixApiAccess = true;
} }
@ -174,7 +173,6 @@ export function prestigeAugmentation(): void {
// Prestige by destroying Bit Node and gaining a Source File // Prestige by destroying Bit Node and gaining a Source File
export function prestigeSourceFile(flume: boolean): void { export function prestigeSourceFile(flume: boolean): void {
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
updateSourceFileFlags(Player);
Player.prestigeSourceFile(); Player.prestigeSourceFile();
prestigeWorkerScripts(); // Delete all Worker Scripts objects prestigeWorkerScripts(); // Delete all Worker Scripts objects
@ -198,9 +196,9 @@ export function prestigeSourceFile(flume: boolean): void {
// Re-create foreign servers // Re-create foreign servers
initForeignServers(Player.getHomeComputer()); initForeignServers(Player.getHomeComputer());
if (SourceFileFlags[9] >= 2) { if (Player.sourceFileLvl(9) >= 2) {
homeComp.setMaxRam(128); homeComp.setMaxRam(128);
} else if (SourceFileFlags[1] > 0) { } else if (Player.sourceFileLvl(1) > 0) {
homeComp.setMaxRam(32); homeComp.setMaxRam(32);
} else { } else {
homeComp.setMaxRam(8); homeComp.setMaxRam(8);
@ -234,10 +232,10 @@ export function prestigeSourceFile(flume: boolean): void {
} }
// Give levels of NeuroFluxGoverner for Source-File 12. Must be done here before Augmentations are recalculated // Give levels of NeuroFluxGoverner for Source-File 12. Must be done here before Augmentations are recalculated
if (SourceFileFlags[12] > 0) { if (Player.sourceFileLvl(12) > 0) {
Player.augmentations.push({ Player.augmentations.push({
name: AugmentationNames.NeuroFluxGovernor, name: AugmentationNames.NeuroFluxGovernor,
level: SourceFileFlags[12], level: Player.sourceFileLvl(12),
}); });
} }
@ -264,7 +262,7 @@ export function prestigeSourceFile(flume: boolean): void {
if (Player.bitNodeN === 8) { if (Player.bitNodeN === 8) {
Player.money = BitNode8StartingMoney; Player.money = BitNode8StartingMoney;
} }
if (Player.bitNodeN === 8 || SourceFileFlags[8] > 0) { if (Player.bitNodeN === 8 || Player.sourceFileLvl(8) > 0) {
Player.hasWseAccount = true; Player.hasWseAccount = true;
Player.hasTixApiAccess = true; Player.hasTixApiAccess = true;
} }
@ -292,7 +290,7 @@ export function prestigeSourceFile(flume: boolean): void {
Player.bladeburner = null; Player.bladeburner = null;
// Source-File 9 (level 3) effect // Source-File 9 (level 3) effect
if (SourceFileFlags[9] >= 3) { if (Player.sourceFileLvl(9) >= 3) {
const hserver = Player.createHacknetServer(); const hserver = Player.createHacknetServer();
hserver.level = 100; hserver.level = 100;
@ -309,7 +307,7 @@ export function prestigeSourceFile(flume: boolean): void {
staneksGift.prestigeSourceFile(); staneksGift.prestigeSourceFile();
// Gain int exp // Gain int exp
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300); if (Player.sourceFileLvl(5) !== 0 && !flume) Player.gainIntelligenceExp(300);
resetPidCounter(); resetPidCounter();
} }

@ -24,9 +24,4 @@ export class Program {
this.create = create; this.create = create;
this.run = run; this.run = run;
} }
htmlID(): string {
const name = this.name.endsWith(".exe") ? this.name.slice(0, -".exe".length) : this.name;
return "create-program-" + name;
}
} }

@ -5,7 +5,6 @@ import React from "react";
import { Player } from "./Player"; import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige"; import { prestigeSourceFile } from "./Prestige";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile"; import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { SourceFiles } from "./SourceFile/SourceFiles"; import { SourceFiles } from "./SourceFile/SourceFiles";
import { dialogBoxCreate } from "./ui/React/DialogBox"; import { dialogBoxCreate } from "./ui/React/DialogBox";
@ -69,7 +68,7 @@ function giveSourceFile(bitNodeNumber: number): void {
export function enterBitNode(router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number): void { export function enterBitNode(router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number): void {
if (!flume) { if (!flume) {
giveSourceFile(destroyedBitNode); giveSourceFile(destroyedBitNode);
} else if (SourceFileFlags[5] === 0 && newBitNode !== 5) { } else if (Player.sourceFileLvl(5) === 0 && newBitNode !== 5) {
Player.intelligence = 0; Player.intelligence = 0;
Player.intelligence_exp = 0; Player.intelligence_exp = 0;
} }

@ -6,7 +6,6 @@ import { loadAllGangs, AllGangs } from "./Gang/AllGangs";
import { Player, loadPlayer } from "./Player"; import { Player, loadPlayer } from "./Player";
import { saveAllServers, loadAllServers, GetAllServers } from "./Server/AllServers"; import { saveAllServers, loadAllServers, GetAllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket"; import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
import { staneksGift, loadStaneksGift } from "./CotMG/Helper"; import { staneksGift, loadStaneksGift } from "./CotMG/Helper";
@ -126,7 +125,7 @@ class BitburnerSaveObject {
// Save file name is based on current timestamp and BitNode // Save file name is based on current timestamp and BitNode
const epochTime = Math.round(Date.now() / 1000); const epochTime = Math.round(Date.now() / 1000);
const bn = Player.bitNodeN; const bn = Player.bitNodeN;
let filename = `bitburnerSave_${epochTime}_BN${bn}x${SourceFileFlags[bn]}.json`; let filename = `bitburnerSave_${epochTime}_BN${bn}x${Player.sourceFileLvl(bn) + 1}.json`;
if (isRecovery) filename = "RECOVERY" + filename; if (isRecovery) filename = "RECOVERY" + filename;
return filename; return filename;
} }
@ -395,6 +394,9 @@ function evaluateVersionCompatibility(ver: string | number): void {
delete anyPlayer.resleeves; delete anyPlayer.resleeves;
} }
} }
if (ver < 14) {
delete (Settings as any).EditorTheme;
}
} }
} }

@ -2386,6 +2386,30 @@ export interface Singularity {
* purchased. Throws an error if the specified program/exploit does not exist * purchased. Throws an error if the specified program/exploit does not exist
*/ */
getDarkwebProgramCost(programName: string): number; getDarkwebProgramCost(programName: string): number;
/**
* b1t_flum3 into a different BN.
* @remarks
* RAM cost: 16 GB * 16/4/1
*
* @param nextBN - BN number to jump to
* @param callbackScript - Name of the script to launch in the next BN.
*/
b1tflum3(nextBN: number, callbackScript?: string): void;
/**
* Destroy the w0r1d_d43m0n and move on to the next BN.
* @remarks
* RAM cost: 32 GB * 16/4/1
*
* You must have the special augment installed and the required hacking level
* OR
* Completed the final black op.
*
* @param nextBN - BN number to jump to
* @param callbackScript - Name of the script to launch in the next BN.
*/
destroyW0r1dD43m0n(nextBN: number, callbackScript?: string): void;
} }
/** /**
@ -3234,7 +3258,7 @@ export interface CodingContract {
* Attempts to solve the Coding Contract with the provided solution. * Attempts to solve the Coding Contract with the provided solution.
* *
* @param answer - Solution for the contract. * @param answer - Solution for the contract.
* @param fn - Filename of the contract. * @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided. * @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @param opts - Optional parameters for configuring function behavior. * @param opts - Optional parameters for configuring function behavior.
* @returns True if the solution was correct, false otherwise. If the returnReward option is configured, then the function will instead return a string. If the contract is successfully solved, the string will contain a description of the contracts reward. Otherwise, it will be an empty string. * @returns True if the solution was correct, false otherwise. If the returnReward option is configured, then the function will instead return a string. If the contract is successfully solved, the string will contain a description of the contracts reward. Otherwise, it will be an empty string.
@ -3249,7 +3273,7 @@ export interface CodingContract {
* Returns a name describing the type of problem posed by the Coding Contract. * Returns a name describing the type of problem posed by the Coding Contract.
* (e.g. Find Largest Prime Factor, Total Ways to Sum, etc.) * (e.g. Find Largest Prime Factor, Total Ways to Sum, etc.)
* *
* @param fn - Filename of the contract. * @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided. * @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns Name describing the type of problem posed by the Coding Contract. * @returns Name describing the type of problem posed by the Coding Contract.
*/ */
@ -3262,7 +3286,7 @@ export interface CodingContract {
* *
* Get the full text description for the problem posed by the Coding Contract. * Get the full text description for the problem posed by the Coding Contract.
* *
* @param fn - Filename of the contract. * @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided. * @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns Contracts text description. * @returns Contracts text description.
*/ */
@ -3290,7 +3314,7 @@ export interface CodingContract {
* *
* Get the number of tries remaining on the contract before it self-destructs. * Get the number of tries remaining on the contract before it self-destructs.
* *
* @param fn - Filename of the contract. * @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided. * @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns How many attempts are remaining for the contract; * @returns How many attempts are remaining for the contract;
*/ */
@ -5085,8 +5109,7 @@ export interface NS {
* PID stands for Process ID. The PID is a unique identifier for each script. * PID stands for Process ID. The PID is a unique identifier for each script.
* The PID will always be a positive integer. * The PID will always be a positive integer.
* *
* Running this function with a numThreads argument of 0 will return 0 without running the script. * Running this function with 0 or a negative numThreads argument will cause a runtime error.
* However, running this function with a negative numThreads argument will cause a runtime error.
* *
* @example * @example
* ```ts * ```ts
@ -6737,8 +6760,9 @@ export interface WarehouseAPI {
* Upgrade warehouse * Upgrade warehouse
* @param divisionName - Name of the division * @param divisionName - Name of the division
* @param cityName - Name of the city * @param cityName - Name of the city
* @param amt - amount of upgrades defaults to 1
*/ */
upgradeWarehouse(divisionName: string, cityName: string): void; upgradeWarehouse(divisionName: string, cityName: string, amt?: number): void;
/** /**
* Create a new product * Create a new product
* @param divisionName - Name of the division * @param divisionName - Name of the division
@ -6754,6 +6778,22 @@ export interface WarehouseAPI {
designInvest: number, designInvest: number,
marketingInvest: number, marketingInvest: number,
): void; ): void;
/**
* Limit Material Production.
* @param divisionName - Name of the division
* @param cityName - Name of the city
* @param materialName - Name of the material
* @param qty - Amount to limit to
*/
limitMaterialProduction(divisionName: string, cityName: string, materialName: string, qty: number): void;
/**
* Limit Product Production.
* @param divisionName - Name of the division
* @param cityName - Name of the city
* @param productName - Name of the product
* @param qty - Amount to limit to
*/
limitProductProduction(divisionName: string, cityName: string, productName: string, qty: number): void;
/** /**
* Gets the cost to purchase a warehouse * Gets the cost to purchase a warehouse
* @returns cost * @returns cost
@ -6761,9 +6801,12 @@ export interface WarehouseAPI {
getPurchaseWarehouseCost(): number; getPurchaseWarehouseCost(): number;
/** /**
* Gets the cost to upgrade a warehouse to the next level * Gets the cost to upgrade a warehouse to the next level
* @param divisionName - Name of the division
* @param cityName - Name of the city
* @param amt - amount of upgrades defaults to 1
* @returns cost to upgrade * @returns cost to upgrade
*/ */
getUpgradeWarehouseCost(adivisionName: any, acityName: any): number; getUpgradeWarehouseCost(adivisionName: any, acityName: any, amt?: number): number;
/** /**
* Check if you have a warehouse in city * Check if you have a warehouse in city
* @returns true if warehouse is present, false if not * @returns true if warehouse is present, false if not
@ -7007,8 +7050,10 @@ interface Material {
cmp: number | undefined; cmp: number | undefined;
/** Amount of material produced */ /** Amount of material produced */
prod: number; prod: number;
/** Amount of material sold */ /** Amount of material sold */
sell: number; sell: number;
/** cost to buy material */
cost: number;
} }
/** /**

@ -9,6 +9,10 @@ import Select from "@mui/material/Select";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import EditIcon from "@mui/icons-material/Edit";
import SaveIcon from "@mui/icons-material/Save";
import { ThemeEditorModal } from "./ThemeEditorModal";
interface IProps { interface IProps {
options: Options; options: Options;
@ -23,6 +27,7 @@ export function OptionsModal(props: IProps): React.ReactElement {
const [fontSize, setFontSize] = useState(props.options.fontSize); const [fontSize, setFontSize] = useState(props.options.fontSize);
const [wordWrap, setWordWrap] = useState(props.options.wordWrap); const [wordWrap, setWordWrap] = useState(props.options.wordWrap);
const [vim, setVim] = useState(props.options.vim); const [vim, setVim] = useState(props.options.vim);
const [themeEditorOpen, setThemeEditorOpen] = useState(false);
function save(): void { function save(): void {
props.save({ props.save({
@ -43,6 +48,7 @@ export function OptionsModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<ThemeEditorModal open={themeEditorOpen} onClose={() => setThemeEditorOpen(false)} />
<Box display="flex" flexDirection="row" alignItems="center"> <Box display="flex" flexDirection="row" alignItems="center">
<Typography>Theme: </Typography> <Typography>Theme: </Typography>
<Select onChange={(event) => setTheme(event.target.value)} value={theme}> <Select onChange={(event) => setTheme(event.target.value)} value={theme}>
@ -53,7 +59,11 @@ export function OptionsModal(props: IProps): React.ReactElement {
<MenuItem value="light">light</MenuItem> <MenuItem value="light">light</MenuItem>
<MenuItem value="dracula">dracula</MenuItem> <MenuItem value="dracula">dracula</MenuItem>
<MenuItem value="one-dark">one-dark</MenuItem> <MenuItem value="one-dark">one-dark</MenuItem>
<MenuItem value="customTheme">Custom theme</MenuItem>
</Select> </Select>
<Button onClick={() => setThemeEditorOpen(true)} sx={{ mx: 1 }} startIcon={<EditIcon />}>
Edit custom theme
</Button>
</Box> </Box>
<Box display="flex" flexDirection="row" alignItems="center"> <Box display="flex" flexDirection="row" alignItems="center">
@ -80,7 +90,9 @@ export function OptionsModal(props: IProps): React.ReactElement {
<TextField type="number" label="Font size" value={fontSize} onChange={onFontChange} /> <TextField type="number" label="Font size" value={fontSize} onChange={onFontChange} />
</Box> </Box>
<br /> <br />
<Button onClick={save}>Save</Button> <Button onClick={save} startIcon={<SaveIcon />}>
Save
</Button>
</Modal> </Modal>
); );
} }

@ -25,7 +25,7 @@ import { Settings } from "../../Settings/Settings";
import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial"; import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial";
import { debounce } from "lodash"; import { debounce } from "lodash";
import { saveObject } from "../../SaveObject"; import { saveObject } from "../../SaveObject";
import { loadThemes } from "./themes"; import { loadThemes, makeTheme, sanitizeTheme } from "./themes";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -362,6 +362,8 @@ export function Root(props: IProps): React.ReactElement {
monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts"); monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts");
monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts"); monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts");
loadThemes(monaco); loadThemes(monaco);
sanitizeTheme(Settings.EditorTheme);
monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
} }
// When the editor is mounted // When the editor is mounted
@ -993,7 +995,11 @@ export function Root(props: IProps): React.ReactElement {
</Box> </Box>
<OptionsModal <OptionsModal
open={optionsOpen} open={optionsOpen}
onClose={() => setOptionsOpen(false)} onClose={() => {
sanitizeTheme(Settings.EditorTheme);
monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
setOptionsOpen(false);
}}
options={{ options={{
theme: Settings.MonacoTheme, theme: Settings.MonacoTheme,
insertSpaces: Settings.MonacoInsertSpaces, insertSpaces: Settings.MonacoInsertSpaces,
@ -1002,6 +1008,8 @@ export function Root(props: IProps): React.ReactElement {
vim: Settings.MonacoVim, vim: Settings.MonacoVim,
}} }}
save={(options: Options) => { save={(options: Options) => {
sanitizeTheme(Settings.EditorTheme);
monacoRef.current?.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
setOptions(options); setOptions(options);
Settings.MonacoTheme = options.theme; Settings.MonacoTheme = options.theme;
Settings.MonacoInsertSpaces = options.insertSpaces; Settings.MonacoInsertSpaces = options.insertSpaces;

@ -0,0 +1,276 @@
import { History, Reply, Save } from "@mui/icons-material";
import { Box, Button, Paper, TextField, Tooltip, Typography } from "@mui/material";
import IconButton from "@mui/material/IconButton";
import _ from "lodash";
import { Color, ColorPicker } from "material-ui-color";
import React, { useState } from "react";
import { Settings } from "../../Settings/Settings";
import { Modal } from "../../ui/React/Modal";
import { OptionSwitch } from "../../ui/React/OptionSwitch";
import { defaultMonacoTheme, IScriptEditorTheme } from "./themes";
interface IProps {
onClose: () => void;
open: boolean;
}
interface IColorEditorProps {
label: string;
themePath: string;
color: string | undefined;
onColorChange: (name: string, value: string) => void;
defaultColor: string;
}
// Slightly tweaked version of the same function found in game options
function ColorEditor({ label, themePath, onColorChange, color, defaultColor }: IColorEditorProps): React.ReactElement {
if (color === undefined) {
console.error(`color ${themePath} was undefined, reverting to default`);
color = defaultColor;
}
return (
<>
<Tooltip title={label}>
<span>
<TextField
label={themePath}
value={"#" + color}
sx={{ display: "block", my: 1 }}
InputProps={{
startAdornment: (
<>
<ColorPicker
hideTextfield
deferred
value={"#" + color}
onChange={(newColor: Color) => onColorChange(themePath, newColor.hex)}
disableAlpha
/>
</>
),
endAdornment: (
<>
<IconButton onClick={() => onColorChange(themePath, defaultColor)}>
<Reply color="primary" />
</IconButton>
</>
),
}}
/>
</span>
</Tooltip>
</>
);
}
export function ThemeEditorModal(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((o) => !o);
}
// Need to deep copy the object since it has nested attributes
const [themeCopy, setThemeCopy] = useState<IScriptEditorTheme>(JSON.parse(JSON.stringify(Settings.EditorTheme)));
function onColorChange(name: string, value: string): void {
setThemeCopy(_.set(themeCopy, name, value));
rerender();
}
function onThemeChange(event: React.ChangeEvent<HTMLInputElement>): void {
try {
const importedTheme = JSON.parse(event.target.value);
if (typeof importedTheme !== "object") return;
setThemeCopy(importedTheme);
} catch (err) {
// ignore
}
}
return (
<Modal
open={props.open}
onClose={() => {
setThemeCopy(Settings.EditorTheme);
props.onClose();
}}
>
<Typography variant="h4">Customize Editor theme</Typography>
<Typography>Hover over input boxes for more information</Typography>
<Paper sx={{ p: 1, my: 1 }}>
<OptionSwitch
checked={themeCopy.base === "vs"}
onChange={(val) => {
setThemeCopy(_.set(themeCopy, "base", val ? "vs" : "vs-dark"));
rerender();
}}
text="Use light theme as base"
tooltip={
<>
If enabled, the <code>vs</code> light theme will be used as the theme base, otherwise,{" "}
<code>vs-dark</code> will be used.
</>
}
/>
<Box display="grid" sx={{ gridTemplateColumns: "1fr 1fr", width: "fit-content", gap: 1 }}>
<Box>
<Typography variant="h6">UI</Typography>
<ColorEditor
label="Background color"
themePath="common.bg"
onColorChange={onColorChange}
color={themeCopy.common.bg}
defaultColor={defaultMonacoTheme.common.bg}
/>
<ColorEditor
label="Current line and minimap background color"
themePath="ui.line"
onColorChange={onColorChange}
color={themeCopy.ui.line}
defaultColor={defaultMonacoTheme.ui.line}
/>
<ColorEditor
label="Base text color"
themePath="common.fg"
onColorChange={onColorChange}
color={themeCopy.common.fg}
defaultColor={defaultMonacoTheme.common.fg}
/>
<ColorEditor
label="Popup background color"
themePath="ui.panel.bg"
onColorChange={onColorChange}
color={themeCopy.ui.panel.bg}
defaultColor={defaultMonacoTheme.ui.panel.bg}
/>
<ColorEditor
label="Background color for selected item in popup"
themePath="ui.panel.selected"
onColorChange={onColorChange}
color={themeCopy.ui.panel.selected}
defaultColor={defaultMonacoTheme.ui.panel.selected}
/>
<ColorEditor
label="Popup border color"
themePath="ui.panel.border"
onColorChange={onColorChange}
color={themeCopy.ui.panel.border}
defaultColor={defaultMonacoTheme.ui.panel.border}
/>
<ColorEditor
label="Background color of highlighted text"
themePath="ui.selection.bg"
onColorChange={onColorChange}
color={themeCopy.ui.selection.bg}
defaultColor={defaultMonacoTheme.ui.selection.bg}
/>
</Box>
<Box>
<Typography variant="h6">Syntax</Typography>
<ColorEditor
label="Numbers, function names, and other key vars"
themePath="common.accent"
onColorChange={onColorChange}
color={themeCopy.common.accent}
defaultColor={defaultMonacoTheme.common.accent}
/>
<ColorEditor
label="Keywords"
themePath="syntax.keyword"
onColorChange={onColorChange}
color={themeCopy.syntax.keyword}
defaultColor={defaultMonacoTheme.syntax.keyword}
/>
<ColorEditor
label="Strings"
themePath="syntax.string"
onColorChange={onColorChange}
color={themeCopy.syntax.string}
defaultColor={defaultMonacoTheme.syntax.string}
/>
<ColorEditor
label="Regexp literals as well as escapes within strings"
themePath="syntax.regexp"
onColorChange={onColorChange}
color={themeCopy.syntax.regexp}
defaultColor={defaultMonacoTheme.syntax.regexp}
/>
<ColorEditor
label="Constants"
themePath="syntax.constant"
onColorChange={onColorChange}
color={themeCopy.syntax.constant}
defaultColor={defaultMonacoTheme.syntax.constant}
/>
<ColorEditor
label="Entities"
themePath="syntax.entity"
onColorChange={onColorChange}
color={themeCopy.syntax.entity}
defaultColor={defaultMonacoTheme.syntax.entity}
/>
<ColorEditor
label="'this', 'ns', types, and tags"
themePath="syntax.tag"
onColorChange={onColorChange}
color={themeCopy.syntax.tag}
defaultColor={defaultMonacoTheme.syntax.tag}
/>
<ColorEditor
label="Netscript functions and constructors"
themePath="syntax.markup"
onColorChange={onColorChange}
color={themeCopy.syntax.markup}
defaultColor={defaultMonacoTheme.syntax.markup}
/>
<ColorEditor
label="Errors"
themePath="syntax.error"
onColorChange={onColorChange}
color={themeCopy.syntax.error}
defaultColor={defaultMonacoTheme.syntax.error}
/>
<ColorEditor
label="Comments"
themePath="syntax.comment"
onColorChange={onColorChange}
color={themeCopy.syntax.comment}
defaultColor={defaultMonacoTheme.syntax.comment}
/>
</Box>
</Box>
</Paper>
<Paper sx={{ p: 1 }}>
<TextField
multiline
fullWidth
maxRows={10}
label={"import / export theme"}
value={JSON.stringify(themeCopy, undefined, 2)}
onChange={onThemeChange}
/>
<Box sx={{ mt: 1 }}>
<Button
onClick={() => {
Settings.EditorTheme = { ...themeCopy };
props.onClose();
}}
startIcon={<Save />}
>
Save
</Button>
<Button
onClick={() => {
setThemeCopy(defaultMonacoTheme);
rerender();
}}
startIcon={<History />}
>
Reset to default
</Button>
</Box>
</Paper>
</Modal>
);
}

@ -1,3 +1,216 @@
export interface IScriptEditorTheme {
[key: string]: any;
base: string;
inherit: boolean;
common: {
[key: string]: string;
accent: string;
bg: string;
fg: string;
};
syntax: {
[key: string]: string;
tag: string;
entity: string;
string: string;
regexp: string;
markup: string;
keyword: string;
comment: string;
constant: string;
error: string;
};
ui: {
[key: string]: any;
line: string;
panel: {
[key: string]: string;
bg: string;
selected: string;
border: string;
};
selection: {
[key: string]: string;
bg: string;
};
};
}
export const defaultMonacoTheme: IScriptEditorTheme = {
base: "vs-dark",
inherit: true,
common: {
accent: "B5CEA8",
bg: "1E1E1E",
fg: "D4D4D4",
},
syntax: {
tag: "569CD6",
entity: "569CD6",
string: "CE9178",
regexp: "646695",
markup: "569CD6",
keyword: "569CD6",
comment: "6A9955",
constant: "569CD6",
error: "F44747",
},
ui: {
line: "1E1E1E",
panel: {
bg: "252526",
selected: "252526",
border: "1E1E1E",
},
selection: {
bg: "ADD6FF26",
},
},
};
// Regex used for token color validation
// https://github.com/microsoft/vscode/blob/973684056e67153952f495fce93bf50d0ec0b892/src/vs/editor/common/languages/supports/tokenization.ts#L153
const colorRegExp = /^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/;
// Recursively sanitize the theme data to prevent errors
// Invalid data will be replaced with FF0000 (bright red)
export const sanitizeTheme = (theme: IScriptEditorTheme): void => {
for (const [k, v] of Object.entries(theme)) {
switch (k) {
case "base":
if (!["vs-dark", "vs"].includes(theme.base)) theme.base = "vs-dark";
continue;
case "inherit":
if (typeof theme.inherit !== "boolean") theme.inherit = true;
continue;
}
const repairBlock = (block: { [key: string]: any }): void => {
for (const [k, v] of Object.entries(block)) {
if (typeof v === "object") {
repairBlock(v as { [key: string]: string });
} else if (!v.match(colorRegExp)) block[k] = "FF0000";
}
};
repairBlock(v);
}
};
export function makeTheme(theme: IScriptEditorTheme): any {
const themeRules = [
{
token: "",
background: theme.ui.line,
foreground: theme.common.fg,
},
{
token: "identifier",
foreground: theme.common.accent,
},
{
token: "keyword",
foreground: theme.syntax.keyword,
},
{
token: "string",
foreground: theme.syntax.string,
},
{
token: "string.escape",
foreground: theme.syntax.regexp,
},
{
token: "comment",
foreground: theme.syntax.comment,
},
{
token: "constant",
foreground: theme.syntax.constant,
},
{
token: "entity",
foreground: theme.syntax.entity,
},
{
token: "type",
foreground: theme.syntax.tag,
},
{
token: "tag",
foreground: theme.syntax.tag,
},
{
token: "regexp",
foreground: theme.syntax.regexp,
},
{
token: "attribute",
foreground: theme.syntax.tag,
},
{
token: "constructor",
foreground: theme.syntax.markup,
},
{
token: "invalid",
foreground: theme.syntax.error,
},
{
token: "number",
foreground: theme.common.accent,
},
{
token: "delimiter",
foreground: theme.common.fg,
},
// Custom tokens
{
token: "ns",
foreground: theme.syntax.tag,
},
{
token: "netscriptfunction",
foreground: theme.syntax.markup,
},
{
token: "otherkeywords",
foreground: theme.syntax.keyword,
},
{
token: "otherkeyvars",
foreground: theme.common.accent,
},
{
token: "this",
foreground: theme.syntax.tag,
},
];
const themeColors = Object.fromEntries(
[
["editor.background", theme.common.bg],
["editor.foreground", theme.common.fg],
["editor.lineHighlightBackground", theme.ui.line],
["editor.selectionBackground", theme.ui.selection.bg],
["editorSuggestWidget.background", theme.ui.panel.bg],
["editorSuggestWidget.border", theme.ui.panel.border],
["editorSuggestWidget.selectedBackground", theme.ui.panel.selected],
["editorHoverWidget.background", theme.ui.panel.bg],
["editorHoverWidget.border", theme.ui.panel.border],
["editorWidget.background", theme.ui.panel.bg],
["editorWidget.border", theme.ui.panel.border],
["input.background", theme.ui.panel.bg],
["input.border", theme.ui.panel.border],
].map(([k, v]) => [k, "#" + v]),
);
return { base: theme.base, inherit: theme.inherit, rules: themeRules, colors: themeColors };
}
export async function loadThemes(monaco: { editor: any }): Promise<void> { export async function loadThemes(monaco: { editor: any }): Promise<void> {
monaco.editor.defineTheme("monokai", { monaco.editor.defineTheme("monokai", {
base: "vs-dark", base: "vs-dark",
@ -261,6 +474,7 @@ export async function loadThemes(monaco: { editor: any }): Promise<void> {
foreground: "FFB86C", foreground: "FFB86C",
fontStyle: "italic", fontStyle: "italic",
}, },
{ {
token: "netscriptfunction", token: "netscriptfunction",
foreground: "FF79C6", foreground: "FF79C6",

@ -5,6 +5,7 @@ import { defaultStyles } from "../Themes/Styles";
import { WordWrapOptions } from "../ScriptEditor/ui/Options"; import { WordWrapOptions } from "../ScriptEditor/ui/Options";
import { OverviewSettings } from "../ui/React/Overview"; import { OverviewSettings } from "../ui/React/Overview";
import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions"; import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions";
import { defaultMonacoTheme, IScriptEditorTheme } from "../ScriptEditor/ui/themes";
/** /**
* Represents the default settings the player could customize. * Represents the default settings the player could customize.
@ -157,6 +158,11 @@ interface IDefaultSettings {
* If the game's sidebar is opened * If the game's sidebar is opened
*/ */
IsSidebarOpened: boolean; IsSidebarOpened: boolean;
/**
* Script editor theme data
*/
EditorTheme: IScriptEditorTheme;
} }
/** /**
@ -216,6 +222,8 @@ export const defaultSettings: IDefaultSettings = {
theme: defaultTheme, theme: defaultTheme,
styles: defaultStyles, styles: defaultStyles,
overview: { x: 0, y: 0, opened: true }, overview: { x: 0, y: 0, opened: true },
EditorTheme: defaultMonacoTheme,
}; };
/** /**
@ -262,6 +270,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
theme: { ...defaultTheme }, theme: { ...defaultTheme },
styles: { ...defaultStyles }, styles: { ...defaultStyles },
overview: defaultSettings.overview, overview: defaultSettings.overview,
EditorTheme: { ...defaultMonacoTheme },
init() { init() {
Object.assign(Settings, defaultSettings); Object.assign(Settings, defaultSettings);
}, },
@ -273,6 +282,8 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
delete save.styles; delete save.styles;
Object.assign(Settings.overview, save.overview); Object.assign(Settings.overview, save.overview);
delete save.overview; delete save.overview;
Object.assign(Settings.EditorTheme, save.EditorTheme);
delete save.EditorTheme;
Object.assign(Settings, save); Object.assign(Settings, save);
}, },
}; };

@ -1,18 +0,0 @@
// Contains an array containing information about the player's source files
// Array[n] returns what level the player has of Source-File N.
import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer";
export const SourceFileFlags: number[] = Array(CONSTANTS.TotalNumBitNodes + 1); // Skip index 0
export function updateSourceFileFlags(p: IPlayer): void {
for (let i = 0; i < SourceFileFlags.length; ++i) {
SourceFileFlags[i] = 0;
}
for (let i = 0; i < p.sourceFiles.length; ++i) {
const sf = p.sourceFiles[i];
SourceFileFlags[sf.n] = sf.lvl;
}
}

@ -16,7 +16,6 @@ import { OrderTypes } from "../data/OrderTypes";
import { PositionTypes } from "../data/PositionTypes"; import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
@ -288,12 +287,12 @@ export function StockTicker(props: IProps): React.ReactElement {
// Whether the player has access to orders besides market orders (limit/stop) // Whether the player has access to orders besides market orders (limit/stop)
function hasOrderAccess(): boolean { function hasOrderAccess(): boolean {
return props.p.bitNodeN === 8 || SourceFileFlags[8] >= 3; return props.p.bitNodeN === 8 || props.p.sourceFileLvl(8) >= 3;
} }
// Whether the player has access to shorting stocks // Whether the player has access to shorting stocks
function hasShortAccess(): boolean { function hasShortAccess(): boolean {
return props.p.bitNodeN === 8 || SourceFileFlags[8] >= 2; return props.p.bitNodeN === 8 || props.p.sourceFileLvl(8) >= 2;
} }
return ( return (

@ -9,7 +9,6 @@ import { Stock } from "../Stock";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
@ -67,7 +66,7 @@ function ShortPosition(props: IProps): React.ReactElement {
percentageGains = 0; percentageGains = 0;
} }
if (props.p.bitNodeN === 8 || SourceFileFlags[8] >= 2) { if (props.p.bitNodeN === 8 || props.p.sourceFileLvl(8) >= 2) {
return ( return (
<> <>
<Box display="flex"> <Box display="flex">

@ -366,9 +366,9 @@ export function ThemeEditorModal(props: IProps): React.ReactElement {
sx={{ mb: 1 }} sx={{ mb: 1 }}
multiline multiline
fullWidth fullWidth
maxRows={3} maxRows={10}
label={"import / export theme"} label={"import / export theme"}
value={JSON.stringify(customTheme)} value={JSON.stringify(customTheme, undefined, 2)}
onChange={onThemeChange} onChange={onThemeChange}
/> />
<> <>

@ -1121,14 +1121,17 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
dfs(0, 0, left, right, data, "", res); dfs(0, 0, left, right, data, "", res);
const sanitizedPlayerAns = removeBracketsFromArrayString(ans).replace(/\s/g, ""); const sanitizedPlayerAns: string = removeBracketsFromArrayString(ans);
const sanitizedPlayerAnsArr: string[] = sanitizedPlayerAns.split(",");
for (let i = 0; i < sanitizedPlayerAnsArr.length; ++i) {
sanitizedPlayerAnsArr[i] = removeQuotesFromString(sanitizedPlayerAnsArr[i]).replace(/\s/g, "");
}
const playerAnsArray: string[] = sanitizedPlayerAns.split(","); if (sanitizedPlayerAnsArr.length !== res.length) {
if (playerAnsArray.length !== res.length) {
return false; return false;
} }
for (const resultInAnswer of res) { for (const resultInAnswer of res) {
if (!playerAnsArray.includes(resultInAnswer)) { if (!sanitizedPlayerAnsArr.includes(resultInAnswer)) {
return false; return false;
} }
} }

@ -2,7 +2,6 @@
* Game engine. Handles the main game loop. * Game engine. Handles the main game loop.
*/ */
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
import { Augmentations } from "./Augmentation/Augmentations";
import { initAugmentations } from "./Augmentation/AugmentationHelpers"; import { initAugmentations } from "./Augmentation/AugmentationHelpers";
import { AugmentationNames } from "./Augmentation/data/AugmentationNames"; import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initBitNodeMultipliers } from "./BitNode/BitNode";
@ -32,7 +31,6 @@ import { saveObject, loadGame } from "./SaveObject";
import { initForeignServers } from "./Server/AllServers"; import { initForeignServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings"; import { Settings } from "./Settings/Settings";
import { ThemeEvents } from "./Themes/ui/Theme"; import { ThemeEvents } from "./Themes/ui/Theme";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
import { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket"; import { initSymbolToStockMap, processStockPrices } from "./StockMarket/StockMarket";
import { Terminal } from "./Terminal"; import { Terminal } from "./Terminal";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve"; import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
@ -256,7 +254,6 @@ const Engine: {
ThemeEvents.emit(); ThemeEvents.emit();
initBitNodeMultipliers(Player); initBitNodeMultipliers(Player);
updateSourceFileFlags(Player);
initAugmentations(); // Also calls Player.reapplyAllAugmentations() initAugmentations(); // Also calls Player.reapplyAllAugmentations()
Player.reapplyAllSourceFiles(); Player.reapplyAllSourceFiles();
if (Player.hasWseAccount) { if (Player.hasWseAccount) {
@ -439,7 +436,6 @@ const Engine: {
initCompanies(); initCompanies();
initFactions(); initFactions();
initAugmentations(); initAugmentations();
updateSourceFileFlags(Player);
// Start interactive tutorial // Start interactive tutorial
iTutorialStart(); iTutorialStart();

@ -6,7 +6,6 @@ import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { HacknetServerConstants } from "../Hacknet/data/Constants"; import { HacknetServerConstants } from "../Hacknet/data/Constants";
import { getPurchaseServerLimit } from "../Server/ServerPurchases"; import { getPurchaseServerLimit } from "../Server/ServerPurchases";
import { Settings } from "../Settings/Settings"; import { Settings } from "../Settings/Settings";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { MoneySourceTracker } from "../utils/MoneySourceTracker"; import { MoneySourceTracker } from "../utils/MoneySourceTracker";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { use } from "./Context"; import { use } from "./Context";
@ -44,6 +43,7 @@ interface MultTableProps {
} }
function MultiplierTable(props: MultTableProps): React.ReactElement { function MultiplierTable(props: MultTableProps): React.ReactElement {
const player = use.Player();
return ( return (
<Table sx={{ display: "table", width: "100%", mb: (props.noMargin ?? false) === true ? 0 : 2 }}> <Table sx={{ display: "table", width: "100%", mb: (props.noMargin ?? false) === true ? 0 : 2 }}>
<TableBody> <TableBody>
@ -52,7 +52,7 @@ function MultiplierTable(props: MultTableProps): React.ReactElement {
value = data[1] as number, value = data[1] as number,
modded = data[2] as number | null; modded = data[2] as number | null;
if (modded && modded !== value && SourceFileFlags[5] > 0) { if (modded && modded !== value && player.sourceFileLvl(5) > 0) {
return ( return (
<StatsRow key={mult} name={mult} color={props.color} data={{}}> <StatsRow key={mult} name={mult} color={props.color} data={{}}>
<> <>
@ -88,7 +88,7 @@ function CurrentBitNode(): React.ReactElement {
<Box> <Box>
<Paper sx={{ p: 1 }}> <Paper sx={{ p: 1 }}>
<Typography variant="h5"> <Typography variant="h5">
BitNode {player.bitNodeN}: {BitNodes[index].name} (Level {lvl}) BitNode {player.bitNodeN}: {BitNodes[index].name} (Level {lvl + 1})
</Typography> </Typography>
<Typography sx={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>{BitNodes[index].info}</Typography> <Typography sx={{ whiteSpace: "pre-wrap", overflowWrap: "break-word" }}>{BitNodes[index].info}</Typography>
</Paper> </Paper>
@ -270,11 +270,13 @@ export function CharacterStats(): React.ReactElement {
data={{ content: `${player.purchasedServers.length} / ${getPurchaseServerLimit()}` }} data={{ content: `${player.purchasedServers.length} / ${getPurchaseServerLimit()}` }}
/> />
<StatsRow <StatsRow
name={`Hacknet ${player.bitNodeN === 9 || SourceFileFlags[9] > 0 ? "Servers" : "Nodes"} owned`} name={`Hacknet ${player.bitNodeN === 9 || player.sourceFileLvl(9) > 0 ? "Servers" : "Nodes"} owned`}
color={Settings.theme.primary} color={Settings.theme.primary}
data={{ data={{
content: `${player.hacknetNodes.length}${ content: `${player.hacknetNodes.length}${
player.bitNodeN === 9 || SourceFileFlags[9] > 0 ? ` / ${HacknetServerConstants.MaxServers}` : "" player.bitNodeN === 9 || player.sourceFileLvl(9) > 0
? ` / ${HacknetServerConstants.MaxServers}`
: ""
}`, }`,
}} }}
/> />
@ -320,7 +322,7 @@ export function CharacterStats(): React.ReactElement {
color={Settings.theme.cha} color={Settings.theme.cha}
data={{ level: player.charisma, exp: player.charisma_exp }} data={{ level: player.charisma, exp: player.charisma_exp }}
/> />
{player.intelligence > 0 && (player.bitNodeN === 5 || SourceFileFlags[5] > 0) && ( {player.intelligence > 0 && (player.bitNodeN === 5 || player.sourceFileLvl(5) > 0) && (
<StatsRow <StatsRow
name="Intelligence" name="Intelligence"
color={Settings.theme.int} color={Settings.theme.int}
@ -335,7 +337,7 @@ export function CharacterStats(): React.ReactElement {
<Paper sx={{ p: 1 }}> <Paper sx={{ p: 1 }}>
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}> <Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
Multipliers Multipliers
{SourceFileFlags[5] > 0 && ( {player.sourceFileLvl(5) > 0 && (
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>

@ -167,6 +167,7 @@ module.exports = (env, argv) => {
options: { options: {
name: "[contenthash].[ext]", name: "[contenthash].[ext]",
outputPath: "images", outputPath: "images",
publicPath: `${outputDirectory}/images`,
}, },
}, },
], ],