bitburner-src/src/Locations/ui/SpecialLocation.tsx

340 lines
11 KiB
TypeScript
Raw Normal View History

/**
* React Subcomponent for displaying a location's UI, when that location has special
* actions/options/properties
*
* Examples:
* - Bladeburner @ NSA
* - Re-sleeving @ VitaLife
* - Create Corporation @ City Hall
*
* This subcomponent creates all of the buttons for interacting with those special
* properties
*/
2021-09-18 09:00:07 +02:00
import React, { useState } from "react";
2021-09-25 21:34:12 +02:00
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
2021-09-05 01:09:30 +02:00
import { Location } from "../Location";
2022-04-01 15:55:23 +02:00
import { CreateCorporationModal } from "../../Corporation/ui/modals/CreateCorporationModal";
2021-09-05 01:09:30 +02:00
import { LocationName } from "../data/LocationNames";
2021-09-25 23:21:50 +02:00
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Factions } from "../../Faction/Factions";
import { joinFaction } from "../../Faction/FactionHelpers";
2022-09-13 00:00:09 +02:00
import { Router } from "../../ui/GameRoot";
import { Player } from "@player";
2021-09-25 20:42:57 +02:00
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { SnackbarEvents, ToastVariant } from "../../ui/React/Snackbar";
2021-11-03 01:30:19 +01:00
import { N00dles } from "../../utils/helpers/N00dles";
2021-11-03 03:11:22 +01:00
import { Exploit } from "../../Exploits/Exploit";
2021-11-18 00:08:58 +01:00
import { applyAugmentation } from "../../Augmentation/AugmentationHelpers";
2021-12-05 05:47:53 +01:00
import { CorruptableText } from "../../ui/React/CorruptableText";
import { HacknetNode } from "../../Hacknet/HacknetNode";
import { HacknetServer } from "../../Hacknet/HacknetServer";
import { GetServer } from "../../Server/AllServers";
2022-03-11 21:19:10 +01:00
import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
import { FactionNames } from "../../Faction/data/FactionNames";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
type IProps = {
2021-09-05 01:09:30 +02:00
loc: Location;
};
2021-09-18 09:00:07 +02:00
export function SpecialLocation(props: IProps): React.ReactElement {
2021-09-20 05:29:02 +02:00
const setRerender = useState(false)[1];
2021-09-05 01:09:30 +02:00
/** Click handler for Bladeburner button at Sector-12 NSA */
2021-09-18 09:00:07 +02:00
function handleBladeburner(): void {
NETSCRIPT: ns.sleeve.getSleeve added. getPlayer and getSleeve can both be used for formulas. (#200) * BREAKING CHANGE: Removed getSleeveStats and getSleeveInformation because this info is provided by getSleeve in a more usable form. * BREAKING CHANGE: Removed tor, inBladeburner, and hasCorporation fields from ns.getPlayer. Functionality still exists via added functions ns.hasTorRouter, ns.corporation.hasCorporation, and ns.bladeburner.inBladeburner. * Separated ns definitions for Person, Sleeve, and Player interfaces with both Player and Sleeve just extending Person. Added getSleeve, which provides a Sleeve object similar to getPlayer. * Renamed the sleeve ns layer's interface as sleeve lowercase because of name conflict. todo: May move all the ns layers interface names to lowercase for consistency * Added ns.formulas.work.crimeSuccessChance and reworked to allow both sleeve and player calculations. * Removed internal Person.getIntelligenceBonus function which was just a wrapper for calculateIntelligenceBonus. Any use of the former in formulas creates a conflict where ns-provided Person objects throw an error. * Renamed helpers.player to helpers.person for netscript person validation. Reduced number of fields validated due to Person being a smaller interface. * Fixed bug in bladeburner where Player multipliers and int were being used no matter which person was performing the task * Fixed leak of Player.jobs at ns.getPlayer * Person / Player / Sleeve classes now implement the netscript equivalent interfaces. Netscript helper for person no longer asserts that it's a real Person class member, only that it's a Person interface. Functions that use netscript persons have been changed to expect just a person interface to prevent needing this incorrect type assertion.
2022-11-09 13:26:26 +01:00
if (Player.bladeburner) {
2021-09-05 01:09:30 +02:00
// Enter Bladeburner division
2022-09-13 00:00:09 +02:00
Router.toBladeburner();
} else if (
2022-09-13 00:00:09 +02:00
Player.skills.strength >= 100 &&
Player.skills.defense >= 100 &&
Player.skills.dexterity >= 100 &&
Player.skills.agility >= 100
) {
2021-09-05 01:09:30 +02:00
// Apply for Bladeburner division
2022-09-13 00:00:09 +02:00
Player.startBladeburner();
dialogBoxCreate("You have been accepted into the Bladeburner division!");
setRerender((old) => !old);
2021-09-05 01:09:30 +02:00
const worldHeader = document.getElementById("world-menu-header");
if (worldHeader instanceof HTMLElement) {
worldHeader.click();
worldHeader.click();
2021-09-05 01:09:30 +02:00
}
} else {
dialogBoxCreate("Rejected! Please apply again when you have 100 of each combat stat (str, def, dex, agi)");
}
2021-09-05 01:09:30 +02:00
}
/** Click handler for Resleeving button at New Tokyo VitaLife */
2022-03-19 05:22:21 +01:00
function handleGrafting(): void {
2022-09-13 00:00:09 +02:00
Router.toGrafting();
2021-09-05 01:09:30 +02:00
}
2021-09-18 09:00:07 +02:00
function renderBladeburner(): React.ReactElement {
2022-09-13 00:00:09 +02:00
if (!Player.canAccessBladeburner() || BitNodeMultipliers.BladeburnerRank === 0) {
2021-09-18 09:00:07 +02:00
return <></>;
}
NETSCRIPT: ns.sleeve.getSleeve added. getPlayer and getSleeve can both be used for formulas. (#200) * BREAKING CHANGE: Removed getSleeveStats and getSleeveInformation because this info is provided by getSleeve in a more usable form. * BREAKING CHANGE: Removed tor, inBladeburner, and hasCorporation fields from ns.getPlayer. Functionality still exists via added functions ns.hasTorRouter, ns.corporation.hasCorporation, and ns.bladeburner.inBladeburner. * Separated ns definitions for Person, Sleeve, and Player interfaces with both Player and Sleeve just extending Person. Added getSleeve, which provides a Sleeve object similar to getPlayer. * Renamed the sleeve ns layer's interface as sleeve lowercase because of name conflict. todo: May move all the ns layers interface names to lowercase for consistency * Added ns.formulas.work.crimeSuccessChance and reworked to allow both sleeve and player calculations. * Removed internal Person.getIntelligenceBonus function which was just a wrapper for calculateIntelligenceBonus. Any use of the former in formulas creates a conflict where ns-provided Person objects throw an error. * Renamed helpers.player to helpers.person for netscript person validation. Reduced number of fields validated due to Person being a smaller interface. * Fixed bug in bladeburner where Player multipliers and int were being used no matter which person was performing the task * Fixed leak of Player.jobs at ns.getPlayer * Person / Player / Sleeve classes now implement the netscript equivalent interfaces. Netscript helper for person no longer asserts that it's a real Person class member, only that it's a Person interface. Functions that use netscript persons have been changed to expect just a person interface to prevent needing this incorrect type assertion.
2022-11-09 13:26:26 +01:00
const text = Player.bladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
2022-03-11 21:19:10 +01:00
return (
<>
<br />
<Button onClick={handleBladeburner}>{text}</Button>
</>
);
2021-09-05 01:09:30 +02:00
}
2021-09-18 09:00:07 +02:00
function renderNoodleBar(): React.ReactElement {
2021-09-05 01:09:30 +02:00
function EatNoodles(): void {
SnackbarEvents.emit("You ate some delicious noodles and feel refreshed", ToastVariant.SUCCESS, 2000);
N00dles(); // This is the true power of the noodles.
2022-09-13 00:00:09 +02:00
if (Player.sourceFiles.length > 0) Player.giveExploit(Exploit.N00dles);
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
Player.exp.intelligence *= 1.0000000000000002;
2021-12-09 22:47:26 +01:00
}
2022-09-13 00:00:09 +02:00
Player.exp.hacking *= 1.0000000000000002;
Player.exp.strength *= 1.0000000000000002;
Player.exp.defense *= 1.0000000000000002;
Player.exp.agility *= 1.0000000000000002;
Player.exp.dexterity *= 1.0000000000000002;
Player.exp.charisma *= 1.0000000000000002;
for (const node of Player.hacknetNodes) {
if (node instanceof HacknetNode) {
2022-09-13 00:00:09 +02:00
Player.gainMoney(node.moneyGainRatePerSecond * 0.001, "other");
} else {
const server = GetServer(node);
if (!(server instanceof HacknetServer)) throw new Error(`Server ${node} is not a hacknet server.`);
2022-09-13 00:00:09 +02:00
Player.hashManager.storeHashes(server.hashRate * 0.001);
}
}
2022-09-13 00:00:09 +02:00
if (Player.bladeburner) {
Player.bladeburner.rank += 0.00001;
}
2022-09-13 00:00:09 +02:00
if (Player.corporation) {
Player.corporation.funds += Player.corporation.revenue * 0.01;
}
}
2021-11-03 01:34:03 +01:00
return (
<>
<br />
<Button onClick={EatNoodles}>Eat noodles</Button>
</>
);
2021-09-05 01:09:30 +02:00
}
2021-09-29 01:38:51 +02:00
function CreateCorporation(): React.ReactElement {
const [open, setOpen] = useState(false);
2022-09-13 00:00:09 +02:00
if (!Player.canAccessCorporation()) {
2021-09-05 01:09:30 +02:00
return (
<>
2021-09-25 21:34:12 +02:00
<Typography>
2022-06-04 02:10:52 +02:00
<i>A businessman is yelling at a clerk. You should come back later.</i>
2021-09-25 21:34:12 +02:00
</Typography>
2021-09-05 01:09:30 +02:00
</>
);
2021-08-19 22:37:59 +02:00
}
2021-09-05 01:09:30 +02:00
return (
2021-09-29 01:38:51 +02:00
<>
NETSCRIPT: ns.sleeve.getSleeve added. getPlayer and getSleeve can both be used for formulas. (#200) * BREAKING CHANGE: Removed getSleeveStats and getSleeveInformation because this info is provided by getSleeve in a more usable form. * BREAKING CHANGE: Removed tor, inBladeburner, and hasCorporation fields from ns.getPlayer. Functionality still exists via added functions ns.hasTorRouter, ns.corporation.hasCorporation, and ns.bladeburner.inBladeburner. * Separated ns definitions for Person, Sleeve, and Player interfaces with both Player and Sleeve just extending Person. Added getSleeve, which provides a Sleeve object similar to getPlayer. * Renamed the sleeve ns layer's interface as sleeve lowercase because of name conflict. todo: May move all the ns layers interface names to lowercase for consistency * Added ns.formulas.work.crimeSuccessChance and reworked to allow both sleeve and player calculations. * Removed internal Person.getIntelligenceBonus function which was just a wrapper for calculateIntelligenceBonus. Any use of the former in formulas creates a conflict where ns-provided Person objects throw an error. * Renamed helpers.player to helpers.person for netscript person validation. Reduced number of fields validated due to Person being a smaller interface. * Fixed bug in bladeburner where Player multipliers and int were being used no matter which person was performing the task * Fixed leak of Player.jobs at ns.getPlayer * Person / Player / Sleeve classes now implement the netscript equivalent interfaces. Netscript helper for person no longer asserts that it's a real Person class member, only that it's a Person interface. Functions that use netscript persons have been changed to expect just a person interface to prevent needing this incorrect type assertion.
2022-11-09 13:26:26 +01:00
<Button disabled={!Player.canAccessCorporation() || !!Player.corporation} onClick={() => setOpen(true)}>
2021-09-29 01:38:51 +02:00
Create a Corporation
</Button>
<CreateCorporationModal open={open} onClose={() => setOpen(false)} />
</>
2021-09-05 01:09:30 +02:00
);
}
2022-03-19 05:22:21 +01:00
function renderGrafting(): React.ReactElement {
2022-09-13 00:00:09 +02:00
if (!Player.canAccessGrafting()) {
2021-09-18 09:00:07 +02:00
return <></>;
}
return (
<Button onClick={handleGrafting} sx={{ my: 5 }}>
Enter the secret lab
</Button>
);
2021-09-05 01:09:30 +02:00
}
2021-09-25 23:21:50 +02:00
function handleCotMG(): void {
const faction = Factions[FactionNames.ChurchOfTheMachineGod];
2022-09-13 00:00:09 +02:00
if (!Player.factions.includes(FactionNames.ChurchOfTheMachineGod)) {
2021-09-25 23:21:50 +02:00
joinFaction(faction);
}
if (
2022-09-13 00:00:09 +02:00
!Player.augmentations.some((a) => a.name === AugmentationNames.StaneksGift1) &&
!Player.queuedAugmentations.some((a) => a.name === AugmentationNames.StaneksGift1)
2021-09-25 23:21:50 +02:00
) {
2021-11-18 00:08:58 +01:00
applyAugmentation({ name: AugmentationNames.StaneksGift1, level: 1 });
2021-09-25 23:21:50 +02:00
}
2022-09-13 00:00:09 +02:00
Router.toStaneksGift();
2021-09-25 23:21:50 +02:00
}
function renderCotMG(): React.ReactElement {
2022-09-13 00:00:09 +02:00
const toStanek = <Button onClick={() => Router.toStaneksGift()}>Open Stanek's Gift</Button>;
2021-09-25 23:21:50 +02:00
// prettier-ignore
2022-03-19 05:22:21 +01:00
const symbol = <Typography sx={{ lineHeight: '1em', whiteSpace: 'pre' }}>
{" `` "}<br />
{" -odmmNmds: "}<br />
{" `hNmo:..-omNh. "}<br />
{" yMd` `hNh "}<br />
{" mMd oNm "}<br />
{" oMNo .mM/ "}<br />
{" `dMN+ -mM+ "}<br />
{" -mMNo -mN+ "}<br />
{" .+- :mMNo/mN/ "}<br />
{":yNMd. :NMNNN/ "}<br />
{"-mMMMh. /NMMh` "}<br />
{" .dMMMd. /NMMMy` "}<br />
{" `yMMMd. /NNyNMMh` "}<br />
{" `sMMMd. +Nm: +NMMh. "}<br />
{" oMMMm- oNm: /NMMd. "}<br />
{" +NMMmsMm- :mMMd. "}<br />
{" /NMMMm- -mMMd. "}<br />
{" /MMMm- -mMMd. "}<br />
{" `sMNMMm- .mMmo "}<br />
{" `sMd:hMMm. ./. "}<br />
{" `yMy` `yNMd` "}<br />
{" `hMs` oMMy "}<br />
{" `hMh sMN- "}<br />
{" /MM- .NMo "}<br />
{" +MM: :MM+ "}<br />
{" sNNo-.`.-omNy` "}<br />
{" -smNNNNmdo- "}<br />
{" `..` "}</Typography>
2022-09-13 00:00:09 +02:00
if (Player.hasAugmentation(AugmentationNames.StaneksGift3, true)) {
2021-10-27 03:22:40 +02:00
return (
<>
<Typography>
<i>
Allison "Mother" Stanek: ..can ...you hear them too ...? Come now, don't be shy and let me get a closer
2022-10-09 07:25:31 +02:00
look at you. Yes wonderful, I see my creation has taken root without consequence or much ill effect it
2021-10-27 03:22:40 +02:00
seems. Curious, Just how much of a machine's soul do you house in that body?
</i>
</Typography>
2022-04-12 23:18:36 +02:00
<br />
{toStanek}
<br />
2021-10-27 03:22:40 +02:00
{symbol}
</>
);
}
2022-09-13 00:00:09 +02:00
if (Player.hasAugmentation(AugmentationNames.StaneksGift2, true)) {
2021-10-27 03:22:40 +02:00
return (
<>
<Typography>
<i>
2022-06-04 02:10:52 +02:00
Allison "Mother" Stanek: I see you've taken to my creation. So much that it could hardly be recognized as
2021-12-12 18:46:56 +01:00
one of my own after your tinkering with it. I see you follow the ways of the Machine God as I do, and your
mastery of the gift clearly demonstrates that. My hopes are climbing by the day for you.
2021-10-27 03:22:40 +02:00
</i>
</Typography>
2022-04-12 23:18:36 +02:00
<br />
{toStanek}
<br />
2021-10-27 03:22:40 +02:00
{symbol}
</>
);
}
2022-09-13 00:00:09 +02:00
if (Player.factions.includes(FactionNames.ChurchOfTheMachineGod)) {
2021-09-25 23:21:50 +02:00
return (
2021-10-04 02:34:36 +02:00
<>
<Typography>
<i>Allison "Mother" Stanek: Welcome back my child!</i>
</Typography>
2022-04-12 23:18:36 +02:00
<br />
{toStanek}
<br />
2021-09-25 23:21:50 +02:00
{symbol}
2021-10-04 02:34:36 +02:00
</>
2021-09-25 23:21:50 +02:00
);
}
2022-09-13 00:00:09 +02:00
if (!Player.canAccessCotMG()) {
2021-09-25 23:21:50 +02:00
return (
<>
2021-10-04 02:34:36 +02:00
<Typography>
A decrepit altar stands in the middle of a dilapidated church.
<br />
<br />A symbol is carved in the altar.
</Typography>
2021-09-25 23:21:50 +02:00
<br />
{symbol}
</>
);
}
if (
2022-09-13 00:00:09 +02:00
Player.augmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0 ||
Player.queuedAugmentations.filter((a) => a.name !== AugmentationNames.NeuroFluxGovernor).length > 0
2021-09-25 23:21:50 +02:00
) {
return (
2021-10-04 02:34:36 +02:00
<>
<Typography>
<i>
2021-09-25 23:21:50 +02:00
Allison "Mother" Stanek: Begone you filth! My gift must be the first modification that your body should
have!
</i>
2021-10-04 02:34:36 +02:00
</Typography>
</>
2021-09-25 23:21:50 +02:00
);
}
return (
2021-10-04 02:34:36 +02:00
<>
<Typography>
<i>
2021-09-25 23:21:50 +02:00
Allison "Mother" Stanek: Welcome child, I see your body is pure. Are you ready to ascend beyond our human
form? If you are, accept my gift.
</i>
2021-10-04 02:34:36 +02:00
</Typography>
2021-09-25 23:21:50 +02:00
<Button onClick={handleCotMG}>Accept Stanek's Gift</Button>
{symbol}
2021-10-04 02:34:36 +02:00
</>
2021-09-25 23:21:50 +02:00
);
}
2021-12-05 05:47:53 +01:00
function renderGlitch(): React.ReactElement {
return (
<>
<Typography>
2022-01-09 03:32:23 +01:00
<CorruptableText content={"An eerie aura surrounds this area. You feel you should leave."} />
2021-12-05 05:47:53 +01:00
</Typography>
</>
);
}
2021-09-18 09:00:07 +02:00
switch (props.loc.name) {
case LocationName.NewTokyoVitaLife: {
2022-03-19 05:22:21 +01:00
return renderGrafting();
2021-09-18 09:00:07 +02:00
}
case LocationName.Sector12CityHall: {
return (BitNodeMultipliers.CorporationSoftcap < 0.15 && <></>) || <CreateCorporation />;
2021-09-18 09:00:07 +02:00
}
case LocationName.Sector12NSA: {
return renderBladeburner();
}
case LocationName.NewTokyoNoodleBar: {
return renderNoodleBar();
}
2021-09-25 23:21:50 +02:00
case LocationName.ChongqingChurchOfTheMachineGod: {
return renderCotMG();
}
2021-12-05 05:47:53 +01:00
case LocationName.IshimaGlitch: {
return renderGlitch();
}
2022-03-11 21:19:10 +01:00
case LocationName.NewTokyoArcade: {
return <ArcadeRoot />;
}
2021-09-18 09:00:07 +02:00
default:
console.error(`Location ${props.loc.name} doesn't have any special properties`);
return <></>;
2021-09-05 01:09:30 +02:00
}
}