DNET: Player feedback (#2545)

This commit is contained in:
Michael Ficocelli
2026-03-19 21:07:43 -07:00
committed by GitHub
parent 73b7921ef0
commit 1b6b07faae
38 changed files with 248 additions and 108 deletions

View File

@@ -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. 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 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:** **Signature:**
```typescript ```typescript
@@ -92,5 +94,5 @@ A promise that resolves to a [DarknetResult](./bitburner.darknetresult.md) objec
## Remarks ## Remarks
RAM cost: 0.6 GB RAM cost: 0.4 GB

View File

@@ -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. 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. 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:** **Signature:**
```typescript ```typescript

View File

@@ -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. 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 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.
</td></tr> </td></tr>
<tr><td> <tr><td>
@@ -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. 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. 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.
</td></tr> </td></tr>
<tr><td> <tr><td>

View File

@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Multipliers](./bitburner.multipliers.md) &gt; [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;
```

View File

@@ -318,6 +318,25 @@ number
Multiplier to dexterity skill Multiplier to dexterity skill
</td></tr>
<tr><td>
[dnet\_money](./bitburner.multipliers.dnet_money.md)
</td><td>
</td><td>
number
</td><td>
Multiplier to amount of money gained from phishing and caches on darknet servers
</td></tr> </td></tr>
<tr><td> <tr><td>

View File

@@ -40,6 +40,7 @@ export interface AugmentationCtorParams {
faction_rep?: number; faction_rep?: number;
crime_money?: number; crime_money?: number;
crime_success?: number; crime_success?: number;
dnet_money?: number;
work_money?: number; work_money?: number;
hacknet_node_money?: number; hacknet_node_money?: number;
hacknet_node_purchase_cost?: number; hacknet_node_purchase_cost?: number;

View File

@@ -1859,9 +1859,11 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"Created by a mysterious figure known only as 'The Sculptor', this augmentation appears as a set of silvery " + "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. " + "metallic patterns on the user's upper back and shoulders. " +
"Awarded to those who discover the secrets of the labyrinth.", "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%.", stats:
charisma: 1.15, "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, agility: 1.1,
dnet_money: 1.3,
isSpecial: true, isSpecial: true,
factions: [], factions: [],
}, },
@@ -1873,9 +1875,9 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"Its creator, the enigmatic Sculptor, refuses to reveal the details of how it works, and only mutters about 'liveware APIs'. " + "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.", "Awarded to those who discover the secrets of the labyrinth.",
stats: stats:
"This augmentation increases the speed of authentication and heartbleed by 20%, and raises charisma and dexterity by 20%.", "This augmentation increases the speed of authentication and heartbleed by 20%, and raises charisma and dexterity by 6%.",
charisma: 1.2, charisma: 1.06,
dexterity: 1.2, dexterity: 1.06,
isSpecial: true, isSpecial: true,
prereqs: [AugmentationName.TheBrokenWings], prereqs: [AugmentationName.TheBrokenWings],
factions: [], factions: [],
@@ -1888,9 +1890,10 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"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. " + "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.", "Awarded to those who discover the secrets of the labyrinth.",
stats: stats:
"This augmentation increases the stasis link limit by one, and raises charisma by 35% and strength by 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.35, charisma: 1.07,
strength: 1.25, strength: 1.1,
dnet_money: 1.1,
isSpecial: true, isSpecial: true,
prereqs: [AugmentationName.TheBoots], prereqs: [AugmentationName.TheBoots],
factions: [], factions: [],
@@ -1902,9 +1905,10 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"This skeletal augmentation greatly enhances the users durability and health. Inspired by the original Staff of Medicine that is said to " + "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. ", "have been given to Daedalus as a reward for the completion of the Labyrinth, which all modern augments are a descendant of. ",
stats: stats:
"This augmentation increases the stasis link limit by one, and raises charisma xp by 20% and defense by 30%.", "This augmentation increases the stasis link limit by one, and raises charisma xp, defense, and darknet money by 10%.",
charisma_exp: 1.2, charisma_exp: 1.1,
defense: 1.3, defense: 1.1,
dnet_money: 1.1,
isSpecial: true, isSpecial: true,
prereqs: [AugmentationName.TheHammer], prereqs: [AugmentationName.TheHammer],
factions: [], factions: [],
@@ -1917,10 +1921,10 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"This augmentation enhances the user's ability to assess probabilities, predict outcomes, and adapt strategies in real-time, " + "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. " + "making them exceptionally persuasive and confident in negotiations and social interactions. " +
"Awarded to those who discover the secrets of the labyrinth.", "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%.", stats: "This augmentation raises charisma by 9%, company rep by 5%, and darknet money by 15%.",
charisma: 1.4, charisma: 1.09,
hacking: 1.1,
company_rep: 1.05, company_rep: 1.05,
dnet_money: 1.15,
isSpecial: true, isSpecial: true,
prereqs: [AugmentationName.TheStaff], prereqs: [AugmentationName.TheStaff],
factions: [], factions: [],
@@ -1934,10 +1938,11 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"turning every interaction into a calculated success. The technique is sometimes referred to as Solomonoff's Lightsaber, as it is a " + "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. " + "more powerful version of Occam's razor. " +
"The final augment awarded to those who discover the secrets of the labyrinth.", "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%.", stats: "This augmentation raises charisma, hacking, darknet money, and company rep by 10%.",
charisma: 1.5, charisma: 1.1,
hacking: 1.16, hacking: 1.1,
company_rep: 1.1, company_rep: 1.1,
dnet_money: 1.1,
isSpecial: true, isSpecial: true,
prereqs: [AugmentationName.TheLaw], prereqs: [AugmentationName.TheLaw],
factions: [], factions: [],

View File

@@ -263,6 +263,13 @@ export function PlayerMultipliers(): React.ReactElement {
bnMult: currentNodeMults.CrimeMoney, bnMult: currentNodeMults.CrimeMoney,
color: Settings.theme.money, 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) { if (Player.canAccessBladeburner() && currentNodeMults.BladeburnerRank > 0) {

View File

@@ -625,6 +625,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
StaneksGiftPowerMultiplier: 0.75, StaneksGiftPowerMultiplier: 0.75,
StaneksGiftExtraSize: -2, StaneksGiftExtraSize: -2,
DarknetMoneyMultiplier: 0.4,
WorldDaemonDifficulty: 2, WorldDaemonDifficulty: 2,
}); });
} }
@@ -653,6 +655,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
StaneksGiftPowerMultiplier: 1.5, StaneksGiftPowerMultiplier: 1.5,
StaneksGiftExtraSize: 0, StaneksGiftExtraSize: 0,
DarknetMoneyMultiplier: 0.4,
WorldDaemonDifficulty: 3, WorldDaemonDifficulty: 3,
}); });
} }
@@ -682,6 +686,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
StaneksGiftPowerMultiplier: 1.3, StaneksGiftPowerMultiplier: 1.3,
StaneksGiftExtraSize: 0, StaneksGiftExtraSize: 0,
DarknetMoneyMultiplier: 0.7,
WorldDaemonDifficulty: 1.5, WorldDaemonDifficulty: 1.5,
}); });
} }
@@ -830,6 +836,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
StaneksGiftPowerMultiplier: 0.5, StaneksGiftPowerMultiplier: 0.5,
StaneksGiftExtraSize: 2, StaneksGiftExtraSize: 2,
DarknetMoneyMultiplier: 0.5,
WorldDaemonDifficulty: 2, WorldDaemonDifficulty: 2,
}); });
} }
@@ -873,6 +881,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
StaneksGiftPowerMultiplier: 0.75, StaneksGiftPowerMultiplier: 0.75,
StaneksGiftExtraSize: -3, StaneksGiftExtraSize: -3,
DarknetMoneyMultiplier: 0.4,
WorldDaemonDifficulty: 2, WorldDaemonDifficulty: 2,
}); });
} }
@@ -946,6 +956,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
ManualHackMoney: dec, ManualHackMoney: dec,
ScriptHackMoney: dec, ScriptHackMoney: dec,
CodingContractMoney: dec, CodingContractMoney: dec,
DarknetMoneyMultiplier: dec,
ClassGymExpGain: dec, ClassGymExpGain: dec,
CompanyWorkExpGain: dec, CompanyWorkExpGain: dec,
@@ -1025,6 +1036,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
StaneksGiftPowerMultiplier: 2, StaneksGiftPowerMultiplier: 2,
StaneksGiftExtraSize: 1, StaneksGiftExtraSize: 1,
DarknetMoneyMultiplier: 0.5,
WorldDaemonDifficulty: 3, WorldDaemonDifficulty: 3,
}); });

View File

@@ -55,6 +55,9 @@ export class CodingContract {
processed outside of this file */ processed outside of this file */
reward: ICodingContractReward | null; 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 */ /* Number of times the Contract has been attempted */
tries = 0; tries = 0;
@@ -65,6 +68,7 @@ export class CodingContract {
fn = "default.cct", fn = "default.cct",
type = CodingContractName.FindLargestPrimeFactor, type = CodingContractName.FindLargestPrimeFactor,
reward: ICodingContractReward | null = null, reward: ICodingContractReward | null = null,
rewardScaling: number = 1,
) { ) {
const path = resolveContractFilePath(fn); const path = resolveContractFilePath(fn);
if (!path) { if (!path) {
@@ -78,6 +82,7 @@ export class CodingContract {
this.type = type; this.type = type;
this.state = CodingContractTypes[type].generate(); this.state = CodingContractTypes[type].generate();
this.reward = reward; this.reward = reward;
this.rewardScaling = rewardScaling;
} }
getAnswer() { getAnswer() {

View File

@@ -130,6 +130,7 @@ interface IGenerateContractParams {
server?: string; server?: string;
filename?: ContractFilePath; filename?: ContractFilePath;
reward?: ICodingContractReward; reward?: ICodingContractReward;
rewardScaling?: number;
} }
export function generateContract(params: IGenerateContractParams): void { export function generateContract(params: IGenerateContractParams): void {
@@ -163,7 +164,7 @@ export function generateContract(params: IGenerateContractParams): void {
if (filename == null) { if (filename == null) {
return; return;
} }
const contract = new CodingContract(filename, problemType, reward); const contract = new CodingContract(filename, problemType, reward, params.rewardScaling);
server.addContract(contract); server.addContract(contract);
} }

View File

@@ -80,6 +80,7 @@ export function initDarkwebServer(): void {
darkweb.isStationary = true; darkweb.isStationary = true;
darkweb.hasAdminRights = true; darkweb.hasAdminRights = true;
darkweb.blockedRam = 0; darkweb.blockedRam = 0;
darkweb.maxRam = 16;
darkweb.scripts = scripts; darkweb.scripts = scripts;
darkweb.contracts = contracts; darkweb.contracts = contracts;
if (hasTOR) { if (hasTOR) {

View File

@@ -17,7 +17,7 @@ import {
getAllDarknetServers, getAllDarknetServers,
getAllMovableDarknetServers, getAllMovableDarknetServers,
getAllOpenPositions, getAllOpenPositions,
getBackdooredDarkwebServers, getBackdooredDarknetServers,
getDarknetCyclesPerMutation, getDarknetCyclesPerMutation,
getIslands, getIslands,
} from "../utils/darknetNetworkUtils"; } from "../utils/darknetNetworkUtils";
@@ -89,7 +89,7 @@ export const mutateDarknet = (): void => {
} }
if (Math.random() < 0.1) { if (Math.random() < 0.1) {
const backdooredServers = getBackdooredDarkwebServers(); const backdooredServers = getBackdooredDarknetServers();
const server = backdooredServers[Math.floor(Math.random() * backdooredServers.length)]; const server = backdooredServers[Math.floor(Math.random() * backdooredServers.length)];
if (server) { if (server) {
restartServer(server); restartServer(server);
@@ -98,7 +98,7 @@ export const mutateDarknet = (): void => {
} }
if (Math.random() < 0.05) { if (Math.random() < 0.05) {
const backdooredServers = getBackdooredDarkwebServers(); const backdooredServers = getBackdooredDarknetServers();
const server = backdooredServers[Math.floor(Math.random() * backdooredServers.length)]; const server = backdooredServers[Math.floor(Math.random() * backdooredServers.length)];
if (server) { if (server) {
deleteDarknetServer(server); deleteDarknetServer(server);

View File

@@ -1,27 +1,26 @@
import { tryGeneratingRandomContract } from "../../CodingContract/ContractGenerator"; import { generateContract } from "../../CodingContract/ContractGenerator";
import { Player } from "@player"; import { Player } from "@player";
import { formatMoney, formatNumber } from "../../ui/formatNumber"; import { formatMoney } from "../../ui/formatNumber";
import { getLabAugReward, isLabyrinthServer, LAB_CACHE_NAME } from "./labyrinth"; import { getLabAugReward, isLabyrinthServer, LAB_CACHE_NAME } from "./labyrinth";
import { SnackbarEvents } from "../../ui/React/Snackbar"; 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 { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
import { CreateProgramWork } from "../../Work/CreateProgramWork"; import { CreateProgramWork } from "../../Work/CreateProgramWork";
import { initStockMarket, isStockMarketInitialized } from "../../StockMarket/StockMarket"; import { initStockMarket, isStockMarketInitialized, StockMarket } from "../../StockMarket/StockMarket";
import { cachePrefixes } from "../models/dictionaryData"; import { cachePrefixes } from "../models/dictionaryData";
import type { DarknetServer } from "../../Server/DarknetServer"; import type { DarknetServer } from "../../Server/DarknetServer";
import { type CacheFilePath, resolveCacheFilePath } from "../../Paths/CacheFilePath"; import { resolveCacheFilePath } from "../../Paths/CacheFilePath";
import type { CacheResult, Result } from "@nsdefs"; 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)]; 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 }> = ( export const addCacheToServer = (server: DarknetServer, isPhishingCache: boolean, prefix?: string) => {
server, const cacheFilename = generateCacheFilename(isPhishingCache, prefix);
prefix,
) => {
const cacheFilename = generateCacheFilename(prefix);
if (!cacheFilename) { if (!cacheFilename) {
return { success: false, message: `Cannot generate path. prefix: ${prefix}` }; 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 reward = rewards[Math.floor(Math.random() * rewards.length)];
const result = reward(difficulty); const result = reward(difficulty, server);
if (!suppressToast) { if (!suppressToast) {
SnackbarEvents.emit( SnackbarEvents.emit(
@@ -62,16 +65,17 @@ export const getRewardFromCache = (server: DarknetServer, cacheName: string, sup
}; };
}; };
export const getCCTReward = (difficulty: number): string => { export const getCCTReward = (difficulty: number, server: DarknetServer): string => {
if (Math.random() < difficulty * 0.2) { if (!cctCooldownReached()) {
return getMoneyReward(difficulty); return getMoneyReward(difficulty);
} }
const contractCount = Math.min(Math.floor(Math.min(20, difficulty) * 0.1 - 1.5 + Math.random() * 3), 3);
const contractCount = Math.floor(Math.min(20, difficulty) * 0.2 - 1.5 + Math.random() * 3); if (contractCount < 1) {
if (contractCount <= 0) {
return getMoneyReward(difficulty); 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!`; 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) * ((200 + Player.skills.charisma) / 200) *
sf15_3Factor * sf15_3Factor *
Player.mults.crime_money * Player.mults.crime_money *
Player.mults.dnet_money *
currentNodeMults.DarknetMoneyMultiplier; // TODO: adjust balance currentNodeMults.DarknetMoneyMultiplier; // TODO: adjust balance
Player.gainMoney(reward, "darknet"); Player.gainMoney(reward, "darknet");
return `You have discovered a cache with ${formatMoney(reward)}.`; return `You have discovered a cache with ${formatMoney(reward)}.`;
}; };
export const getXpReward = (difficulty: number): string => { export const getStockReward = (difficulty: number): string => {
const sf15_3Factor = Player.activeSourceFileLvl(15) > 3 ? 1.5 : 1; if (!isStockMarketInitialized()) {
const reward = 1.2 ** difficulty * 500 * sf15_3Factor * Player.mults.charisma_exp; // TODO: adjust balance initStockMarket();
Player.gainCharismaExp(reward); }
return `You have discovered a cache with ${formatNumber(reward, 0)} cha XP.`; 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 => { 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 `You have discovered a cache of stolen 4S Data!`;
} }
return getXpReward(difficulty); return getMoneyReward(difficulty);
}; };
const getLabReward = (): string => { const getLabReward = (): string => {

View File

@@ -1,7 +1,6 @@
import { Player } from "@player"; import { Player } from "@player";
import type { DarknetServerData, Person as IPerson } from "@nsdefs"; import type { DarknetServerData, Person as IPerson } from "@nsdefs";
import { AugmentationName, CompletedProgramName, LiteratureName } from "@enums"; import { AugmentationName, CompletedProgramName, LiteratureName } from "@enums";
import { generateContract } from "../../CodingContract/ContractGenerator";
import { import {
commonPasswordDictionary, commonPasswordDictionary,
notebookFileNames, notebookFileNames,
@@ -20,7 +19,7 @@ import { populateDarknet } from "../controllers/NetworkGenerator";
import { getDarknetServer } from "../utils/darknetServerUtils"; import { getDarknetServer } from "../utils/darknetServerUtils";
import { import {
getAllMovableDarknetServers, getAllMovableDarknetServers,
getBackdooredDarkwebServers, getBackdooredDarknetServers,
getNearbyNonEmptyPasswordServer, getNearbyNonEmptyPasswordServer,
getStasisLinkServers, getStasisLinkServers,
} from "../utils/darknetNetworkUtils"; } from "../utils/darknetNetworkUtils";
@@ -41,14 +40,9 @@ export const handleSuccessfulAuth = (server: DarknetServer, threads: number, pid
server.hasAdminRights = true; server.hasAdminRights = true;
addClue(server); 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; const chance = 0.1 * 1.05 ** server?.difficulty;
if (Math.random() < chance && !isLabyrinthServer(server.hostname)) { if (Math.random() < chance && !isLabyrinthServer(server.hostname)) {
addCacheToServer(server); addCacheToServer(server, false);
} }
}; };
@@ -110,7 +104,7 @@ export const calculateAuthenticationTime = (
}; };
export const getBackdoorAuthTimeDebuff = () => { export const getBackdoorAuthTimeDebuff = () => {
const backdooredServerCount = getBackdooredDarkwebServers().length; const backdooredServerCount = getBackdooredDarknetServers().length;
const serverCount = getAllMovableDarknetServers().filter((s) => s.hasAdminRights).length; const serverCount = getAllMovableDarknetServers().filter((s) => s.hasAdminRights).length;
const safeBackdoors = Math.max(serverCount / (NET_WIDTH * 3), 2); const safeBackdoors = Math.max(serverCount / (NET_WIDTH * 3), 2);
const backdoorSurplus = Math.max(0, backdooredServerCount - safeBackdoors); 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) => { export const calculatePasswordAttemptChaGain = (server: DarknetServerData, threads: number = 1, success = false) => {
const baseXpGain = 3; const baseXpGain = 3;
const difficultyBase = 0.8; const difficultyBase = 1.1;
const xpGain = baseXpGain + difficultyBase ** server.difficulty; const xpGain = baseXpGain + difficultyBase ** server.difficulty;
const alreadyHackedMult = server.hasAdminRights ? 0.2 : 1; const alreadyHackedMult = server.hasAdminRights ? 0.2 : 1;
const successMult = success && !server.hasAdminRights ? 10 : 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) => { export const chargeServerMigration = (server: DarknetServer, threads = 1) => {
const chargeIncrease = ((Player.skills.charisma + 500) / (server.difficulty * 200 + 1000)) * 0.01 * threads; 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); Player.gainCharismaExp(xpGained);
const currentCharge = DarknetState.migrationInductionServers.get(server.hostname) ?? 0; const currentCharge = DarknetState.migrationInductionServers.get(server.hostname) ?? 0;
const newCharge = Math.min(currentCharge + chargeIncrease, 1); const newCharge = Math.min(currentCharge + chargeIncrease, 1);
@@ -292,4 +286,9 @@ export const getDarkscapeNavigator = () => {
populateDarknet(); 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; export const hasFullDarknetAccess = (): boolean => Player.bitNodeN === 15 || Player.activeSourceFileLvl(15) > 0;

View File

@@ -303,7 +303,7 @@ export const handleLabyrinthPassword = (
server.hasAdminRights = true; server.hasAdminRights = true;
const cacheCount = getLabyrinthDetails().name === SpecialServers.BonusLab ? 3 : 1; const cacheCount = getLabyrinthDetails().name === SpecialServers.BonusLab ? 3 : 1;
for (let i = 0; i < cacheCount; i++) { for (let i = 0; i < cacheCount; i++) {
addCacheToServer(server, LAB_CACHE_NAME); addCacheToServer(server, false, LAB_CACHE_NAME);
} }
addSessionToServer(labServer, pid); addSessionToServer(labServer, pid);

View File

@@ -6,7 +6,7 @@ import { errorMessage } from "../../Netscript/ErrorMessages";
import type { BaseServer } from "../../Server/BaseServer"; import type { BaseServer } from "../../Server/BaseServer";
import { GetServer } from "../../Server/AllServers"; import { GetServer } from "../../Server/AllServers";
import { GenericResponseMessage, ResponseCodeEnum } from "../Enums"; import { GenericResponseMessage, ResponseCodeEnum } from "../Enums";
import { getBackdooredDarkwebServers } from "../utils/darknetNetworkUtils"; import { getBackdooredDarknetServers } from "../utils/darknetNetworkUtils";
import { hasDarknetAccess } from "../utils/darknetAuthUtils"; import { hasDarknetAccess } from "../utils/darknetAuthUtils";
import { DarknetServer } from "../../Server/DarknetServer"; import { DarknetServer } from "../../Server/DarknetServer";
import { CompletedProgramName } from "../../Enums"; import { CompletedProgramName } from "../../Enums";
@@ -149,6 +149,6 @@ export function expectRunningOnDarknetServer(ctx: NetscriptContext): DarknetServ
} }
export function getTimeoutChance() { export function getTimeoutChance() {
const backdooredDarknetServerCount = getBackdooredDarkwebServers().length - 2; const backdooredDarknetServerCount = getBackdooredDarknetServers().length - 2;
return Math.max(Math.min(backdooredDarknetServerCount * 0.03, 0.5), 0); return Math.max(Math.min(backdooredDarknetServerCount * 0.03, 0.5), 0);
} }

View File

@@ -10,23 +10,20 @@ import { ResponseCodeEnum } from "../Enums";
import { isLabyrinthServer } from "./labyrinth"; import { isLabyrinthServer } from "./labyrinth";
export const getPhishingAttackSpeed = () => Math.max(10000 * (400 / (400 + Player.skills.charisma)), 200); 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) => { export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServer) => {
const threads = ctx.workerScript.scriptRef.threads; const threads = ctx.workerScript.scriptRef.threads;
const xpGained = Player.mults.charisma_exp * threads * 50 * ((200 + Player.skills.charisma) / 200); const xpReward = Player.mults.charisma_exp * threads * 50;
Player.gainCharismaExp(xpGained);
const timeSinceLastRewardCache = new Date().getTime() - DarknetState.lastPhishingCacheTime.getTime();
const rewardCacheChance = 0.005 * Player.mults.crime_success * threads * ((400 + Player.skills.charisma) / 400); 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 moneyRewardChance = 0.05 * Player.mults.crime_success * ((200 + Player.skills.charisma) / 200);
const cooldown = getPhishingCacheCooldownDuration();
const isLabServer = isLabyrinthServer(server.hostname); const isLabServer = isLabyrinthServer(server.hostname);
if (timeSinceLastRewardCache > cooldown && Math.random() < rewardCacheChance && !isLabServer) { if (phishingCacheCooldownReached() && Math.random() < rewardCacheChance && !isLabServer) {
addCacheToServer(server); addCacheToServer(server, true);
Player.gainCharismaExp(xpReward);
DarknetState.lastPhishingCacheTime = new Date(); 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); helpers.log(ctx, () => result);
return { return {
success: true, success: true,
@@ -38,17 +35,19 @@ export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServe
const bonusTimeFactor = hasDarknetBonusTime() ? 1.3 : 1; const bonusTimeFactor = hasDarknetBonusTime() ? 1.3 : 1;
const depthFactor = 0.1 + server.depth * 0.05; const depthFactor = 0.1 + server.depth * 0.05;
const moneyReward = const moneyReward =
2000 * 500 *
Player.mults.crime_money * Player.mults.crime_money *
Player.mults.dnet_money *
depthFactor * depthFactor *
threads * threads *
((200 + Player.skills.charisma) / 200) * ((400 + Player.skills.charisma) / 400) *
bonusTimeFactor * bonusTimeFactor *
randomFactor * randomFactor *
currentNodeMults.DarknetMoneyMultiplier; currentNodeMults.DarknetMoneyMultiplier;
Player.gainMoney(moneyReward, "darknet"); Player.gainMoney(moneyReward, "darknet");
Player.gainCharismaExp(xpReward);
const result = `Phishing attack succeeded! $${formatNumber(moneyReward, 2)} retrieved. (Gained ${formatNumber( const result = `Phishing attack succeeded! $${formatNumber(moneyReward, 2)} retrieved. (Gained ${formatNumber(
xpGained, xpReward,
1, 1,
)} cha xp)`; )} cha xp)`;
helpers.log(ctx, () => result); helpers.log(ctx, () => result);
@@ -58,7 +57,8 @@ export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServe
message: result, 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); helpers.log(ctx, () => result);
return { return {
success: false, success: false,
@@ -66,3 +66,8 @@ export const handlePhishingAttack = (ctx: NetscriptContext, server: DarknetServe
message: result, message: result,
}; };
}; };
const phishingCacheCooldownReached = () => {
const timeSinceLastRewardCache = new Date().getTime() - DarknetState.lastPhishingCacheTime.getTime();
return timeSinceLastRewardCache > 3 * 60 * 1000; // 3 minutes;
};

View File

@@ -27,8 +27,7 @@ export const handleRamBlockRemoved = (ctx: NetscriptContext, server: DarknetServ
if (server.blockedRam <= 0) { if (server.blockedRam <= 0) {
handleRamBlockClearedRewards(server); handleRamBlockClearedRewards(server);
} }
const xpGained = const xpGained = Player.mults.charisma_exp * threads * 10 * 1.1 ** difficulty;
Player.mults.charisma_exp * threads * 10 * 1.1 ** difficulty * ((200 + Player.skills.charisma) / 200);
Player.gainCharismaExp(xpGained); Player.gainCharismaExp(xpGained);
const result = `Liberated ${formatNumber( const result = `Liberated ${formatNumber(
@@ -48,7 +47,7 @@ export const handleRamBlockRemoved = (ctx: NetscriptContext, server: DarknetServ
*/ */
export const handleRamBlockClearedRewards = (server: DarknetServer) => { export const handleRamBlockClearedRewards = (server: DarknetServer) => {
if (!isLabyrinthServer(server.hostname)) { if (!isLabyrinthServer(server.hostname)) {
addCacheToServer(server); addCacheToServer(server, false);
} }
if (Math.random() < 0.3) { if (Math.random() < 0.3) {
addClue(server); addClue(server);

View File

@@ -17,6 +17,7 @@ import { getRamBlock } from "../effects/ramblock";
import { hasFullDarknetAccess } from "../effects/effects"; import { hasFullDarknetAccess } from "../effects/effects";
import { getFriendlyType, TypeAssertionError } from "../../utils/TypeAssertion"; import { getFriendlyType, TypeAssertionError } from "../../utils/TypeAssertion";
import { isIPAddress } from "../../Types/strings"; import { isIPAddress } from "../../Types/strings";
import { roundToTwo } from "../../utils/helpers/roundToTwo";
export type PasswordResponse = { export type PasswordResponse = {
code: DarknetResponseCode; code: DarknetResponseCode;
@@ -58,7 +59,7 @@ export type DarknetServerOptions = {
}; };
export const DnetServerBuilder = (options: DarknetServerOptions): DarknetServer => { 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 ramBlock = options.preventBlockedRam ? 0 : getRamBlock(maxRam);
const name = options.name ?? generateDarknetServerName(); const name = options.name ?? generateDarknetServerName();
@@ -192,3 +193,10 @@ const l33tifyName = (name: string): string => {
} }
return updatedName; 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));
};

View File

@@ -47,6 +47,7 @@ export const DarknetState = {
labLocations: { "-1": [1, 1] } as Record<number, [number, number] | undefined>, labLocations: { "-1": [1, 1] } as Record<number, [number, number] | undefined>,
lastPhishingCacheTime: new Date(), lastPhishingCacheTime: new Date(),
lastCctRewardTime: new Date(),
lastStormTime: new Date(), lastStormTime: new Date(),
stockPromotions: {} as Record<string, number>, stockPromotions: {} as Record<string, number>,

View File

@@ -14,7 +14,7 @@ import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
const MAX_LOG_LINES = 200; const MAX_LOG_LINES = 200;
export const capturePackets = (server: DarknetServer) => { 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_MODIFIER = 0.88;
const difficulty = server.difficulty * 1.3; const difficulty = server.difficulty * 1.3;
const vulnerability = server.modelId === ModelIds.packetSniffer ? 8 : 1; const vulnerability = server.modelId === ModelIds.packetSniffer ? 8 : 1;

View File

@@ -252,7 +252,7 @@ export function NetworkDisplayWrapper(): React.ReactElement {
<Typography variant={"h5"} sx={{ fontWeight: "bold" }}> <Typography variant={"h5"} sx={{ fontWeight: "bold" }}>
Dark Net Dark Net
</Typography> </Typography>
{instability && ( {instability > 0 && (
<Tooltip <Tooltip
title={ title={
<> <>
@@ -299,13 +299,13 @@ export function NetworkDisplayWrapper(): React.ReactElement {
{DarknetState.Network.slice(0, netDisplayDepth).map((row) => {DarknetState.Network.slice(0, netDisplayDepth).map((row) =>
row.map( row.map(
(server) => (server) =>
server && ( !!server && (
<ServerStatusBox server={server} key={server.ip} enableAuth={allowAuth(server)} classes={classes} /> <ServerStatusBox server={server} key={server.ip} enableAuth={allowAuth(server)} classes={classes} />
), ),
), ),
)} )}
{labyrinth && netDisplayDepth > depth && ( {!!labyrinth && netDisplayDepth > depth && (
<ServerStatusBox server={labyrinth} enableAuth={allowAuth(labyrinth)} classes={classes} /> <ServerStatusBox server={labyrinth} enableAuth={allowAuth(labyrinth)} classes={classes} />
)} )}
</div> </div>

View File

@@ -149,7 +149,7 @@ export const PasswordPrompt = ({ server, onClose }: PasswordPromptProps): React.
<pre style={{ whiteSpace: "pre-wrap", margin: 0 }}> <pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>
<span className={classes.serverDetailsText}>Hint:</span> {server.staticPasswordHint} <span className={classes.serverDetailsText}>Hint:</span> {server.staticPasswordHint}
<br /> <br />
{server.passwordHintData && ( {!!server.passwordHintData && (
<> <>
<span className={classes.serverDetailsText}>Data: </span> {server.passwordHintData} <span className={classes.serverDetailsText}>Data: </span> {server.passwordHintData}
<br /> <br />

View File

@@ -86,7 +86,7 @@ export const getAllAdjacentNeighbors = (x: number, y: number): DarknetServer[] =
export const getIslands = () => getAllMovableDarknetServers().filter((s) => !s.serversOnNetwork.length); export const getIslands = () => getAllMovableDarknetServers().filter((s) => !s.serversOnNetwork.length);
export const getBackdooredDarkwebServers = (): DarknetServer[] => export const getBackdooredDarknetServers = (): DarknetServer[] =>
getAllMovableDarknetServers().filter((s) => !s.hasStasisLink && s.backdoorInstalled); getAllMovableDarknetServers().filter((s) => !s.hasStasisLink && s.backdoorInstalled);
export const getNearbyNonEmptyPasswordServer = (server: DarknetServer, disconnected = false) => { export const getNearbyNonEmptyPasswordServer = (server: DarknetServer, disconnected = false) => {

View File

@@ -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_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_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_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_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_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"; 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.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_exp.md"] = nsDoc_bitburner_multipliers_dexterity_exp_md;
AllPages["nsDoc/bitburner.multipliers.dexterity.md"] = nsDoc_bitburner_multipliers_dexterity_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.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_chance.md"] = nsDoc_bitburner_multipliers_hacking_chance_md;
AllPages["nsDoc/bitburner.multipliers.hacking_exp.md"] = nsDoc_bitburner_multipliers_hacking_exp_md; AllPages["nsDoc/bitburner.multipliers.hacking_exp.md"] = nsDoc_bitburner_multipliers_hacking_exp_md;

View File

@@ -38,7 +38,11 @@ export function NetscriptCodingContract(): InternalAPI<ICodingContract> {
const resultOfCheckingSolution = contract.isSolution(answer); const resultOfCheckingSolution = contract.isSolution(answer);
switch (resultOfCheckingSolution.result) { switch (resultOfCheckingSolution.result) {
case CodingContractResult.Success: { 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}`); helpers.log(ctx, () => `Successfully completed Coding Contract '${contract.fn}'. Reward: ${reward}`);
server.removeContract(contract.fn); server.removeContract(contract.fn);
return reward; return reward;

View File

@@ -426,7 +426,7 @@ export function NetscriptDarknet(): InternalAPI<DarknetAPI> {
const server = serverCheck.server; const server = serverCheck.server;
const networkDelay = 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); const xp = formatNumber(calculatePasswordAttemptChaGain(server, ctx.workerScript.scriptRef.threads), 1);
logger(ctx)(`Captured some outgoing transmissions from ${server.hostname}. (Gained ${xp} cha xp)`); logger(ctx)(`Captured some outgoing transmissions from ${server.hostname}. (Gained ${xp} cha xp)`);

View File

@@ -30,6 +30,7 @@ export const calculateEntropy = (stacks = 1): Multipliers => {
crime_money: Player.mults.crime_money * nerf, crime_money: Player.mults.crime_money * nerf,
crime_success: Player.mults.crime_success * 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_money: Player.mults.hacknet_node_money * nerf,
hacknet_node_purchase_cost: Player.mults.hacknet_node_purchase_cost / nerf, hacknet_node_purchase_cost: Player.mults.hacknet_node_purchase_cost / nerf,

View File

@@ -25,6 +25,7 @@ export interface Multipliers {
work_money: number; work_money: number;
crime_success: number; crime_success: number;
crime_money: number; crime_money: number;
dnet_money: number;
bladeburner_max_stamina: number; bladeburner_max_stamina: number;
bladeburner_stamina_gain: number; bladeburner_stamina_gain: number;
bladeburner_analysis: number; bladeburner_analysis: number;
@@ -59,6 +60,7 @@ export const defaultMultipliers = (): Multipliers => {
work_money: 1, work_money: 1,
crime_success: 1, crime_success: 1,
crime_money: 1, crime_money: 1,
dnet_money: 1,
bladeburner_max_stamina: 1, bladeburner_max_stamina: 1,
bladeburner_stamina_gain: 1, bladeburner_stamina_gain: 1,
bladeburner_analysis: 1, bladeburner_analysis: 1,
@@ -94,6 +96,7 @@ export const mergeMultipliers = (m0: Multipliers, m1: Multipliers): Multipliers
work_money: m0.work_money * m1.work_money, work_money: m0.work_money * m1.work_money,
crime_success: m0.crime_success * m1.crime_success, crime_success: m0.crime_success * m1.crime_success,
crime_money: m0.crime_money * m1.crime_money, 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_max_stamina: m0.bladeburner_max_stamina * m1.bladeburner_max_stamina,
bladeburner_stamina_gain: m0.bladeburner_stamina_gain * m1.bladeburner_stamina_gain, bladeburner_stamina_gain: m0.bladeburner_stamina_gain * m1.bladeburner_stamina_gain,
bladeburner_analysis: m0.bladeburner_analysis * m1.bladeburner_analysis, 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, work_money: (m0.work_money - 1) * v + 1,
crime_success: (m0.crime_success - 1) * v + 1, crime_success: (m0.crime_success - 1) * v + 1,
crime_money: (m0.crime_money - 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_max_stamina: (m0.bladeburner_max_stamina - 1) * v + 1,
bladeburner_stamina_gain: (m0.bladeburner_stamina_gain - 1) * v + 1, bladeburner_stamina_gain: (m0.bladeburner_stamina_gain - 1) * v + 1,
bladeburner_analysis: (m0.bladeburner_analysis - 1) * v + 1, bladeburner_analysis: (m0.bladeburner_analysis - 1) * v + 1,

View File

@@ -499,7 +499,8 @@ export function queueAugmentation(this: PlayerObject, name: AugmentationName): v
export function gainCodingContractReward( export function gainCodingContractReward(
this: PlayerObject, this: PlayerObject,
reward: ICodingContractReward | null, reward: ICodingContractReward | null,
difficulty = 1, difficulty: number,
rewardScaling: number,
): string { ): string {
if (!reward) { if (!reward) {
return `No reward for this contract`; return `No reward for this contract`;
@@ -509,20 +510,20 @@ export function gainCodingContractReward(
case CodingContractRewardType.FactionReputation: { case CodingContractRewardType.FactionReputation: {
const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork); const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork);
if (factionsThatAllowHacking.length === 0) { 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 randomFaction = factionsThatAllowHacking[getRandomIntInclusive(0, factionsThatAllowHacking.length - 1)];
const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty; const repGain = CONSTANTS.CodingContractBaseFactionRepGain * difficulty * rewardScaling;
Factions[randomFaction].playerReputation += repGain; Factions[randomFaction].playerReputation += repGain;
return `Gained ${repGain} faction reputation for ${randomFaction}`; return `Gained ${repGain} faction reputation for ${randomFaction}`;
} }
case CodingContractRewardType.FactionReputationAll: { case CodingContractRewardType.FactionReputationAll: {
const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork); const factionsThatAllowHacking = Player.factions.filter((fac) => Factions[fac].getInfo().offerHackingWork);
if (factionsThatAllowHacking.length === 0) { 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); const gainPerFaction = Math.floor(totalGain / factionsThatAllowHacking.length);
for (const facName of factionsThatAllowHacking) { for (const facName of factionsThatAllowHacking) {
Factions[facName].playerReputation += gainPerFaction; Factions[facName].playerReputation += gainPerFaction;
@@ -542,15 +543,17 @@ export function gainCodingContractReward(
: CodingContractRewardType.FactionReputationAll, : CodingContractRewardType.FactionReputationAll,
}, },
difficulty, difficulty,
rewardScaling,
); );
} }
const randomCompany = companies[getRandomIntInclusive(0, companies.length - 1)]; const randomCompany = companies[getRandomIntInclusive(0, companies.length - 1)];
const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty; const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty * rewardScaling;
Companies[randomCompany].playerReputation += repGain; Companies[randomCompany].playerReputation += repGain;
return `Gained ${repGain} company reputation for ${randomCompany}`; return `Gained ${repGain} company reputation for ${randomCompany}`;
} }
case CodingContractRewardType.Money: { case CodingContractRewardType.Money: {
const moneyGain = CONSTANTS.CodingContractBaseMoneyGain * difficulty * currentNodeMults.CodingContractMoney; const moneyGain =
CONSTANTS.CodingContractBaseMoneyGain * difficulty * currentNodeMults.CodingContractMoney * rewardScaling;
this.gainMoney(moneyGain, "codingcontract"); this.gainMoney(moneyGain, "codingcontract");
return `Gained ${formatMoney(moneyGain)}`; return `Gained ${formatMoney(moneyGain)}`;
} }

View File

@@ -15,7 +15,7 @@ function mult(favor: number): number {
export function getHackingWorkRepGain(p: IPerson, favor: number): number { export function getHackingWorkRepGain(p: IPerson, favor: number): number {
return ( 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 * p.mults.faction_rep *
calculateIntelligenceBonus(p.skills.intelligence, 1) * calculateIntelligenceBonus(p.skills.intelligence, 1) *
mult(favor) * mult(favor) *
@@ -30,7 +30,8 @@ export function getFactionSecurityWorkRepGain(p: IPerson, favor: number): number
p.skills.defense + p.skills.defense +
p.skills.dexterity + p.skills.dexterity +
p.skills.agility + 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 / CONSTANTS.MaxSkillLevel /
4.5; 4.5;
return t * p.mults.faction_rep * mult(favor) * calculateIntelligenceBonus(p.skills.intelligence, 1); return t * p.mults.faction_rep * mult(favor) * calculateIntelligenceBonus(p.skills.intelligence, 1);

View File

@@ -178,6 +178,8 @@ interface Multipliers {
crime_money: number; crime_money: number;
/** Multiplier to crime success rate */ /** Multiplier to crime success rate */
crime_success: number; 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 */ /** Multiplier to amount of money gained from working */
work_money: number; work_money: number;
/** Multiplier to amount of money produced by Hacknet Nodes */ /** 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 * 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. * 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 * 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. * 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 * @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 host - Hostname/IP of the target server (connected to the current server) to try a password.
* @param password - Password to attempt to authenticate with. * @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`, * 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. * 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, * 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. * 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. * 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 * @remarks
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
* *

View File

@@ -551,7 +551,11 @@ export class Terminal {
switch (promptResult.result) { switch (promptResult.result) {
case CodingContractResult.Success: case CodingContractResult.Success:
if (contract.reward !== null) { 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}`); this.print(`Contract SUCCESS - ${reward}`);
} }
server.removeContract(contract); server.removeContract(contract);

View File

@@ -534,6 +534,12 @@ export function CharacterStats(): React.ReactElement {
effValue: Player.mults.crime_money * currentNodeMults.CrimeMoney, effValue: Player.mults.crime_money * currentNodeMults.CrimeMoney,
color: Settings.theme.money, 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} color={Settings.theme.combat}
/> />

View File

@@ -813,7 +813,7 @@ describe("Darknet server name generator", () => {
describe("Cache filename generator", () => { describe("Cache filename generator", () => {
test("Random prefix", () => { test("Random prefix", () => {
for (let i = 0; i < 10000; ++i) { for (let i = 0; i < 10000; ++i) {
const cacheFilename = generateCacheFilename(); const cacheFilename = generateCacheFilename(false);
if (!cacheFilename) { if (!cacheFilename) {
throw new Error("Invalid cache filename"); throw new Error("Invalid cache filename");
} }
@@ -821,7 +821,7 @@ describe("Cache filename generator", () => {
} }
}); });
test("Cache file in labyrinth server", () => { test("Cache file in labyrinth server", () => {
const cacheFilename = generateCacheFilename(LAB_CACHE_NAME); const cacheFilename = generateCacheFilename(false, LAB_CACHE_NAME);
if (!cacheFilename) { if (!cacheFilename) {
throw new Error("Invalid cache filename"); throw new Error("Invalid cache filename");
} }

View File

@@ -774,7 +774,7 @@ describe("darkweb", () => {
test("openCache", () => { test("openCache", () => {
const ns = getNsOnDarkWeb(); const ns = getNsOnDarkWeb();
const darkweb = getDarknetServerOrThrow(SpecialServers.DarkWeb); const darkweb = getDarknetServerOrThrow(SpecialServers.DarkWeb);
const result = addCacheToServer(darkweb, "test"); const result = addCacheToServer(darkweb, false, "test");
if (!result.success) { if (!result.success) {
throw new Error("Cannot add cache"); throw new Error("Cannot add cache");
} }

View File

@@ -517,6 +517,7 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = `
"defense_exp": 1, "defense_exp": 1,
"dexterity": 1, "dexterity": 1,
"dexterity_exp": 1, "dexterity_exp": 1,
"dnet_money": 1,
"faction_rep": 1, "faction_rep": 1,
"hacking": 1, "hacking": 1,
"hacking_chance": 1, "hacking_chance": 1,
@@ -601,6 +602,7 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = `
"defense_exp": 1, "defense_exp": 1,
"dexterity": 1.3, "dexterity": 1.3,
"dexterity_exp": 1, "dexterity_exp": 1,
"dnet_money": 1,
"faction_rep": 1, "faction_rep": 1,
"hacking": 1, "hacking": 1,
"hacking_chance": 1, "hacking_chance": 1,
@@ -676,6 +678,7 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = `
"defense_exp": 1, "defense_exp": 1,
"dexterity": 1, "dexterity": 1,
"dexterity_exp": 1, "dexterity_exp": 1,
"dnet_money": 1,
"faction_rep": 1, "faction_rep": 1,
"hacking": 1, "hacking": 1,
"hacking_chance": 1, "hacking_chance": 1,