mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2025-03-07 11:04:36 +01:00
UI: Automatically show Bitverse UI if BN is finished (#1358)
This commit is contained in:
@ -19,7 +19,6 @@ import { HacknetNode } from "../Hacknet/HacknetNode";
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
import { Player } from "@player";
|
||||
import { GetAllServers, GetServer } from "../Server/AllServers";
|
||||
import { SpecialServers } from "../Server/data/SpecialServers";
|
||||
import { Server } from "../Server/Server";
|
||||
import { Router } from "../ui/GameRoot";
|
||||
import { Page } from "../ui/Router";
|
||||
@ -30,7 +29,7 @@ import { workerScripts } from "../Netscript/WorkerScripts";
|
||||
|
||||
import { getRecordValues } from "../Types/Record";
|
||||
import { ServerConstants } from "../Server/data/Constants";
|
||||
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
|
||||
|
||||
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
|
||||
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
|
||||
@ -61,15 +60,8 @@ export interface AchievementData {
|
||||
Description: string;
|
||||
}
|
||||
|
||||
function bitNodeFinishedState(): boolean {
|
||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||
if (!(wd instanceof Server)) return false;
|
||||
if (wd.backdoorInstalled) return true;
|
||||
return Player.bladeburner !== null && Player.bladeburner.numBlackOpsComplete >= blackOpsArray.length;
|
||||
}
|
||||
|
||||
function hasAccessToSF(bn: number): boolean {
|
||||
return Player.bitNodeN === bn || Player.sourceFileLvl(bn) > 0;
|
||||
function canAccessBitNodeFeature(bitNode: number): boolean {
|
||||
return Player.bitNodeN === bitNode || Player.sourceFileLvl(bitNode) > 0;
|
||||
}
|
||||
|
||||
function knowsAboutBitverse(): boolean {
|
||||
@ -338,25 +330,25 @@ export const achievements: Record<string, Achievement> = {
|
||||
GANG: {
|
||||
...achievementData.GANG,
|
||||
Icon: "GANG",
|
||||
Visible: () => hasAccessToSF(2),
|
||||
Visible: () => canAccessBitNodeFeature(2),
|
||||
Condition: () => Player.gang !== null,
|
||||
},
|
||||
FULL_GANG: {
|
||||
...achievementData.FULL_GANG,
|
||||
Icon: "GANGMAX",
|
||||
Visible: () => hasAccessToSF(2),
|
||||
Visible: () => canAccessBitNodeFeature(2),
|
||||
Condition: () => Player.gang !== null && Player.gang.members.length === GangConstants.MaximumGangMembers,
|
||||
},
|
||||
GANG_TERRITORY: {
|
||||
...achievementData.GANG_TERRITORY,
|
||||
Icon: "GANG100%",
|
||||
Visible: () => hasAccessToSF(2),
|
||||
Visible: () => canAccessBitNodeFeature(2),
|
||||
Condition: () => Player.gang !== null && AllGangs[Player.gang.facName].territory >= 0.999,
|
||||
},
|
||||
GANG_MEMBER_POWER: {
|
||||
...achievementData.GANG_MEMBER_POWER,
|
||||
Icon: "GANG10000",
|
||||
Visible: () => hasAccessToSF(2),
|
||||
Visible: () => canAccessBitNodeFeature(2),
|
||||
Condition: () =>
|
||||
Player.gang !== null &&
|
||||
Player.gang.members.some(
|
||||
@ -367,19 +359,19 @@ export const achievements: Record<string, Achievement> = {
|
||||
CORPORATION: {
|
||||
...achievementData.CORPORATION,
|
||||
Icon: "CORP",
|
||||
Visible: () => hasAccessToSF(3),
|
||||
Visible: () => canAccessBitNodeFeature(3),
|
||||
Condition: () => Player.corporation !== null,
|
||||
},
|
||||
CORPORATION_BRIBE: {
|
||||
...achievementData.CORPORATION_BRIBE,
|
||||
Icon: "CORPLOBBY",
|
||||
Visible: () => hasAccessToSF(3),
|
||||
Visible: () => canAccessBitNodeFeature(3),
|
||||
Condition: () => !!Player.corporation && Player.corporation.unlocks.has(CorpUnlockName.GovernmentPartnership),
|
||||
},
|
||||
CORPORATION_PROD_1000: {
|
||||
...achievementData.CORPORATION_PROD_1000,
|
||||
Icon: "CORP1000",
|
||||
Visible: () => hasAccessToSF(3),
|
||||
Visible: () => canAccessBitNodeFeature(3),
|
||||
Condition: () => {
|
||||
if (!Player.corporation) return false;
|
||||
for (const division of Player.corporation.divisions.values()) {
|
||||
@ -391,7 +383,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
CORPORATION_EMPLOYEE_3000: {
|
||||
...achievementData.CORPORATION_EMPLOYEE_3000,
|
||||
Icon: "CORPCITY",
|
||||
Visible: () => hasAccessToSF(3),
|
||||
Visible: () => canAccessBitNodeFeature(3),
|
||||
Condition: (): boolean => {
|
||||
if (!Player.corporation) return false;
|
||||
for (const division of Player.corporation.divisions.values()) {
|
||||
@ -406,7 +398,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
Icon: "CORPRE",
|
||||
Name: "Own the land",
|
||||
Description: "Expand to the Real Estate division.",
|
||||
Visible: () => hasAccessToSF(3),
|
||||
Visible: () => canAccessBitNodeFeature(3),
|
||||
Condition: () => {
|
||||
if (!Player.corporation) return false;
|
||||
for (const division of Player.corporation.divisions.values()) {
|
||||
@ -418,26 +410,26 @@ export const achievements: Record<string, Achievement> = {
|
||||
INTELLIGENCE_255: {
|
||||
...achievementData.INTELLIGENCE_255,
|
||||
Icon: "INT255",
|
||||
Visible: () => hasAccessToSF(5),
|
||||
Visible: () => canAccessBitNodeFeature(5),
|
||||
Condition: () => Player.skills.intelligence >= 255,
|
||||
},
|
||||
BLADEBURNER_DIVISION: {
|
||||
...achievementData.BLADEBURNER_DIVISION,
|
||||
Icon: "BLADE",
|
||||
Visible: () => hasAccessToSF(6),
|
||||
Visible: () => canAccessBitNodeFeature(6),
|
||||
Condition: () => Player.bladeburner !== null,
|
||||
},
|
||||
BLADEBURNER_OVERCLOCK: {
|
||||
...achievementData.BLADEBURNER_OVERCLOCK,
|
||||
Icon: "BLADEOVERCLOCK",
|
||||
Visible: () => hasAccessToSF(6),
|
||||
Visible: () => canAccessBitNodeFeature(6),
|
||||
Condition: () =>
|
||||
Player.bladeburner?.getSkillLevel(BladeSkillName.overclock) === Skills[BladeSkillName.overclock].maxLvl,
|
||||
},
|
||||
BLADEBURNER_UNSPENT_100000: {
|
||||
...achievementData.BLADEBURNER_UNSPENT_100000,
|
||||
Icon: "BLADE100K",
|
||||
Visible: () => hasAccessToSF(6),
|
||||
Visible: () => canAccessBitNodeFeature(6),
|
||||
Condition: () => Player.bladeburner !== null && Player.bladeburner.skillPoints >= 100000,
|
||||
},
|
||||
"4S": {
|
||||
@ -448,21 +440,21 @@ export const achievements: Record<string, Achievement> = {
|
||||
FIRST_HACKNET_SERVER: {
|
||||
...achievementData.FIRST_HACKNET_SERVER,
|
||||
Icon: "HASHNET",
|
||||
Visible: () => hasAccessToSF(9),
|
||||
Visible: () => canAccessBitNodeFeature(9),
|
||||
Condition: () => hasHacknetServers() && Player.hacknetNodes.length > 0,
|
||||
AdditionalUnlock: [achievementData.FIRST_HACKNET_NODE.ID],
|
||||
},
|
||||
ALL_HACKNET_SERVER: {
|
||||
...achievementData.ALL_HACKNET_SERVER,
|
||||
Icon: "HASHNETALL",
|
||||
Visible: () => hasAccessToSF(9),
|
||||
Visible: () => canAccessBitNodeFeature(9),
|
||||
Condition: () => hasHacknetServers() && Player.hacknetNodes.length === HacknetServerConstants.MaxServers,
|
||||
AdditionalUnlock: [achievementData["30_HACKNET_NODE"].ID],
|
||||
},
|
||||
MAX_HACKNET_SERVER: {
|
||||
...achievementData.MAX_HACKNET_SERVER,
|
||||
Icon: "HASHNETALL",
|
||||
Visible: () => hasAccessToSF(9),
|
||||
Visible: () => canAccessBitNodeFeature(9),
|
||||
Condition: (): boolean => {
|
||||
if (!hasHacknetServers()) return false;
|
||||
for (const h of Player.hacknetNodes) {
|
||||
@ -484,14 +476,14 @@ export const achievements: Record<string, Achievement> = {
|
||||
HACKNET_SERVER_1B: {
|
||||
...achievementData.HACKNET_SERVER_1B,
|
||||
Icon: "HASHNETMONEY",
|
||||
Visible: () => hasAccessToSF(9),
|
||||
Visible: () => canAccessBitNodeFeature(9),
|
||||
Condition: () => hasHacknetServers() && Player.moneySourceB.hacknet >= 1e9,
|
||||
AdditionalUnlock: [achievementData.HACKNET_NODE_10M.ID],
|
||||
},
|
||||
MAX_CACHE: {
|
||||
...achievementData.MAX_CACHE,
|
||||
Icon: "HASHNETCAP",
|
||||
Visible: () => hasAccessToSF(9),
|
||||
Visible: () => canAccessBitNodeFeature(9),
|
||||
Condition: () =>
|
||||
hasHacknetServers() &&
|
||||
Player.hashManager.hashes === Player.hashManager.capacity &&
|
||||
@ -500,7 +492,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
SLEEVE_8: {
|
||||
...achievementData.SLEEVE_8,
|
||||
Icon: "SLEEVE8",
|
||||
Visible: () => hasAccessToSF(10),
|
||||
Visible: () => canAccessBitNodeFeature(10),
|
||||
Condition: () => Player.sleeves.length === 8 && Player.sourceFileLvl(10) === 3,
|
||||
},
|
||||
INDECISIVE: {
|
||||
@ -523,7 +515,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
...achievementData.FAST_BN,
|
||||
Icon: "2DAYS",
|
||||
Visible: knowsAboutBitverse,
|
||||
Condition: () => bitNodeFinishedState() && Player.playtimeSinceLastBitnode < 1000 * 60 * 60 * 24 * 2,
|
||||
Condition: () => isBitNodeFinished() && Player.playtimeSinceLastBitnode < 1000 * 60 * 60 * 24 * 2,
|
||||
},
|
||||
CHALLENGE_BN1: {
|
||||
...achievementData.CHALLENGE_BN1,
|
||||
@ -531,57 +523,57 @@ export const achievements: Record<string, Achievement> = {
|
||||
Visible: knowsAboutBitverse,
|
||||
Condition: () =>
|
||||
Player.bitNodeN === 1 &&
|
||||
bitNodeFinishedState() &&
|
||||
isBitNodeFinished() &&
|
||||
Player.getHomeComputer().maxRam <= 128 &&
|
||||
Player.getHomeComputer().cpuCores === 1,
|
||||
},
|
||||
CHALLENGE_BN2: {
|
||||
...achievementData.CHALLENGE_BN2,
|
||||
Icon: "BN2+",
|
||||
Visible: () => hasAccessToSF(2),
|
||||
Condition: () => Player.bitNodeN === 2 && bitNodeFinishedState() && Player.gang === null,
|
||||
Visible: () => canAccessBitNodeFeature(2),
|
||||
Condition: () => Player.bitNodeN === 2 && isBitNodeFinished() && Player.gang === null,
|
||||
},
|
||||
CHALLENGE_BN3: {
|
||||
...achievementData.CHALLENGE_BN3,
|
||||
Icon: "BN3+",
|
||||
Visible: () => hasAccessToSF(3),
|
||||
Condition: () => Player.bitNodeN === 3 && bitNodeFinishedState() && Player.corporation === null,
|
||||
Visible: () => canAccessBitNodeFeature(3),
|
||||
Condition: () => Player.bitNodeN === 3 && isBitNodeFinished() && Player.corporation === null,
|
||||
},
|
||||
CHALLENGE_BN6: {
|
||||
...achievementData.CHALLENGE_BN6,
|
||||
Icon: "BN6+",
|
||||
Visible: () => hasAccessToSF(6),
|
||||
Condition: () => Player.bitNodeN === 6 && bitNodeFinishedState() && Player.bladeburner === null,
|
||||
Visible: () => canAccessBitNodeFeature(6),
|
||||
Condition: () => Player.bitNodeN === 6 && isBitNodeFinished() && Player.bladeburner === null,
|
||||
},
|
||||
CHALLENGE_BN7: {
|
||||
...achievementData.CHALLENGE_BN7,
|
||||
Icon: "BN7+",
|
||||
Visible: () => hasAccessToSF(7),
|
||||
Condition: () => Player.bitNodeN === 7 && bitNodeFinishedState() && Player.bladeburner === null,
|
||||
Visible: () => canAccessBitNodeFeature(7),
|
||||
Condition: () => Player.bitNodeN === 7 && isBitNodeFinished() && Player.bladeburner === null,
|
||||
},
|
||||
CHALLENGE_BN8: {
|
||||
...achievementData.CHALLENGE_BN8,
|
||||
Icon: "BN8+",
|
||||
Visible: () => hasAccessToSF(8),
|
||||
Condition: () => Player.bitNodeN === 8 && bitNodeFinishedState() && !Player.has4SData && !Player.has4SDataTixApi,
|
||||
Visible: () => canAccessBitNodeFeature(8),
|
||||
Condition: () => Player.bitNodeN === 8 && isBitNodeFinished() && !Player.has4SData && !Player.has4SDataTixApi,
|
||||
},
|
||||
CHALLENGE_BN9: {
|
||||
...achievementData.CHALLENGE_BN9,
|
||||
Icon: "BN9+",
|
||||
Visible: () => hasAccessToSF(9),
|
||||
Visible: () => canAccessBitNodeFeature(9),
|
||||
Condition: () =>
|
||||
Player.bitNodeN === 9 &&
|
||||
bitNodeFinishedState() &&
|
||||
isBitNodeFinished() &&
|
||||
Player.moneySourceB.hacknet === 0 &&
|
||||
Player.moneySourceB.hacknet_expenses === 0,
|
||||
},
|
||||
CHALLENGE_BN10: {
|
||||
...achievementData.CHALLENGE_BN10,
|
||||
Icon: "BN10+",
|
||||
Visible: () => hasAccessToSF(10),
|
||||
Visible: () => canAccessBitNodeFeature(10),
|
||||
Condition: () =>
|
||||
Player.bitNodeN === 10 &&
|
||||
bitNodeFinishedState() &&
|
||||
isBitNodeFinished() &&
|
||||
!Player.sleeves.some(
|
||||
(s) =>
|
||||
s.augmentations.length > 0 ||
|
||||
@ -596,7 +588,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
CHALLENGE_BN12: {
|
||||
...achievementData.CHALLENGE_BN12,
|
||||
Icon: "BN12+",
|
||||
Visible: () => hasAccessToSF(12),
|
||||
Visible: () => canAccessBitNodeFeature(12),
|
||||
Condition: () => Player.sourceFileLvl(12) >= 50,
|
||||
},
|
||||
BYPASS: {
|
||||
@ -657,10 +649,10 @@ export const achievements: Record<string, Achievement> = {
|
||||
CHALLENGE_BN13: {
|
||||
...achievementData.CHALLENGE_BN13,
|
||||
Icon: "BN13+",
|
||||
Visible: () => hasAccessToSF(13),
|
||||
Visible: () => canAccessBitNodeFeature(13),
|
||||
Condition: () =>
|
||||
Player.bitNodeN === 13 &&
|
||||
bitNodeFinishedState() &&
|
||||
isBitNodeFinished() &&
|
||||
!Player.augmentations.some((a) => a.name === AugmentationName.StaneksGift1),
|
||||
},
|
||||
DEVMENU: {
|
||||
|
11
src/BitNode/BitNodeUtils.ts
Normal file
11
src/BitNode/BitNodeUtils.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { GetServer } from "../Server/AllServers";
|
||||
import { Server } from "../Server/Server";
|
||||
import { SpecialServers } from "../Server/data/SpecialServers";
|
||||
|
||||
export function isBitNodeFinished(): boolean {
|
||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||
if (!(wd instanceof Server)) {
|
||||
throw new Error("WorldDaemon is not a normal server. This is a bug. Please contact developers.");
|
||||
}
|
||||
return wd.backdoorInstalled;
|
||||
}
|
@ -8,11 +8,23 @@ import { Router } from "../../ui/GameRoot";
|
||||
import { Page } from "../../ui/Router";
|
||||
import { CorruptableText } from "../../ui/React/CorruptableText";
|
||||
import { blackOpsArray } from "../data/BlackOperations";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
import { SpecialServers } from "../../Server/data/SpecialServers";
|
||||
import { Server } from "../../Server/Server";
|
||||
|
||||
interface BlackOpPageProps {
|
||||
bladeburner: Bladeburner;
|
||||
}
|
||||
|
||||
function finishBitNode() {
|
||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||
if (!(wd instanceof Server)) {
|
||||
throw new Error("WorldDaemon is not a normal server. This is a bug. Please contact developers.");
|
||||
}
|
||||
wd.backdoorInstalled = true;
|
||||
Router.toPage(Page.BitVerse, { flume: false, quick: false });
|
||||
}
|
||||
|
||||
export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactElement {
|
||||
const blackOperations = blackOpsArray.slice(0, bladeburner.numBlackOpsComplete + 1).reverse();
|
||||
|
||||
@ -33,8 +45,8 @@ export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactEleme
|
||||
losses.
|
||||
</Typography>
|
||||
{bladeburner.numBlackOpsComplete >= blackOpsArray.length ? (
|
||||
<Button sx={{ my: 1, p: 1 }} onClick={() => Router.toPage(Page.BitVerse, { flume: false, quick: false })}>
|
||||
<CorruptableText content="Destroy w0rld_d34mon" spoiler={false}></CorruptableText>
|
||||
<Button sx={{ my: 1, p: 1 }} onClick={finishBitNode}>
|
||||
<CorruptableText content="Destroy w0r1d_d43m0n" spoiler={false}></CorruptableText>
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
|
@ -1121,7 +1121,9 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||
if (cbScript === null) throw helpers.errorMessage(ctx, `Could not resolve file path: ${_cbScript}`);
|
||||
|
||||
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 (!(wd instanceof Server)) {
|
||||
throw new Error("WorldDaemon is not a normal server. This is a bug. Please contact developers.");
|
||||
}
|
||||
const hackingRequirements = () => {
|
||||
if (Player.skills.hacking < wd.requiredHackingSkill) return false;
|
||||
if (!wd.hasAdminRights) return false;
|
||||
|
@ -72,6 +72,7 @@ import { MathJaxContext } from "better-react-mathjax";
|
||||
import { useRerender } from "./React/hooks";
|
||||
import { HistoryProvider } from "./React/Documentation";
|
||||
import { GoRoot } from "../Go/ui/GoRoot";
|
||||
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
|
||||
|
||||
const htmlLocation = location;
|
||||
|
||||
@ -108,16 +109,24 @@ export let Router: IRouter = {
|
||||
},
|
||||
};
|
||||
|
||||
function determineStartPage() {
|
||||
if (RecoveryMode) return Page.Recovery;
|
||||
if (Player.currentWork !== null) return Page.Work;
|
||||
return Page.Terminal;
|
||||
function determineStartPage(): PageWithContext {
|
||||
if (RecoveryMode) {
|
||||
return { page: Page.Recovery };
|
||||
}
|
||||
if (isBitNodeFinished()) {
|
||||
// Go to BitVerse UI without animation.
|
||||
return { page: Page.BitVerse, flume: false, quick: true };
|
||||
}
|
||||
if (Player.currentWork !== null) {
|
||||
return { page: Page.Work };
|
||||
}
|
||||
return { page: Page.Terminal };
|
||||
}
|
||||
|
||||
export function GameRoot(): React.ReactElement {
|
||||
const { classes } = useStyles();
|
||||
|
||||
const [pages, setPages] = useState<PageWithContext[]>(() => [{ page: determineStartPage() }]);
|
||||
const [pages, setPages] = useState<PageWithContext[]>(() => [determineStartPage()]);
|
||||
const pageWithContext = pages[0];
|
||||
|
||||
const setNextPage = (pageWithContext: PageWithContext) =>
|
||||
|
Reference in New Issue
Block a user