UI: Show hints of Sleeves mechanic in pre-endgame (#2605)

This commit is contained in:
catloversg
2026-04-01 03:29:49 +07:00
committed by GitHub
parent 3e44f08a0f
commit 7425d8a8fd
6 changed files with 132 additions and 57 deletions

View File

@@ -370,7 +370,7 @@ export function initBitNodes() {
<ul>
<li>
Sleeve: Duplicate your consciousness into Synthoids, allowing you to perform different tasks asynchronously.
You cannot buy Sleeves outside this BitNode.
You cannot buy Sleeves or upgrade them outside this BitNode.
</li>
<li>
Grafting: Visit VitaLife in New Tokyo to get access to this technology. It allows you to graft

View File

@@ -196,7 +196,7 @@ achieved immortality - at least for those that could afford it.
This BitNode unlocks Sleeve and Grafting technology:
- Sleeve: Duplicate your consciousness into Synthoids, allowing you to perform different tasks asynchronously. You cannot buy Sleeves outside this BitNode.
- Sleeve: Duplicate your consciousness into Synthoids, allowing you to perform different tasks asynchronously. You cannot buy Sleeves or upgrade them outside this BitNode.
- Grafting: Visit VitaLife in New Tokyo to get access to this technology. It allows you to graft augmentations, which is an alternative way of installing augmentations.
Destroying this BitNode will give you Source-File 10, or if you already have this Source-File, it will upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve and Grafting API in other BitNodes. Each level of this Source-File also grants you a Sleeve.

View File

@@ -39,6 +39,7 @@ import { SpecialServers } from "../Server/data/SpecialServers";
import { CONSTANTS } from "../Constants";
import { BladeburnerConstants } from "../Bladeburner/data/Constants";
import type { PlayerObject } from "../PersonObjects/Player/PlayerObject";
import { CovenantCampaign } from "./ui/CovenantCampaign";
interface FactionInfoParams {
infoText?: JSX.Element;
@@ -51,7 +52,7 @@ interface FactionInfoParams {
offerSecurityWork?: boolean;
special?: boolean;
keepOnInstall?: boolean;
assignment?: () => React.ReactElement;
campaign?: () => React.ReactElement;
}
/** Contains the "information" property for all the Factions, which is just a description of each faction */
@@ -65,10 +66,10 @@ export class FactionInfo {
/** The hint to show about how to get invited to this faction. */
rumorText: JSX.Element;
/** Conditions for being automatically inivited to this facton. */
/** Conditions for being automatically invited to this faction. */
inviteReqs: CompoundPlayerCondition;
/** Conditions for automatically hearing a rumor about this facton. */
/** Conditions for automatically hearing a rumor about this faction. */
rumorReqs: CompoundPlayerCondition;
/** A flag indicating if the faction supports field work to earn reputation. */
@@ -87,7 +88,7 @@ export class FactionInfo {
special: boolean;
/** The data to display on the faction screen. */
assignment?: () => React.ReactElement;
campaign?: () => React.ReactElement;
constructor(params: FactionInfoParams) {
this.infoText = params.infoText ?? <></>;
@@ -101,7 +102,7 @@ export class FactionInfo {
this.keep = params.keepOnInstall ?? false;
this.special = params.special ?? false;
this.assignment = params.assignment;
this.campaign = params.campaign;
}
offersWork(): boolean {
@@ -170,6 +171,9 @@ export const FactionInfos: Record<FactionName, FactionInfo> = {
],
offerHackingWork: true,
offerFieldWork: true,
campaign: () => {
return <CovenantCampaign />;
},
}),
// Megacorporations, each forms its own faction
@@ -706,7 +710,7 @@ export const FactionInfos: Record<FactionName, FactionInfo> = {
offerFieldWork: false,
offerSecurityWork: false,
special: true,
assignment: (): React.ReactElement => {
campaign: (): React.ReactElement => {
return (
<Option
buttonText={"Open Bladeburner headquarters"}
@@ -768,7 +772,7 @@ export const FactionInfos: Record<FactionName, FactionInfo> = {
offerSecurityWork: false,
special: true,
keepOnInstall: true,
assignment: (): React.ReactElement => {
campaign: (): React.ReactElement => {
return (
<Option
buttonText={"Open Stanek's Gift"}
@@ -803,7 +807,7 @@ export const FactionInfos: Record<FactionName, FactionInfo> = {
offerSecurityWork: false,
special: true,
keepOnInstall: true,
assignment: (): React.ReactElement => {
campaign: (): React.ReactElement => {
return <Typography>{FactionName.ShadowsOfAnarchy} can only gain reputation by infiltrating.</Typography>;
},
}),

View File

@@ -0,0 +1,63 @@
import Typography from "@mui/material/Typography";
import { Player } from "@player";
import React, { useState } from "react";
import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
import { Modal } from "../../ui/React/Modal";
import { Option } from "./Option";
import { knowAboutBitverse } from "../../BitNode/BitNodeUtils";
function CovenantIncompleteCampaign() {
const [open, setOpen] = useState(false);
return (
<>
<Option
buttonText={"Research"}
infoText={"The Beginning of True Immortality"}
onClick={() => setOpen(true)}
></Option>
<Modal open={open} onClose={() => setOpen(false)}>
<Typography component="div">
You tried your best to help the research team, but this research isn't making any progress.
<br />
<br />
{knowAboutBitverse() ? (
"Maybe this research can only be completed in BitNode 10?"
) : (
<>
Research data is always randomly corrupted for unknown reasons, and a weird message is sent to you every
time it happens:
<br />
<br />
#@)($*&@__Y0U__^%$#@&*()__HAV3__(&@#*$%(@
<br />
()@#*$%(__N0T__@&$#)@*(__S33N__)(*@#&$)(
<br />
@&*($#@&__TH3__#@A&#@*)(@$#@)*
<br />
%$#@&()@__TRU1H__()*@#$&()@#$
</>
)}
</Typography>
</Modal>
</>
);
}
export function CovenantCampaign() {
const [open, setOpen] = useState(false);
if (Player.bitNodeN !== 10) {
return <CovenantIncompleteCampaign />;
}
return (
<>
<Option
buttonText={"Purchase & Upgrade Duplicate Sleeves"}
infoText={"Purchase Duplicate Sleeves and upgrades. These are permanent!"}
onClick={() => setOpen(true)}
></Option>
<CovenantPurchasesRoot open={open} onClose={() => setOpen(false)} />
</>
);
}

View File

@@ -3,7 +3,7 @@
* This is the component for displaying a single faction's UI, not the list of all
* accessible factions
*/
import React, { useState } from "react";
import React from "react";
import { DonateOption } from "./DonateOption";
import { Info } from "./Info";
@@ -16,12 +16,12 @@ import { Page } from "../../ui/Router";
import { Player } from "@player";
import { Typography, Button } from "@mui/material";
import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
import { FactionName, FactionWorkType } from "@enums";
import { FactionWorkType } from "@enums";
import { GangButton } from "./GangButton";
import { FactionWork } from "../../Work/FactionWork";
import { useCycleRerender } from "../../ui/React/hooks";
import { favorNeededToDonate } from "../formulas/donation";
import { knowAboutBitverse } from "../../BitNode/BitNodeUtils";
type FactionRootProps = {
faction: Faction;
@@ -47,7 +47,6 @@ const augmentationsInfo =
"As your reputation with this faction rises, you will " +
"unlock augmentations, which you can purchase to enhance " +
"your abilities.";
const sleevePurchasesInfo = "Purchase Duplicate Sleeves and upgrades. These are permanent!";
interface IMainProps {
faction: Faction;
@@ -56,7 +55,6 @@ interface IMainProps {
}
function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.ReactElement {
const [sleevesOpen, setSleevesOpen] = useState(false);
const factionInfo = faction.getInfo();
function startWork(): void {
@@ -105,7 +103,6 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
// should be shown
const favorToDonate = favorNeededToDonate();
const canDonate = faction.favor >= favorToDonate;
const canPurchaseSleeves = faction.name === FactionName.TheCovenant && Player.bitNodeN === 10;
return (
<>
@@ -115,33 +112,41 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
</Typography>
<Info faction={faction} factionInfo={factionInfo} />
<GangButton faction={faction} />
{!isPlayersGang && factionInfo.offerHackingWork && (
<Option
buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo}
onClick={() => startHackingContracts(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerFieldWork && (
<Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={() => startFieldWork(faction)} />
)}
{!isPlayersGang && factionInfo.offerSecurityWork && (
<Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={() => startSecurityWork(faction)} />
)}
{!isPlayersGang && factionInfo.offersWork() && (
<DonateOption faction={faction} rerender={rerender} favorToDonate={favorToDonate} disabled={!canDonate} />
)}
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={onAugmentations} />
{canPurchaseSleeves && (
{!isPlayersGang && (
<>
<Option
buttonText={"Purchase & Upgrade Duplicate Sleeves"}
infoText={sleevePurchasesInfo}
onClick={() => setSleevesOpen(true)}
/>
<CovenantPurchasesRoot open={sleevesOpen} onClose={() => setSleevesOpen(false)} />
{factionInfo.offersWork() && (
<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.&nbsp;
{knowAboutBitverse() && <>Note that the passive reputation gain is disabled in some BitNodes. </>}
Earning reputation will allow you to purchase augmentations through this faction, which are powerful
upgrades that enhance your abilities.
</Typography>
)}
{factionInfo.offerHackingWork && (
<Option
buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo}
onClick={() => startHackingContracts(faction)}
/>
)}
{factionInfo.offerFieldWork && (
<Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={() => startFieldWork(faction)} />
)}
{factionInfo.offerSecurityWork && (
<Option
buttonText={"Security Work"}
infoText={securityWorkInfo}
onClick={() => startSecurityWork(faction)}
/>
)}
{factionInfo.offersWork() && (
<DonateOption faction={faction} rerender={rerender} favorToDonate={favorToDonate} disabled={!canDonate} />
)}
</>
)}
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={onAugmentations} />
</>
);
}

View File

@@ -9,33 +9,20 @@ import { FactionInfo } from "../FactionInfo";
import Typography from "@mui/material/Typography";
import { useCycleRerender } from "../../ui/React/hooks";
import { knowAboutBitverse } from "../../BitNode/BitNodeUtils";
import { ReputationInfo } from "../../ui/React/ReputationInfo";
import { FavorInfo } from "../../ui/React/FavorInfo";
import Tooltip from "@mui/material/Tooltip";
import InfoIcon from "@mui/icons-material/Info";
import Grade from "@mui/icons-material/Grade";
interface IProps {
faction: Faction;
factionInfo: FactionInfo;
}
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.&nbsp;
{knowAboutBitverse() && <>Note that the passive reputation gain is disabled in some BitNodes. </>}
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 {
useCycleRerender();
const Assignment = props.factionInfo.assignment ?? DefaultAssignment;
return (
<>
<Typography sx={{ whiteSpace: "pre-wrap" }}>{props.factionInfo.infoText}</Typography>
@@ -50,7 +37,23 @@ export function Info(props: IProps): React.ReactElement {
<Typography>-------------------------</Typography>
<FavorInfo favor={props.faction.favor} />
<Typography>-------------------------</Typography>
<Assignment />
<Typography variant="h5" style={{ display: "flex", alignItems: "center" }}>
<Grade style={{ fontSize: "1.1em", marginRight: "10px" }} />
Special Campaign
<Tooltip
title={
<>
Some factions are developing special campaigns for researching breakthrough technology or executing
initiatives. Some campaigns may be complete, while others remain unfinished. Explore them now, and return
later if a campaign is not yet complete to see what unfolds.
</>
}
>
<InfoIcon sx={{ fontSize: "0.8em", marginLeft: "10px" }} />
</Tooltip>
</Typography>
{props.factionInfo.campaign ? props.factionInfo.campaign() : <Typography>None</Typography>}
<Typography>-------------------------</Typography>
</>
);
}