diff --git a/assets/Steam/achievements/icons/darknet-depths.svg b/assets/Steam/achievements/icons/darknet-depths.svg new file mode 100644 index 000000000..74d323a19 --- /dev/null +++ b/assets/Steam/achievements/icons/darknet-depths.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + diff --git a/dist/icons/achievements/darknet-depths.svg b/dist/icons/achievements/darknet-depths.svg new file mode 100644 index 000000000..bd34261c4 --- /dev/null +++ b/dist/icons/achievements/darknet-depths.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + diff --git a/src/Achievements/AchievementData.json b/src/Achievements/AchievementData.json index 096ce6aa2..46ec2e1b7 100644 --- a/src/Achievements/AchievementData.json +++ b/src/Achievements/AchievementData.json @@ -416,6 +416,11 @@ "Name": "Make your Own Network", "Description": "Install a backdoor on 50 or more darknet servers at once." }, + "DARKNET_DEPTHS": { + "ID": "DARKNET_DEPTHS", + "Name": "Into the Depths", + "Description": "Install the augment from the deepest server." + }, "CHALLENGE_BN1": { "ID": "CHALLENGE_BN1", "Name": "BN1: Challenge", @@ -474,7 +479,7 @@ "CHALLENGE_BN15": { "ID": "CHALLENGE_BN15", "Name": "BN15: Challenge", - "Description": "Open the cache on the final lab in the darknet." + "Description": "Complete BN15 without ever calling dnet.heartbleed." }, "BYPASS": { "ID": "BYPASS", diff --git a/src/Achievements/Achievements.ts b/src/Achievements/Achievements.ts index 286659aaa..13f6ce3ff 100644 --- a/src/Achievements/Achievements.ts +++ b/src/Achievements/Achievements.ts @@ -38,6 +38,7 @@ import { Go } from "../Go/Go"; import { type AchievementId, type SFAchievementId, SFAchievementIds } from "./Types"; import { getAllMovableDarknetServers } from "../DarkNet/utils/darknetNetworkUtils"; +import { DarknetState } from "../DarkNet/models/DarknetState"; function assertAchievements( achievements: typeof data.achievements, @@ -591,6 +592,13 @@ export const achievements: Record = { Condition: () => getAllMovableDarknetServers().filter((s) => s.backdoorInstalled).length >= 50, NotInSteam: true, }, + DARKNET_DEPTHS: { + ...achievementData.DARKNET_DEPTHS, + Icon: "darknet-depths", + Visible: knowAboutBitverse, + Condition: () => Player.augmentations.some((a) => a.name === AugmentationName.TheSword), + NotInSteam: true, + }, CHALLENGE_BN1: { ...achievementData.CHALLENGE_BN1, Icon: "BN1+", @@ -685,7 +693,7 @@ export const achievements: Record = { ...achievementData.CHALLENGE_BN15, Icon: "BN15+", Visible: knowAboutBitverse, - Condition: () => Player.augmentations.some((a) => a.name === AugmentationName.TheSword), + Condition: () => Player.bitNodeN === 15 && isBitNodeFinished() && !DarknetState.hasUsedHeartbleed, NotInSteam: true, }, BYPASS: { diff --git a/src/DarkNet/effects/SaveLoad.ts b/src/DarkNet/effects/SaveLoad.ts index 7a8dc78a8..3fadfc31a 100644 --- a/src/DarkNet/effects/SaveLoad.ts +++ b/src/DarkNet/effects/SaveLoad.ts @@ -3,11 +3,13 @@ import { assertObject } from "../../utils/TypeAssertion"; export type DarknetSaveFormat = { storedCycles: number; + hasUsedHeartbleed: boolean; }; export function getDarkNetSave(): DarknetSaveFormat { return { storedCycles: Math.floor(DarknetState.storedCycles), + hasUsedHeartbleed: DarknetState.hasUsedHeartbleed, }; } @@ -18,11 +20,12 @@ export function loadDarkNet(saveString: unknown): void { try { const parsedData: unknown = JSON.parse(saveString); assertObject(parsedData); - const { storedCycles } = parsedData; + const { storedCycles, hasUsedHeartbleed } = parsedData; if (typeof storedCycles !== "number" || !Number.isFinite(storedCycles)) { throw new Error(`Invalid storedCycles: ${storedCycles}`); } DarknetState.storedCycles = storedCycles < 0 ? 0 : storedCycles; + DarknetState.hasUsedHeartbleed = Boolean(hasUsedHeartbleed); } catch (error) { console.error(error); console.error("Invalid DarkNet data:", saveString); diff --git a/src/DarkNet/models/DarknetState.ts b/src/DarkNet/models/DarknetState.ts index 9ded8e374..8b6f2e301 100644 --- a/src/DarkNet/models/DarknetState.ts +++ b/src/DarkNet/models/DarknetState.ts @@ -28,6 +28,7 @@ export const DarknetState = { nextMutation: Promise.resolve(), nextMutationResolver: null as (() => void) | null, storedCycles: 0, + hasUsedHeartbleed: false, cyclesSinceLastMutation: 0, Network: new Array(MAX_NET_DEPTH).fill(null).map(() => new Array(NET_WIDTH).fill(null)), diff --git a/src/DarkNet/ui/NetworkDisplayWrapper.tsx b/src/DarkNet/ui/NetworkDisplayWrapper.tsx index 31dd164e9..7d0f0625f 100644 --- a/src/DarkNet/ui/NetworkDisplayWrapper.tsx +++ b/src/DarkNet/ui/NetworkDisplayWrapper.tsx @@ -15,7 +15,7 @@ import { useRerender } from "../../ui/React/hooks"; import { DarknetEvents, DarknetState } from "../models/DarknetState"; import { SpecialServers } from "../../Server/data/SpecialServers"; import { drawOnCanvas, getPixelPosition } from "./networkCanvas"; -import { dnetStyles } from "./dnetStyles"; +import { dnetStyles, DWServerLogStyles } from "./dnetStyles"; import { getLabyrinthDetails, isLabyrinthServer } from "../effects/labyrinth"; import { DarknetServer } from "../../Server/DarknetServer"; import { getAllDarknetServers } from "../utils/darknetNetworkUtils"; @@ -358,7 +358,12 @@ export function NetworkDisplayWrapper(): React.ReactElement { Darknet Docs diff --git a/src/NetscriptFunctions/Darknet.ts b/src/NetscriptFunctions/Darknet.ts index b0f618f90..97ca603b7 100644 --- a/src/NetscriptFunctions/Darknet.ts +++ b/src/NetscriptFunctions/Darknet.ts @@ -247,6 +247,7 @@ export function NetscriptDarknet(): InternalAPI { logger(ctx)( `Attempting to extract data from ${server.hostname}... (Est: ${formatNumber(networkDelay / 1000, 1)}s)`, ); + DarknetState.hasUsedHeartbleed = true; if (Player.skills.charisma < server.requiredCharismaSkill) { logger(ctx)( @@ -263,6 +264,7 @@ export function NetscriptDarknet(): InternalAPI { return helpers.netscriptDelay(ctx, networkDelay).then(() => { const xpGained = Player.mults.charisma_exp * 50 * ((500 + Player.skills.charisma) / 500); Player.gainCharismaExp(xpGained); + const onlineConnectionCheck = getFailureResult(ctx, targetHost, { requireDirectConnection: true }); if (!onlineConnectionCheck.success) { return {