Merge branch 'dev' into 4125

This commit is contained in:
Conner Tennery 2022-10-04 09:39:12 -07:00 committed by GitHub
commit 40e8e82b6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
485 changed files with 8628 additions and 9817 deletions

@ -1,3 +1,5 @@
# Only bugfix are accepted
# DELETE THIS AFTER READING
# READ CONTRIBUTING.md
@ -5,16 +7,20 @@
# PR title
Formatted as such:
SECTION: FIX #xzyw PLAYER DESCRIPTION
SECTION: PLAYER DESCRIPTION
SECTION is something like "API", "UI", "MISC", "STANEK", "CORPORATION"
FIX #xyzw is the issue number, if any
PLAYER DESCRIPTION is what you'd tell a non-contributor to convey what is changed.
# Linked issues
If your pull request is related to a git issue, please link it in the description using #xyz.
If your PR should close the issue when it is merged in, use `fixes #xyz` or `closes #xyz`. It'll automate the process.
If your PR should close the issue when it is merged in, use `fixes #xyz` or `closes #xyz` like this:
closes #xxxx
closes #yyyy
It'll automate the process.
# Documentation

2
.gitignore vendored

@ -18,3 +18,5 @@ Netburner.txt
# editor files
.vscode
.idea/

@ -8,7 +8,7 @@ Bitburner is a programming-based [incremental game](https://en.wikipedia.org/wik
that revolves around hacking and cyberpunk themes.
The game can be played at https://danielyxie.github.io/bitburner or installed through [Steam](https://store.steampowered.com/app/1812820/Bitburner/).
See the [frequently asked questions](./doc/FAQ.md) for more information . To discuss the game or get help, join the [official discord server](https://discord.gg/TFc3hKD)
See the [frequently asked questions](./doc/FAQ.md) for more information . To discuss the game or get help, join the [official Discord server](https://discord.gg/TFc3hKD).
# Documentation
@ -19,7 +19,7 @@ The [in-game documentation](./markdown/bitburner.md) is generated from the [Type
Anyone is welcome to contribute to the documentation by editing the [source
files](/doc/source) and then making a pull request with your contributions.
For further guidance, please refer to the "As A Documentor" section of
For further guidance, please refer to the "As A Documenter" section of
[CONTRIBUTING](./doc/CONTRIBUTING.md).
# Contribution

178
dist/bitburner.d.ts vendored

@ -96,7 +96,7 @@ export declare interface AutocompleteData {
servers: string[];
scripts: string[];
txts: string[];
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg };
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg | string[] };
}
/**
@ -773,11 +773,16 @@ export declare interface CodingContract {
*
* @param answer - Solution for the contract.
* @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @param host - Hostname of the server containing the contract. Optional. Defaults to current server if not provided.
* @param opts - Optional parameters for configuring function behavior.
* @returns True if the solution was correct, false otherwise. If the returnReward option is configured, then the function will instead return a string. If the contract is successfully solved, the string will contain a description of the contracts reward. Otherwise, it will be an empty string.
*/
attempt(answer: string[] | number, filename: string, host?: string, opts?: CodingAttemptOptions): boolean | string;
attempt(
answer: string | number | any[],
filename: string,
host?: string,
opts?: CodingAttemptOptions,
): boolean | string;
/**
* Get the type of a coding contract.
@ -788,7 +793,7 @@ export declare interface CodingContract {
* (e.g. Find Largest Prime Factor, Total Ways to Sum, etc.)
*
* @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @param host - Hostname of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns Name describing the type of problem posed by the Coding Contract.
*/
getContractType(filename: string, host?: string): string;
@ -801,7 +806,7 @@ export declare interface CodingContract {
* Get the full text description for the problem posed by the Coding Contract.
*
* @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @param host - Hostname of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns Contracts text description.
*/
getDescription(filename: string, host?: string): string;
@ -813,24 +818,24 @@ export declare interface CodingContract {
*
* Get the data associated with the specific Coding Contract.
* Note that this is not the same as the contracts description.
* This is just the data that the contract wants you to act on in order to solve
* This is just the data that the contract wants you to act on in order to solve the contract.
*
* @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns The specified contracts data, data type depends on contract type.;
* @returns The specified contracts data, data type depends on contract type.
*/
getData(filename: string, host?: string): CodingContractData;
/**
* Get the number of attempt remaining.
* Get the number of attempts remaining.
* @remarks
* RAM cost: 2 GB
*
* Get the number of tries remaining on the contract before it self-destructs.
*
* @param filename - Filename of the contract.
* @param host - Host of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns How many attempts are remaining for the contract;
* @param host - Hostname of the server containing the contract. Optional. Defaults to current server if not provided.
* @returns How many attempts are remaining for the contract.
*/
getNumTriesRemaining(filename: string, host?: string): number;
}
@ -1021,7 +1026,7 @@ export declare interface CorporationInfo {
numShares: number;
/** Cooldown until shares can be sold again */
shareSaleCooldown: number;
/** Amount of shares issued */
/** Amount of aqcuirable shares. */
issuedShares: number;
/** Price of the shares */
sharePrice: number;
@ -1183,6 +1188,19 @@ export declare interface EquipmentStats {
hack?: number;
}
/**
* Export order for a material
* @public
*/
export declare interface Export {
/** Division the material is being exported to */
div: string;
/** City the material is being exported to */
loc: string;
/** Amount of material exported */
amt: string;
}
/**
* @public
*/
@ -1195,6 +1213,8 @@ export declare type FilenameOrPID = number | string;
* @public
*/
export declare interface Formulas {
mockServer(): Server;
mockPlayer(): Player;
/** Reputation formulas */
reputation: ReputationFormulas;
/** Skills formulas */
@ -1436,7 +1456,7 @@ export declare interface Gang {
* Ascend the specified Gang Member.
*
* @param memberName - Name of member to ascend.
* @returns Object with info about the ascension results. undefined if ascension did not occur.
* @returns Object with info about the ascension results. Undefined if ascension did not occur.
*/
ascendMember(memberName: string): GangMemberAscension | undefined;
@ -1448,7 +1468,7 @@ export declare interface Gang {
* Get the result of an ascension without ascending.
*
* @param memberName - Name of member.
* @returns Object with info about the ascension results. undefined if ascension is impossible.
* @returns Object with info about the ascension results. Undefined if ascension is impossible.
*/
getAscensionResult(memberName: string): GangMemberAscension | undefined;
@ -1891,7 +1911,7 @@ export declare interface Hacknet {
*
* Returns the number of Hacknet Nodes you own.
*
* @returns number of hacknet nodes.
* @returns Number of hacknet nodes.
*/
numNodes(): number;
@ -1900,7 +1920,7 @@ export declare interface Hacknet {
* @remarks
* RAM cost: 0 GB
*
* @returns maximum number of hacknet nodes.
* @returns Maximum number of hacknet nodes.
*/
maxNumNodes(): number;
@ -1975,13 +1995,13 @@ export declare interface Hacknet {
* So this is equivalent to multiplying the Nodes RAM by 2 n.
*
* Returns true if the Hacknet Nodes RAM is successfully upgraded n times
* or if it is upgraded some positive number of times and the Node reaches it max RAM.
* or if it is upgraded some positive number of times and the Node reaches its max RAM.
*
* Returns false otherwise.
*
* @param index - Index/Identifier of Hacknet Node.
* @param n - Number of times to upgrade RAM. Must be positive. Rounded to nearest integer.
* @returns True if the Hacknet Nodes ram is successfully upgraded, false otherwise.
* @returns True if the Hacknet Nodes RAM is successfully upgraded, false otherwise.
*/
upgradeRam(index: number, n: number): boolean;
@ -2019,7 +2039,7 @@ export declare interface Hacknet {
*
* @param index - Index/Identifier of Hacknet Node.
* @param n - Number of cache levels to purchase. Must be positive. Rounded to nearest integer.
* @returns True if the Hacknet Nodes cores are successfully purchased, false otherwise.
* @returns True if the Hacknet Nodes cache level is successfully upgraded, false otherwise.
*/
upgradeCache(index: number, n: number): boolean;
@ -2047,11 +2067,11 @@ export declare interface Hacknet {
* Returns the cost of upgrading the RAM of the specified Hacknet Node n times.
*
* If an invalid value for n is provided, then this function returns 0.
* If the specified Hacknet Node is already at max level, then Infinity is returned.
* If the specified Hacknet Node already has max RAM, then Infinity is returned.
*
* @param index - Index/Identifier of Hacknet Node.
* @param n - Number of times to upgrade RAM. Must be positive. Rounded to nearest integer.
* @returns Cost of upgrading the specified Hacknet Node's ram.
* @returns Cost of upgrading the specified Hacknet Node's RAM.
*/
getRamUpgradeCost(index: number, n: number): number;
@ -2176,7 +2196,7 @@ export declare interface Hacknet {
* @param upgTarget - Object to which upgrade applies. Required for certain upgrades.
* @param count - Number of upgrades to buy at once. Defaults to 1 if not specified.
* For compatability reasons, upgTarget must be specified, even if it is not used, in order to specify count.
* @returns True if the upgrade is successfully purchased, and false otherwise..
* @returns True if the upgrade is successfully purchased, and false otherwise.
*/
spendHashes(upgName: string, upgTarget?: string, count?: number): boolean;
@ -2538,10 +2558,12 @@ export declare interface Material {
prod: number;
/** Amount of material sold */
sell: number;
/** cost to buy material */
/** Cost to buy material */
cost: number;
/** Sell cost, can be "MP+5" */
sCost: string | number;
/** Export orders */
exp: Export[];
}
/**
@ -3262,6 +3284,32 @@ export declare interface NS {
*/
tail(fn?: FilenameOrPID, host?: string, ...args: (string | number | boolean)[]): void;
/**
* Move a tail window
* @remarks
* RAM cost: 0 GB
*
* Moves a tail window. Coordinates are in screenspace pixels (top left is 0,0)
*
* @param x - x coordinate.
* @param y - y coordinate.
* @param pid - Optional. PID of the script having its tail moved. If omitted, the current script is used.
*/
moveTail(x: number, y: number, pid?: number): void;
/**
* Resize a tail window
* @remarks
* RAM cost: 0 GB
*
* Resize a tail window. Size are in pixel
*
* @param width - width of the window.
* @param height - height of the window.
* @param pid - Optional. PID of the script having its tail resized. If omitted, the current script is used.
*/
resizeTail(width: number, height: number, pid?: number): void;
/**
* Close the tail window of a script.
* @remarks
@ -3433,8 +3481,7 @@ export declare interface NS {
* PID stands for Process ID. The PID is a unique identifier for each script.
* The PID will always be a positive integer.
*
* Running this function with a numThreads argument of 0 will return 0 without running the script.
* However, running this function with a negative numThreads argument will cause a runtime error.
* Running this function with a numThreads argument of 0 or less will cause a runtime error.
*
* @example
* ```ts
@ -3481,7 +3528,7 @@ export declare interface NS {
* PID stands for Process ID. The PID is a unique identifier for each script.
* The PID will always be a positive integer.
*
* Running this function with 0 or a negative numThreads argument will cause a runtime error.
* Running this function with a numThreads argument of 0 or less will cause a runtime error.
*
* @example
* ```ts
@ -3527,6 +3574,8 @@ export declare interface NS {
*
* Because this function immediately terminates the script, it does not have a return value.
*
* Running this function with a numThreads argument of 0 or less will cause a runtime error.
*
* @example
* ```ts
* // NS1:
@ -3636,7 +3685,7 @@ export declare interface NS {
* @remarks
* RAM cost: 0 GB
*/
exit(): void;
exit(): never;
/**
* Copy file between servers.
@ -3660,25 +3709,25 @@ export declare interface NS {
* ```ts
* // NS2:
* //Copies foo.lit from the helios server to the home computer:
* await ns.scp("foo.lit", "home", "helios" );
* ns.scp("foo.lit", "home", "helios" );
*
* //Tries to copy three files from rothman-uni to home computer:
* files = ["foo1.lit", "foo2.script", "foo3.script"];
* await ns.scp(files, "home", "rothman-uni");
* ns.scp(files, "home", "rothman-uni");
* ```
* @example
* ```ts
* //ns2, copies files from home to a target server
* const server = ns.args[0];
* const files = ["hack.js","weaken.js","grow.js"];
* await ns.scp(files, server, "home");
* ns.scp(files, server, "home");
* ```
* @param files - Filename or an array of filenames of script/literature files to copy.
* @param source - Host of the source server, which is the server from which the file will be copied. This argument is optional and if its omitted the source will be the current server.
* @param destination - Host of the destination server, which is the server to which the file will be copied.
* @returns True if the script/literature file is successfully copied over and false otherwise. If the files argument is an array then this function will return true if at least one of the files in the array is successfully copied.
* @param source - Host of the source server, which is the server from which the file will be copied. This argument is optional and if its omitted the source will be the current server.
* @returns True if the file is successfully copied over and false otherwise. If the files argument is an array then this function will return false if any of the operations failed.
*/
scp(files: string | string[], destination: string, source?: string): Promise<boolean>;
scp(files: string | string[], destination: string, source?: string): boolean;
/**
* List files on a server.
@ -4218,20 +4267,20 @@ export declare interface NS {
* @remarks
* RAM cost: 0 GB
*
* This function can be used to write data to a text file (.txt).
* This function can be used to write data to a text file (.txt) or a script (.js or .script).
*
* This function will write data to that text file. If the specified text file does not exist,
* This function will write data to that file. If the specified file does not exist,
* then it will be created. The third argument mode, defines how the data will be written to
* the text file. If *mode is set to w, then the data is written in write mode which means
* that it will overwrite all existing data on the text file. If mode is set to any other value
* the file. If *mode is set to w, then the data is written in write mode which means
* that it will overwrite all existing data on the file. If mode is set to any other value
* then the data will be written in append mode which means that the data will be added at the
* end of the text file.
* end of the file.
*
* @param handle - Filename of the text file that will be written to.
* @param filename - Name of the file to be written to.
* @param data - Data to write.
* @param mode - Defines the write mode. Only valid when writing to text files.
* @param mode - Defines the write mode.
*/
write(handle: string, data?: string[] | number | string, mode?: "w" | "a"): Promise<void>;
write(filename: string, data?: string[] | number | string, mode?: "w" | "a"): void;
/**
* Attempt to write to a port.
@ -4253,15 +4302,15 @@ export declare interface NS {
* @remarks
* RAM cost: 0 GB
*
* This function is used to read data from a text file (.txt).
* This function is used to read data from a text file (.txt) or script (.script, .js).
*
* This function will return the data in the specified text
* file. If the text file does not exist, an empty string will be returned.
* This function will return the data in the specified file.
* If the file does not exist, an empty string will be returned.
*
* @param handle - Filename to read from.
* @param filename - Name of the file to be read.
* @returns Data in the specified text file.
*/
read(handle: string): PortData;
read(filename: string): string;
/**
* Get a copy of the data from a port without popping it.
@ -4778,7 +4827,7 @@ export declare interface NS {
* // {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":true}
* ```
*/
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg };
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg | string[] };
/**
* Share your computer with your factions.
@ -5218,6 +5267,28 @@ export declare interface Server {
* @public
*/
export declare interface Singularity {
/**
* Backup game save.
* @remarks
* RAM cost: 1 GB * 16/4/1
*
*
* This function will automatically opens the backup save prompt and claim the free faction favour if available.
*
*/
exportGame(): void;
/**
* Returns Backup save bonus availability.
* @remarks
* RAM cost: 0.5 GB * 16/4/1
*
*
* This function will check if there is a bonus for backing up your save.
*
*/
exportGameBonus(): boolean;
/**
* Take university class.
*
@ -5236,7 +5307,7 @@ export declare interface Singularity {
* @param universityName - Name of university. You must be in the correct city for whatever university you specify.
* @param courseName - Name of course.
* @param focus - Acquire player focus on this class. Optional. Defaults to true.
* @returns True if actions is successfully started, false otherwise.
* @returns True if action is successfully started, false otherwise.
*/
universityCourse(universityName: string, courseName: string, focus?: boolean): boolean;
@ -5272,7 +5343,7 @@ export declare interface Singularity {
* function is the same as the cost for traveling through the Travel Agency.
*
* @param city - City to travel to.
* @returns True if actions is successful, false otherwise.
* @returns True if action is successful, false otherwise.
*/
travelToCity(city: string): boolean;
@ -5712,7 +5783,7 @@ export declare interface Singularity {
* guarantee that your browser will follow that time limit.
*
* @param crime - Name of crime to attempt.
* @param focus - Acquire player focus on this program creation. Optional. Defaults to true.
* @param focus - Acquire player focus on this crime. Optional. Defaults to true.
* @returns The number of milliseconds it takes to attempt the specified crime.
*/
commitCrime(crime: string, focus?: boolean): number;
@ -6976,6 +7047,15 @@ export declare type ToastVariantValues = `${ToastVariant}`;
* @public
*/
export declare interface UserInterface {
/**
* Get the current window size
* @remarks
* RAM cost: 0 GB
*
* @returns An array of 2 value containing the window width and height.
*/
windowSize(): [number, number];
/**
* Get the current theme
* @remarks

6
dist/main.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

42
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,24 +1,26 @@
# Only bugfix are accepted
# Contributing to Bitburner
## In General
The game is made better because the community as a whole speaks up about
ways to improve the game. Here's some of the ways you can make your voice
ways to improve the game. Here are some of the ways you can make your voice
heard:
- [Discord](https://discord.gg/XKEGvHqVr3)
- [Discord](https://discord.gg/XKEGvHqVr3).
There is a dedicated Discord instance set up for more free-form chats
between all members of the community. Regular players, heavy scripters,
Bitburner contributors, and everyone in between can be found on the
server.
- [Github Issues](https://github.com/danielyxie/bitburner/issues)
- [Github Issues](https://github.com/danielyxie/bitburner/issues).
Although the term "issues" can have a negative connotation, they are a
means of communicating with the community. A new Issue can be a
means of communicating with the community. A new Issue can be an
interesting new feature that you feel would improve the game. It could be
an unexpected behavior within the game. Or because the game is about
scripting perhaps there is something that is conflicting with the
browser's Javascript interaction. So please do not be afraid to open a
[new issue](https://github.com/danielyxie/bitburner/issues/new).
browser's JavaScript interaction. So please do not be afraid to open a
[new Issue](https://github.com/danielyxie/bitburner/issues/new).
## Reporting Bugs
@ -33,16 +35,17 @@ already been reported as an [Issue](https://github.com/danielyxie/bitburner/issu
#### How to Submit a Good Bug Report
- **Use a clear and descriptive title** for the issue
- **State your browser, your browser's version, and your computer's OS**
- **Attach your save file**, if you think it would help solve the issue
- **Use a clear and descriptive title** for the Issue.
- **State your browser, your browser's version, and your computer's OS.**
- **Attach your save file**, if you think it would help solve the Issue.
Zip your save file first, then attach the zipped save file.
- **Provide instructions on how to reproduce the bug** in as much detail
as possible. If you cannot reliably reproduce the bug, then just try
your best to explain what was happening when the bug occurred
- **Provide any scripts** that triggered the bug if the issue is Netscript-related
your best to explain what was happening when the bug occurred.
- **Provide any scripts** that triggered the bug if the Issue is Netscript-related.
- **Open your browser's Dev Console and report any error-related output**
that may be printed there. The Dev Console can be opened on most modern
browsers by pressing F12
browsers by pressing F12.
## As a Developer
@ -71,13 +74,13 @@ changes are okay to contribute:
##### Contributions that Will Most Likely Be Accepted
- Bug Fixes
- Quality-of-Life Changes
- Bug fixes
- Quality-of-life changes
- Adding a new, commonly-requested Netscript function
- Fixing or improving UI elements
- Adding game settings/options
- Adding a new Terminal command
- Code Refactors that conform to good/standard practices
- Code refactors that conform to good/standard practices
##### Contributions that will not be Accepted without prior approval
@ -88,67 +91,108 @@ changes are okay to contribute:
## How to setup fork properly
Fork and clone the repo
Clone and fork the game's repository by using one of these methods: web browser, GitHub
Desktop, or command line.
```
# This will add the game original code as a repo in your local copy
$ git remote add danielyxie git@github.com:danielyxie/bitburner.git
- Web browser. Log in to your GitHub account, navigate to the
[game's repository](https://github.com/danielyxie/bitburner), and fork the
repository. Refer to
[this page](https://docs.github.com/en/get-started/quickstart/fork-a-repo) for more
detail.
- GitHub Desktop. Click on `File`, then click `Clone repository`. Click on the `URL`
tab and type `danielyxie/bitburner` into the text box for repository URL. Choose
the path where you want to clone the repository and click the `Clone` button.
Refer to [this page](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/adding-and-cloning-repositories/cloning-and-forking-repositories-from-github-desktop)
for more detail.
- Command line.
# You can verify you did this right by doing the following command
$ git remote show
danielyxie
origin
```sh
# This clones the game's code repository. The output you get might vary.
$ git clone https://github.com/danielyxie/bitburner.git
Cloning into 'bitburner'...
remote: Enumerating objects: 57072, done.
remote: Counting objects: 100% (404/404), done.
remote: Compressing objects: 100% (205/205), done.
remote: Total 57072 (delta 210), reused 375 (delta 199), pack-reused 56668
Receiving objects: 100% (57072/57072), 339.11 MiB | 5.42 MiB/s, done.
Resolving deltas: 100% (43708/43708), done.
Updating files: 100% (2561/2561), done.
# Then download all the branches from the game. (there might be more branches)
$ git fetch danielyxie
From github.com:danielyxie/bitburner
* [new branch] dev -> danielyxie/dev
* [new branch] master -> danielyxie/master
# Change to the directory that contains your local copy.
$ cd bitburner
# Makes sure you always start from `danielyxie/dev` to avoid merge conflicts.
# The upstream is the repository that contains the game's source code. The
# upstream is also the place where proposed changes are merged into the game.
$ git remote rename origin upstream
Renaming remote references: 100% (8/8), done.
# The origin is your own copy or fork of the game's source code. Assume that
# your fork will be on GitHub. Change "myname" to your GitHub username. Change
# "myfork" to the name of your forked repository.
$ git remote add origin https://github.com/myname/myfork
# Now "origin" is your fork and "upstream" is where changes should be merged.
$ git remote show
origin
upstream
# You can now download all changes and branches from the upstream repository.
# The output you get might vary.
$ git fetch upstream
# Make sure you always start from "upstream/dev" to avoid merge conflicts.
$ git branch
* dev
$ git branch -r
upstream/BN14
upstream/HEAD -> upstream/dev
upstream/dev
upstream/folders
upstream/master
```
## Development Workflow Best Practices
- Work in a new branch forked from the `dev` branch to isolate your new code
- Work in a new branch forked from the `dev` branch to isolate your new code.
- Keep code-changes on a branch as small as possible. This makes it easier for code review. Each branch should be its own independent feature.
- Regularly rebase your branch against `dev` to make sure you have the latest updates pulled.
- When merging, always merge your branch into `dev`. When releasing a new update, then merge `dev` into `master`
- When merging, always merge your branch into `dev`. When releasing a new update, merge `dev` into `master`.
## Running locally.
## Running locally
Install
- `npm` (maybe via `nvm`)
- Github Desktop (windows only)
- Visual Studio code (optional)
- Github Desktop (Windows only)
- Visual Studio Code (optional)
Inside the root of the repo run
`npm install` to install all the dependencies
`npm run start:dev` to launch the game in dev mode.
Inside the root of the repository run:
- `npm install` to install all the dependencies; and
- `npm run start:dev` to launch the game in dev mode.
After that you can open any browser and navigate to `localhost:8000` and play the game.
Saving a file will reload the game automatically.
### How to build the electron app
Tested on Node v16.13.1 (LTS) on Windows
These steps only work in a bash-like environment, like MinGW for Windows.
Tested on Node v16.13.1 (LTS) on Windows.
These steps only work in a Bash-like environment, like MinGW for Windows.
```sh
# Install the main game dependencies & build the app in debug mode
npm install
npm run build:dev
# Install the main game dependencies & build the app in debug mode.
$ npm install
$ npm run build:dev
# Use electron-packager to build the app to the .build/ folder
npm run electron
# Use electron-packager to build the app to the .build/ folder.
$ npm run electron
# When launching the .exe directly, you'll need the steam_appid.txt file in the root
# If not using windows, change this line accordingly
cp .build/bitburner-win32-x64/resources/app/steam_appid.txt .build/bitburner-win32-x64/steam_appid.txt
# When launching the .exe directly, you'll need the steam_appid.txt file in the root.
# If not using Windows, change this line accordingly.
$ cp .build/bitburner-win32-x64/resources/app/steam_appid.txt .build/bitburner-win32-x64/steam_appid.txt
# And run the game...
.build/bitburner-win32-x64/bitburner.exe
$ .build/bitburner-win32-x64/bitburner.exe
```
### Submitting a Pull Request
@ -156,15 +200,15 @@ cp .build/bitburner-win32-x64/resources/app/steam_appid.txt .build/bitburner-win
When submitting a pull request with your code contributions, please abide by
the following rules:
- Work in a branch forked from `dev` to isolate the new code
- Ensure you have latest from the [game's main
repository](danielyxie/bitburner@dev)
- Rebase your branch if necessary
- Run the game locally to test out your changes
- Work in a branch forked from `dev` to isolate the new code.
- Ensure you have the latest from the [game's main
repository](../../../tree/dev).
- Rebase your branch if necessary.
- Run the game locally to test out your changes.
- When submitting the pull request, make sure that the base fork is
_danielyxie/bitburner_ and the base is _dev_.
- If your changes affect the game's UI, attach some screenshots or GIFs showing
the changes to the UI
the changes to the UI.
- If your changes affect Netscript, provide some
scripts that can be used to test the Netscript changes.
- Ensure you have run `npm run lint` to make sure your changes conform to the
@ -174,23 +218,23 @@ the following rules:
in the root of the repository. These will be updated as part of official
releases.
## As a Documentor
## As a Documenter
To contribute to and view your changes to the BitBurner documentation on [Read The
Docs](http://bitburner.readthedocs.io/), you will
need to have Python installed, along with [Sphinx](http://www.sphinx-doc.org).
To make change to the [in-game documentation](../markdown/bitburner.md), you will need to modify the [TypeScript definitions](../src/ScriptEditor/NetscriptDefinitions.d.ts), not the markdown files.
To make change to the [in-game documentation](../markdown/bitburner.md), you will need to modify the [TypeScript definitions](../src/ScriptEditor/NetscriptDefinitions.d.ts), not the Markdown files.
We are using [API Extractor](https://api-extractor.com/pages/tsdoc/doc_comment_syntax/) (tsdoc hints) to generate the markdown doc. Make your changes to the TypeScript definitions and then run `npm run doc`.
We are using [API Extractor](https://api-extractor.com/pages/tsdoc/doc_comment_syntax/) (tsdoc hints) to generate the Markdown doc. Make your changes to the TypeScript definitions and then run `npm run doc`.
Before submitting your code for a pull request, please try to follow these
rules:
- Work in a branch forked from `dev` to isolate the new code
- Ensure you have latest from the [game's main
repository](danielyxie/bitburner@dev)
- Rebase your branch if necessary
- Work in a branch forked from `dev` to isolate the new code.
- Ensure you have the latest from the [game's main
repository](../../../tree/dev).
- Rebase your branch if necessary.
- When submitting the pull request, make sure that the base fork is
_danielyxie/bitburner_ and the base is _dev_.
- Do not check in any generated files under `doc\`. The documentation is built
@ -204,5 +248,5 @@ Update the following
- `package.json` `version`
- `doc/source/conf.py` `version` and `release`
- `doc/source/changelog.rst`
- post to discord
- post to Discord
- post to reddit.com/r/Bitburner

@ -10,8 +10,9 @@ user's physical and mental faculties.
Augmentations provide persistent upgrades in the form of multipliers.
These multipliers apply to a wide variety of things such as stats,
experience gain, and hacking, just to name a few. Your multipliers
can be viewed in the 'Character' page (:ref:`keyboard shortcut <shortcuts>` Alt + c).
experience gain, and hacking, just to name a few. The effects of
Autmentations stack multiplicatively. Your multiplierscan be viewed in
the 'Character' page (:ref:`keyboard shortcut <shortcuts>` Alt + c).
How to acquire Augmentations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -75,4 +76,6 @@ will cause the Augmentations to get progressively more expensive. When
you purchase an Augmentation, the price of purchasing another Augmentation
doubles. This multiplier stacks for each Augmentation you
purchase. Once you install your purchased Augmentations, their costs
are reset back to the original prices.
are reset back to the original prices. You can only purchase each augmentation
once, with the exception of NeuroFlux Governor, which can be purchased infinitely
at increasing cost.

@ -32,6 +32,18 @@ an area to provide an answer.
Interacting through Scripts
^^^^^^^^^^^^^^^^^^^^^^^^^^^
See the `Coding Contract API <https://github.com/danielyxie/bitburner/blob/dev/markdown/bitburner.codingcontract.md>`_.
Interacting with Coding Contracts via the Terminal can be tedious the more
contracts you solve. Consider using the API to automate various aspects of
your solution. For example, some contracts have long solutions while others
have even longer solutions. You might want to use the API to automate the
process of submitting your solution rather than copy and paste a long
solution into an answer box.
However, using the API comes at a cost. Like most functions in other APIs,
each function in the Coding Contract API has a RAM cost. Depending on which
function you use, the initial RAM on your home server might not be enough
to allow you to use various API functions. Plan on upgrading the RAM on your
home server if you want to use the Coding Contract API.
Submitting Solutions
^^^^^^^^^^^^^^^^^^^^
@ -374,7 +386,7 @@ The list contains the name of (i.e. the value returned by
| | | substitution cipher in which each letter in the plaintext is replaced by a letter some |
| | | fixed number of positions down the alphabet. For example, with a left shift of 3, D |
| | | would be replaced by A, E would become B, and A would become X (because of rotation). |
| | | You are given an array with two elements.The first element is the plaintext, the |
| | | You are given an array with two elements. The first element is the plaintext, the |
| | | second element is the left shift value. Return the ciphertext as uppercase string. |
| | | Spaces remains the same. |
+-----------------------------------------+------------------------------------------------------------------------------------------+

@ -11,12 +11,3 @@ While working for a company, you can click "Do something else simultaneously" to
to do things while you continue to work in the background. There is a 20% penalty to the
related gains. Clicking the "Focus" button under the overview will return you to the
current work.
Reputation is required to apply for a promotion. This reputation is not counted towards
your career until the shift ends, either due to the time spent or clicking the
"Stop Working" button. For most jobs there is a penalty of 50% of the reputation gained
if you stop your shift early.
Information about all Companies
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TODO

@ -13,7 +13,12 @@ Basic Mechanics
When you visit the 'Slums' you will see a list of buttons that show all of the
available crimes. Simply select one of the options to begin attempting that
crime. Attempting to commit a crime takes a certain amount of time. This time
varies between crimes. During this time, you cannot do anything else.
varies between crimes.
While doing criemes, you can click “Do something else simultaneously”
to be able to do things while you continue to do crimes in the background. There is a
20% penalty to the related gains. Clicking the “Focus” button under the overview
will return you to the current task.
Crimes are not always successful. Your rate of success is determined by your
stats (and Augmentation multipliers) and can be seen on the crime-selection
@ -27,15 +32,27 @@ Harder crimes are typically more profitable, and also give more EXP.
Crime details
^^^^^^^^^^^^^
Available crimes, and their descriptions, which all begin with "attempt to..."
Shoplift …shoplift from a low-end retailer
Rob store …commit armed robbery on a high-end store
Mug someone …mug a random person on the street
Larceny …rob property from someone's house
Deal Drugs …deal drugs
Bond Forgery …forge corporate bonds
Traffick illegal Arms …smuggle illegal arms into the city
Homicide …murder a random person on the street
Grand theft Auto …commit grand theft auto
Kidnap and Ransom …kidnap and ransom a high-profile-target
Assassinate …assassinate a high-profile target
Heist …pull off the ultimate heist

@ -39,21 +39,23 @@ List of Factions and their Requirements
.. _gameplay_factions::
+---------------------+----------------+-----------------------------------------+-------------------------------+
| Early Game | Faction Name | Requirements | Joining this Faction prevents |
| Factions | | | you from joining: |
+ +----------------+-----------------------------------------+-------------------------------+
| | CyberSec | * Install a backdoor on the CSEC server | |
+ +----------------+-----------------------------------------+-------------------------------+
| | Tian Di Hui | * $1m | |
| | | * Hacking Level 50 | |
| | | * Be in Chongqing, New Tokyo, or Ishima | |
+ +----------------+-----------------------------------------+-------------------------------+
| | Netburners | * Hacking Level 80 | |
| | | * Total Hacknet Levels of 100 | |
| | | * Total Hacknet RAM of 8 | |
| | | * Total Hacknet Cores of 4 | |
+---------------------+----------------+-----------------------------------------+-------------------------------+
+---------------------+--------------------+-----------------------------------------+-------------------------------+
| Early Game | Faction Name | Requirements | Joining this Faction prevents |
| Factions | | | you from joining: |
+ +--------------------+-----------------------------------------+-------------------------------+
| | CyberSec | * Install a backdoor on the CSEC server | |
+ +--------------------+-----------------------------------------+-------------------------------+
| | Tian Di Hui | * $1m | |
| | | * Hacking Level 50 | |
| | | * Be in Chongqing, New Tokyo, or Ishima | |
+ +--------------------+-----------------------------------------+-------------------------------+
| | Netburners | * Hacking Level 80 | |
| | | * Total Hacknet Levels of 100 | |
| | | * Total Hacknet RAM of 8 | |
| | | * Total Hacknet Cores of 4 | |
+ +--------------------+-----------------------------------------+-------------------------------+
| | Shadows of Anarchy | * Successfully infiltrate a company | |
+---------------------+--------------------+-----------------------------------------+-------------------------------+
.. raw:: html
</details>
@ -127,34 +129,34 @@ List of Factions and their Requirements
| Megacorporations | Faction Name | Requirements | Joining this Faction prevents |
| | | | you from joining: |
+ +----------------+-----------------------------------------+-------------------------------+
| | ECorp | * Have 200k reputation with | |
| | ECorp | * Have 400k reputation with | |
| | | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | MegaCorp | * Have 200k reputation with | |
| | MegaCorp | * Have 400k reputation with | |
| | | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | KuaiGong | * Have 200k reputation with | |
| | KuaiGong | * Have 400k reputation with | |
| | International | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | Four Sigma | * Have 200k reputation with | |
| | Four Sigma | * Have 400k reputation with | |
| | | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | NWO | * Have 200k reputation with | |
| | NWO | * Have 400k reputation with | |
| | | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | Blade | * Have 200k reputation with | |
| | Blade | * Have 400k reputation with | |
| | Industries | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | OmniTek | * Have 200k reputation with | |
| | OmniTek | * Have 400k reputation with | |
| | Incorporated | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | Bachman & | * Have 200k reputation with | |
| | Bachman & | * Have 400k reputation with | |
| | Associates | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | Clarke | * Have 200k reputation with | |
| | Clarke | * Have 400k reputation with | |
| | Incorporated | the Corporation | |
+ +----------------+-----------------------------------------+-------------------------------+
| | Fulcrum Secret | * Have 250k reputation with | |
| | Fulcrum Secret | * Have 400k reputation with | |
| | Technologies | the Corporation | |
| | | * Install a backdoor on the | |
| | | fulcrumassets server | |

@ -45,7 +45,7 @@ files.
Absolute vs Relative Paths
^^^^^^^^^^^^^^^^^^^^^^^^^^
Many Terminal commands accept absolute both absolute and relative paths for specifying a
Many Terminal commands accept both absolute and relative paths for specifying a
file.
An absolute path specifies the location of the file from the root directory (/).

@ -3,6 +3,162 @@
Changelog
=========
v2.1.0 - 2022-09-23 Remote File API
-----------------------------------
Dev note
* The most important change about this update is the introduction of the Remote File API (RFA).
With this we also deprecate the HTTP file API and the Visual Studio extension. Those things
were made during the rush of Steam and aren't well thought out. This new process works with
both the web and Steam version of the game and every text editor. Moving forward we also
won't be doing much, if any, upgrades to the in-game editor. We think it's good enough for
now and if you need more we recommend you hook up your favorite external editor.
--- NEW FEATURES ---
* New Remote File API for transmitting files to the game (by @Hoekstraa)
* Added a new Augmentation, Z.O.Ë., which allows Sleeves to benefit from Stanek.
--- FIXES ---
API
* Remove incorrectly placed 's' in ns.tFormat() (by @LJNeon)
* More ports (previously max 20, now practically unlimited) (by @Hoekstraa)
* Corp functions now return copy of constant arrays instead of the original (by @Mughur)
* All the player sub-objects need to be copied for `getPlayer`. (by @MageKing17)
* add corp get<constant> functions, UI (by @Mughur)
* [danielyxie/bitburner#3860] destroyW0r1dD43m0n now properly gives achievements
* [danielyxie/bitburner#3890] favor now properly syncs across pages and the Donate achievement is now given correctly (by @Aerophia)
* getCrimeStats use bitnode multipliers in the output of crime stats (by @phyzical)
* add singularity function for exporting game save back (by @phyzical)
CODING CONTRACTS
* inconsistent probability for generation between online and offline (by @quacksouls)
* Don't stringify answer if already a string (by @alainbryden)
* [danielyxie/bitburner#3755] change input handling for contract attempts (by @Snarling)
CORPORATION
* [danielyxie/bitburner#3880], [danielyxie/bitburner#3876], [danielyxie/bitburner#3322], [danielyxie/bitburner#3138] Bunch of corporation fixes (by @Mughur)
* Gave investors some economics classes (by @Mughur)
* Limit shareholder priority on newly issued shares (by @Undeemiss)
* dont take research points for something already researched via api (by @phyzical)
CORPORATION API
* Fix up param order for limitProductProduction to match docs (by @phyzical)
* [danielyxie/bitburner#3655] Expose exports from Material (by @Rasmoh)
DOCUMENTATION
* update docs a bit more, amending some BN and SF texts (by @Mughur)
* Fixed Argument order for scp() (by @njalooo)
* Some typo fixes in Netscript functions (by @quacksouls)
* [danielyxie/bitburner#4033] Why use Coding Contract API (by @quacksouls)
* typo fix in description of Caesar cipher (by @quacksouls)
* typo fix in terminal.rst (by @BugiDev)
* Update bitburner.sleeve.settobladeburneraction.md (by @borisflagell)
* Correct documentation for `run()` with 0 threads. (by @MageKing17)
* Some doc updates (by @Mughur)
* fix documentation for remote api (by @hydroflame)
NETSCRIPT
* Added functions to resize, move, and close tail windows
* [danielyxie/bitburner#2376] ns.exit now exits immediately (by @Snarling)
* [danielyxie/bitburner#4055] Fix dynamic ram check (by @Snarling)
* [danielyxie/bitburner#4037] ns1 wraps deeper layers correctly. (by @Snarling)
* [danielyxie/bitburner#3963] Prevent bladeburner.setActionLevel from setting invalid action levels (by @MPJ-K)
* Typo fixes in CodingContract, Hacknet, Singularity APIs (by @quacksouls)
* Fix a typo in doc of Singularity.travelToCity() (by @quacksouls)
* Update netscript definition file for scp, write, read, and flags (by @Snarling)
* Correct missing ! for boolean coercion in Corporation.createCorporation(). (by @Risenafis)
* Normalized Stock API logging (by @Snarling)
* [danielyxie/bitburner#3992] allow null duration in toast ns function (by @RollerKnobster)
* Correct missing `!` for boolean coercion in `singularity.workForCompany()`. (by @MageKing17)
* ns.scp and ns.write are now synchronous + fix exec race condition (by @Snarling)
* [danielyxie/bitburner#2931] atExit now allows synchronous ns functions (by @Snarling)
* Improve real life CPU and memory performance of scripts. (by @Snarling)
* Prompt Add user friendly message to avoid throwing recovery screen for invalid choices (by @phyzical)
* [danielyxie/bitburner#4081] Rerunning a script from tail window recalculates ram usage (by @Snarling)
* [danielyxie/bitburner#3962] The correct script will be closed even if the player modifies args (v2.0) (by @Snarling)
* Corrected ns formula for infiltration rewards (by @ezylot)
* Add singularity check for finishing company work (by @Snarling)
SLEEVES
* [danielyxie/bitburner#3819] Allow using the regeneration chamber with sleeves to heal them. (by @coderanger)
* [danielyxie/bitburner#4063] fix crash when player tries to assign more than 3 sleeves to Bladeburner contracts (by @Snarling)
* [danielyxie/bitburner#4051] Sleeves no longer crash when player quits company sleeve was working (by @Snarling)
* [danielyxie/bitburner#4022], [danielyxie/bitburner#4024], [danielyxie/bitburner#4025], [danielyxie/bitburner#3998] (by @Mughur)
* Sleeve crime gain bitnode multiplier fix (by @Mughur)
REMOTE FILE API
* NetscriptDefinitions retains export strings (by @Hoekstraa)
* Fix type of RFAMessages with non-String results (by @Hoekstraa)
UI
* [danielyxie/bitburner#2962] add a setting to display middle time unit in Time Elapsed String (by @hydroflame)
* [danielyxie/bitburner#4106] fix incorrect experience display in Crime UI. (by @SilverNexus)
* Bitnode stats now show if BB/Corporation are disabled (by @Kelenius)
* Removed three empty lines from BB status screen (by @Kelenius)
* Add missing space to BN7 description (by @hex7cd)
* Improvements to crime work UI (by @Kelenius)
* [danielyxie/bitburner#3975], [danielyxie/bitburner#3882] Script Editor more responsive on resize, and fix dirty file indicator (by @Snarling)
MISC
* Added weight to GangMemberTask construction call (by @ezylot)
* Fix ANSI display bugs (by @Snarling)
* Debounce updateRAM calls in script editor. (by @Snarling)
* [danielyxie/bitburner#3979] Allow characters & and ' in filenames (by @Snarling)
* [danielyxie/bitburner#3965] Corrected tutorial text (by @mihilt)
* Fix infil definitions.d.ts (by @phyzical)
* Modify PR template (by @Hoekstraa)
* crime gains, sleeve gang augs and faq (by @Mughur)
* [danielyxie/bitburner#3649] Preventing server starting security level from going above 100 (by @Shiiyu)
* Adds Shadows of Anarchy (by @Lagicrus)
* Added intormation about hacking managers to hacking algorithms page (by @Kelenius)
* Fix Jest CI Error (by @geggleto)
* multiple hasAugmentation checks didn't check if the augment was installed (by @Mughur)
* [danielyxie/bitburner#2442] & [danielyxie/bitburner#2795] (by @G4mingJon4s)
* Adds info regarding augments and focus (by @Lagicrus)
* Removed console.log line (by @dhosborne)
* Update some doc (by @hydroflame)
* trying to fix int problems (by @hydroflame)
* Fix broken ns filesnames (by @hydroflame)
* new formula functions (by @hydroflame)
* test fixes/md updates (by @phyzical)
* Remove "based" from positive adjectives in infiltrations (by @faangbait)
* minor fix in instance calculation (by @hydroflame)
* fix dynamic ram miscalc not triggering (by @hydroflame)
* Refactor game options into separate components (by @hydroflame)
* fix settings unfocusing on every key stroke (by @hydroflame)
* fix some stuff with the timestamp settings (by @hydroflame)
* Fix unique key problem with ascii elements (by @hydroflame)
* Improve wrong arg user message and add ui.windowSize (by @hydroflame)
* fix stack trace missing in some errors (by @hydroflame)
* Fix scp and write in ns1 (by @hydroflame)
* Did some changes of the remote api and added documentation (by @hydroflame)
* Add dummy function to generate a mock server or player for formulas stuff (by @hydroflame)
* fix compile error (by @hydroflame)
* regen doc (by @hydroflame)
* rm console log (by @hydroflame)
* regen doc (by @hydroflame)
* Added more info about blood program, change some aug descriptions (by @hydroflame)
* use triple equal (by @hydroflame)
* Minor improvements to Netscript Port loading and unloading (by @hydroflame)
* Fix hostname generation being weird about dash 0 added (by @hydroflame)
* upgrade version number. (by @hydroflame)
* Nerf noodle bar.
v2.0.0 - 2022-07-19 Work rework
-------------------------------

@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
# built documents.
#
# The short X.Y version.
version = '2.0'
version = '2.1'
# The full version, including alpha/beta/rc tags.
release = '2.0.0'
release = '2.1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

@ -19,6 +19,53 @@ Then, to fix your script, make sure you have a sleep or any other timed function
await ns.sleep(1000); // Add a 1s sleep to prevent freezing
}
Also make sure that each while loop gets to `awaited` function or `break`, for example the next snippet has a sleep
function, but it nor any possible conditional breaks are never reached and therefore will crash the game::
while(true) {
let currentMoney = ns.getServerMoneyAvailable("n00dles");
let maxMoney = ns.getServerMaxMoney("n00dles");
if (currentMoney < maxMoney/2){
await ns.grow("n00dles");
}
if (currentMoney === maxMoney){
break;
}
}
If `n00dles` current money is, for example, 75% of the maximum money, the script will not reach neither `grow` nor `break` and crashes the game.
Adding a sleep like in the first example, or changing the code so that `awaited` function or `break` is always reached, would prevent the crash.
Common infinite loop when translating the server purchasing script in starting guide to :ref:`netscriptjs` is to have a
while loop, that's condition's change is conditional::
var ram = 8;
var i = 0;
while (i < ns.getPurchasedServerLimit()) {
if (ns.getServerMoneyAvailable("home") > ns.getPurchasedServerCost(ram)) {
var hostname = ns.purchaseServer("pserv-" + i, ram);
ns.scp("early-hack-template.script", hostname);
ns.exec("early-hack-template.script", hostname, 3);
++i;
}
}
if player does not currently have enough money to purchase a server, the `if`'s condition will be false and `++i` will not be reached.
Since the script doesn't have `sleep` and value `i` will not change without the `if` being true, this will crash the game. Adding a `sleep`
that is always reached would prevent the crash.
Blackscreen
-----------
If the game window becomes a black screen without the game itself crashing, this is caused by
the game running too many concurrent scripts (the game runs on a browser and each tab can only
use so much ram until it crashes). Depending on which scripts are running and your hardware,
this number can vary between 50000 to 100000 instances (in version 2.0.2. In prior versions this number
was about 1/5th of that). To prevent this from happening make sure to :ref:`multithread<gameplay_scripts_multithreadingscripts>`
the scripts as much as possible.
Bug
---

@ -860,6 +860,8 @@ Random Tips
* At this stage in the game, your combat stats (strength, defense, etc.) are not nearly
as useful as your hacking stat. Do not invest too much time or money into gaining combat
stat exp.
* As a rule of thumb, your hacking target should be the server with highest max money that's
required hacking level is under 1/3 of your hacking level.

@ -15,6 +15,7 @@ BitNode-1: Source Genesis
Description
The first BitNode created by the Enders to imprison the minds of humans. It became
the prototype and testing-grounds for all of the BitNodes that followed.
This is the first BitNode that you play through. It has no special
modifications or mechanics.
@ -34,6 +35,8 @@ Difficulty
BitNode-2: Rise of the Underworld
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Description
From the shadows, they rose.
Organized crime groups quickly filled the void of power left behind from the collapse of
Western government in the 2050s. As society and civlization broke down, people quickly
succumbed to the innate human impulse of evil and savagery. The organized crime
@ -47,10 +50,12 @@ Description
* Certain Factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead,
NiteSec, The Black Hand) give the player the ability to form and manage their own gangs. These gangs
will earn the player money and reputation with the corresponding Faction
* Every Augmentation in the game will be available through the Factions listed above
* Every Augmentation* in the game will be available through the Factions listed above
* For every Faction NOT listed above, reputation gains are halved
* You will no longer gain passive reputation with Factions
(* except Neuroflux Governor, The Red Pill and augments of secret factions)
Source-File
:Max Level: 3
@ -71,15 +76,17 @@ BitNode-3: Corporatocracy
Description
Our greatest illusion is that a healthy society can revolve around a
single-minded pursuit of wealth.
Sometime in the early 21st century economic and political globalization turned
the world into a corporatocracy, and it never looked back. Now, the privileged
elite will happily bankrupt their own countrymen, decimate their own community,
and evict their neighbors from houses in their desperate bid to increase their wealth.
In this BitNode you can create and manage your own corporation. Running a successful corporation
has the potential of generating massive profits. All other forms of income are reduced by 75%. Furthermore:
* The price and reputation cost of all Augmentations is tripled
* The starting and maximum amount of money on servers is reduced by 75%
* The starting and maximum amount of money on servers is reduced by 80%
* Server growth rate is reduced by 80%
* You now only need 75 favour with a faction in order to donate to it, rather than 150
@ -87,7 +94,7 @@ Source-File
:Max Level: 3
This Source-File lets you create corporations on other BitNodes (although
some BitNodes will disable this mechanic). This Source-File also increases your
some BitNodes will disable this mechanic) and level 3 permanently unlocks the full API. This Source-File also increases your
charisma and company salary multipliers by:
* Level 1: 8%
@ -117,7 +124,11 @@ Source-File
:Max Level: 3
This Source-File lets you access and use the Singularity Functions in other BitNodes.
Each level of this Source-File will reduce RAM costs.
Each level of this Source-File will reduce RAM costs:
* Level 1: 16x
* Level 2: 4x
* Level 3: 1x
Difficulty:
Depending on what Source-Files you have unlocked before attempting this BitNode,
@ -134,11 +145,11 @@ Description
In this BitNode:
* The base security level of servers is doubled
* The starting money on servers is halved, but the maximum money remains the same
* The starting money on servers is halved, but the maximum money is doubled
* Most methods of earning money now give significantly less
* Infiltration gives 50% more reputation and money
* Corporations have 50% lower valuations and are therefore less profitable
* Augmentations are more expensive
* Augmentation price is doubled
* Hacking experience gain rates are reduced
Source-File
@ -176,7 +187,7 @@ Description
* Hacking and Hacknet Nodes will be less profitable
* Your hacking level is reduced by 65%
* Hacking experience gain from scripts is reduced by 75%
* Hacking experience gain is reduced by 75%
* Corporations have 80% lower valuations and are therefore less profitable
* Working for companies is 50% less profitable
* Crimes and Infiltration are 25% less profitable
@ -215,7 +226,7 @@ Description
* Augmentations are 3x more expensive
* Hacking and Hacknet Nodes will be significantly less profitable
* Your hacking level is reduced by 65%
* Hacking experience gain from scripts is reduced by 75%
* Hacking experience gain is reduced by 75%
* Corporations have 80% lower valuations and are therefore less profitable
* Working for companies is 50% less profitable
* Crimes and Infiltration are 25% less profitable
@ -295,6 +306,12 @@ Source-File
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT
when installing Augmentations.)
This Source-File also increases your hacknet multipliers by:
* Level 1: 8%
* Level 2: 12%
* Level 3: 14%
Difficulty
Hard
@ -307,11 +324,13 @@ Description
for the human consciousness. Mankind had finally achieved immortality - at least for those
that could afford it.
This BitNode unlocks Sleeve technology. Sleeve technology allows you to:
This BitNode unlocks Sleeve and grafting technologies. Sleeve technology allows you to:
1. Re-sleeve: Purchase and transfer your consciousness into a new body
2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously
Grafting technology allows you to graft Augmentations, which is an alternative way of installing Augmentations.
In this BitNode:
* Your stats are significantly decreased
@ -322,7 +341,7 @@ Description
Source-File
:Max Level: 3
This Source-File unlocks Sleeve technology in other BitNodes.
This Source-File unlocks Sleeve and grafting technologies in other BitNodes.
Each level of this Source-File also grants you a Duplicate Sleeve.
Difficulty
@ -334,18 +353,19 @@ Description
The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period
of disorder that eventually lead to the governmental reformation of many global superpowers, most notably
the USA and China. But just as the world was slowly beginning to recover from these dark times, financial catastrophe hit.
In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of this chaos and confusion, hackers
were able to steal billions of dollars from the world's largest electronic banks, prompting an international banking crisis as
governments were unable to bail out insolvent banks. Now, the world is slowly crumbling in the middle of the biggest economic crisis of all time.
In this BitNode:
* Your hacking stat and experience gain are halved
* The starting and maximum amount of money available on servers is significantly decreased
* Your hacking stat and experience gain are reduced
* The starting and maximum amount of money available on servers are reduced by 90%
* The growth rate of servers is significantly reduced
* Weakening a server is twice as effective
* Company wages are decreased by 50%
* Corporation valuations are 99% lower and are therefore significantly less profitable
* Corporation valuations are 90% lower and are therefore significantly less profitable
* Hacknet Node production is significantly decreased
* Crime and Infiltration are more lucrative
* Augmentations are twice as expensive
@ -362,6 +382,12 @@ Source-File
* Level 2: 48%
* Level 3: 56%
This Source-File reduces the price increase for every aug bought by:
* Level 1: 4%
* Level 2: 6%
* Level 3: 7%
Difficulty
Hard
@ -381,6 +407,47 @@ Source-File
Difficulty
Initially very easy, but then it (obviously) becomes harder as you continue to do it.
BitNode-13: They're lunatics
^^^^^^^^^^^^^^^^^^^^^^^^^
Description
With the invention of Augmentations in the 2040s a religious group known as the Church of the Machine God has rallied far more support than anyone would have hoped.
Their leader, Allison "Mother" Stanek is said to have created her own Augmentation whose power goes beyond any other. Find her in Chongqing and gain her trust.
In this BitNode:
* Your hacking stat is reduced by 75% and exp by 90%
* Your combat stats are reduced by 30%
* Class and gym exp gains halved
* The starting and maximum amount of money available on servers is decreased
* The starting security on servers is significantly increased
* Hacking money is decreased by 80%
* Company wages are decreased by 60% and exp gains by 50%
* Hacknet Node production is decreased by 60%
* Crime money is decreased by 60% and exp gains by 50%
* Stockmarket data costs are increased 10-fold
* Corporation valuations are 99.9% lower and are therefore extremely less profitable
* The rank you gain from Bladeburner contracts/operations is reduced by 55%
* Bladeburner skills cost twice as many skill points
* Coding contracts rewards reduced by 60%
* Gangs gain are reduced significantly and offer low amount of Augmentations
* Size of Stanek's Gift is increased by 1 size
Source-File
:Max Level: 3
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File it will upgrade its level up to a maximum of 3.
This Source-File lets the Church of the Machine God appear in other BitNodes.
Each level of this Source-File increases the size of Stanek's Gift.
* Level 1: 5x5
* Level 2: 6x6
* Level 3: 7x7
Difficulty
Hard
Recommended BitNodes
--------------------
As a player, you are not forced to tackle the BitNodes in any particular order. You are
@ -390,9 +457,43 @@ are the recommended BitNodes for different things:
For fast progression
^^^^^^^^^^^^^^^^^^^^
.. note:: This does not recommend the absolute fastest path, as I don't know what
exactly the fastest path is. But it does recommend the BitNodes that are
commonly considered to be optimal by players.
.. note:: These paths do not recommend the absolute fastest path, since speed of progression is
highly dependant on playing style. Path 1 is the recommended path according to the discord community.
Path 1 (new):
1. (Optional) Repeat **BitNode-1: Source Genesis** until you max out its Source-File. Its Source-File
is extremely powerful, as it raises all multipliers by a significant amount. This also a let's you
get used to augments and other features resetting.
2. Do **BitNode-3: Corporatocracy** once to unlock the Corporation mechanic. This mechanic
has highest profit potential in the game.
3. Do **BitNode-10: Digital Carbon** once to unlock sleeves and grafting. Sleeves are useful in all nodes
and grafting can be useful in future BitNodes (especially 8). It's recommended to buy all sleeves and
their memory during the first run.
The ordering of the next three is dependant on playing style and wants/needs.
4. Do **BitNode-5: Artificial Intelligence** once or twice. The intelligence stat it unlocks
will gradually build up as you continue to play the game, and will be helpful
in the future. The Source-File also provides hacking multipliers, which are
strong because hacking is typically one of the best ways of earning money.
5. Do **BitNode-4: The Singularity**. Its Source-File does not directly make you
more powerful in any way, but it does unlock the `Singularity API <https://github.com/danielyxie/bitburner/blob/dev/markdown/bitburner.singularity.md>`_ which
let you automate significantly more aspects of the game. Consider repeating until Level 3,
since each level decreases the RAM cost of Singularity functions.
6. Do **BitNode-2: Rise of the Underworld** once to unlock the gang mechanic. This mechanic
has high profit potential and offers large amounts of Augmentations in a single faction.
Having sleeves (Source-File 10) greatly reduces the time it takes to get access to gangs
outside this BitNode.
7. Do **BitNode-9: Hacktocracy** to unlock the Hacknet Server mechanic. You can
consider repeating it as well, as its Level 2 and 3 effects are pretty helpful as well.
Path 2 (old):
1. Repeat **BitNode-1: Source Genesis** until you max out its Source-File. Its Source-File
is extremely powerful, as it raises all multipliers by a significant amount.
@ -479,6 +580,9 @@ simple money-generator to a more interesting mechanic.
**BitNode-10: Digital Carbon** unlocks two new mechanics: Re-Sleeving and
Duplicate Sleeves.
**BitNode-13: They're lunatics** unlocks Stanek's Gift. This gift can improve skills,
hacknet production and costs, working and crime gains as well hacking power and speed.
For a Challenge
^^^^^^^^^^^^^^^
In general, the higher BitNodes are more difficult than the lower ones.

@ -24,6 +24,7 @@ secrets that you've been searching for.
Basic documentation <netscript>
Basic Gameplay <basicgameplay>
Advanced Gameplay <advancedgameplay>
Remote API <remoteapi.rst>
Keyboard Shortcuts <shortcuts>
Game Frozen or Stuck? <gamefrozen>
Guides & Tips <guidesandtips>

@ -1,14 +1,14 @@
scp() Netscript Function
========================
.. js:function:: scp(files[, source], destination)
.. js:function:: scp(files, destination [, source])
:RAM cost: 0.6 GB
:param string/array files: Filename or an array of filenames of script/literature files to copy
:param string destination: Hostname of the destination server, which is the server to which the file will be copied.
:param string source:
Hostname of the source server, which is the server from which the file will be copied.
This argument is optional and if it's omitted the source will be the current server.
:param string destination: Hostname of the destination server, which is the server to which the file will be copied.
:returns: ``true`` if the copy was a success.
Copies a script or literature (.lit) file(s) to another server. The
@ -26,8 +26,8 @@ scp() Netscript Function
scp("hack-template.script", "foodnstuff"); // returns: true
//Copies "foo.lit" from the helios server to the "home" computer
scp("foo.lit", "helios", "home"); // returns: true
scp("foo.lit", "home", "helios"); // returns: true
//Tries to copy three files from "rothman-uni" to "home" computer
files = ["foo1.lit", "foo2.script", "foo3.script"];
scp(files, "rothman-uni", "home"); // returns: true
scp(files, "home", "rothman-uni"); // returns: true

206
doc/source/remoteapi.rst Normal file

@ -0,0 +1,206 @@
Remote API
==========
All versions of Bitburner can use websockets to connect to a server.
That server can then perform a number of actions.
Most commonly this is used in conjunction with an external text editor or API
in order to make writing scripts easier, or even use typescript.
To make use of this Remote API through the official server, look here: https://github.com/bitburner-official/typescript-template.
If you want to make your own server, see below for API details.
This API uses the JSON RCP 2.0 protocol. Inputs are in the following form:
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": string,
"params": any
}
Outputs:
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": any,
"error": any
}
Methods
-------
`pushFile`
^^^^^^^^^^
Create or update a file.
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": "pushFile",
"params": {
filename: string;
content: string;
server: string;
}
}
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": "OK"
}
`getFile`
^^^^^^^^^
Read a file and it's content.
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": "getFile",
"params": {
filename: string;
server: string;
}
}
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": string
}
`deleteFile`
^^^^^^^^^^^^
Delete a file.
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": "deleteFile",
"params": {
filename: string;
server: string;
}
}
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": "OK"
}
`getFileNames`
^^^^^^^^^^^^^^
List all file names on a server.
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": "getFileNames",
"params": {
server: string;
}
}
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": string[]
}
`getAllFiles`
^^^^^^^^^^^^^
Get the content of all files on a server.
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": "getAllFiles",
"params": {
server: string;
}
}
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": {
filename: string,
content: string
}[]
}
`calculateRam`
^^^^^^^^^^^^^^
Calculate the in-game ram cost of a script.
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": "calculateRam",
"params": {
filename: string;
server: string;
}
}
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": number
}
`getDefinitionFile`
^^^^^^^^^^^^^^^^^^^
Get the definition file of the API.
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"method": "getDefinitionFile"
}
.. code-block:: javascript
{
"jsonrpc": "2.0",
"id": number,
"result": string
}

@ -72,6 +72,6 @@
</style>
<link rel="shortcut icon" href="favicon.ico"></head>
<body>
<div id="root"/>
<div id="root" style="display:flex"/>
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="dist/main.bundle.js"></script></body>
</html>

@ -9,5 +9,6 @@ module.exports = {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
"<rootDir>/test/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js",
"\\!!raw-loader!.*$": "<rootDir>/test/__mocks__/rawLoader.js",
},
};

@ -7,7 +7,7 @@
<b>Signature:</b>
```typescript
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg };
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg | string[] };
```
## Parameters
@ -18,5 +18,5 @@ flags(schema: [string, string | number | boolean | string[]][]): { [key: string]
<b>Returns:</b>
{ \[key: string\]: [ScriptArg](./bitburner.scriptarg.md) }
{ \[key: string\]: [ScriptArg](./bitburner.scriptarg.md) \| string\[\] }

@ -9,16 +9,21 @@ Attempts a coding contract.
<b>Signature:</b>
```typescript
attempt(answer: string[] | number, filename: string, host?: string, opts?: CodingAttemptOptions): boolean | string;
attempt(
answer: string | number | any[],
filename: string,
host?: string,
opts?: CodingAttemptOptions,
): boolean | string;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| answer | string\[\] \| number | Solution for the contract. |
| answer | string \| number \| any\[\] | Solution for the contract. |
| filename | string | Filename of the contract. |
| host | string | Host of the server containing the contract. Optional. Defaults to current server if not provided. |
| host | string | Hostname of the server containing the contract. Optional. Defaults to current server if not provided. |
| opts | [CodingAttemptOptions](./bitburner.codingattemptoptions.md) | Optional parameters for configuring function behavior. |
<b>Returns:</b>

@ -17,7 +17,7 @@ getContractType(filename: string, host?: string): string;
| Parameter | Type | Description |
| --- | --- | --- |
| filename | string | Filename of the contract. |
| host | string | Host of the server containing the contract. Optional. Defaults to current server if not provided. |
| host | string | Hostname of the server containing the contract. Optional. Defaults to current server if not provided. |
<b>Returns:</b>

@ -23,11 +23,11 @@ getData(filename: string, host?: string): CodingContractData;
[CodingContractData](./bitburner.codingcontractdata.md)
The specified contracts data, data type depends on contract type.;
The specified contracts data, data type depends on contract type.
## Remarks
RAM cost: 5 GB
Get the data associated with the specific Coding Contract. Note that this is not the same as the contracts description. This is just the data that the contract wants you to act on in order to solve
Get the data associated with the specific Coding Contract. Note that this is not the same as the contracts description. This is just the data that the contract wants you to act on in order to solve the contract.

@ -17,7 +17,7 @@ getDescription(filename: string, host?: string): string;
| Parameter | Type | Description |
| --- | --- | --- |
| filename | string | Filename of the contract. |
| host | string | Host of the server containing the contract. Optional. Defaults to current server if not provided. |
| host | string | Hostname of the server containing the contract. Optional. Defaults to current server if not provided. |
<b>Returns:</b>

@ -4,7 +4,7 @@
## CodingContract.getNumTriesRemaining() method
Get the number of attempt remaining.
Get the number of attempts remaining.
<b>Signature:</b>
@ -17,13 +17,13 @@ getNumTriesRemaining(filename: string, host?: string): number;
| Parameter | Type | Description |
| --- | --- | --- |
| filename | string | Filename of the contract. |
| host | string | Host of the server containing the contract. Optional. Defaults to current server if not provided. |
| host | string | Hostname of the server containing the contract. Optional. Defaults to current server if not provided. |
<b>Returns:</b>
number
How many attempts are remaining for the contract;
How many attempts are remaining for the contract.
## Remarks

@ -20,5 +20,5 @@ export interface CodingContract
| [getContractType(filename, host)](./bitburner.codingcontract.getcontracttype.md) | Get the type of a coding contract. |
| [getData(filename, host)](./bitburner.codingcontract.getdata.md) | Get the input data. |
| [getDescription(filename, host)](./bitburner.codingcontract.getdescription.md) | Get the description. |
| [getNumTriesRemaining(filename, host)](./bitburner.codingcontract.getnumtriesremaining.md) | Get the number of attempt remaining. |
| [getNumTriesRemaining(filename, host)](./bitburner.codingcontract.getnumtriesremaining.md) | Get the number of attempts remaining. |

@ -4,7 +4,7 @@
## CorporationInfo.issuedShares property
Amount of shares issued
Amount of aqcuirable shares.
<b>Signature:</b>

@ -22,7 +22,7 @@ interface CorporationInfo
| [divisions](./bitburner.corporationinfo.divisions.md) | [Division](./bitburner.division.md)<!-- -->\[\] | Array of all divisions |
| [expenses](./bitburner.corporationinfo.expenses.md) | number | Expenses per second this cycle |
| [funds](./bitburner.corporationinfo.funds.md) | number | Funds available |
| [issuedShares](./bitburner.corporationinfo.issuedshares.md) | number | Amount of shares issued |
| [issuedShares](./bitburner.corporationinfo.issuedshares.md) | number | Amount of aqcuirable shares. |
| [name](./bitburner.corporationinfo.name.md) | string | Name of the corporation |
| [numShares](./bitburner.corporationinfo.numshares.md) | number | Amount of share owned |
| [public](./bitburner.corporationinfo.public.md) | boolean | Indicating if the company is public |

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [amt](./bitburner.export.amt.md)
## Export.amt property
Amount of material exported
<b>Signature:</b>
```typescript
amt: string;
```

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [div](./bitburner.export.div.md)
## Export.div property
Division the material is being exported to
<b>Signature:</b>
```typescript
div: string;
```

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [loc](./bitburner.export.loc.md)
## Export.loc property
City the material is being exported to
<b>Signature:</b>
```typescript
loc: string;
```

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md)
## Export interface
Export order for a material
<b>Signature:</b>
```typescript
interface Export
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [amt](./bitburner.export.amt.md) | string | Amount of material exported |
| [div](./bitburner.export.div.md) | string | Division the material is being exported to |
| [loc](./bitburner.export.loc.md) | string | City the material is being exported to |

@ -28,3 +28,10 @@ You need Formulas.exe on your home computer to use this API.
| [skills](./bitburner.formulas.skills.md) | [SkillsFormulas](./bitburner.skillsformulas.md) | Skills formulas |
| [work](./bitburner.formulas.work.md) | [WorkFormulas](./bitburner.workformulas.md) | Work formulas |
## Methods
| Method | Description |
| --- | --- |
| [mockPlayer()](./bitburner.formulas.mockplayer.md) | |
| [mockServer()](./bitburner.formulas.mockserver.md) | |

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Formulas](./bitburner.formulas.md) &gt; [mockPlayer](./bitburner.formulas.mockplayer.md)
## Formulas.mockPlayer() method
<b>Signature:</b>
```typescript
mockPlayer(): Player;
```
<b>Returns:</b>
[Player](./bitburner.player.md)

@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Formulas](./bitburner.formulas.md) &gt; [mockServer](./bitburner.formulas.mockserver.md)
## Formulas.mockServer() method
<b>Signature:</b>
```typescript
mockServer(): Server;
```
<b>Returns:</b>
[Server](./bitburner.server.md)

@ -22,7 +22,7 @@ ascendMember(memberName: string): GangMemberAscension | undefined;
[GangMemberAscension](./bitburner.gangmemberascension.md) \| undefined
Object with info about the ascension results. undefined if ascension did not occur.
Object with info about the ascension results. Undefined if ascension did not occur.
## Remarks

@ -22,7 +22,7 @@ getAscensionResult(memberName: string): GangMemberAscension | undefined;
[GangMemberAscension](./bitburner.gangmemberascension.md) \| undefined
Object with info about the ascension results. undefined if ascension is impossible.
Object with info about the ascension results. Undefined if ascension is impossible.
## Remarks

@ -23,7 +23,7 @@ getRamUpgradeCost(index: number, n: number): number;
number
Cost of upgrading the specified Hacknet Node's ram.
Cost of upgrading the specified Hacknet Node's RAM.
## Remarks
@ -31,5 +31,5 @@ RAM cost: 0 GB
Returns the cost of upgrading the RAM of the specified Hacknet Node n times.
If an invalid value for n is provided, then this function returns 0. If the specified Hacknet Node is already at max level, then Infinity is returned.
If an invalid value for n is provided, then this function returns 0. If the specified Hacknet Node already has max RAM, then Infinity is returned.

@ -15,7 +15,7 @@ maxNumNodes(): number;
number
maximum number of hacknet nodes.
Maximum number of hacknet nodes.
## Remarks

@ -15,7 +15,7 @@ numNodes(): number;
number
number of hacknet nodes.
Number of hacknet nodes.
## Remarks

@ -24,7 +24,7 @@ spendHashes(upgName: string, upgTarget?: string, count?: number): boolean;
boolean
True if the upgrade is successfully purchased, and false otherwise..
True if the upgrade is successfully purchased, and false otherwise.
## Remarks

@ -23,7 +23,7 @@ upgradeCache(index: number, n: number): boolean;
boolean
True if the Hacknet Nodes cores are successfully purchased, false otherwise.
True if the Hacknet Nodes cache level is successfully upgraded, false otherwise.
## Remarks

@ -23,7 +23,7 @@ upgradeRam(index: number, n: number): boolean;
boolean
True if the Hacknet Nodes ram is successfully upgraded, false otherwise.
True if the Hacknet Nodes RAM is successfully upgraded, false otherwise.
## Remarks
@ -31,7 +31,7 @@ RAM cost: 0 GB
Tries to upgrade the specified Hacknet Nodes RAM n times. Note that each upgrade doubles the Nodes RAM. So this is equivalent to multiplying the Nodes RAM by 2 n.
Returns true if the Hacknet Nodes RAM is successfully upgraded n times or if it is upgraded some positive number of times and the Node reaches it max RAM.
Returns true if the Hacknet Nodes RAM is successfully upgraded n times or if it is upgraded some positive number of times and the Node reaches its max RAM.
Returns false otherwise.

@ -4,7 +4,7 @@
## Material.cost property
cost to buy material
Cost to buy material
<b>Signature:</b>

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [exp](./bitburner.material.exp.md)
## Material.exp property
Export orders
<b>Signature:</b>
```typescript
exp: Export[];
```

@ -17,8 +17,9 @@ interface Material
| Property | Type | Description |
| --- | --- | --- |
| [cmp](./bitburner.material.cmp.md) | number \| undefined | Competition for the material, only present if "Market Research - Competition" unlocked |
| [cost](./bitburner.material.cost.md) | number | cost to buy material |
| [cost](./bitburner.material.cost.md) | number | Cost to buy material |
| [dmd](./bitburner.material.dmd.md) | number \| undefined | Demand for the material, only present if "Market Research - Demand" unlocked |
| [exp](./bitburner.material.exp.md) | [Export](./bitburner.export.md)<!-- -->\[\] | Export orders |
| [name](./bitburner.material.name.md) | string | Name of the material |
| [prod](./bitburner.material.prod.md) | number | Amount of material produced |
| [qlt](./bitburner.material.qlt.md) | number | Quality of the material |

@ -34,6 +34,7 @@
| [Employee](./bitburner.employee.md) | Employee in an office |
| [EmployeeJobs](./bitburner.employeejobs.md) | Object representing the number of employee in each job. |
| [EquipmentStats](./bitburner.equipmentstats.md) | Object representing data representing a gang member equipment. |
| [Export](./bitburner.export.md) | Export order for a material |
| [Formulas](./bitburner.formulas.md) | Formulas API |
| [Fragment](./bitburner.fragment.md) | |
| [Gang](./bitburner.gang.md) | Gang API |

@ -37,7 +37,7 @@ If the script was successfully started, then this functions returns the PID of t
PID stands for Process ID. The PID is a unique identifier for each script. The PID will always be a positive integer.
Running this function with 0 or a negative numThreads argument will cause a runtime error.
Running this function with a numThreads argument of 0 or less will cause a runtime error.
## Example 1

@ -9,11 +9,11 @@ Terminates the current script immediately.
<b>Signature:</b>
```typescript
exit(): void;
exit(): never;
```
<b>Returns:</b>
void
never
## Remarks

@ -9,7 +9,7 @@ Parse command line flags.
<b>Signature:</b>
```typescript
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg };
flags(schema: [string, string | number | boolean | string[]][]): { [key: string]: ScriptArg | string[] };
```
## Parameters
@ -20,7 +20,7 @@ flags(schema: [string, string | number | boolean | string[]][]): { [key: string]
<b>Returns:</b>
{ \[key: string\]: [ScriptArg](./bitburner.scriptarg.md) }
{ \[key: string\]: [ScriptArg](./bitburner.scriptarg.md) \| string\[\] }
## Remarks

@ -130,6 +130,7 @@ export async function main(ns) {
| [kill(script, host, args)](./bitburner.ns.kill_1.md) | Terminate another script. |
| [killall(host, safetyguard)](./bitburner.ns.killall.md) | Terminate all scripts on a server. |
| [ls(host, grep)](./bitburner.ns.ls.md) | List files on a server. |
| [moveTail(x, y, pid)](./bitburner.ns.movetail.md) | Move a tail window |
| [mv(host, source, destination)](./bitburner.ns.mv.md) | Move a file on the target server. |
| [nFormat(n, format)](./bitburner.ns.nformat.md) | Format a number |
| [nuke(host)](./bitburner.ns.nuke.md) | Runs NUKE.exe on a server. |
@ -139,9 +140,10 @@ export async function main(ns) {
| [prompt(txt, options)](./bitburner.ns.prompt.md) | Prompt the player with an input modal. |
| [ps(host)](./bitburner.ns.ps.md) | List running scripts on a server. |
| [purchaseServer(hostname, ram)](./bitburner.ns.purchaseserver.md) | Purchase a server. |
| [read(handle)](./bitburner.ns.read.md) | Read content of a file. |
| [read(filename)](./bitburner.ns.read.md) | Read content of a file. |
| [readPort(port)](./bitburner.ns.readport.md) | Read data from a port. |
| [relaysmtp(host)](./bitburner.ns.relaysmtp.md) | Runs relaySMTP.exe on a server. |
| [resizeTail(width, height, pid)](./bitburner.ns.resizetail.md) | Resize a tail window |
| [rm(name, host)](./bitburner.ns.rm.md) | Delete a file. |
| [run(script, numThreads, args)](./bitburner.ns.run.md) | Start another script on the current server. |
| [scan(host)](./bitburner.ns.scan.md) | Get the list of servers connected to a server. |
@ -164,6 +166,6 @@ export async function main(ns) {
| [weaken(host, opts)](./bitburner.ns.weaken.md) | Reduce a server security level. |
| [weakenAnalyze(threads, cores)](./bitburner.ns.weakenanalyze.md) | Predict the effect of weaken. |
| [wget(url, target, host)](./bitburner.ns.wget.md) | Download a file from the internet. |
| [write(handle, data, mode)](./bitburner.ns.write.md) | Write data to a file. |
| [write(filename, data, mode)](./bitburner.ns.write.md) | Write data to a file. |
| [writePort(port, data)](./bitburner.ns.writeport.md) | Write data to a port. |

@ -0,0 +1,32 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [NS](./bitburner.ns.md) &gt; [moveTail](./bitburner.ns.movetail.md)
## NS.moveTail() method
Move a tail window
<b>Signature:</b>
```typescript
moveTail(x: number, y: number, pid?: number): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| x | number | x coordinate. |
| y | number | y coordinate. |
| pid | number | Optional. PID of the script having its tail moved. If omitted, the current script is used. |
<b>Returns:</b>
void
## Remarks
RAM cost: 0 GB
Moves a tail window. Coordinates are in screenspace pixels (top left is 0,0)

@ -9,18 +9,18 @@ Read content of a file.
<b>Signature:</b>
```typescript
read(handle: string): PortData;
read(filename: string): string;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| handle | string | Filename to read from. |
| filename | string | Name of the file to be read. |
<b>Returns:</b>
[PortData](./bitburner.portdata.md)
string
Data in the specified text file.
@ -28,7 +28,7 @@ Data in the specified text file.
RAM cost: 0 GB
This function is used to read data from a text file (.txt).
This function is used to read data from a text file (.txt) or script (.script, .js).
This function will return the data in the specified text file. If the text file does not exist, an empty string will be returned.
This function will return the data in the specified file. If the file does not exist, an empty string will be returned.

@ -0,0 +1,32 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [NS](./bitburner.ns.md) &gt; [resizeTail](./bitburner.ns.resizetail.md)
## NS.resizeTail() method
Resize a tail window
<b>Signature:</b>
```typescript
resizeTail(width: number, height: number, pid?: number): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| width | number | width of the window. |
| height | number | height of the window. |
| pid | number | Optional. PID of the script having its tail resized. If omitted, the current script is used. |
<b>Returns:</b>
void
## Remarks
RAM cost: 0 GB
Resize a tail window. Size are in pixel

@ -36,7 +36,7 @@ If the script was successfully started, then this functions returns the PID of t
PID stands for Process ID. The PID is a unique identifier for each script. The PID will always be a positive integer.
Running this function with a numThreads argument of 0 will return 0 without running the script. However, running this function with a negative numThreads argument will cause a runtime error.
Running this function with a numThreads argument of 0 or less will cause a runtime error.
## Example 1

@ -9,7 +9,7 @@ Copy file between servers.
<b>Signature:</b>
```typescript
scp(files: string | string[], destination: string, source?: string): Promise<boolean>;
scp(files: string | string[], destination: string, source?: string): boolean;
```
## Parameters
@ -22,9 +22,9 @@ scp(files: string | string[], destination: string, source?: string): Promise<boo
<b>Returns:</b>
Promise&lt;boolean&gt;
boolean
True if the script/literature file is successfully copied over and false otherwise. If the files argument is an array then this function will return true if at least one of the files in the array is successfully copied.
True if the file is successfully copied over and false otherwise. If the files argument is an array then this function will return false if any of the operations failed.
## Remarks
@ -51,11 +51,11 @@ scp(files, "home", "rothman-uni");
```ts
// NS2:
//Copies foo.lit from the helios server to the home computer:
await ns.scp("foo.lit", "home", "helios" );
ns.scp("foo.lit", "home", "helios" );
//Tries to copy three files from rothman-uni to home computer:
files = ["foo1.lit", "foo2.script", "foo3.script"];
await ns.scp(files, "home", "rothman-uni");
ns.scp(files, "home", "rothman-uni");
```
## Example 3
@ -65,6 +65,6 @@ await ns.scp(files, "home", "rothman-uni");
//ns2, copies files from home to a target server
const server = ns.args[0];
const files = ["hack.js","weaken.js","grow.js"];
await ns.scp(files, server, "home");
ns.scp(files, server, "home");
```

@ -32,6 +32,8 @@ Terminates the current script, and then after a delay of about 10 seconds it wil
Because this function immediately terminates the script, it does not have a return value.
Running this function with a numThreads argument of 0 or less will cause a runtime error.
## Example 1

@ -9,26 +9,26 @@ Write data to a file.
<b>Signature:</b>
```typescript
write(handle: string, data?: string[] | number | string, mode?: "w" | "a"): Promise<void>;
write(filename: string, data?: string[] | number | string, mode?: "w" | "a"): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| handle | string | Filename of the text file that will be written to. |
| filename | string | Name of the file to be written to. |
| data | string\[\] \| number \| string | Data to write. |
| mode | "w" \| "a" | Defines the write mode. Only valid when writing to text files. |
| mode | "w" \| "a" | Defines the write mode. |
<b>Returns:</b>
Promise&lt;void&gt;
void
## Remarks
RAM cost: 0 GB
This function can be used to write data to a text file (.txt).
This function can be used to write data to a text file (.txt) or a script (.js or .script).
This function will write data to that text file. If the specified text file does not exist, then it will be created. The third argument mode, defines how the data will be written to the text file. If \*mode is set to “w”, then the data is written in “write” mode which means that it will overwrite all existing data on the text file. If mode is set to any other value then the data will be written in “append” mode which means that the data will be added at the end of the text file.
This function will write data to that file. If the specified file does not exist, then it will be created. The third argument mode, defines how the data will be written to the file. If \*mode is set to “w”, then the data is written in “write” mode which means that it will overwrite all existing data on the file. If mode is set to any other value then the data will be written in “append” mode which means that the data will be added at the end of the file.

@ -17,7 +17,7 @@ commitCrime(crime: string, focus?: boolean): number;
| Parameter | Type | Description |
| --- | --- | --- |
| crime | string | Name of crime to attempt. |
| focus | boolean | Acquire player focus on this program creation. Optional. Defaults to true. |
| focus | boolean | Acquire player focus on this crime. Optional. Defaults to true. |
<b>Returns:</b>

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Singularity](./bitburner.singularity.md) &gt; [exportGame](./bitburner.singularity.exportgame.md)
## Singularity.exportGame() method
Backup game save.
<b>Signature:</b>
```typescript
exportGame(): void;
```
<b>Returns:</b>
void
## Remarks
RAM cost: 1 GB \* 16/4/1
This function will automatically opens the backup save prompt and claim the free faction favour if available.

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Singularity](./bitburner.singularity.md) &gt; [exportGameBonus](./bitburner.singularity.exportgamebonus.md)
## Singularity.exportGameBonus() method
Returns Backup save bonus availability.
<b>Signature:</b>
```typescript
exportGameBonus(): boolean;
```
<b>Returns:</b>
boolean
## Remarks
RAM cost: 0.5 GB \* 16/4/1
This function will check if there is a bonus for backing up your save.

@ -28,6 +28,8 @@ This API requires Source-File 4 to use. The RAM cost of all these functions is m
| [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. |
| [donateToFaction(faction, amount)](./bitburner.singularity.donatetofaction.md) | Donate to a faction. |
| [exportGame()](./bitburner.singularity.exportgame.md) | Backup game save. |
| [exportGameBonus()](./bitburner.singularity.exportgamebonus.md) | Returns Backup save bonus availability. |
| [getAugmentationBasePrice(augName)](./bitburner.singularity.getaugmentationbaseprice.md) | Get base price of an augmentation. |
| [getAugmentationCost(augName)](./bitburner.singularity.getaugmentationcost.md) | Get the price and reputation of an augmentation. |
| [getAugmentationPrereq(augName)](./bitburner.singularity.getaugmentationprereq.md) | Get the pre-requisite of an augmentation. |

@ -22,7 +22,7 @@ travelToCity(city: string): boolean;
boolean
True if actions is successful, false otherwise.
True if action is successful, false otherwise.
## Remarks

@ -24,7 +24,7 @@ universityCourse(universityName: string, courseName: string, focus?: boolean): b
boolean
True if actions is successfully started, false otherwise.
True if action is successfully started, false otherwise.
## Remarks

@ -24,4 +24,5 @@ interface UserInterface
| [resetTheme()](./bitburner.userinterface.resettheme.md) | Resets the player's theme to the default values |
| [setStyles(newStyles)](./bitburner.userinterface.setstyles.md) | Sets the current styles |
| [setTheme(newTheme)](./bitburner.userinterface.settheme.md) | Sets the current theme |
| [windowSize()](./bitburner.userinterface.windowsize.md) | Get the current window size |

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [UserInterface](./bitburner.userinterface.md) &gt; [windowSize](./bitburner.userinterface.windowsize.md)
## UserInterface.windowSize() method
Get the current window size
<b>Signature:</b>
```typescript
windowSize(): [number, number];
```
<b>Returns:</b>
\[number, number\]
An array of 2 value containing the window width and height.
## Remarks
RAM cost: 0 GB

16
package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "bitburner",
"version": "1.7.0",
"version": "2.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "bitburner",
"version": "1.7.0",
"version": "2.1.0",
"hasInstallScript": true,
"license": "SEE LICENSE IN license.txt",
"dependencies": {
@ -6074,9 +6074,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001328",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz",
"integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ==",
"version": "1.0.30001414",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001414.tgz",
"integrity": "sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg==",
"funding": [
{
"type": "opencollective",
@ -26801,9 +26801,9 @@
"dev": true
},
"caniuse-lite": {
"version": "1.0.30001328",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz",
"integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ=="
"version": "1.0.30001414",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001414.tgz",
"integrity": "sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg=="
},
"caseless": {
"version": "0.12.0",

@ -1,7 +1,7 @@
{
"name": "bitburner",
"license": "SEE LICENSE IN license.txt",
"version": "2.0.1",
"version": "2.1.0",
"main": "electron-main.js",
"author": {
"name": "Daniel Xie & Olivier Gagnon"

@ -25,6 +25,7 @@ import * as data from "./AchievementData.json";
import { FactionNames } from "../Faction/data/FactionNames";
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
import { isClassWork } from "../Work/ClassWork";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
@ -346,18 +347,18 @@ export const achievements: IMap<Achievement> = {
FIRST_HACKNET_NODE: {
...achievementData["FIRST_HACKNET_NODE"],
Icon: "node",
Condition: () => !hasHacknetServers(Player) && Player.hacknetNodes.length > 0,
Condition: () => !hasHacknetServers() && Player.hacknetNodes.length > 0,
},
"30_HACKNET_NODE": {
...achievementData["30_HACKNET_NODE"],
Icon: "hacknet-all",
Condition: () => !hasHacknetServers(Player) && Player.hacknetNodes.length >= 30,
Condition: () => !hasHacknetServers() && Player.hacknetNodes.length >= 30,
},
MAX_HACKNET_NODE: {
...achievementData["MAX_HACKNET_NODE"],
Icon: "hacknet-max",
Condition: (): boolean => {
if (hasHacknetServers(Player)) return false;
if (hasHacknetServers()) return false;
for (const h of Player.hacknetNodes) {
if (!(h instanceof HacknetNode)) return false;
if (
@ -373,7 +374,7 @@ export const achievements: IMap<Achievement> = {
HACKNET_NODE_10M: {
...achievementData["HACKNET_NODE_10M"],
Icon: "hacknet-10m",
Condition: () => !hasHacknetServers(Player) && Player.moneySourceB.hacknet >= 10e6,
Condition: () => !hasHacknetServers() && Player.moneySourceB.hacknet >= 10e6,
},
REPUTATION_10M: {
...achievementData["REPUTATION_10M"],
@ -383,7 +384,10 @@ export const achievements: IMap<Achievement> = {
DONATION: {
...achievementData["DONATION"],
Icon: "donation",
Condition: () => Object.values(Factions).some((f) => f.favor >= 150),
Condition: () =>
Object.values(Factions).some(
(f) => f.favor >= Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction),
),
},
TRAVEL: {
...achievementData["TRAVEL"],
@ -511,14 +515,14 @@ export const achievements: IMap<Achievement> = {
...achievementData["FIRST_HACKNET_SERVER"],
Icon: "HASHNET",
Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length > 0,
Condition: () => hasHacknetServers() && Player.hacknetNodes.length > 0,
AdditionalUnlock: [achievementData.FIRST_HACKNET_NODE.ID],
},
ALL_HACKNET_SERVER: {
...achievementData["ALL_HACKNET_SERVER"],
Icon: "HASHNETALL",
Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.hacknetNodes.length === HacknetServerConstants.MaxServers,
Condition: () => hasHacknetServers() && Player.hacknetNodes.length === HacknetServerConstants.MaxServers,
AdditionalUnlock: [achievementData["30_HACKNET_NODE"].ID],
},
MAX_HACKNET_SERVER: {
@ -526,7 +530,7 @@ export const achievements: IMap<Achievement> = {
Icon: "HASHNETALL",
Visible: () => hasAccessToSF(Player, 9),
Condition: (): boolean => {
if (!hasHacknetServers(Player)) return false;
if (!hasHacknetServers()) return false;
for (const h of Player.hacknetNodes) {
if (typeof h !== "string") return false;
const hs = GetServer(h);
@ -547,7 +551,7 @@ export const achievements: IMap<Achievement> = {
...achievementData["HACKNET_SERVER_1B"],
Icon: "HASHNETMONEY",
Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) && Player.moneySourceB.hacknet >= 1e9,
Condition: () => hasHacknetServers() && Player.moneySourceB.hacknet >= 1e9,
AdditionalUnlock: [achievementData.HACKNET_NODE_10M.ID],
},
MAX_CACHE: {
@ -555,7 +559,7 @@ export const achievements: IMap<Achievement> = {
Icon: "HASHNETCAP",
Visible: () => hasAccessToSF(Player, 9),
Condition: () =>
hasHacknetServers(Player) &&
hasHacknetServers() &&
Player.hashManager.hashes === Player.hashManager.capacity &&
Player.hashManager.capacity > 0,
},

@ -2,7 +2,7 @@ import React, { useState } from "react";
import { BBCabinetRoot } from "./BBCabinet";
import Button from "@mui/material/Button";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { AlertEvents } from "../../ui/React/AlertManager";
enum Page {
@ -11,11 +11,10 @@ enum Page {
}
export function ArcadeRoot(): React.ReactElement {
const player = use.Player();
const [page, setPage] = useState(Page.None);
function mbBurner2000(): void {
if (player.sourceFileLvl(1) === 0) {
if (Player.sourceFileLvl(1) === 0) {
AlertEvents.emit("This machine is broken.");
} else {
setPage(Page.Megabyteburner2000);

@ -1,6 +1,6 @@
import React, { useEffect } from "react";
import Typography from "@mui/material/Typography";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { Exploit } from "../../Exploits/Exploit";
const metaBB = "https://bitburner-official.github.io/bitburner-legacy/";
@ -12,11 +12,10 @@ const style = {
};
export function BBCabinetRoot(): React.ReactElement {
const player = use.Player();
useEffect(() => {
window.addEventListener("message", function (this: Window, ev: MessageEvent<boolean>) {
if (ev.isTrusted && ev.origin == "https://bitburner-official.github.io" && ev.data) {
player.giveExploit(Exploit.TrueRecursion);
Player.giveExploit(Exploit.TrueRecursion);
}
});
});

@ -8,7 +8,7 @@ import { Money } from "../ui/React/Money";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { FactionNames } from "../Faction/data/FactionNames";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Player } from "../Player";
import { AugmentationNames } from "./data/AugmentationNames";
import { CONSTANTS } from "../Constants";
import { StaticAugmentations } from "./StaticAugmentations";
@ -62,12 +62,6 @@ export interface IConstructorParams {
bladeburner_stamina_gain?: number;
bladeburner_analysis?: number;
bladeburner_success_chance?: number;
infiltration_base_rep_increase?: number;
infiltration_rep?: number;
infiltration_trade?: number;
infiltration_sell?: number;
infiltration_timer?: number;
infiltration_damage_reduction?: number;
startingMoney?: number;
programs?: string[];
@ -537,26 +531,26 @@ export class Augmentation {
}
}
getCost(player: IPlayer): AugmentationCosts {
getCost(): AugmentationCosts {
const augmentationReference = StaticAugmentations[this.name];
let moneyCost = augmentationReference.baseCost;
let repCost = augmentationReference.baseRepRequirement;
if (augmentationReference.name === AugmentationNames.NeuroFluxGovernor) {
let nextLevel = this.getLevel(player);
let nextLevel = this.getLevel();
--nextLevel;
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel);
repCost = augmentationReference.baseRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost;
moneyCost = augmentationReference.baseCost * multiplier * BitNodeMultipliers.AugmentationMoneyCost;
for (let i = 0; i < player.queuedAugmentations.length; ++i) {
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
moneyCost *= getBaseAugmentationPriceMultiplier();
}
} else if (augmentationReference.factions.includes(FactionNames.ShadowsOfAnarchy)) {
const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name);
const soaMultiplier = Math.pow(
CONSTANTS.SoACostMult,
soaAugmentationNames.filter((augmentationName) => player.hasAugmentation(augmentationName)).length,
soaAugmentationNames.filter((augmentationName) => Player.hasAugmentation(augmentationName)).length,
);
moneyCost = augmentationReference.baseCost * soaMultiplier;
if (soaAugmentationNames.find((augmentationName) => augmentationName === augmentationReference.name)) {
@ -572,19 +566,19 @@ export class Augmentation {
return { moneyCost, repCost };
}
getLevel(player: IPlayer): number {
getLevel(): number {
// Get current Neuroflux level based on Player's augmentations
if (this.name === AugmentationNames.NeuroFluxGovernor) {
let currLevel = 0;
for (let i = 0; i < player.augmentations.length; ++i) {
if (player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = player.augmentations[i].level;
for (let i = 0; i < Player.augmentations.length; ++i) {
if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
currLevel = Player.augmentations[i].level;
}
}
// Account for purchased but uninstalled Augmentations
for (let i = 0; i < player.queuedAugmentations.length; ++i) {
if (player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) {
++currLevel;
}
}

@ -1,6 +1,6 @@
import { Augmentation } from "./Augmentation";
import { StaticAugmentations } from "./StaticAugmentations";
import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames";
import { CONSTANTS } from "../Constants";
@ -71,7 +71,7 @@ function resetAugmentation(aug: Augmentation): void {
AddToStaticAugmentations(aug);
}
function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void {
function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void {
const staticAugmentation = StaticAugmentations[aug.name];
// Apply multipliers
@ -126,15 +126,15 @@ function installAugmentations(force?: boolean): boolean {
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
level = ` - ${ownedAug.level}`;
}
augmentationList += aug.name + level + "<br>";
augmentationList += aug.name + level + "\n";
}
Player.queuedAugmentations = [];
if (!force) {
dialogBoxCreate(
"You slowly drift to sleep as scientists put you under in order " +
"to install the following Augmentations:<br>" +
"to install the following Augmentations:\n" +
augmentationList +
"<br>You wake up in your home...you feel different...",
"\nYou wake up in your home...you feel different...",
);
}
prestigeAugmentation();
@ -146,8 +146,8 @@ function augmentationExists(name: string): boolean {
return StaticAugmentations.hasOwnProperty(name);
}
export function isRepeatableAug(aug: Augmentation): boolean {
const augName = aug instanceof Augmentation ? aug.name : aug;
export function isRepeatableAug(aug: Augmentation | string): boolean {
const augName = typeof aug === "string" ? aug : aug.name;
return augName === AugmentationNames.NeuroFluxGovernor;
}

@ -6,8 +6,3 @@ export class PlayerOwnedAugmentation {
this.name = name;
}
}
export interface IPlayerOwnedAugmentation {
level: number;
name: string;
}

@ -1384,9 +1384,8 @@ export const initGeneralAugmentations = (): Augmentation[] => [
"The left arm of a legendary BitRunner who ascended beyond this world. " +
"It projects a light blue energy shield that protects the exposed inner parts. " +
"Even though it contains no weapons, the advanced tungsten titanium " +
"alloy increases the user's strength to unbelievable levels. The augmentation " +
"gets more powerful over time for seemingly no reason.",
strength: 2.7,
"alloy increases the user's strength to unbelievable levels.",
strength: 2.8,
factions: [FactionNames.NWO],
}),
new Augmentation({
@ -1631,6 +1630,16 @@ export const initGeneralAugmentations = (): Augmentation[] => [
factions: [FactionNames.TianDiHui],
}),
// new Augmentation({
// name: AugmentationNames.UnnamedAug2,
// repCost: 500e3,
// moneyCost: 5e9,
// info: "Undecided description",
// startingMoney: 100e6,
// programs: [Programs.HTTPWormProgram.name, Programs.SQLInjectProgram.name],
// factions: [FactionNames.OmniTekIncorporated],
// }),
// Grafting-exclusive Augmentation
new Augmentation({
name: AugmentationNames.CongruityImplant,
@ -1649,6 +1658,22 @@ export const initGeneralAugmentations = (): Augmentation[] => [
stats: <>This Augmentation removes the Entropy virus, and prevents it from affecting you again.</>,
factions: [],
}),
// Sleeve exclusive augmentations
new Augmentation({
name: AugmentationNames.ZOE,
isSpecial: true,
repCost: Infinity,
moneyCost: 1e12,
info:
"Zoë's Omnicerebrum Ënhancer for sleeves inserts an omnicerebrum into your sleeve. " +
"An omnicerebrum is a near perfect simulation of the human brain, allowing it to take advantage of a larger variety of augments. " +
"But you should know about this BitRunner, since you have one of these yourself!",
stats: <>Allows sleeves to benefit from Stanek's Gift but it is less powerful if several are installed.</>,
factions: [
/*Technically in FactionNames.ChurchOfTheMachineGod but not really for display reasons */
],
}),
];
export const initBladeburnerAugmentations = (): Augmentation[] => [
@ -2035,6 +2060,62 @@ export const initChurchOfTheMachineGodAugmentations = (): Augmentation[] => [
stats: <>Stanek's Gift has no penalty.</>,
factions: [FactionNames.ChurchOfTheMachineGod],
}),
new Augmentation({
name: AugmentationNames.BigDsBigBrain,
isSpecial: true,
factions: [],
repCost: Infinity,
moneyCost: Infinity,
info:
"A chip containing the psyche of the greatest BitRunner to ever exists. " +
"Installing this relic significantly increases ALL of your stats. " +
"However it may have unintended consequence on the users mental well-being.",
stats: <>Grants access to unimaginable power.</>,
hacking: 2,
strength: 2,
defense: 2,
dexterity: 2,
agility: 2,
charisma: 2,
hacking_exp: 2,
strength_exp: 2,
defense_exp: 2,
dexterity_exp: 2,
agility_exp: 2,
charisma_exp: 2,
hacking_chance: 2,
hacking_speed: 2,
hacking_money: 2,
hacking_grow: 2,
company_rep: 2,
faction_rep: 2,
crime_money: 2,
crime_success: 2,
work_money: 2,
hacknet_node_money: 2,
hacknet_node_purchase_cost: 0.5,
hacknet_node_ram_cost: 0.5,
hacknet_node_core_cost: 0.5,
hacknet_node_level_cost: 0.5,
bladeburner_max_stamina: 2,
bladeburner_stamina_gain: 2,
bladeburner_analysis: 2,
bladeburner_success_chance: 2,
startingMoney: 1e12,
programs: [
Programs.BruteSSHProgram.name,
Programs.FTPCrackProgram.name,
Programs.RelaySMTPProgram.name,
Programs.HTTPWormProgram.name,
Programs.SQLInjectProgram.name,
Programs.DeepscanV1.name,
Programs.DeepscanV2.name,
Programs.ServerProfiler.name,
Programs.AutoLink.name,
Programs.Formulas.name,
],
}),
];
export function initNeuroFluxGovernor(): Augmentation {
@ -2044,15 +2125,13 @@ export function initNeuroFluxGovernor(): Augmentation {
repCost: 500,
moneyCost: 750e3,
info:
"A device that is embedded in the back of the neck. The NeuroFlux Governor " +
"monitors and regulates nervous impulses coming to and from the spinal column, " +
"essentially 'governing' the body. By doing so, it improves the functionality of the " +
"body's nervous system.",
"Undetectable adamantium nanobots injected in the users bloodstream. The NeuroFlux Governor " +
"monitors and regulates all aspects of the human body, essentially 'governing' the body. " +
"By doing so, it improves the users performance for most actions.",
stats: (
<>
This special augmentation can be leveled up infinitely. Each level of this augmentation increases MOST
multipliers by 1% (+{(donationBonus * 100).toFixed(6)}% boosted by real life blood donations), stacking
multiplicatively.
multipliers by 1% (+{(donationBonus * 100).toFixed(6)}%), stacking multiplicatively.
</>
),
isSpecial: true,

@ -90,8 +90,13 @@ export enum AugmentationNames {
BrachiBlades = "BrachiBlades",
BionicArms = "Bionic Arms",
SNA = "Social Negotiation Assistant (S.N.A)",
HydroflameLeftArm = "Hydroflame Left Arm",
CongruityImplant = "nickofolas Congruity Implant",
HydroflameLeftArm = "Hydroflame Left Arm",
BigDsBigBrain = "BigD's Big ... Brain",
ZOE = "Z.O.Ë.",
// UnnamedAug2 = "UnnamedAug2",
// Bladeburner augs
EsperEyewear = "EsperTech Bladeburner Eyewear",
EMS4Recombination = "EMS-4 Recombination",
OrionShoulder = "ORION-MKIV Shoulder",

@ -10,8 +10,6 @@ import { PurchasedAugmentations } from "./PurchasedAugmentations";
import { SourceFilesElement } from "./SourceFiles";
import { canGetBonus } from "../../ExportBonus";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
@ -20,20 +18,31 @@ import Paper from "@mui/material/Paper";
import Container from "@mui/material/Container";
import { Settings } from "../../Settings/Settings";
import { ConfirmationModal } from "../../ui/React/ConfirmationModal";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Player } from "../../Player";
import { AugmentationNames } from "../data/AugmentationNames";
import { StaticAugmentations } from "../StaticAugmentations";
import { CONSTANTS } from "../../Constants";
import { formatNumber } from "../../utils/StringHelperFunctions";
import { Info } from "@mui/icons-material";
import { Link } from "@mui/material";
import { AlertEvents } from "../../ui/React/AlertManager";
interface NFGDisplayProps {
player: IPlayer;
}
const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
const level = player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
const NeuroFluxDisplay = (): React.ReactElement => {
const level = Player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0;
const openBloodDonation = () => {
AlertEvents.emit(
<>
<Typography variant="h5">Bitburner blood donation community program</Typography>
<Typography>
The blood donation program is a continuous real life event started on 2022-04-01. To participate simply go
donate blood, plasma, or platelets to your local organisation and take a picture as proof (hide your personal
information). Then send the proof to hydroflame on reddit or discord.
</Typography>
<Typography>Currently accumulated {CONSTANTS.Donations} donations.</Typography>
</>,
);
};
return level > 0 ? (
<Paper sx={{ p: 1 }}>
<Typography variant="h5" color={Settings.theme.info}>
@ -42,24 +51,24 @@ const NeuroFluxDisplay = ({ player }: NFGDisplayProps): React.ReactElement => {
<Typography color={Settings.theme.info}>
{StaticAugmentations[AugmentationNames.NeuroFluxGovernor].stats}
</Typography>
<Typography color={Settings.theme.info}>
The power of {AugmentationNames.NeuroFluxGovernor} increases with blood donations from players in real life.
Learn more <Link onClick={openBloodDonation}>here</Link>
</Typography>
</Paper>
) : (
<></>
);
};
interface EntropyDisplayProps {
player: IPlayer;
}
const EntropyDisplay = ({ player }: EntropyDisplayProps): React.ReactElement => {
return player.entropy > 0 ? (
const EntropyDisplay = (): React.ReactElement => {
return Player.entropy > 0 ? (
<Paper sx={{ p: 1 }}>
<Typography variant="h5" color={Settings.theme.error}>
Entropy Virus - Level {player.entropy}
Entropy Virus - Level {Player.entropy}
</Typography>
<Typography color={Settings.theme.error}>
<b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropy) * 100, 3)}%
<b>All multipliers decreased by:</b> {formatNumber((1 - CONSTANTS.EntropyEffect ** Player.entropy) * 100, 3)}%
(multiplicative)
</Typography>
</Paper>
@ -75,7 +84,6 @@ interface IProps {
export function AugmentationsRoot(props: IProps): React.ReactElement {
const [installOpen, setInstallOpen] = useState(false);
const player = use.Player();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((o) => !o);
@ -168,7 +176,7 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
<Box sx={{ display: "grid", width: "100%", gridTemplateColumns: "1fr 1fr" }}>
<Tooltip title={<Typography>'I never asked for this'</Typography>}>
<span>
<Button sx={{ width: "100%" }} disabled={player.queuedAugmentations.length === 0} onClick={doInstall}>
<Button sx={{ width: "100%" }} disabled={Player.queuedAugmentations.length === 0} onClick={doInstall}>
Install Augmentations
</Button>
</span>
@ -180,7 +188,7 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
</Tooltip>
</Box>
</Paper>
{player.queuedAugmentations.length > 0 ? (
{Player.queuedAugmentations.length > 0 ? (
<Box sx={{ display: "grid", gridTemplateColumns: "1fr 3fr" }}>
<PurchasedAugmentations />
<PlayerMultipliers />
@ -197,14 +205,14 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
my: 1,
display: "grid",
gridTemplateColumns: `repeat(${
+!!((player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0) > 0) +
+!!(player.entropy > 0)
+!!((Player.augmentations.find((e) => e.name === AugmentationNames.NeuroFluxGovernor)?.level ?? 0) > 0) +
+!!(Player.entropy > 0)
}, 1fr)`,
gap: 1,
}}
>
<NeuroFluxDisplay player={player} />
<EntropyDisplay player={player} />
<NeuroFluxDisplay />
<EntropyDisplay />
</Box>
<Box>

@ -12,14 +12,13 @@ import Tooltip from "@mui/material/Tooltip";
import React, { useState } from "react";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { StaticAugmentations } from "../StaticAugmentations";
import { AugmentationNames } from "../data/AugmentationNames";
export function InstalledAugmentations(): React.ReactElement {
const setRerender = useState(true)[1];
const player = use.Player();
const sourceAugs = player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor);
const sourceAugs = Player.augmentations.slice().filter((aug) => aug.name !== AugmentationNames.NeuroFluxGovernor);
const [selectedAug, setSelectedAug] = useState(sourceAugs[0]);

@ -252,7 +252,7 @@ export function PlayerMultipliers(): React.ReactElement {
},
];
if (Player.canAccessBladeburner()) {
if (Player.canAccessBladeburner() && BitNodeMultipliers.BladeburnerRank > 0) {
rightColData.push(
{
mult: "Bladeburner Success Chance",

@ -6,7 +6,7 @@ import { CheckBox, CheckBoxOutlineBlank, CheckCircle, NewReleases, Report } from
import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
import React, { useState } from "react";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Augmentation } from "../Augmentation";
@ -15,12 +15,11 @@ import { StaticAugmentations } from "../StaticAugmentations";
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
interface IPreReqsProps {
player: IPlayer;
aug: Augmentation;
}
const PreReqs = (props: IPreReqsProps): React.ReactElement => {
const ownedPreReqs = props.aug.prereqs.filter((aug) => props.player.hasAugmentation(aug));
const ownedPreReqs = props.aug.prereqs.filter((aug) => Player.hasAugmentation(aug));
const hasPreReqs = props.aug.prereqs.length > 0 && ownedPreReqs.length === props.aug.prereqs.length;
return (
@ -32,7 +31,7 @@ const PreReqs = (props: IPreReqsProps): React.ReactElement => {
</Typography>
{props.aug.prereqs.map((preAug) => (
<Requirement
fulfilled={props.player.hasAugmentation(preAug)}
fulfilled={Player.hasAugmentation(preAug)}
value={preAug}
color={Settings.theme.money}
key={preAug}
@ -68,7 +67,6 @@ const PreReqs = (props: IPreReqsProps): React.ReactElement => {
};
interface IExclusiveProps {
player: IPlayer;
aug: Augmentation;
}
@ -85,18 +83,16 @@ const Exclusive = (props: IExclusiveProps): React.ReactElement => {
<li>
<b>{props.aug.factions[0]}</b> faction
</li>
{props.player.isAwareOfGang() && !props.aug.isSpecial && (
{Player.isAwareOfGang() && !props.aug.isSpecial && (
<li>
Certain <b>gangs</b>
</li>
)}
{props.player.canAccessGrafting() &&
!props.aug.isSpecial &&
props.aug.name !== AugmentationNames.TheRedPill && (
<li>
<b>Grafting</b>
</li>
)}
{Player.canAccessGrafting() && !props.aug.isSpecial && props.aug.name !== AugmentationNames.TheRedPill && (
<li>
<b>Grafting</b>
</li>
)}
</Typography>
</ul>
</>
@ -130,10 +126,9 @@ const Requirement = (props: IReqProps): React.ReactElement => {
interface IPurchasableAugsProps {
augNames: string[];
ownedAugNames: string[];
player: IPlayer;
canPurchase: (player: IPlayer, aug: Augmentation) => boolean;
purchaseAugmentation: (player: IPlayer, aug: Augmentation, showModal: (open: boolean) => void) => void;
canPurchase: (aug: Augmentation) => boolean;
purchaseAugmentation: (aug: Augmentation, showModal: (open: boolean) => void) => void;
rep?: number;
sleeveAugs?: boolean;
@ -167,7 +162,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
const [open, setOpen] = useState(false);
const aug = StaticAugmentations[props.augName];
const augCosts = aug.getCost(props.parent.player);
const augCosts = aug.getCost();
const cost = props.parent.sleeveAugs ? aug.baseCost : augCosts.moneyCost;
const repCost = augCosts.repCost;
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
@ -195,11 +190,11 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
<Box sx={{ display: "flex", alignItems: "center" }}>
<Button
onClick={() =>
props.parent.purchaseAugmentation(props.parent.player, aug, (open): void => {
props.parent.purchaseAugmentation(aug, (open): void => {
setOpen(open);
})
}
disabled={!props.parent.canPurchase(props.parent.player, aug) || props.owned}
disabled={!props.parent.canPurchase(aug) || props.owned}
sx={{ width: "48px", height: "36px", float: "left", clear: "none", mr: 1 }}
>
{props.owned ? "Owned" : "Buy"}
@ -212,8 +207,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
<>
<Typography variant="h5">
{props.augName}
{props.augName === AugmentationNames.NeuroFluxGovernor &&
` - Level ${aug.getLevel(props.parent.player)}`}
{props.augName === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel()}`}
</Typography>
<Typography>{description}</Typography>
</>
@ -226,20 +220,16 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
whiteSpace: "nowrap",
overflow: "hidden",
color:
props.owned || !props.parent.canPurchase(props.parent.player, aug)
? Settings.theme.disabled
: Settings.theme.primary,
props.owned || !props.parent.canPurchase(aug) ? Settings.theme.disabled : Settings.theme.primary,
}}
>
{aug.name}
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel(props.parent.player)}`}
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${aug.getLevel()}`}
</Typography>
</Tooltip>
{aug.factions.length === 1 && !props.parent.sleeveAugs && (
<Exclusive player={props.parent.player} aug={aug} />
)}
{aug.prereqs.length > 0 && !props.parent.sleeveAugs && <PreReqs player={props.parent.player} aug={aug} />}
{aug.factions.length === 1 && !props.parent.sleeveAugs && <Exclusive aug={aug} />}
{aug.prereqs.length > 0 && !props.parent.sleeveAugs && <PreReqs aug={aug} />}
</Box>
</Box>
</Box>
@ -247,7 +237,7 @@ export function PurchasableAugmentation(props: IPurchasableAugProps): React.Reac
{props.owned || (
<Box sx={{ display: "grid", alignItems: "center", gridTemplateColumns: "1fr 1fr" }}>
<Requirement
fulfilled={cost === 0 || props.parent.player.money > cost}
fulfilled={cost === 0 || Player.money > cost}
value={numeralWrapper.formatMoney(cost)}
color={Settings.theme.money}
/>

@ -6,7 +6,7 @@ import { purchaseAugmentation } from "../../Faction/FactionHelpers";
import { isRepeatableAug } from "../AugmentationHelpers";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@ -18,14 +18,12 @@ interface IProps {
}
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
if (typeof props.aug === "undefined" || typeof props.faction === "undefined") {
if (!props.aug || !props.faction) {
return <></>;
}
const player = use.Player();
function buy(): void {
if (!isRepeatableAug(props.aug as Augmentation) && player.hasAugmentation(props.aug as Augmentation)) {
if (!props.aug || (!isRepeatableAug(props.aug) && Player.hasAugmentation(props.aug.name))) {
return;
}
@ -44,7 +42,7 @@ export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
<br />
<br />
Would you like to purchase the {props.aug.name} Augmentation for&nbsp;
<Money money={props.aug.getCost(player).moneyCost} />?
<Money money={props.aug.getCost().moneyCost} />?
<br />
<br />
</Typography>

@ -1,6 +1,6 @@
import React from "react";
import { Player } from "../Player";
import { BitNodeMultipliers, IBitNodeMultipliers } from "./BitNodeMultipliers";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IMap } from "../types";
import { FactionNames } from "../Faction/data/FactionNames";
import { CityName } from "../Locations/data/CityNames";
@ -30,414 +30,425 @@ class BitNode {
}
export const BitNodes: IMap<BitNode> = {};
BitNodes["BitNode1"] = new BitNode(
1,
0,
"Source Genesis",
"The original BitNode",
(
<>
The first BitNode created by the Enders to imprison the minds of humans. It became the prototype and
testing-grounds for all of the BitNodes that followed.
<br />
<br />
This is the first BitNode that you play through. It has no special modifications or mechanics.
<br />
<br />
Destroying this BitNode will give you Source-File 1, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets the player start with 32GB of RAM on his/her home computer when
entering a new BitNode, and also increases all of the player's multipliers by:
<br />
<br />
Level 1: 16%
<br />
Level 2: 24%
<br />
Level 3: 28%
</>
),
);
BitNodes["BitNode2"] = new BitNode(
2,
0,
"Rise of the Underworld",
"From the shadows, they rose", //Gangs
(
<>
From the shadows, they rose.
<br />
<br />
Organized crime groups quickly filled the void of power left behind from the collapse of Western government in the
2050s. As society and civilization broke down, people quickly succumbed to the innate human impulse of evil and
savagery. The organized crime factions quickly rose to the top of the modern world.
<br />
<br />
Certain Factions ({FactionNames.SlumSnakes}, {FactionNames.Tetrads}, {FactionNames.TheSyndicate},{" "}
{FactionNames.TheDarkArmy}, {FactionNames.SpeakersForTheDead}, {FactionNames.NiteSec}, {FactionNames.TheBlackHand}
) give the player the ability to form and manage their own gangs. These gangs will earn the player money and
reputation with the corresponding Faction
<br />
Every Augmentation in the game will be available through the Factions listed above
<br />
<br />
Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes once your karma decreases
to a certain value. It also increases the player's crime success rate, crime money, and charisma multipliers by:
<br />
<br />
Level 1: 24%
<br />
Level 2: 36%
<br />
Level 3: 42%
</>
),
);
BitNodes["BitNode3"] = new BitNode(
3,
0,
"Corporatocracy",
"The Price of Civilization",
(
<>
Our greatest illusion is that a healthy society can revolve around a single-minded pursuit of wealth.
<br />
<br />
Sometime in the early 21st century economic and political globalization turned the world into a corporatocracy,
and it never looked back. Now, the privileged elite will happily bankrupt their own countrymen, decimate their own
community, and evict their neighbors from houses in their desperate bid to increase their wealth.
<br />
<br />
In this BitNode you can create and manage your own corporation. Running a successful corporation has the potential
of generating massive profits.
<br />
<br />
Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although some
BitNodes will disable this mechanic). This Source-File also increases your charisma and company salary multipliers
by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode4"] = new BitNode(
4,
1,
"The Singularity",
"The Man and the Machine",
(
<>
The Singularity has arrived. The human race is gone, replaced by artificially superintelligent beings that are
more machine than man. <br />
<br />
<br />
In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. These
functions allow you to control most aspects of the game through scripts, including working for factions/companies,
purchasing/installing Augmentations, and creating programs.
<br />
<br />
Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you access and use the Singularity Functions in other BitNodes.
Each level of this Source-File reduces the RAM cost of singularity functions.
</>
),
);
BitNodes["BitNode5"] = new BitNode(
5,
1,
"Artificial Intelligence",
"Posthuman",
(
<>
They said it couldn't be done. They said the human brain, along with its consciousness and intelligence, couldn't
be replicated. They said the complexity of the brain results from unpredictable, nonlinear interactions that
couldn't be modeled by 1's and 0's. They were wrong.
<br />
<br />
Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. Intelligence is
unique because it is permanent and persistent (it never gets reset back to 1). However gaining Intelligence
experience is much slower than other stats. Higher Intelligence levels will boost your production for many actions
in the game. <br />
<br />
In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function and let you start with
Formulas.exe, and will also raise all of your hacking-related multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode6"] = new BitNode(
6,
1,
FactionNames.Bladeburners,
"Like Tears in Rain",
(
<>
In the middle of the 21st century, {FactionNames.OmniTekIncorporated} began designing and manufacturing advanced
synthetic androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth
generation of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was
the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more
intelligent than the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the {FactionNames.Bladeburners} Division at the NSA, which provides a
new mechanic for progression.
<br />
<br />
Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the NSA's {FactionNames.Bladeburners} Division
in other BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your
combat stats by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode7"] = new BitNode(
7,
2,
`${FactionNames.Bladeburners} 2079`,
"More human than humans",
(
<>
In the middle of the 21st century, you were doing cutting-edge work at {FactionNames.OmniTekIncorporated} as part
of the AI design team for advanced synthetic androids, or Synthoids for short. You helped achieve a major
technological breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing a
hyperintelligent AI. Many argue that this was the first sentient AI ever created. This resulted in Synthoid models
that were stronger, faster, and more intelligent than the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the {FactionNames.Bladeburners} API, which allows you to access{" "}
{FactionNames.Bladeburners}
functionality through Netscript.
<br />
<br />
Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the {FactionNames.Bladeburners} Netscript API in
other BitNodes. In addition, this Source-File will increase all of your {FactionNames.Bladeburners} multipliers
by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode8"] = new BitNode(
8,
2,
"Ghost of Wall Street",
"Money never sleeps",
(
<>
You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.
<br />
<br />
In this BitNode:
<br />
<br />
You start with $250 million
<br />
You start with a WSE membership and access to the TIX API
<br />
You are able to short stocks and place different types of orders (limit/stop)
<br />
<br />
Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanent access to WSE and TIX API
<br />
Level 2: Ability to short stocks in other BitNodes
<br />
Level 3: Ability to use limit/stop orders in other BitNodes
<br />
<br />
This Source-File also increases your hacking growth multipliers by:
<br />
Level 1: 12%
<br />
Level 2: 18%
<br />
Level 3: 21%
</>
),
);
BitNodes["BitNode9"] = new BitNode(
9,
2,
"Hacktocracy",
"Hacknet Unleashed",
(
<>
When {FactionNames.FulcrumSecretTechnologies} released their open-source Linux distro Chapeau, it quickly became
the OS of choice for the underground hacking community. Chapeau became especially notorious for powering the
Hacknet, a global, decentralized network used for nefarious purposes. {FactionNames.FulcrumSecretTechnologies}{" "}
quickly abandoned the project and dissociated themselves from it.
<br />
<br />
This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate hashes,
which can be spent on a variety of different upgrades.
<br />
<br />
Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanently unlocks the Hacknet Server in other BitNodes
<br />
Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode
<br />
Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
<br />
<br />
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
Augmentations)
<br />
<br />
This Source-File also increases your hacknet multipliers by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode10"] = new BitNode(
10,
2,
"Digital Carbon",
"Your body is not who you are",
(
<>
In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people to digitize their
consciousness. Their consciousness could then be transferred into Synthoids or other bodies by trasmitting the
digitized data. Human bodies became nothing more than 'sleeves' for the human consciousness. Mankind had finally
achieved immortality - at least for those that could afford it.
<br />
<br />
This BitNode unlocks Sleeve technology. Sleeve technology allows you to:
<br />
<br />
1. Grafting: Visit VitaLife in New Tokyo to be able to obtain Augmentations without needing to install
<br />
2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks
synchronously.
<br />
<br />
Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. Each level of this
Source-File also grants you a Duplicate Sleeve
</>
),
);
BitNodes["BitNode11"] = new BitNode(
11,
1,
"The Big Crash",
"Okay. Sell it all.",
(
<>
The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around
the world. It was this period of disorder that eventually lead to the governmental reformation of many global
superpowers, most notably the USA and China. But just as the world was slowly beginning to recover from these dark
times, financial catastrophe hit.
<br />
<br />
In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of
this chaos and confusion, hackers were able to steal billions of dollars from the world's largest electronic
banks, prompting an international banking crisis as governments were unable to bail out insolvent banks. Now, the
world is slowly crumbling in the middle of the biggest economic crisis of all time.
<br />
<br />
Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH the player's salary and
reputation gain rate at that company by 1% per favor (rather than just the reputation gain). This Source-File also
increases the player's company salary and reputation gain multipliers by:
<br />
<br />
Level 1: 32%
<br />
Level 2: 48%
<br />
Level 3: 56%
<br />
<br />
It also reduces the price increase for every aug bought by:
<br />
<br />
Level 1: 4%
<br />
Level 2: 6%
<br />
Level 3: 7%
</>
),
);
BitNodes["BitNode12"] = new BitNode(
12,
0,
"The Recursion",
"Repeat.",
(
<>
To iterate is human, to recurse divine.
<br />
<br />
Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give you
Source-File 12, or if you already have this Source-File it will upgrade its level. There is no maximum level for
Source-File 12. Each level of Source-File 12 lets you start any BitNodes with NeuroFlux Governor equal to the
level of this source file.
</>
),
);
BitNodes["BitNode13"] = new BitNode(
13,
2,
"They're lunatics",
"1 step back, 2 steps forward",
(
<>
With the invention of Augmentations in the 2040s a religious group known as the{" "}
{FactionNames.ChurchOfTheMachineGod} has rallied far more support than anyone would have hoped.
<br />
<br />
Their leader, Allison "Mother" Stanek is said to have created her own Augmentation whose power goes beyond any
other. Find her in {CityName.Chongqing} and gain her trust.
<br />
<br />
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets the {FactionNames.ChurchOfTheMachineGod} appear in other
BitNodes.
<br />
<br />
Each level of this Source-File increases the size of Stanek's Gift.
</>
),
);
export function initBitNodes() {
BitNodes["BitNode1"] = new BitNode(
1,
0,
"Source Genesis",
"The original BitNode",
(
<>
The first BitNode created by the Enders to imprison the minds of humans. It became the prototype and
testing-grounds for all of the BitNodes that followed.
<br />
<br />
This is the first BitNode that you play through. It has no special modifications or mechanics.
<br />
<br />
Destroying this BitNode will give you Source-File 1, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets the player start with 32GB of RAM on his/her home computer
when entering a new BitNode, and also increases all of the player's multipliers by:
<br />
<br />
Level 1: 16%
<br />
Level 2: 24%
<br />
Level 3: 28%
</>
),
);
BitNodes["BitNode2"] = new BitNode(
2,
0,
"Rise of the Underworld",
"From the shadows, they rose", //Gangs
(
<>
From the shadows, they rose.
<br />
<br />
Organized crime groups quickly filled the void of power left behind from the collapse of Western government in
the 2050s. As society and civilization broke down, people quickly succumbed to the innate human impulse of evil
and savagery. The organized crime factions quickly rose to the top of the modern world.
<br />
<br />
Certain Factions ({FactionNames.SlumSnakes}, {FactionNames.Tetrads}, {FactionNames.TheSyndicate},{" "}
{FactionNames.TheDarkArmy}, {FactionNames.SpeakersForTheDead}, {FactionNames.NiteSec},{" "}
{FactionNames.TheBlackHand}
) give the player the ability to form and manage their own gangs. These gangs will earn the player money and
reputation with the corresponding Faction
<br />
Every Augmentation in the game will be available through the Factions listed above
<br />
<br />
Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes once your karma
decreases to a certain value. It also increases the player's crime success rate, crime money, and charisma
multipliers by:
<br />
<br />
Level 1: 24%
<br />
Level 2: 36%
<br />
Level 3: 42%
</>
),
);
BitNodes["BitNode3"] = new BitNode(
3,
0,
"Corporatocracy",
"The Price of Civilization",
(
<>
Our greatest illusion is that a healthy society can revolve around a single-minded pursuit of wealth.
<br />
<br />
Sometime in the early 21st century economic and political globalization turned the world into a corporatocracy,
and it never looked back. Now, the privileged elite will happily bankrupt their own countrymen, decimate their
own community, and evict their neighbors from houses in their desperate bid to increase their wealth.
<br />
<br />
In this BitNode you can create and manage your own corporation. Running a successful corporation has the
potential of generating massive profits.
<br />
<br />
Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although some
BitNodes will disable this mechanic) and level 3 permanently unlocks the full API. This Source-File also
increases your charisma and company salary multipliers by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode4"] = new BitNode(
4,
1,
"The Singularity",
"The Man and the Machine",
(
<>
The Singularity has arrived. The human race is gone, replaced by artificially superintelligent beings that are
more machine than man. <br />
<br />
<br />
In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. These
functions allow you to control most aspects of the game through scripts, including working for
factions/companies, purchasing/installing Augmentations, and creating programs.
<br />
<br />
Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you access and use the Singularity Functions in other
BitNodes. Each level of this Source-File reduces the RAM cost of singularity functions:
<br />
Level 1: 16x
<br />
Level 2: 4x
<br />
Level 3: 1x
</>
),
);
BitNodes["BitNode5"] = new BitNode(
5,
1,
"Artificial Intelligence",
"Posthuman",
(
<>
They said it couldn't be done. They said the human brain, along with its consciousness and intelligence,
couldn't be replicated. They said the complexity of the brain results from unpredictable, nonlinear interactions
that couldn't be modeled by 1's and 0's. They were wrong.
<br />
<br />
Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. Intelligence is
unique because it is permanent and persistent (it never gets reset back to 1). However gaining Intelligence
experience is much slower than other stats. Higher Intelligence levels will boost your production for many
actions in the game. <br />
<br />
In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function and let you start with
Formulas.exe, and will also raise all of your hacking-related multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode6"] = new BitNode(
6,
1,
FactionNames.Bladeburners,
"Like Tears in Rain",
(
<>
In the middle of the 21st century, {FactionNames.OmniTekIncorporated} began designing and manufacturing advanced
synthetic androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth
generation of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was
the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more
intelligent than the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the {FactionNames.Bladeburners} Division at the NSA, which provides a
new mechanic for progression.
<br />
<br />
Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the NSA's {FactionNames.Bladeburners} Division
in other BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your
combat stats by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode7"] = new BitNode(
7,
2,
`${FactionNames.Bladeburners} 2079`,
"More human than humans",
(
<>
In the middle of the 21st century, you were doing cutting-edge work at {FactionNames.OmniTekIncorporated} as
part of the AI design team for advanced synthetic androids, or Synthoids for short. You helped achieve a major
technological breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing
a hyperintelligent AI. Many argue that this was the first sentient AI ever created. This resulted in Synthoid
models that were stronger, faster, and more intelligent than the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the {FactionNames.Bladeburners} API, which allows you to access{" "}
{FactionNames.Bladeburners} functionality through Netscript.
<br />
<br />
Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the {FactionNames.Bladeburners} Netscript API
in other BitNodes. In addition, this Source-File will increase all of your {FactionNames.Bladeburners}{" "}
multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode8"] = new BitNode(
8,
2,
"Ghost of Wall Street",
"Money never sleeps",
(
<>
You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.
<br />
<br />
In this BitNode:
<br />
<br />
You start with $250 million
<br />
You start with a WSE membership and access to the TIX API
<br />
You are able to short stocks and place different types of orders (limit/stop)
<br />
<br />
Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanent access to WSE and TIX API
<br />
Level 2: Ability to short stocks in other BitNodes
<br />
Level 3: Ability to use limit/stop orders in other BitNodes
<br />
<br />
This Source-File also increases your hacking growth multipliers by:
<br />
Level 1: 12%
<br />
Level 2: 18%
<br />
Level 3: 21%
</>
),
);
BitNodes["BitNode9"] = new BitNode(
9,
2,
"Hacktocracy",
"Hacknet Unleashed",
(
<>
When {FactionNames.FulcrumSecretTechnologies} released their open-source Linux distro Chapeau, it quickly became
the OS of choice for the underground hacking community. Chapeau became especially notorious for powering the
Hacknet, a global, decentralized network used for nefarious purposes. {FactionNames.FulcrumSecretTechnologies}{" "}
quickly abandoned the project and dissociated themselves from it.
<br />
<br />
This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate
hashes, which can be spent on a variety of different upgrades.
<br />
<br />
Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanently unlocks the Hacknet Server in other BitNodes
<br />
Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode
<br />
Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
<br />
<br />
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
Augmentations)
<br />
<br />
This Source-File also increases your hacknet multipliers by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
</>
),
);
BitNodes["BitNode10"] = new BitNode(
10,
2,
"Digital Carbon",
"Your body is not who you are",
(
<>
In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people to digitize their
consciousness. Their consciousness could then be transferred into Synthoids or other bodies by transmitting the
digitized data. Human bodies became nothing more than 'sleeves' for the human consciousness. Mankind had finally
achieved immortality - at least for those that could afford it.
<br />
<br />
This BitNode unlocks Sleeve and grafting technologies. Sleeve technology allows you to:
<br />
<br />
1. Grafting: Visit VitaLife in New Tokyo to be able to obtain Augmentations without needing to install
<br />
2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks
synchronously.
<br />
<br />
Grafting technology allows you to graft Augmentations, which is an alternative way of installing Augmentations.
<br />
<br />
Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will upgrade
its level up to a maximum of 3. This Source-File unlocks Sleeve technology, and the Grafting API in other
BitNodes. Each level of this Source-File also grants you a Duplicate Sleeve
</>
),
);
BitNodes["BitNode11"] = new BitNode(
11,
1,
"The Big Crash",
"Okay. Sell it all.",
(
<>
The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around
the world. It was this period of disorder that eventually lead to the governmental reformation of many global
superpowers, most notably the USA and China. But just as the world was slowly beginning to recover from these
dark times, financial catastrophe hit.
<br />
<br />
In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of
this chaos and confusion, hackers were able to steal billions of dollars from the world's largest electronic
banks, prompting an international banking crisis as governments were unable to bail out insolvent banks. Now,
the world is slowly crumbling in the middle of the biggest economic crisis of all time.
<br />
<br />
Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will upgrade
its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH the player's
salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). This
Source-File also increases the player's company salary and reputation gain multipliers by:
<br />
<br />
Level 1: 32%
<br />
Level 2: 48%
<br />
Level 3: 56%
<br />
<br />
It also reduces the price increase for every aug bought by:
<br />
<br />
Level 1: 4%
<br />
Level 2: 6%
<br />
Level 3: 7%
</>
),
);
BitNodes["BitNode12"] = new BitNode(
12,
0,
"The Recursion",
"Repeat.",
(
<>
To iterate is human, to recurse divine.
<br />
<br />
Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give you
Source-File 12, or if you already have this Source-File it will upgrade its level. There is no maximum level for
Source-File 12. Each level of Source-File 12 lets you start any BitNodes with NeuroFlux Governor equal to the
level of this source file.
</>
),
);
BitNodes["BitNode13"] = new BitNode(
13,
2,
"They're lunatics",
"1 step back, 2 steps forward",
(
<>
With the invention of Augmentations in the 2040s a religious group known as the{" "}
{FactionNames.ChurchOfTheMachineGod} has rallied far more support than anyone would have hoped.
<br />
<br />
Their leader, Allison "Mother" Stanek is said to have created her own Augmentation whose power goes beyond any
other. Find her in {CityName.Chongqing} and gain her trust.
<br />
<br />
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File it will upgrade
its level up to a maximum of 3. This Source-File lets the {FactionNames.ChurchOfTheMachineGod} appear in other
BitNodes.
<br />
<br />
Each level of this Source-File increases the size of Stanek's Gift.
</>
),
);
}
export const defaultMultipliers: IBitNodeMultipliers = {
HackingLevelMultiplier: 1,
@ -646,6 +657,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}
case 8: {
return Object.assign(mults, {
BladeburnerRank: 0,
ScriptHackMoney: 0.3,
ScriptHackMoneyGain: 0,
ManualHackMoney: 0,
@ -867,6 +879,6 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
}
}
export function initBitNodeMultipliers(p: IPlayer): void {
Object.assign(BitNodeMultipliers, getBitNodeMultipliers(p.bitNodeN, p.sourceFileLvl(p.bitNodeN)));
export function initBitNodeMultipliers(): void {
Object.assign(BitNodeMultipliers, getBitNodeMultipliers(Player.bitNodeN, Player.sourceFileLvl(Player.bitNodeN)));
}

@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import { Router } from "../../ui/GameRoot";
import { EventEmitter } from "../../utils/EventEmitter";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@ -8,10 +8,9 @@ import Button from "@mui/material/Button";
export const BitFlumeEvent = new EventEmitter<[]>();
export function BitFlumeModal(): React.ReactElement {
const router = use.Router();
const [open, setOpen] = useState(false);
function flume(): void {
router.toBitVerse(true, false);
Router.toBitVerse(true, false);
setOpen(false);
}

@ -5,7 +5,7 @@ import { uniqueId } from "lodash";
import React from "react";
import { SpecialServers } from "../../Server/data/SpecialServers";
import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import { StatsRow } from "../../ui/React/StatsRow";
import { defaultMultipliers, getBitNodeMultipliers } from "../BitNode";
import { IBitNodeMultipliers } from "../BitNodeMultipliers";
@ -33,13 +33,12 @@ export function BitnodeMultiplierDescription({ n, level }: IProps): React.ReactE
}
export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElement => {
const player = use.Player();
// If a level argument has been provided, use that as the multiplier level
// 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,
// or if it's BN12, ∞
const maxSfLevel = n === 12 ? Infinity : 3;
const mults = getBitNodeMultipliers(n, level ?? Math.min(player.sourceFileLvl(n) + 1, maxSfLevel));
const mults = getBitNodeMultipliers(n, level ?? Math.min(Player.sourceFileLvl(n) + 1, maxSfLevel));
return (
<Box sx={{ columnCount: 2, columnGap: 1, mb: -2 }}>
@ -277,8 +276,15 @@ function InfiltrationMults({ mults }: IMultsProps): React.ReactElement {
}
function BladeburnerMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
if (!player.canAccessBladeburner()) return <></>;
if (!Player.canAccessBladeburner()) return <></>;
if (mults.BladeburnerRank === 0) {
const rows: IBNMultRows = {
BladeburnerRank: { name: "Disabled", content: "" },
};
return <BNMultTable sectionName="Bladeburner" rowData={rows} mults={mults} />;
}
const rows: IBNMultRows = {
BladeburnerRank: { name: "Rank Gain" },
@ -289,8 +295,7 @@ function BladeburnerMults({ mults }: IMultsProps): React.ReactElement {
}
function StanekMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
if (!player.canAccessCotMG()) return <></>;
if (!Player.canAccessCotMG()) return <></>;
const extraSize = mults.StaneksGiftExtraSize.toFixed(3);
const rows: IBNMultRows = {
@ -305,8 +310,7 @@ function StanekMults({ mults }: IMultsProps): React.ReactElement {
}
function GangMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
if (player.bitNodeN !== 2 && player.sourceFileLvl(2) <= 0) return <></>;
if (Player.bitNodeN !== 2 && Player.sourceFileLvl(2) <= 0) return <></>;
const rows: IBNMultRows = {
GangSoftcap: {
@ -320,8 +324,18 @@ function GangMults({ mults }: IMultsProps): React.ReactElement {
}
function CorporationMults({ mults }: IMultsProps): React.ReactElement {
const player = use.Player();
if (!player.canAccessCorporation()) return <></>;
if (!Player.canAccessCorporation()) return <></>;
if (mults.CorporationSoftcap < 0.15) {
const rows: IBNMultRows = {
CorporationSoftcap: {
name: "Disabled",
content: "",
},
};
return <BNMultTable sectionName="Corporation" rowData={rows} mults={mults} />;
}
const rows: IBNMultRows = {
CorporationSoftcap: {

@ -1,10 +1,8 @@
import React, { useState } from "react";
import { IRouter } from "../../ui/Router";
import { BitNodes } from "../BitNode";
import { enterBitNode } from "../../RedPill";
import { PortalModal } from "./PortalModal";
import { CinematicText } from "../../ui/React/CinematicText";
import { use } from "../../ui/Context";
import { Player } from "../../Player";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import IconButton from "@mui/material/IconButton";
@ -46,7 +44,6 @@ interface IPortalProps {
level: number;
destroyedBitNode: number;
flume: boolean;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
function BitNodePortal(props: IPortalProps): React.ReactElement {
const [portalOpen, setPortalOpen] = useState(false);
@ -105,7 +102,6 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
onClose={() => setPortalOpen(false)}
n={props.n}
level={props.level}
enter={props.enter}
destroyedBitNode={props.destroyedBitNode}
flume={props.flume}
/>
@ -118,13 +114,10 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
interface IProps {
flume: boolean;
quick: boolean;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
export function BitverseRoot(props: IProps): React.ReactElement {
const player = use.Player();
const enter = enterBitNode;
const destroyed = player.bitNodeN;
const destroyed = Player.bitNodeN;
const [destroySequence, setDestroySequence] = useState(!props.quick);
if (destroySequence) {
@ -158,7 +151,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
}
const nextSourceFileLvl = (n: number): number => {
const lvl = player.sourceFileLvl(n);
const lvl = Player.sourceFileLvl(n);
if (n !== destroyed) {
return lvl;
}
@ -181,7 +174,6 @@ export function BitverseRoot(props: IProps): React.ReactElement {
key={node.number}
n={node.number}
level={nextSourceFileLvl(node.number)}
enter={enter}
flume={props.flume}
destroyedBitNode={destroyed}
/>
@ -234,19 +226,19 @@ export function BitverseRoot(props: IProps): React.ReactElement {
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={n(13)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={n(13)} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={n(10)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={n(11)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={n(9)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={n(12)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={n(10)} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={n(11)} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={n(9)} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={n(12)} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={n(7)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={n(8)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={n(7)} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={n(8)} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={n(5)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={n(6)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={n(5)} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={n(6)} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={n(1)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={n(2)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={n(3)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={n(4)} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={n(1)} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={n(2)} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={n(3)} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={n(4)} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
<br />

@ -1,8 +1,7 @@
import React from "react";
import { enterBitNode } from "../../RedPill";
import { BitNodes } from "../BitNode";
import { IRouter } from "../../ui/Router";
import { use } from "../../ui/Context";
import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@ -15,11 +14,9 @@ interface IProps {
level: number;
destroyedBitNode: number;
flume: boolean;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
export function PortalModal(props: IProps): React.ReactElement {
const router = use.Router();
const bitNodeKey = "BitNode" + props.n;
const bitNode = BitNodes[bitNodeKey];
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
@ -48,7 +45,7 @@ export function PortalModal(props: IProps): React.ReactElement {
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
autoFocus={true}
onClick={() => {
props.enter(router, props.flume, props.destroyedBitNode, props.n);
enterBitNode(props.flume, props.destroyedBitNode, props.n);
props.onClose();
}}
>

@ -3,9 +3,12 @@ import { getRandomInt } from "../utils/helpers/getRandomInt";
import { addOffset } from "../utils/helpers/addOffset";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { BladeburnerConstants } from "./data/Constants";
import { IBladeburner } from "./IBladeburner";
import { IAction, ISuccessChanceParams } from "./IAction";
import { IPerson } from "../PersonObjects/IPerson";
import { Bladeburner } from "./Bladeburner";
import { Person } from "../PersonObjects/Person";
interface ISuccessChanceParams {
est: boolean;
}
class StatsMultiplier {
[key: string]: number;
@ -41,7 +44,7 @@ export interface IActionParams {
teamCount?: number;
}
export class Action implements IAction {
export class Action {
name = "";
// Difficulty scales with level. See getDifficulty() method
@ -153,7 +156,7 @@ export class Action implements IAction {
* Tests for success. Should be called when an action has completed
* @param inst {Bladeburner} - Bladeburner instance
*/
attempt(inst: IBladeburner, person: IPerson): boolean {
attempt(inst: Bladeburner, person: Person): boolean {
return Math.random() < this.getSuccessChance(inst, person);
}
@ -162,7 +165,7 @@ export class Action implements IAction {
return 1;
}
getActionTime(inst: IBladeburner, person: IPerson): number {
getActionTime(inst: Bladeburner, person: Person): number {
const difficulty = this.getDifficulty();
let baseTime = difficulty / BladeburnerConstants.DifficultyToTimeFactor;
const skillFac = inst.skillMultipliers.actionTime; // Always < 1
@ -183,16 +186,16 @@ export class Action implements IAction {
// For actions that have teams. To be implemented by subtypes.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getTeamSuccessBonus(inst: IBladeburner): number {
getTeamSuccessBonus(inst: Bladeburner): number {
return 1;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getActionTypeSkillSuccessBonus(inst: IBladeburner): number {
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
return 1;
}
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number {
getChaosCompetencePenalty(inst: Bladeburner, params: ISuccessChanceParams): number {
const city = inst.getCurrentCity();
if (params.est) {
return Math.pow(city.popEst / BladeburnerConstants.PopulationThreshold, BladeburnerConstants.PopulationExponent);
@ -201,7 +204,7 @@ export class Action implements IAction {
}
}
getChaosDifficultyBonus(inst: IBladeburner /*, params: ISuccessChanceParams*/): number {
getChaosDifficultyBonus(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
const city = inst.getCurrentCity();
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
@ -212,7 +215,7 @@ export class Action implements IAction {
return 1;
}
getEstSuccessChance(inst: IBladeburner, person: IPerson): [number, number] {
getEstSuccessChance(inst: Bladeburner, person: Person): [number, number] {
function clamp(x: number): number {
return Math.max(0, Math.min(x, 1));
}
@ -233,7 +236,7 @@ export class Action implements IAction {
* @params - options:
* est (bool): Get success chance estimate instead of real success chance
*/
getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams = { est: false }): number {
getSuccessChance(inst: Bladeburner, person: Person, params: ISuccessChanceParams = { est: false }): number {
if (inst == null) {
throw new Error("Invalid Bladeburner instance passed into Action.getSuccessChance");
}

@ -1,4 +1,3 @@
import { IActionIdentifier } from "./IActionIdentifier";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
interface IParams {
@ -6,7 +5,7 @@ interface IParams {
type?: number;
}
export class ActionIdentifier implements IActionIdentifier {
export class ActionIdentifier {
name = "";
type = -1;

@ -12,11 +12,11 @@ export class BlackOperation extends Operation {
return 1.5;
}
getChaosCompetencePenalty(/*inst: IBladeburner, params: ISuccessChanceParams*/): number {
getChaosCompetencePenalty(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
return 1;
}
getChaosDifficultyBonus(/*inst: IBladeburner, params: ISuccessChanceParams*/): number {
getChaosDifficultyBonus(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
return 1;
}

@ -1,6 +1,4 @@
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { IBladeburner } from "./IBladeburner";
import { IActionIdentifier } from "./IActionIdentifier";
import { ActionIdentifier } from "./ActionIdentifier";
import { ActionTypes } from "./data/ActionTypes";
import { Growths } from "./data/Growths";
@ -13,11 +11,11 @@ import { formatNumber } from "../utils/StringHelperFunctions";
import { Skills } from "./Skills";
import { Skill } from "./Skill";
import { City } from "./City";
import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Action } from "./Action";
import { Player } from "../Player";
import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IPerson } from "../PersonObjects/IPerson";
import { IRouter } from "../ui/Router";
import { Person } from "../PersonObjects/Person";
import { Router } from "../ui/GameRoot";
import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt";
@ -25,7 +23,6 @@ import { BladeburnerConstants } from "./data/Constants";
import { numeralWrapper } from "../ui/numeralFormat";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { dialogBoxCreate } from "../ui/React/DialogBox";
@ -39,13 +36,13 @@ import { KEY } from "../utils/helpers/keyCodes";
import { isSleeveInfiltrateWork } from "../PersonObjects/Sleeve/Work/SleeveInfiltrateWork";
import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportWork";
interface BlackOpsAttempt {
export interface BlackOpsAttempt {
error?: string;
isAvailable?: boolean;
action?: BlackOperation;
}
export class Bladeburner implements IBladeburner {
export class Bladeburner {
numHosp = 0;
moneyLost = 0;
rank = 0;
@ -67,7 +64,7 @@ export class Bladeburner implements IBladeburner {
actionTimeCurrent = 0;
actionTimeOverflow = 0;
action: IActionIdentifier = new ActionIdentifier({
action: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"],
});
@ -89,18 +86,18 @@ export class Bladeburner implements IBladeburner {
events: true,
};
automateEnabled = false;
automateActionHigh: IActionIdentifier = new ActionIdentifier({
automateActionHigh: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"],
});
automateThreshHigh = 0;
automateActionLow: IActionIdentifier = new ActionIdentifier({
automateActionLow: ActionIdentifier = new ActionIdentifier({
type: ActionTypes["Idle"],
});
automateThreshLow = 0;
consoleHistory: string[] = [];
consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"];
constructor(player?: IPlayer) {
constructor() {
for (let i = 0; i < BladeburnerConstants.CityNames.length; ++i) {
this.cities[BladeburnerConstants.CityNames[i]] = new City(BladeburnerConstants.CityNames[i]);
}
@ -108,16 +105,14 @@ export class Bladeburner implements IBladeburner {
this.updateSkillMultipliers(); // Calls resetSkillMultipliers()
// Max Stamina is based on stats and Bladeburner-specific bonuses
if (player) this.calculateMaxStamina(player);
this.calculateMaxStamina();
this.stamina = this.maxStamina;
this.create();
}
getCurrentCity(): City {
const city = this.cities[this.city];
if (!(city instanceof City)) {
throw new Error("Bladeburner.getCurrentCity() did not properly return a City object");
}
if (!city) throw new Error("Invalid city in Bladeburner.getCurrentCity()");
return city;
}
@ -125,7 +120,7 @@ export class Bladeburner implements IBladeburner {
return Math.min(1, this.stamina / (0.5 * this.maxStamina));
}
canAttemptBlackOp(actionId: IActionIdentifier): BlackOpsAttempt {
canAttemptBlackOp(actionId: ActionIdentifier): BlackOpsAttempt {
// Safety measure - don't repeat BlackOps that are already done
if (this.blackops[actionId.name] != null) {
return { error: "Tried to start a Black Operation that had already been completed" };
@ -162,7 +157,8 @@ export class Bladeburner implements IBladeburner {
return { isAvailable: true, action };
}
startAction(person: IPerson, actionId: IActionIdentifier): void {
/** This function is only for the player. Sleeves use their own functions to perform blade work. */
startAction(actionId: ActionIdentifier): void {
if (actionId == null) return;
this.action = actionId;
this.actionTimeCurrent = 0;
@ -179,7 +175,7 @@ export class Bladeburner implements IBladeburner {
if (action.count < 1) {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this, person);
this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) {
exceptionAlert(e);
}
@ -196,7 +192,7 @@ export class Bladeburner implements IBladeburner {
if (actionId.name === "Raid" && this.getCurrentCity().comms === 0) {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this, person);
this.actionTimeToComplete = action.getActionTime(this, Player);
} catch (e: unknown) {
exceptionAlert(e);
}
@ -214,14 +210,14 @@ export class Bladeburner implements IBladeburner {
if (testBlackOp.action === undefined) {
throw new Error("action should not be null");
}
this.actionTimeToComplete = testBlackOp.action.getActionTime(this, person);
this.actionTimeToComplete = testBlackOp.action.getActionTime(this, Player);
} catch (e: unknown) {
exceptionAlert(e);
}
break;
}
case ActionTypes["Recruitment"]:
this.actionTimeToComplete = this.getRecruitmentTime(person);
this.actionTimeToComplete = this.getRecruitmentTime(Player);
break;
case ActionTypes["Training"]:
case ActionTypes["FieldAnalysis"]:
@ -234,7 +230,7 @@ export class Bladeburner implements IBladeburner {
this.actionTimeToComplete = 60;
break;
default:
throw new Error("Invalid Action Type in startAction(Bladeburner,player, ): " + actionId.type);
throw new Error("Invalid Action Type in bladeburner.startAction(): " + actionId.type);
}
}
@ -252,7 +248,7 @@ export class Bladeburner implements IBladeburner {
this.updateSkillMultipliers();
}
executeConsoleCommands(player: IPlayer, commands: string): void {
executeConsoleCommands(commands: string): void {
try {
// Console History
if (this.consoleHistory[this.consoleHistory.length - 1] != commands) {
@ -264,7 +260,7 @@ export class Bladeburner implements IBladeburner {
const arrayOfCommands = commands.split(";");
for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]);
this.executeConsoleCommand(arrayOfCommands[i]);
}
} catch (e: unknown) {
exceptionAlert(e);
@ -309,7 +305,7 @@ export class Bladeburner implements IBladeburner {
}
// working on
getActionIdFromTypeAndName(type = "", name = ""): IActionIdentifier | null {
getActionIdFromTypeAndName(type = "", name = ""): ActionIdentifier | null {
if (type === "" || name === "") {
return null;
}
@ -394,7 +390,7 @@ export class Bladeburner implements IBladeburner {
return null;
}
executeStartConsoleCommand(player: IPlayer, args: string[]): void {
executeStartConsoleCommand(args: string[]): void {
if (args.length !== 3) {
this.postToConsole("Invalid usage of 'start' console command: start [type] [name]");
this.postToConsole("Use 'help start' for more info");
@ -407,7 +403,7 @@ export class Bladeburner implements IBladeburner {
if (GeneralActions[name] != null) {
this.action.type = ActionTypes[name];
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid action name specified: " + args[2]);
}
@ -417,7 +413,7 @@ export class Bladeburner implements IBladeburner {
if (this.contracts[name] != null) {
this.action.type = ActionTypes.Contract;
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid contract name specified: " + args[2]);
}
@ -429,7 +425,7 @@ export class Bladeburner implements IBladeburner {
if (this.operations[name] != null) {
this.action.type = ActionTypes.Operation;
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid Operation name specified: " + args[2]);
}
@ -441,7 +437,7 @@ export class Bladeburner implements IBladeburner {
if (BlackOperations[name] != null) {
this.action.type = ActionTypes.BlackOperation;
this.action.name = name;
this.startAction(player, this.action);
this.startAction(this.action);
} else {
this.postToConsole("Invalid BlackOp name specified: " + args[2]);
}
@ -542,7 +538,7 @@ export class Bladeburner implements IBladeburner {
case 3: {
const skillName = args[2];
const skill = Skills[skillName];
if (skill == null || !(skill instanceof Skill)) {
if (!skill) {
this.postToConsole("Invalid skill name (Note that it is case-sensitive): " + skillName);
break;
}
@ -684,10 +680,7 @@ export class Bladeburner implements IBladeburner {
".",
);
} else if (flag.toLowerCase().includes("en")) {
if (
!(this.automateActionLow instanceof ActionIdentifier) ||
!(this.automateActionHigh instanceof ActionIdentifier)
) {
if (!this.automateActionLow || !this.automateActionHigh) {
return this.log("Failed to enable automation. Actions were not set");
}
this.automateEnabled = true;
@ -820,7 +813,7 @@ export class Bladeburner implements IBladeburner {
return args;
}
executeConsoleCommand(player: IPlayer, command: string): void {
executeConsoleCommand(command: string): void {
command = command.trim();
command = command.replace(/\s\s+/g, " "); // Replace all whitespace w/ a single space
@ -845,7 +838,7 @@ export class Bladeburner implements IBladeburner {
this.executeSkillConsoleCommand(args);
break;
case "start":
this.executeStartConsoleCommand(player, args);
this.executeStartConsoleCommand(args);
break;
case "stop":
this.resetAction();
@ -898,9 +891,7 @@ export class Bladeburner implements IBladeburner {
// Choose random source/destination city for events
const sourceCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)];
const sourceCity = this.cities[sourceCityName];
if (!(sourceCity instanceof City)) {
throw new Error("sourceCity was not a City object in Bladeburner.randomEvent()");
}
if (!sourceCity) throw new Error("Invalid sourceCity in Bladeburner.randomEvent()");
let destCityName = BladeburnerConstants.CityNames[getRandomInt(0, 5)];
while (destCityName === sourceCityName) {
@ -908,9 +899,7 @@ export class Bladeburner implements IBladeburner {
}
const destCity = this.cities[destCityName];
if (!(sourceCity instanceof City) || !(destCity instanceof City)) {
throw new Error("sourceCity/destCity was not a City object in Bladeburner.randomEvent()");
}
if (!sourceCity || !destCity) throw new Error("Invalid sourceCity or destCity in Bladeburner.randomEvent()");
if (chance <= 0.05) {
// New Synthoid Community, 5%
@ -994,7 +983,7 @@ export class Bladeburner implements IBladeburner {
* @param action(Action obj) - Derived action class
* @param success(bool) - Whether action was successful
*/
getActionStats(action: IAction, success: boolean): ITaskTracker {
getActionStats(action: Action, person: Person, success: boolean): ITaskTracker {
const difficulty = action.getDifficulty();
/**
@ -1005,7 +994,7 @@ export class Bladeburner implements IBladeburner {
Math.pow(difficulty, BladeburnerConstants.DiffMultExponentialFactor) +
difficulty / BladeburnerConstants.DiffMultLinearFactor;
const time = this.actionTimeToComplete;
const time = action.getActionTime(this, person);
const successMult = success ? 1 : 0.5;
const unweightedGain = time * BladeburnerConstants.BaseStatGain * successMult * difficultyMult;
@ -1024,7 +1013,7 @@ export class Bladeburner implements IBladeburner {
};
}
getDiplomacyEffectiveness(person: IPerson): number {
getDiplomacyEffectiveness(person: Person): number {
// Returns a decimal by which the city's chaos level should be multiplied (e.g. 0.98)
const CharismaLinearFactor = 1e3;
const CharismaExponentialFactor = 0.045;
@ -1034,11 +1023,11 @@ export class Bladeburner implements IBladeburner {
return (100 - charismaEff) / 100;
}
getRecruitmentSuccessChance(person: IPerson): number {
getRecruitmentSuccessChance(person: Person): number {
return Math.pow(person.skills.charisma, 0.45) / (this.teamSize - this.sleeveSize + 1);
}
getRecruitmentTime(person: IPerson): number {
getRecruitmentTime(person: Person): number {
const effCharisma = person.skills.charisma * this.skillMultipliers.effCha;
const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90;
return Math.max(10, Math.round(BladeburnerConstants.BaseRecruitmentTimeNeeded - charismaFactor));
@ -1105,7 +1094,7 @@ export class Bladeburner implements IBladeburner {
}
}
completeOperation(success: boolean, player: IPlayer): void {
completeOperation(success: boolean): void {
if (this.action.type !== ActionTypes.Operation) {
throw new Error("completeOperation() called even though current action is not an Operation");
}
@ -1126,7 +1115,7 @@ export class Bladeburner implements IBladeburner {
const losses = getRandomInt(0, max);
this.teamSize -= losses;
if (this.teamSize < this.sleeveSize) {
const sup = player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
const r = Math.floor(Math.random() * sup.length);
sup[r].takeDamage(sup[r].hp.max);
@ -1201,7 +1190,7 @@ export class Bladeburner implements IBladeburner {
}
}
getActionObject(actionId: IActionIdentifier): IAction | null {
getActionObject(actionId: ActionIdentifier): Action | null {
/**
* Given an ActionIdentifier object, returns the corresponding
* GeneralAction, Contract, Operation, or BlackOperation object
@ -1231,7 +1220,7 @@ export class Bladeburner implements IBladeburner {
}
}
completeContract(success: boolean, actionIdent: IActionIdentifier): void {
completeContract(success: boolean, actionIdent: ActionIdentifier): void {
if (actionIdent.type !== ActionTypes.Contract) {
throw new Error("completeContract() called even though current action is not a Contract");
}
@ -1240,7 +1229,7 @@ export class Bladeburner implements IBladeburner {
switch (actionIdent.name) {
case "Tracking":
// Increase estimate accuracy by a relatively small amount
city.improvePopulationEstimateByCount(getRandomInt(100, 1e3));
city.improvePopulationEstimateByCount(getRandomInt(100, 1e3) * this.skillMultipliers.successChanceEstimate);
break;
case "Bounty Hunter":
city.changePopulationByCount(-1, { estChange: -1, estOffset: 0 });
@ -1256,7 +1245,7 @@ export class Bladeburner implements IBladeburner {
}
}
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer = true): ITaskTracker {
completeAction(person: Person, actionIdent: ActionIdentifier, isPlayer = true): ITaskTracker {
let retValue = createTaskTracker();
switch (actionIdent.type) {
case ActionTypes["Contract"]:
@ -1283,7 +1272,7 @@ export class Bladeburner implements IBladeburner {
// Process Contract/Operation success/failure
if (action.attempt(this, person)) {
retValue = this.getActionStats(action, true);
retValue = this.getActionStats(action, person, true);
++action.successes;
--action.count;
@ -1304,26 +1293,18 @@ export class Bladeburner implements IBladeburner {
this.changeRank(person, gain);
if (isOperation && this.logging.ops) {
this.log(
`${person.whoAmI()}: ` +
action.name +
" successfully completed! Gained " +
formatNumber(gain, 3) +
" rank",
`${person.whoAmI()}: ${action.name} successfully completed! Gained ${formatNumber(gain, 3)} rank`,
);
} else if (!isOperation && this.logging.contracts) {
this.log(
`${person.whoAmI()}: ` +
action.name +
" contract successfully completed! Gained " +
formatNumber(gain, 3) +
" rank and " +
numeralWrapper.formatMoney(moneyGain),
`${person.whoAmI()}: ${action.name} contract successfully completed! Gained ` +
`${formatNumber(gain, 3)} rank and ${numeralWrapper.formatMoney(moneyGain)}`,
);
}
}
isOperation ? this.completeOperation(true, player) : this.completeContract(true, actionIdent);
isOperation ? this.completeOperation(true) : this.completeContract(true, actionIdent);
} else {
retValue = this.getActionStats(action, false);
retValue = this.getActionStats(action, person, false);
++action.failures;
let loss = 0,
damage = 0;
@ -1335,7 +1316,7 @@ export class Bladeburner implements IBladeburner {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
this.hpLost += damage;
const cost = calculateHospitalizationCost(player, damage);
const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) {
++this.numHosp;
this.moneyLost += cost;
@ -1353,7 +1334,7 @@ export class Bladeburner implements IBladeburner {
} else if (!isOperation && this.logging.contracts) {
this.log(`${person.whoAmI()}: ` + action.name + " contract failed! " + logLossText);
}
isOperation ? this.completeOperation(false, player) : this.completeContract(false, actionIdent);
isOperation ? this.completeOperation(false) : this.completeContract(false, actionIdent);
}
if (action.autoLevel) {
action.level = action.maxLevel;
@ -1386,7 +1367,7 @@ export class Bladeburner implements IBladeburner {
let teamLossMax;
if (action.attempt(this, person)) {
retValue = this.getActionStats(action, true);
retValue = this.getActionStats(action, person, true);
action.count = 0;
this.blackops[action.name] = true;
let rankGain = 0;
@ -1402,7 +1383,7 @@ export class Bladeburner implements IBladeburner {
);
}
} else {
retValue = this.getActionStats(action, false);
retValue = this.getActionStats(action, person, false);
let rankLoss = 0;
let damage = 0;
if (action.rankLoss) {
@ -1412,7 +1393,7 @@ export class Bladeburner implements IBladeburner {
if (action.hpLoss) {
damage = action.hpLoss * difficultyMultiplier;
damage = Math.ceil(addOffset(damage, 10));
const cost = calculateHospitalizationCost(player, damage);
const cost = calculateHospitalizationCost(damage);
if (person.takeDamage(damage)) {
++this.numHosp;
this.moneyLost += cost;
@ -1440,7 +1421,7 @@ export class Bladeburner implements IBladeburner {
const losses = getRandomInt(1, teamLossMax);
this.teamSize -= losses;
if (this.teamSize < this.sleeveSize) {
const sup = player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
const sup = Player.sleeves.filter((x) => isSleeveSupportWork(x.currentWork));
for (let i = 0; i > this.teamSize - this.sleeveSize; i--) {
const r = Math.floor(Math.random() * sup.length);
sup[r].takeDamage(sup[r].hp.max);
@ -1603,8 +1584,8 @@ export class Bladeburner implements IBladeburner {
return retValue;
}
infiltrateSynthoidCommunities(p: IPlayer): void {
const infilSleeves = p.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length;
infiltrateSynthoidCommunities(): void {
const infilSleeves = Player.sleeves.filter((s) => isSleeveInfiltrateWork(s.currentWork)).length;
const amt = Math.pow(infilSleeves, -0.5) / 2;
for (const contract of Object.keys(this.contracts)) {
this.contracts[contract].count += amt;
@ -1617,7 +1598,7 @@ export class Bladeburner implements IBladeburner {
}
}
changeRank(person: IPerson, change: number): void {
changeRank(person: Person, change: number): void {
if (isNaN(change)) {
throw new Error("NaN passed into Bladeburner.changeRank()");
}
@ -1630,7 +1611,7 @@ export class Bladeburner implements IBladeburner {
const bladeburnersFactionName = FactionNames.Bladeburners;
if (factionExists(bladeburnersFactionName)) {
const bladeburnerFac = Factions[bladeburnersFactionName];
if (!(bladeburnerFac instanceof Faction)) {
if (!bladeburnerFac) {
throw new Error(
`Could not properly get ${FactionNames.Bladeburners} Faction object in ${FactionNames.Bladeburners} UI Overview Faction button`,
);
@ -1654,12 +1635,12 @@ export class Bladeburner implements IBladeburner {
}
}
processAction(router: IRouter, player: IPlayer, seconds: number): void {
processAction(seconds: number): void {
if (this.action.type === ActionTypes["Idle"]) return;
if (this.actionTimeToComplete <= 0) {
throw new Error(`Invalid actionTimeToComplete value: ${this.actionTimeToComplete}, type; ${this.action.type}`);
}
if (!(this.action instanceof ActionIdentifier)) {
if (!this.action) {
throw new Error("Bladeburner.action is not an ActionIdentifier Object");
}
@ -1670,31 +1651,31 @@ export class Bladeburner implements IBladeburner {
if (this.actionTimeCurrent >= this.actionTimeToComplete) {
this.actionTimeOverflow = this.actionTimeCurrent - this.actionTimeToComplete;
const action = this.getActionObject(this.action);
const retValue = this.completeAction(player, player, this.action);
player.gainMoney(retValue.money, "bladeburner");
player.gainStats(retValue);
const retValue = this.completeAction(Player, this.action);
Player.gainMoney(retValue.money, "bladeburner");
Player.gainStats(retValue);
// Operation Daedalus
if (action == null) {
throw new Error("Failed to get BlackOperation Object for: " + this.action.name);
} else if (this.action.type != ActionTypes["BlackOperation"] && this.action.type != ActionTypes["BlackOp"]) {
this.startAction(player, this.action); // Repeat action
this.startAction(this.action); // Repeat action
}
}
}
calculateStaminaGainPerSecond(player: IPlayer): number {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi;
calculateStaminaGainPerSecond(): number {
const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor;
const gain = (BladeburnerConstants.StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17);
return gain * (this.skillMultipliers.stamina * player.mults.bladeburner_stamina_gain);
return gain * (this.skillMultipliers.stamina * Player.mults.bladeburner_stamina_gain);
}
calculateMaxStamina(player: IPlayer): void {
const effAgility = player.skills.agility * this.skillMultipliers.effAgi;
calculateMaxStamina(): void {
const effAgility = Player.skills.agility * this.skillMultipliers.effAgi;
const maxStamina =
(Math.pow(effAgility, 0.8) + this.staminaBonus) *
this.skillMultipliers.stamina *
player.mults.bladeburner_max_stamina;
Player.mults.bladeburner_max_stamina;
if (this.maxStamina !== maxStamina) {
const oldMax = this.maxStamina;
this.maxStamina = maxStamina;
@ -1974,12 +1955,12 @@ export class Bladeburner implements IBladeburner {
});
}
process(router: IRouter, player: IPlayer): void {
process(): void {
// Edge race condition when the engine checks the processing counters and attempts to route before the router is initialized.
if (!router.isInitialized) return;
if (!Router.isInitialized) return;
// If the Player starts doing some other actions, set action to idle and alert
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.currentWork) {
if (!Player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && Player.currentWork) {
if (this.action.type !== ActionTypes["Idle"]) {
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) {
@ -2006,8 +1987,8 @@ export class Bladeburner implements IBladeburner {
this.storedCycles -= seconds * BladeburnerConstants.CyclesPerSecond;
// Stamina
this.calculateMaxStamina(player);
this.stamina += this.calculateStaminaGainPerSecond(player) * seconds;
this.calculateMaxStamina();
this.stamina += this.calculateStaminaGainPerSecond() * seconds;
this.stamina = Math.min(this.maxStamina, this.stamina);
// Count increase for contracts/operations
@ -2027,9 +2008,7 @@ export class Bladeburner implements IBladeburner {
// Chaos goes down very slowly
for (const cityName of BladeburnerConstants.CityNames) {
const city = this.cities[cityName];
if (!(city instanceof City)) {
throw new Error("Invalid City object when processing passive chaos reduction in Bladeburner.process");
}
if (!city) throw new Error("Invalid city when processing passive chaos reduction in Bladeburner.process");
city.chaos -= 0.0001 * seconds;
city.chaos = Math.max(0, city.chaos);
}
@ -2042,7 +2021,7 @@ export class Bladeburner implements IBladeburner {
this.randomEventCounter += getRandomInt(240, 600);
}
this.processAction(router, player, seconds);
this.processAction(seconds);
// Automation
if (this.automateEnabled) {
@ -2053,7 +2032,7 @@ export class Bladeburner implements IBladeburner {
type: this.automateActionLow.type,
name: this.automateActionLow.name,
});
this.startAction(player, this.action);
this.startAction(this.action);
}
} else if (this.stamina >= this.automateThreshHigh) {
if (this.action.name !== this.automateActionHigh.name || this.action.type !== this.automateActionHigh.type) {
@ -2061,14 +2040,14 @@ export class Bladeburner implements IBladeburner {
type: this.automateActionHigh.type,
name: this.automateActionHigh.name,
});
this.startAction(player, this.action);
this.startAction(this.action);
}
}
}
}
}
getTypeAndNameFromActionId(actionId: IActionIdentifier): {
getTypeAndNameFromActionId(actionId: ActionIdentifier): {
type: string;
name: string;
} {
@ -2121,7 +2100,7 @@ export class Bladeburner implements IBladeburner {
return Object.keys(Skills);
}
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean {
startActionNetscriptFn(type: string, name: string, workerScript: WorkerScript): boolean {
const errorLogText = `Invalid action: type='${type}' name='${name}'`;
const actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) {
@ -2139,7 +2118,7 @@ export class Bladeburner implements IBladeburner {
}
try {
this.startAction(player, actionId);
this.startAction(actionId);
workerScript.log(
"bladeburner.startAction",
() => `Starting bladeburner action with type '${type}' and name '${name}'`,
@ -2153,7 +2132,7 @@ export class Bladeburner implements IBladeburner {
}
}
getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string {
getActionTimeNetscriptFn(person: Person, type: string, name: string): number | string {
const actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) {
return "bladeburner.getActionTime";
@ -2184,7 +2163,7 @@ export class Bladeburner implements IBladeburner {
}
}
getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string {
getActionEstimatedSuccessChanceNetscriptFn(person: Person, type: string, name: string): [number, number] | string {
const actionId = this.getActionIdFromTypeAndName(type, name);
if (actionId == null) {
return "bladeburner.getActionEstimatedSuccessChance";

@ -1,4 +1,4 @@
import { IBladeburner } from "./IBladeburner";
import { Bladeburner } from "./Bladeburner";
import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
@ -7,7 +7,7 @@ export class Contract extends Action {
super(params);
}
getActionTypeSkillSuccessBonus(inst: IBladeburner): number {
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
return inst.skillMultipliers.successChanceContract;
}

@ -1,72 +0,0 @@
import { IReviverValue } from "../utils/JSONReviver";
import { IPerson } from "../PersonObjects/IPerson";
import { IBladeburner } from "./IBladeburner";
interface IStatsMultiplier {
[key: string]: number;
hack: number;
str: number;
def: number;
dex: number;
agi: number;
cha: number;
int: number;
}
export interface ISuccessChanceParams {
est: boolean;
}
export interface IAction {
name: string;
// Difficulty scales with level. See getDifficulty() method
level: number;
maxLevel: number;
autoLevel: boolean;
baseDifficulty: number;
difficultyFac: number;
// Rank increase/decrease is affected by this exponent
rewardFac: number;
successes: number;
failures: number;
// All of these scale with level/difficulty
rankGain: number;
rankLoss: number;
hpLoss: number;
hpLost: number;
// Action Category. Current categories are stealth and kill
isStealth: boolean;
isKill: boolean;
/**
* Number of this contract remaining, and its growth rate
* Growth rate is an integer and the count will increase by that integer every "cycle"
*/
count: number;
// Weighting of each stat in determining action success rate
weights: IStatsMultiplier;
// Diminishing returns of stats (stat ^ decay where 0 <= decay <= 1)
decays: IStatsMultiplier;
teamCount: number;
getDifficulty(): number;
attempt(inst: IBladeburner, person: IPerson): boolean;
getActionTimePenalty(): number;
getActionTime(inst: IBladeburner, person: IPerson): number;
getTeamSuccessBonus(inst: IBladeburner): number;
getActionTypeSkillSuccessBonus(inst: IBladeburner): number;
getChaosCompetencePenalty(inst: IBladeburner, params: ISuccessChanceParams): number;
getChaosDifficultyBonus(inst: IBladeburner): number;
getEstSuccessChance(inst: IBladeburner, person: IPerson): [number, number];
getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams): number;
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number;
setMaxLevel(baseSuccessesPerLevel: number): void;
toJSON(): IReviverValue;
}

@ -1,4 +0,0 @@
export interface IActionIdentifier {
name: string;
type: number;
}

@ -1,121 +0,0 @@
import { IActionIdentifier } from "./IActionIdentifier";
import { City } from "./City";
import { Skill } from "./Skill";
import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IPerson } from "../PersonObjects/IPerson";
import { ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript";
import { Contract } from "./Contract";
import { Operation } from "./Operation";
export interface IBladeburner {
numHosp: number;
moneyLost: number;
rank: number;
maxRank: number;
skillPoints: number;
totalSkillPoints: number;
teamSize: number;
teamLost: number;
hpLost: number;
storedCycles: number;
randomEventCounter: number;
actionTimeToComplete: number;
actionTimeCurrent: number;
actionTimeOverflow: number;
action: IActionIdentifier;
cities: Record<string, City>;
city: string;
skills: Record<string, number>;
skillMultipliers: Record<string, number>;
staminaBonus: number;
maxStamina: number;
stamina: number;
contracts: Record<string, Contract>;
operations: Record<string, Operation>;
blackops: Record<string, boolean>;
logging: {
general: boolean;
contracts: boolean;
ops: boolean;
blackops: boolean;
events: boolean;
};
automateEnabled: boolean;
automateActionHigh: IActionIdentifier;
automateThreshHigh: number;
automateActionLow: IActionIdentifier;
automateThreshLow: number;
consoleHistory: string[];
consoleLogs: string[];
getCurrentCity(): City;
calculateStaminaPenalty(): number;
startAction(player: IPlayer, action: IActionIdentifier): void;
upgradeSkill(skill: Skill): void;
executeConsoleCommands(player: IPlayer, command: string): void;
postToConsole(input: string, saveToLogs?: boolean): void;
log(input: string): void;
resetAction(): void;
clearConsole(): void;
prestige(): void;
storeCycles(numCycles?: number): void;
getTypeAndNameFromActionId(actionId: IActionIdentifier): {
type: string;
name: string;
};
getContractNamesNetscriptFn(): string[];
getOperationNamesNetscriptFn(): string[];
getBlackOpNamesNetscriptFn(): string[];
getGeneralActionNamesNetscriptFn(): string[];
getSkillNamesNetscriptFn(): string[];
startActionNetscriptFn(player: IPlayer, type: string, name: string, workerScript: WorkerScript): boolean;
getActionTimeNetscriptFn(person: IPerson, type: string, name: string): number | string;
getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string;
getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
getSkillLevelNetscriptFn(skillName: string, workerScript: WorkerScript): number;
getSkillUpgradeCostNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): number;
upgradeSkillNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): boolean;
getTeamSizeNetscriptFn(type: string, name: string, workerScript: WorkerScript): number;
setTeamSizeNetscriptFn(type: string, name: string, size: number, workerScript: WorkerScript): number;
joinBladeburnerFactionNetscriptFn(workerScript: WorkerScript): boolean;
getActionIdFromTypeAndName(type: string, name: string): IActionIdentifier | null;
executeStartConsoleCommand(player: IPlayer, args: string[]): void;
executeSkillConsoleCommand(args: string[]): void;
executeLogConsoleCommand(args: string[]): void;
executeHelpConsoleCommand(args: string[]): void;
executeAutomateConsoleCommand(args: string[]): void;
parseCommandArguments(command: string): string[];
executeConsoleCommand(player: IPlayer, command: string): void;
triggerMigration(sourceCityName: string): void;
triggerPotentialMigration(sourceCityName: string, chance: number): void;
randomEvent(): void;
getDiplomacyEffectiveness(player: IPlayer): number;
getRecruitmentSuccessChance(player: IPerson): number;
getRecruitmentTime(player: IPerson): number;
resetSkillMultipliers(): void;
updateSkillMultipliers(): void;
completeOperation(success: boolean, player: IPlayer): void;
getActionObject(actionId: IActionIdentifier): IAction | null;
completeContract(success: boolean, actionIdent: IActionIdentifier): void;
completeAction(player: IPlayer, person: IPerson, actionIdent: IActionIdentifier, isPlayer?: boolean): ITaskTracker;
infiltrateSynthoidCommunities(p: IPlayer): void;
changeRank(player: IPlayer, change: number): void;
processAction(router: IRouter, player: IPlayer, seconds: number): void;
calculateStaminaGainPerSecond(player: IPlayer): number;
calculateMaxStamina(player: IPlayer): void;
create(): void;
process(router: IRouter, player: IPlayer): void;
getActionStats(action: IAction, success: boolean): ITaskTracker;
sleeveSupport(joining: boolean): void;
}

Some files were not shown because too many files have changed in this diff Show More