From 1b6b07faae7ea9d94c75a46b2454f15ebd3aae0c Mon Sep 17 00:00:00 2001 From: Michael Ficocelli Date: Thu, 19 Mar 2026 21:07:43 -0700 Subject: [PATCH] DNET: Player feedback (#2545) --- markdown/bitburner.darknet.authenticate.md | 6 +- .../bitburner.darknet.connecttosession.md | 4 +- markdown/bitburner.darknet.md | 8 +- markdown/bitburner.multipliers.dnet_money.md | 13 ++++ markdown/bitburner.multipliers.md | 19 +++++ src/Augmentation/Augmentation.ts | 1 + src/Augmentation/Augmentations.ts | 39 +++++----- src/Augmentation/ui/PlayerMultipliers.tsx | 7 ++ src/BitNode/BitNode.tsx | 12 +++ src/CodingContract/Contract.ts | 5 ++ src/CodingContract/ContractGenerator.ts | 3 +- src/DarkNet/controllers/NetworkGenerator.ts | 1 + src/DarkNet/controllers/NetworkMovement.ts | 6 +- src/DarkNet/effects/cacheFiles.ts | 74 ++++++++++++------- src/DarkNet/effects/effects.ts | 21 +++--- src/DarkNet/effects/labyrinth.ts | 2 +- src/DarkNet/effects/offlineServerHandling.ts | 4 +- src/DarkNet/effects/phishing.ts | 31 ++++---- src/DarkNet/effects/ramblock.ts | 5 +- src/DarkNet/models/DarknetServerOptions.ts | 10 ++- src/DarkNet/models/DarknetState.ts | 1 + src/DarkNet/models/packetSniffing.ts | 2 +- src/DarkNet/ui/NetworkDisplayWrapper.tsx | 6 +- src/DarkNet/ui/PasswordPrompt.tsx | 2 +- src/DarkNet/utils/darknetNetworkUtils.ts | 2 +- src/Documentation/pages.ts | 2 + src/NetscriptFunctions/CodingContract.ts | 6 +- src/NetscriptFunctions/Darknet.ts | 2 +- .../Grafting/EntropyAccumulation.ts | 1 + src/PersonObjects/Multipliers.ts | 4 + .../Player/PlayerObjectGeneralMethods.ts | 17 +++-- src/PersonObjects/formulas/reputation.ts | 5 +- src/ScriptEditor/NetscriptDefinitions.d.ts | 14 +++- src/Terminal/Terminal.ts | 6 +- src/ui/CharacterStats.tsx | 6 ++ test/jest/Darknet/Darknet.test.ts | 4 +- test/jest/Netscript/Darknet.test.ts | 2 +- test/jest/__snapshots__/FullSave.test.ts.snap | 3 + 38 files changed, 248 insertions(+), 108 deletions(-) create mode 100644 markdown/bitburner.multipliers.dnet_money.md diff --git a/markdown/bitburner.darknet.authenticate.md b/markdown/bitburner.darknet.authenticate.md index 27166ee32..24f454fab 100644 --- a/markdown/bitburner.darknet.authenticate.md +++ b/markdown/bitburner.darknet.authenticate.md @@ -6,10 +6,12 @@ Sends a network request to try to authenticate on a darkweb server. The target server must be directly connected to the server that the script is running on. The speed of authentication scales with the number of threads used. -If successful, grants the script a session, allowing it to exec() scripts on that server, or scp() files to it. (scp() \*from\* the server is always allowed.) +If successful, grants the current script a session, allowing it to exec() scripts on that server, or scp() files to it. (scp() \*from\* the server is always allowed.) Note that the charisma level on a server is not a requirement for authentication, but authentication takes longer if the player's charisma is below the server's charisma level. +Note that the session granted is only for the current script instance (by PID) - other running scripts will need to use connectToSession with the correct password to also get a session with the target server. + **Signature:** ```typescript @@ -92,5 +94,5 @@ A promise that resolves to a [DarknetResult](./bitburner.darknetresult.md) objec ## Remarks -RAM cost: 0.6 GB +RAM cost: 0.4 GB diff --git a/markdown/bitburner.darknet.connecttosession.md b/markdown/bitburner.darknet.connecttosession.md index 489db2955..5342f239d 100644 --- a/markdown/bitburner.darknet.connecttosession.md +++ b/markdown/bitburner.darknet.connecttosession.md @@ -6,10 +6,12 @@ Attempts to connect to a target darkweb server that you have previously authenticated on. Unlike `authenticate`, connectToSession can be used to get a session on servers at any distance. -If successful, grants the script a session, allowing it to scp() files from that target. It also allows starting scripts with exec() on that target, if the target is directly connected to the server that the script is running on, or has a backdoor or stasis link. +If successful, grants the script a session, allowing it to scp() files to that target. It also allows starting scripts with exec() on that target, if the target is directly connected to the server that the script is running on, or has a backdoor or stasis link. If unsuccessful, more detail may be able to be gathered by using heartbleed() to look at the resulting logs on the server. +Note that the session granted is only for the current script instance (by PID) - other running scripts will need to use connectToSession with the correct password to also get a session with the target server. + **Signature:** ```typescript diff --git a/markdown/bitburner.darknet.md b/markdown/bitburner.darknet.md index 8c68db256..0804617e9 100644 --- a/markdown/bitburner.darknet.md +++ b/markdown/bitburner.darknet.md @@ -34,10 +34,12 @@ Description Sends a network request to try to authenticate on a darkweb server. The target server must be directly connected to the server that the script is running on. The speed of authentication scales with the number of threads used. -If successful, grants the script a session, allowing it to exec() scripts on that server, or scp() files to it. (scp() \*from\* the server is always allowed.) +If successful, grants the current script a session, allowing it to exec() scripts on that server, or scp() files to it. (scp() \*from\* the server is always allowed.) Note that the charisma level on a server is not a requirement for authentication, but authentication takes longer if the player's charisma is below the server's charisma level. +Note that the session granted is only for the current script instance (by PID) - other running scripts will need to use connectToSession with the correct password to also get a session with the target server. + @@ -49,10 +51,12 @@ Note that the charisma level on a server is not a requirement for authentication Attempts to connect to a target darkweb server that you have previously authenticated on. Unlike `authenticate`, connectToSession can be used to get a session on servers at any distance. -If successful, grants the script a session, allowing it to scp() files from that target. It also allows starting scripts with exec() on that target, if the target is directly connected to the server that the script is running on, or has a backdoor or stasis link. +If successful, grants the script a session, allowing it to scp() files to that target. It also allows starting scripts with exec() on that target, if the target is directly connected to the server that the script is running on, or has a backdoor or stasis link. If unsuccessful, more detail may be able to be gathered by using heartbleed() to look at the resulting logs on the server. +Note that the session granted is only for the current script instance (by PID) - other running scripts will need to use connectToSession with the correct password to also get a session with the target server. + diff --git a/markdown/bitburner.multipliers.dnet_money.md b/markdown/bitburner.multipliers.dnet_money.md new file mode 100644 index 000000000..66ea7ce54 --- /dev/null +++ b/markdown/bitburner.multipliers.dnet_money.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [bitburner](./bitburner.md) > [Multipliers](./bitburner.multipliers.md) > [dnet\_money](./bitburner.multipliers.dnet_money.md) + +## Multipliers.dnet\_money property + +Multiplier to amount of money gained from phishing and caches on darknet servers + +**Signature:** + +```typescript +dnet_money: number; +``` diff --git a/markdown/bitburner.multipliers.md b/markdown/bitburner.multipliers.md index 35bc695e8..aa97bbc11 100644 --- a/markdown/bitburner.multipliers.md +++ b/markdown/bitburner.multipliers.md @@ -318,6 +318,25 @@ number Multiplier to dexterity skill + + + +[dnet\_money](./bitburner.multipliers.dnet_money.md) + + + + + + + +number + + + + +Multiplier to amount of money gained from phishing and caches on darknet servers + + diff --git a/src/Augmentation/Augmentation.ts b/src/Augmentation/Augmentation.ts index 5b666795c..231c95df3 100644 --- a/src/Augmentation/Augmentation.ts +++ b/src/Augmentation/Augmentation.ts @@ -40,6 +40,7 @@ export interface AugmentationCtorParams { faction_rep?: number; crime_money?: number; crime_success?: number; + dnet_money?: number; work_money?: number; hacknet_node_money?: number; hacknet_node_purchase_cost?: number; diff --git a/src/Augmentation/Augmentations.ts b/src/Augmentation/Augmentations.ts index 68b7a2c8b..693b90219 100644 --- a/src/Augmentation/Augmentations.ts +++ b/src/Augmentation/Augmentations.ts @@ -1859,9 +1859,11 @@ export const Augmentations: Record = (() => { "Created by a mysterious figure known only as 'The Sculptor', this augmentation appears as a set of silvery " + "metallic patterns on the user's upper back and shoulders. " + "Awarded to those who discover the secrets of the labyrinth.", - stats: "This augmentation increases the stasis link limit by one, and raises charisma by 15% and agility by 10%.", - charisma: 1.15, + stats: + "This augmentation increases the stasis link limit by one, and raises charisma by 5% and agility by 10%, and darknet money by 30%.", + charisma: 1.05, agility: 1.1, + dnet_money: 1.3, isSpecial: true, factions: [], }, @@ -1873,9 +1875,9 @@ export const Augmentations: Record = (() => { "Its creator, the enigmatic Sculptor, refuses to reveal the details of how it works, and only mutters about 'liveware APIs'. " + "Awarded to those who discover the secrets of the labyrinth.", stats: - "This augmentation increases the speed of authentication and heartbleed by 20%, and raises charisma and dexterity by 20%.", - charisma: 1.2, - dexterity: 1.2, + "This augmentation increases the speed of authentication and heartbleed by 20%, and raises charisma and dexterity by 6%.", + charisma: 1.06, + dexterity: 1.06, isSpecial: true, prereqs: [AugmentationName.TheBrokenWings], factions: [], @@ -1888,9 +1890,10 @@ export const Augmentations: Record = (() => { "Appearing as a simple insignia on the user's forarm, its true function is unknown. It is said to be one of the tools of The Sculptor. " + "Awarded to those who discover the secrets of the labyrinth.", stats: - "This augmentation increases the stasis link limit by one, and raises charisma by 35% and strength by 25%.", - charisma: 1.35, - strength: 1.25, + "This augmentation increases the stasis link limit by one, and raises charisma by 7%, strength by 10%, and darknet money by 10%.", + charisma: 1.07, + strength: 1.1, + dnet_money: 1.1, isSpecial: true, prereqs: [AugmentationName.TheBoots], factions: [], @@ -1902,9 +1905,10 @@ export const Augmentations: Record = (() => { "This skeletal augmentation greatly enhances the users durability and health. Inspired by the original Staff of Medicine that is said to " + "have been given to Daedalus as a reward for the completion of the Labyrinth, which all modern augments are a descendant of. ", stats: - "This augmentation increases the stasis link limit by one, and raises charisma xp by 20% and defense by 30%.", - charisma_exp: 1.2, - defense: 1.3, + "This augmentation increases the stasis link limit by one, and raises charisma xp, defense, and darknet money by 10%.", + charisma_exp: 1.1, + defense: 1.1, + dnet_money: 1.1, isSpecial: true, prereqs: [AugmentationName.TheHammer], factions: [], @@ -1917,10 +1921,10 @@ export const Augmentations: Record = (() => { "This augmentation enhances the user's ability to assess probabilities, predict outcomes, and adapt strategies in real-time, " + "making them exceptionally persuasive and confident in negotiations and social interactions. " + "Awarded to those who discover the secrets of the labyrinth.", - stats: "This augmentation raises charisma by 40%, hacking by 10%, and company rep by 5%.", - charisma: 1.4, - hacking: 1.1, + stats: "This augmentation raises charisma by 9%, company rep by 5%, and darknet money by 15%.", + charisma: 1.09, company_rep: 1.05, + dnet_money: 1.15, isSpecial: true, prereqs: [AugmentationName.TheStaff], factions: [], @@ -1934,10 +1938,11 @@ export const Augmentations: Record = (() => { "turning every interaction into a calculated success. The technique is sometimes referred to as Solomonoff's Lightsaber, as it is a " + "more powerful version of Occam's razor. " + "The final augment awarded to those who discover the secrets of the labyrinth.", - stats: "This augmentation raises charisma by 50%, hacking by 16%, and company rep by 10%.", - charisma: 1.5, - hacking: 1.16, + stats: "This augmentation raises charisma, hacking, darknet money, and company rep by 10%.", + charisma: 1.1, + hacking: 1.1, company_rep: 1.1, + dnet_money: 1.1, isSpecial: true, prereqs: [AugmentationName.TheLaw], factions: [], diff --git a/src/Augmentation/ui/PlayerMultipliers.tsx b/src/Augmentation/ui/PlayerMultipliers.tsx index 93240ef85..c1469dbe7 100644 --- a/src/Augmentation/ui/PlayerMultipliers.tsx +++ b/src/Augmentation/ui/PlayerMultipliers.tsx @@ -263,6 +263,13 @@ export function PlayerMultipliers(): React.ReactElement { bnMult: currentNodeMults.CrimeMoney, color: Settings.theme.money, }, + { + mult: "Darknet Money", + current: Player.mults.dnet_money, + augmented: Player.mults.dnet_money * mults.dnet_money, + bnMult: currentNodeMults.DarknetMoneyMultiplier, + color: Settings.theme.money, + }, ]; if (Player.canAccessBladeburner() && currentNodeMults.BladeburnerRank > 0) { diff --git a/src/BitNode/BitNode.tsx b/src/BitNode/BitNode.tsx index f9cfeb43f..7c5b7b5e8 100644 --- a/src/BitNode/BitNode.tsx +++ b/src/BitNode/BitNode.tsx @@ -625,6 +625,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier StaneksGiftPowerMultiplier: 0.75, StaneksGiftExtraSize: -2, + DarknetMoneyMultiplier: 0.4, + WorldDaemonDifficulty: 2, }); } @@ -653,6 +655,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier StaneksGiftPowerMultiplier: 1.5, StaneksGiftExtraSize: 0, + DarknetMoneyMultiplier: 0.4, + WorldDaemonDifficulty: 3, }); } @@ -682,6 +686,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier StaneksGiftPowerMultiplier: 1.3, StaneksGiftExtraSize: 0, + DarknetMoneyMultiplier: 0.7, + WorldDaemonDifficulty: 1.5, }); } @@ -830,6 +836,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier StaneksGiftPowerMultiplier: 0.5, StaneksGiftExtraSize: 2, + DarknetMoneyMultiplier: 0.5, + WorldDaemonDifficulty: 2, }); } @@ -873,6 +881,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier StaneksGiftPowerMultiplier: 0.75, StaneksGiftExtraSize: -3, + DarknetMoneyMultiplier: 0.4, + WorldDaemonDifficulty: 2, }); } @@ -946,6 +956,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier ManualHackMoney: dec, ScriptHackMoney: dec, CodingContractMoney: dec, + DarknetMoneyMultiplier: dec, ClassGymExpGain: dec, CompanyWorkExpGain: dec, @@ -1025,6 +1036,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier StaneksGiftPowerMultiplier: 2, StaneksGiftExtraSize: 1, + DarknetMoneyMultiplier: 0.5, WorldDaemonDifficulty: 3, }); diff --git a/src/CodingContract/Contract.ts b/src/CodingContract/Contract.ts index 3b4b966ea..209de01d2 100644 --- a/src/CodingContract/Contract.ts +++ b/src/CodingContract/Contract.ts @@ -55,6 +55,9 @@ export class CodingContract { processed outside of this file */ reward: ICodingContractReward | null; + /* Scalar for the reward, used to generate lower-value CCTs more frequently */ + rewardScaling: number = 1; + /* Number of times the Contract has been attempted */ tries = 0; @@ -65,6 +68,7 @@ export class CodingContract { fn = "default.cct", type = CodingContractName.FindLargestPrimeFactor, reward: ICodingContractReward | null = null, + rewardScaling: number = 1, ) { const path = resolveContractFilePath(fn); if (!path) { @@ -78,6 +82,7 @@ export class CodingContract { this.type = type; this.state = CodingContractTypes[type].generate(); this.reward = reward; + this.rewardScaling = rewardScaling; } getAnswer() { diff --git a/src/CodingContract/ContractGenerator.ts b/src/CodingContract/ContractGenerator.ts index bb3cb8632..472bf7021 100644 --- a/src/CodingContract/ContractGenerator.ts +++ b/src/CodingContract/ContractGenerator.ts @@ -130,6 +130,7 @@ interface IGenerateContractParams { server?: string; filename?: ContractFilePath; reward?: ICodingContractReward; + rewardScaling?: number; } export function generateContract(params: IGenerateContractParams): void { @@ -163,7 +164,7 @@ export function generateContract(params: IGenerateContractParams): void { if (filename == null) { return; } - const contract = new CodingContract(filename, problemType, reward); + const contract = new CodingContract(filename, problemType, reward, params.rewardScaling); server.addContract(contract); } diff --git a/src/DarkNet/controllers/NetworkGenerator.ts b/src/DarkNet/controllers/NetworkGenerator.ts index 81c52a71d..8c5d363ff 100644 --- a/src/DarkNet/controllers/NetworkGenerator.ts +++ b/src/DarkNet/controllers/NetworkGenerator.ts @@ -80,6 +80,7 @@ export function initDarkwebServer(): void { darkweb.isStationary = true; darkweb.hasAdminRights = true; darkweb.blockedRam = 0; + darkweb.maxRam = 16; darkweb.scripts = scripts; darkweb.contracts = contracts; if (hasTOR) { diff --git a/src/DarkNet/controllers/NetworkMovement.ts b/src/DarkNet/controllers/NetworkMovement.ts index 733f66573..e466bd0c0 100644 --- a/src/DarkNet/controllers/NetworkMovement.ts +++ b/src/DarkNet/controllers/NetworkMovement.ts @@ -17,7 +17,7 @@ import { getAllDarknetServers, getAllMovableDarknetServers, getAllOpenPositions, - getBackdooredDarkwebServers, + getBackdooredDarknetServers, getDarknetCyclesPerMutation, getIslands, } from "../utils/darknetNetworkUtils"; @@ -89,7 +89,7 @@ export const mutateDarknet = (): void => { } if (Math.random() < 0.1) { - const backdooredServers = getBackdooredDarkwebServers(); + const backdooredServers = getBackdooredDarknetServers(); const server = backdooredServers[Math.floor(Math.random() * backdooredServers.length)]; if (server) { restartServer(server); @@ -98,7 +98,7 @@ export const mutateDarknet = (): void => { } if (Math.random() < 0.05) { - const backdooredServers = getBackdooredDarkwebServers(); + const backdooredServers = getBackdooredDarknetServers(); const server = backdooredServers[Math.floor(Math.random() * backdooredServers.length)]; if (server) { deleteDarknetServer(server); diff --git a/src/DarkNet/effects/cacheFiles.ts b/src/DarkNet/effects/cacheFiles.ts index 108b81e16..16dd4623d 100644 --- a/src/DarkNet/effects/cacheFiles.ts +++ b/src/DarkNet/effects/cacheFiles.ts @@ -1,27 +1,26 @@ -import { tryGeneratingRandomContract } from "../../CodingContract/ContractGenerator"; +import { generateContract } from "../../CodingContract/ContractGenerator"; import { Player } from "@player"; -import { formatMoney, formatNumber } from "../../ui/formatNumber"; +import { formatMoney } from "../../ui/formatNumber"; import { getLabAugReward, isLabyrinthServer, LAB_CACHE_NAME } from "./labyrinth"; import { SnackbarEvents } from "../../ui/React/Snackbar"; -import { AugmentationName, CompletedProgramName, ToastVariant } from "@enums"; +import { AugmentationName, CompletedProgramName, StockSymbol, ToastVariant } from "@enums"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers"; import { CreateProgramWork } from "../../Work/CreateProgramWork"; -import { initStockMarket, isStockMarketInitialized } from "../../StockMarket/StockMarket"; +import { initStockMarket, isStockMarketInitialized, StockMarket } from "../../StockMarket/StockMarket"; import { cachePrefixes } from "../models/dictionaryData"; import type { DarknetServer } from "../../Server/DarknetServer"; -import { type CacheFilePath, resolveCacheFilePath } from "../../Paths/CacheFilePath"; -import type { CacheResult, Result } from "@nsdefs"; +import { resolveCacheFilePath } from "../../Paths/CacheFilePath"; +import type { CacheResult } from "@nsdefs"; +import { addClue, cctCooldownReached } from "./effects"; -export const generateCacheFilename: (prefix?: string) => CacheFilePath | null = (prefix) => { +export const generateCacheFilename = (isPhishingCache: boolean, prefix?: string) => { const filenamePrefix = prefix ?? cachePrefixes[Math.floor(Math.random() * cachePrefixes.length)]; - return resolveCacheFilePath(`${filenamePrefix}_${Math.random().toString().substring(2, 5)}.cache`); + const suffix = isPhishingCache ? ".d.cache" : ".cache"; + return resolveCacheFilePath(`${filenamePrefix}_${Math.random().toString().substring(2, 5)}${suffix}`); }; -export const addCacheToServer: (server: DarknetServer, prefix?: string) => Result<{ cacheFilename: CacheFilePath }> = ( - server, - prefix, -) => { - const cacheFilename = generateCacheFilename(prefix); +export const addCacheToServer = (server: DarknetServer, isPhishingCache: boolean, prefix?: string) => { + const cacheFilename = generateCacheFilename(isPhishingCache, prefix); if (!cacheFilename) { return { success: false, message: `Cannot generate path. prefix: ${prefix}` }; } @@ -42,9 +41,13 @@ export const getRewardFromCache = (server: DarknetServer, cacheName: string, sup }; } - const rewards = [getMoneyReward, getXpReward, getProgramAndStockMarketRelatedRewards, getCCTReward]; + const rewards = [getMoneyReward, getProgramAndStockMarketRelatedRewards, getStockReward, getDataFileReward]; + if (cacheName.endsWith(".d.cache")) { + // only include ccts from caches generated from phishing attacks + rewards.push(getCCTReward); + } const reward = rewards[Math.floor(Math.random() * rewards.length)]; - const result = reward(difficulty); + const result = reward(difficulty, server); if (!suppressToast) { SnackbarEvents.emit( @@ -62,16 +65,17 @@ export const getRewardFromCache = (server: DarknetServer, cacheName: string, sup }; }; -export const getCCTReward = (difficulty: number): string => { - if (Math.random() < difficulty * 0.2) { +export const getCCTReward = (difficulty: number, server: DarknetServer): string => { + if (!cctCooldownReached()) { return getMoneyReward(difficulty); } - - const contractCount = Math.floor(Math.min(20, difficulty) * 0.2 - 1.5 + Math.random() * 3); - if (contractCount <= 0) { + const contractCount = Math.min(Math.floor(Math.min(20, difficulty) * 0.1 - 1.5 + Math.random() * 3), 3); + if (contractCount < 1) { return getMoneyReward(difficulty); } - tryGeneratingRandomContract(contractCount); + for (let i = 0; i < contractCount; i++) { + generateContract({ server: server.hostname, rewardScaling: 1 / 5 }); + } return `New coding contracts are now available on the network!`; }; @@ -83,16 +87,32 @@ export const getMoneyReward = (difficulty: number): string => { ((200 + Player.skills.charisma) / 200) * sf15_3Factor * Player.mults.crime_money * + Player.mults.dnet_money * currentNodeMults.DarknetMoneyMultiplier; // TODO: adjust balance Player.gainMoney(reward, "darknet"); return `You have discovered a cache with ${formatMoney(reward)}.`; }; -export const getXpReward = (difficulty: number): string => { - const sf15_3Factor = Player.activeSourceFileLvl(15) > 3 ? 1.5 : 1; - const reward = 1.2 ** difficulty * 500 * sf15_3Factor * Player.mults.charisma_exp; // TODO: adjust balance - Player.gainCharismaExp(reward); - return `You have discovered a cache with ${formatNumber(reward, 0)} cha XP.`; +export const getStockReward = (difficulty: number): string => { + if (!isStockMarketInitialized()) { + initStockMarket(); + } + const stockSymbols = Object.keys(StockSymbol); + const randomStock = stockSymbols[Math.floor(Math.random() * stockSymbols.length)]; + const shares = Math.floor(1 + difficulty * 5 + Math.random() * 10); + StockMarket[randomStock].playerShares += shares; + return `You have discovered a stock option cache containing ${shares} shares of ${randomStock}!`; +}; + +export const getDataFileReward = (difficulty: number, server: DarknetServer): string => { + const currentDataFiles = server.textFiles.size; + addClue(server); + addClue(server); + const dataFilesGained = server.textFiles.size - currentDataFiles; + if (dataFilesGained === 0) { + return getMoneyReward(difficulty); + } + return `You have discovered a data file cache!`; }; export const getProgramAndStockMarketRelatedRewards = (difficulty: number): string => { @@ -135,7 +155,7 @@ export const getProgramAndStockMarketRelatedRewards = (difficulty: number): stri return `You have discovered a cache of stolen 4S Data!`; } - return getXpReward(difficulty); + return getMoneyReward(difficulty); }; const getLabReward = (): string => { diff --git a/src/DarkNet/effects/effects.ts b/src/DarkNet/effects/effects.ts index fbfff3dca..f259e9f39 100644 --- a/src/DarkNet/effects/effects.ts +++ b/src/DarkNet/effects/effects.ts @@ -1,7 +1,6 @@ import { Player } from "@player"; import type { DarknetServerData, Person as IPerson } from "@nsdefs"; import { AugmentationName, CompletedProgramName, LiteratureName } from "@enums"; -import { generateContract } from "../../CodingContract/ContractGenerator"; import { commonPasswordDictionary, notebookFileNames, @@ -20,7 +19,7 @@ import { populateDarknet } from "../controllers/NetworkGenerator"; import { getDarknetServer } from "../utils/darknetServerUtils"; import { getAllMovableDarknetServers, - getBackdooredDarkwebServers, + getBackdooredDarknetServers, getNearbyNonEmptyPasswordServer, getStasisLinkServers, } from "../utils/darknetNetworkUtils"; @@ -41,14 +40,9 @@ export const handleSuccessfulAuth = (server: DarknetServer, threads: number, pid server.hasAdminRights = true; addClue(server); - const cctChance = Math.min(0.12, 0.02 * (server.difficulty - 1)); - if (Math.random() < cctChance) { - generateContract({ server: server.hostname }); - } - const chance = 0.1 * 1.05 ** server?.difficulty; if (Math.random() < chance && !isLabyrinthServer(server.hostname)) { - addCacheToServer(server); + addCacheToServer(server, false); } }; @@ -110,7 +104,7 @@ export const calculateAuthenticationTime = ( }; export const getBackdoorAuthTimeDebuff = () => { - const backdooredServerCount = getBackdooredDarkwebServers().length; + const backdooredServerCount = getBackdooredDarknetServers().length; const serverCount = getAllMovableDarknetServers().filter((s) => s.hasAdminRights).length; const safeBackdoors = Math.max(serverCount / (NET_WIDTH * 3), 2); const backdoorSurplus = Math.max(0, backdooredServerCount - safeBackdoors); @@ -132,7 +126,7 @@ export const getMultiplierFromCharisma = (scalar = 1) => { export const calculatePasswordAttemptChaGain = (server: DarknetServerData, threads: number = 1, success = false) => { const baseXpGain = 3; - const difficultyBase = 0.8; + const difficultyBase = 1.1; const xpGain = baseXpGain + difficultyBase ** server.difficulty; const alreadyHackedMult = server.hasAdminRights ? 0.2 : 1; const successMult = success && !server.hasAdminRights ? 10 : 1; @@ -264,7 +258,7 @@ export const setStasisLink = (ctx: NetscriptContext, server: DarknetServer, shou export const chargeServerMigration = (server: DarknetServer, threads = 1) => { const chargeIncrease = ((Player.skills.charisma + 500) / (server.difficulty * 200 + 1000)) * 0.01 * threads; - const xpGained = Player.mults.charisma_exp * 50 * ((200 + Player.skills.charisma) / 200) * threads; + const xpGained = Player.mults.charisma_exp * 5 * threads * server.difficulty; Player.gainCharismaExp(xpGained); const currentCharge = DarknetState.migrationInductionServers.get(server.hostname) ?? 0; const newCharge = Math.min(currentCharge + chargeIncrease, 1); @@ -292,4 +286,9 @@ export const getDarkscapeNavigator = () => { populateDarknet(); }; +export const cctCooldownReached = () => { + const timeSinceLastCCT = new Date().getTime() - DarknetState.lastCctRewardTime.getTime(); + return timeSinceLastCCT > 10 * 60 * 1000; +}; + export const hasFullDarknetAccess = (): boolean => Player.bitNodeN === 15 || Player.activeSourceFileLvl(15) > 0; diff --git a/src/DarkNet/effects/labyrinth.ts b/src/DarkNet/effects/labyrinth.ts index dab18566c..041a3de3d 100644 --- a/src/DarkNet/effects/labyrinth.ts +++ b/src/DarkNet/effects/labyrinth.ts @@ -303,7 +303,7 @@ export const handleLabyrinthPassword = ( server.hasAdminRights = true; const cacheCount = getLabyrinthDetails().name === SpecialServers.BonusLab ? 3 : 1; for (let i = 0; i < cacheCount; i++) { - addCacheToServer(server, LAB_CACHE_NAME); + addCacheToServer(server, false, LAB_CACHE_NAME); } addSessionToServer(labServer, pid); diff --git a/src/DarkNet/effects/offlineServerHandling.ts b/src/DarkNet/effects/offlineServerHandling.ts index a92e66e10..5fc80f943 100644 --- a/src/DarkNet/effects/offlineServerHandling.ts +++ b/src/DarkNet/effects/offlineServerHandling.ts @@ -6,7 +6,7 @@ import { errorMessage } from "../../Netscript/ErrorMessages"; import type { BaseServer } from "../../Server/BaseServer"; import { GetServer } from "../../Server/AllServers"; import { GenericResponseMessage, ResponseCodeEnum } from "../Enums"; -import { getBackdooredDarkwebServers } from "../utils/darknetNetworkUtils"; +import { getBackdooredDarknetServers } from "../utils/darknetNetworkUtils"; import { hasDarknetAccess } from "../utils/darknetAuthUtils"; import { DarknetServer } from "../../Server/DarknetServer"; import { CompletedProgramName } from "../../Enums"; @@ -149,6 +149,6 @@ export function expectRunningOnDarknetServer(ctx: NetscriptContext): DarknetServ } export function getTimeoutChance() { - const backdooredDarknetServerCount = getBackdooredDarkwebServers().length - 2; + const backdooredDarknetServerCount = getBackdooredDarknetServers().length - 2; return Math.max(Math.min(backdooredDarknetServerCount * 0.03, 0.5), 0); } diff --git a/src/DarkNet/effects/phishing.ts b/src/DarkNet/effects/phishing.ts index 6ebce92c1..bb9af7230 100644 --- a/src/DarkNet/effects/phishing.ts +++ b/src/DarkNet/effects/phishing.ts @@ -10,23 +10,20 @@ import { ResponseCodeEnum } from "../Enums"; import { isLabyrinthServer } from "./labyrinth"; export const getPhishingAttackSpeed = () => Math.max(10000 * (400 / (400 + Player.skills.charisma)), 200); -const getPhishingCacheCooldownDuration = () => (hasDarknetBonusTime() ? 12_000 : 24_000); export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServer) => { const threads = ctx.workerScript.scriptRef.threads; - const xpGained = Player.mults.charisma_exp * threads * 50 * ((200 + Player.skills.charisma) / 200); - Player.gainCharismaExp(xpGained); + const xpReward = Player.mults.charisma_exp * threads * 50; - const timeSinceLastRewardCache = new Date().getTime() - DarknetState.lastPhishingCacheTime.getTime(); const rewardCacheChance = 0.005 * Player.mults.crime_success * threads * ((400 + Player.skills.charisma) / 400); - const moneyRewardChance = 0.05 * Player.mults.crime_success * ((100 + Player.skills.charisma) / 100); - const cooldown = getPhishingCacheCooldownDuration(); + const moneyRewardChance = 0.05 * Player.mults.crime_success * ((200 + Player.skills.charisma) / 200); const isLabServer = isLabyrinthServer(server.hostname); - if (timeSinceLastRewardCache > cooldown && Math.random() < rewardCacheChance && !isLabServer) { - addCacheToServer(server); + if (phishingCacheCooldownReached() && Math.random() < rewardCacheChance && !isLabServer) { + addCacheToServer(server, true); + Player.gainCharismaExp(xpReward); DarknetState.lastPhishingCacheTime = new Date(); - const result = `Phishing attack succeeded! Found a cache file. (Gained ${formatNumber(xpGained, 1)} cha xp)`; + const result = `Phishing attack succeeded! Found a cache file. (Gained ${formatNumber(xpReward, 1)} cha xp)`; helpers.log(ctx, () => result); return { success: true, @@ -38,17 +35,19 @@ export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServe const bonusTimeFactor = hasDarknetBonusTime() ? 1.3 : 1; const depthFactor = 0.1 + server.depth * 0.05; const moneyReward = - 2000 * + 500 * Player.mults.crime_money * + Player.mults.dnet_money * depthFactor * threads * - ((200 + Player.skills.charisma) / 200) * + ((400 + Player.skills.charisma) / 400) * bonusTimeFactor * randomFactor * currentNodeMults.DarknetMoneyMultiplier; Player.gainMoney(moneyReward, "darknet"); + Player.gainCharismaExp(xpReward); const result = `Phishing attack succeeded! $${formatNumber(moneyReward, 2)} retrieved. (Gained ${formatNumber( - xpGained, + xpReward, 1, )} cha xp)`; helpers.log(ctx, () => result); @@ -58,7 +57,8 @@ export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServe message: result, }; } - const result = `There were no takers on that phishing attempt. (Gained ${formatNumber(xpGained, 1)} cha xp)`; + Player.gainCharismaExp(xpReward / 4); + const result = `There were no takers on that phishing attempt. (Gained ${formatNumber(xpReward / 4, 1)} cha xp)`; helpers.log(ctx, () => result); return { success: false, @@ -66,3 +66,8 @@ export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServe message: result, }; }; + +const phishingCacheCooldownReached = () => { + const timeSinceLastRewardCache = new Date().getTime() - DarknetState.lastPhishingCacheTime.getTime(); + return timeSinceLastRewardCache > 3 * 60 * 1000; // 3 minutes; +}; diff --git a/src/DarkNet/effects/ramblock.ts b/src/DarkNet/effects/ramblock.ts index c40728c50..8b1e5c2d9 100644 --- a/src/DarkNet/effects/ramblock.ts +++ b/src/DarkNet/effects/ramblock.ts @@ -27,8 +27,7 @@ export const handleRamBlockRemoved = (ctx: NetscriptContext, server: DarknetServ if (server.blockedRam <= 0) { handleRamBlockClearedRewards(server); } - const xpGained = - Player.mults.charisma_exp * threads * 10 * 1.1 ** difficulty * ((200 + Player.skills.charisma) / 200); + const xpGained = Player.mults.charisma_exp * threads * 10 * 1.1 ** difficulty; Player.gainCharismaExp(xpGained); const result = `Liberated ${formatNumber( @@ -48,7 +47,7 @@ export const handleRamBlockRemoved = (ctx: NetscriptContext, server: DarknetServ */ export const handleRamBlockClearedRewards = (server: DarknetServer) => { if (!isLabyrinthServer(server.hostname)) { - addCacheToServer(server); + addCacheToServer(server, false); } if (Math.random() < 0.3) { addClue(server); diff --git a/src/DarkNet/models/DarknetServerOptions.ts b/src/DarkNet/models/DarknetServerOptions.ts index 8a9edf67e..ccdce54ca 100644 --- a/src/DarkNet/models/DarknetServerOptions.ts +++ b/src/DarkNet/models/DarknetServerOptions.ts @@ -17,6 +17,7 @@ import { getRamBlock } from "../effects/ramblock"; import { hasFullDarknetAccess } from "../effects/effects"; import { getFriendlyType, TypeAssertionError } from "../../utils/TypeAssertion"; import { isIPAddress } from "../../Types/strings"; +import { roundToTwo } from "../../utils/helpers/roundToTwo"; export type PasswordResponse = { code: DarknetResponseCode; @@ -58,7 +59,7 @@ export type DarknetServerOptions = { }; export const DnetServerBuilder = (options: DarknetServerOptions): DarknetServer => { - const maxRam = 16 * 2 ** Math.floor(options.difficulty / 4); + const maxRam = getMaxRam(options.difficulty); const ramBlock = options.preventBlockedRam ? 0 : getRamBlock(maxRam); const name = options.name ?? generateDarknetServerName(); @@ -192,3 +193,10 @@ const l33tifyName = (name: string): string => { } return updatedName; }; + +const getMaxRam = (difficulty: number): number => { + const baseRam = 16 * 2 ** Math.floor(difficulty / 6); + const sizeMutations = [0.5, 1, 1, 1.15, 1.4]; + const mutation = sizeMutations[Math.floor(Math.random() * sizeMutations.length)]; + return roundToTwo(Math.max(baseRam * mutation, 16)); +}; diff --git a/src/DarkNet/models/DarknetState.ts b/src/DarkNet/models/DarknetState.ts index 7da5a3336..0419dbf38 100644 --- a/src/DarkNet/models/DarknetState.ts +++ b/src/DarkNet/models/DarknetState.ts @@ -47,6 +47,7 @@ export const DarknetState = { labLocations: { "-1": [1, 1] } as Record, lastPhishingCacheTime: new Date(), + lastCctRewardTime: new Date(), lastStormTime: new Date(), stockPromotions: {} as Record, diff --git a/src/DarkNet/models/packetSniffing.ts b/src/DarkNet/models/packetSniffing.ts index 488e7bf71..0e59a9160 100644 --- a/src/DarkNet/models/packetSniffing.ts +++ b/src/DarkNet/models/packetSniffing.ts @@ -14,7 +14,7 @@ import { exceptionAlert } from "../../utils/helpers/exceptionAlert"; const MAX_LOG_LINES = 200; export const capturePackets = (server: DarknetServer) => { - const BASE_PASSWORD_INCLUSION_RATE = 0.18; + const BASE_PASSWORD_INCLUSION_RATE = 0.12; const DIFFICULTY_MODIFIER = 0.88; const difficulty = server.difficulty * 1.3; const vulnerability = server.modelId === ModelIds.packetSniffer ? 8 : 1; diff --git a/src/DarkNet/ui/NetworkDisplayWrapper.tsx b/src/DarkNet/ui/NetworkDisplayWrapper.tsx index ee64b38d0..db0a54e21 100644 --- a/src/DarkNet/ui/NetworkDisplayWrapper.tsx +++ b/src/DarkNet/ui/NetworkDisplayWrapper.tsx @@ -252,7 +252,7 @@ export function NetworkDisplayWrapper(): React.ReactElement { Dark Net - {instability && ( + {instability > 0 && ( @@ -299,13 +299,13 @@ export function NetworkDisplayWrapper(): React.ReactElement { {DarknetState.Network.slice(0, netDisplayDepth).map((row) => row.map( (server) => - server && ( + !!server && ( ), ), )} - {labyrinth && netDisplayDepth > depth && ( + {!!labyrinth && netDisplayDepth > depth && ( )} diff --git a/src/DarkNet/ui/PasswordPrompt.tsx b/src/DarkNet/ui/PasswordPrompt.tsx index 33395c54f..18f42583f 100644 --- a/src/DarkNet/ui/PasswordPrompt.tsx +++ b/src/DarkNet/ui/PasswordPrompt.tsx @@ -149,7 +149,7 @@ export const PasswordPrompt = ({ server, onClose }: PasswordPromptProps): React.
                   Hint: {server.staticPasswordHint}
                   
- {server.passwordHintData && ( + {!!server.passwordHintData && ( <> Data: {server.passwordHintData}
diff --git a/src/DarkNet/utils/darknetNetworkUtils.ts b/src/DarkNet/utils/darknetNetworkUtils.ts index d330b3b60..aed129413 100644 --- a/src/DarkNet/utils/darknetNetworkUtils.ts +++ b/src/DarkNet/utils/darknetNetworkUtils.ts @@ -86,7 +86,7 @@ export const getAllAdjacentNeighbors = (x: number, y: number): DarknetServer[] = export const getIslands = () => getAllMovableDarknetServers().filter((s) => !s.serversOnNetwork.length); -export const getBackdooredDarkwebServers = (): DarknetServer[] => +export const getBackdooredDarknetServers = (): DarknetServer[] => getAllMovableDarknetServers().filter((s) => !s.hasStasisLink && s.backdoorInstalled); export const getNearbyNonEmptyPasswordServer = (server: DarknetServer, disconnected = false) => { diff --git a/src/Documentation/pages.ts b/src/Documentation/pages.ts index 199be3949..5d917420c 100644 --- a/src/Documentation/pages.ts +++ b/src/Documentation/pages.ts @@ -950,6 +950,7 @@ import nsDoc_bitburner_multipliers_defense_exp_md from "../../markdown/bitburner import nsDoc_bitburner_multipliers_defense_md from "../../markdown/bitburner.multipliers.defense.md?raw"; import nsDoc_bitburner_multipliers_dexterity_exp_md from "../../markdown/bitburner.multipliers.dexterity_exp.md?raw"; import nsDoc_bitburner_multipliers_dexterity_md from "../../markdown/bitburner.multipliers.dexterity.md?raw"; +import nsDoc_bitburner_multipliers_dnet_money_md from "../../markdown/bitburner.multipliers.dnet_money.md?raw"; import nsDoc_bitburner_multipliers_faction_rep_md from "../../markdown/bitburner.multipliers.faction_rep.md?raw"; import nsDoc_bitburner_multipliers_hacking_chance_md from "../../markdown/bitburner.multipliers.hacking_chance.md?raw"; import nsDoc_bitburner_multipliers_hacking_exp_md from "../../markdown/bitburner.multipliers.hacking_exp.md?raw"; @@ -2545,6 +2546,7 @@ AllPages["nsDoc/bitburner.multipliers.defense_exp.md"] = nsDoc_bitburner_multipl AllPages["nsDoc/bitburner.multipliers.defense.md"] = nsDoc_bitburner_multipliers_defense_md; AllPages["nsDoc/bitburner.multipliers.dexterity_exp.md"] = nsDoc_bitburner_multipliers_dexterity_exp_md; AllPages["nsDoc/bitburner.multipliers.dexterity.md"] = nsDoc_bitburner_multipliers_dexterity_md; +AllPages["nsDoc/bitburner.multipliers.dnet_money.md"] = nsDoc_bitburner_multipliers_dnet_money_md; AllPages["nsDoc/bitburner.multipliers.faction_rep.md"] = nsDoc_bitburner_multipliers_faction_rep_md; AllPages["nsDoc/bitburner.multipliers.hacking_chance.md"] = nsDoc_bitburner_multipliers_hacking_chance_md; AllPages["nsDoc/bitburner.multipliers.hacking_exp.md"] = nsDoc_bitburner_multipliers_hacking_exp_md; diff --git a/src/NetscriptFunctions/CodingContract.ts b/src/NetscriptFunctions/CodingContract.ts index 22fdbfaff..6ed4a6d24 100644 --- a/src/NetscriptFunctions/CodingContract.ts +++ b/src/NetscriptFunctions/CodingContract.ts @@ -38,7 +38,11 @@ export function NetscriptCodingContract(): InternalAPI { const resultOfCheckingSolution = contract.isSolution(answer); switch (resultOfCheckingSolution.result) { case CodingContractResult.Success: { - const reward = Player.gainCodingContractReward(contract.reward, contract.getDifficulty()); + const reward = Player.gainCodingContractReward( + contract.reward, + contract.getDifficulty(), + contract.rewardScaling, + ); helpers.log(ctx, () => `Successfully completed Coding Contract '${contract.fn}'. Reward: ${reward}`); server.removeContract(contract.fn); return reward; diff --git a/src/NetscriptFunctions/Darknet.ts b/src/NetscriptFunctions/Darknet.ts index 7c41332d5..5daa73055 100644 --- a/src/NetscriptFunctions/Darknet.ts +++ b/src/NetscriptFunctions/Darknet.ts @@ -426,7 +426,7 @@ export function NetscriptDarknet(): InternalAPI { const server = serverCheck.server; const networkDelay = - calculateAuthenticationTime(server, Player, ctx.workerScript.scriptRef.threads, "", true) * 4; + calculateAuthenticationTime(server, Player, ctx.workerScript.scriptRef.threads, "", true) * 6; const xp = formatNumber(calculatePasswordAttemptChaGain(server, ctx.workerScript.scriptRef.threads), 1); logger(ctx)(`Captured some outgoing transmissions from ${server.hostname}. (Gained ${xp} cha xp)`); diff --git a/src/PersonObjects/Grafting/EntropyAccumulation.ts b/src/PersonObjects/Grafting/EntropyAccumulation.ts index 0998a655a..86d86d60f 100644 --- a/src/PersonObjects/Grafting/EntropyAccumulation.ts +++ b/src/PersonObjects/Grafting/EntropyAccumulation.ts @@ -30,6 +30,7 @@ export const calculateEntropy = (stacks = 1): Multipliers => { crime_money: Player.mults.crime_money * nerf, crime_success: Player.mults.crime_success * nerf, + dnet_money: Player.mults.dnet_money * nerf, hacknet_node_money: Player.mults.hacknet_node_money * nerf, hacknet_node_purchase_cost: Player.mults.hacknet_node_purchase_cost / nerf, diff --git a/src/PersonObjects/Multipliers.ts b/src/PersonObjects/Multipliers.ts index 1976ecb3e..4b966e477 100644 --- a/src/PersonObjects/Multipliers.ts +++ b/src/PersonObjects/Multipliers.ts @@ -25,6 +25,7 @@ export interface Multipliers { work_money: number; crime_success: number; crime_money: number; + dnet_money: number; bladeburner_max_stamina: number; bladeburner_stamina_gain: number; bladeburner_analysis: number; @@ -59,6 +60,7 @@ export const defaultMultipliers = (): Multipliers => { work_money: 1, crime_success: 1, crime_money: 1, + dnet_money: 1, bladeburner_max_stamina: 1, bladeburner_stamina_gain: 1, bladeburner_analysis: 1, @@ -94,6 +96,7 @@ export const mergeMultipliers = (m0: Multipliers, m1: Multipliers): Multipliers work_money: m0.work_money * m1.work_money, crime_success: m0.crime_success * m1.crime_success, crime_money: m0.crime_money * m1.crime_money, + dnet_money: m0.dnet_money * m1.dnet_money, bladeburner_max_stamina: m0.bladeburner_max_stamina * m1.bladeburner_max_stamina, bladeburner_stamina_gain: m0.bladeburner_stamina_gain * m1.bladeburner_stamina_gain, bladeburner_analysis: m0.bladeburner_analysis * m1.bladeburner_analysis, @@ -129,6 +132,7 @@ export const scaleMultipliers = (m0: Multipliers, v: number): Multipliers => { work_money: (m0.work_money - 1) * v + 1, crime_success: (m0.crime_success - 1) * v + 1, crime_money: (m0.crime_money - 1) * v + 1, + dnet_money: (m0.dnet_money - 1) * v + 1, bladeburner_max_stamina: (m0.bladeburner_max_stamina - 1) * v + 1, bladeburner_stamina_gain: (m0.bladeburner_stamina_gain - 1) * v + 1, bladeburner_analysis: (m0.bladeburner_analysis - 1) * v + 1, diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts index 2f612b792..28411faec 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts @@ -499,7 +499,8 @@ export function queueAugmentation(this: PlayerObject, name: AugmentationName): v export function gainCodingContractReward( this: PlayerObject, reward: ICodingContractReward | null, - difficulty = 1, + difficulty: number, + rewardScaling: number, ): string { if (!reward) { return `No reward for this contract`; @@ -509,20 +510,20 @@ export function gainCodingContractReward( case CodingContractRewardType.FactionReputation: { const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork); if (factionsThatAllowHacking.length === 0) { - return this.gainCodingContractReward({ type: CodingContractRewardType.Money }, difficulty); + return this.gainCodingContractReward({ type: CodingContractRewardType.Money }, difficulty, rewardScaling); } const randomFaction = factionsThatAllowHacking[getRandomIntInclusive(0, factionsThatAllowHacking.length - 1)]; - const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty; + const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty * rewardScaling; Factions[randomFaction].playerReputation += repGain; return `Gained ${repGain} faction reputation for ${randomFaction}`; } case CodingContractRewardType.FactionReputationAll: { const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork); if (factionsThatAllowHacking.length === 0) { - return this.gainCodingContractReward({ type: CodingContractRewardType.Money }, difficulty); + return this.gainCodingContractReward({ type: CodingContractRewardType.Money }, difficulty, rewardScaling); } - const totalGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty; + const totalGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty * rewardScaling; const gainPerFaction = Math.floor(totalGain / factionsThatAllowHacking.length); for (const facName of factionsThatAllowHacking) { Factions[facName].playerReputation += gainPerFaction; @@ -542,15 +543,17 @@ export function gainCodingContractReward( : CodingContractRewardType.FactionReputationAll, }, difficulty, + rewardScaling, ); } const randomCompany = companies[getRandomIntInclusive(0, companies.length - 1)]; - const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty; + const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty * rewardScaling; Companies[randomCompany].playerReputation += repGain; return `Gained ${repGain} company reputation for ${randomCompany}`; } case CodingContractRewardType.Money: { - const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * currentNodeMults.CodingContractMoney; + const moneyGain = + CONSTANTS.CodingContractBaseMoneyGain * difficulty * currentNodeMults.CodingContractMoney * rewardScaling; this.gainMoney(moneyGain, "codingcontract"); return `Gained ${formatMoney(moneyGain)}`; } diff --git a/src/PersonObjects/formulas/reputation.ts b/src/PersonObjects/formulas/reputation.ts index 17955642a..17e4059e1 100644 --- a/src/PersonObjects/formulas/reputation.ts +++ b/src/PersonObjects/formulas/reputation.ts @@ -15,7 +15,7 @@ function mult(favor: number): number { export function getHackingWorkRepGain(p: IPerson, favor: number): number { return ( - ((p.skills.hacking + p.skills.intelligence + getDarknetCharismaBonus(p, 0.15) / 3) / CONSTANTS.MaxSkillLevel) * + ((p.skills.hacking + p.skills.intelligence / 3 + getDarknetCharismaBonus(p, 0.1)) / CONSTANTS.MaxSkillLevel) * p.mults.faction_rep * calculateIntelligenceBonus(p.skills.intelligence, 1) * mult(favor) * @@ -30,7 +30,8 @@ export function getFactionSecurityWorkRepGain(p: IPerson, favor: number): number p.skills.defense + p.skills.dexterity + p.skills.agility + - (p.skills.hacking + p.skills.intelligence + getDarknetCharismaBonus(p, 0.3)) * calculateCurrentShareBonus())) / + getDarknetCharismaBonus(p, 0.3) + + (p.skills.hacking + p.skills.intelligence) * calculateCurrentShareBonus())) / CONSTANTS.MaxSkillLevel / 4.5; return t * p.mults.faction_rep * mult(favor) * calculateIntelligenceBonus(p.skills.intelligence, 1); diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 52e5bf18d..b22ab6b28 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -178,6 +178,8 @@ interface Multipliers { crime_money: number; /** Multiplier to crime success rate */ crime_success: number; + /** Multiplier to amount of money gained from phishing and caches on darknet servers */ + dnet_money: number; /** Multiplier to amount of money gained from working */ work_money: number; /** Multiplier to amount of money produced by Hacknet Nodes */ @@ -4497,13 +4499,16 @@ export interface Darknet { * Sends a network request to try to authenticate on a darkweb server. The target server must be directly connected * to the server that the script is running on. The speed of authentication scales with the number of threads used. * - * If successful, grants the script a session, allowing it to exec() scripts on that server, or scp() files to it. (scp() *from* the server is always allowed.) + * If successful, grants the current script a session, allowing it to exec() scripts on that server, or scp() files to it. (scp() *from* the server is always allowed.) * * Note that the charisma level on a server is not a requirement for authentication, but authentication takes longer * if the player's charisma is below the server's charisma level. * + * Note that the session granted is only for the current script instance (by PID) - other running scripts will need to + * use connectToSession with the correct password to also get a session with the target server. + * * @remarks - * RAM cost: 0.6 GB + * RAM cost: 0.4 GB * * @param host - Hostname/IP of the target server (connected to the current server) to try a password. * @param password - Password to attempt to authenticate with. @@ -4518,12 +4523,15 @@ export interface Darknet { * Attempts to connect to a target darkweb server that you have previously authenticated on. Unlike `authenticate`, * connectToSession can be used to get a session on servers at any distance. * - * If successful, grants the script a session, allowing it to scp() files from that target. It also allows starting scripts + * If successful, grants the script a session, allowing it to scp() files to that target. It also allows starting scripts * with exec() on that target, if the target is directly connected to the server that the script is running on, * or has a backdoor or stasis link. * * If unsuccessful, more detail may be able to be gathered by using heartbleed() to look at the resulting logs on the server. * + * Note that the session granted is only for the current script instance (by PID) - other running scripts will need to + * use connectToSession with the correct password to also get a session with the target server. + * * @remarks * RAM cost: 0.05 GB * diff --git a/src/Terminal/Terminal.ts b/src/Terminal/Terminal.ts index 91a5765e7..919389f09 100644 --- a/src/Terminal/Terminal.ts +++ b/src/Terminal/Terminal.ts @@ -551,7 +551,11 @@ export class Terminal { switch (promptResult.result) { case CodingContractResult.Success: if (contract.reward !== null) { - const reward = Player.gainCodingContractReward(contract.reward, contract.getDifficulty()); + const reward = Player.gainCodingContractReward( + contract.reward, + contract.getDifficulty(), + contract.rewardScaling, + ); this.print(`Contract SUCCESS - ${reward}`); } server.removeContract(contract); diff --git a/src/ui/CharacterStats.tsx b/src/ui/CharacterStats.tsx index 9abcb2905..823eae207 100644 --- a/src/ui/CharacterStats.tsx +++ b/src/ui/CharacterStats.tsx @@ -534,6 +534,12 @@ export function CharacterStats(): React.ReactElement { effValue: Player.mults.crime_money * currentNodeMults.CrimeMoney, color: Settings.theme.money, }, + { + mult: "Darknet Money", + value: Player.mults.dnet_money, + effValue: Player.mults.dnet_money * currentNodeMults.DarknetMoneyMultiplier, + color: Settings.theme.money, + }, ]} color={Settings.theme.combat} /> diff --git a/test/jest/Darknet/Darknet.test.ts b/test/jest/Darknet/Darknet.test.ts index c212456b4..40d29a8cf 100644 --- a/test/jest/Darknet/Darknet.test.ts +++ b/test/jest/Darknet/Darknet.test.ts @@ -813,7 +813,7 @@ describe("Darknet server name generator", () => { describe("Cache filename generator", () => { test("Random prefix", () => { for (let i = 0; i < 10000; ++i) { - const cacheFilename = generateCacheFilename(); + const cacheFilename = generateCacheFilename(false); if (!cacheFilename) { throw new Error("Invalid cache filename"); } @@ -821,7 +821,7 @@ describe("Cache filename generator", () => { } }); test("Cache file in labyrinth server", () => { - const cacheFilename = generateCacheFilename(LAB_CACHE_NAME); + const cacheFilename = generateCacheFilename(false, LAB_CACHE_NAME); if (!cacheFilename) { throw new Error("Invalid cache filename"); } diff --git a/test/jest/Netscript/Darknet.test.ts b/test/jest/Netscript/Darknet.test.ts index 08f9d4e01..2a1584e6f 100644 --- a/test/jest/Netscript/Darknet.test.ts +++ b/test/jest/Netscript/Darknet.test.ts @@ -774,7 +774,7 @@ describe("darkweb", () => { test("openCache", () => { const ns = getNsOnDarkWeb(); const darkweb = getDarknetServerOrThrow(SpecialServers.DarkWeb); - const result = addCacheToServer(darkweb, "test"); + const result = addCacheToServer(darkweb, false, "test"); if (!result.success) { throw new Error("Cannot add cache"); } diff --git a/test/jest/__snapshots__/FullSave.test.ts.snap b/test/jest/__snapshots__/FullSave.test.ts.snap index 4062c8341..71667566f 100644 --- a/test/jest/__snapshots__/FullSave.test.ts.snap +++ b/test/jest/__snapshots__/FullSave.test.ts.snap @@ -517,6 +517,7 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = ` "defense_exp": 1, "dexterity": 1, "dexterity_exp": 1, + "dnet_money": 1, "faction_rep": 1, "hacking": 1, "hacking_chance": 1, @@ -601,6 +602,7 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = ` "defense_exp": 1, "dexterity": 1.3, "dexterity_exp": 1, + "dnet_money": 1, "faction_rep": 1, "hacking": 1, "hacking_chance": 1, @@ -676,6 +678,7 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = ` "defense_exp": 1, "dexterity": 1, "dexterity_exp": 1, + "dnet_money": 1, "faction_rep": 1, "hacking": 1, "hacking_chance": 1,