mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-23 22:52:29 +01:00
FEATURE: BitNode options (#1411)
This commit is contained in:
parent
0e1e8a9862
commit
783120c886
11
markdown/bitburner.bitnodebooleanoptions.disable4sdata.md
Normal file
11
markdown/bitburner.bitnodebooleanoptions.disable4sdata.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) > [disable4SData](./bitburner.bitnodebooleanoptions.disable4sdata.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions.disable4SData property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
disable4SData: boolean;
|
||||||
|
```
|
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) > [disableBladeburner](./bitburner.bitnodebooleanoptions.disablebladeburner.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions.disableBladeburner property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
disableBladeburner: boolean;
|
||||||
|
```
|
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) > [disableCorporation](./bitburner.bitnodebooleanoptions.disablecorporation.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions.disableCorporation property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
disableCorporation: boolean;
|
||||||
|
```
|
11
markdown/bitburner.bitnodebooleanoptions.disablegang.md
Normal file
11
markdown/bitburner.bitnodebooleanoptions.disablegang.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) > [disableGang](./bitburner.bitnodebooleanoptions.disablegang.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions.disableGang property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
disableGang: boolean;
|
||||||
|
```
|
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) > [disableHacknetServer](./bitburner.bitnodebooleanoptions.disablehacknetserver.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions.disableHacknetServer property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
disableHacknetServer: boolean;
|
||||||
|
```
|
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) > [disableSleeveExpAndAugmentation](./bitburner.bitnodebooleanoptions.disablesleeveexpandaugmentation.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions.disableSleeveExpAndAugmentation property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
disableSleeveExpAndAugmentation: boolean;
|
||||||
|
```
|
28
markdown/bitburner.bitnodebooleanoptions.md
Normal file
28
markdown/bitburner.bitnodebooleanoptions.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions interface
|
||||||
|
|
||||||
|
restrictHomePCUpgrade: The home computer's maximum RAM and number of cores are lower than normal. Max RAM: 128GB. Max core: 1.
|
||||||
|
|
||||||
|
disableSleeveExpAndAugmentation: Your Sleeves do not gain experience when they perform action. You also cannot buy augmentations for them.
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface BitNodeBooleanOptions
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Modifiers | Type | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [disable4SData](./bitburner.bitnodebooleanoptions.disable4sdata.md) | | boolean | |
|
||||||
|
| [disableBladeburner](./bitburner.bitnodebooleanoptions.disablebladeburner.md) | | boolean | |
|
||||||
|
| [disableCorporation](./bitburner.bitnodebooleanoptions.disablecorporation.md) | | boolean | |
|
||||||
|
| [disableGang](./bitburner.bitnodebooleanoptions.disablegang.md) | | boolean | |
|
||||||
|
| [disableHacknetServer](./bitburner.bitnodebooleanoptions.disablehacknetserver.md) | | boolean | |
|
||||||
|
| [disableSleeveExpAndAugmentation](./bitburner.bitnodebooleanoptions.disablesleeveexpandaugmentation.md) | | boolean | |
|
||||||
|
| [restrictHomePCUpgrade](./bitburner.bitnodebooleanoptions.restricthomepcupgrade.md) | | boolean | |
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) > [restrictHomePCUpgrade](./bitburner.bitnodebooleanoptions.restricthomepcupgrade.md)
|
||||||
|
|
||||||
|
## BitNodeBooleanOptions.restrictHomePCUpgrade property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
restrictHomePCUpgrade: boolean;
|
||||||
|
```
|
11
markdown/bitburner.bitnodeoptions.intelligenceoverride.md
Normal file
11
markdown/bitburner.bitnodeoptions.intelligenceoverride.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeOptions](./bitburner.bitnodeoptions.md) > [intelligenceOverride](./bitburner.bitnodeoptions.intelligenceoverride.md)
|
||||||
|
|
||||||
|
## BitNodeOptions.intelligenceOverride property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
intelligenceOverride: number | undefined;
|
||||||
|
```
|
24
markdown/bitburner.bitnodeoptions.md
Normal file
24
markdown/bitburner.bitnodeoptions.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeOptions](./bitburner.bitnodeoptions.md)
|
||||||
|
|
||||||
|
## BitNodeOptions interface
|
||||||
|
|
||||||
|
Default value: - sourceFileOverrides: an empty Map - intelligenceOverride: undefined - All boolean options: false
|
||||||
|
|
||||||
|
If you specify intelligenceOverride, it must be a non-negative integer.
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface BitNodeOptions extends BitNodeBooleanOptions
|
||||||
|
```
|
||||||
|
**Extends:** [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md)
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Modifiers | Type | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [intelligenceOverride](./bitburner.bitnodeoptions.intelligenceoverride.md) | | number \| undefined | |
|
||||||
|
| [sourceFileOverrides](./bitburner.bitnodeoptions.sourcefileoverrides.md) | | Map<number, number> | |
|
||||||
|
|
11
markdown/bitburner.bitnodeoptions.sourcefileoverrides.md
Normal file
11
markdown/bitburner.bitnodeoptions.sourcefileoverrides.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [BitNodeOptions](./bitburner.bitnodeoptions.md) > [sourceFileOverrides](./bitburner.bitnodeoptions.sourcefileoverrides.md)
|
||||||
|
|
||||||
|
## BitNodeOptions.sourceFileOverrides property
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
sourceFileOverrides: Map<number, number>;
|
||||||
|
```
|
@ -30,7 +30,9 @@
|
|||||||
| [AutocompleteData](./bitburner.autocompletedata.md) | Used for autocompletion |
|
| [AutocompleteData](./bitburner.autocompletedata.md) | Used for autocompletion |
|
||||||
| [BackdoorRequirement](./bitburner.backdoorrequirement.md) | Player must have installed a backdoor on this server. |
|
| [BackdoorRequirement](./bitburner.backdoorrequirement.md) | Player must have installed a backdoor on this server. |
|
||||||
| [BasicHGWOptions](./bitburner.basichgwoptions.md) | Options to affect the behavior of [hack](./bitburner.ns.hack.md)<!-- -->, [grow](./bitburner.ns.grow.md)<!-- -->, and [weaken](./bitburner.ns.weaken.md)<!-- -->. |
|
| [BasicHGWOptions](./bitburner.basichgwoptions.md) | Options to affect the behavior of [hack](./bitburner.ns.hack.md)<!-- -->, [grow](./bitburner.ns.grow.md)<!-- -->, and [weaken](./bitburner.ns.weaken.md)<!-- -->. |
|
||||||
|
| [BitNodeBooleanOptions](./bitburner.bitnodebooleanoptions.md) | <p>restrictHomePCUpgrade: The home computer's maximum RAM and number of cores are lower than normal. Max RAM: 128GB. Max core: 1.</p><p>disableSleeveExpAndAugmentation: Your Sleeves do not gain experience when they perform action. You also cannot buy augmentations for them.</p> |
|
||||||
| [BitNodeMultipliers](./bitburner.bitnodemultipliers.md) | All multipliers affecting the difficulty of the current challenge. |
|
| [BitNodeMultipliers](./bitburner.bitnodemultipliers.md) | All multipliers affecting the difficulty of the current challenge. |
|
||||||
|
| [BitNodeOptions](./bitburner.bitnodeoptions.md) | <p>Default value: - sourceFileOverrides: an empty Map - intelligenceOverride: undefined - All boolean options: false</p><p>If you specify intelligenceOverride, it must be a non-negative integer.</p> |
|
||||||
| [BitNodeRequirement](./bitburner.bitnoderequirement.md) | Player must be located in this BitNode. |
|
| [BitNodeRequirement](./bitburner.bitnoderequirement.md) | Player must be located in this BitNode. |
|
||||||
| [Bladeburner](./bitburner.bladeburner.md) | Bladeburner API |
|
| [Bladeburner](./bitburner.bladeburner.md) | Bladeburner API |
|
||||||
| [BladeburnerCurAction](./bitburner.bladeburnercuraction.md) | Bladeburner current action. |
|
| [BladeburnerCurAction](./bitburner.bladeburnercuraction.md) | Bladeburner current action. |
|
||||||
|
13
markdown/bitburner.resetinfo.bitnodeoptions.md
Normal file
13
markdown/bitburner.resetinfo.bitnodeoptions.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [ResetInfo](./bitburner.resetinfo.md) > [bitNodeOptions](./bitburner.resetinfo.bitnodeoptions.md)
|
||||||
|
|
||||||
|
## ResetInfo.bitNodeOptions property
|
||||||
|
|
||||||
|
Current BitNode options
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
bitNodeOptions: BitNodeOptions;
|
||||||
|
```
|
@ -16,6 +16,7 @@ interface ResetInfo
|
|||||||
|
|
||||||
| Property | Modifiers | Type | Description |
|
| Property | Modifiers | Type | Description |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
| [bitNodeOptions](./bitburner.resetinfo.bitnodeoptions.md) | | [BitNodeOptions](./bitburner.bitnodeoptions.md) | Current BitNode options |
|
||||||
| [currentNode](./bitburner.resetinfo.currentnode.md) | | number | The current bitnode |
|
| [currentNode](./bitburner.resetinfo.currentnode.md) | | number | The current bitnode |
|
||||||
| [lastAugReset](./bitburner.resetinfo.lastaugreset.md) | | number | Numeric timestamp (from Date.now()) of last augmentation reset |
|
| [lastAugReset](./bitburner.resetinfo.lastaugreset.md) | | number | Numeric timestamp (from Date.now()) of last augmentation reset |
|
||||||
| [lastNodeReset](./bitburner.resetinfo.lastnodereset.md) | | number | Numeric timestamp (from Date.now()) of last bitnode reset |
|
| [lastNodeReset](./bitburner.resetinfo.lastnodereset.md) | | number | Numeric timestamp (from Date.now()) of last bitnode reset |
|
||||||
|
@ -9,7 +9,7 @@ b1t\_flum3 into a different BN.
|
|||||||
**Signature:**
|
**Signature:**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
b1tflum3(nextBN: number, callbackScript?: string): void;
|
b1tflum3(nextBN: number, callbackScript?: string, bitNodeOptions?: BitNodeOptions): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
@ -18,6 +18,7 @@ b1tflum3(nextBN: number, callbackScript?: string): void;
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| nextBN | number | BN number to jump to |
|
| nextBN | number | BN number to jump to |
|
||||||
| callbackScript | string | _(Optional)_ Name of the script to launch in the next BN. |
|
| callbackScript | string | _(Optional)_ Name of the script to launch in the next BN. |
|
||||||
|
| bitNodeOptions | [BitNodeOptions](./bitburner.bitnodeoptions.md) | _(Optional)_ BitNode options for the next BN. |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ Destroy the w0r1d\_d43m0n and move on to the next BN.
|
|||||||
**Signature:**
|
**Signature:**
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
destroyW0r1dD43m0n(nextBN: number, callbackScript?: string): void;
|
destroyW0r1dD43m0n(nextBN: number, callbackScript?: string, bitNodeOptions?: BitNodeOptions): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
@ -18,6 +18,7 @@ destroyW0r1dD43m0n(nextBN: number, callbackScript?: string): void;
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| nextBN | number | BN number to jump to |
|
| nextBN | number | BN number to jump to |
|
||||||
| callbackScript | string | _(Optional)_ Name of the script to launch in the next BN. |
|
| callbackScript | string | _(Optional)_ Name of the script to launch in the next BN. |
|
||||||
|
| bitNodeOptions | [BitNodeOptions](./bitburner.bitnodeoptions.md) | _(Optional)_ BitNode options for the next BN. |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
|
@ -21,12 +21,12 @@ This API requires Source-File 4 to use. The RAM cost of all these functions is m
|
|||||||
| Method | Description |
|
| Method | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [applyToCompany(companyName, field)](./bitburner.singularity.applytocompany.md) | Apply for a job at a company. |
|
| [applyToCompany(companyName, field)](./bitburner.singularity.applytocompany.md) | Apply for a job at a company. |
|
||||||
| [b1tflum3(nextBN, callbackScript)](./bitburner.singularity.b1tflum3.md) | b1t\_flum3 into a different BN. |
|
| [b1tflum3(nextBN, callbackScript, bitNodeOptions)](./bitburner.singularity.b1tflum3.md) | b1t\_flum3 into a different BN. |
|
||||||
| [checkFactionInvitations()](./bitburner.singularity.checkfactioninvitations.md) | List all current faction invitations. |
|
| [checkFactionInvitations()](./bitburner.singularity.checkfactioninvitations.md) | List all current faction invitations. |
|
||||||
| [commitCrime(crime, focus)](./bitburner.singularity.commitcrime.md) | Commit a crime. |
|
| [commitCrime(crime, focus)](./bitburner.singularity.commitcrime.md) | Commit a crime. |
|
||||||
| [connect(hostname)](./bitburner.singularity.connect.md) | Connect to a server. |
|
| [connect(hostname)](./bitburner.singularity.connect.md) | Connect to a server. |
|
||||||
| [createProgram(program, focus)](./bitburner.singularity.createprogram.md) | Create a program. |
|
| [createProgram(program, focus)](./bitburner.singularity.createprogram.md) | Create a program. |
|
||||||
| [destroyW0r1dD43m0n(nextBN, callbackScript)](./bitburner.singularity.destroyw0r1dd43m0n.md) | Destroy the w0r1d\_d43m0n and move on to the next BN. |
|
| [destroyW0r1dD43m0n(nextBN, callbackScript, bitNodeOptions)](./bitburner.singularity.destroyw0r1dd43m0n.md) | Destroy the w0r1d\_d43m0n and move on to the next BN. |
|
||||||
| [donateToFaction(faction, amount)](./bitburner.singularity.donatetofaction.md) | Donate to a faction. |
|
| [donateToFaction(faction, amount)](./bitburner.singularity.donatetofaction.md) | Donate to a faction. |
|
||||||
| [exportGame()](./bitburner.singularity.exportgame.md) | Backup game save. |
|
| [exportGame()](./bitburner.singularity.exportgame.md) | Backup game save. |
|
||||||
| [exportGameBonus()](./bitburner.singularity.exportgamebonus.md) | Returns Backup save bonus availability. |
|
| [exportGameBonus()](./bitburner.singularity.exportgamebonus.md) | Returns Backup save bonus availability. |
|
||||||
|
@ -29,7 +29,7 @@ import { workerScripts } from "../Netscript/WorkerScripts";
|
|||||||
|
|
||||||
import { getRecordValues } from "../Types/Record";
|
import { getRecordValues } from "../Types/Record";
|
||||||
import { ServerConstants } from "../Server/data/Constants";
|
import { ServerConstants } from "../Server/data/Constants";
|
||||||
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
|
import { canAccessBitNodeFeature, isBitNodeFinished, knowAboutBitverse } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
|
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
|
||||||
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
|
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
|
||||||
@ -60,14 +60,6 @@ export interface AchievementData {
|
|||||||
Description: string;
|
Description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function canAccessBitNodeFeature(bitNode: number): boolean {
|
|
||||||
return Player.bitNodeN === bitNode || Player.sourceFileLvl(bitNode) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function knowsAboutBitverse(): boolean {
|
|
||||||
return Player.sourceFiles.size > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sfAchievements(): Record<string, Achievement> {
|
function sfAchievements(): Record<string, Achievement> {
|
||||||
const achs: Record<string, Achievement> = {};
|
const achs: Record<string, Achievement> = {};
|
||||||
for (let i = 1; i <= 12; i++) {
|
for (let i = 1; i <= 12; i++) {
|
||||||
@ -75,7 +67,7 @@ function sfAchievements(): Record<string, Achievement> {
|
|||||||
achs[ID] = {
|
achs[ID] = {
|
||||||
...achievementData[ID],
|
...achievementData[ID],
|
||||||
Icon: ID,
|
Icon: ID,
|
||||||
Visible: knowsAboutBitverse,
|
Visible: knowAboutBitverse,
|
||||||
Condition: () => Player.sourceFileLvl(i) >= 1,
|
Condition: () => Player.sourceFileLvl(i) >= 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -498,7 +490,7 @@ export const achievements: Record<string, Achievement> = {
|
|||||||
INDECISIVE: {
|
INDECISIVE: {
|
||||||
...achievementData.INDECISIVE,
|
...achievementData.INDECISIVE,
|
||||||
Icon: "1H",
|
Icon: "1H",
|
||||||
Visible: knowsAboutBitverse,
|
Visible: knowAboutBitverse,
|
||||||
Condition: (function () {
|
Condition: (function () {
|
||||||
let c = 0;
|
let c = 0;
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@ -514,13 +506,13 @@ export const achievements: Record<string, Achievement> = {
|
|||||||
FAST_BN: {
|
FAST_BN: {
|
||||||
...achievementData.FAST_BN,
|
...achievementData.FAST_BN,
|
||||||
Icon: "2DAYS",
|
Icon: "2DAYS",
|
||||||
Visible: knowsAboutBitverse,
|
Visible: knowAboutBitverse,
|
||||||
Condition: () => isBitNodeFinished() && Player.playtimeSinceLastBitnode < 1000 * 60 * 60 * 24 * 2,
|
Condition: () => isBitNodeFinished() && Player.playtimeSinceLastBitnode < 1000 * 60 * 60 * 24 * 2,
|
||||||
},
|
},
|
||||||
CHALLENGE_BN1: {
|
CHALLENGE_BN1: {
|
||||||
...achievementData.CHALLENGE_BN1,
|
...achievementData.CHALLENGE_BN1,
|
||||||
Icon: "BN1+",
|
Icon: "BN1+",
|
||||||
Visible: knowsAboutBitverse,
|
Visible: knowAboutBitverse,
|
||||||
Condition: () =>
|
Condition: () =>
|
||||||
Player.bitNodeN === 1 &&
|
Player.bitNodeN === 1 &&
|
||||||
isBitNodeFinished() &&
|
isBitNodeFinished() &&
|
||||||
|
@ -14,7 +14,7 @@ export function ArcadeRoot(): React.ReactElement {
|
|||||||
const [page, setPage] = useState(Page.None);
|
const [page, setPage] = useState(Page.None);
|
||||||
|
|
||||||
function mbBurner2000(): void {
|
function mbBurner2000(): void {
|
||||||
if (Player.sourceFileLvl(1) === 0) {
|
if (Player.activeSourceFileLvl(1) === 0) {
|
||||||
AlertEvents.emit("This machine is broken.");
|
AlertEvents.emit("This machine is broken.");
|
||||||
} else {
|
} else {
|
||||||
setPage(Page.Megabyteburner2000);
|
setPage(Page.Megabyteburner2000);
|
||||||
|
@ -26,7 +26,7 @@ const soaAugmentationNames = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export function getBaseAugmentationPriceMultiplier(): number {
|
export function getBaseAugmentationPriceMultiplier(): number {
|
||||||
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
|
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.activeSourceFileLvl(11)];
|
||||||
}
|
}
|
||||||
export function getGenericAugmentationPriceMultiplier(): number {
|
export function getGenericAugmentationPriceMultiplier(): number {
|
||||||
const queuedNonSoAAugmentationList = Player.queuedAugmentations.filter((augmentation) => {
|
const queuedNonSoAAugmentationList = Player.queuedAugmentations.filter((augmentation) => {
|
||||||
|
@ -7,6 +7,7 @@ import { Player } from "@player";
|
|||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { formatPercent } from "../../ui/formatNumber";
|
import { formatPercent } from "../../ui/formatNumber";
|
||||||
import { Augmentations } from "../Augmentations";
|
import { Augmentations } from "../Augmentations";
|
||||||
|
import { canAccessBitNodeFeature } from "../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
function calculateAugmentedStats(): Multipliers {
|
function calculateAugmentedStats(): Multipliers {
|
||||||
let augP: Multipliers = defaultMultipliers();
|
let augP: Multipliers = defaultMultipliers();
|
||||||
@ -29,7 +30,7 @@ function customFormatPercent(value: number): string {
|
|||||||
|
|
||||||
function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement {
|
function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement {
|
||||||
// If the player doesn't have access to SF5 feature or if the property isn't affected by BitNode mults
|
// If the player doesn't have access to SF5 feature or if the property isn't affected by BitNode mults
|
||||||
if (props.mult === 1 || (Player.bitNodeN !== 5 && Player.sourceFileLvl(5) === 0)) {
|
if (props.mult === 1 || !canAccessBitNodeFeature(5)) {
|
||||||
return <Typography color={props.color}>{customFormatPercent(props.base)}</Typography>;
|
return <Typography color={props.color}>{customFormatPercent(props.base)}</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,93 +3,101 @@ import Box from "@mui/material/Box";
|
|||||||
import List from "@mui/material/List";
|
import List from "@mui/material/List";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Exploit, ExploitName } from "../../Exploits/Exploit";
|
import { Exploit, ExploitDescription } from "../../Exploits/Exploit";
|
||||||
import { Player } from "@player";
|
import { Player } from "@player";
|
||||||
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { SourceFile } from "../../SourceFile/SourceFile";
|
|
||||||
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||||
|
|
||||||
interface SfMinus1 {
|
interface SourceFileData {
|
||||||
info: React.ReactElement;
|
|
||||||
n: number;
|
n: number;
|
||||||
|
level: number;
|
||||||
|
maxLevel: number;
|
||||||
|
activeLevel: number;
|
||||||
name: string;
|
name: string;
|
||||||
lvl: number;
|
info: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
const safeGetSf = (sfNum: number): SourceFile | SfMinus1 | null => {
|
const getSourceFileData = (sfNumber: number): SourceFileData | null => {
|
||||||
if (sfNum === -1) {
|
let maxLevel: number;
|
||||||
const sfMinus1: SfMinus1 = {
|
switch (sfNumber) {
|
||||||
|
case -1:
|
||||||
|
maxLevel = Object.keys(Exploit).length;
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
maxLevel = Infinity;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
maxLevel = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceFile = SourceFiles["SourceFile" + sfNumber];
|
||||||
|
if (sourceFile === undefined) {
|
||||||
|
console.error(`Invalid source file number: ${sfNumber}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
n: sfNumber,
|
||||||
|
level: Player.sourceFileLvl(sfNumber),
|
||||||
|
maxLevel: maxLevel,
|
||||||
|
activeLevel: Player.activeSourceFileLvl(sfNumber),
|
||||||
|
name: sourceFile.name,
|
||||||
|
info: sourceFile.info,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SourceFilesElement(): React.ReactElement {
|
||||||
|
const sourceFileList: SourceFileData[] = [];
|
||||||
|
|
||||||
|
const exploits = Player.exploits;
|
||||||
|
// Create a fake SF for -1, if "owned"
|
||||||
|
if (exploits.length > 0) {
|
||||||
|
sourceFileList.push({
|
||||||
|
n: -1,
|
||||||
|
level: Player.exploits.length,
|
||||||
|
maxLevel: Object.keys(Exploit).length,
|
||||||
|
activeLevel: Player.exploits.length,
|
||||||
|
name: "Source-File -1: Exploits in the BitNodes",
|
||||||
info: (
|
info: (
|
||||||
<>
|
<>
|
||||||
This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web ecosystem.
|
This Source-File can only be acquired with obscure knowledge of the game, Javascript, and the web ecosystem.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
It increases all of the player's multipliers by 0.1%
|
It increases all of the player's multipliers by 0.1%
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
You have found the following exploits:
|
You have found the following exploits:
|
||||||
<br />
|
<ul>
|
||||||
<br />
|
{Player.exploits.map((c) => (
|
||||||
{Player.exploits.map((c) => (
|
<li key={c}>
|
||||||
<React.Fragment key={c}>
|
{c}: {ExploitDescription[c]}
|
||||||
* {ExploitName(c)}
|
</li>
|
||||||
<br />
|
))}
|
||||||
</React.Fragment>
|
</ul>
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
lvl: Player.exploits.length,
|
});
|
||||||
n: -1,
|
}
|
||||||
name: "Source-File -1: Exploits in the BitNodes",
|
for (const sfNumber of Player.sourceFiles.keys()) {
|
||||||
};
|
const sourceFileData = getSourceFileData(sfNumber);
|
||||||
return sfMinus1;
|
if (!sourceFileData) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sourceFileList.push(sourceFileData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const srcFileKey = "SourceFile" + sfNum;
|
|
||||||
const sfObj = SourceFiles[srcFileKey];
|
|
||||||
if (sfObj == null) {
|
|
||||||
console.error(`Invalid source file number: ${sfNum}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return sfObj;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMaxLevel = (sfObj: SourceFile | SfMinus1): string | number => {
|
|
||||||
let maxLevel;
|
|
||||||
switch (sfObj.n) {
|
|
||||||
case 12:
|
|
||||||
maxLevel = "∞";
|
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
maxLevel = Object.keys(Exploit).length;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
maxLevel = "3";
|
|
||||||
}
|
|
||||||
return maxLevel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function SourceFilesElement(): React.ReactElement {
|
|
||||||
const sourceFilesCopy = new Map(Player.sourceFiles);
|
|
||||||
const exploits = Player.exploits;
|
|
||||||
// Create a fake SF for -1, if "owned"
|
|
||||||
if (exploits.length > 0) {
|
|
||||||
sourceFilesCopy.set(-1, exploits.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
const sfList = [...sourceFilesCopy];
|
|
||||||
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
|
||||||
sfList.sort(([n1, __lvl1], [n2, __lvl2]) => n1 - n2);
|
sourceFileList.sort((a, b) => a.n - b.n);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [selectedSf, setSelectedSf] = useState(() => {
|
const [selectedSfData, setSelectedSfData] = useState<SourceFileData | null>(() => {
|
||||||
if (sfList.length === 0) return null;
|
if (sourceFileList.length === 0) {
|
||||||
const [n, lvl] = sfList[0];
|
return null;
|
||||||
return { n, lvl };
|
}
|
||||||
|
return sourceFileList[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!selectedSf) {
|
if (!selectedSfData) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,26 +112,26 @@ export function SourceFilesElement(): React.ReactElement {
|
|||||||
sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}
|
sx={{ height: 400, overflowY: "scroll", borderRight: `1px solid ${Settings.theme.welllight}` }}
|
||||||
disablePadding
|
disablePadding
|
||||||
>
|
>
|
||||||
{sfList.map(([n, lvl], i) => {
|
{sourceFileList.map((sourceFileData, i) => {
|
||||||
const sfObj = safeGetSf(n);
|
|
||||||
if (!sfObj) return;
|
|
||||||
|
|
||||||
const maxLevel = getMaxLevel(sfObj);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
key={i + 1}
|
key={i + 1}
|
||||||
onClick={() => setSelectedSf({ n, lvl })}
|
onClick={() => setSelectedSfData(sourceFileData)}
|
||||||
selected={selectedSf.n === n}
|
selected={selectedSfData.n === sourceFileData.n}
|
||||||
sx={{ py: 0 }}
|
sx={{ py: 0 }}
|
||||||
>
|
>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
disableTypography
|
disableTypography
|
||||||
primary={<Typography>{sfObj.name}</Typography>}
|
primary={<Typography>{sourceFileData.name}</Typography>}
|
||||||
secondary={
|
secondary={
|
||||||
<Typography>
|
<>
|
||||||
Level {lvl} / {maxLevel}
|
<Typography>
|
||||||
</Typography>
|
Level: {sourceFileData.level} / {sourceFileData.maxLevel}
|
||||||
|
</Typography>
|
||||||
|
{sourceFileData.activeLevel < sourceFileData.level && (
|
||||||
|
<Typography>Active level: {sourceFileData.activeLevel}</Typography>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
@ -131,28 +139,25 @@ export function SourceFilesElement(): React.ReactElement {
|
|||||||
})}
|
})}
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ m: 1 }}>
|
{selectedSfData !== null && (
|
||||||
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
<Box sx={{ m: 1 }}>
|
||||||
{safeGetSf(selectedSf.n)?.name}
|
<Typography variant="h6" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||||
</Typography>
|
{selectedSfData.name}
|
||||||
<Typography component="div" sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
</Typography>
|
||||||
{(() => {
|
<Typography component="div" sx={{ maxHeight: 350, overflowY: "scroll" }}>
|
||||||
const sfObj = safeGetSf(selectedSf.n);
|
Level: {selectedSfData.level} / {selectedSfData.maxLevel}
|
||||||
if (!sfObj) return;
|
<br />
|
||||||
|
{selectedSfData.activeLevel < selectedSfData.level && (
|
||||||
const maxLevel = getMaxLevel(sfObj);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
Level {selectedSf.lvl} / {maxLevel}
|
Active level: {selectedSfData.activeLevel}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
|
||||||
{sfObj.info}
|
|
||||||
</>
|
</>
|
||||||
);
|
)}
|
||||||
})()}
|
<br />
|
||||||
</Typography>
|
{selectedSfData.info}
|
||||||
</Box>
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -962,5 +962,5 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function initBitNodeMultipliers(): void {
|
export function initBitNodeMultipliers(): void {
|
||||||
replaceCurrentNodeMults(getBitNodeMultipliers(Player.bitNodeN, Player.sourceFileLvl(Player.bitNodeN) + 1));
|
replaceCurrentNodeMults(getBitNodeMultipliers(Player.bitNodeN, Player.activeSourceFileLvl(Player.bitNodeN) + 1));
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
|
import { Player } from "@player";
|
||||||
|
import { type BitNodeOptions } from "@nsdefs";
|
||||||
import { GetServer } from "../Server/AllServers";
|
import { GetServer } from "../Server/AllServers";
|
||||||
import { Server } from "../Server/Server";
|
import { Server } from "../Server/Server";
|
||||||
import { SpecialServers } from "../Server/data/SpecialServers";
|
import { SpecialServers } from "../Server/data/SpecialServers";
|
||||||
|
import { JSONMap } from "../Types/Jsonable";
|
||||||
|
|
||||||
|
export const validBitNodes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
|
||||||
|
|
||||||
export function isBitNodeFinished(): boolean {
|
export function isBitNodeFinished(): boolean {
|
||||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||||
@ -9,3 +14,70 @@ export function isBitNodeFinished(): boolean {
|
|||||||
}
|
}
|
||||||
return wd.backdoorInstalled;
|
return wd.backdoorInstalled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canAccessBitNodeFeature(bitNode: number): boolean {
|
||||||
|
return Player.bitNodeN === bitNode || Player.activeSourceFileLvl(bitNode) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function knowAboutBitverse(): boolean {
|
||||||
|
for (const sfActiveLevel of Player.activeSourceFiles.values()) {
|
||||||
|
if (sfActiveLevel > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDefaultBitNodeOptions(): BitNodeOptions {
|
||||||
|
return {
|
||||||
|
sourceFileOverrides: new Map<number, number>(),
|
||||||
|
intelligenceOverride: undefined,
|
||||||
|
restrictHomePCUpgrade: false,
|
||||||
|
disableGang: false,
|
||||||
|
disableCorporation: false,
|
||||||
|
disableBladeburner: false,
|
||||||
|
disable4SData: false,
|
||||||
|
disableHacknetServer: false,
|
||||||
|
disableSleeveExpAndAugmentation: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateSourceFileOverrides(
|
||||||
|
sourceFileOverrides: Map<number, number>,
|
||||||
|
isDataFromPlayer: boolean,
|
||||||
|
): {
|
||||||
|
valid: boolean;
|
||||||
|
message?: string;
|
||||||
|
} {
|
||||||
|
if (!isDataFromPlayer && !(sourceFileOverrides instanceof JSONMap)) {
|
||||||
|
return { valid: false, message: `It must be a JSONMap.` };
|
||||||
|
}
|
||||||
|
for (const [sfNumber, sfLevel] of sourceFileOverrides.entries()) {
|
||||||
|
if (!validBitNodes.includes(sfNumber)) {
|
||||||
|
return { valid: false, message: `Invalid BitNode: ${sfNumber}.` };
|
||||||
|
}
|
||||||
|
if (!Number.isFinite(sfLevel)) {
|
||||||
|
return { valid: false, message: `Invalid SF level: ${sfLevel}.` };
|
||||||
|
}
|
||||||
|
const maxSfLevel = Player.sourceFileLvl(sfNumber);
|
||||||
|
if (sfLevel > maxSfLevel) {
|
||||||
|
return { valid: false, message: `Invalid SF level: ${sfLevel}. Max level: ${maxSfLevel}.` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { valid: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setBitNodeOptions(bitNodeOptions: BitNodeOptions): void {
|
||||||
|
const validationResultForSourceFileOverrides = validateSourceFileOverrides(bitNodeOptions.sourceFileOverrides, false);
|
||||||
|
if (!validationResultForSourceFileOverrides.valid) {
|
||||||
|
throw new Error(`sourceFileOverrides is invalid. Reason: ${validationResultForSourceFileOverrides.message}`);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
bitNodeOptions.intelligenceOverride !== undefined &&
|
||||||
|
(!Number.isInteger(bitNodeOptions.intelligenceOverride) || bitNodeOptions.intelligenceOverride < 0)
|
||||||
|
) {
|
||||||
|
throw new Error(`intelligenceOverride is invalid. It must be a non-negative integer.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(Player.bitNodeOptions, bitNodeOptions);
|
||||||
|
}
|
||||||
|
433
src/BitNode/ui/BitNodeAdvancedOptions.tsx
Normal file
433
src/BitNode/ui/BitNodeAdvancedOptions.tsx
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
import { type BitNodeBooleanOptions } from "@nsdefs";
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Collapse,
|
||||||
|
ListItemButton,
|
||||||
|
ListItemText,
|
||||||
|
MenuItem,
|
||||||
|
Paper,
|
||||||
|
Select,
|
||||||
|
TextField,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { OptionSwitch } from "../../ui/React/OptionSwitch";
|
||||||
|
import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
|
||||||
|
import { ExpandLess, ExpandMore } from "@mui/icons-material";
|
||||||
|
import { JSONMap } from "../../Types/Jsonable";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import { Player } from "@player";
|
||||||
|
|
||||||
|
interface SourceFileButtonRowProps {
|
||||||
|
sfNumber: number;
|
||||||
|
sfLevel: number;
|
||||||
|
sfActiveLevel: number;
|
||||||
|
callbacks: BitNodeAdvancedOptionsProps["callbacks"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function SourceFileButtonRow({
|
||||||
|
sfNumber,
|
||||||
|
sfLevel,
|
||||||
|
sfActiveLevel,
|
||||||
|
callbacks,
|
||||||
|
}: SourceFileButtonRowProps): React.ReactElement {
|
||||||
|
const title = `SF-${sfNumber}`;
|
||||||
|
const sourceFileLevelTool =
|
||||||
|
sfNumber !== 12 ? (
|
||||||
|
[...Array(sfLevel + 1).keys()].map((level) => (
|
||||||
|
<Button
|
||||||
|
key={level}
|
||||||
|
onClick={() => {
|
||||||
|
callbacks.setSfActiveLevel(sfNumber, level);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
marginRight: "0.5rem !important",
|
||||||
|
border: level === sfActiveLevel ? `1px solid ${Settings.theme.info}` : "",
|
||||||
|
minWidth: "40px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{level}
|
||||||
|
</Button>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
// The usage of TextField instead of NumberInput is intentional.
|
||||||
|
<TextField
|
||||||
|
sx={{ maxWidth: "185px" }}
|
||||||
|
value={sfActiveLevel}
|
||||||
|
onChange={(event) => {
|
||||||
|
// Empty string will be automatically changed to "0".
|
||||||
|
if (event.target.value === "") {
|
||||||
|
callbacks.setSfActiveLevel(sfNumber, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const level = Number.parseInt(event.target.value);
|
||||||
|
if (!Number.isFinite(level) || level < 0 || level > sfLevel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callbacks.setSfActiveLevel(sfNumber, level);
|
||||||
|
}}
|
||||||
|
></TextField>
|
||||||
|
);
|
||||||
|
const extraInfo =
|
||||||
|
sfNumber === 12 ? (
|
||||||
|
<td>
|
||||||
|
<Typography marginLeft="1rem">Max level: {sfLevel}</Typography>
|
||||||
|
</td>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Typography>{title}</Typography>
|
||||||
|
</td>
|
||||||
|
<td>{sourceFileLevelTool}</td>
|
||||||
|
{extraInfo}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SourceFileOverrides({
|
||||||
|
currentSourceFiles,
|
||||||
|
sourceFileOverrides,
|
||||||
|
callbacks,
|
||||||
|
getSfLevel,
|
||||||
|
}: {
|
||||||
|
currentSourceFiles: BitNodeAdvancedOptionsProps["currentSourceFiles"];
|
||||||
|
sourceFileOverrides: BitNodeAdvancedOptionsProps["sourceFileOverrides"];
|
||||||
|
callbacks: BitNodeAdvancedOptionsProps["callbacks"];
|
||||||
|
getSfLevel: (sfNumber: number) => number;
|
||||||
|
}): React.ReactElement {
|
||||||
|
const firstSourceFile = React.useMemo(
|
||||||
|
() => (currentSourceFiles.size > 0 ? [...currentSourceFiles.keys()][0] : null),
|
||||||
|
[currentSourceFiles],
|
||||||
|
);
|
||||||
|
const [selectElementValue, setSelectElementValue] = React.useState<number | null>(firstSourceFile);
|
||||||
|
const getMenuItemList = (data: typeof sourceFileOverrides): number[] => {
|
||||||
|
return [...currentSourceFiles.keys()].filter((sfNumber) => ![...data.keys()].includes(sfNumber));
|
||||||
|
};
|
||||||
|
const menuItemList = getMenuItemList(sourceFileOverrides);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (sourceFileOverrides.size === 0) {
|
||||||
|
setSelectElementValue(firstSourceFile);
|
||||||
|
}
|
||||||
|
}, [sourceFileOverrides, firstSourceFile]);
|
||||||
|
|
||||||
|
const basicNote = `Changing the active level of a SF is temporary; you still permanently own that SF level. For example, if
|
||||||
|
you enter BN 1.3 while having SF 1.2 but with the active level set to 0, you will not get the bonuses from SF
|
||||||
|
1.1 or SF 1.2, but you will still earn SF 1.3 when destroying the BN.`;
|
||||||
|
const note = currentSourceFiles.has(10) ? (
|
||||||
|
<>
|
||||||
|
<Typography>Note:</Typography>
|
||||||
|
<ul style={{ marginTop: 0 }}>
|
||||||
|
<li>{basicNote}</li>
|
||||||
|
<li>
|
||||||
|
Changing the active level of SF 10 does not affect your current sleeves or the maximum number of sleeves.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Typography>Note: {basicNote}</Typography>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography>Override active level of Source-File:</Typography>
|
||||||
|
<br />
|
||||||
|
<Typography component="div">{note}</Typography>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
disabled={menuItemList.length === 0}
|
||||||
|
value={selectElementValue ?? ""}
|
||||||
|
onChange={(event) => {
|
||||||
|
setSelectElementValue(Number(event.target.value));
|
||||||
|
}}
|
||||||
|
sx={{ minWidth: "80px" }}
|
||||||
|
>
|
||||||
|
{menuItemList.map((sfNumber) => (
|
||||||
|
<MenuItem key={sfNumber} value={sfNumber}>
|
||||||
|
SF-{sfNumber}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
<Button
|
||||||
|
disabled={menuItemList.length === 0}
|
||||||
|
onClick={() => {
|
||||||
|
if (!selectElementValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newSfOverrides = new JSONMap(sourceFileOverrides);
|
||||||
|
newSfOverrides.set(selectElementValue, getSfLevel(selectElementValue));
|
||||||
|
const newMenuItemList = getMenuItemList(newSfOverrides);
|
||||||
|
if (newMenuItemList.length > 0) {
|
||||||
|
setSelectElementValue(newMenuItemList[0]);
|
||||||
|
} else {
|
||||||
|
setSelectElementValue(null);
|
||||||
|
}
|
||||||
|
callbacks.setSfOverrides(newSfOverrides);
|
||||||
|
}}
|
||||||
|
sx={{ marginLeft: "1rem" }}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
<ButtonWithTooltip
|
||||||
|
normalTooltip="Remove all overridden SF"
|
||||||
|
disabledTooltip={sourceFileOverrides.size === 0 ? "No overridden SF" : ""}
|
||||||
|
onClick={() => {
|
||||||
|
callbacks.setSfOverrides(new JSONMap());
|
||||||
|
}}
|
||||||
|
buttonProps={{ sx: { marginLeft: "1rem" } }}
|
||||||
|
>
|
||||||
|
Remove all
|
||||||
|
</ButtonWithTooltip>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
{sourceFileOverrides.size > 0 && (
|
||||||
|
<>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Tooltip title="Set active level for all chosen SF">
|
||||||
|
<Typography minWidth="7rem">Set all SF</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{[0, 1, 2, 3].map((level) => (
|
||||||
|
<ButtonWithTooltip
|
||||||
|
key={level}
|
||||||
|
onClick={() => {
|
||||||
|
const newSfOverrides = new JSONMap(sourceFileOverrides);
|
||||||
|
for (const [sfNumber] of newSfOverrides) {
|
||||||
|
newSfOverrides.set(sfNumber, Math.min(level, getSfLevel(sfNumber)));
|
||||||
|
}
|
||||||
|
callbacks.setSfOverrides(newSfOverrides);
|
||||||
|
}}
|
||||||
|
buttonProps={{ sx: { marginRight: "0.5rem", minWidth: "40px" } }}
|
||||||
|
>
|
||||||
|
{level}
|
||||||
|
</ButtonWithTooltip>
|
||||||
|
))}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{[...sourceFileOverrides.keys()].map((sfNumber) => (
|
||||||
|
<SourceFileButtonRow
|
||||||
|
key={sfNumber}
|
||||||
|
sfNumber={sfNumber}
|
||||||
|
sfLevel={getSfLevel(sfNumber)}
|
||||||
|
sfActiveLevel={sourceFileOverrides.get(sfNumber) ?? 0}
|
||||||
|
callbacks={callbacks}
|
||||||
|
></SourceFileButtonRow>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function IntelligenceOverride({
|
||||||
|
intelligenceOverride,
|
||||||
|
callbacks,
|
||||||
|
}: {
|
||||||
|
intelligenceOverride: BitNodeAdvancedOptionsProps["intelligenceOverride"];
|
||||||
|
callbacks: BitNodeAdvancedOptionsProps["callbacks"];
|
||||||
|
}): React.ReactElement {
|
||||||
|
const [enabled, setEnabled] = React.useState<boolean>(false);
|
||||||
|
const [lastValueOfIntelligenceOverride, setLastValueOfIntelligenceOverride] = React.useState<number | undefined>(
|
||||||
|
Player.skills.intelligence,
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<OptionSwitch
|
||||||
|
disabled={Player.skills.intelligence <= 0}
|
||||||
|
checked={false}
|
||||||
|
onChange={(value) => {
|
||||||
|
setEnabled(value);
|
||||||
|
if (!value) {
|
||||||
|
// If this option is disabled, save last value and reset data.
|
||||||
|
setLastValueOfIntelligenceOverride(intelligenceOverride);
|
||||||
|
callbacks.setIntelligenceOverride(undefined);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// If this option is enabled, load last value.
|
||||||
|
callbacks.setIntelligenceOverride(lastValueOfIntelligenceOverride);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
text={
|
||||||
|
<>
|
||||||
|
<Typography component="div" display="flex" gap="1rem">
|
||||||
|
<Typography display="flex" alignItems="center">
|
||||||
|
Override Intelligence:
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
sx={{ maxWidth: "4rem" }}
|
||||||
|
disabled={!enabled}
|
||||||
|
value={intelligenceOverride !== undefined ? intelligenceOverride : ""}
|
||||||
|
onChange={(event) => {
|
||||||
|
// Empty string will be automatically changed to "0".
|
||||||
|
if (event.target.value === "") {
|
||||||
|
callbacks.setIntelligenceOverride(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value = Number.parseInt(event.target.value);
|
||||||
|
if (!Number.isInteger(value) || value < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callbacks.setIntelligenceOverride(value);
|
||||||
|
}}
|
||||||
|
></TextField>
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
tooltip={
|
||||||
|
<>
|
||||||
|
<Typography component="div">
|
||||||
|
The Intelligence bonuses for you and your Sleeves will be limited by this value. For example:
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
If your Intelligence is 1000 and you set this value to 500, the "effective" Intelligence, which is used
|
||||||
|
for bonus calculation, is only 500.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
If a Sleeve's Intelligence is 200 and you set this value to 500, the "effective" Intelligence, which is
|
||||||
|
used for bonus calculation, is still 200.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
You will still gain Intelligence experience as normal. Intelligence Override only affects the Intelligence
|
||||||
|
bonus.
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
<Typography>
|
||||||
|
The "effective" Intelligence will be shown in the character overview. If the effective value is different
|
||||||
|
from the original value, you can hover your mouse over it to see the original value.
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BitNodeAdvancedOptionsProps {
|
||||||
|
targetBitNode: number;
|
||||||
|
currentSourceFiles: Map<number, number>;
|
||||||
|
sourceFileOverrides: JSONMap<number, number>;
|
||||||
|
intelligenceOverride: number | undefined;
|
||||||
|
bitNodeBooleanOptions: BitNodeBooleanOptions;
|
||||||
|
callbacks: {
|
||||||
|
setSfOverrides: (value: JSONMap<number, number>) => void;
|
||||||
|
setSfActiveLevel: (sfNumber: number, sfLevel: number) => void;
|
||||||
|
setIntelligenceOverride: (value: number | undefined) => void;
|
||||||
|
setBooleanOption: (key: keyof BitNodeBooleanOptions, value: boolean) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BitNodeAdvancedOptions({
|
||||||
|
targetBitNode,
|
||||||
|
currentSourceFiles,
|
||||||
|
sourceFileOverrides,
|
||||||
|
intelligenceOverride,
|
||||||
|
bitNodeBooleanOptions,
|
||||||
|
callbacks,
|
||||||
|
}: BitNodeAdvancedOptionsProps): React.ReactElement {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const getSfLevel = React.useCallback(
|
||||||
|
(sfNumber: number): number => {
|
||||||
|
return currentSourceFiles.get(sfNumber) ?? 0;
|
||||||
|
},
|
||||||
|
[currentSourceFiles],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box component={Paper} sx={{ mt: 1, p: 1 }}>
|
||||||
|
<ListItemButton disableGutters onClick={() => setOpen((old) => !old)} sx={{ padding: "4px 8px" }}>
|
||||||
|
<ListItemText primary={<Typography variant="h6">Advanced options</Typography>} />
|
||||||
|
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||||
|
</ListItemButton>
|
||||||
|
<Collapse in={open}>
|
||||||
|
<Box sx={{ padding: "0 1rem" }}>
|
||||||
|
<OptionSwitch
|
||||||
|
checked={bitNodeBooleanOptions.restrictHomePCUpgrade}
|
||||||
|
onChange={(value) => {
|
||||||
|
callbacks.setBooleanOption("restrictHomePCUpgrade", value);
|
||||||
|
}}
|
||||||
|
text="Restrict max RAM and core of Home PC"
|
||||||
|
tooltip="The home computer's maximum RAM and number of cores are lower than normal. Max RAM: 128GB. Max core: 1."
|
||||||
|
/>
|
||||||
|
<OptionSwitch
|
||||||
|
disabled={getSfLevel(2) === 0 && targetBitNode !== 2}
|
||||||
|
checked={bitNodeBooleanOptions.disableGang}
|
||||||
|
onChange={(value) => {
|
||||||
|
callbacks.setBooleanOption("disableGang", value);
|
||||||
|
}}
|
||||||
|
text="Disable Gang"
|
||||||
|
tooltip="Disable Gang"
|
||||||
|
/>
|
||||||
|
<OptionSwitch
|
||||||
|
disabled={getSfLevel(3) === 0 && targetBitNode !== 3}
|
||||||
|
checked={bitNodeBooleanOptions.disableCorporation}
|
||||||
|
onChange={(value) => {
|
||||||
|
callbacks.setBooleanOption("disableCorporation", value);
|
||||||
|
}}
|
||||||
|
text="Disable Corporation"
|
||||||
|
tooltip="Disable Corporation"
|
||||||
|
/>
|
||||||
|
<OptionSwitch
|
||||||
|
disabled={getSfLevel(6) === 0 && getSfLevel(7) === 0 && targetBitNode !== 6 && targetBitNode !== 7}
|
||||||
|
checked={bitNodeBooleanOptions.disableBladeburner}
|
||||||
|
onChange={(value) => {
|
||||||
|
callbacks.setBooleanOption("disableBladeburner", value);
|
||||||
|
}}
|
||||||
|
text="Disable Bladeburner"
|
||||||
|
tooltip="Disable Bladeburner"
|
||||||
|
/>
|
||||||
|
<OptionSwitch
|
||||||
|
checked={bitNodeBooleanOptions.disable4SData}
|
||||||
|
onChange={(value) => {
|
||||||
|
callbacks.setBooleanOption("disable4SData", value);
|
||||||
|
}}
|
||||||
|
text="Disable 4S Market Data"
|
||||||
|
tooltip="Disable 4S Market Data"
|
||||||
|
/>
|
||||||
|
<OptionSwitch
|
||||||
|
disabled={getSfLevel(9) === 0 && targetBitNode !== 9}
|
||||||
|
checked={bitNodeBooleanOptions.disableHacknetServer}
|
||||||
|
onChange={(value) => {
|
||||||
|
callbacks.setBooleanOption("disableHacknetServer", value);
|
||||||
|
}}
|
||||||
|
text="Disable Hacknet Server"
|
||||||
|
tooltip="Hacknet Node is re-enabled in place of Hacknet Server."
|
||||||
|
/>
|
||||||
|
<OptionSwitch
|
||||||
|
disabled={getSfLevel(10) === 0 && targetBitNode !== 10}
|
||||||
|
checked={bitNodeBooleanOptions.disableSleeveExpAndAugmentation}
|
||||||
|
onChange={(value) => {
|
||||||
|
callbacks.setBooleanOption("disableSleeveExpAndAugmentation", value);
|
||||||
|
}}
|
||||||
|
text="Disable Sleeves' experience and augmentation"
|
||||||
|
tooltip="Sleeves cannot gain experience or install augmentations"
|
||||||
|
/>
|
||||||
|
<IntelligenceOverride
|
||||||
|
intelligenceOverride={intelligenceOverride}
|
||||||
|
callbacks={callbacks}
|
||||||
|
></IntelligenceOverride>
|
||||||
|
<br />
|
||||||
|
<SourceFileOverrides
|
||||||
|
currentSourceFiles={currentSourceFiles}
|
||||||
|
sourceFileOverrides={sourceFileOverrides}
|
||||||
|
callbacks={callbacks}
|
||||||
|
getSfLevel={getSfLevel}
|
||||||
|
></SourceFileOverrides>
|
||||||
|
</Box>
|
||||||
|
</Collapse>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -11,6 +11,7 @@ import { StatsRow } from "../../ui/React/StatsRow";
|
|||||||
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
|
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
|
||||||
import { BitNodeMultipliers } from "../BitNodeMultipliers";
|
import { BitNodeMultipliers } from "../BitNodeMultipliers";
|
||||||
import { PartialRecord, getRecordEntries } from "../../Types/Record";
|
import { PartialRecord, getRecordEntries } from "../../Types/Record";
|
||||||
|
import { canAccessBitNodeFeature } from "../BitNodeUtils";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
n: number;
|
n: number;
|
||||||
@ -23,7 +24,7 @@ export function BitnodeMultiplierDescription({ n, level }: IProps): React.ReactE
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box component={Paper} sx={{ mt: 1, p: 1 }}>
|
<Box component={Paper} sx={{ mt: 1, p: 1 }}>
|
||||||
<ListItemButton disableGutters onClick={() => setOpen((old) => !old)}>
|
<ListItemButton disableGutters onClick={() => setOpen((old) => !old)} sx={{ padding: "4px 8px" }}>
|
||||||
<ListItemText primary={<Typography variant="h6">Bitnode Multipliers</Typography>} />
|
<ListItemText primary={<Typography variant="h6">Bitnode Multipliers</Typography>} />
|
||||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
@ -39,8 +40,8 @@ export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElem
|
|||||||
// If not, then we have to assume that we want the next level up from the
|
// If not, then we have to assume that we want the next level up from the
|
||||||
// current node's source file, so we get the min of that, the SF's max level,
|
// current node's source file, so we get the min of that, the SF's max level,
|
||||||
// or if it's BN12, ∞
|
// or if it's BN12, ∞
|
||||||
const maxSfLevel = n === 12 ? Infinity : 3;
|
const maxSfLevel = n === 12 ? Number.MAX_VALUE : 3;
|
||||||
const mults = getBitNodeMultipliers(n, level ?? Math.min(Player.sourceFileLvl(n) + 1, maxSfLevel));
|
const mults = getBitNodeMultipliers(n, level ?? Math.min(Player.activeSourceFileLvl(n) + 1, maxSfLevel));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ columnCount: 2, columnGap: 1, mb: n === 1 ? 0 : -2 }}>
|
<Box sx={{ columnCount: 2, columnGap: 1, mb: n === 1 ? 0 : -2 }}>
|
||||||
@ -314,7 +315,7 @@ function StanekMults({ mults }: IMultsProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function GangMults({ mults }: IMultsProps): React.ReactElement {
|
function GangMults({ mults }: IMultsProps): React.ReactElement {
|
||||||
if (Player.bitNodeN !== 2 && Player.sourceFileLvl(2) <= 0) return <></>;
|
if (!canAccessBitNodeFeature(2)) return <></>;
|
||||||
|
|
||||||
const rows: IBNMultRows = {
|
const rows: IBNMultRows = {
|
||||||
GangSoftcap: {
|
GangSoftcap: {
|
||||||
|
@ -172,7 +172,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
|||||||
if (n !== destroyed) {
|
if (n !== destroyed) {
|
||||||
return lvl;
|
return lvl;
|
||||||
}
|
}
|
||||||
const max = n === 12 ? Infinity : 3;
|
const max = n === 12 ? Number.MAX_VALUE : 3;
|
||||||
|
|
||||||
// If accessed via flume, display the current BN level, else the next
|
// If accessed via flume, display the current BN level, else the next
|
||||||
return Math.min(max, lvl + Number(!props.flume));
|
return Math.min(max, lvl + Number(!props.flume));
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import { Player } from "@player";
|
||||||
|
import { type BitNodeBooleanOptions } from "@nsdefs";
|
||||||
import { enterBitNode } from "../../RedPill";
|
import { enterBitNode } from "../../RedPill";
|
||||||
import { BitNodes } from "../BitNode";
|
import { BitNodes } from "../BitNode";
|
||||||
import { Modal } from "../../ui/React/Modal";
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import { BitnodeMultiplierDescription } from "./BitnodeMultipliersDescription";
|
import { BitnodeMultiplierDescription } from "./BitnodeMultipliersDescription";
|
||||||
|
import { BitNodeAdvancedOptions } from "./BitNodeAdvancedOptions";
|
||||||
|
import { JSONMap } from "../../Types/Jsonable";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -17,14 +21,77 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PortalModal(props: IProps): React.ReactElement {
|
export function PortalModal(props: IProps): React.ReactElement {
|
||||||
|
const [sourceFileOverrides, setSourceFileOverrides] = React.useState<JSONMap<number, number>>(new JSONMap());
|
||||||
|
const [intelligenceOverride, setIntelligenceOverride] = React.useState<number | undefined>();
|
||||||
|
const [bitNodeBooleanOptions, setBitNodeBooleanOptions] = React.useState<BitNodeBooleanOptions>({
|
||||||
|
restrictHomePCUpgrade: false,
|
||||||
|
disableGang: false,
|
||||||
|
disableCorporation: false,
|
||||||
|
disableBladeburner: false,
|
||||||
|
disable4SData: false,
|
||||||
|
disableHacknetServer: false,
|
||||||
|
disableSleeveExpAndAugmentation: false,
|
||||||
|
});
|
||||||
|
|
||||||
const bitNodeKey = "BitNode" + props.n;
|
const bitNodeKey = "BitNode" + props.n;
|
||||||
const bitNode = BitNodes[bitNodeKey];
|
const bitNode = BitNodes[bitNodeKey];
|
||||||
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
|
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
|
||||||
const maxSourceFileLevel = props.n === 12 ? "∞" : "3";
|
const maxSourceFileLevel = props.n === 12 ? "∞" : "3";
|
||||||
|
const newLevel = Math.min(props.level + 1, props.n === 12 ? Number.MAX_VALUE : 3);
|
||||||
|
|
||||||
|
let currentSourceFiles = new Map(Player.sourceFiles);
|
||||||
|
if (!props.flume) {
|
||||||
|
const currentSourceFileLevel = Player.sourceFileLvl(props.destroyedBitNode);
|
||||||
|
if (currentSourceFileLevel < 3 || props.destroyedBitNode === 12) {
|
||||||
|
currentSourceFiles.set(props.destroyedBitNode, currentSourceFileLevel + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentSourceFiles = new Map([...currentSourceFiles].sort((a, b) => a[0] - b[0]));
|
||||||
|
|
||||||
|
const callbacks = {
|
||||||
|
setSfOverrides: (value: JSONMap<number, number>) => {
|
||||||
|
setSourceFileOverrides(value);
|
||||||
|
},
|
||||||
|
setSfActiveLevel: (sfNumber: number, sfLevel: number) => {
|
||||||
|
setSourceFileOverrides((old) => {
|
||||||
|
const newValue = new JSONMap(old);
|
||||||
|
newValue.set(sfNumber, sfLevel);
|
||||||
|
return newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setIntelligenceOverride: (value: number | undefined) => {
|
||||||
|
setIntelligenceOverride(value);
|
||||||
|
},
|
||||||
|
setBooleanOption: (key: keyof BitNodeBooleanOptions, value: boolean) => {
|
||||||
|
if (!(key in bitNodeBooleanOptions)) {
|
||||||
|
throw new Error(`Invalid key of booleanOptions: ${key}`);
|
||||||
|
}
|
||||||
|
setBitNodeBooleanOptions((old) => {
|
||||||
|
return {
|
||||||
|
...old,
|
||||||
|
[key]: value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
setSourceFileOverrides(new JSONMap());
|
||||||
|
setIntelligenceOverride(undefined);
|
||||||
|
setBitNodeBooleanOptions({
|
||||||
|
restrictHomePCUpgrade: false,
|
||||||
|
disableGang: false,
|
||||||
|
disableCorporation: false,
|
||||||
|
disableBladeburner: false,
|
||||||
|
disable4SData: false,
|
||||||
|
disableHacknetServer: false,
|
||||||
|
disableSleeveExpAndAugmentation: false,
|
||||||
|
});
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3);
|
|
||||||
return (
|
return (
|
||||||
<Modal open={props.open} onClose={props.onClose}>
|
<Modal open={props.open} onClose={onClose}>
|
||||||
<Typography variant="h4">
|
<Typography variant="h4">
|
||||||
BitNode-{props.n}: {bitNode.name}
|
BitNode-{props.n}: {bitNode.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -39,12 +106,25 @@ export function PortalModal(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
<Typography component="div">{bitNode.info}</Typography>
|
<Typography component="div">{bitNode.info}</Typography>
|
||||||
<BitnodeMultiplierDescription n={props.n} level={newLevel} />
|
<BitnodeMultiplierDescription n={props.n} level={newLevel} />
|
||||||
|
<BitNodeAdvancedOptions
|
||||||
|
targetBitNode={props.n}
|
||||||
|
currentSourceFiles={currentSourceFiles}
|
||||||
|
sourceFileOverrides={sourceFileOverrides}
|
||||||
|
intelligenceOverride={intelligenceOverride}
|
||||||
|
bitNodeBooleanOptions={bitNodeBooleanOptions}
|
||||||
|
callbacks={callbacks}
|
||||||
|
></BitNodeAdvancedOptions>
|
||||||
<br />
|
<br />
|
||||||
<Button
|
<Button
|
||||||
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
|
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
enterBitNode(props.flume, props.destroyedBitNode, props.n);
|
const bitNodeOptions = {
|
||||||
|
sourceFileOverrides,
|
||||||
|
intelligenceOverride,
|
||||||
|
...bitNodeBooleanOptions,
|
||||||
|
};
|
||||||
|
enterBitNode(props.flume, props.destroyedBitNode, props.n, bitNodeOptions);
|
||||||
props.onClose();
|
props.onClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -23,7 +23,7 @@ export class StaneksGift extends BaseGift {
|
|||||||
}
|
}
|
||||||
|
|
||||||
baseSize(): number {
|
baseSize(): number {
|
||||||
return StanekConstants.BaseSize + currentNodeMults.StaneksGiftExtraSize + Player.sourceFileLvl(13);
|
return StanekConstants.BaseSize + currentNodeMults.StaneksGiftExtraSize + Player.activeSourceFileLvl(13);
|
||||||
}
|
}
|
||||||
|
|
||||||
width(): number {
|
width(): number {
|
||||||
|
@ -32,12 +32,18 @@ export function SourceFilesDev({ parentRerender }: { parentRerender: () => void
|
|||||||
}
|
}
|
||||||
if (sfLvl === 0) {
|
if (sfLvl === 0) {
|
||||||
Player.sourceFiles.delete(sfN);
|
Player.sourceFiles.delete(sfN);
|
||||||
if (sfN === 10) Sleeve.recalculateNumOwned();
|
Player.bitNodeOptions.sourceFileOverrides.delete(sfN);
|
||||||
|
if (sfN === 10) {
|
||||||
|
Sleeve.recalculateNumOwned();
|
||||||
|
}
|
||||||
parentRerender();
|
parentRerender();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Player.sourceFiles.set(sfN, sfLvl);
|
Player.sourceFiles.set(sfN, sfLvl);
|
||||||
if (sfN === 10) Sleeve.recalculateNumOwned();
|
Player.bitNodeOptions.sourceFileOverrides.set(sfN, sfLvl);
|
||||||
|
if (sfN === 10) {
|
||||||
|
Sleeve.recalculateNumOwned();
|
||||||
|
}
|
||||||
parentRerender();
|
parentRerender();
|
||||||
},
|
},
|
||||||
[parentRerender],
|
[parentRerender],
|
||||||
@ -113,6 +119,8 @@ export function SourceFilesDev({ parentRerender }: { parentRerender: () => void
|
|||||||
<Typography>Source-Files</Typography>
|
<Typography>Source-Files</Typography>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
|
<Typography>Note: This tool sets both the owned level and the overridden level.</Typography>
|
||||||
|
<br />
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -29,24 +29,20 @@ export enum Exploit {
|
|||||||
EditSaveFile = "EditSaveFile",
|
EditSaveFile = "EditSaveFile",
|
||||||
}
|
}
|
||||||
|
|
||||||
const names: Record<Exploit, string> = {
|
export const ExploitDescription: Record<Exploit, string> = {
|
||||||
Bypass: "by circumventing the ram cost of document.",
|
[Exploit.Bypass]: "by circumventing the ram cost of document.",
|
||||||
EditSaveFile: "by editing your save file.",
|
[Exploit.EditSaveFile]: "by editing your save file.",
|
||||||
PrototypeTampering: "by tampering with Numbers prototype.",
|
[Exploit.PrototypeTampering]: "by tampering with Numbers prototype.",
|
||||||
TimeCompression: "by compressing time.",
|
[Exploit.TimeCompression]: "by compressing time.",
|
||||||
Unclickable: "by clicking the unclickable.",
|
[Exploit.Unclickable]: "by clicking the unclickable.",
|
||||||
UndocumentedFunctionCall: "by looking beyond the documentation.",
|
[Exploit.UndocumentedFunctionCall]: "by looking beyond the documentation.",
|
||||||
RealityAlteration: "by altering reality to suit your whims.",
|
[Exploit.RealityAlteration]: "by altering reality to suit your whims.",
|
||||||
N00dles: "by harnessing the power of the n00dles.",
|
[Exploit.N00dles]: "by harnessing the power of the n00dles.",
|
||||||
YoureNotMeantToAccessThis: "by accessing the dev menu.",
|
[Exploit.YoureNotMeantToAccessThis]: "by accessing the dev menu.",
|
||||||
TrueRecursion: "by truly recursing.",
|
[Exploit.TrueRecursion]: "by truly recursing.",
|
||||||
INeedARainbow: "by using the power of the rainbow.",
|
[Exploit.INeedARainbow]: "by using the power of the rainbow.",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ExploitName(exploit: Exploit): string {
|
|
||||||
return names[exploit];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needed in case player edits save file poorly
|
// Needed in case player edits save file poorly
|
||||||
export function sanitizeExploits(exploits: Exploit[]): Exploit[] {
|
export function sanitizeExploits(exploits: Exploit[]): Exploit[] {
|
||||||
exploits = exploits.filter((e: Exploit) => Object.values(Exploit).includes(e));
|
exploits = exploits.filter((e: Exploit) => Object.values(Exploit).includes(e));
|
||||||
|
@ -149,7 +149,7 @@ export const getFactionAugmentationsFiltered = (faction: Faction): AugmentationN
|
|||||||
augs.push(Augmentations[AugmentationName.TheRedPill]);
|
augs.push(Augmentations[AugmentationName.TheRedPill]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rng = SFC32RNG(`BN${Player.bitNodeN}.${Player.sourceFileLvl(Player.bitNodeN)}`);
|
const rng = SFC32RNG(`BN${Player.bitNodeN}.${Player.activeSourceFileLvl(Player.bitNodeN)}`);
|
||||||
// Remove faction-unique augs that don't belong to this faction
|
// Remove faction-unique augs that don't belong to this faction
|
||||||
const uniqueFilter = (a: Augmentation): boolean => {
|
const uniqueFilter = (a: Augmentation): boolean => {
|
||||||
// Keep all the non-unique one
|
// Keep all the non-unique one
|
||||||
|
@ -302,7 +302,7 @@ export const haveSourceFile = (n: number): PlayerCondition => ({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
isSatisfied(p: PlayerObject): boolean {
|
isSatisfied(p: PlayerObject): boolean {
|
||||||
return p.bitNodeN == n || p.sourceFileLvl(n) > 0;
|
return p.bitNodeN == n || p.activeSourceFileLvl(n) > 0;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ export const AutoexecInput = (props: IProps): React.ReactElement => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Stolen from Prestige.ts
|
// Stolen from Prestige.ts
|
||||||
const minRam = Player.sourceFileLvl(9) >= 2 ? 128 : Player.sourceFileLvl(1) > 0 ? 32 : 8;
|
const minRam = Player.activeSourceFileLvl(9) >= 2 ? 128 : Player.activeSourceFileLvl(1) > 0 ? 32 : 8;
|
||||||
if (ramUsage <= minRam) {
|
if (ramUsage <= minRam) {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -829,5 +829,5 @@ function waitCycle(useOfflineCycles = true): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function showWorldDemon() {
|
export function showWorldDemon() {
|
||||||
return Player.hasAugmentation(AugmentationName.TheRedPill, true) && Player.sourceFileLvl(1);
|
return Player.hasAugmentation(AugmentationName.TheRedPill, true) && Player.activeSourceFileLvl(1);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ import { getRecordEntries, getRecordValues } from "../../Types/Record";
|
|||||||
*/
|
*/
|
||||||
export function CalculateEffect(nodes: number, faction: GoOpponent): number {
|
export function CalculateEffect(nodes: number, faction: GoOpponent): number {
|
||||||
const power = getEffectPowerForFaction(faction);
|
const power = getEffectPowerForFaction(faction);
|
||||||
const sourceFileBonus = Player.sourceFileLvl(14) ? 2 : 1;
|
const sourceFileBonus = Player.activeSourceFileLvl(14) ? 2 : 1;
|
||||||
return (
|
return (
|
||||||
1 + Math.log(nodes + 1) * Math.pow(nodes + 1, 0.3) * 0.002 * power * currentNodeMults.GoPower * sourceFileBonus
|
1 + Math.log(nodes + 1) * Math.pow(nodes + 1, 0.3) * 0.002 * power * currentNodeMults.GoPower * sourceFileBonus
|
||||||
);
|
);
|
||||||
@ -26,7 +26,7 @@ export function CalculateEffect(nodes: number, faction: GoOpponent): number {
|
|||||||
* for factions you are a member of
|
* for factions you are a member of
|
||||||
*/
|
*/
|
||||||
export function getMaxFavor() {
|
export function getMaxFavor() {
|
||||||
const sourceFileLevel = Player.sourceFileLvl(14);
|
const sourceFileLevel = Player.activeSourceFileLvl(14);
|
||||||
|
|
||||||
if (sourceFileLevel === 1) {
|
if (sourceFileLevel === 1) {
|
||||||
return 80;
|
return 80;
|
||||||
|
@ -323,8 +323,8 @@ export function getStats() {
|
|||||||
|
|
||||||
/** Validate singularity access by throwing an error if the player does not have access. */
|
/** Validate singularity access by throwing an error if the player does not have access. */
|
||||||
export function checkCheatApiAccess(error: (s: string) => void): void {
|
export function checkCheatApiAccess(error: (s: string) => void): void {
|
||||||
const hasSourceFile = Player.sourceFileLvl(14) > 1;
|
const hasSourceFile = Player.activeSourceFileLvl(14) > 1;
|
||||||
const isBitnodeFourteenTwo = Player.sourceFileLvl(14) === 1 && Player.bitNodeN === 14;
|
const isBitnodeFourteenTwo = Player.activeSourceFileLvl(14) === 1 && Player.bitNodeN === 14;
|
||||||
if (!hasSourceFile && !isBitnodeFourteenTwo) {
|
if (!hasSourceFile && !isBitnodeFourteenTwo) {
|
||||||
error(
|
error(
|
||||||
`The go.cheat API requires Source-File 14.2 to run, a power up you obtain later in the game.
|
`The go.cheat API requires Source-File 14.2 to run, a power up you obtain later in the game.
|
||||||
@ -387,7 +387,7 @@ export async function determineCheatSuccess(
|
|||||||
* 15: +31,358,645%
|
* 15: +31,358,645%
|
||||||
*/
|
*/
|
||||||
export function cheatSuccessChance(cheatCount: number) {
|
export function cheatSuccessChance(cheatCount: number) {
|
||||||
const sourceFileBonus = Player.sourceFileLvl(14) === 3 ? 0.25 : 0;
|
const sourceFileBonus = Player.activeSourceFileLvl(14) === 3 ? 0.25 : 0;
|
||||||
const cheatCountScalar = (0.7 - 0.02 * cheatCount) ** cheatCount;
|
const cheatCountScalar = (0.7 - 0.02 * cheatCount) ** cheatCount;
|
||||||
return Math.max(Math.min(0.6 * cheatCountScalar * Player.mults.crime_success + sourceFileBonus, 1), 0);
|
return Math.max(Math.min(0.6 * cheatCountScalar * Player.mults.crime_success + sourceFileBonus, 1), 0);
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,12 @@ import { GetServer } from "../Server/AllServers";
|
|||||||
import { Server } from "../Server/Server";
|
import { Server } from "../Server/Server";
|
||||||
import { Companies } from "../Company/Companies";
|
import { Companies } from "../Company/Companies";
|
||||||
import { isMember } from "../utils/EnumHelper";
|
import { isMember } from "../utils/EnumHelper";
|
||||||
|
import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
// Returns a boolean indicating whether the player has Hacknet Servers
|
// Returns a boolean indicating whether the player has Hacknet Servers
|
||||||
// (the upgraded form of Hacknet Nodes)
|
// (the upgraded form of Hacknet Nodes)
|
||||||
export function hasHacknetServers(): boolean {
|
export function hasHacknetServers(): boolean {
|
||||||
return Player.bitNodeN === 9 || Player.sourceFileLvl(9) > 0;
|
return canAccessBitNodeFeature(9) && !Player.bitNodeOptions.disableHacknetServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function purchaseHacknet(): number {
|
export function purchaseHacknet(): number {
|
||||||
|
@ -14,7 +14,7 @@ interface IProps {
|
|||||||
|
|
||||||
export function CoresButton(props: IProps): React.ReactElement {
|
export function CoresButton(props: IProps): React.ReactElement {
|
||||||
const homeComputer = Player.getHomeComputer();
|
const homeComputer = Player.getHomeComputer();
|
||||||
const maxCores = homeComputer.cpuCores >= 8;
|
const maxCores = Player.bitNodeOptions.restrictHomePCUpgrade || homeComputer.cpuCores >= 8;
|
||||||
if (maxCores) {
|
if (maxCores) {
|
||||||
return <Button>Upgrade 'home' cores - MAX</Button>;
|
return <Button>Upgrade 'home' cores - MAX</Button>;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,10 @@ interface IProps {
|
|||||||
|
|
||||||
export function RamButton(props: IProps): React.ReactElement {
|
export function RamButton(props: IProps): React.ReactElement {
|
||||||
const homeComputer = Player.getHomeComputer();
|
const homeComputer = Player.getHomeComputer();
|
||||||
if (homeComputer.maxRam >= ServerConstants.HomeComputerMaxRam) {
|
if (
|
||||||
|
(Player.bitNodeOptions.restrictHomePCUpgrade && homeComputer.maxRam >= 128) ||
|
||||||
|
homeComputer.maxRam >= ServerConstants.HomeComputerMaxRam
|
||||||
|
) {
|
||||||
return <Button>Upgrade 'home' RAM - MAX</Button>;
|
return <Button>Upgrade 'home' RAM - MAX</Button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import { HacknetServer } from "../../Hacknet/HacknetServer";
|
|||||||
import { GetServer } from "../../Server/AllServers";
|
import { GetServer } from "../../Server/AllServers";
|
||||||
import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
|
import { ArcadeRoot } from "../../Arcade/ui/ArcadeRoot";
|
||||||
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
|
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
|
||||||
|
import { canAccessBitNodeFeature, knowAboutBitverse } from "../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
interface SpecialLocationProps {
|
interface SpecialLocationProps {
|
||||||
loc: Location;
|
loc: Location;
|
||||||
@ -91,8 +92,10 @@ export function SpecialLocation(props: SpecialLocationProps): React.ReactElement
|
|||||||
function EatNoodles(): void {
|
function EatNoodles(): void {
|
||||||
SnackbarEvents.emit("You ate some delicious noodles and feel refreshed", ToastVariant.SUCCESS, 2000);
|
SnackbarEvents.emit("You ate some delicious noodles and feel refreshed", ToastVariant.SUCCESS, 2000);
|
||||||
N00dles(); // This is the true power of the noodles.
|
N00dles(); // This is the true power of the noodles.
|
||||||
if (Player.sourceFiles.size > 0) Player.giveExploit(Exploit.N00dles);
|
if (knowAboutBitverse()) {
|
||||||
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
|
Player.giveExploit(Exploit.N00dles);
|
||||||
|
}
|
||||||
|
if (canAccessBitNodeFeature(5)) {
|
||||||
Player.exp.intelligence *= 1.0000000000000002;
|
Player.exp.intelligence *= 1.0000000000000002;
|
||||||
}
|
}
|
||||||
Player.exp.hacking *= 1.0000000000000002;
|
Player.exp.hacking *= 1.0000000000000002;
|
||||||
|
@ -8,6 +8,7 @@ import { SpecialServers } from "../Server/data/SpecialServers";
|
|||||||
import { Settings } from "../Settings/Settings";
|
import { Settings } from "../Settings/Settings";
|
||||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { Server } from "../Server/Server";
|
import { Server } from "../Server/Server";
|
||||||
|
import { knowAboutBitverse } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
//Sends message to player, including a pop up
|
//Sends message to player, including a pop up
|
||||||
function sendMessage(name: MessageFilename, forced = false): void {
|
function sendMessage(name: MessageFilename, forced = false): void {
|
||||||
@ -63,9 +64,9 @@ function checkForMessagesToSend(): void {
|
|||||||
//If the daemon can be hacked, send the player icarus.msg
|
//If the daemon can be hacked, send the player icarus.msg
|
||||||
if (
|
if (
|
||||||
Player.skills.hacking >= worldDaemon.requiredHackingSkill &&
|
Player.skills.hacking >= worldDaemon.requiredHackingSkill &&
|
||||||
(Player.sourceFiles.size === 0 || !recvd(MessageFilename.RedPill))
|
(!knowAboutBitverse() || !recvd(MessageFilename.RedPill))
|
||||||
) {
|
) {
|
||||||
sendMessage(MessageFilename.RedPill, Player.sourceFiles.size === 0);
|
sendMessage(MessageFilename.RedPill, !knowAboutBitverse());
|
||||||
}
|
}
|
||||||
//If the daemon cannot be hacked, send the player truthgazer.msg a single time.
|
//If the daemon cannot be hacked, send the player truthgazer.msg a single time.
|
||||||
else if (!recvd(MessageFilename.TruthGazer)) {
|
else if (!recvd(MessageFilename.TruthGazer)) {
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import type { NetscriptContext } from "./APIWrapper";
|
import type { NetscriptContext } from "./APIWrapper";
|
||||||
import type { RunningScript as IRunningScript, Person as IPerson, Server as IServer, ScriptArg } from "@nsdefs";
|
import type {
|
||||||
|
RunningScript as IRunningScript,
|
||||||
|
Person as IPerson,
|
||||||
|
Server as IServer,
|
||||||
|
ScriptArg,
|
||||||
|
BitNodeOptions,
|
||||||
|
} from "@nsdefs";
|
||||||
import type { WorkerScript } from "./WorkerScript";
|
import type { WorkerScript } from "./WorkerScript";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@ -49,6 +55,12 @@ import { CustomBoundary } from "../ui/Components/CustomBoundary";
|
|||||||
import { ServerConstants } from "../Server/data/Constants";
|
import { ServerConstants } from "../Server/data/Constants";
|
||||||
import { basicErrorMessage, errorMessage, log } from "./ErrorMessages";
|
import { basicErrorMessage, errorMessage, log } from "./ErrorMessages";
|
||||||
import { assertString, debugType } from "./TypeAssertion";
|
import { assertString, debugType } from "./TypeAssertion";
|
||||||
|
import {
|
||||||
|
canAccessBitNodeFeature,
|
||||||
|
getDefaultBitNodeOptions,
|
||||||
|
validateSourceFileOverrides,
|
||||||
|
} from "../BitNode/BitNodeUtils";
|
||||||
|
import { JSONMap } from "../Types/Jsonable";
|
||||||
|
|
||||||
export const helpers = {
|
export const helpers = {
|
||||||
string,
|
string,
|
||||||
@ -83,6 +95,7 @@ export const helpers = {
|
|||||||
getCannotFindRunningScriptErrorMessage,
|
getCannotFindRunningScriptErrorMessage,
|
||||||
createPublicRunningScript,
|
createPublicRunningScript,
|
||||||
failOnHacknetServer,
|
failOnHacknetServer,
|
||||||
|
validateBitNodeOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** RunOptions with non-optional, type-validated members, for passing between internal functions. */
|
/** RunOptions with non-optional, type-validated members, for passing between internal functions. */
|
||||||
@ -279,7 +292,7 @@ function validateHGWOptions(ctx: NetscriptContext, opts: unknown): CompleteHGWOp
|
|||||||
|
|
||||||
/** Validate singularity access by throwing an error if the player does not have access. */
|
/** Validate singularity access by throwing an error if the player does not have access. */
|
||||||
function checkSingularityAccess(ctx: NetscriptContext): void {
|
function checkSingularityAccess(ctx: NetscriptContext): void {
|
||||||
if (Player.bitNodeN !== 4 && Player.sourceFileLvl(4) === 0) {
|
if (!canAccessBitNodeFeature(4)) {
|
||||||
throw errorMessage(
|
throw errorMessage(
|
||||||
ctx,
|
ctx,
|
||||||
`This singularity function requires Source-File 4 to run. A power up you obtain later in the game.
|
`This singularity function requires Source-File 4 to run. A power up you obtain later in the game.
|
||||||
@ -732,3 +745,40 @@ let customElementKey = 0;
|
|||||||
export function wrapUserNode(value: unknown) {
|
export function wrapUserNode(value: unknown) {
|
||||||
return <CustomBoundary key={`PlayerContent${customElementKey++}`}>{value}</CustomBoundary>;
|
return <CustomBoundary key={`PlayerContent${customElementKey++}`}>{value}</CustomBoundary>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateBitNodeOptions(ctx: NetscriptContext, bitNodeOptions: unknown): BitNodeOptions {
|
||||||
|
const result = getDefaultBitNodeOptions();
|
||||||
|
if (bitNodeOptions == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (typeof bitNodeOptions !== "object") {
|
||||||
|
throw errorMessage(ctx, `bitNodeOptions must be an object if it's specified. It was ${bitNodeOptions}.`);
|
||||||
|
}
|
||||||
|
const options = bitNodeOptions as Unknownify<BitNodeOptions>;
|
||||||
|
if (!(options.sourceFileOverrides instanceof Map)) {
|
||||||
|
throw errorMessage(ctx, `sourceFileOverrides must be a Map.`);
|
||||||
|
}
|
||||||
|
const validationResultForSourceFileOverrides = validateSourceFileOverrides(options.sourceFileOverrides, true);
|
||||||
|
if (!validationResultForSourceFileOverrides.valid) {
|
||||||
|
throw errorMessage(
|
||||||
|
ctx,
|
||||||
|
`sourceFileOverrides is invalid. Reason: ${validationResultForSourceFileOverrides.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.sourceFileOverrides = new JSONMap(options.sourceFileOverrides);
|
||||||
|
if (options.intelligenceOverride !== undefined) {
|
||||||
|
result.intelligenceOverride = number(ctx, "intelligenceOverride", options.intelligenceOverride);
|
||||||
|
} else {
|
||||||
|
result.intelligenceOverride = undefined;
|
||||||
|
}
|
||||||
|
result.restrictHomePCUpgrade = !!options.restrictHomePCUpgrade;
|
||||||
|
result.disableGang = !!options.disableGang;
|
||||||
|
result.disableCorporation = !!options.disableCorporation;
|
||||||
|
result.disableBladeburner = !!options.disableBladeburner;
|
||||||
|
result.disable4SData = !!options.disable4SData;
|
||||||
|
result.disableHacknetServer = !!options.disableHacknetServer;
|
||||||
|
result.disableSleeveExpAndAugmentation = !!options.disableSleeveExpAndAugmentation;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -86,10 +86,16 @@ export const RamCostConstants = {
|
|||||||
|
|
||||||
function SF4Cost(cost: number): () => number {
|
function SF4Cost(cost: number): () => number {
|
||||||
return () => {
|
return () => {
|
||||||
if (Player.bitNodeN === 4) return cost;
|
if (Player.bitNodeN === 4) {
|
||||||
const sf4 = Player.sourceFileLvl(4);
|
return cost;
|
||||||
if (sf4 <= 1) return cost * 16;
|
}
|
||||||
if (sf4 === 2) return cost * 4;
|
const sf4 = Player.activeSourceFileLvl(4);
|
||||||
|
if (sf4 <= 1) {
|
||||||
|
return cost * 16;
|
||||||
|
}
|
||||||
|
if (sf4 === 2) {
|
||||||
|
return cost * 4;
|
||||||
|
}
|
||||||
return cost;
|
return cost;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ import { ServerConstants } from "./Server/data/Constants";
|
|||||||
import { assertFunction } from "./Netscript/TypeAssertion";
|
import { assertFunction } from "./Netscript/TypeAssertion";
|
||||||
import { Router } from "./ui/GameRoot";
|
import { Router } from "./ui/GameRoot";
|
||||||
import { Page } from "./ui/Router";
|
import { Page } from "./ui/Router";
|
||||||
|
import { canAccessBitNodeFeature, validBitNodes } from "./BitNode/BitNodeUtils";
|
||||||
|
|
||||||
export const enums: NSEnums = {
|
export const enums: NSEnums = {
|
||||||
CityName,
|
CityName,
|
||||||
@ -972,13 +973,19 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
},
|
},
|
||||||
getBitNodeMultipliers:
|
getBitNodeMultipliers:
|
||||||
(ctx) =>
|
(ctx) =>
|
||||||
(_n = Player.bitNodeN, _lvl = Player.sourceFileLvl(Player.bitNodeN) + 1) => {
|
(_n = Player.bitNodeN, _lvl = Player.activeSourceFileLvl(Player.bitNodeN) + 1) => {
|
||||||
if (Player.sourceFileLvl(5) <= 0 && Player.bitNodeN !== 5)
|
if (!canAccessBitNodeFeature(5)) {
|
||||||
throw helpers.errorMessage(ctx, "Requires Source-File 5 to run.");
|
throw helpers.errorMessage(ctx, "Requires Source-File 5 to run.");
|
||||||
|
}
|
||||||
|
// TODO v3.0: check n and lvl with helpers.number() and Number.isInteger().
|
||||||
const n = Math.round(helpers.number(ctx, "n", _n));
|
const n = Math.round(helpers.number(ctx, "n", _n));
|
||||||
const lvl = Math.round(helpers.number(ctx, "lvl", _lvl));
|
const lvl = Math.round(helpers.number(ctx, "lvl", _lvl));
|
||||||
if (n < 1 || n > 14) throw new Error("n must be between 1 and 14");
|
if (!validBitNodes.includes(n)) {
|
||||||
if (lvl < 1) throw new Error("lvl must be >= 1");
|
throw new Error(`Invalid BitNode: ${n}.`);
|
||||||
|
}
|
||||||
|
if (lvl < 1) {
|
||||||
|
throw new Error("SF level must be greater than or equal to 1.");
|
||||||
|
}
|
||||||
|
|
||||||
return Object.assign({}, getBitNodeMultipliers(n, lvl));
|
return Object.assign({}, getBitNodeMultipliers(n, lvl));
|
||||||
},
|
},
|
||||||
@ -1800,6 +1807,10 @@ export const ns: InternalAPI<NSFull> = {
|
|||||||
currentNode: Player.bitNodeN,
|
currentNode: Player.bitNodeN,
|
||||||
ownedAugs: new Map(Player.augmentations.map((aug) => [aug.name, aug.level])),
|
ownedAugs: new Map(Player.augmentations.map((aug) => [aug.name, aug.level])),
|
||||||
ownedSF: new Map(Player.sourceFiles),
|
ownedSF: new Map(Player.sourceFiles),
|
||||||
|
bitNodeOptions: {
|
||||||
|
...Player.bitNodeOptions,
|
||||||
|
sourceFileOverrides: new Map(Player.bitNodeOptions.sourceFileOverrides),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
getFunctionRamCost: (ctx) => (_name) => {
|
getFunctionRamCost: (ctx) => (_name) => {
|
||||||
const name = helpers.string(ctx, "name", _name);
|
const name = helpers.string(ctx, "name", _name);
|
||||||
|
@ -12,6 +12,7 @@ import { Skills } from "../Bladeburner/data/Skills";
|
|||||||
import { assertString } from "../Netscript/TypeAssertion";
|
import { assertString } from "../Netscript/TypeAssertion";
|
||||||
import { BlackOperations, blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
import { BlackOperations, blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||||
import { checkSleeveAPIAccess, checkSleeveNumber } from "../NetscriptFunctions/Sleeve";
|
import { checkSleeveAPIAccess, checkSleeveNumber } from "../NetscriptFunctions/Sleeve";
|
||||||
|
import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
|
export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
|
||||||
const checkBladeburnerAccess = function (ctx: NetscriptContext): void {
|
const checkBladeburnerAccess = function (ctx: NetscriptContext): void {
|
||||||
@ -19,7 +20,7 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const getBladeburner = function (ctx: NetscriptContext): Bladeburner {
|
const getBladeburner = function (ctx: NetscriptContext): Bladeburner {
|
||||||
const apiAccess = Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0;
|
const apiAccess = canAccessBitNodeFeature(7);
|
||||||
if (!apiAccess) {
|
if (!apiAccess) {
|
||||||
throw helpers.errorMessage(ctx, "You have not unlocked the Bladeburner API.", "API ACCESS");
|
throw helpers.errorMessage(ctx, "You have not unlocked the Bladeburner API.", "API ACCESS");
|
||||||
}
|
}
|
||||||
@ -298,28 +299,28 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
|
|||||||
return !!attempt.success;
|
return !!attempt.success;
|
||||||
},
|
},
|
||||||
joinBladeburnerDivision: (ctx) => () => {
|
joinBladeburnerDivision: (ctx) => () => {
|
||||||
if (Player.bitNodeN === 7 || Player.sourceFileLvl(7) > 0) {
|
if (!canAccessBitNodeFeature(7) || Player.bitNodeOptions.disableBladeburner) {
|
||||||
if (currentNodeMults.BladeburnerRank === 0) {
|
return false;
|
||||||
return false; // Disabled in this bitnode
|
|
||||||
}
|
|
||||||
if (Player.bladeburner) {
|
|
||||||
return true; // Already member
|
|
||||||
} else if (
|
|
||||||
Player.skills.strength >= 100 &&
|
|
||||||
Player.skills.defense >= 100 &&
|
|
||||||
Player.skills.dexterity >= 100 &&
|
|
||||||
Player.skills.agility >= 100
|
|
||||||
) {
|
|
||||||
Player.startBladeburner();
|
|
||||||
helpers.log(ctx, () => "You have been accepted into the Bladeburner division");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
helpers.log(ctx, () => "You do not meet the requirements for joining the Bladeburner division");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
if (currentNodeMults.BladeburnerRank === 0) {
|
||||||
|
return false; // Disabled in this bitnode
|
||||||
|
}
|
||||||
|
if (Player.bladeburner) {
|
||||||
|
return true; // Already member
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Player.skills.strength < 100 ||
|
||||||
|
Player.skills.defense < 100 ||
|
||||||
|
Player.skills.dexterity < 100 ||
|
||||||
|
Player.skills.agility < 100
|
||||||
|
) {
|
||||||
|
helpers.log(ctx, () => "You do not meet the requirements for joining the Bladeburner division");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Player.startBladeburner();
|
||||||
|
helpers.log(ctx, () => "You have been accepted into the Bladeburner division");
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
getBonusTime: (ctx) => () => {
|
getBonusTime: (ctx) => () => {
|
||||||
const bladeburner = getBladeburner(ctx);
|
const bladeburner = getBladeburner(ctx);
|
||||||
|
@ -59,6 +59,7 @@ import { ServerConstants } from "../Server/data/Constants";
|
|||||||
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||||
import { calculateEffectiveRequiredReputation } from "../Company/utils";
|
import { calculateEffectiveRequiredReputation } from "../Company/utils";
|
||||||
import { calculateFavorAfterResetting } from "../Faction/formulas/favor";
|
import { calculateFavorAfterResetting } from "../Faction/formulas/favor";
|
||||||
|
import { validBitNodes } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
||||||
const runAfterReset = function (cbScript: ScriptFilePath) {
|
const runAfterReset = function (cbScript: ScriptFilePath) {
|
||||||
@ -628,7 +629,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
|||||||
|
|
||||||
// Check if we're at max cores
|
// Check if we're at max cores
|
||||||
const homeComputer = Player.getHomeComputer();
|
const homeComputer = Player.getHomeComputer();
|
||||||
if (homeComputer.cpuCores >= 8) {
|
if (Player.bitNodeOptions.restrictHomePCUpgrade || homeComputer.cpuCores >= 8) {
|
||||||
helpers.log(ctx, () => `Your home computer is at max cores.`);
|
helpers.log(ctx, () => `Your home computer is at max cores.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -659,7 +660,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
|||||||
|
|
||||||
// Check if we're at max RAM
|
// Check if we're at max RAM
|
||||||
const homeComputer = Player.getHomeComputer();
|
const homeComputer = Player.getHomeComputer();
|
||||||
if (homeComputer.maxRam >= ServerConstants.HomeComputerMaxRam) {
|
if (
|
||||||
|
(Player.bitNodeOptions.restrictHomePCUpgrade && homeComputer.maxRam >= 128) ||
|
||||||
|
homeComputer.maxRam >= ServerConstants.HomeComputerMaxRam
|
||||||
|
) {
|
||||||
helpers.log(ctx, () => `Your home computer is at max RAM.`);
|
helpers.log(ctx, () => `Your home computer is at max RAM.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1137,38 +1141,47 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
|||||||
}
|
}
|
||||||
return item.price;
|
return item.price;
|
||||||
},
|
},
|
||||||
b1tflum3: (ctx) => (_nextBN, _cbScript) => {
|
b1tflum3: (ctx) => (_nextBN, _cbScript, _bitNodeOptions) => {
|
||||||
helpers.checkSingularityAccess(ctx);
|
helpers.checkSingularityAccess(ctx);
|
||||||
const nextBN = helpers.number(ctx, "nextBN", _nextBN);
|
const nextBN = helpers.number(ctx, "nextBN", _nextBN);
|
||||||
const cbScript = _cbScript
|
const cbScript = _cbScript
|
||||||
? resolveScriptFilePath(helpers.string(ctx, "cbScript", _cbScript), ctx.workerScript.name)
|
? resolveScriptFilePath(helpers.string(ctx, "cbScript", _cbScript), ctx.workerScript.name)
|
||||||
: false;
|
: false;
|
||||||
if (cbScript === null) throw helpers.errorMessage(ctx, `Could not resolve file path: ${_cbScript}`);
|
if (cbScript === null) {
|
||||||
enterBitNode(true, Player.bitNodeN, nextBN);
|
throw helpers.errorMessage(ctx, `Could not resolve file path: ${_cbScript}`);
|
||||||
if (cbScript) setTimeout(() => runAfterReset(cbScript), 500);
|
}
|
||||||
|
enterBitNode(true, Player.bitNodeN, nextBN, helpers.validateBitNodeOptions(ctx, _bitNodeOptions));
|
||||||
|
if (cbScript) {
|
||||||
|
setTimeout(() => runAfterReset(cbScript), 500);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
destroyW0r1dD43m0n: (ctx) => (_nextBN, _cbScript) => {
|
destroyW0r1dD43m0n: (ctx) => (_nextBN, _cbScript, _bitNodeOptions) => {
|
||||||
helpers.checkSingularityAccess(ctx);
|
helpers.checkSingularityAccess(ctx);
|
||||||
const nextBN = helpers.number(ctx, "nextBN", _nextBN);
|
const nextBN = helpers.number(ctx, "nextBN", _nextBN);
|
||||||
if (nextBN > 14 || nextBN < 1 || !Number.isInteger(nextBN)) {
|
if (!validBitNodes.includes(nextBN)) {
|
||||||
throw new Error(`Invalid bitnode specified: ${_nextBN}`);
|
throw new Error(`Invalid BitNode: ${_nextBN}.`);
|
||||||
}
|
}
|
||||||
const cbScript = _cbScript
|
const cbScript = _cbScript
|
||||||
? resolveScriptFilePath(helpers.string(ctx, "cbScript", _cbScript), ctx.workerScript.name)
|
? resolveScriptFilePath(helpers.string(ctx, "cbScript", _cbScript), ctx.workerScript.name)
|
||||||
: false;
|
: false;
|
||||||
if (cbScript === null) throw helpers.errorMessage(ctx, `Could not resolve file path: ${_cbScript}`);
|
if (cbScript === null) {
|
||||||
|
throw helpers.errorMessage(ctx, `Could not resolve file path: ${_cbScript}`);
|
||||||
|
}
|
||||||
|
|
||||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||||
if (!(wd instanceof Server)) {
|
if (!(wd instanceof Server)) {
|
||||||
throw new Error("WorldDaemon is not a normal server. This is a bug. Please contact developers.");
|
throw new Error("WorldDaemon is not a normal server. This is a bug. Please contact developers.");
|
||||||
}
|
}
|
||||||
const hackingRequirements = () => {
|
const hackingRequirements = () => {
|
||||||
if (Player.skills.hacking < wd.requiredHackingSkill) return false;
|
if (Player.skills.hacking < wd.requiredHackingSkill || !wd.hasAdminRights) {
|
||||||
if (!wd.hasAdminRights) return false;
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
const bladeburnerRequirements = () => {
|
const bladeburnerRequirements = () => {
|
||||||
if (!Player.bladeburner) return false;
|
if (!Player.bladeburner) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return Player.bladeburner.numBlackOpsComplete >= blackOpsArray.length;
|
return Player.bladeburner.numBlackOpsComplete >= blackOpsArray.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1179,8 +1192,10 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
|
|||||||
|
|
||||||
wd.backdoorInstalled = true;
|
wd.backdoorInstalled = true;
|
||||||
calculateAchievements();
|
calculateAchievements();
|
||||||
enterBitNode(false, Player.bitNodeN, nextBN);
|
enterBitNode(false, Player.bitNodeN, nextBN, helpers.validateBitNodeOptions(ctx, _bitNodeOptions));
|
||||||
if (cbScript) setTimeout(() => runAfterReset(cbScript), 500);
|
if (cbScript) {
|
||||||
|
setTimeout(() => runAfterReset(cbScript), 500);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getCurrentWork: (ctx) => () => {
|
getCurrentWork: (ctx) => () => {
|
||||||
helpers.checkSingularityAccess(ctx);
|
helpers.checkSingularityAccess(ctx);
|
||||||
|
@ -15,6 +15,7 @@ import { helpers } from "../Netscript/NetscriptHelpers";
|
|||||||
import { getAugCost } from "../Augmentation/AugmentationHelpers";
|
import { getAugCost } from "../Augmentation/AugmentationHelpers";
|
||||||
import { Factions } from "../Faction/Factions";
|
import { Factions } from "../Faction/Factions";
|
||||||
import { SleeveWorkType } from "../PersonObjects/Sleeve/Work/Work";
|
import { SleeveWorkType } from "../PersonObjects/Sleeve/Work/Work";
|
||||||
|
import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
export const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
|
export const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
|
||||||
if (Player.bitNodeN !== 10 && !Player.sourceFileLvl(10)) {
|
if (Player.bitNodeN !== 10 && !Player.sourceFileLvl(10)) {
|
||||||
@ -33,6 +34,23 @@ export const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber:
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
|
export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
|
||||||
|
const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
|
||||||
|
if (!canAccessBitNodeFeature(10)) {
|
||||||
|
throw helpers.errorMessage(
|
||||||
|
ctx,
|
||||||
|
"You do not have access to the Sleeve API. This is either because you are not in BitNode-10 or because you do not have Source-File 10.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkSleeveNumber = function (ctx: NetscriptContext, sleeveNumber: number) {
|
||||||
|
if (sleeveNumber >= Player.sleeves.length || sleeveNumber < 0) {
|
||||||
|
const msg = `Invalid sleeve number: ${sleeveNumber}`;
|
||||||
|
helpers.log(ctx, () => msg);
|
||||||
|
throw helpers.errorMessage(ctx, msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const sleeveFunctions: InternalAPI<NetscriptSleeve> = {
|
const sleeveFunctions: InternalAPI<NetscriptSleeve> = {
|
||||||
getNumSleeves: (ctx) => () => {
|
getNumSleeves: (ctx) => () => {
|
||||||
checkSleeveAPIAccess(ctx);
|
checkSleeveAPIAccess(ctx);
|
||||||
|
@ -167,10 +167,8 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
|
|||||||
const symbol = helpers.string(ctx, "symbol", _symbol);
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
||||||
const shares = helpers.number(ctx, "shares", _shares);
|
const shares = helpers.number(ctx, "shares", _shares);
|
||||||
checkTixApiAccess(ctx);
|
checkTixApiAccess(ctx);
|
||||||
if (Player.bitNodeN !== 8) {
|
if (Player.bitNodeN !== 8 && Player.activeSourceFileLvl(8) <= 1) {
|
||||||
if (Player.sourceFileLvl(8) <= 1) {
|
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
|
||||||
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const stock = getStockFromSymbol(ctx, symbol);
|
const stock = getStockFromSymbol(ctx, symbol);
|
||||||
const res = shortStock(stock, shares, ctx, {});
|
const res = shortStock(stock, shares, ctx, {});
|
||||||
@ -181,10 +179,8 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
|
|||||||
const symbol = helpers.string(ctx, "symbol", _symbol);
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
||||||
const shares = helpers.number(ctx, "shares", _shares);
|
const shares = helpers.number(ctx, "shares", _shares);
|
||||||
checkTixApiAccess(ctx);
|
checkTixApiAccess(ctx);
|
||||||
if (Player.bitNodeN !== 8) {
|
if (Player.bitNodeN !== 8 && Player.activeSourceFileLvl(8) <= 1) {
|
||||||
if (Player.sourceFileLvl(8) <= 1) {
|
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
|
||||||
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 2.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const stock = getStockFromSymbol(ctx, symbol);
|
const stock = getStockFromSymbol(ctx, symbol);
|
||||||
const res = sellShort(stock, shares, ctx, {});
|
const res = sellShort(stock, shares, ctx, {});
|
||||||
@ -198,10 +194,8 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
|
|||||||
const type = helpers.string(ctx, "type", _type);
|
const type = helpers.string(ctx, "type", _type);
|
||||||
const pos = helpers.string(ctx, "pos", _pos);
|
const pos = helpers.string(ctx, "pos", _pos);
|
||||||
checkTixApiAccess(ctx);
|
checkTixApiAccess(ctx);
|
||||||
if (Player.bitNodeN !== 8) {
|
if (Player.bitNodeN !== 8 && Player.activeSourceFileLvl(8) <= 2) {
|
||||||
if (Player.sourceFileLvl(8) <= 2) {
|
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
|
||||||
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const stock = getStockFromSymbol(ctx, symbol);
|
const stock = getStockFromSymbol(ctx, symbol);
|
||||||
|
|
||||||
@ -238,10 +232,8 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
|
|||||||
const type = helpers.string(ctx, "type", _type);
|
const type = helpers.string(ctx, "type", _type);
|
||||||
const pos = helpers.string(ctx, "pos", _pos);
|
const pos = helpers.string(ctx, "pos", _pos);
|
||||||
checkTixApiAccess(ctx);
|
checkTixApiAccess(ctx);
|
||||||
if (Player.bitNodeN !== 8) {
|
if (Player.bitNodeN !== 8 && Player.activeSourceFileLvl(8) <= 2) {
|
||||||
if (Player.sourceFileLvl(8) <= 2) {
|
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
|
||||||
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const stock = getStockFromSymbol(ctx, symbol);
|
const stock = getStockFromSymbol(ctx, symbol);
|
||||||
if (isNaN(shares) || isNaN(price)) {
|
if (isNaN(shares) || isNaN(price)) {
|
||||||
@ -281,10 +273,8 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
|
|||||||
},
|
},
|
||||||
getOrders: (ctx) => () => {
|
getOrders: (ctx) => () => {
|
||||||
checkTixApiAccess(ctx);
|
checkTixApiAccess(ctx);
|
||||||
if (Player.bitNodeN !== 8) {
|
if (Player.bitNodeN !== 8 && Player.activeSourceFileLvl(8) <= 2) {
|
||||||
if (Player.sourceFileLvl(8) <= 2) {
|
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or have Source-File 8 Level 3.");
|
||||||
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or have Source-File 8 Level 3.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const orders: StockOrder = {};
|
const orders: StockOrder = {};
|
||||||
@ -328,6 +318,11 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
|
|||||||
return forecast / 100; // Convert from percentage to decimal
|
return forecast / 100; // Convert from percentage to decimal
|
||||||
},
|
},
|
||||||
purchase4SMarketData: (ctx) => () => {
|
purchase4SMarketData: (ctx) => () => {
|
||||||
|
if (Player.bitNodeOptions.disable4SData) {
|
||||||
|
helpers.log(ctx, () => "4S Market Data is disabled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (Player.has4SData) {
|
if (Player.has4SData) {
|
||||||
helpers.log(ctx, () => "Already purchased 4S Market Data.");
|
helpers.log(ctx, () => "Already purchased 4S Market Data.");
|
||||||
return true;
|
return true;
|
||||||
@ -344,6 +339,11 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
purchase4SMarketDataTixApi: (ctx) => () => {
|
purchase4SMarketDataTixApi: (ctx) => () => {
|
||||||
|
if (Player.bitNodeOptions.disable4SData) {
|
||||||
|
helpers.log(ctx, () => "4S Market Data is disabled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
checkTixApiAccess(ctx);
|
checkTixApiAccess(ctx);
|
||||||
|
|
||||||
if (Player.has4SDataTixApi) {
|
if (Player.has4SDataTixApi) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Player as IPlayer } from "@nsdefs";
|
import type { BitNodeOptions, Player as IPlayer } from "@nsdefs";
|
||||||
import type { PlayerAchievement } from "../../Achievements/Achievements";
|
import type { PlayerAchievement } from "../../Achievements/Achievements";
|
||||||
import type { Bladeburner } from "../../Bladeburner/Bladeburner";
|
import type { Bladeburner } from "../../Bladeburner/Bladeburner";
|
||||||
import type { Corporation } from "../../Corporation/Corporation";
|
import type { Corporation } from "../../Corporation/Corporation";
|
||||||
@ -74,6 +74,22 @@ export class PlayerObject extends Person implements IPlayer {
|
|||||||
|
|
||||||
entropy = 0;
|
entropy = 0;
|
||||||
|
|
||||||
|
bitNodeOptions: BitNodeOptions = {
|
||||||
|
sourceFileOverrides: new JSONMap<number, number>(),
|
||||||
|
intelligenceOverride: undefined,
|
||||||
|
restrictHomePCUpgrade: false,
|
||||||
|
disableGang: false,
|
||||||
|
disableCorporation: false,
|
||||||
|
disableBladeburner: false,
|
||||||
|
disable4SData: false,
|
||||||
|
disableHacknetServer: false,
|
||||||
|
disableSleeveExpAndAugmentation: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
get activeSourceFiles(): JSONMap<number, number> {
|
||||||
|
return new JSONMap([...this.sourceFiles, ...this.bitNodeOptions.sourceFileOverrides]);
|
||||||
|
}
|
||||||
|
|
||||||
// Player-specific methods
|
// Player-specific methods
|
||||||
init = generalMethods.init;
|
init = generalMethods.init;
|
||||||
startWork = workMethods.startWork;
|
startWork = workMethods.startWork;
|
||||||
@ -129,6 +145,7 @@ export class PlayerObject extends Person implements IPlayer {
|
|||||||
setBitNodeNumber = generalMethods.setBitNodeNumber;
|
setBitNodeNumber = generalMethods.setBitNodeNumber;
|
||||||
canAccessCotMG = generalMethods.canAccessCotMG;
|
canAccessCotMG = generalMethods.canAccessCotMG;
|
||||||
sourceFileLvl = generalMethods.sourceFileLvl;
|
sourceFileLvl = generalMethods.sourceFileLvl;
|
||||||
|
activeSourceFileLvl = generalMethods.activeSourceFileLvl;
|
||||||
applyEntropy = augmentationMethods.applyEntropy;
|
applyEntropy = augmentationMethods.applyEntropy;
|
||||||
focusPenalty = generalMethods.focusPenalty;
|
focusPenalty = generalMethods.focusPenalty;
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import { canAccessBitNodeFeature } from "../../BitNode/BitNodeUtils";
|
||||||
import { Bladeburner } from "../../Bladeburner/Bladeburner";
|
import { Bladeburner } from "../../Bladeburner/Bladeburner";
|
||||||
|
|
||||||
import type { PlayerObject } from "./PlayerObject";
|
import type { PlayerObject } from "./PlayerObject";
|
||||||
|
|
||||||
export function canAccessBladeburner(this: PlayerObject): boolean {
|
export function canAccessBladeburner(this: PlayerObject): boolean {
|
||||||
return this.bitNodeN === 6 || this.bitNodeN === 7 || this.sourceFileLvl(6) > 0 || this.sourceFileLvl(7) > 0;
|
return (canAccessBitNodeFeature(6) || canAccessBitNodeFeature(7)) && !this.bitNodeOptions.disableBladeburner;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startBladeburner(this: PlayerObject): void {
|
export function startBladeburner(this: PlayerObject): void {
|
||||||
|
@ -3,9 +3,10 @@ import { resetIndustryResearchTrees } from "../../Corporation/data/IndustryData"
|
|||||||
import { Corporation } from "../../Corporation/Corporation";
|
import { Corporation } from "../../Corporation/Corporation";
|
||||||
|
|
||||||
import type { PlayerObject } from "./PlayerObject";
|
import type { PlayerObject } from "./PlayerObject";
|
||||||
|
import { canAccessBitNodeFeature } from "../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
export function canAccessCorporation(this: PlayerObject): boolean {
|
export function canAccessCorporation(this: PlayerObject): boolean {
|
||||||
return this.bitNodeN === 3 || this.sourceFileLvl(3) > 0;
|
return canAccessBitNodeFeature(3) && !this.bitNodeOptions.disableCorporation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startCorporation(this: PlayerObject, corpName: string, seedFunded: boolean): void {
|
export function startCorporation(this: PlayerObject, corpName: string, seedFunded: boolean): void {
|
||||||
@ -17,7 +18,7 @@ export function startCorporation(this: PlayerObject, corpName: string, seedFunde
|
|||||||
//reset the research tree in case the corporation was restarted
|
//reset the research tree in case the corporation was restarted
|
||||||
resetIndustryResearchTrees();
|
resetIndustryResearchTrees();
|
||||||
|
|
||||||
if (this.bitNodeN === 3 || this.sourceFileLvl(3) === 3) {
|
if (this.bitNodeN === 3 || this.activeSourceFileLvl(3) === 3) {
|
||||||
this.corporation.unlocks.add(CorpUnlockName.WarehouseAPI);
|
this.corporation.unlocks.add(CorpUnlockName.WarehouseAPI);
|
||||||
this.corporation.unlocks.add(CorpUnlockName.OfficeAPI);
|
this.corporation.unlocks.add(CorpUnlockName.OfficeAPI);
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,16 @@ import { Factions } from "../../Faction/Factions";
|
|||||||
import { Gang } from "../../Gang/Gang";
|
import { Gang } from "../../Gang/Gang";
|
||||||
import { GangConstants } from "../../Gang/data/Constants";
|
import { GangConstants } from "../../Gang/data/Constants";
|
||||||
import { isFactionWork } from "../../Work/FactionWork";
|
import { isFactionWork } from "../../Work/FactionWork";
|
||||||
|
import { canAccessBitNodeFeature } from "../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
export function canAccessGang(this: PlayerObject): boolean {
|
export function canAccessGang(this: PlayerObject): boolean {
|
||||||
|
if (this.bitNodeOptions.disableGang) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (this.bitNodeN === 2) {
|
if (this.bitNodeN === 2) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.sourceFileLvl(2) <= 0) {
|
if (this.activeSourceFileLvl(2) === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +23,7 @@ export function canAccessGang(this: PlayerObject): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isAwareOfGang(this: PlayerObject): boolean {
|
export function isAwareOfGang(this: PlayerObject): boolean {
|
||||||
return this.bitNodeN === 2 || this.sourceFileLvl(2) >= 1;
|
return canAccessBitNodeFeature(2) && !this.bitNodeOptions.disableGang;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGangFaction(this: PlayerObject): Faction {
|
export function getGangFaction(this: PlayerObject): Faction {
|
||||||
|
@ -49,6 +49,7 @@ import { achievements } from "../../Achievements/Achievements";
|
|||||||
|
|
||||||
import { isCompanyWork } from "../../Work/CompanyWork";
|
import { isCompanyWork } from "../../Work/CompanyWork";
|
||||||
import { isMember } from "../../utils/EnumHelper";
|
import { isMember } from "../../utils/EnumHelper";
|
||||||
|
import { canAccessBitNodeFeature } from "../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
export function init(this: PlayerObject): void {
|
export function init(this: PlayerObject): void {
|
||||||
/* Initialize Player's home computer */
|
/* Initialize Player's home computer */
|
||||||
@ -415,7 +416,7 @@ export function reapplyAllSourceFiles(this: PlayerObject): void {
|
|||||||
//Will always be called after reapplyAllAugmentations() so multipliers do not have to be reset
|
//Will always be called after reapplyAllAugmentations() so multipliers do not have to be reset
|
||||||
//this.resetMultipliers();
|
//this.resetMultipliers();
|
||||||
|
|
||||||
for (const [bn, lvl] of this.sourceFiles) {
|
for (const [bn, lvl] of this.activeSourceFiles) {
|
||||||
const srcFileKey = "SourceFile" + bn;
|
const srcFileKey = "SourceFile" + bn;
|
||||||
const sourceFileObject = SourceFiles[srcFileKey];
|
const sourceFileObject = SourceFiles[srcFileKey];
|
||||||
if (!sourceFileObject) {
|
if (!sourceFileObject) {
|
||||||
@ -536,7 +537,7 @@ export function gotoLocation(this: PlayerObject, to: LocationName): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function canAccessGrafting(this: PlayerObject): boolean {
|
export function canAccessGrafting(this: PlayerObject): boolean {
|
||||||
return this.bitNodeN === 10 || this.sourceFileLvl(10) > 0;
|
return canAccessBitNodeFeature(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function giveExploit(this: PlayerObject, exploit: Exploit): void {
|
export function giveExploit(this: PlayerObject, exploit: Exploit): void {
|
||||||
@ -560,13 +561,20 @@ export function getCasinoWinnings(this: PlayerObject): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function canAccessCotMG(this: PlayerObject): boolean {
|
export function canAccessCotMG(this: PlayerObject): boolean {
|
||||||
return this.bitNodeN === 13 || this.sourceFileLvl(13) > 0;
|
return canAccessBitNodeFeature(13);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sourceFileLvl(this: PlayerObject, n: number): number {
|
export function sourceFileLvl(this: PlayerObject, n: number): number {
|
||||||
return this.sourceFiles.get(n) ?? 0;
|
return this.sourceFiles.get(n) ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function activeSourceFileLvl(this: PlayerObject, n: number): number {
|
||||||
|
if (this.bitNodeOptions.sourceFileOverrides.has(n)) {
|
||||||
|
return this.bitNodeOptions.sourceFileOverrides.get(n) ?? 0;
|
||||||
|
}
|
||||||
|
return this.sourceFiles.get(n) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
export function focusPenalty(this: PlayerObject): number {
|
export function focusPenalty(this: PlayerObject): number {
|
||||||
let focus = 1;
|
let focus = 1;
|
||||||
if (!this.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) {
|
if (!this.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Sleeve } from "../Sleeve";
|
import { Sleeve } from "../Sleeve";
|
||||||
import { Player } from "@player";
|
|
||||||
import { formatExp, formatPercent } from "../../../ui/formatNumber";
|
import { formatExp, formatPercent } from "../../../ui/formatNumber";
|
||||||
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||||
import { CONSTANTS } from "../../../Constants";
|
import { CONSTANTS } from "../../../Constants";
|
||||||
@ -7,6 +6,7 @@ import { Typography } from "@mui/material";
|
|||||||
import { StatsTable } from "../../../ui/React/StatsTable";
|
import { StatsTable } from "../../../ui/React/StatsTable";
|
||||||
import { Modal } from "../../../ui/React/Modal";
|
import { Modal } from "../../../ui/React/Modal";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { canAccessBitNodeFeature } from "../../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -30,7 +30,7 @@ export function MoreStatsModal(props: IProps): React.ReactElement {
|
|||||||
[<>Agility: </>, props.sleeve.skills.agility, <> ({formatExp(props.sleeve.exp.agility)} exp)</>],
|
[<>Agility: </>, props.sleeve.skills.agility, <> ({formatExp(props.sleeve.exp.agility)} exp)</>],
|
||||||
[<>Charisma: </>, props.sleeve.skills.charisma, <> ({formatExp(props.sleeve.exp.charisma)} exp)</>],
|
[<>Charisma: </>, props.sleeve.skills.charisma, <> ({formatExp(props.sleeve.exp.charisma)} exp)</>],
|
||||||
[
|
[
|
||||||
...(Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5
|
...(canAccessBitNodeFeature(5)
|
||||||
? [
|
? [
|
||||||
<>Intelligence: </>,
|
<>Intelligence: </>,
|
||||||
props.sleeve.skills.intelligence,
|
props.sleeve.skills.intelligence,
|
||||||
|
@ -25,6 +25,7 @@ import { isSleeveClassWork } from "../Work/SleeveClassWork";
|
|||||||
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
|
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
|
||||||
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
|
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
|
||||||
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
|
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
|
||||||
|
import { canAccessBitNodeFeature } from "../../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
|
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ export function StatsElement(props: IProps): React.ReactElement {
|
|||||||
color={Settings.theme.cha}
|
color={Settings.theme.cha}
|
||||||
data={{ level: props.sleeve.skills.charisma, exp: props.sleeve.exp.charisma }}
|
data={{ level: props.sleeve.skills.charisma, exp: props.sleeve.exp.charisma }}
|
||||||
/>
|
/>
|
||||||
{(Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) && (
|
{canAccessBitNodeFeature(5) && (
|
||||||
<StatsRow
|
<StatsRow
|
||||||
name="Intelligence"
|
name="Intelligence"
|
||||||
color={Settings.theme.int}
|
color={Settings.theme.int}
|
||||||
@ -111,7 +112,7 @@ export function StatsElement(props: IProps): React.ReactElement {
|
|||||||
export function EarningsElement(props: IProps): React.ReactElement {
|
export function EarningsElement(props: IProps): React.ReactElement {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
let data: (string | JSX.Element)[][] = [];
|
let data: [string, string | JSX.Element][] = [];
|
||||||
if (isSleeveCrimeWork(props.sleeve.currentWork)) {
|
if (isSleeveCrimeWork(props.sleeve.currentWork)) {
|
||||||
const gains = props.sleeve.currentWork.getExp(props.sleeve);
|
const gains = props.sleeve.currentWork.getExp(props.sleeve);
|
||||||
data = [
|
data = [
|
||||||
@ -150,20 +151,21 @@ export function EarningsElement(props: IProps): React.ReactElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
companyWork: if (isSleeveCompanyWork(props.sleeve.currentWork)) {
|
if (isSleeveCompanyWork(props.sleeve.currentWork)) {
|
||||||
const job = Player.jobs[props.sleeve.currentWork.companyName];
|
const job = Player.jobs[props.sleeve.currentWork.companyName];
|
||||||
if (!job) break companyWork;
|
if (job) {
|
||||||
const rates = props.sleeve.currentWork.getGainRates(props.sleeve, job);
|
const rates = props.sleeve.currentWork.getGainRates(props.sleeve, job);
|
||||||
data = [
|
data = [
|
||||||
[`Money:`, <MoneyRate key="money-rate" money={CYCLES_PER_SEC * rates.money} />],
|
[`Money:`, <MoneyRate key="money-rate" money={CYCLES_PER_SEC * rates.money} />],
|
||||||
[`Hacking Exp:`, `${formatExp(CYCLES_PER_SEC * rates.hackExp)} / sec`],
|
[`Hacking Exp:`, `${formatExp(CYCLES_PER_SEC * rates.hackExp)} / sec`],
|
||||||
[`Strength Exp:`, `${formatExp(CYCLES_PER_SEC * rates.strExp)} / sec`],
|
[`Strength Exp:`, `${formatExp(CYCLES_PER_SEC * rates.strExp)} / sec`],
|
||||||
[`Defense Exp:`, `${formatExp(CYCLES_PER_SEC * rates.defExp)} / sec`],
|
[`Defense Exp:`, `${formatExp(CYCLES_PER_SEC * rates.defExp)} / sec`],
|
||||||
[`Dexterity Exp:`, `${formatExp(CYCLES_PER_SEC * rates.dexExp)} / sec`],
|
[`Dexterity Exp:`, `${formatExp(CYCLES_PER_SEC * rates.dexExp)} / sec`],
|
||||||
[`Agility Exp:`, `${formatExp(CYCLES_PER_SEC * rates.agiExp)} / sec`],
|
[`Agility Exp:`, `${formatExp(CYCLES_PER_SEC * rates.agiExp)} / sec`],
|
||||||
[`Charisma Exp:`, `${formatExp(CYCLES_PER_SEC * rates.chaExp)} / sec`],
|
[`Charisma Exp:`, `${formatExp(CYCLES_PER_SEC * rates.chaExp)} / sec`],
|
||||||
[`Reputation:`, <ReputationRate key="reputation-rate" reputation={CYCLES_PER_SEC * rates.reputation} />],
|
[`Reputation:`, <ReputationRate key="reputation-rate" reputation={CYCLES_PER_SEC * rates.reputation} />],
|
||||||
];
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
import { Player } from "@player";
|
||||||
|
|
||||||
export function calculateIntelligenceBonus(intelligence: number, weight = 1): number {
|
export function calculateIntelligenceBonus(intelligence: number, weight = 1): number {
|
||||||
return 1 + (weight * Math.pow(intelligence, 0.8)) / 600;
|
const effectiveIntelligence =
|
||||||
|
Player.bitNodeOptions.intelligenceOverride !== undefined
|
||||||
|
? Math.min(Player.bitNodeOptions.intelligenceOverride, intelligence)
|
||||||
|
: intelligence;
|
||||||
|
return 1 + (weight * Math.pow(effectiveIntelligence, 0.8)) / 600;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import { initCircadianModulator } from "./Augmentation/Augmentations";
|
|||||||
import { Go } from "./Go/Go";
|
import { Go } from "./Go/Go";
|
||||||
import { calculateExp } from "./PersonObjects/formulas/skill";
|
import { calculateExp } from "./PersonObjects/formulas/skill";
|
||||||
import { currentNodeMults } from "./BitNode/BitNodeMultipliers";
|
import { currentNodeMults } from "./BitNode/BitNodeMultipliers";
|
||||||
|
import { canAccessBitNodeFeature } from "./BitNode/BitNodeUtils";
|
||||||
|
|
||||||
const BitNode8StartingMoney = 250e6;
|
const BitNode8StartingMoney = 250e6;
|
||||||
function delayedDialog(message: string) {
|
function delayedDialog(message: string) {
|
||||||
@ -82,7 +83,7 @@ export function prestigeAugmentation(): void {
|
|||||||
homeComp.programs.push(program);
|
homeComp.programs.push(program);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
|
if (canAccessBitNodeFeature(5)) {
|
||||||
homeComp.programs.push(CompletedProgramName.formulas);
|
homeComp.programs.push(CompletedProgramName.formulas);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ export function prestigeAugmentation(): void {
|
|||||||
if (Player.bitNodeN === 8) {
|
if (Player.bitNodeN === 8) {
|
||||||
Player.money = BitNode8StartingMoney;
|
Player.money = BitNode8StartingMoney;
|
||||||
}
|
}
|
||||||
if (Player.bitNodeN === 8 || Player.sourceFileLvl(8) > 0) {
|
if (canAccessBitNodeFeature(8)) {
|
||||||
Player.hasWseAccount = true;
|
Player.hasWseAccount = true;
|
||||||
Player.hasTixApiAccess = true;
|
Player.hasTixApiAccess = true;
|
||||||
}
|
}
|
||||||
@ -169,7 +170,7 @@ export function prestigeAugmentation(): void {
|
|||||||
// Bitnode 13: Church of the Machine God
|
// Bitnode 13: Church of the Machine God
|
||||||
if (Player.hasAugmentation(AugmentationName.StaneksGift1, true)) {
|
if (Player.hasAugmentation(AugmentationName.StaneksGift1, true)) {
|
||||||
joinFaction(Factions[FactionName.ChurchOfTheMachineGod]);
|
joinFaction(Factions[FactionName.ChurchOfTheMachineGod]);
|
||||||
} else if (Player.bitNodeN != 13) {
|
} else if (Player.bitNodeN !== 13) {
|
||||||
if (Player.augmentations.some((a) => a.name !== AugmentationName.NeuroFluxGovernor)) {
|
if (Player.augmentations.some((a) => a.name !== AugmentationName.NeuroFluxGovernor)) {
|
||||||
Factions[FactionName.ChurchOfTheMachineGod].isBanned = true;
|
Factions[FactionName.ChurchOfTheMachineGod].isBanned = true;
|
||||||
}
|
}
|
||||||
@ -215,9 +216,9 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
|||||||
// Re-create foreign servers
|
// Re-create foreign servers
|
||||||
initForeignServers(Player.getHomeComputer());
|
initForeignServers(Player.getHomeComputer());
|
||||||
|
|
||||||
if (Player.sourceFileLvl(9) >= 2) {
|
if (Player.activeSourceFileLvl(9) >= 2) {
|
||||||
homeComp.setMaxRam(128);
|
homeComp.setMaxRam(128);
|
||||||
} else if (Player.sourceFileLvl(1) > 0) {
|
} else if (Player.activeSourceFileLvl(1) > 0) {
|
||||||
homeComp.setMaxRam(32);
|
homeComp.setMaxRam(32);
|
||||||
} else {
|
} else {
|
||||||
homeComp.setMaxRam(8);
|
homeComp.setMaxRam(8);
|
||||||
@ -234,10 +235,10 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Give levels of NeuroFluxGovernor for Source-File 12. Must be done here before Augmentations are recalculated
|
// Give levels of NeuroFluxGovernor for Source-File 12. Must be done here before Augmentations are recalculated
|
||||||
if (Player.sourceFileLvl(12) > 0) {
|
if (Player.activeSourceFileLvl(12) > 0) {
|
||||||
Player.augmentations.push({
|
Player.augmentations.push({
|
||||||
name: AugmentationName.NeuroFluxGovernor,
|
name: AugmentationName.NeuroFluxGovernor,
|
||||||
level: Player.sourceFileLvl(12),
|
level: Player.activeSourceFileLvl(12),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +247,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
|||||||
Player.reapplyAllAugmentations();
|
Player.reapplyAllAugmentations();
|
||||||
Player.reapplyAllSourceFiles();
|
Player.reapplyAllSourceFiles();
|
||||||
|
|
||||||
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
|
if (canAccessBitNodeFeature(5)) {
|
||||||
homeComp.programs.push(CompletedProgramName.formulas);
|
homeComp.programs.push(CompletedProgramName.formulas);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +270,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
|||||||
if (Player.bitNodeN === 8) {
|
if (Player.bitNodeN === 8) {
|
||||||
Player.money = BitNode8StartingMoney;
|
Player.money = BitNode8StartingMoney;
|
||||||
}
|
}
|
||||||
if (Player.bitNodeN === 8 || Player.sourceFileLvl(8) > 0) {
|
if (Player.bitNodeN === 8 || Player.activeSourceFileLvl(8) > 0) {
|
||||||
Player.hasWseAccount = true;
|
Player.hasWseAccount = true;
|
||||||
Player.hasTixApiAccess = true;
|
Player.hasTixApiAccess = true;
|
||||||
}
|
}
|
||||||
@ -282,7 +283,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BitNode 12: The Recursion
|
// BitNode 12: The Recursion
|
||||||
if (Player.bitNodeN === 12 && Player.sourceFileLvl(12) > 100) {
|
if (Player.bitNodeN === 12 && Player.activeSourceFileLvl(12) > 100) {
|
||||||
delayedDialog("Saynt_Garmo is watching you");
|
delayedDialog("Saynt_Garmo is watching you");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +302,7 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
|||||||
|
|
||||||
// Source-File 9 (level 3) effect
|
// Source-File 9 (level 3) effect
|
||||||
// also now applies when entering bn9 until install
|
// also now applies when entering bn9 until install
|
||||||
if (Player.sourceFileLvl(9) >= 3 || Player.bitNodeN === 9) {
|
if ((Player.activeSourceFileLvl(9) >= 3 || Player.bitNodeN === 9) && !Player.bitNodeOptions.disableHacknetServer) {
|
||||||
const hserver = Player.createHacknetServer();
|
const hserver = Player.createHacknetServer();
|
||||||
|
|
||||||
hserver.level = 100;
|
hserver.level = 100;
|
||||||
@ -319,7 +320,9 @@ export function prestigeSourceFile(isFlume: boolean): void {
|
|||||||
staneksGift.prestigeSourceFile();
|
staneksGift.prestigeSourceFile();
|
||||||
|
|
||||||
// Gain int exp
|
// Gain int exp
|
||||||
if (Player.sourceFileLvl(5) !== 0 && !isFlume) Player.gainIntelligenceExp(300);
|
if (Player.activeSourceFileLvl(5) !== 0 && !isFlume) {
|
||||||
|
Player.gainIntelligenceExp(300);
|
||||||
|
}
|
||||||
|
|
||||||
// Clear recent scripts
|
// Clear recent scripts
|
||||||
recentScripts.splice(0, recentScripts.length);
|
recentScripts.splice(0, recentScripts.length);
|
||||||
|
@ -13,6 +13,7 @@ import { calculateHackingTime, calculateGrowTime, calculateWeakenTime } from "..
|
|||||||
import { CompletedProgramName, FactionName } from "@enums";
|
import { CompletedProgramName, FactionName } from "@enums";
|
||||||
import { Router } from "../ui/GameRoot";
|
import { Router } from "../ui/GameRoot";
|
||||||
import { Page } from "../ui/Router";
|
import { Page } from "../ui/Router";
|
||||||
|
import { knowAboutBitverse } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
function requireHackingLevel(lvl: number) {
|
function requireHackingLevel(lvl: number) {
|
||||||
return function () {
|
return function () {
|
||||||
@ -22,7 +23,7 @@ function requireHackingLevel(lvl: number) {
|
|||||||
|
|
||||||
function bitFlumeRequirements() {
|
function bitFlumeRequirements() {
|
||||||
return function () {
|
return function () {
|
||||||
return Player.sourceFiles.size > 0 && Player.skills.hacking >= 1;
|
return knowAboutBitverse() && Player.skills.hacking >= 1;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
/** Implementation for what happens when you destroy a BitNode */
|
/** Implementation for what happens when you destroy a BitNode */
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Player } from "@player";
|
import { Player } from "@player";
|
||||||
|
import { type BitNodeOptions } from "@nsdefs";
|
||||||
import { SourceFiles } from "./SourceFile/SourceFiles";
|
import { SourceFiles } from "./SourceFile/SourceFiles";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
import { dialogBoxCreate } from "./ui/React/DialogBox";
|
||||||
import { Router } from "./ui/GameRoot";
|
import { Router } from "./ui/GameRoot";
|
||||||
import { Page } from "./ui/Router";
|
import { Page } from "./ui/Router";
|
||||||
import { prestigeSourceFile } from "./Prestige";
|
import { prestigeSourceFile } from "./Prestige";
|
||||||
|
import { setBitNodeOptions } from "./BitNode/BitNodeUtils";
|
||||||
|
|
||||||
function giveSourceFile(bitNodeNumber: number): void {
|
function giveSourceFile(bitNodeNumber: number): void {
|
||||||
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
|
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
|
||||||
@ -48,7 +50,12 @@ function giveSourceFile(bitNodeNumber: number): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enterBitNode(isFlume: boolean, destroyedBitNode: number, newBitNode: number): void {
|
export function enterBitNode(
|
||||||
|
isFlume: boolean,
|
||||||
|
destroyedBitNode: number,
|
||||||
|
newBitNode: number,
|
||||||
|
bitNodeOptions: BitNodeOptions,
|
||||||
|
): void {
|
||||||
if (!isFlume) {
|
if (!isFlume) {
|
||||||
giveSourceFile(destroyedBitNode);
|
giveSourceFile(destroyedBitNode);
|
||||||
} else if (Player.sourceFileLvl(5) === 0 && newBitNode !== 5) {
|
} else if (Player.sourceFileLvl(5) === 0 && newBitNode !== 5) {
|
||||||
@ -61,6 +68,9 @@ export function enterBitNode(isFlume: boolean, destroyedBitNode: number, newBitN
|
|||||||
// Set new Bit Node
|
// Set new Bit Node
|
||||||
Player.bitNodeN = newBitNode;
|
Player.bitNodeN = newBitNode;
|
||||||
|
|
||||||
|
// Set BitNode options
|
||||||
|
setBitNodeOptions(bitNodeOptions);
|
||||||
|
|
||||||
if (newBitNode === 6) {
|
if (newBitNode === 6) {
|
||||||
Router.toPage(Page.BladeburnerCinematic);
|
Router.toPage(Page.BladeburnerCinematic);
|
||||||
} else {
|
} else {
|
||||||
|
38
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
38
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -76,6 +76,8 @@ interface ResetInfo {
|
|||||||
ownedAugs: Map<string, number>;
|
ownedAugs: Map<string, number>;
|
||||||
/** A map of owned SF to their levels. Keyed by the SF number. Map values are the SF level. */
|
/** A map of owned SF to their levels. Keyed by the SF number. Map values are the SF level. */
|
||||||
ownedSF: Map<number, number>;
|
ownedSF: Map<number, number>;
|
||||||
|
/** Current BitNode options */
|
||||||
|
bitNodeOptions: BitNodeOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
@ -1701,6 +1703,36 @@ export interface GraftingTask {
|
|||||||
*/
|
*/
|
||||||
export type Task = StudyTask | CompanyWorkTask | CreateProgramWorkTask | CrimeTask | FactionWorkTask | GraftingTask;
|
export type Task = StudyTask | CompanyWorkTask | CreateProgramWorkTask | CrimeTask | FactionWorkTask | GraftingTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value:
|
||||||
|
* - sourceFileOverrides: an empty Map
|
||||||
|
* - intelligenceOverride: undefined
|
||||||
|
* - All boolean options: false
|
||||||
|
*
|
||||||
|
* If you specify intelligenceOverride, it must be a non-negative integer.
|
||||||
|
*/
|
||||||
|
export interface BitNodeOptions extends BitNodeBooleanOptions {
|
||||||
|
sourceFileOverrides: Map<number, number>;
|
||||||
|
intelligenceOverride: number | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* restrictHomePCUpgrade: The home computer's maximum RAM and number of cores are lower than normal. Max RAM: 128GB. Max
|
||||||
|
* core: 1.
|
||||||
|
*
|
||||||
|
* disableSleeveExpAndAugmentation: Your Sleeves do not gain experience when they perform action. You also cannot buy
|
||||||
|
* augmentations for them.
|
||||||
|
*/
|
||||||
|
export interface BitNodeBooleanOptions {
|
||||||
|
restrictHomePCUpgrade: boolean;
|
||||||
|
disableGang: boolean;
|
||||||
|
disableCorporation: boolean;
|
||||||
|
disableBladeburner: boolean;
|
||||||
|
disable4SData: boolean;
|
||||||
|
disableHacknetServer: boolean;
|
||||||
|
disableSleeveExpAndAugmentation: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singularity API
|
* Singularity API
|
||||||
* @remarks
|
* @remarks
|
||||||
@ -2615,8 +2647,9 @@ export interface Singularity {
|
|||||||
*
|
*
|
||||||
* @param nextBN - BN number to jump to
|
* @param nextBN - BN number to jump to
|
||||||
* @param callbackScript - Name of the script to launch in the next BN.
|
* @param callbackScript - Name of the script to launch in the next BN.
|
||||||
|
* @param bitNodeOptions - BitNode options for the next BN.
|
||||||
*/
|
*/
|
||||||
b1tflum3(nextBN: number, callbackScript?: string): void;
|
b1tflum3(nextBN: number, callbackScript?: string, bitNodeOptions?: BitNodeOptions): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the w0r1d_d43m0n and move on to the next BN.
|
* Destroy the w0r1d_d43m0n and move on to the next BN.
|
||||||
@ -2629,8 +2662,9 @@ export interface Singularity {
|
|||||||
*
|
*
|
||||||
* @param nextBN - BN number to jump to
|
* @param nextBN - BN number to jump to
|
||||||
* @param callbackScript - Name of the script to launch in the next BN.
|
* @param callbackScript - Name of the script to launch in the next BN.
|
||||||
|
* @param bitNodeOptions - BitNode options for the next BN.
|
||||||
*/
|
*/
|
||||||
destroyW0r1dD43m0n(nextBN: number, callbackScript?: string): void;
|
destroyW0r1dD43m0n(nextBN: number, callbackScript?: string, bitNodeOptions?: BitNodeOptions): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current work the player is doing.
|
* Get the current work the player is doing.
|
||||||
|
@ -164,7 +164,10 @@ export function purchaseRamForHomeComputer(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const homeComputer = Player.getHomeComputer();
|
const homeComputer = Player.getHomeComputer();
|
||||||
if (homeComputer.maxRam >= ServerConstants.HomeComputerMaxRam) {
|
if (
|
||||||
|
(Player.bitNodeOptions.restrictHomePCUpgrade && homeComputer.maxRam >= 128) ||
|
||||||
|
homeComputer.maxRam >= ServerConstants.HomeComputerMaxRam
|
||||||
|
) {
|
||||||
dialogBoxCreate(`You cannot upgrade your home computer RAM because it is at its maximum possible value`);
|
dialogBoxCreate(`You cannot upgrade your home computer RAM because it is at its maximum possible value`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ import { hash } from "../../hash/hash";
|
|||||||
import { Locations } from "../../Locations/Locations";
|
import { Locations } from "../../Locations/Locations";
|
||||||
import { useRerender } from "../../ui/React/hooks";
|
import { useRerender } from "../../ui/React/hooks";
|
||||||
import { playerHasDiscoveredGo } from "../../Go/effects/effect";
|
import { playerHasDiscoveredGo } from "../../Go/effects/effect";
|
||||||
|
import { knowAboutBitverse } from "../../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
const RotatedDoubleArrowIcon = React.forwardRef(function RotatedDoubleArrowIcon(
|
const RotatedDoubleArrowIcon = React.forwardRef(function RotatedDoubleArrowIcon(
|
||||||
props: { color: "primary" | "secondary" | "error" },
|
props: { color: "primary" | "secondary" | "error" },
|
||||||
@ -141,12 +142,12 @@ export function SidebarRoot(props: { page: Page }): React.ReactElement {
|
|||||||
Player.factions.length > 0 ||
|
Player.factions.length > 0 ||
|
||||||
Player.augmentations.length > 0 ||
|
Player.augmentations.length > 0 ||
|
||||||
Player.queuedAugmentations.length > 0 ||
|
Player.queuedAugmentations.length > 0 ||
|
||||||
Player.sourceFiles.size > 0;
|
knowAboutBitverse();
|
||||||
|
|
||||||
const canOpenAugmentations =
|
const canOpenAugmentations =
|
||||||
Player.augmentations.length > 0 ||
|
Player.augmentations.length > 0 ||
|
||||||
Player.queuedAugmentations.length > 0 ||
|
Player.queuedAugmentations.length > 0 ||
|
||||||
Player.sourceFiles.size > 0 ||
|
knowAboutBitverse() ||
|
||||||
Player.exploits.length > 0;
|
Player.exploits.length > 0;
|
||||||
|
|
||||||
const canOpenSleeves = Player.sleeves.length > 0;
|
const canOpenSleeves = Player.sleeves.length > 0;
|
||||||
|
@ -105,6 +105,9 @@ function PurchaseWseAccountButton(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
function PurchaseTixApiAccessButton(props: IProps): React.ReactElement {
|
function PurchaseTixApiAccessButton(props: IProps): React.ReactElement {
|
||||||
function purchaseTixApiAccess(): void {
|
function purchaseTixApiAccess(): void {
|
||||||
|
if (Player.bitNodeOptions.disable4SData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Player.hasTixApiAccess) {
|
if (Player.hasTixApiAccess) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -135,6 +138,9 @@ function PurchaseTixApiAccessButton(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
function Purchase4SMarketDataButton(props: IProps): React.ReactElement {
|
function Purchase4SMarketDataButton(props: IProps): React.ReactElement {
|
||||||
function purchase4SMarketData(): void {
|
function purchase4SMarketData(): void {
|
||||||
|
if (Player.bitNodeOptions.disable4SData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Player.has4SData) {
|
if (Player.has4SData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -184,18 +190,22 @@ export function InfoAndPurchases(props: IProps): React.ReactElement {
|
|||||||
the TIX API lets you write code to create your own algorithmic/automated trading strategies.
|
the TIX API lets you write code to create your own algorithmic/automated trading strategies.
|
||||||
</Typography>
|
</Typography>
|
||||||
<PurchaseTixApiAccessButton {...props} />
|
<PurchaseTixApiAccessButton {...props} />
|
||||||
<Typography variant="h5" color="primary">
|
{!Player.bitNodeOptions.disable4SData && (
|
||||||
{FactionName.FourSigma} (4S) Market Data Feed
|
<>
|
||||||
</Typography>
|
<Typography variant="h5" color="primary">
|
||||||
<Typography>
|
{FactionName.FourSigma} (4S) Market Data Feed
|
||||||
{FactionName.FourSigma}'s (4S) Market Data Feed provides information about stocks that will help your trading
|
</Typography>
|
||||||
strategies.
|
<Typography>
|
||||||
<IconButton onClick={() => setHelpOpen(true)}>
|
{FactionName.FourSigma}'s (4S) Market Data Feed provides information about stocks that will help your
|
||||||
<HelpIcon />
|
trading strategies.
|
||||||
</IconButton>
|
<IconButton onClick={() => setHelpOpen(true)}>
|
||||||
</Typography>
|
<HelpIcon />
|
||||||
<Purchase4SMarketDataTixApiAccessButton {...props} />
|
</IconButton>
|
||||||
<Purchase4SMarketDataButton {...props} />
|
</Typography>
|
||||||
|
<Purchase4SMarketDataTixApiAccessButton {...props} />
|
||||||
|
<Purchase4SMarketDataButton {...props} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Typography>
|
<Typography>
|
||||||
Commission Fees: Every transaction you make has a{" "}
|
Commission Fees: Every transaction you make has a{" "}
|
||||||
<Money money={StockMarketConstants.StockMarketCommission} forPurchase={true} /> commission fee.
|
<Money money={StockMarketConstants.StockMarketCommission} forPurchase={true} /> commission fee.
|
||||||
|
@ -270,12 +270,12 @@ export function StockTicker(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
// Whether the player has access to orders besides market orders (limit/stop)
|
// Whether the player has access to orders besides market orders (limit/stop)
|
||||||
function hasOrderAccess(): boolean {
|
function hasOrderAccess(): boolean {
|
||||||
return Player.bitNodeN === 8 || Player.sourceFileLvl(8) >= 3;
|
return Player.bitNodeN === 8 || Player.activeSourceFileLvl(8) >= 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether the player has access to shorting stocks
|
// Whether the player has access to shorting stocks
|
||||||
function hasShortAccess(): boolean {
|
function hasShortAccess(): boolean {
|
||||||
return Player.bitNodeN === 8 || Player.sourceFileLvl(8) >= 2;
|
return Player.bitNodeN === 8 || Player.activeSourceFileLvl(8) >= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -65,7 +65,7 @@ function ShortPosition(props: IProps): React.ReactElement {
|
|||||||
percentageGains = 0;
|
percentageGains = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Player.bitNodeN === 8 || Player.sourceFileLvl(8) >= 2) {
|
if (Player.bitNodeN === 8 || Player.activeSourceFileLvl(8) >= 2) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box display="flex">
|
<Box display="flex">
|
||||||
|
@ -20,6 +20,20 @@ import { Company } from "../Company/Company";
|
|||||||
import { CompanyPosition } from "../Company/CompanyPosition";
|
import { CompanyPosition } from "../Company/CompanyPosition";
|
||||||
import { isMember } from "../utils/EnumHelper";
|
import { isMember } from "../utils/EnumHelper";
|
||||||
|
|
||||||
|
function processWorkStats(person: IPerson, workStats: WorkStats): WorkStats {
|
||||||
|
// "person" can be a normal object that the player passes to NS APIs, so we cannot use `person instanceof Sleeve`.
|
||||||
|
if (Player.bitNodeOptions.disableSleeveExpAndAugmentation && "shock" in person) {
|
||||||
|
workStats.hackExp = 0;
|
||||||
|
workStats.strExp = 0;
|
||||||
|
workStats.defExp = 0;
|
||||||
|
workStats.dexExp = 0;
|
||||||
|
workStats.agiExp = 0;
|
||||||
|
workStats.chaExp = 0;
|
||||||
|
workStats.intExp = 0;
|
||||||
|
}
|
||||||
|
return workStats;
|
||||||
|
}
|
||||||
|
|
||||||
const gameCPS = 1000 / CONSTANTS.MilliPerCycle; // 5 cycles per second
|
const gameCPS = 1000 / CONSTANTS.MilliPerCycle; // 5 cycles per second
|
||||||
export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
|
export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
|
||||||
[FactionWorkType.hacking]: newWorkStats({ hackExp: 2 }),
|
[FactionWorkType.hacking]: newWorkStats({ hackExp: 2 }),
|
||||||
@ -60,7 +74,7 @@ export function calculateCrimeWorkStats(person: IPerson, crime: Crime): WorkStat
|
|||||||
currentNodeMults.CrimeExpGain,
|
currentNodeMults.CrimeExpGain,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
return gains;
|
return processWorkStats(person, gains);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns faction rep rate per cycle */
|
/** @returns faction rep rate per cycle */
|
||||||
@ -75,9 +89,9 @@ export const calculateFactionRep = (person: IPerson, type: FactionWorkType, favo
|
|||||||
|
|
||||||
/** @returns per-cycle WorkStats */
|
/** @returns per-cycle WorkStats */
|
||||||
export function calculateFactionExp(person: IPerson, type: FactionWorkType): WorkStats {
|
export function calculateFactionExp(person: IPerson, type: FactionWorkType): WorkStats {
|
||||||
return scaleWorkStats(
|
return processWorkStats(
|
||||||
multWorkStats(FactionWorkStats[type], person.mults),
|
person,
|
||||||
currentNodeMults.FactionWorkExpGain / gameCPS,
|
scaleWorkStats(multWorkStats(FactionWorkStats[type], person.mults), currentNodeMults.FactionWorkExpGain / gameCPS),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +116,7 @@ export function calculateClassEarnings(person: IPerson, type: ClassType, locatio
|
|||||||
person.mults,
|
person.mults,
|
||||||
);
|
);
|
||||||
earnings.money = calculateCost(classs, location) / gameCPS;
|
earnings.money = calculateCost(classs, location) / gameCPS;
|
||||||
return earnings;
|
return processWorkStats(person, earnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns per-cycle WorkStats */
|
/** @returns per-cycle WorkStats */
|
||||||
@ -114,7 +128,7 @@ export const calculateCompanyWorkStats = (
|
|||||||
): WorkStats => {
|
): WorkStats => {
|
||||||
// If player has SF-11, calculate salary multiplier from favor
|
// If player has SF-11, calculate salary multiplier from favor
|
||||||
const favorMult = isNaN(favor) ? 1 : 1 + favor / 100;
|
const favorMult = isNaN(favor) ? 1 : 1 + favor / 100;
|
||||||
const bn11Mult = Player.sourceFileLvl(11) > 0 ? favorMult : 1;
|
const bn11Mult = Player.activeSourceFileLvl(11) > 0 ? favorMult : 1;
|
||||||
|
|
||||||
const gains = scaleWorkStats(
|
const gains = scaleWorkStats(
|
||||||
multWorkStats(
|
multWorkStats(
|
||||||
@ -138,5 +152,5 @@ export const calculateCompanyWorkStats = (
|
|||||||
|
|
||||||
gains.reputation = jobPerformance * worker.mults.company_rep * favorMult * currentNodeMults.CompanyWorkRepGain;
|
gains.reputation = jobPerformance * worker.mults.company_rep * favorMult * currentNodeMults.CompanyWorkRepGain;
|
||||||
|
|
||||||
return gains;
|
return processWorkStats(worker, gains);
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ import { StatsRow } from "./React/StatsRow";
|
|||||||
import { StatsTable } from "./React/StatsTable";
|
import { StatsTable } from "./React/StatsTable";
|
||||||
import { useRerender } from "./React/hooks";
|
import { useRerender } from "./React/hooks";
|
||||||
import { getMaxFavor } from "../Go/effects/effect";
|
import { getMaxFavor } from "../Go/effects/effect";
|
||||||
|
import { canAccessBitNodeFeature, knowAboutBitverse } from "../BitNode/BitNodeUtils";
|
||||||
|
|
||||||
interface EmployersModalProps {
|
interface EmployersModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -68,7 +69,7 @@ function MultiplierTable(props: MultTableProps): React.ReactElement {
|
|||||||
{props.rows.map((data) => {
|
{props.rows.map((data) => {
|
||||||
const { mult, value, effValue = null, color = props.color } = data;
|
const { mult, value, effValue = null, color = props.color } = data;
|
||||||
|
|
||||||
if (effValue !== null && effValue !== value && Player.sourceFileLvl(5) > 0) {
|
if (effValue !== null && effValue !== value && canAccessBitNodeFeature(5)) {
|
||||||
return (
|
return (
|
||||||
<StatsRow key={mult} name={mult} color={color} data={{}}>
|
<StatsRow key={mult} name={mult} color={color} data={{}}>
|
||||||
<>
|
<>
|
||||||
@ -100,9 +101,9 @@ function MultiplierTable(props: MultTableProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CurrentBitNode(): React.ReactElement {
|
function CurrentBitNode(): React.ReactElement {
|
||||||
if (Player.sourceFiles.size > 0) {
|
if (knowAboutBitverse()) {
|
||||||
const index = "BitNode" + Player.bitNodeN;
|
const index = "BitNode" + Player.bitNodeN;
|
||||||
const lvl = Math.min(Player.sourceFileLvl(Player.bitNodeN) + 1, Player.bitNodeN === 12 ? Infinity : 3);
|
const lvl = Math.min(Player.sourceFileLvl(Player.bitNodeN) + 1, Player.bitNodeN === 12 ? Number.MAX_VALUE : 3);
|
||||||
return (
|
return (
|
||||||
<Paper sx={{ mb: 1, p: 1 }}>
|
<Paper sx={{ mb: 1, p: 1 }}>
|
||||||
<Typography variant="h5">
|
<Typography variant="h5">
|
||||||
@ -194,7 +195,7 @@ function MoneyModal({ open, onClose }: IMoneyModalProps): React.ReactElement {
|
|||||||
{convertMoneySourceTrackerToString(Player.moneySourceA)}
|
{convertMoneySourceTrackerToString(Player.moneySourceA)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
if (Player.sourceFiles.size > 0) {
|
if (knowAboutBitverse()) {
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
{content}
|
{content}
|
||||||
@ -224,7 +225,7 @@ export function CharacterStats(): React.ReactElement {
|
|||||||
const timeRows = [
|
const timeRows = [
|
||||||
["Since last Augmentation installation", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastAug)],
|
["Since last Augmentation installation", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastAug)],
|
||||||
];
|
];
|
||||||
if (Player.sourceFiles.size > 0) {
|
if (knowAboutBitverse()) {
|
||||||
timeRows.push(["Since last Bitnode destroyed", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastBitnode)]);
|
timeRows.push(["Since last Bitnode destroyed", convertTimeMsToTimeElapsedString(Player.playtimeSinceLastBitnode)]);
|
||||||
}
|
}
|
||||||
timeRows.push(["Total", convertTimeMsToTimeElapsedString(Player.totalPlaytime)]);
|
timeRows.push(["Total", convertTimeMsToTimeElapsedString(Player.totalPlaytime)]);
|
||||||
@ -265,13 +266,11 @@ export function CharacterStats(): React.ReactElement {
|
|||||||
data={{ content: `${Player.purchasedServers.length} / ${getPurchaseServerLimit()}` }}
|
data={{ content: `${Player.purchasedServers.length} / ${getPurchaseServerLimit()}` }}
|
||||||
/>
|
/>
|
||||||
<StatsRow
|
<StatsRow
|
||||||
name={`Hacknet ${Player.bitNodeN === 9 || Player.sourceFileLvl(9) > 0 ? "Servers" : "Nodes"} owned`}
|
name={`Hacknet ${canAccessBitNodeFeature(9) ? "Servers" : "Nodes"} owned`}
|
||||||
color={Settings.theme.primary}
|
color={Settings.theme.primary}
|
||||||
data={{
|
data={{
|
||||||
content: `${Player.hacknetNodes.length}${
|
content: `${Player.hacknetNodes.length}${
|
||||||
Player.bitNodeN === 9 || Player.sourceFileLvl(9) > 0
|
canAccessBitNodeFeature(9) ? ` / ${HacknetServerConstants.MaxServers}` : ""
|
||||||
? ` / ${HacknetServerConstants.MaxServers}`
|
|
||||||
: ""
|
|
||||||
}`,
|
}`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -317,7 +316,7 @@ export function CharacterStats(): React.ReactElement {
|
|||||||
color={Settings.theme.cha}
|
color={Settings.theme.cha}
|
||||||
data={{ level: Player.skills.charisma, exp: Player.exp.charisma }}
|
data={{ level: Player.skills.charisma, exp: Player.exp.charisma }}
|
||||||
/>
|
/>
|
||||||
{Player.skills.intelligence > 0 && (Player.bitNodeN === 5 || Player.sourceFileLvl(5) > 0) && (
|
{Player.skills.intelligence > 0 && canAccessBitNodeFeature(5) && (
|
||||||
<StatsRow
|
<StatsRow
|
||||||
name="Intelligence"
|
name="Intelligence"
|
||||||
color={Settings.theme.int}
|
color={Settings.theme.int}
|
||||||
@ -332,7 +331,7 @@ export function CharacterStats(): React.ReactElement {
|
|||||||
<Paper sx={{ p: 1, mb: 1 }}>
|
<Paper sx={{ p: 1, mb: 1 }}>
|
||||||
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
<Typography variant="h5" color="primary" sx={{ display: "flex", alignItems: "center", flexWrap: "wrap" }}>
|
||||||
Multipliers
|
Multipliers
|
||||||
{Player.sourceFileLvl(5) > 0 && (
|
{canAccessBitNodeFeature(5) && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<Typography>
|
||||||
@ -553,15 +552,15 @@ export function CharacterStats(): React.ReactElement {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
color={Settings.theme.primary}
|
color={Settings.theme.primary}
|
||||||
noMargin={!Player.sourceFileLvl(14) && Player.bitNodeN !== 14}
|
noMargin={!canAccessBitNodeFeature(14)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(Player.sourceFileLvl(14) || Player.bitNodeN === 14) && (
|
{canAccessBitNodeFeature(14) && (
|
||||||
<MultiplierTable
|
<MultiplierTable
|
||||||
rows={[
|
rows={[
|
||||||
{
|
{
|
||||||
mult: "IPvGO Node Power bonus",
|
mult: "IPvGO Node Power bonus",
|
||||||
value: Player.sourceFileLvl(14) ? 2 * currentNodeMults.GoPower : currentNodeMults.GoPower,
|
value: Player.activeSourceFileLvl(14) ? 2 * currentNodeMults.GoPower : currentNodeMults.GoPower,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
mult: "IPvGO Max Favor",
|
mult: "IPvGO Max Favor",
|
||||||
@ -590,7 +589,7 @@ export function CharacterStats(): React.ReactElement {
|
|||||||
|
|
||||||
<CurrentBitNode />
|
<CurrentBitNode />
|
||||||
|
|
||||||
{(Player.bitNodeN === 5 || Player.sourceFileLvl(5) > 0) && (
|
{canAccessBitNodeFeature(5) && (
|
||||||
<Paper sx={{ p: 1, mb: 1 }}>
|
<Paper sx={{ p: 1, mb: 1 }}>
|
||||||
<Typography variant="h5">BitNode Multipliers</Typography>
|
<Typography variant="h5">BitNode Multipliers</Typography>
|
||||||
<BitNodeMultipliersDisplay n={Player.bitNodeN} />
|
<BitNodeMultipliersDisplay n={Player.bitNodeN} />
|
||||||
|
@ -118,6 +118,20 @@ export function Val({ name, color }: ValProps): React.ReactElement {
|
|||||||
return clearSubscription;
|
return clearSubscription;
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
|
if (
|
||||||
|
name === "Int" &&
|
||||||
|
Player.bitNodeOptions.intelligenceOverride !== undefined &&
|
||||||
|
Player.bitNodeOptions.intelligenceOverride < Player.skills.intelligence
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Tooltip title={`Intelligence: ${formatSkill(Player.skills.intelligence)}`}>
|
||||||
|
<Typography color={color}>
|
||||||
|
{formatSkill(Player.bitNodeOptions.intelligenceOverride)}
|
||||||
|
<sup>*</sup>
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
return <Typography color={color}>{formattedVals[name]()}</Typography>;
|
return <Typography color={color}>{formattedVals[name]()}</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ export function OptionSwitch({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
control={<Switch checked={value} onChange={handleSwitchChange} />}
|
control={<Switch checked={value} onChange={handleSwitchChange} />}
|
||||||
label={
|
label={
|
||||||
<Tooltip title={<Typography>{tooltip}</Typography>}>
|
<Tooltip title={<Typography component="div">{tooltip}</Typography>}>
|
||||||
<Typography>{text}</Typography>
|
<Typography component="div">{text}</Typography>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -56,6 +56,19 @@ exports[`Check Save File Continuity PlayerSave continuity 1`] = `
|
|||||||
"achievements": [],
|
"achievements": [],
|
||||||
"augmentations": [],
|
"augmentations": [],
|
||||||
"bitNodeN": 1,
|
"bitNodeN": 1,
|
||||||
|
"bitNodeOptions": {
|
||||||
|
"disable4SData": false,
|
||||||
|
"disableBladeburner": false,
|
||||||
|
"disableCorporation": false,
|
||||||
|
"disableGang": false,
|
||||||
|
"disableHacknetServer": false,
|
||||||
|
"disableSleeveExpAndAugmentation": false,
|
||||||
|
"restrictHomePCUpgrade": false,
|
||||||
|
"sourceFileOverrides": {
|
||||||
|
"ctor": "JSONMap",
|
||||||
|
"data": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
"bladeburner": {
|
"bladeburner": {
|
||||||
"ctor": "Bladeburner",
|
"ctor": "Bladeburner",
|
||||||
"data": {
|
"data": {
|
||||||
|
Loading…
Reference in New Issue
Block a user