UI: Automatically show Bitverse UI if BN is finished (#1358)

This commit is contained in:
catloversg
2024-06-09 03:54:44 +07:00
committed by GitHub
parent a354867fc4
commit a0fc9cc713
5 changed files with 83 additions and 57 deletions

View File

@ -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: {

View 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;
}

View File

@ -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>
) : (
<>

View File

@ -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;

View File

@ -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) =>