mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 22:38:34 +02:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
633da38301 | ||
|
|
3c29757827 | ||
|
|
960fe5aa8b | ||
|
|
922f0bfcc5 | ||
|
|
e66a8e319f | ||
|
|
3fafa23f28 | ||
|
|
b1c1fc24a9 | ||
|
|
32eb6324fd | ||
|
|
61ec7dde80 | ||
|
|
9c9a69f2e2 | ||
|
|
1c20a24079 | ||
|
|
06d742a7f3 | ||
|
|
357cc568e9 | ||
|
|
21e984bda6 | ||
|
|
f620ec889c | ||
|
|
f162faf60a | ||
|
|
031b8b9cbb | ||
|
|
64933419d6 | ||
|
|
b597746343 | ||
|
|
abdf5f52cd | ||
|
|
dc057d05fc | ||
|
|
cef789eb7c | ||
|
|
fd8eae5cf5 | ||
|
|
bf8c15332e | ||
|
|
819f877370 | ||
|
|
67cdd57728 | ||
|
|
5a8f0e99af | ||
|
|
0d8cc54c99 | ||
|
|
c0036b03d4 | ||
|
|
48bebeea2b | ||
|
|
49668f10b2 | ||
|
|
847d45f4f4 | ||
|
|
a62bdcafef | ||
|
|
337fa4e274 | ||
|
|
eff834bfe9 | ||
|
|
99b22a221c | ||
|
|
4382f860db | ||
|
|
7a39a93fa9 | ||
|
|
ceb58bc6b3 | ||
|
|
f6de21ea18 | ||
|
|
4936d14639 | ||
|
|
a780880531 | ||
|
|
e9347fca76 | ||
|
|
417d420793 | ||
|
|
a12056a898 | ||
|
|
bec6e82d7f | ||
|
|
805ca06922 | ||
|
|
344054f10d | ||
|
|
39b18e7659 | ||
|
|
2198a02152 |
30
electron/fileError.html
Normal file
30
electron/fileError.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Bitburner</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: black;
|
||||
color: #0c0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>Attempts to access local files outside the normal game environment will be directed to this file.</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
Attempts to access local files outside the normal game environment will be directed to this file.
|
||||
@@ -29,6 +29,7 @@ const debounce = require("lodash/debounce");
|
||||
const Store = require("electron-store");
|
||||
const store = new Store();
|
||||
const path = require("path");
|
||||
const { realpathSync } = require("fs");
|
||||
const { fileURLToPath } = require("url");
|
||||
|
||||
log.transports.file.level = store.get("file-log-level", "info");
|
||||
@@ -201,13 +202,18 @@ app.on("ready", async () => {
|
||||
// Intercept file protocol requests and only let valid requests through
|
||||
protocol.interceptFileProtocol("file", ({ url, method }, callback) => {
|
||||
const filePath = fileURLToPath(url);
|
||||
const relativePath = path.relative(__dirname, filePath);
|
||||
//only provide html files in same directory, or anything in dist
|
||||
if ((method === "GET" && relativePath.startsWith("dist")) || relativePath.match(/^[a-zA-Z-_]*\.html/)) {
|
||||
return callback(filePath);
|
||||
const realPath = realpathSync(filePath);
|
||||
const relativePath = path.relative(__dirname, realPath);
|
||||
// Only allow access to files in "dist" folder or html files in the same directory
|
||||
if (method === "GET" && (relativePath.startsWith("dist") || relativePath.match(/^[a-zA-Z-_]*\.html/))) {
|
||||
callback(realPath);
|
||||
return;
|
||||
}
|
||||
log.error(`Tried to access a page outside the sandbox. Url: ${url}. Method: ${method}.`);
|
||||
callback(path.join(__dirname, "fileError.txt"));
|
||||
log.error(
|
||||
`Tried to access a page outside the sandbox. Url: ${url}. FilePath: ${filePath}. RealPath: ${realPath}.` +
|
||||
` __dirname: ${__dirname}. RelativePath: ${relativePath}. Method: ${method}.`,
|
||||
);
|
||||
callback(path.join(__dirname, "fileError.html"));
|
||||
});
|
||||
|
||||
log.info("Application is ready!");
|
||||
|
||||
@@ -192,9 +192,9 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
|
||||
*
|
||||
* Instead of implementing it, the old code (encoding in base64) is used here for backward compatibility.
|
||||
*/
|
||||
const content = saveData.toString("base64");
|
||||
log.debug(`Uncompressed: ${saveData.length} bytes`);
|
||||
log.debug(`Compressed: ${content.length} bytes`);
|
||||
const content = Buffer.from(saveData).toString("base64");
|
||||
log.debug(`saveData: ${saveData.length} bytes`);
|
||||
log.debug(`Base64 string of saveData: ${content.length} bytes`);
|
||||
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
||||
|
||||
try {
|
||||
|
||||
11
markdown/bitburner.autocompletedata.enums.md
Normal file
11
markdown/bitburner.autocompletedata.enums.md
Normal file
@@ -0,0 +1,11 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [AutocompleteData](./bitburner.autocompletedata.md) > [enums](./bitburner.autocompletedata.enums.md)
|
||||
|
||||
## AutocompleteData.enums property
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
enums: NSEnums;
|
||||
```
|
||||
@@ -16,6 +16,7 @@ interface AutocompleteData
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [enums](./bitburner.autocompletedata.enums.md) | | [NSEnums](./bitburner.nsenums.md) | |
|
||||
| [scripts](./bitburner.autocompletedata.scripts.md) | | string\[\] | |
|
||||
| [servers](./bitburner.autocompletedata.servers.md) | | string\[\] | |
|
||||
| [txts](./bitburner.autocompletedata.txts.md) | | string\[\] | |
|
||||
|
||||
@@ -9,7 +9,7 @@ Get estimate success chance of an action.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getActionEstimatedSuccessChance(type: string, name: string): [number, number];
|
||||
getActionEstimatedSuccessChance(type: string, name: string, sleeveNumber?: number): [number, number];
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,6 +18,7 @@ getActionEstimatedSuccessChance(type: string, name: string): [number, number];
|
||||
| --- | --- | --- |
|
||||
| type | string | Type of action. |
|
||||
| name | string | Name of action. Must be an exact match. |
|
||||
| sleeveNumber | number | _(Optional)_ Optional. Index of the sleeve to retrieve information. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ You have to be employed in the Bladeburner division and be in BitNode-7 or have
|
||||
| [getActionCountRemaining(type, name)](./bitburner.bladeburner.getactioncountremaining.md) | Get action count remaining. |
|
||||
| [getActionCurrentLevel(type, name)](./bitburner.bladeburner.getactioncurrentlevel.md) | Get the current level of an action. |
|
||||
| [getActionCurrentTime()](./bitburner.bladeburner.getactioncurrenttime.md) | Get the time elapsed on current action. |
|
||||
| [getActionEstimatedSuccessChance(type, name)](./bitburner.bladeburner.getactionestimatedsuccesschance.md) | Get estimate success chance of an action. |
|
||||
| [getActionEstimatedSuccessChance(type, name, sleeveNumber)](./bitburner.bladeburner.getactionestimatedsuccesschance.md) | Get estimate success chance of an action. |
|
||||
| [getActionMaxLevel(type, name)](./bitburner.bladeburner.getactionmaxlevel.md) | Get the maximum level of an action. |
|
||||
| [getActionRepGain(type, name, level)](./bitburner.bladeburner.getactionrepgain.md) | Get the reputation gain of an action. |
|
||||
| [getActionSuccesses(type, name)](./bitburner.bladeburner.getactionsuccesses.md) | Get action successes. |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Grafting.getAugmentationGraftTime() method
|
||||
|
||||
Retrieves the time required to graft an aug.
|
||||
Retrieves the time required to graft an aug. Do not use this value to determine when the ongoing grafting finishes. The ongoing grafting is affected by current intelligence level and focus bonus. You should use [waitForOngoingGrafting](./bitburner.grafting.waitforongoinggrafting.md) for that purpose.
|
||||
|
||||
**Signature:**
|
||||
|
||||
@@ -22,7 +22,7 @@ getAugmentationGraftTime(augName: string): number;
|
||||
|
||||
number
|
||||
|
||||
The time required, in millis, to graft the named augmentation.
|
||||
The time required, in milliseconds, to graft the named augmentation.
|
||||
|
||||
## Exceptions
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Grafting.graftAugmentation() method
|
||||
|
||||
Begins grafting the named aug. You must be in New Tokyo to use this.
|
||||
Begins grafting the named aug. You must be in New Tokyo to use this. When you call this API, the current work (grafting or other actions) will be canceled.
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ This API requires Source-File 10 to use.
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [getAugmentationGraftPrice(augName)](./bitburner.grafting.getaugmentationgraftprice.md) | Retrieve the grafting cost of an aug. |
|
||||
| [getAugmentationGraftTime(augName)](./bitburner.grafting.getaugmentationgrafttime.md) | Retrieves the time required to graft an aug. |
|
||||
| [getAugmentationGraftTime(augName)](./bitburner.grafting.getaugmentationgrafttime.md) | Retrieves the time required to graft an aug. Do not use this value to determine when the ongoing grafting finishes. The ongoing grafting is affected by current intelligence level and focus bonus. You should use [waitForOngoingGrafting](./bitburner.grafting.waitforongoinggrafting.md) for that purpose. |
|
||||
| [getGraftableAugmentations()](./bitburner.grafting.getgraftableaugmentations.md) | Retrieves a list of Augmentations that can be grafted. |
|
||||
| [graftAugmentation(augName, focus)](./bitburner.grafting.graftaugmentation.md) | Begins grafting the named aug. You must be in New Tokyo to use this. |
|
||||
| [graftAugmentation(augName, focus)](./bitburner.grafting.graftaugmentation.md) | Begins grafting the named aug. You must be in New Tokyo to use this. When you call this API, the current work (grafting or other actions) will be canceled. |
|
||||
| [waitForOngoingGrafting()](./bitburner.grafting.waitforongoinggrafting.md) | Wait until the ongoing grafting finishes or is canceled. |
|
||||
|
||||
|
||||
23
markdown/bitburner.grafting.waitforongoinggrafting.md
Normal file
23
markdown/bitburner.grafting.waitforongoinggrafting.md
Normal file
@@ -0,0 +1,23 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Grafting](./bitburner.grafting.md) > [waitForOngoingGrafting](./bitburner.grafting.waitforongoinggrafting.md)
|
||||
|
||||
## Grafting.waitForOngoingGrafting() method
|
||||
|
||||
Wait until the ongoing grafting finishes or is canceled.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
waitForOngoingGrafting(): Promise<void>;
|
||||
```
|
||||
**Returns:**
|
||||
|
||||
Promise<void>
|
||||
|
||||
A promise that resolves when the current grafting finishes or is canceled. If there is no current work, the promise resolves immediately. If the current work is not a grafting work, the promise rejects immediately.
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 1 GB
|
||||
|
||||
11
markdown/bitburner.graftingtask.completion.md
Normal file
11
markdown/bitburner.graftingtask.completion.md
Normal file
@@ -0,0 +1,11 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [GraftingTask](./bitburner.graftingtask.md) > [completion](./bitburner.graftingtask.completion.md)
|
||||
|
||||
## GraftingTask.completion property
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
completion: Promise<void>;
|
||||
```
|
||||
@@ -21,6 +21,7 @@ An object representing the current grafting status
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [augmentation](./bitburner.graftingtask.augmentation.md) | | string | |
|
||||
| [completion](./bitburner.graftingtask.completion.md) | | Promise<void> | |
|
||||
| [cyclesWorked](./bitburner.graftingtask.cyclesworked.md) | | number | |
|
||||
| [type](./bitburner.graftingtask.type.md) | | "GRAFTING" | |
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## NS.disableLog() method
|
||||
|
||||
Disables logging for the given function.
|
||||
Disables logging for the given NS function.
|
||||
|
||||
**Signature:**
|
||||
|
||||
@@ -16,7 +16,7 @@ disableLog(fn: string): void;
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| fn | string | Name of function for which to disable logging. |
|
||||
| fn | string | Name of the NS function for which to disable logging. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
@@ -30,3 +30,11 @@ Logging can be disabled for all functions by passing `ALL` as the argument.
|
||||
|
||||
For specific interfaces, use the form "namespace.functionName". (e.g. "ui.setTheme")
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
```js
|
||||
ns.disableLog("hack"); // Disable logging for `ns.hack()`
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## NS.enableLog() method
|
||||
|
||||
Enable logging for a certain function.
|
||||
Enables logging for the given NS function.
|
||||
|
||||
**Signature:**
|
||||
|
||||
@@ -16,7 +16,7 @@ enableLog(fn: string): void;
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| fn | string | Name of function for which to enable logging. |
|
||||
| fn | string | Name of the NS function for which to enable logging. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
@@ -26,5 +26,13 @@ void
|
||||
|
||||
RAM cost: 0 GB
|
||||
|
||||
Re-enables logging for the given function. If `ALL` is passed into this function as an argument, then it will revert the effects of disableLog(`ALL`<!-- -->).
|
||||
Re-enables logging for the given function. If `ALL` is passed into this function as an argument, it will revert the effect of disableLog("ALL").
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
```js
|
||||
ns.enableLog("hack"); // Enable logging for `ns.hack()`
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## NS.isLogEnabled() method
|
||||
|
||||
Checks the status of the logging for the given function.
|
||||
Checks the status of the logging for the given NS function.
|
||||
|
||||
**Signature:**
|
||||
|
||||
@@ -22,9 +22,17 @@ isLogEnabled(fn: string): boolean;
|
||||
|
||||
boolean
|
||||
|
||||
Returns a boolean indicating whether or not logging is enabled for that function (or `ALL`<!-- -->).
|
||||
Returns a boolean indicating whether or not logging is enabled for that NS function (or `ALL`<!-- -->).
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 0 GB
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
```js
|
||||
ns.print(ns.isLogEnabled("hack")); // Check if logging is enabled for `ns.hack()`
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ export async function main(ns) {
|
||||
| [clearPort(portNumber)](./bitburner.ns.clearport.md) | Clear data from a port. |
|
||||
| [closeTail(pid)](./bitburner.ns.closetail.md) | Close the tail window of a script. |
|
||||
| [deleteServer(host)](./bitburner.ns.deleteserver.md) | Delete a purchased server. |
|
||||
| [disableLog(fn)](./bitburner.ns.disablelog.md) | Disables logging for the given function. |
|
||||
| [enableLog(fn)](./bitburner.ns.enablelog.md) | Enable logging for a certain function. |
|
||||
| [disableLog(fn)](./bitburner.ns.disablelog.md) | Disables logging for the given NS function. |
|
||||
| [enableLog(fn)](./bitburner.ns.enablelog.md) | Enables logging for the given NS function. |
|
||||
| [exec(script, hostname, threadOrOptions, args)](./bitburner.ns.exec.md) | Start another script on any server. |
|
||||
| [exit()](./bitburner.ns.exit.md) | Terminates the current script immediately. |
|
||||
| [fileExists(filename, host)](./bitburner.ns.fileexists.md) | Check if a file exists. |
|
||||
@@ -125,7 +125,7 @@ export async function main(ns) {
|
||||
| [hasRootAccess(host)](./bitburner.ns.hasrootaccess.md) | Check if you have root access on a server. |
|
||||
| [hasTorRouter()](./bitburner.ns.hastorrouter.md) | Returns whether the player has access to the darkweb. |
|
||||
| [httpworm(host)](./bitburner.ns.httpworm.md) | Runs HTTPWorm.exe on a server. |
|
||||
| [isLogEnabled(fn)](./bitburner.ns.islogenabled.md) | Checks the status of the logging for the given function. |
|
||||
| [isLogEnabled(fn)](./bitburner.ns.islogenabled.md) | Checks the status of the logging for the given NS function. |
|
||||
| [isRunning(script, host, args)](./bitburner.ns.isrunning.md) | Check if a script is running. |
|
||||
| [kill(pid)](./bitburner.ns.kill.md) | Terminate the script with the provided PID. |
|
||||
| [kill(filename, hostname, args)](./bitburner.ns.kill_1.md) | Terminate the script(s) with the provided filename, hostname, and script arguments. |
|
||||
@@ -143,6 +143,7 @@ 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. |
|
||||
| [ramOverride(ram)](./bitburner.ns.ramoverride.md) | Change the current static RAM allocation of the script. |
|
||||
| [read(filename)](./bitburner.ns.read.md) | Read content of a file. |
|
||||
| [readPort(portNumber)](./bitburner.ns.readport.md) | Read data from a port. |
|
||||
| [relaysmtp(host)](./bitburner.ns.relaysmtp.md) | Runs relaySMTP.exe on a server. |
|
||||
|
||||
34
markdown/bitburner.ns.ramoverride.md
Normal file
34
markdown/bitburner.ns.ramoverride.md
Normal file
@@ -0,0 +1,34 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [NS](./bitburner.ns.md) > [ramOverride](./bitburner.ns.ramoverride.md)
|
||||
|
||||
## NS.ramOverride() method
|
||||
|
||||
Change the current static RAM allocation of the script.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
ramOverride(ram?: number): number;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| ram | number | _(Optional)_ The new RAM limit to set. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
number
|
||||
|
||||
The new static RAM limit, which will be the old one if it wasn't changed. This means you can use no parameters to check the current ram limit.
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 0 GB
|
||||
|
||||
This acts analagously to the ramOverride parameter in runOptions, but for changing RAM in the current running script. The static RAM allocation (the amount of RAM used by ONE thread) will be adjusted to the given value, if possible. This can fail if the number is less than the current dynamic RAM limit, or if adjusting upward would require more RAM than is available on the server.
|
||||
|
||||
RAM usage will be rounded to the nearest hundredth of a GB, which is the granularity of all RAM calculations.
|
||||
|
||||
@@ -30,6 +30,8 @@ RAM cost: 2 GB
|
||||
|
||||
Terminates the current script, and then after a defined delay it will execute the newly-specified script. The purpose of this function is to execute a new script without being constrained by the RAM usage of the current one. This function can only be used to run scripts on the local server.
|
||||
|
||||
The delay specified can be 0; in this case the new script will synchronously replace the old one. (There will not be any opportunity for other scripts to use up the RAM in-between.)
|
||||
|
||||
Because this function immediately terminates the script, it does not have a return value.
|
||||
|
||||
Running this function with 0 or fewer threads will cause a runtime error.
|
||||
|
||||
@@ -31,3 +31,5 @@ RAM cost: 0 GB
|
||||
|
||||
Write data to the given Netscript port.
|
||||
|
||||
There is a limit on the maximum number of ports, but you won't reach that limit in normal situations. If you do, it usually means that there is a bug in your script that leaks port data. A port is freed when it does not have any data in its underlying queue. `ns.clearPort` deletes all data on a port. `ns.readPort` reads the first element in the port's queue, then removes it from the queue.
|
||||
|
||||
|
||||
15
markdown/bitburner.runningscript.dynamicramusage.md
Normal file
15
markdown/bitburner.runningscript.dynamicramusage.md
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [RunningScript](./bitburner.runningscript.md) > [dynamicRamUsage](./bitburner.runningscript.dynamicramusage.md)
|
||||
|
||||
## RunningScript.dynamicRamUsage property
|
||||
|
||||
The dynamic RAM usage of (one thread of) this script instance. Does not affect overall RAM consumption (ramUsage is for that), but rather shows how much of the reserved RAM is currently in use via all the ns functions the script has called. Initially 1.6GB, this increases as new functions are called.
|
||||
|
||||
Only set for scripts that are still running.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
dynamicRamUsage: number | undefined;
|
||||
```
|
||||
@@ -16,6 +16,7 @@ interface RunningScript
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [args](./bitburner.runningscript.args.md) | | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments the script was called with |
|
||||
| [dynamicRamUsage](./bitburner.runningscript.dynamicramusage.md) | | number \| undefined | <p>The dynamic RAM usage of (one thread of) this script instance. Does not affect overall RAM consumption (ramUsage is for that), but rather shows how much of the reserved RAM is currently in use via all the ns functions the script has called. Initially 1.6GB, this increases as new functions are called.</p><p>Only set for scripts that are still running.</p> |
|
||||
| [filename](./bitburner.runningscript.filename.md) | | string | Filename of the script |
|
||||
| [logs](./bitburner.runningscript.logs.md) | | string\[\] | Script logs as an array. The newest log entries are at the bottom. Timestamps, if enabled, are placed inside <code>[brackets]</code> at the start of each line. |
|
||||
| [offlineExpGained](./bitburner.runningscript.offlineexpgained.md) | | number | Total amount of hacking experience earned from this script when offline |
|
||||
@@ -25,7 +26,7 @@ interface RunningScript
|
||||
| [onlineMoneyMade](./bitburner.runningscript.onlinemoneymade.md) | | number | Total amount of money made by this script when online |
|
||||
| [onlineRunningTime](./bitburner.runningscript.onlinerunningtime.md) | | number | Number of seconds that this script has been running online |
|
||||
| [pid](./bitburner.runningscript.pid.md) | | number | Process ID. Must be an integer |
|
||||
| [ramUsage](./bitburner.runningscript.ramusage.md) | | number | How much RAM this script uses for ONE thread |
|
||||
| [ramUsage](./bitburner.runningscript.ramusage.md) | | number | How much RAM this script uses for ONE thread. Also known as "static RAM usage," this value does not change once the script is started, unless you call ns.ramOverride(). |
|
||||
| [server](./bitburner.runningscript.server.md) | | string | Hostname of the server on which this script runs |
|
||||
| [tailProperties](./bitburner.runningscript.tailproperties.md) | | [TailProperties](./bitburner.tailproperties.md) \| null | Properties of the tail window, or null if it is not shown |
|
||||
| [temporary](./bitburner.runningscript.temporary.md) | | boolean | Whether this RunningScript is excluded from saves |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## RunningScript.ramUsage property
|
||||
|
||||
How much RAM this script uses for ONE thread
|
||||
How much RAM this script uses for ONE thread. Also known as "static RAM usage," this value does not change once the script is started, unless you call ns.ramOverride().
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ interface RunOptions
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [preventDuplicates?](./bitburner.runoptions.preventduplicates.md) | | boolean | _(Optional)_ Should we fail to run if another instance is running with the exact same arguments? This used to be the default behavior, now defaults to false. |
|
||||
| [ramOverride?](./bitburner.runoptions.ramoverride.md) | | number | <p>_(Optional)_ The RAM allocation to launch each thread of the script with.</p><p>Lowering this will <i>not</i> automatically let you get away with using less RAM: the dynamic RAM check enforces that all [NS](./bitburner.ns.md) functions actually called incur their cost. However, if you know that certain functions that are statically present (and thus included in the static RAM cost) will never be called in a particular circumstance, you can use this to avoid paying for them.</p><p>You can also use this to <i>increase</i> the RAM if the static RAM checker has missed functions that you need to call.</p><p>Must be greater-or-equal to the base RAM cost. Defaults to the statically calculated cost.</p> |
|
||||
| [ramOverride?](./bitburner.runoptions.ramoverride.md) | | number | <p>_(Optional)_ The RAM allocation to launch each thread of the script with.</p><p>Lowering this will <i>not</i> automatically let you get away with using less RAM: the dynamic RAM check enforces that all [NS](./bitburner.ns.md) functions actually called incur their cost. However, if you know that certain functions that are statically present (and thus included in the static RAM cost) will never be called in a particular circumstance, you can use this to avoid paying for them.</p><p>You can also use this to <i>increase</i> the RAM if the static RAM checker has missed functions that you need to call.</p><p>Must be greater-or-equal to the base RAM cost. Will be rounded to the nearest hundredth-of-a-GB, which is the granularity of all RAM calculations. Defaults to the statically calculated cost.</p> |
|
||||
| [temporary?](./bitburner.runoptions.temporary.md) | | boolean | _(Optional)_ Whether this script is excluded from saves, defaults to false |
|
||||
| [threads?](./bitburner.runoptions.threads.md) | | number | _(Optional)_ Number of threads that the script will run with, defaults to 1 |
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Lowering this will <i>not</i> automatically let you get away with using less RAM
|
||||
|
||||
You can also use this to <i>increase</i> the RAM if the static RAM checker has missed functions that you need to call.
|
||||
|
||||
Must be greater-or-equal to the base RAM cost. Defaults to the statically calculated cost.
|
||||
Must be greater-or-equal to the base RAM cost. Will be rounded to the nearest hundredth-of-a-GB, which is the granularity of all RAM calculations. Defaults to the statically calculated cost.
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
||||
32
markdown/bitburner.singularity.getfactionworktypes.md
Normal file
32
markdown/bitburner.singularity.getfactionworktypes.md
Normal file
@@ -0,0 +1,32 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Singularity](./bitburner.singularity.md) > [getFactionWorkTypes](./bitburner.singularity.getfactionworktypes.md)
|
||||
|
||||
## Singularity.getFactionWorkTypes() method
|
||||
|
||||
Get the work types of a faction.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getFactionWorkTypes(faction: string): FactionWorkType[];
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| faction | string | Name of the faction. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
[FactionWorkType](./bitburner.factionworktype.md)<!-- -->\[\]
|
||||
|
||||
The work types of the faction.
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 1 GB \* 16/4/1
|
||||
|
||||
This function returns an array containing the work types of the specified faction.
|
||||
|
||||
@@ -53,6 +53,7 @@ This API requires Source-File 4 to use. The RAM cost of all these functions is m
|
||||
| [getFactionFavorGain(faction)](./bitburner.singularity.getfactionfavorgain.md) | Get faction favor gain. |
|
||||
| [getFactionInviteRequirements(faction)](./bitburner.singularity.getfactioninviterequirements.md) | List conditions for being invited to a faction. |
|
||||
| [getFactionRep(faction)](./bitburner.singularity.getfactionrep.md) | Get faction reputation. |
|
||||
| [getFactionWorkTypes(faction)](./bitburner.singularity.getfactionworktypes.md) | Get the work types of a faction. |
|
||||
| [getOwnedAugmentations(purchased)](./bitburner.singularity.getownedaugmentations.md) | Get a list of owned augmentation. |
|
||||
| [getOwnedSourceFiles()](./bitburner.singularity.getownedsourcefiles.md) | Get a list of acquired Source-Files. |
|
||||
| [getUpgradeHomeCoresCost()](./bitburner.singularity.getupgradehomecorescost.md) | Get the price of upgrading home cores. |
|
||||
|
||||
@@ -16,5 +16,5 @@ interface SpawnOptions extends RunOptions
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [spawnDelay?](./bitburner.spawnoptions.spawndelay.md) | | number | _(Optional)_ Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a positive integer. |
|
||||
| [spawnDelay?](./bitburner.spawnoptions.spawndelay.md) | | number | _(Optional)_ Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a non-negative integer. If 0, the script will be spawned synchronously. |
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## SpawnOptions.spawnDelay property
|
||||
|
||||
Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a positive integer.
|
||||
Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a non-negative integer. If 0, the script will be spawned synchronously.
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
||||
99
package-lock.json
generated
99
package-lock.json
generated
@@ -5946,13 +5946,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
@@ -5960,7 +5961,7 @@
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
@@ -5974,6 +5975,7 @@
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -5983,6 +5985,7 @@
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -5991,7 +5994,8 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bonjour-service": {
|
||||
"version": "1.1.1",
|
||||
@@ -6029,12 +6033,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -6599,6 +6604,7 @@
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -6610,10 +6616,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -8233,17 +8240,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -8485,10 +8493,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -8635,9 +8644,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -8645,6 +8654,7 @@
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
@@ -8901,20 +8911,6 @@
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
@@ -9811,6 +9807,7 @@
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
@@ -10306,6 +10303,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -12503,14 +12501,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/katex": {
|
||||
"version": "0.16.9",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.9.tgz",
|
||||
"integrity": "sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==",
|
||||
"version": "0.16.10",
|
||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz",
|
||||
"integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://opencollective.com/katex",
|
||||
"https://github.com/sponsors/katex"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^8.3.0"
|
||||
},
|
||||
@@ -13127,6 +13126,7 @@
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -14926,6 +14926,7 @@
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
@@ -14998,10 +14999,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
@@ -15017,6 +15019,7 @@
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -16788,6 +16791,7 @@
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
@@ -16959,6 +16963,7 @@
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
@@ -18023,9 +18028,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.14.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
|
||||
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
||||
@@ -13,11 +13,26 @@ import { Page } from "../ui/Router";
|
||||
import { mergeMultipliers } from "../PersonObjects/Multipliers";
|
||||
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
||||
|
||||
const soaAugmentationNames = [
|
||||
AugmentationName.BeautyOfAphrodite,
|
||||
AugmentationName.ChaosOfDionysus,
|
||||
AugmentationName.FloodOfPoseidon,
|
||||
AugmentationName.HuntOfArtemis,
|
||||
AugmentationName.KnowledgeOfApollo,
|
||||
AugmentationName.MightOfAres,
|
||||
AugmentationName.TrickeryOfHermes,
|
||||
AugmentationName.WKSharmonizer,
|
||||
AugmentationName.WisdomOfAthena,
|
||||
];
|
||||
|
||||
export function getBaseAugmentationPriceMultiplier(): number {
|
||||
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
|
||||
}
|
||||
export function getGenericAugmentationPriceMultiplier(): number {
|
||||
return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length);
|
||||
const queuedNonSoAAugmentationList = Player.queuedAugmentations.filter((augmentation) => {
|
||||
return !soaAugmentationNames.includes(augmentation.name);
|
||||
});
|
||||
return Math.pow(getBaseAugmentationPriceMultiplier(), queuedNonSoAAugmentationList.length);
|
||||
}
|
||||
|
||||
export function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void {
|
||||
@@ -111,7 +126,7 @@ export function getAugCost(aug: Augmentation): AugmentationCosts {
|
||||
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, aug.getLevel());
|
||||
repCost = aug.baseRepRequirement * multiplier * currentNodeMults.AugmentationRepCost;
|
||||
moneyCost = aug.baseCost * multiplier * currentNodeMults.AugmentationMoneyCost;
|
||||
moneyCost *= getBaseAugmentationPriceMultiplier() ** Player.queuedAugmentations.length;
|
||||
moneyCost *= getGenericAugmentationPriceMultiplier();
|
||||
break;
|
||||
}
|
||||
// SOA Augments use a unique cost method
|
||||
@@ -124,17 +139,6 @@ export function getAugCost(aug: Augmentation): AugmentationCosts {
|
||||
case AugmentationName.TrickeryOfHermes:
|
||||
case AugmentationName.WKSharmonizer:
|
||||
case AugmentationName.WisdomOfAthena: {
|
||||
const soaAugmentationNames = [
|
||||
AugmentationName.BeautyOfAphrodite,
|
||||
AugmentationName.ChaosOfDionysus,
|
||||
AugmentationName.FloodOfPoseidon,
|
||||
AugmentationName.HuntOfArtemis,
|
||||
AugmentationName.KnowledgeOfApollo,
|
||||
AugmentationName.MightOfAres,
|
||||
AugmentationName.TrickeryOfHermes,
|
||||
AugmentationName.WKSharmonizer,
|
||||
AugmentationName.WisdomOfAthena,
|
||||
];
|
||||
const soaAugCount = soaAugmentationNames.filter((augName) => Player.hasAugmentation(augName)).length;
|
||||
moneyCost = aug.baseCost * Math.pow(CONSTANTS.SoACostMult, soaAugCount);
|
||||
repCost = aug.baseRepRequirement * Math.pow(CONSTANTS.SoARepMult, soaAugCount);
|
||||
|
||||
@@ -1013,9 +1013,9 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
|
||||
moneyCost: 1e6,
|
||||
info:
|
||||
"Extra-ocular neurons taken from old martial art master. Injecting them gives the user the ability to " +
|
||||
"predict the enemy's attack before they even know it themselves.",
|
||||
"predict the enemy's movement.",
|
||||
stats:
|
||||
"This augmentation makes the Slash minigame easier by showing you via an indicator when the slash is coming.",
|
||||
"This augmentation makes the Slash minigame easier by showing you via an indicator when the sentinel is distracted.",
|
||||
isSpecial: true,
|
||||
factions: [FactionName.ShadowsOfAnarchy],
|
||||
},
|
||||
|
||||
@@ -28,9 +28,10 @@ function customFormatPercent(value: number): string {
|
||||
}
|
||||
|
||||
function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement {
|
||||
// If player doesn't have SF5 or if the property isn't affected by BitNode mults
|
||||
if (props.mult === 1 || Player.sourceFileLvl(5) === 0)
|
||||
// If the player doesn't have access to SF5 feature or if the property isn't affected by BitNode mults
|
||||
if (props.mult === 1 || (Player.bitNodeN !== 5 && Player.sourceFileLvl(5) === 0)) {
|
||||
return <Typography color={props.color}>{customFormatPercent(props.base)}</Typography>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography color={props.color}>
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from "react";
|
||||
import { Player } from "@player";
|
||||
import { CityName, FactionName } from "@enums";
|
||||
import { BitNodeMultipliers, replaceCurrentNodeMults } from "./BitNodeMultipliers";
|
||||
import { CorruptableText } from "../ui/React/CorruptableText";
|
||||
|
||||
class BitNode {
|
||||
// A short description, or tagline, about the BitNode
|
||||
@@ -37,23 +36,21 @@ export function initBitNodes() {
|
||||
"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.
|
||||
This is the first BitNode created by the Enders to imprison the minds of humans. It became the prototype and
|
||||
testing ground 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%
|
||||
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 their home computer
|
||||
when entering a new BitNode and increases all of the player's multipliers by:
|
||||
<ul>
|
||||
<li>Level 1: 16%</li>
|
||||
<li>Level 2: 24%</li>
|
||||
<li>Level 3: 28%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -61,36 +58,30 @@ export function initBitNodes() {
|
||||
2,
|
||||
0,
|
||||
"Rise of the Underworld",
|
||||
"From the shadows, they rose", //Gangs
|
||||
"From the shadows, they rose",
|
||||
(
|
||||
<>
|
||||
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 ({FactionName.SlumSnakes}, {FactionName.Tetrads}, {FactionName.TheSyndicate},{" "}
|
||||
{FactionName.TheDarkArmy}, {FactionName.SpeakersForTheDead}, {FactionName.NiteSec}, {FactionName.TheBlackHand}
|
||||
) give the player the ability to form and manage their own gang, which can earn the player money and reputation
|
||||
with the corresponding Faction. Gangs offer more Augmentations than Factions, and in BitNode-2 offer a way to
|
||||
destroy the BitNode.
|
||||
Certain factions ({FactionName.SlumSnakes}, {FactionName.Tetrads}, {FactionName.TheSyndicate},{" "}
|
||||
{FactionName.TheDarkArmy}, {FactionName.SpeakersForTheDead}, {FactionName.NiteSec}, and{" "}
|
||||
{FactionName.TheBlackHand}) give the player the ability to form and manage their own gang, which can earn the
|
||||
player money and reputation with the corresponding faction. The gang faction offers more augmentations than
|
||||
other factions, and in BitNode-2, it offers a way to destroy the BitNode.
|
||||
<br />
|
||||
<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
|
||||
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 your crime success rate, crime money, and charisma multipliers
|
||||
by:
|
||||
<br />
|
||||
<br />
|
||||
Level 1: 24%
|
||||
<br />
|
||||
Level 2: 36%
|
||||
<br />
|
||||
Level 3: 42%
|
||||
<ul>
|
||||
<li>Level 1: 24%</li>
|
||||
<li>Level 2: 36%</li>
|
||||
<li>Level 3: 42%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -104,25 +95,24 @@ export function initBitNodes() {
|
||||
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,
|
||||
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.
|
||||
In this BitNode, you can create and manage your own corporation. Running a successful corporation has the
|
||||
potential to generate 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
|
||||
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%
|
||||
<ul>
|
||||
<li>Level 1: 8%</li>
|
||||
<li>Level 2: 12%</li>
|
||||
<li>Level 3: 14%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -133,24 +123,23 @@ export function initBitNodes() {
|
||||
"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 />
|
||||
The Singularity has arrived. The human race is gone, replaced by artificially super intelligent beings that are
|
||||
more machine than man.
|
||||
<br />
|
||||
<br />
|
||||
In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. These
|
||||
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.
|
||||
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
|
||||
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
|
||||
<ul>
|
||||
<li>Level 1: 16x</li>
|
||||
<li>Level 2: 4x</li>
|
||||
<li>Level 3: 1x</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -166,21 +155,20 @@ export function initBitNodes() {
|
||||
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
|
||||
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 />
|
||||
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%
|
||||
<ul>
|
||||
<li>Level 1: 8%</li>
|
||||
<li>Level 2: 12%</li>
|
||||
<li>Level 3: 14%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -193,26 +181,24 @@ export function initBitNodes() {
|
||||
<>
|
||||
In the middle of the 21st century, {FactionName.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
|
||||
generation of their Synthoid design, called MK-VI, by developing a hyper-intelligent 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 {FactionName.Bladeburners} Division at the NSA, which provides a
|
||||
In this BitNode, you will be able to access the {FactionName.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 {FactionName.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%
|
||||
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 {FactionName.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:
|
||||
<ul>
|
||||
<li>Level 1: 8%</li>
|
||||
<li>Level 2: 12%</li>
|
||||
<li>Level 3: 14%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -226,25 +212,23 @@ export function initBitNodes() {
|
||||
In the middle of the 21st century, you were doing cutting-edge work at {FactionName.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
|
||||
a hyper-intelligent 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 {FactionName.Bladeburners} API, which allows you to access{" "}
|
||||
In this BitNode, you will be able to access the {FactionName.Bladeburners} API, which allows you to access{" "}
|
||||
{FactionName.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 {FactionName.Bladeburners} Netscript API
|
||||
in other BitNodes. In addition, this Source-File will increase all of your {FactionName.Bladeburners}{" "}
|
||||
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 {FactionName.Bladeburners} Netscript
|
||||
API in other BitNodes. In addition, this Source-File will increase all of your {FactionName.Bladeburners}{" "}
|
||||
multipliers by:
|
||||
<br />
|
||||
<br />
|
||||
Level 1: 8%
|
||||
<br />
|
||||
Level 2: 12%
|
||||
<br />
|
||||
Level 3: 14%
|
||||
<ul>
|
||||
<li>Level 1: 8%</li>
|
||||
<li>Level 2: 12%</li>
|
||||
<li>Level 3: 14%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -259,33 +243,24 @@ export function initBitNodes() {
|
||||
<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 />
|
||||
<ul>
|
||||
<li>You start with $250 million.</li>
|
||||
<li>You start with a WSE membership and access to the TIX API.</li>
|
||||
<li>You can short stocks and place different types of orders (limit/stop).</li>
|
||||
</ul>
|
||||
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:
|
||||
<ul>
|
||||
<li>Level 1: Permanent access to WSE and TIX API</li>
|
||||
<li>Level 2: Ability to short stocks in other BitNodes</li>
|
||||
<li>Level 3: Ability to use limit/stop orders in other BitNodes</li>
|
||||
</ul>
|
||||
This Source-File also increases your hacking growth multipliers by:
|
||||
<br />
|
||||
Level 1: 12%
|
||||
<br />
|
||||
Level 2: 18%
|
||||
<br />
|
||||
Level 3: 21%
|
||||
<ul>
|
||||
<li>Level 1: 12%</li>
|
||||
<li>Level 2: 18%</li>
|
||||
<li>Level 3: 21%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -298,36 +273,31 @@ export function initBitNodes() {
|
||||
<>
|
||||
When {FactionName.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. {FactionName.FulcrumSecretTechnologies}{" "}
|
||||
quickly abandoned the project and dissociated themselves from it.
|
||||
Hacknet, which is a global, decentralized network used for nefarious purposes.{" "}
|
||||
{FactionName.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
|
||||
This BitNode unlocks the Hacknet Server, which is 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:
|
||||
<ul>
|
||||
<li>Level 1: Permanently unlocks the Hacknet Server in other BitNodes</li>
|
||||
<li>Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode</li>
|
||||
<li>Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode</li>
|
||||
</ul>
|
||||
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
|
||||
Augmentations)
|
||||
augmentations)
|
||||
<br />
|
||||
<br />
|
||||
This Source-File also increases hacknet production and reduces hacknet costs by:
|
||||
<br />
|
||||
Level 1: 12%
|
||||
<br />
|
||||
Level 2: 18%
|
||||
<br />
|
||||
Level 3: 21%
|
||||
<ul>
|
||||
<li>Level 1: 12%</li>
|
||||
<li>Level 2: 18%</li>
|
||||
<li>Level 3: 21%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -344,21 +314,20 @@ export function initBitNodes() {
|
||||
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
|
||||
This BitNode unlocks Sleeve and Grafting technology:
|
||||
<ul>
|
||||
<li>
|
||||
Sleeve: Duplicate your consciousness into Synthoids, allowing you to perform different tasks asynchronously.
|
||||
You cannot buy Sleeves outside this BitNode.
|
||||
</li>
|
||||
<li>
|
||||
Grafting: Visit VitaLife in New Tokyo to get access to this technology. It allows you to graft
|
||||
augmentations, which is an alternative way of installing augmentations.
|
||||
</li>
|
||||
</ul>
|
||||
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 and Grafting API in other BitNodes. Each level
|
||||
of this Source-File also grants you a Sleeve.
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -370,9 +339,9 @@ export function initBitNodes() {
|
||||
(
|
||||
<>
|
||||
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
|
||||
the world. It was this period of disorder that eventually led 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.
|
||||
dark times, financial catastrophes hit.
|
||||
<br />
|
||||
<br />
|
||||
In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of
|
||||
@@ -381,27 +350,21 @@ export function initBitNodes() {
|
||||
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
|
||||
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%
|
||||
<ul>
|
||||
<li>Level 1: 32%</li>
|
||||
<li>Level 2: 48%</li>
|
||||
<li>Level 3: 56%</li>
|
||||
</ul>
|
||||
It also reduces the price increase for every augmentation bought by:
|
||||
<ul>
|
||||
<li>Level 1: 4%</li>
|
||||
<li>Level 2: 6%</li>
|
||||
<li>Level 3: 7%</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -412,13 +375,13 @@ export function initBitNodes() {
|
||||
"Repeat.",
|
||||
(
|
||||
<>
|
||||
To iterate is human, to recurse divine.
|
||||
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.
|
||||
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.
|
||||
</>
|
||||
),
|
||||
);
|
||||
@@ -429,15 +392,15 @@ export function initBitNodes() {
|
||||
"1 step back, 2 steps forward",
|
||||
(
|
||||
<>
|
||||
With the invention of Augmentations in the 2040s a religious group known as the{" "}
|
||||
With the invention of augmentations in the 2040s, a religious group known as the{" "}
|
||||
{FactionName.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
|
||||
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
|
||||
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 {FactionName.ChurchOfTheMachineGod} appear in other
|
||||
BitNodes.
|
||||
<br />
|
||||
@@ -460,40 +423,22 @@ export function initBitNodes() {
|
||||
networks by controlling the open space in the 'net!
|
||||
<br />
|
||||
<br />
|
||||
Destroying this BitNode will give you Source-File 14, or if you already have this Source-File it will upgrade
|
||||
Destroying this BitNode will give you Source-File 14, 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: 100% increased stat multipliers from Node Power
|
||||
<br />
|
||||
Level 2: Permanently unlocks the go.cheat API
|
||||
<br />
|
||||
Level 3: 25% additive increased success rate for the go.cheat API
|
||||
<br />
|
||||
<br />
|
||||
<ul>
|
||||
<li>Level 1: 100% increased stat multipliers from Node Power</li>
|
||||
<li>Level 2: Permanently unlocks the go.cheat API</li>
|
||||
<li>Level 3: 25% additive increased success rate for the go.cheat API</li>
|
||||
</ul>
|
||||
This Source-File also increases the maximum favor you can gain for each faction from IPvGO to:
|
||||
<br />
|
||||
Level 1: 80
|
||||
<br />
|
||||
Level 2: 100
|
||||
<br />
|
||||
Level 3: 120
|
||||
<ul>
|
||||
<li>Level 1: 80</li>
|
||||
<li>Level 2: 100</li>
|
||||
<li>Level 3: 120</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
);
|
||||
|
||||
BitNodes.BitNode19 = new BitNode(
|
||||
19,
|
||||
2,
|
||||
"MyrianOS",
|
||||
"l̷i̵g̵h̴t̵ ̴a̷t̸ ̶t̵h̵e̸ ̶e̷n̵d̶ ̶o̸f̶ ̸t̴h̸e̴ ̸t̷u̶n̸n̸e̷l̵.̷",
|
||||
(
|
||||
<CorruptableText
|
||||
content={`yNjHLAgecI ASW1fQdKx5 n9DQ3rmHp3 mnv0XEdwH2 sBkAlBOPhx NohIDL9eRy TbIl8U3WKz 1wjnJ9iuwS VML36vYLNH K06StviNvI cRboTarefZ 7BSNntPpJj DfayVbfxU6 46xvOPQd2Y Ogyj2gnyLr FIND THE GLITCH IN ISHIMA S6E0Vpmxk6 GTF9dWvE6n EEGg7xvtYR Um8YIC0Qww PG4vauBKBk JWG8V1j5Z5 bfYYTTFnBY 7uoicoqIaV IeUu0F42aA EhTF7Fkxyt OBYgGSu0es bJQpenVoO6 L9cL39tRhh xfLroUMvY8 xmMckUHLSQ`}
|
||||
spoiler={false}
|
||||
/>
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export const defaultMultipliers = new BitNodeMultipliers();
|
||||
@@ -1001,64 +946,6 @@ export function getBitNodeMultipliers(n: number, lvl: number): BitNodeMultiplier
|
||||
WorldDaemonDifficulty: 5,
|
||||
});
|
||||
}
|
||||
case 19: {
|
||||
return new BitNodeMultipliers({
|
||||
MyrianPower: 10,
|
||||
|
||||
AgilityLevelMultiplier: 0.01,
|
||||
AugmentationMoneyCost: 100,
|
||||
AugmentationRepCost: 100,
|
||||
BladeburnerRank: 0.01,
|
||||
BladeburnerSkillCost: 100,
|
||||
CharismaLevelMultiplier: 0.01,
|
||||
ClassGymExpGain: 0.01,
|
||||
CodingContractMoney: 0.01,
|
||||
CompanyWorkExpGain: 0.01,
|
||||
CompanyWorkMoney: 0.01,
|
||||
CompanyWorkRepGain: 0.01,
|
||||
CorporationValuation: 0.01,
|
||||
CrimeExpGain: 0.01,
|
||||
CrimeMoney: 0.01,
|
||||
CrimeSuccessRate: 0.01,
|
||||
DaedalusAugsRequirement: 100,
|
||||
DefenseLevelMultiplier: 0.01,
|
||||
DexterityLevelMultiplier: 0.01,
|
||||
FactionPassiveRepGain: 0.01,
|
||||
FactionWorkExpGain: 0.01,
|
||||
FactionWorkRepGain: 0.01,
|
||||
FourSigmaMarketDataApiCost: 100,
|
||||
FourSigmaMarketDataCost: 100,
|
||||
GangSoftcap: 0.01,
|
||||
GangUniqueAugs: 0.01,
|
||||
GoPower: 0.01,
|
||||
HackExpGain: 0.01,
|
||||
HackingLevelMultiplier: 0.01,
|
||||
HackingSpeedMultiplier: 0.01,
|
||||
HacknetNodeMoney: 0.01,
|
||||
HomeComputerRamCost: 100,
|
||||
InfiltrationMoney: 0.01,
|
||||
InfiltrationRep: 0.01,
|
||||
ManualHackMoney: 0.01,
|
||||
PurchasedServerCost: 100,
|
||||
PurchasedServerSoftcap: 100,
|
||||
PurchasedServerLimit: 0.01,
|
||||
PurchasedServerMaxRam: 0.01,
|
||||
RepToDonateToFaction: 10000,
|
||||
ScriptHackMoney: 0.01,
|
||||
ScriptHackMoneyGain: 0.01,
|
||||
ServerGrowthRate: 0.01,
|
||||
ServerMaxMoney: 0.01,
|
||||
ServerStartingMoney: 0.01,
|
||||
ServerStartingSecurity: 100,
|
||||
ServerWeakenRate: 0.01,
|
||||
StrengthLevelMultiplier: 0.01,
|
||||
StaneksGiftPowerMultiplier: 0.01,
|
||||
StaneksGiftExtraSize: -100,
|
||||
WorldDaemonDifficulty: 100,
|
||||
CorporationSoftcap: 0.01,
|
||||
CorporationDivisions: 0.01,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
throw new Error("Invalid BitNodeN");
|
||||
}
|
||||
|
||||
@@ -114,9 +114,6 @@ export class BitNodeMultipliers {
|
||||
*/
|
||||
ManualHackMoney = 1;
|
||||
|
||||
/** Influence how strongly Myrian improves bitnode multipliers */
|
||||
MyrianPower = 1;
|
||||
|
||||
/** Influence how much it costs to purchase a server */
|
||||
PurchasedServerCost = 1;
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | <BitNodePortal n={19} level={n(19)} flume={props.flume} destroyedBitNode={destroyed} /> / | O | | O | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | <BitNodePortal n={14} level={n(14)} flume={props.flume} destroyedBitNode={destroyed} /> | | O__/ | / \__ | | O | | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
|
||||
|
||||
@@ -28,19 +28,18 @@ export function PortalModal(props: IProps): React.ReactElement {
|
||||
<Typography variant="h4">
|
||||
BitNode-{props.n}: {bitNode.name}
|
||||
</Typography>
|
||||
<Typography variant="h5">{bitNode.desc}</Typography>
|
||||
<br />
|
||||
<Typography>
|
||||
Source-File Level: {props.level} / {maxSourceFileLevel}
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography> Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>{bitNode.info}</Typography>
|
||||
<Typography component="div">{bitNode.info}</Typography>
|
||||
<BitnodeMultiplierDescription n={props.n} level={newLevel} />
|
||||
<br />
|
||||
<br />
|
||||
<Button
|
||||
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
|
||||
autoFocus={true}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { BladeburnerConstants } from "../data/Constants";
|
||||
import { calculateIntelligenceBonus } from "../../PersonObjects/formulas/intelligence";
|
||||
import { BladeMultName } from "../Enums";
|
||||
import { getRecordKeys } from "../../Types/Record";
|
||||
import { clampNumber } from "../../utils/helpers/clampNumber";
|
||||
|
||||
export interface ActionParams {
|
||||
desc: string;
|
||||
@@ -146,9 +147,16 @@ export abstract class ActionClass {
|
||||
let high = real + diff;
|
||||
const city = bladeburner.getCurrentCity();
|
||||
let r = city.pop / city.popEst;
|
||||
if (Number.isNaN(r)) r = 0;
|
||||
if (r < 1) low *= r;
|
||||
else high *= r;
|
||||
if (Number.isNaN(r)) {
|
||||
r = 0;
|
||||
}
|
||||
if (r < 1) {
|
||||
low *= r;
|
||||
} else {
|
||||
// We need to "clamp" r with "clampNumber" (not "clamp"), otherwise (high *= r) may be NaN. This happens when the
|
||||
// action is Raid, popEst=0, and comms=0.
|
||||
high *= clampNumber(r);
|
||||
}
|
||||
return [clamp(low), clamp(high)];
|
||||
}
|
||||
|
||||
|
||||
@@ -34,18 +34,20 @@ export class BlackOperation extends ActionClass {
|
||||
if (bladeburner.rank < this.reqdRank) return { error: "Insufficient rank" };
|
||||
return { available: true };
|
||||
}
|
||||
// To be implemented by subtypes
|
||||
|
||||
getActionTimePenalty(): number {
|
||||
return 1.5;
|
||||
}
|
||||
|
||||
getPopulationSuccessFactor(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
|
||||
getPopulationSuccessFactor(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getChaosSuccessFactor(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
|
||||
getChaosSuccessFactor(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getTeamSuccessBonus = operationTeamSuccessBonus;
|
||||
|
||||
getActionTypeSkillSuccessBonus = operationSkillSuccessBonus;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export class Operation extends LevelableActionClass {
|
||||
|
||||
// These functions are shared between operations and blackops, so they are defined outside of Operation
|
||||
getTeamSuccessBonus = operationTeamSuccessBonus;
|
||||
|
||||
getActionTypeSkillSuccessBonus = operationSkillSuccessBonus;
|
||||
|
||||
getChaosSuccessFactor(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
|
||||
@@ -45,7 +46,9 @@ export class Operation extends LevelableActionClass {
|
||||
return 1;
|
||||
}
|
||||
getSuccessChance(inst: Bladeburner, person: Person, params: SuccessChanceParams) {
|
||||
if (this.name == BladeOperationName.raid && inst.getCurrentCity().comms <= 0) return 0;
|
||||
if (this.name === BladeOperationName.raid && inst.getCurrentCity().comms <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return ActionClass.prototype.getSuccessChance.call(this, inst, person, params);
|
||||
}
|
||||
|
||||
@@ -57,6 +60,7 @@ export class Operation extends LevelableActionClass {
|
||||
toJSON(): IReviverValue {
|
||||
return this.save("Operation", "teamCount");
|
||||
}
|
||||
|
||||
loadData(loadedObject: Operation): void {
|
||||
this.teamCount = clampInteger(loadedObject.teamCount, 0);
|
||||
LevelableActionClass.prototype.loadData.call(this, loadedObject);
|
||||
@@ -73,6 +77,7 @@ constructorsForReviver.Operation = Operation;
|
||||
export const operationSkillSuccessBonus = (inst: Bladeburner) => {
|
||||
return inst.getSkillMult(BladeMultName.successChanceOperation);
|
||||
};
|
||||
|
||||
export function operationTeamSuccessBonus(this: Operation | BlackOperation, inst: Bladeburner) {
|
||||
if (this.teamCount && this.teamCount > 0) {
|
||||
this.teamCount = Math.min(this.teamCount, inst.teamSize);
|
||||
|
||||
@@ -125,7 +125,7 @@ export class Bladeburner {
|
||||
startAction(actionId: ActionIdentifier | null): Attempt<{ message: string }> {
|
||||
if (!actionId) {
|
||||
this.resetAction();
|
||||
return { success: true, message: "Stopped current bladeburner action" };
|
||||
return { success: true, message: "Stopped current Bladeburner action" };
|
||||
}
|
||||
if (!Player.hasAugmentation(AugmentationName.BladesSimulacrum, true)) Player.finishWork(true);
|
||||
const action = this.getActionObject(actionId);
|
||||
|
||||
@@ -11,18 +11,51 @@ import { TeamSizeButton } from "./TeamSizeButton";
|
||||
|
||||
import { formatNumberNoSuffix } from "../../ui/formatNumber";
|
||||
import { BlackOperation, Operation } from "../Actions";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
|
||||
interface ActionHeaderProps {
|
||||
bladeburner: Bladeburner;
|
||||
action: Action;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function ActionHeader({ bladeburner, action, rerender }: ActionHeaderProps): React.ReactElement {
|
||||
const isActive = action.name === bladeburner.action?.name;
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
|
||||
bladeburner.actionTimeToComplete,
|
||||
);
|
||||
const remainingSeconds = Math.max(
|
||||
bladeburner.actionTimeToComplete - bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
|
||||
0,
|
||||
);
|
||||
const remainingBonusSeconds = Math.floor(bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond);
|
||||
/**
|
||||
* Bladeburner is processed every second. Each time it's processed, we use (up to) 4 bonus seconds and process it as
|
||||
* if (up to) 5 seconds passed.
|
||||
* For example, with 20 bonus seconds, we need 5 seconds to use up all those bonus seconds. After 5 seconds, we used
|
||||
* up 20 bonus seconds and processed Bladeburner as if 25 seconds had passed.
|
||||
*/
|
||||
const effectiveBonusSeconds = (remainingBonusSeconds / 4) * 5;
|
||||
let eta;
|
||||
if (remainingSeconds <= effectiveBonusSeconds) {
|
||||
// If we have enough effectiveBonusSeconds, ETA is (remainingSeconds / 5).
|
||||
eta = Math.floor(remainingSeconds / 5);
|
||||
} else {
|
||||
/**
|
||||
* For example, let's say we start the "Training" action with 20 bonus seconds: remainingSeconds=30;remainingBonusSeconds=20.
|
||||
* After 5 seconds (remainingBonusSeconds / 4), we processed Bladeburner as if 25 seconds (effectiveBonusSeconds)
|
||||
* had passed. We still need 5 more seconds (30 - 25 = remainingTime - effectiveBonusSeconds) to complete the action
|
||||
* at normal speed.
|
||||
*
|
||||
* ETA = remainingBonusSeconds / 4 + remainingTime - effectiveBonusSeconds
|
||||
* = remainingBonusSeconds / 4 + remainingTime - ((remainingBonusSeconds / 4) * 5)
|
||||
* = remainingTime - remainingBonusSeconds
|
||||
*/
|
||||
eta = remainingSeconds - remainingBonusSeconds;
|
||||
}
|
||||
|
||||
const allowTeam = action instanceof Operation || action instanceof BlackOperation;
|
||||
|
||||
if (isActive) {
|
||||
@@ -36,11 +69,14 @@ export function ActionHeader({ bladeburner, action, rerender }: ActionHeaderProp
|
||||
(IN PROGRESS - {formatNumberNoSuffix(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumberNoSuffix(bladeburner.actionTimeToComplete, 0)})
|
||||
</Typography>
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</Typography>
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</Typography>
|
||||
<Typography marginLeft="1rem">Remaining time: {convertTimeMsToTimeElapsedString(eta * 1000)}</Typography>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ export function Stats({ bladeburner }: StatsProps): React.ReactElement {
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
{(bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000 > 15000 && (
|
||||
{bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond > 3 && (
|
||||
<>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
|
||||
@@ -55,9 +55,9 @@ export const CONSTANTS: {
|
||||
CompanyRequiredReputationMultiplier: number; // Only use this if a backdoor is installed in the company's server
|
||||
LatestUpdate: string;
|
||||
} = {
|
||||
VersionString: "2.6.2dev",
|
||||
isDevBranch: true,
|
||||
VersionNumber: 39,
|
||||
VersionString: "2.6.2",
|
||||
isDevBranch: false,
|
||||
VersionNumber: 40,
|
||||
|
||||
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
||||
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
||||
@@ -156,10 +156,72 @@ export const CONSTANTS: {
|
||||
|
||||
// Also update doc/source/changelog.rst
|
||||
LatestUpdate: `
|
||||
## v2.6.2 dev - Last update 22 May 2024
|
||||
## v2.6.2 Release: 3 July 2024
|
||||
|
||||
See 2.6.1 changelog at https://github.com/bitburner-official/bitburner-src/blob/v2.6.1/src/Documentation/doc/changelog.md
|
||||
### CHANGES
|
||||
|
||||
No changes yet since 2.6.1 release
|
||||
- Hotfix (also backported to 2.6.1): Fixed an issue with invalid format on steam cloud save (@catloversg)
|
||||
- Augmentations: Augmentations that affect starting money now add money instead of replacing it (@jjclark1982)
|
||||
- API: ns.spawn can now be used with 0 delay for a synchronous effect (@d0sboots)
|
||||
- API: Added the ns.ramOverride function, which allows altering the ram usage of a script at runtime (@d0sboots)
|
||||
- Coding Contracts: Improved the performance of the All Valid Math Expressions contract checker (@yichizhng)
|
||||
- Coding Contracts: Simplified the Shortest Path contract checker (@gmcew)
|
||||
- Documentation: Various improvements (@mrsimo, @catloversg, @ficocelliguy, @gmcew, @otac0n)
|
||||
- Electron: Game can now load correctly when the path includes symbolic links (@catloversg)
|
||||
- Faction: Fixed some edge cases around Favor overflow (@catloversg)
|
||||
- Faction: All available invites are sent at once (@catloversg)
|
||||
- Faction UI: show which skills are relevant for each type of Faction work (@gmcew)
|
||||
- Font: Embedded the JetBrains Mono font as "JetBrainsMono" (@catloversg)
|
||||
- Go: Can now play manually as white against your own scripts (@ficocelliguy)
|
||||
- Go: Save a full game history to prevent repeat moves (@ficocelliguy)
|
||||
- Go: Support offline time / bonus cycles to allow less time between AI moved (@ficocelliguy)
|
||||
- Hacking: Clamp hack success chance to prevent issues with infinity (@Caldwell-74)
|
||||
- Hacknet: Fix an issue that caused inaccurate level base cost (@JamesWilcox-git)
|
||||
- Hacknet: UI improvements (@jjclark1982)
|
||||
- Hospital: Can now be hospitalized even when busy performing a work task (@catloversg)
|
||||
- Infiltration: Automating infiltration is now more difficult (@catloversg)
|
||||
- Infiltration: Wire game shows wire colors on wires (@muesli4brekkies)
|
||||
- Misc: Changed how negative changes in system time are handled (@catloversg)
|
||||
- Programs UI: Now displays time remaining (@TheAimMan)
|
||||
- Servers: Existing servers can now have more than 1 core (@TheAimMan)
|
||||
- Scripts: Relative imports can now be used (@Caldwell-74)
|
||||
- Script Editor: Improved detection of possible infinite loops (@G4mingJon4s)
|
||||
- Script Editor: Cursor location is remembered when switching tabs or game pages (@catloversg)
|
||||
- Script Editor: Improvements to vim mode (@G4mingJon4s)
|
||||
- Script Editor: Individual script tabs can be in separate editor modes (@G4mingJon4s)
|
||||
- Skills: Fix an inconsistency in experience needed to level a skill from 1 to 2 (@catloversg)
|
||||
- Terminal: Add more options to the rm command (@G4mingJon4s, @d0sboots)
|
||||
- Terminal: Added grep command (@muesli4brekkies)
|
||||
- Terminal: Improved autocompletion for mixed case strings (@yichizhng)
|
||||
- Codebase: Partial migration away from outdated mui/styles (@Caldwell-74)
|
||||
- Codebase: Cleanup / refactoring (@catloversg, @Caldwell-74, @Snarling, @ficocelliguy, @tomprince)
|
||||
|
||||
### SPOILER CHANGES
|
||||
|
||||
- Bladeburner: Added a button to stop the current action (@Kelenius, @catloversg)
|
||||
- Bladeburner: Improved logging of information in the Bladeburner console (@Kelenius, @catloversg)
|
||||
- Bladeburner: Black Operations show in the expected order again (@catloversg)
|
||||
- Bitnode 5: Show bitnode multipliers while in BN5.1 (@catloversg)
|
||||
- Bitverse: Spawned scripts will no longer launch from the bitverse screen (@catloversg)
|
||||
- Corporation: Refactor markup multiplier (@catloversg)
|
||||
- Corporation: Allow mass discarding products by selling for 0 (@gmcew)
|
||||
- Corporation: Allow access to constants even when API access is not available (@ilkecan)
|
||||
- Gang: Show equipment even when it cannot be purchased yet (@catloversg)
|
||||
- Gang: Fix an issue with wanted gain rate (@catloversg)
|
||||
- Gang: Show effective gain rates when bonus time in effect (@G4mingJon4s)
|
||||
- Grafting: Fixed a spacing issue (@Sphyxis)
|
||||
- Grafting: Fixed an issue that could cause hacknet node production to be inaccurrate when combined with Grafting (@catloversg)
|
||||
- Grafting: Fixed an issue that could cause inaccurate HP after Grafting (@catloversg)
|
||||
- Grafting: Added ns.grafting.waitForOngoingGrafting (@catloversg)
|
||||
- Intelligence: Changed scaling for intelligence gain from manual hacking (@catloversg)
|
||||
- Sleeve: Sleeve travel can no longer be performed if the player has insufficient funds (@gmcew)
|
||||
- Sleeve: It's no longer possible to install an unavailable augmentation on a sleeve (@yichizhng)
|
||||
- Sleeve: No longer show a dialog message if a sleeve is working at a job while quitting that company (@Kelenius)
|
||||
- Sleeve: ns.sleeve.setToBladeburnerAction works again for setting sleeves to Bladeburner contract work (@Sphyxis)
|
||||
- Singularity: Add ns.singularity.getFactionWorkTypes (@catloversg)
|
||||
- Singularity: Fix an edge case issue with ns.singularity.getAugmentationFactions (@catloversg)
|
||||
|
||||
### OTHER
|
||||
- Nerf noodle bar
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -574,8 +574,7 @@ export class Division {
|
||||
sCost = optimalPrice;
|
||||
} else if (mat.marketTa1) {
|
||||
sCost = mat.marketPrice + markupLimit;
|
||||
// check truthyness to avoid unnecessary eval
|
||||
} else if (typeof mat.desiredSellPrice === "string" && mat.desiredSellPrice) {
|
||||
} else if (typeof mat.desiredSellPrice === "string") {
|
||||
sCost = mat.desiredSellPrice.replace(/MP/g, mat.marketPrice.toString());
|
||||
sCost = eval(sCost);
|
||||
} else {
|
||||
@@ -903,7 +902,7 @@ export class Division {
|
||||
product.markup = 1;
|
||||
}
|
||||
sCostString = sCostString.replace(/MP/g, product.cityData[city].productionCost.toString());
|
||||
sCost = Math.max(product.cityData[city].productionCost, eval(sCostString));
|
||||
sCost = eval(sCostString);
|
||||
} else {
|
||||
sCost = sellPrice;
|
||||
}
|
||||
|
||||
@@ -45,3 +45,196 @@ The only things that will persist through destroying BitNodes are:
|
||||
- [Source-Files](sourcefiles.md)
|
||||
- [Scripts](../basic/scripts.md) on the home computer
|
||||
- [Intelligence](intelligence.md)
|
||||
|
||||
## BitNode list
|
||||
|
||||
### BitNode 1: Source Genesis
|
||||
|
||||
This is the first BitNode created by the Enders to imprison the minds of humans. It became the prototype and testing ground for all of the BitNodes that followed.
|
||||
|
||||
This is the first BitNode that you play through. It has no special modifications or mechanics.
|
||||
|
||||
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 their home computer when entering a new BitNode and increases all of the player's multipliers by:
|
||||
|
||||
- Level 1: 16%
|
||||
- Level 2: 24%
|
||||
- Level 3: 28%
|
||||
|
||||
### BitNode 2: Rise of the Underworld
|
||||
|
||||
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.
|
||||
|
||||
Certain factions (Slum Snakes, Tetrads, The Syndicate, The Dark Army, Speakers for the Dead, NiteSec, and The Black Hand) give the player the ability to form and manage their own gang, which can earn the player money and reputation with the corresponding faction. The gang faction offers more augmentations than other factions, and in BitNode-2, it offers a way to destroy the BitNode.
|
||||
|
||||
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 your crime success rate, crime money, and charisma multipliers by:
|
||||
|
||||
- Level 1: 24%
|
||||
- Level 2: 36%
|
||||
- Level 3: 42%
|
||||
|
||||
### BitNode 3: Corporatocracy
|
||||
|
||||
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 to generate massive profits.
|
||||
|
||||
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:
|
||||
|
||||
- Level 1: 8%
|
||||
- Level 2: 12%
|
||||
- Level 3: 14%
|
||||
|
||||
### BitNode 4: The Singularity
|
||||
|
||||
The Singularity has arrived. The human race is gone, replaced by artificially super intelligent beings that are more machine than man.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
- Level 1: 16x
|
||||
- Level 2: 4x
|
||||
- Level 3: 1x
|
||||
|
||||
### BitNode 5: Artificial Intelligence
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
- Level 1: 8%
|
||||
- Level 2: 12%
|
||||
- Level 3: 14%
|
||||
|
||||
### BitNode 6: Bladeburners
|
||||
|
||||
In the middle of the 21st century, OmniTek Incorporated 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 hyper-intelligent 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.
|
||||
|
||||
In this BitNode, you will be able to access the Bladeburners division at the NSA, which provides a new mechanic for progression.
|
||||
|
||||
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 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:
|
||||
|
||||
- Level 1: 8%
|
||||
- Level 2: 12%
|
||||
- Level 3: 14%
|
||||
|
||||
### BitNode 7: Bladeburners 2079
|
||||
|
||||
In the middle of the 21st century, you were doing cutting-edge work at OmniTek Incorporated 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 hyper-intelligent 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.
|
||||
|
||||
In this BitNode, you will be able to access the Bladeburners API, which allows you to access Bladeburners functionality through Netscript.
|
||||
|
||||
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 Bladeburners Netscript API in other BitNodes. In addition, this Source-File will increase all of your Bladeburners multipliers by:
|
||||
|
||||
- Level 1: 8%
|
||||
- Level 2: 12%
|
||||
- Level 3: 14%
|
||||
|
||||
### BitNode 8: Ghost of Wall Street
|
||||
|
||||
You are trying to make a name for yourself as an up-and-coming hedge fund manager on Wall Street.
|
||||
|
||||
In this BitNode:
|
||||
|
||||
- You start with $250 million
|
||||
- You start with a WSE membership and access to the TIX API
|
||||
- You are able to short stocks and place different types of orders (limit/stop)
|
||||
|
||||
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:
|
||||
|
||||
- Level 1: Permanent access to WSE and TIX API
|
||||
- Level 2: Ability to short stocks in other BitNodes
|
||||
- Level 3: Ability to use limit/stop orders in other BitNodes
|
||||
|
||||
This Source-File also increases your hacking growth multipliers by:
|
||||
|
||||
- Level 1: 12%
|
||||
- Level 2: 18%
|
||||
- Level 3: 21%
|
||||
|
||||
### BitNode 9: Hacktocracy
|
||||
|
||||
When Fulcrum Secret Technologies 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, which is a global, decentralized network used for nefarious purposes. Fulcrum Secret Technologies quickly abandoned the project and dissociated themselves from it.
|
||||
|
||||
This BitNode unlocks the Hacknet Server, which is an upgraded version of the Hacknet Node. Hacknet Servers generate hashes, which can be spent on a variety of different upgrades.
|
||||
|
||||
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:
|
||||
|
||||
- Level 1: Permanently unlocks the Hacknet Server in other BitNodes
|
||||
- Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode
|
||||
- Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
|
||||
|
||||
(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 hacknet production and reduces hacknet costs by:
|
||||
|
||||
- Level 1: 12%
|
||||
- Level 2: 18%
|
||||
- Level 3: 21%
|
||||
|
||||
### BitNode 10: Digital Carbon
|
||||
|
||||
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.
|
||||
|
||||
This BitNode unlocks Sleeve and Grafting technology:
|
||||
|
||||
- Sleeve: Duplicate your consciousness into Synthoids, allowing you to perform different tasks asynchronously. You cannot buy Sleeves outside this BitNode.
|
||||
- Grafting: Visit VitaLife in New Tokyo to get access to this technology. It allows you to graft augmentations, which is an alternative way of installing augmentations.
|
||||
|
||||
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 and Grafting API in other BitNodes. Each level of this Source-File also grants you a Sleeve.
|
||||
|
||||
### BitNode 11: The Big Crash
|
||||
|
||||
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 led 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 catastrophes 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.
|
||||
|
||||
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:
|
||||
|
||||
- Level 1: 32%
|
||||
- Level 2: 48%
|
||||
- Level 3: 56%
|
||||
|
||||
It also reduces the price increase for every augmentation bought by:
|
||||
|
||||
- Level 1: 4%
|
||||
- Level 2: 6%
|
||||
- Level 3: 7%
|
||||
|
||||
### BitNode 12: The Recursion
|
||||
|
||||
To iterate is human; to recurse, divine.
|
||||
|
||||
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.
|
||||
|
||||
### BitNode 13: They're lunatics
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### BitNode 14: IPvGO Subnet Takeover
|
||||
|
||||
In late 2070, the .org bubble burst, and most of the newly-implemented IPvGO 'net collapsed overnight. Since then, various factions have been fighting over small subnets to control their computational power. These subnets are very valuable in the right hands, if you can wrest them from their current owners. You will be opposed by the other factions, but you can overcome them with careful choices. Prevent their attempts to destroy your networks by controlling the open space in the 'net!
|
||||
|
||||
Destroying this BitNode will give you Source-File 14, 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:
|
||||
|
||||
- Level 1: 100% increased stat multipliers from Node Power
|
||||
- Level 2: Permanently unlocks the go.cheat API
|
||||
- Level 3: 25% additive increased success rate for the go.cheat API
|
||||
|
||||
This Source-File also increases the maximum favor you can gain for each faction from IPvGO to:
|
||||
|
||||
- Level 1: 80
|
||||
- Level 2: 100
|
||||
- Level 3: 120
|
||||
|
||||
@@ -1,5 +1,74 @@
|
||||
# Changelog
|
||||
|
||||
## v2.6.2 Release: 3 July 2024
|
||||
|
||||
### CHANGES
|
||||
|
||||
- Hotfix (also backported to 2.6.1): Fixed an issue with invalid format on steam cloud save (@catloversg)
|
||||
- Augmentations: Augmentations that affect starting money now add money instead of replacing it (@jjclark1982)
|
||||
- API: ns.spawn can be used with 0 delay for a synchronous effect (@d0sboots)
|
||||
- API: Added the ns.ramOverride function, which allows altering the ram usage of a script at runtime (@d0sboots)
|
||||
- Coding Contracts: Improved the performance of the All Valid Math Expressions contract checker (@yichizhng)
|
||||
- Coding Contracts: Simplified the Shortest Path contract checker (@gmcew)
|
||||
- Documentation: Various improvements (@mrsimo, @catloversg, @ficocelliguy, @gmcew, @otac0n)
|
||||
- Electron: Game can now load correctly when the path includes symbolic links (@catloversg)
|
||||
- Faction: Fixed some edge cases around Favor overflow (@catloversg)
|
||||
- Faction: All available invites are sent at once (@catloversg)
|
||||
- Faction UI: show which skills are relevant for each type of Faction work (@gmcew)
|
||||
- Font: Embedded the JetBrains Mono font as "JetBrainsMono" (@catloversg)
|
||||
- Go: Can now play manually as white against your own scripts (@ficocelliguy)
|
||||
- Go: Save a full game history to prevent repeat moves (@ficocelliguy)
|
||||
- Go: Support offline time / bonus cycles to allow less time between AI moved (@ficocelliguy)
|
||||
- Hacking: Clamp hack success chance to prevent issues with infinity (@Caldwell-74)
|
||||
- Hacknet: Fix an issue that caused inaccurate level base cost (@JamesWilcox-git)
|
||||
- Hacknet: UI improvements (@jjclark1982)
|
||||
- Hospital: Can now be hospitalized even when busy performing a work task (@catloversg)
|
||||
- Infiltration: Automating infiltration is now more difficult (@catloversg)
|
||||
- Infiltration: Wire game shows wire colors on wires (@muesli4brekkies)
|
||||
- Misc: Changed how negative changes in system time are handled (@catloversg)
|
||||
- Programs UI: Now displays time remaining (@TheAimMan)
|
||||
- Servers: Existing servers can now have more than 1 core (@TheAimMan)
|
||||
- Scripts: Relative imports can now be used (@Caldwell-74)
|
||||
- Script Editor: Improved detection of possible infinite loops (@G4mingJon4s)
|
||||
- Script Editor: Cursor location is remembered when switching tabs or game pages (@catloversg)
|
||||
- Script Editor: Improvements to vim mode (@G4mingJon4s)
|
||||
- Script Editor: Individual script tabs can be in separate editor modes (@G4mingJon4s)
|
||||
- Skills: Fix an inconsistency in experience needed to level a skill from 1 to 2 (@catloversg)
|
||||
- Terminal: Add more options to the rm command (@G4mingJon4s, @d0sboots)
|
||||
- Terminal: Added grep command (@muesli4brekkies)
|
||||
- Terminal: Improved autocompletion for mixed case strings (@yichizhng)
|
||||
- Codebase: Partial migration away from outdated mui/styles (@Caldwell-74)
|
||||
- Codebase: Cleanup / refactoring (@catloversg, @Caldwell-74, @Snarling, @ficocelliguy, @tomprince)
|
||||
|
||||
### SPOILER CHANGES
|
||||
|
||||
- Bladeburner: Added a button to stop the current action (@Kelenius, @catloversg)
|
||||
- Bladeburner: Improved logging of information in the Bladeburner console (@Kelenius, @catloversg)
|
||||
- Bladeburner: Black Operations show in the expected order again (@catloversg)
|
||||
- Bitnode 5: Show bitnode multipliers while in BN5.1 (@catloversg)
|
||||
- Bitverse: Spawned scripts will no longer launch from the bitverse screen (@catloversg)
|
||||
- Corporation: Refactor markup multiplier (@catloversg)
|
||||
- Corporation: Allow mass discarding products by selling for 0 (@gmcew)
|
||||
- Corporation: Allow access to constants even when API access is not available (@ilkecan)
|
||||
- Gang: Show equipment even when it cannot be purchased yet (@catloversg)
|
||||
- Gang: Fix an issue with wanted gain rate (@catloversg)
|
||||
- Gang: Show effective gain rates when bonus time in effect (@G4mingJon4s)
|
||||
- Grafting: Fixed a spacing issue (@Sphyxis)
|
||||
- Grafting: Fixed an issue that could cause hacknet node production to be inaccurrate when combined with Grafting (@catloversg)
|
||||
- Grafting: Fixed an issue that could cause inaccurate HP after Grafting (@catloversg)
|
||||
- Grafting: Added ns.grafting.waitForOngoingGrafting (@catloversg)
|
||||
- Intelligence: Changed scaling for intelligence gain from manual hacking (@catloversg)
|
||||
- Sleeve: Sleeve travel can no longer be performed if the player has insufficient funds (@gmcew)
|
||||
- Sleeve: It's no longer possible to install an unavailable augmentation on a sleeve (@yichizhng)
|
||||
- Sleeve: No longer show a dialog message if a sleeve is working at a job while quitting that company (@Kelenius)
|
||||
- Sleeve: ns.sleeve.setToBladeburnerAction works again for setting sleeves to Bladeburner contract work (@Sphyxis)
|
||||
- Singularity: Add ns.singularity.getFactionWorkTypes (@catloversg)
|
||||
- Singularity: Fix an edge case issue with ns.singularity.getAugmentationFactions (@catloversg)
|
||||
|
||||
### OTHER
|
||||
|
||||
- Nerf noodle bar
|
||||
|
||||
## v2.6.1 - 21 May 2024
|
||||
|
||||
### MAJOR CHANGES
|
||||
|
||||
@@ -27,19 +27,19 @@
|
||||
## Advanced Mechanics
|
||||
|
||||
- [Hacking Algorithms](programming/hackingalgorithms.md)
|
||||
- [IPvGO](programming/go_algorithms.md)
|
||||
- [BitNodes](advanced/bitnodes.md)
|
||||
- [BladeBurners](advanced/bladeburners.md)
|
||||
- [Corporations](advanced/corporations.md)
|
||||
- [Gang](advanced/gang.md)
|
||||
- [Grafting](advanced/grafting.md)
|
||||
- [Hacknet Servers](advanced/hacknetservers.md)
|
||||
- [Intelligence](advanced/intelligence.md)
|
||||
- [List of Factions and their Requirements](advanced/faction_list.md)
|
||||
- [Offline Scripts and Bonus Time](advanced/offlineandbonustime.md)
|
||||
- [Sleeves](advanced/sleeves.md)
|
||||
- [BitNodes](advanced/bitnodes.md)
|
||||
- [Source-Files](advanced/sourcefiles.md)
|
||||
- [Gang](advanced/gang.md)
|
||||
- [Corporations](advanced/corporations.md)
|
||||
- [Intelligence](advanced/intelligence.md)
|
||||
- [BladeBurners](advanced/bladeburners.md)
|
||||
- [Hacknet Servers](advanced/hacknetservers.md)
|
||||
- [Sleeves](advanced/sleeves.md)
|
||||
- [Grafting](advanced/grafting.md)
|
||||
- [Stanek's Gift](advanced/stanek.md)
|
||||
- [IPvGO](programming/go_algorithms.md)
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
@@ -91,14 +91,12 @@ Now that a simple move type is available, it can be used to play on the current
|
||||
|
||||
`await ns.go.passTurn()` can be used if no moves are found. This will end the game if the AI also passes (or just passed previously).
|
||||
|
||||
Both `makeMove()` and `passTurn()` , when awaited, return an object that tells you if your move was valid and successfully played, and what the AI's response is.
|
||||
Both `makeMove()` and `passTurn()`, when awaited, return an object that tells you what the AI's response is, and if the game is over.
|
||||
|
||||
```js
|
||||
{
|
||||
// If your move was successfully applied to the subnet
|
||||
success: boolean;
|
||||
// If the opponent moved or passed, or if the game is now over, or if your move was invalid
|
||||
type: "invalid" | "move" | "pass" | "gameOver";
|
||||
// If the opponent moved or passed, or if the game is now over.
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null; // Opponent move's x coord (if applicable)
|
||||
y: number | null; // Opponent move's y coord (if applicable)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useLayoutEffect, useState } from "react";
|
||||
|
||||
import Button from "@mui/material/Button";
|
||||
import { MD } from "../../ui/MD/MD";
|
||||
|
||||
import { getPage } from "../root";
|
||||
import { Navigator, useHistory } from "../../ui/React/Documentation";
|
||||
import { Navigator, windowTopPositionOfPages, useHistory } from "../../ui/React/Documentation";
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { asFilePath, resolveFilePath } from "../../Paths/FilePath";
|
||||
import Box from "@mui/material/Box";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { Router } from "../../ui/GameRoot";
|
||||
import { Page } from "../../ui/Router";
|
||||
|
||||
export function DocumentationRoot({ docPage }: { docPage?: string }): React.ReactElement {
|
||||
const history = useHistory();
|
||||
@@ -15,7 +18,6 @@ export function DocumentationRoot({ docPage }: { docPage?: string }): React.Reac
|
||||
history.push(asFilePath(deepLink));
|
||||
setDeepLink(undefined);
|
||||
}
|
||||
const page = getPage(history.page);
|
||||
const navigator = {
|
||||
navigate(relPath: string, external: boolean) {
|
||||
const newPath = resolveFilePath("./" + relPath, history.page);
|
||||
@@ -30,19 +32,30 @@ export function DocumentationRoot({ docPage }: { docPage?: string }): React.Reac
|
||||
return;
|
||||
}
|
||||
history.push(newPath);
|
||||
|
||||
// Reset scroll to the top of the page.
|
||||
window.scrollTo(0, 0);
|
||||
},
|
||||
};
|
||||
|
||||
// We need to use "useLayoutEffect" instead of "useEffect". "useLayoutEffect" is fired before the browser repaints the
|
||||
// screen.
|
||||
useLayoutEffect(() => {
|
||||
return () => {
|
||||
if (Router.page() !== Page.Documentation) {
|
||||
windowTopPositionOfPages.set(history.page, window.scrollY);
|
||||
}
|
||||
};
|
||||
}, [history]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => history.pop()}>Back</Button>
|
||||
<Button onClick={() => history.home()}>Home</Button>
|
||||
<Navigator.Provider value={navigator}>
|
||||
<MD md={page + ""} />
|
||||
</Navigator.Provider>
|
||||
<Box position="fixed" top={0} zIndex={1} width="100%" paddingTop="8px" bgcolor={Settings.theme.backgroundprimary}>
|
||||
<Button onClick={() => history.pop()}>Back</Button>
|
||||
<Button onClick={() => history.home()}>Home</Button>
|
||||
</Box>
|
||||
<Box paddingTop="50px">
|
||||
<Navigator.Provider value={navigator}>
|
||||
<MD pageFilePath={history.page} top={windowTopPositionOfPages.get(history.page) ?? 0} />
|
||||
</Navigator.Provider>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,4 +14,3 @@ export * from "./Programs/Enums";
|
||||
export * from "./StockMarket/Enums";
|
||||
export * from "./ui/Enums";
|
||||
export * from "./Work/Enums";
|
||||
export * from "./Myrian/Enums";
|
||||
|
||||
@@ -657,8 +657,8 @@ export const FactionInfos: Record<FactionName, FactionInfo> = {
|
||||
|
||||
// Early game factions - factions the player will prestige with early on that don't belong in other categories.
|
||||
[FactionName.Netburners]: new FactionInfo({
|
||||
infoText: <>{"~~//*>H4CK|\\|3T 8URN3R5**>?>\\~~"}</>,
|
||||
rumorText: <>{"~~//*>H4CK|\\|3T 8URN3R5**>?>\\~~"}</>,
|
||||
infoText: <>{"~~//*>H4CK|\\|3T 8URN3R5**>?>\\\\~~"}</>,
|
||||
rumorText: <>{"~~//*>H4CK|\\|3T 8URN3R5**>?>\\\\~~"}</>,
|
||||
inviteReqs: [haveSkill("hacking", 80), totalHacknetRam(8), totalHacknetCores(4), totalHacknetLevels(100)],
|
||||
rumorReqs: [totalHacknetLevels(50)],
|
||||
offerHackingWork: true,
|
||||
|
||||
@@ -42,6 +42,18 @@ export const MiscPage = (): React.ReactElement => {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch
|
||||
checked={Settings.MonacoDefaultToVim}
|
||||
onChange={(newValue) => (Settings.MonacoDefaultToVim = newValue)}
|
||||
text="Enable Vim as default editor"
|
||||
tooltip={
|
||||
<>
|
||||
This setting is only used when opening a file through ways that do not determine the editor mode. Using
|
||||
'nano' or 'vim' will set the editor mode for the specified files, while 'ls' will open the file using the
|
||||
the value from this setting.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</GameOptionsPage>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -154,9 +154,11 @@ export class Gang {
|
||||
const newWanted = oldWanted + wantedLevelGainPerCycle * numCycles;
|
||||
// Allows recovery when wanted / respect ratio is too high
|
||||
this.wanted = newWanted * (1 - justice * 0.001);
|
||||
this.wantedGainRate -= newWanted - this.wanted;
|
||||
this.wantedGainRate = (this.wanted - oldWanted) / numCycles;
|
||||
// Prevent overflow
|
||||
if (this.wanted < 1 || (wantedLevelGainPerCycle <= 0 && this.wanted > oldWanted)) this.wanted = 1;
|
||||
if (this.wanted < 1 || (wantedLevelGainPerCycle <= 0 && this.wanted > oldWanted)) {
|
||||
this.wanted = 1;
|
||||
}
|
||||
}
|
||||
Player.gainMoney(moneyGainPerCycle * numCycles, "gang");
|
||||
}
|
||||
@@ -315,10 +317,14 @@ export class Gang {
|
||||
}
|
||||
|
||||
getRecruitsAvailable(): number {
|
||||
if (this.members.length >= GangConstants.MaximumGangMembers) {
|
||||
return 0;
|
||||
}
|
||||
const numFreeMembers = 3;
|
||||
const recruitCostBase = 5;
|
||||
if (this.members.length < numFreeMembers && this.respect < Math.pow(recruitCostBase, numFreeMembers))
|
||||
if (this.members.length < numFreeMembers && this.respect < Math.pow(recruitCostBase, numFreeMembers)) {
|
||||
return numFreeMembers - this.members.length; // if the max possible is less than freeMembers
|
||||
}
|
||||
return Math.floor(Math.log(this.respect) / Math.log(recruitCostBase)) + numFreeMembers - this.members.length; //else
|
||||
}
|
||||
|
||||
|
||||
@@ -21,31 +21,6 @@ import { Settings } from "../../Settings/Settings";
|
||||
import { StatsRow } from "../../ui/React/StatsRow";
|
||||
import { useRerender } from "../../ui/React/hooks";
|
||||
|
||||
interface INextRevealProps {
|
||||
upgrades: string[];
|
||||
type: UpgradeType;
|
||||
}
|
||||
|
||||
function NextReveal(props: INextRevealProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const upgrades = Object.keys(GangMemberUpgrades)
|
||||
.filter((upgName: string) => {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
if (Player.money > gang.getUpgradeCost(upg)) return false;
|
||||
if (upg.type !== props.type) return false;
|
||||
if (props.upgrades.includes(upgName)) return false;
|
||||
return true;
|
||||
})
|
||||
.map((upgName: string) => GangMemberUpgrades[upgName]);
|
||||
|
||||
if (upgrades.length === 0) return <></>;
|
||||
return (
|
||||
<Typography>
|
||||
Next at <Money money={gang.getUpgradeCost(upgrades[0])} />
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
function PurchasedUpgrade({ upgName }: { upgName: string }): React.ReactElement {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
return (
|
||||
@@ -65,6 +40,8 @@ interface IUpgradeButtonProps {
|
||||
|
||||
function UpgradeButton(props: IUpgradeButtonProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const upgradeCost = gang.getUpgradeCost(props.upg);
|
||||
const isUpgradable = Player.money >= upgradeCost;
|
||||
function onClick(): void {
|
||||
props.member.buyUpgrade(props.upg);
|
||||
props.rerender();
|
||||
@@ -72,9 +49,13 @@ function UpgradeButton(props: IUpgradeButtonProps): React.ReactElement {
|
||||
return (
|
||||
<Tooltip title={<Typography dangerouslySetInnerHTML={{ __html: props.upg.desc }} />}>
|
||||
<span>
|
||||
<Button onClick={onClick} sx={{ display: "flex", flexDirection: "column", width: "100%", height: "100%" }}>
|
||||
<Button
|
||||
disabled={!isUpgradable}
|
||||
onClick={onClick}
|
||||
sx={{ display: "flex", flexDirection: "column", width: "100%", height: "100%" }}
|
||||
>
|
||||
<Typography sx={{ display: "block" }}>{props.upg.name}</Typography>
|
||||
<Money money={gang.getUpgradeCost(props.upg)} />
|
||||
<Money money={upgradeCost} forPurchase />
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -86,7 +67,6 @@ interface IPanelProps {
|
||||
}
|
||||
|
||||
function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
const gang = useGang();
|
||||
const rerender = useRerender();
|
||||
const [currentCategory, setCurrentCategory] = useState("Weapons");
|
||||
|
||||
@@ -94,7 +74,6 @@ function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
return Object.keys(GangMemberUpgrades)
|
||||
.filter((upgName: string) => {
|
||||
const upg = GangMemberUpgrades[upgName];
|
||||
if (Player.money < gang.getUpgradeCost(upg)) return false;
|
||||
if (upg.type !== type) return false;
|
||||
if (list.includes(upgName)) return false;
|
||||
return true;
|
||||
@@ -214,7 +193,6 @@ function GangMemberUpgradePanel(props: IPanelProps): React.ReactElement {
|
||||
<UpgradeButton key={upg.name} rerender={rerender} member={props.member} upg={upg} />
|
||||
))}
|
||||
</Box>
|
||||
<NextReveal type={categories[currentCategory][1] as UpgradeType} upgrades={props.member.upgrades} />
|
||||
</Box>
|
||||
</span>
|
||||
</Box>
|
||||
|
||||
@@ -27,6 +27,21 @@ export function GangStats(): React.ReactElement {
|
||||
} else {
|
||||
territoryStr = formatNumberNoSuffix(territoryMult, 2);
|
||||
}
|
||||
const hasEnoughBonusTime = gang.storedCycles > GangConstants.maxCyclesToProcess;
|
||||
const bonusCyclesInOneSecond = 5 * GangConstants.maxCyclesToProcess;
|
||||
const respectGainRateInBonusTime = hasEnoughBonusTime
|
||||
? `[Effective Gain: ${formatRespect(gang.respectGainRate * bonusCyclesInOneSecond)} / sec]`
|
||||
: "";
|
||||
const wantedGainRateInBonusTime = hasEnoughBonusTime
|
||||
? `[Effective Gain: ${formatWanted(gang.wantedGainRate * bonusCyclesInOneSecond)} / sec]`
|
||||
: "";
|
||||
const moneyGainRateInBonusTime = hasEnoughBonusTime ? (
|
||||
<>
|
||||
[Effective Gain: <MoneyRate money={gang.moneyGainRate * bonusCyclesInOneSecond} />]
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -42,9 +57,7 @@ export function GangStats(): React.ReactElement {
|
||||
>
|
||||
<Typography>
|
||||
Respect: {formatRespect(gang.respect)} ({formatRespect(5 * gang.respectGainRate)} / sec){" "}
|
||||
{gang.storedCycles > 2 * GangConstants.maxCyclesToProcess
|
||||
? `[Effective Gain: ${formatRespect(5 * gang.respectGainRate * GangConstants.maxCyclesToProcess)} / sec]`
|
||||
: ""}
|
||||
{respectGainRateInBonusTime}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
@@ -60,9 +73,7 @@ export function GangStats(): React.ReactElement {
|
||||
>
|
||||
<Typography>
|
||||
Wanted Level: {formatWanted(gang.wanted)} ({formatWanted(5 * gang.wantedGainRate)} / sec){" "}
|
||||
{gang.storedCycles > 2 * GangConstants.maxCyclesToProcess
|
||||
? `[Effective Gain: ${formatWanted(5 * gang.wantedGainRate * GangConstants.maxCyclesToProcess)} / sec]`
|
||||
: ""}
|
||||
{wantedGainRateInBonusTime}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
@@ -76,14 +87,7 @@ export function GangStats(): React.ReactElement {
|
||||
</Box>
|
||||
|
||||
<Typography>
|
||||
Money gain rate: <MoneyRate money={5 * gang.moneyGainRate} />{" "}
|
||||
{gang.storedCycles > 2 * GangConstants.maxCyclesToProcess ? "[Effective Gain:" : ""}{" "}
|
||||
{gang.storedCycles > 2 * GangConstants.maxCyclesToProcess ? (
|
||||
<MoneyRate money={5 * gang.moneyGainRate * GangConstants.maxCyclesToProcess} />
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{gang.storedCycles > 2 * GangConstants.maxCyclesToProcess ? "]" : ""}
|
||||
Money gain rate: <MoneyRate money={5 * gang.moneyGainRate} /> {moneyGainRateInBonusTime}
|
||||
</Typography>
|
||||
|
||||
<Box display="flex">
|
||||
|
||||
@@ -22,7 +22,7 @@ export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1
|
||||
|
||||
const mult = HacknetNodeConstants.UpgradeLevelMult;
|
||||
let totalMultiplier = 0;
|
||||
let currLevel = startingLevel;
|
||||
let currLevel = startingLevel - 1;
|
||||
for (let i = 0; i < sanitizedLevels; ++i) {
|
||||
totalMultiplier += Math.pow(mult, currLevel);
|
||||
++currLevel;
|
||||
|
||||
@@ -17,6 +17,7 @@ export function GeneralInfo(props: IProps): React.ReactElement {
|
||||
The Hacknet is a global, decentralized network of machines. It is used by hackers all around the world to
|
||||
anonymously share computing power and perform distributed cyberattacks without the fear of being traced.
|
||||
</Typography>
|
||||
<br />
|
||||
{!props.hasHacknetServers ? (
|
||||
<>
|
||||
<Typography>
|
||||
|
||||
@@ -164,7 +164,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Grid item component={Paper} p={1}>
|
||||
<Table size="small">
|
||||
<Table size="small" sx={{ whiteSpace: "nowrap" }}>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={3}>
|
||||
|
||||
@@ -102,21 +102,28 @@ export function HacknetRoot(): React.ReactElement {
|
||||
<Typography variant="h4">Hacknet {hasHacknetServers() ? "Servers" : "Nodes"}</Typography>
|
||||
<GeneralInfo hasHacknetServers={hasHacknetServers()} />
|
||||
|
||||
<PurchaseButton cost={purchaseCost} multiplier={purchaseMultiplier} onClick={handlePurchaseButtonClick} />
|
||||
<br />
|
||||
|
||||
<PlayerInfo totalProduction={totalProduction} />
|
||||
|
||||
<br />
|
||||
|
||||
{hasHacknetServers() && (
|
||||
<>
|
||||
<Button onClick={() => setOpen(true)}>Spend Hashes on Upgrades</Button>
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<PlayerInfo totalProduction={totalProduction} />
|
||||
<PurchaseButton cost={purchaseCost} multiplier={purchaseMultiplier} onClick={handlePurchaseButtonClick} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={purchaseMultiplier} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{hasHacknetServers() && <Button onClick={() => setOpen(true)}>Spend Hashes on Upgrades</Button>}
|
||||
|
||||
<Box sx={{ display: "grid", width: "100%", gridTemplateColumns: "repeat(auto-fit, 30em)" }}>{nodes}</Box>
|
||||
<HashUpgradeModal open={open} onClose={() => setOpen(false)} />
|
||||
</>
|
||||
|
||||
@@ -245,10 +245,10 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<Grid item component={Paper} p={1}>
|
||||
<Table size="small">
|
||||
<Table size="small" sx={{ whiteSpace: "nowrap" }}>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<TableCell colSpan={3}>
|
||||
<Typography>{node.hostname}</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -12,7 +12,9 @@ import { Money } from "../../ui/React/Money";
|
||||
import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||
import { HashRate } from "../../ui/React/HashRate";
|
||||
import { Hashes } from "../../ui/React/Hashes";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Paper, Typography } from "@mui/material";
|
||||
import { StatsTable } from "../../ui/React/StatsTable";
|
||||
import { Tooltip } from "@mui/material";
|
||||
|
||||
interface IProps {
|
||||
totalProduction: number;
|
||||
@@ -21,31 +23,39 @@ interface IProps {
|
||||
export function PlayerInfo(props: IProps): React.ReactElement {
|
||||
const hasServers = hasHacknetServers();
|
||||
|
||||
let prod;
|
||||
const rows: React.ReactNode[][] = [];
|
||||
rows.push(["Money Spent:", <Money key="money" money={-Player.moneySourceA.hacknet_expenses || 0} />]);
|
||||
rows.push(["Money Produced:", <Money key="money" money={Player.moneySourceA.hacknet} />]);
|
||||
if (hasServers) {
|
||||
prod = <HashRate hashes={props.totalProduction} />;
|
||||
rows.push([
|
||||
"Hashes:",
|
||||
<span key={"hashes"}>
|
||||
<Hashes hashes={Player.hashManager.hashes} /> / <Hashes hashes={Player.hashManager.capacity} />
|
||||
</span>,
|
||||
]);
|
||||
rows.push([
|
||||
"Hash Rate:",
|
||||
<Tooltip
|
||||
key="moneyRate"
|
||||
title={
|
||||
<Typography>
|
||||
<MoneyRate money={(props.totalProduction * 1e6) / 4} /> if sold for money
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<HashRate key="hashRate" hashes={props.totalProduction} />
|
||||
</span>
|
||||
</Tooltip>,
|
||||
]);
|
||||
} else {
|
||||
prod = <MoneyRate money={props.totalProduction} />;
|
||||
rows.push(["Production Rate:", <MoneyRate key="moneyRate" money={props.totalProduction} />]);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
Money:
|
||||
<Money money={Player.money} />
|
||||
</Typography>
|
||||
|
||||
{hasServers && (
|
||||
<>
|
||||
<Typography>
|
||||
Hashes: <Hashes hashes={Player.hashManager.hashes} /> / <Hashes hashes={Player.hashManager.capacity} />
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Typography>
|
||||
Total Hacknet {hasServers ? "Server" : "Node"} Production: {prod}
|
||||
</Typography>
|
||||
</>
|
||||
<Paper sx={{ display: "inline-block", padding: "0.5em 1em", margin: "0.5em 0" }}>
|
||||
<Typography variant="h6">Hacknet Summary</Typography>
|
||||
<StatsTable rows={rows} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,15 +5,23 @@ interface IProps {
|
||||
onFinish: () => void;
|
||||
}
|
||||
|
||||
export function Countdown(props: IProps): React.ReactElement {
|
||||
export function Countdown({ onFinish }: IProps): React.ReactElement {
|
||||
const [x, setX] = useState(3);
|
||||
|
||||
useEffect(() => {
|
||||
if (x === 0) {
|
||||
props.onFinish();
|
||||
return;
|
||||
onFinish();
|
||||
}
|
||||
setTimeout(() => setX(x - 1), 300);
|
||||
});
|
||||
}, [x, onFinish]);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => {
|
||||
setX((previousValue) => previousValue - 1);
|
||||
}, 300);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Paper sx={{ p: 1, textAlign: "center" }}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button, Container, Paper, Typography } from "@mui/material";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { FactionName } from "@enums";
|
||||
import { FactionName, ToastVariant } from "@enums";
|
||||
import { Router } from "../../ui/GameRoot";
|
||||
import { Page } from "../../ui/Router";
|
||||
import { Player } from "@player";
|
||||
@@ -15,6 +15,7 @@ import { SlashGame } from "./SlashGame";
|
||||
import { Victory } from "./Victory";
|
||||
import { WireCuttingGame } from "./WireCuttingGame";
|
||||
import { calculateDamageAfterFailingInfiltration } from "../utils";
|
||||
import { SnackbarEvents } from "../../ui/React/Snackbar";
|
||||
|
||||
type GameProps = {
|
||||
StartingDifficulty: number;
|
||||
@@ -91,11 +92,18 @@ export function Game(props: GameProps): React.ReactElement {
|
||||
setStage(Stage.Countdown);
|
||||
pushResult(false);
|
||||
Player.receiveRumor(FactionName.ShadowsOfAnarchy);
|
||||
// Kill the player immediately if they use automation, so
|
||||
// it's clear they're not meant to
|
||||
const damage = options?.automated
|
||||
? Player.hp.current
|
||||
: calculateDamageAfterFailingInfiltration(props.StartingDifficulty);
|
||||
let damage = calculateDamageAfterFailingInfiltration(props.StartingDifficulty);
|
||||
// Kill the player immediately if they use automation, so it's clear they're not meant to
|
||||
if (options?.automated) {
|
||||
damage = Player.hp.current;
|
||||
setTimeout(() => {
|
||||
SnackbarEvents.emit(
|
||||
"You were hospitalized. Do not try to automate infiltration!",
|
||||
ToastVariant.WARNING,
|
||||
5000,
|
||||
);
|
||||
}, 500);
|
||||
}
|
||||
if (Player.takeDamage(damage)) {
|
||||
Router.toPage(Page.City);
|
||||
return;
|
||||
|
||||
@@ -23,10 +23,15 @@ export function GameTimer({
|
||||
const totalMillis =
|
||||
(!ignoreAugment_WKSharmonizer && Player.hasAugmentation(AugmentationName.WKSharmonizer, true) ? 1.3 : 1) * millis;
|
||||
|
||||
useEffect(() => {
|
||||
if (v <= 0) {
|
||||
onExpire();
|
||||
}
|
||||
}, [v, onExpire]);
|
||||
|
||||
useEffect(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
setV((old) => {
|
||||
if (old <= 0) onExpire();
|
||||
return old - (tick / totalMillis) * 100;
|
||||
});
|
||||
}, tick);
|
||||
@@ -40,10 +45,10 @@ export function GameTimer({
|
||||
// TODO(hydroflame): there's like a bug where it triggers the end before the
|
||||
// bar physically reaches the end
|
||||
return noPaper ? (
|
||||
<ProgressBar variant="determinate" value={v} color="primary" />
|
||||
<ProgressBar variant="determinate" value={Math.max(v, 0)} color="primary" />
|
||||
) : (
|
||||
<Paper sx={{ p: 1, mb: 1 }}>
|
||||
<ProgressBar variant="determinate" value={v} color="primary" />
|
||||
<ProgressBar variant="determinate" value={Math.max(v, 0)} color="primary" />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
interface IProps {
|
||||
onKeyDown: (this: Document, event: KeyboardEvent) => void;
|
||||
onKeyDown: (event: KeyboardEvent) => void;
|
||||
onFailure: (options?: { automated: boolean }) => void;
|
||||
}
|
||||
|
||||
export function KeyHandler(props: IProps): React.ReactElement {
|
||||
useEffect(() => {
|
||||
function press(this: Document, event: KeyboardEvent): void {
|
||||
if (!event.isTrusted) return;
|
||||
const f = props.onKeyDown.bind(this);
|
||||
f(event);
|
||||
function press(event: KeyboardEvent): void {
|
||||
if (!event.isTrusted || !(event instanceof KeyboardEvent)) {
|
||||
props.onFailure({ automated: true });
|
||||
return;
|
||||
}
|
||||
props.onKeyDown(event);
|
||||
}
|
||||
document.addEventListener("keydown", press);
|
||||
return () => document.removeEventListener("keydown", press);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Box, Paper, Typography } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { AugmentationName } from "@enums";
|
||||
import { Player } from "@player";
|
||||
import { KEY } from "../../utils/helpers/keyCodes";
|
||||
@@ -27,34 +27,53 @@ const difficulties: {
|
||||
|
||||
export function SlashGame({ difficulty, onSuccess, onFailure }: IMinigameProps): React.ReactElement {
|
||||
const [phase, setPhase] = useState(0);
|
||||
const [hasAugment, setHasAugment] = useState(false);
|
||||
const [guardingTime, setGuardingTime] = useState(0);
|
||||
const timeOutId = useRef<number | ReturnType<typeof setTimeout>>(-1);
|
||||
const hasWKSharmonizer = Player.hasAugmentation(AugmentationName.WKSharmonizer, true);
|
||||
const hasMightOfAres = Player.hasAugmentation(AugmentationName.MightOfAres, true);
|
||||
|
||||
useEffect(() => {
|
||||
// Determine timeframes for game phase changes
|
||||
const data = useMemo(() => {
|
||||
// Determine time window of phases
|
||||
const newDifficulty: Difficulty = { window: 0 };
|
||||
interpolate(difficulties, difficulty, newDifficulty);
|
||||
const distractedTime =
|
||||
newDifficulty.window * (Player.hasAugmentation(AugmentationName.WKSharmonizer, true) ? 1.3 : 1);
|
||||
const distractedTime = newDifficulty.window * (hasWKSharmonizer ? 1.3 : 1);
|
||||
const alertedTime = 250;
|
||||
const guardingTime = Math.random() * 3250 + 1500 - (distractedTime + alertedTime);
|
||||
|
||||
// Set initial game state
|
||||
setPhase(0);
|
||||
setGuardingTime(guardingTime);
|
||||
setHasAugment(Player.hasAugmentation(AugmentationName.MightOfAres, true));
|
||||
return {
|
||||
hasAugment: hasMightOfAres,
|
||||
guardingTime,
|
||||
distractedTime,
|
||||
alertedTime,
|
||||
};
|
||||
}, [difficulty, hasWKSharmonizer, hasMightOfAres]);
|
||||
|
||||
// Setup timer for game phases
|
||||
let id = setTimeout(() => {
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (timeOutId.current !== -1) {
|
||||
clearTimeout(timeOutId.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const startPhase1 = useCallback(
|
||||
(alertedTime: number, distractedTime: number) => {
|
||||
setPhase(1);
|
||||
id = setTimeout(() => {
|
||||
timeOutId.current = setTimeout(() => {
|
||||
setPhase(2);
|
||||
id = setTimeout(() => onFailure(), alertedTime);
|
||||
timeOutId.current = setTimeout(() => onFailure(), alertedTime);
|
||||
}, distractedTime);
|
||||
}, guardingTime);
|
||||
},
|
||||
[onFailure],
|
||||
);
|
||||
|
||||
return () => clearTimeout(id);
|
||||
}, [difficulty, onSuccess, onFailure]);
|
||||
useEffect(() => {
|
||||
// Start the timer if the player does not have MightOfAres augmentation.
|
||||
if (phase === 0 && !data.hasAugment) {
|
||||
timeOutId.current = setTimeout(() => {
|
||||
startPhase1(data.alertedTime, data.distractedTime);
|
||||
}, data.guardingTime);
|
||||
}
|
||||
}, [phase, data, startPhase1]);
|
||||
|
||||
function press(this: Document, event: KeyboardEvent): void {
|
||||
event.preventDefault();
|
||||
@@ -76,10 +95,18 @@ export function SlashGame({ difficulty, onSuccess, onFailure }: IMinigameProps):
|
||||
Do not alert him!
|
||||
</Typography>
|
||||
<br />
|
||||
{hasAugment && (
|
||||
{phase === 0 && data.hasAugment && (
|
||||
<Box sx={{ my: 1 }}>
|
||||
<Typography variant="h5">The sentinel will drop his guard and be distracted in ...</Typography>
|
||||
<GameTimer millis={guardingTime} onExpire={() => null} ignoreAugment_WKSharmonizer noPaper tick={20} />
|
||||
<GameTimer
|
||||
millis={data.guardingTime}
|
||||
onExpire={() => {
|
||||
startPhase1(data.alertedTime, data.distractedTime);
|
||||
}}
|
||||
ignoreAugment_WKSharmonizer
|
||||
noPaper
|
||||
tick={20}
|
||||
/>
|
||||
<br />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -80,7 +80,7 @@ export enum LocationType {
|
||||
Gym = "Gym",
|
||||
Hospital = "Hospital",
|
||||
Slums = "Slums",
|
||||
Special = "Special", // This location has special options/activities (e.g. Bladeburner, Re-sleeving)
|
||||
Special = "Special", // This location has special options/activities (e.g., Bladeburner, Grafting, etc.)
|
||||
StockMarket = "Stock Market",
|
||||
TechVendor = "Tech Vendor",
|
||||
TravelAgency = "Travel Agency",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Examples:
|
||||
* - Bladeburner @ NSA
|
||||
* - Re-sleeving @ VitaLife
|
||||
* - Grafting @ VitaLife
|
||||
* - Create Corporation @ City Hall
|
||||
*
|
||||
* This subcomponent creates all of the buttons for interacting with those special
|
||||
@@ -69,7 +69,7 @@ export function SpecialLocation(props: SpecialLocationProps): React.ReactElement
|
||||
}
|
||||
}
|
||||
|
||||
/** Click handler for Resleeving button at New Tokyo VitaLife */
|
||||
/** Click handler for Secret lab button at New Tokyo VitaLife */
|
||||
function handleGrafting(): void {
|
||||
Router.toPage(Page.Grafting);
|
||||
}
|
||||
@@ -300,36 +300,11 @@ export function SpecialLocation(props: SpecialLocationProps): React.ReactElement
|
||||
}
|
||||
|
||||
function renderGlitch(): React.ReactElement {
|
||||
const onClick = () => {
|
||||
dialogBoxCreate(
|
||||
"Hexabytes of information are streamed to your mind. Completely drained one thing remained clear as crystal. You now understand how to connect directly to the machine running the bitnodes. Myrian.",
|
||||
);
|
||||
Router.toPage(Page.MyrianOS);
|
||||
Player.myrianConnection = true;
|
||||
};
|
||||
if (!Player.canAccessMyrian())
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
<CorruptableText
|
||||
content={"An eerie aura surrounds this area. You feel you should leave."}
|
||||
spoiler={false}
|
||||
/>
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
You find yourself standing in a small, unremarkable alley. Despite the lack of air you do not feel the need to
|
||||
breathe. There is no light, yet you can see every details of every objects in the alley. A rat walking in the
|
||||
alley completely stop in it's track as if frozen in time. You know what this means. This location has fallen
|
||||
through the crack of the Enders.
|
||||
<br />
|
||||
In the middle of the alley is a personal link port. You can connect your personal link to the anomaly.
|
||||
<CorruptableText content={"An eerie aura surrounds this area. You feel you should leave."} spoiler={false} />
|
||||
</Typography>
|
||||
<Button onClick={onClick}>Connect your personal link to the anomaly</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
export enum DeviceType {
|
||||
Bus = "bus",
|
||||
ISocket = "isocket",
|
||||
OSocket = "osocket",
|
||||
Reducer = "reducer",
|
||||
Cache = "cache",
|
||||
Lock = "lock",
|
||||
Battery = "battery",
|
||||
}
|
||||
|
||||
export enum Component {
|
||||
// tier 0
|
||||
R0 = "r0",
|
||||
G0 = "g0",
|
||||
B0 = "b0",
|
||||
|
||||
// tier 1
|
||||
R1 = "r1",
|
||||
G1 = "g1",
|
||||
B1 = "b1",
|
||||
|
||||
Y1 = "y1",
|
||||
C1 = "c1",
|
||||
M1 = "m1",
|
||||
|
||||
// tier 2
|
||||
R2 = "r2",
|
||||
G2 = "g2",
|
||||
B2 = "b2",
|
||||
|
||||
Y2 = "y2",
|
||||
C2 = "c2",
|
||||
M2 = "m2",
|
||||
|
||||
W2 = "w2",
|
||||
|
||||
// tier 3
|
||||
R3 = "r3",
|
||||
G3 = "g3",
|
||||
B3 = "b3",
|
||||
|
||||
Y3 = "y3",
|
||||
C3 = "c3",
|
||||
M3 = "m3",
|
||||
|
||||
W3 = "w3",
|
||||
|
||||
// tier 4
|
||||
R4 = "r4",
|
||||
G4 = "g4",
|
||||
B4 = "b4",
|
||||
|
||||
Y4 = "y4",
|
||||
C4 = "c4",
|
||||
M4 = "m4",
|
||||
|
||||
W4 = "w4",
|
||||
|
||||
// tier 5
|
||||
R5 = "r5",
|
||||
G5 = "g5",
|
||||
B5 = "b5",
|
||||
|
||||
Y5 = "y5",
|
||||
C5 = "c5",
|
||||
M5 = "m5",
|
||||
|
||||
W5 = "w5",
|
||||
|
||||
// tier 6
|
||||
Y6 = "y6",
|
||||
C6 = "c6",
|
||||
M6 = "m6",
|
||||
|
||||
W6 = "w6",
|
||||
|
||||
// tier 7
|
||||
W7 = "w7",
|
||||
}
|
||||
|
||||
export enum Glitch {
|
||||
// Locks spawn at random
|
||||
Segmentation = "segmentation",
|
||||
// ISockets and OSockets move around on their own
|
||||
Roaming = "roaming",
|
||||
// OSocket ask for more complicated components
|
||||
Encryption = "encryption",
|
||||
// Energy starts being consumed (level 0 is no consumption)
|
||||
Magnetism = "magnetism",
|
||||
// Hidden tiles on the board, when stepped on the bus loses upgrades
|
||||
Rust = "rust",
|
||||
// Move slows down
|
||||
Friction = "friction",
|
||||
// Transfer components and charging slows down
|
||||
Isolation = "isolation",
|
||||
// Install/Uninstall slows down
|
||||
Virtualization = "virtualization",
|
||||
// Reduce slows down
|
||||
Jamming = "jamming",
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
import { Device, DeviceID } from "@nsdefs";
|
||||
import { DeviceType, Component, Glitch } from "@enums";
|
||||
import { glitchMult } from "./formulas/glitches";
|
||||
import { isDeviceISocket, pickOne } from "./utils";
|
||||
import { componentTiers } from "./formulas/components";
|
||||
import { NewBus, NewISocket, NewOSocket } from "./NewDevices";
|
||||
import { startRoaming } from "./glitches/roaming";
|
||||
import { startRust } from "./glitches/rust";
|
||||
import { startSegmentation } from "./glitches/segmentation";
|
||||
import { startBattery } from "./glitches/battery";
|
||||
|
||||
export interface Myrian {
|
||||
vulns: number;
|
||||
totalVulns: number;
|
||||
devices: Device[];
|
||||
glitches: Record<Glitch, number>;
|
||||
rust: Record<string, boolean>;
|
||||
}
|
||||
|
||||
export const myrianSize = 12;
|
||||
|
||||
const defaultGlitches = Object.values(Glitch).reduce((acc, g) => ({ ...acc, [g]: 0 }), {}) as Record<Glitch, number>;
|
||||
|
||||
export const myrian: Myrian = {
|
||||
vulns: 0,
|
||||
totalVulns: 0,
|
||||
devices: [],
|
||||
glitches: { ...defaultGlitches },
|
||||
rust: {},
|
||||
};
|
||||
|
||||
export const loadMyrian = (save: string) => {
|
||||
resetMyrian();
|
||||
startRoaming();
|
||||
startRust();
|
||||
startSegmentation();
|
||||
startBattery();
|
||||
if (!save) return;
|
||||
const savedMyrian = JSON.parse(save);
|
||||
Object.assign(myrian, savedMyrian);
|
||||
myrian.devices.forEach((d) => (d.isBusy = false));
|
||||
myrian.devices.filter(isDeviceISocket).forEach((d) => (d.content = new Array(d.maxContent).fill(d.emitting)));
|
||||
};
|
||||
|
||||
export const inMyrianBounds = (x: number, y: number) => x >= 0 && x < myrianSize && y >= 0 && y < myrianSize;
|
||||
|
||||
export const findDevice = (id: DeviceID, type?: DeviceType): Device | undefined =>
|
||||
myrian.devices.find(
|
||||
(e) => (typeof id === "string" ? e.name === id : e.x === id[0] && e.y === id[1]) && (!type || type === e.type),
|
||||
);
|
||||
|
||||
export const removeDevice = (id: DeviceID, type?: DeviceType) => {
|
||||
myrian.devices = myrian.devices.filter(
|
||||
(e) => !((typeof id === "string" ? e.name === id : e.x === id[0] && e.y === id[1]) && (!type || type === e.type)),
|
||||
);
|
||||
};
|
||||
|
||||
export const getTotalGlitchMult = () =>
|
||||
Object.entries(myrian.glitches).reduce((acc, [glitch, lvl]) => {
|
||||
return acc * glitchMult(glitch as Glitch, lvl);
|
||||
}, 1);
|
||||
|
||||
export const getNextOSocketRequest = (tier: number) => {
|
||||
const potential = componentTiers.slice(0, tier + 1).flat();
|
||||
return new Array(Math.floor(Math.pow(Math.random() * tier, 0.75) + 1)).fill(null).map(() => pickOne(potential));
|
||||
};
|
||||
|
||||
export const countDevices = (type: DeviceType) =>
|
||||
myrian.devices.reduce((acc, d) => (d.type === type ? acc + 1 : acc), 0);
|
||||
|
||||
export const resetMyrian = () => {
|
||||
myrian.vulns = 0;
|
||||
myrian.totalVulns = 0;
|
||||
myrian.devices = [];
|
||||
myrian.glitches = { ...defaultGlitches };
|
||||
myrian.rust = {};
|
||||
|
||||
NewBus("alice", Math.floor(myrianSize / 2), Math.floor(myrianSize / 2));
|
||||
|
||||
NewISocket("isocket0", Math.floor(myrianSize / 4), 0, Component.R0);
|
||||
NewISocket("isocket1", Math.floor(myrianSize / 2), 0, Component.G0);
|
||||
NewISocket("isocket2", Math.floor((myrianSize * 3) / 4), 0, Component.B0);
|
||||
|
||||
NewOSocket("osocket0", Math.floor(myrianSize / 4), Math.floor(myrianSize - 1));
|
||||
NewOSocket("osocket1", Math.floor(myrianSize / 2), Math.floor(myrianSize - 1));
|
||||
NewOSocket("osocket2", Math.floor((myrianSize * 3) / 4), Math.floor(myrianSize - 1));
|
||||
};
|
||||
@@ -1,110 +0,0 @@
|
||||
import { Battery, Bus, Cache, ISocket, Lock, OSocket, Reducer } from "@nsdefs";
|
||||
import { Component, DeviceType } from "@enums";
|
||||
import { myrian } from "./Myrian";
|
||||
import { getNextOSocketRequest } from "./Myrian";
|
||||
|
||||
export const NewBus = (name: string, x: number, y: number) => {
|
||||
const bus: Bus = {
|
||||
name,
|
||||
type: DeviceType.Bus,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
content: [],
|
||||
maxContent: 1,
|
||||
|
||||
moveLvl: 0,
|
||||
transferLvl: 0,
|
||||
reduceLvl: 0,
|
||||
installLvl: 0,
|
||||
energy: 16,
|
||||
maxEnergy: 16,
|
||||
};
|
||||
myrian.devices.push(bus);
|
||||
};
|
||||
|
||||
export const NewCache = (name: string, x: number, y: number) => {
|
||||
const cache: Cache = {
|
||||
name,
|
||||
type: DeviceType.Cache,
|
||||
isBusy: false,
|
||||
content: [],
|
||||
maxContent: 1,
|
||||
x,
|
||||
y,
|
||||
};
|
||||
myrian.devices.push(cache);
|
||||
return cache;
|
||||
};
|
||||
|
||||
export const NewReducer = (name: string, x: number, y: number) => {
|
||||
const reducer: Reducer = {
|
||||
name,
|
||||
type: DeviceType.Reducer,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
content: [],
|
||||
maxContent: 2,
|
||||
tier: 1,
|
||||
};
|
||||
myrian.devices.push(reducer);
|
||||
return reducer;
|
||||
};
|
||||
|
||||
export const NewISocket = (name: string, x: number, y: number, emitting: Component) => {
|
||||
const isocket: ISocket = {
|
||||
name,
|
||||
type: DeviceType.ISocket,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
emitting: emitting,
|
||||
emissionLvl: 0,
|
||||
cooldownUntil: 0,
|
||||
content: [emitting],
|
||||
maxContent: 1,
|
||||
};
|
||||
myrian.devices.push(isocket);
|
||||
};
|
||||
|
||||
export const NewOSocket = (name: string, x: number, y: number) => {
|
||||
const osocket: OSocket = {
|
||||
name,
|
||||
type: DeviceType.OSocket,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
currentRequest: getNextOSocketRequest(0),
|
||||
content: [],
|
||||
maxContent: 1,
|
||||
};
|
||||
myrian.devices.push(osocket);
|
||||
return osocket;
|
||||
};
|
||||
|
||||
export const NewLock = (name: string, x: number, y: number) => {
|
||||
const lock: Lock = {
|
||||
name,
|
||||
type: DeviceType.Lock,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
};
|
||||
myrian.devices.push(lock);
|
||||
return lock;
|
||||
};
|
||||
|
||||
export const NewBattery = (name: string, x: number, y: number) => {
|
||||
const battery: Battery = {
|
||||
name,
|
||||
type: DeviceType.Battery,
|
||||
isBusy: false,
|
||||
x,
|
||||
y,
|
||||
tier: 0,
|
||||
energy: 64,
|
||||
maxEnergy: 64,
|
||||
};
|
||||
myrian.devices.push(battery);
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Component } from "@enums";
|
||||
|
||||
export const componentTiers = [
|
||||
[Component.R0, Component.G0, Component.B0],
|
||||
[Component.R1, Component.G1, Component.B1, Component.Y1, Component.C1, Component.M1],
|
||||
[Component.R2, Component.G2, Component.B2, Component.Y2, Component.C2, Component.M2, Component.W2],
|
||||
[Component.R3, Component.G3, Component.B3, Component.Y3, Component.C3, Component.M3, Component.W3],
|
||||
[Component.R4, Component.G4, Component.B4, Component.Y4, Component.C4, Component.M4, Component.W4],
|
||||
[Component.R5, Component.G5, Component.B5, Component.Y5, Component.C5, Component.M5, Component.W5],
|
||||
[Component.Y6, Component.C6, Component.M6, Component.W6],
|
||||
[Component.W7],
|
||||
];
|
||||
@@ -1,67 +0,0 @@
|
||||
import { DeviceType } from "@enums";
|
||||
|
||||
// parameters for a exponential formula, a^(b*X+c)+d
|
||||
type ExponentialFormulaParams = [number, number, number, number];
|
||||
|
||||
// Parameters for a cost that shouldn't be available. Such as upgrading the max energy of a isocket.
|
||||
const NA: ExponentialFormulaParams = [Infinity, Infinity, Infinity, Infinity];
|
||||
|
||||
type DeviceScale = Record<DeviceType, ExponentialFormulaParams>;
|
||||
|
||||
// Default scale for each device type, helps simplify code.
|
||||
const defaultScale = Object.keys(DeviceType).reduce((acc, type) => ({ ...acc, [type]: NA }), {}) as DeviceScale;
|
||||
|
||||
// Exponential formula, a^(b*X+c)+d
|
||||
const exp = (p: ExponentialFormulaParams, x: number): number => Math.pow(p[0], p[1] * x + p[2]) + p[3];
|
||||
|
||||
// Wrap exp with a specific scale for each device type.
|
||||
const makeExpFunction = (p: Partial<DeviceScale>) => {
|
||||
const scale = { ...defaultScale, ...p };
|
||||
return (type: DeviceType, x: number) => exp(scale[type], x);
|
||||
};
|
||||
|
||||
export const upgradeMaxContentCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [8, 0.5, 2, 0],
|
||||
[DeviceType.ISocket]: [4, 1, 5, 0],
|
||||
[DeviceType.Reducer]: [256, 1, -3, 512],
|
||||
[DeviceType.Cache]: [1.2, 10, 0, 63],
|
||||
});
|
||||
|
||||
export const upgradeTierCost = makeExpFunction({
|
||||
[DeviceType.Reducer]: [1.5, 1, 2, 0],
|
||||
[DeviceType.Battery]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeEmissionCost = makeExpFunction({
|
||||
[DeviceType.ISocket]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeMoveLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeTransferLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeReduceLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeInstallLvlCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [2, 1, 3, 0],
|
||||
});
|
||||
|
||||
export const upgradeMaxEnergyCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [1.1, 1, -8, 16],
|
||||
[DeviceType.Battery]: [1.1, 1, -16, 8],
|
||||
});
|
||||
|
||||
export const installDeviceCost = makeExpFunction({
|
||||
[DeviceType.Bus]: [4, 0.5, 2, 0],
|
||||
[DeviceType.ISocket]: [2, 1, 4, 0],
|
||||
[DeviceType.OSocket]: [4, 1, 3, 0],
|
||||
[DeviceType.Reducer]: [5, 0.5, 1, 0],
|
||||
[DeviceType.Cache]: [1.2, 5, 0, 18],
|
||||
[DeviceType.Battery]: [1.2, 10, 0, 63],
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
import { Glitch } from "@enums";
|
||||
|
||||
export const glitchMaxLvl: Record<Glitch, number> = {
|
||||
[Glitch.Segmentation]: 10,
|
||||
[Glitch.Roaming]: 10,
|
||||
[Glitch.Encryption]: 7,
|
||||
[Glitch.Magnetism]: 10,
|
||||
[Glitch.Rust]: 10,
|
||||
[Glitch.Friction]: 3,
|
||||
[Glitch.Isolation]: 3,
|
||||
[Glitch.Virtualization]: 3,
|
||||
[Glitch.Jamming]: 3,
|
||||
};
|
||||
|
||||
export const giltchMultCoefficients: Record<Glitch, number> = {
|
||||
[Glitch.Segmentation]: 1,
|
||||
[Glitch.Roaming]: 1,
|
||||
[Glitch.Encryption]: 0.1,
|
||||
[Glitch.Magnetism]: 0.2,
|
||||
[Glitch.Rust]: 1,
|
||||
[Glitch.Friction]: 0.2,
|
||||
[Glitch.Isolation]: 0.2,
|
||||
[Glitch.Virtualization]: 0.2,
|
||||
[Glitch.Jamming]: 0.2,
|
||||
};
|
||||
|
||||
// vulns mult by glitch lvl
|
||||
export const glitchMult = (glitch: Glitch, lvl: number) => 1 + lvl * giltchMultCoefficients[glitch];
|
||||
|
||||
// move hinderance
|
||||
export const frictionMult = (lvl: number) => Math.pow(2.5, lvl);
|
||||
|
||||
// transfer slow down
|
||||
export const isolationMult = (lvl: number) => Math.pow(8, lvl);
|
||||
|
||||
// install/uninstall slow down
|
||||
export const virtualizationMult = (lvl: number) => Math.pow(5, lvl);
|
||||
|
||||
// reduce slow down
|
||||
export const jammingMult = (lvl: number) => Math.pow(2.5, lvl);
|
||||
|
||||
// energy loss
|
||||
export const magnetismLoss = (lvl: number) => lvl;
|
||||
|
||||
// How often isocket/osocke move
|
||||
export const roamingTime = (lvl: number) => 30000 * Math.pow(0.7, lvl);
|
||||
@@ -1,83 +0,0 @@
|
||||
import { Recipe } from "@nsdefs";
|
||||
import { Component } from "@enums";
|
||||
|
||||
const make = (input: Component[], output: Component): Recipe => ({ input, output });
|
||||
|
||||
export const Tier1Recipes: Recipe[] = [
|
||||
make([Component.R0, Component.R0], Component.R1),
|
||||
make([Component.G0, Component.G0], Component.G1),
|
||||
make([Component.B0, Component.B0], Component.B1),
|
||||
|
||||
make([Component.R0, Component.G0], Component.Y1),
|
||||
make([Component.G0, Component.B0], Component.C1),
|
||||
make([Component.B0, Component.R0], Component.M1),
|
||||
];
|
||||
|
||||
export const Tier2Recipes: Recipe[] = [
|
||||
make([Component.R1, Component.R1], Component.R2),
|
||||
make([Component.G1, Component.G1], Component.G2),
|
||||
make([Component.B1, Component.B1], Component.B2),
|
||||
|
||||
make([Component.R1, Component.G1], Component.Y2),
|
||||
make([Component.G1, Component.B1], Component.C2),
|
||||
make([Component.B1, Component.R1], Component.M2),
|
||||
|
||||
make([Component.Y1, Component.C1, Component.M1], Component.W2),
|
||||
];
|
||||
|
||||
export const Tier3Recipes: Recipe[] = [
|
||||
make([Component.R2, Component.R2], Component.R3),
|
||||
make([Component.G2, Component.G2], Component.G3),
|
||||
make([Component.B2, Component.B2], Component.B3),
|
||||
|
||||
make([Component.R2, Component.G2], Component.Y3),
|
||||
make([Component.G2, Component.B2], Component.C3),
|
||||
make([Component.B2, Component.R2], Component.M3),
|
||||
|
||||
make([Component.Y2, Component.C2, Component.M2], Component.W3),
|
||||
];
|
||||
|
||||
export const Tier4Recipes: Recipe[] = [
|
||||
make([Component.R3, Component.R3], Component.R4),
|
||||
make([Component.G3, Component.G3], Component.G4),
|
||||
make([Component.B3, Component.B3], Component.B4),
|
||||
|
||||
make([Component.R3, Component.G3], Component.Y4),
|
||||
make([Component.G3, Component.B3], Component.C4),
|
||||
make([Component.B3, Component.R3], Component.M4),
|
||||
|
||||
make([Component.Y3, Component.C3, Component.M3], Component.W4),
|
||||
];
|
||||
|
||||
export const Tier5Recipes: Recipe[] = [
|
||||
make([Component.R4, Component.R4], Component.R5),
|
||||
make([Component.G4, Component.G4], Component.G5),
|
||||
make([Component.B4, Component.B4], Component.B5),
|
||||
|
||||
make([Component.R4, Component.G4], Component.Y5),
|
||||
make([Component.G4, Component.B4], Component.C5),
|
||||
make([Component.B4, Component.R4], Component.M5),
|
||||
|
||||
make([Component.Y4, Component.C4, Component.M4], Component.W5),
|
||||
];
|
||||
|
||||
export const Tier6Recipes: Recipe[] = [
|
||||
make([Component.R5, Component.G5], Component.Y6),
|
||||
make([Component.G5, Component.B5], Component.C6),
|
||||
make([Component.B5, Component.R5], Component.M6),
|
||||
|
||||
make([Component.Y5, Component.C5, Component.M5], Component.W6),
|
||||
];
|
||||
|
||||
export const Tier7Recipes: Recipe[] = [make([Component.Y6, Component.C6, Component.M6], Component.W7)];
|
||||
|
||||
export const recipes: Recipe[][] = [
|
||||
[],
|
||||
Tier1Recipes,
|
||||
Tier2Recipes,
|
||||
Tier3Recipes,
|
||||
Tier4Recipes,
|
||||
Tier5Recipes,
|
||||
Tier6Recipes,
|
||||
Tier7Recipes,
|
||||
];
|
||||
@@ -1,14 +0,0 @@
|
||||
// speed to move between 2 tiles
|
||||
export const moveSpeed = (level: number) => 1000 / (level + 10);
|
||||
|
||||
// speed to reduce components
|
||||
export const reduceSpeed = (level: number) => 50000 / (level + 10);
|
||||
|
||||
// speed to transfer components between devices
|
||||
export const transferSpeed = (level: number) => 4000 / (level + 10);
|
||||
|
||||
// speed to install / uninstall devices and tweak ISockets
|
||||
export const installSpeed = (level: number) => 100000 / (level + 10);
|
||||
|
||||
// time until ISocket refreshes
|
||||
export const emissionSpeed = (level: number) => 100000 / (level + 10);
|
||||
@@ -1,12 +0,0 @@
|
||||
import { myrian } from "../Myrian";
|
||||
import { isDeviceBattery } from "../utils";
|
||||
|
||||
const applyBattery = () => {
|
||||
myrian.devices.forEach((device) => {
|
||||
if (!isDeviceBattery(device)) return;
|
||||
const up = Math.pow(2, device.tier + 1);
|
||||
device.energy = Math.min(device.energy + up, device.maxEnergy);
|
||||
});
|
||||
};
|
||||
|
||||
export const startBattery = () => setInterval(applyBattery, 1000);
|
||||
@@ -1,74 +0,0 @@
|
||||
import { DeviceType, Glitch } from "@enums";
|
||||
import { myrian, myrianSize } from "../Myrian";
|
||||
import { findDevice, inMyrianBounds } from "../Myrian";
|
||||
import { roamingTime } from "../formulas/glitches";
|
||||
|
||||
const clamp = (min: number, v: number, max: number) => Math.min(Math.max(v, min), max);
|
||||
|
||||
let globalOffset = 0;
|
||||
|
||||
const dirDiff = (v: number): number => {
|
||||
globalOffset++;
|
||||
const r = Math.random();
|
||||
const d = v - (myrianSize - 1) / 2;
|
||||
const h = d > 0 ? -1 : 1;
|
||||
|
||||
const dv = Math.floor(r * 3 + h * Math.random() * Math.sin(globalOffset * 0.05) * Math.abs(d)) - 1;
|
||||
return clamp(-1, dv, 1);
|
||||
};
|
||||
|
||||
const isEmpty = (x: number, y: number) => {
|
||||
if (!inMyrianBounds(x, y)) return false;
|
||||
return !findDevice([x, y]);
|
||||
};
|
||||
|
||||
const dirs = [
|
||||
[0, 1],
|
||||
[0, -1],
|
||||
[1, 0],
|
||||
[-1, 0],
|
||||
];
|
||||
|
||||
const applyRoaming = () => {
|
||||
const roaming = myrian.glitches[Glitch.Roaming];
|
||||
setTimeout(applyRoaming, roamingTime(roaming));
|
||||
if (roaming === 0) return;
|
||||
myrian.devices.forEach((device) => {
|
||||
if (device.type !== DeviceType.OSocket && device.type !== DeviceType.ISocket) return;
|
||||
if (device.isBusy) return;
|
||||
let canMove = false;
|
||||
for (const dir of dirs) {
|
||||
const [dx, dy] = dir;
|
||||
if (isEmpty(device.x + dx, device.y + dy)) {
|
||||
canMove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let x = -1;
|
||||
let y = -1;
|
||||
if (canMove) {
|
||||
let dx = dirDiff(device.x);
|
||||
let dy = dirDiff(device.y);
|
||||
|
||||
if (dx !== 0 && dy !== 0) {
|
||||
if (Math.random() > 0.5) {
|
||||
dx = 0;
|
||||
} else {
|
||||
dy = 0;
|
||||
}
|
||||
}
|
||||
x = device.x + dx;
|
||||
y = device.y + dy;
|
||||
} else {
|
||||
x = Math.floor(Math.random() * myrianSize);
|
||||
y = Math.floor(Math.random() * myrianSize);
|
||||
}
|
||||
|
||||
if (findDevice([x, y])) return;
|
||||
if (!inMyrianBounds(x, y)) return;
|
||||
device.x = x;
|
||||
device.y = y;
|
||||
});
|
||||
};
|
||||
|
||||
export const startRoaming = () => setTimeout(applyRoaming, 0);
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Bus } from "@nsdefs";
|
||||
import { Glitch } from "@enums";
|
||||
import { myrian, myrianSize } from "../Myrian";
|
||||
import { pickOne } from "../utils";
|
||||
|
||||
const applyRust = () => {
|
||||
myrian.rust = {};
|
||||
const rust = myrian.glitches[Glitch.Rust];
|
||||
for (let i = 0; i < rust * 3; i++) {
|
||||
const x = Math.floor(Math.random() * myrianSize);
|
||||
const y = Math.floor(Math.random() * myrianSize);
|
||||
myrian.rust[`${x}:${y}`] = true;
|
||||
}
|
||||
};
|
||||
|
||||
// DO NOT use `Object.keys` on a Rustable because it will return way more than just the rustable stats.
|
||||
const rustStats: (keyof Rustable)[] = ["moveLvl", "transferLvl", "reduceLvl", "installLvl", "maxEnergy"];
|
||||
type Rustable = Pick<Bus, "moveLvl" | "transferLvl" | "reduceLvl" | "installLvl" | "maxEnergy">;
|
||||
|
||||
export const rustBus = (bus: Bus, rust: number) => {
|
||||
const rustable = bus as Rustable;
|
||||
const nonZero = rustStats.filter((stat) => rustable[stat] > 0);
|
||||
const chosen = pickOne(nonZero);
|
||||
rustable[chosen] = Math.max(0, rustable[chosen] - rust * 0.1);
|
||||
|
||||
// cap energy when maxEnergy is reduced
|
||||
bus.energy = Math.min(bus.energy, bus.maxEnergy);
|
||||
};
|
||||
|
||||
export const startRust = () => setInterval(applyRust, 30000);
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Glitch } from "@enums";
|
||||
import { myrian, myrianSize } from "../Myrian";
|
||||
import { findDevice } from "../Myrian";
|
||||
import { NewLock } from "../NewDevices";
|
||||
|
||||
const applySegmentation = () => {
|
||||
const segmentation = myrian.glitches[Glitch.Segmentation];
|
||||
for (let i = 0; i < segmentation; i++) {
|
||||
const x = Math.floor(Math.random() * myrianSize);
|
||||
const y = Math.floor(Math.random() * myrianSize);
|
||||
if (findDevice([x, y])) continue;
|
||||
NewLock(`lock-${x}-${y}`, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
export const startSegmentation = () => setInterval(applySegmentation, 30000);
|
||||
@@ -1 +0,0 @@
|
||||
New device that makes special requests like "has red" | "at least tier 3" | "does not contain blue" that increases the reward.
|
||||
@@ -1,22 +0,0 @@
|
||||
import React from "react";
|
||||
import { Battery } from "@nsdefs";
|
||||
import BatteryChargingFullIcon from "@mui/icons-material/BatteryChargingFull";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { styled } from "@mui/styles";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipTier } from "./TooltipTier";
|
||||
import { TooltipEnergy } from "./TooltipEnergy";
|
||||
|
||||
const Template = styled(BatteryChargingFullIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface IBatteryIconProps {
|
||||
battery: Battery;
|
||||
}
|
||||
|
||||
export const BatteryIcon = ({ battery }: IBatteryIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={battery} icon={Icon}>
|
||||
<TooltipTier device={battery} />
|
||||
<TooltipEnergy device={battery} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
@@ -1,27 +0,0 @@
|
||||
import React from "react";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import DirectionsBusIcon from "@mui/icons-material/DirectionsBus";
|
||||
import { styled } from "@mui/styles";
|
||||
import { Bus } from "@nsdefs";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipEnergy } from "./TooltipEnergy";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
const Template = styled(DirectionsBusIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface IBusIconProps {
|
||||
bus: Bus;
|
||||
}
|
||||
|
||||
export const BusIcon = ({ bus }: IBusIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={bus} icon={Icon}>
|
||||
<Typography>moveLvl: {bus.moveLvl}</Typography>
|
||||
<Typography>transferLvl: {bus.transferLvl}</Typography>
|
||||
<Typography>reduceLvl: {bus.reduceLvl}</Typography>
|
||||
<Typography>installLvl: {bus.installLvl}</Typography>
|
||||
<TooltipEnergy device={bus} />
|
||||
<TooltipContent device={bus} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from "react";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank";
|
||||
import { styled } from "@mui/styles";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { Cache } from "@nsdefs";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
|
||||
const Template = styled(CheckBoxOutlineBlankIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface ICacheIconProps {
|
||||
cache: Cache;
|
||||
}
|
||||
|
||||
export const CacheIcon = ({ cache }: ICacheIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={cache} icon={Icon}>
|
||||
<TooltipContent device={cache} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from "react";
|
||||
import { Component } from "@enums";
|
||||
import { getComponentColor } from "./common";
|
||||
|
||||
interface IComponent {
|
||||
component: Component;
|
||||
}
|
||||
|
||||
export const ComponentText = ({ component }: IComponent): React.ReactElement => (
|
||||
<span style={{ color: getComponentColor(component) }}>
|
||||
{component}
|
||||
<br />
|
||||
</span>
|
||||
);
|
||||
@@ -1,35 +0,0 @@
|
||||
import React from "react";
|
||||
import { Device } from "@nsdefs";
|
||||
import { DeviceType } from "@enums";
|
||||
|
||||
import { BusIcon } from "./BusIcon";
|
||||
import { ReducerIcon } from "./Reducer";
|
||||
import { LockIcon } from "./LockIcon";
|
||||
import { CacheIcon } from "./CacheIcon";
|
||||
import { BatteryIcon } from "./BatteryIcon";
|
||||
import { OSocketIcon } from "./OSocketIcon";
|
||||
import { ISocketIcon } from "./ISocketIcon";
|
||||
import {
|
||||
isDeviceBattery,
|
||||
isDeviceBus,
|
||||
isDeviceCache,
|
||||
isDeviceISocket,
|
||||
isDeviceLock,
|
||||
isDeviceOSocket,
|
||||
isDeviceReducer,
|
||||
} from "../utils";
|
||||
|
||||
interface IDeviceIconProps {
|
||||
device: Device;
|
||||
}
|
||||
|
||||
export const DeviceIcon = ({ device }: IDeviceIconProps): React.ReactElement => {
|
||||
if (isDeviceISocket(device)) return <ISocketIcon socket={device} />;
|
||||
if (isDeviceOSocket(device)) return <OSocketIcon socket={device} />;
|
||||
if (isDeviceBus(device)) return <BusIcon bus={device} />;
|
||||
if (isDeviceReducer(device)) return <ReducerIcon reducer={device} />;
|
||||
if (isDeviceLock(device)) return <LockIcon lock={device} />;
|
||||
if (isDeviceCache(device)) return <CacheIcon cache={device} />;
|
||||
if (isDeviceBattery(device)) return <BatteryIcon battery={device} />;
|
||||
return <></>;
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { Device } from "../../ScriptEditor/NetscriptDefinitions";
|
||||
import { Tooltip, Typography } from "@mui/material";
|
||||
|
||||
interface INewTooltipProps {
|
||||
icon: React.JSX.Element;
|
||||
device: Device;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export const DeviceTooltip = ({ device, icon, children }: INewTooltipProps): React.ReactElement => (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography>
|
||||
{device.name} ({device.type})
|
||||
</Typography>
|
||||
{children}
|
||||
</>
|
||||
}
|
||||
>
|
||||
{icon}
|
||||
</Tooltip>
|
||||
);
|
||||
@@ -1,88 +0,0 @@
|
||||
import React from "react";
|
||||
import { cellSize } from "./common";
|
||||
import { styled } from "@mui/system";
|
||||
import { findDevice, myrianSize } from "../Myrian";
|
||||
import { DeviceIcon } from "./DeviceIcon";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
const BaseCell = styled("div")({
|
||||
width: cellSize,
|
||||
height: cellSize,
|
||||
backgroundColor: "#444",
|
||||
padding: 0,
|
||||
margin: "2px",
|
||||
marginTop: "4px",
|
||||
marginBottom: "4px",
|
||||
});
|
||||
|
||||
const TextCell = styled(BaseCell)({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#00000000",
|
||||
});
|
||||
|
||||
const DeviceCell = ({ x, y }: { x: number; y: number }): React.ReactElement => {
|
||||
const device = findDevice([x, y]);
|
||||
return <BaseCell>{device && <DeviceIcon device={device} />}</BaseCell>;
|
||||
};
|
||||
|
||||
const ColD = styled("div")({
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
interface IColProps {
|
||||
x: number;
|
||||
}
|
||||
|
||||
const DeviceCol = ({ x }: IColProps): React.ReactElement => {
|
||||
return (
|
||||
<ColD>
|
||||
<TextCell>
|
||||
<Typography>{x}</Typography>
|
||||
</TextCell>
|
||||
{new Array(myrianSize).fill(undefined).map((_, y) => (
|
||||
<DeviceCell key={y} x={x} y={y}></DeviceCell>
|
||||
))}
|
||||
</ColD>
|
||||
);
|
||||
};
|
||||
|
||||
const Table = styled("div")({
|
||||
border: "1px solid #fff",
|
||||
borderSpacing: "2px",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
paddingLeft: "2px",
|
||||
paddingRight: "2px",
|
||||
});
|
||||
|
||||
const YColumn = (
|
||||
<ColD>
|
||||
<TextCell>
|
||||
<Typography>
|
||||
X
|
||||
<br />
|
||||
Y
|
||||
</Typography>
|
||||
</TextCell>
|
||||
{new Array(myrianSize).fill(undefined).map((_, y) => (
|
||||
<TextCell key={y}>
|
||||
<Typography>{y}</Typography>
|
||||
</TextCell>
|
||||
))}
|
||||
</ColD>
|
||||
);
|
||||
|
||||
export const Grid = () => (
|
||||
<div style={{ display: "flex" }}>
|
||||
<Table>
|
||||
{YColumn}
|
||||
{new Array(myrianSize).fill(undefined).map((_, x) => (
|
||||
<DeviceCol key={x} x={x} />
|
||||
))}
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { ISocket } from "../../ScriptEditor/NetscriptDefinitions";
|
||||
import { defaultIconStyle, getComponentColor } from "./common";
|
||||
import OutboxIcon from "@mui/icons-material/Outbox";
|
||||
import { styled } from "@mui/styles";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { ComponentText } from "./ComponentText";
|
||||
|
||||
interface IIconProps {
|
||||
socket: ISocket;
|
||||
col: string;
|
||||
}
|
||||
|
||||
const Icon = styled(OutboxIcon)(({ socket, col }: IIconProps) => ({
|
||||
...defaultIconStyle,
|
||||
color: new Date().getTime() > socket.cooldownUntil ? col : "gray",
|
||||
}));
|
||||
|
||||
interface IIsocketIconProps {
|
||||
socket: ISocket;
|
||||
}
|
||||
|
||||
export const ISocketIcon = ({ socket }: IIsocketIconProps) => (
|
||||
<DeviceTooltip device={socket} icon={<Icon socket={socket} col={getComponentColor(socket.emitting)} />}>
|
||||
<Typography>
|
||||
dispensing:
|
||||
<ComponentText component={socket.emitting} />
|
||||
</Typography>
|
||||
<TooltipContent device={socket} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from "react";
|
||||
import BlockIcon from "@mui/icons-material/Block";
|
||||
import { styled } from "@mui/styles";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { Lock } from "@nsdefs";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
|
||||
const Template = styled(BlockIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface ILockIconProps {
|
||||
lock: Lock;
|
||||
}
|
||||
|
||||
export const LockIcon = ({ lock }: ILockIconProps): React.ReactElement => <DeviceTooltip device={lock} icon={Icon} />;
|
||||
@@ -1,32 +0,0 @@
|
||||
import React from "react";
|
||||
import { Container, IconButton, Typography } from "@mui/material";
|
||||
|
||||
import { myrian } from "../Myrian";
|
||||
import { useRerender } from "../../ui/React/hooks";
|
||||
import { Info } from "@mui/icons-material";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { MD } from "../../ui/MD/MD";
|
||||
import { tutorial } from "./tutorial";
|
||||
import { Grid } from "./Grid";
|
||||
|
||||
const tut = <MD md={tutorial} />;
|
||||
|
||||
export const MyrianRoot = (): React.ReactElement => {
|
||||
useRerender(50);
|
||||
|
||||
const onHelp = () => dialogBoxCreate(tut);
|
||||
return (
|
||||
<Container maxWidth="lg" disableGutters sx={{ mx: 0 }}>
|
||||
<Typography variant="h4">
|
||||
Myrian OS
|
||||
<IconButton onClick={onHelp}>
|
||||
<Info />
|
||||
</IconButton>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{myrian.vulns} vulns : {myrian.totalVulns} total vulns
|
||||
</Typography>
|
||||
<Grid />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
import React from "react";
|
||||
import { Typography } from "@mui/material";
|
||||
import { OSocket } from "@nsdefs";
|
||||
import { defaultIconStyle, getComponentColor } from "./common";
|
||||
import MoveToInboxIcon from "@mui/icons-material/MoveToInbox";
|
||||
import { styled } from "@mui/styles";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { ComponentText } from "./ComponentText";
|
||||
|
||||
interface IIconProps {
|
||||
col: string;
|
||||
}
|
||||
|
||||
const Icon = styled(MoveToInboxIcon)(({ col }: IIconProps) => ({
|
||||
...defaultIconStyle,
|
||||
color: col,
|
||||
}));
|
||||
|
||||
interface IOSocketIconProps {
|
||||
socket: OSocket;
|
||||
}
|
||||
|
||||
export const OSocketIcon = ({ socket }: IOSocketIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={socket} icon={<Icon col={getComponentColor(socket.currentRequest[0])} />}>
|
||||
<Typography>
|
||||
requesting:
|
||||
<br />
|
||||
{socket.currentRequest.map((c, i) => (
|
||||
<ComponentText key={i} component={c} />
|
||||
))}
|
||||
</Typography>
|
||||
<TooltipContent device={socket} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
@@ -1,22 +0,0 @@
|
||||
import React from "react";
|
||||
import { TooltipContent } from "./TooltipContent";
|
||||
import { Reducer } from "@nsdefs";
|
||||
import MergeTypeIcon from "@mui/icons-material/MergeType";
|
||||
import { styled } from "@mui/styles";
|
||||
import { defaultIconStyle } from "./common";
|
||||
import { DeviceTooltip } from "./DeviceTooltip";
|
||||
import { TooltipTier } from "./TooltipTier";
|
||||
|
||||
const Template = styled(MergeTypeIcon)(defaultIconStyle);
|
||||
const Icon = <Template />;
|
||||
|
||||
interface IReducerIconProps {
|
||||
reducer: Reducer;
|
||||
}
|
||||
|
||||
export const ReducerIcon = ({ reducer }: IReducerIconProps): React.ReactElement => (
|
||||
<DeviceTooltip device={reducer} icon={Icon}>
|
||||
<TooltipTier device={reducer} />
|
||||
<TooltipContent device={reducer} />
|
||||
</DeviceTooltip>
|
||||
);
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from "react";
|
||||
import { ContainerDevice } from "@nsdefs";
|
||||
import { Typography } from "@mui/material";
|
||||
import { ComponentText } from "./ComponentText";
|
||||
|
||||
export const TooltipContent = ({ device }: { device: ContainerDevice }): React.ReactElement => (
|
||||
<>
|
||||
{device.content.length !== 0 && (
|
||||
<Typography>
|
||||
content ({device.content.length} / {device.maxContent}):
|
||||
<br />
|
||||
{device.content.map((component, i) => (
|
||||
<ComponentText key={i} component={component} />
|
||||
))}
|
||||
</Typography>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -1,13 +0,0 @@
|
||||
import React from "react";
|
||||
import { EnergyDevice } from "@nsdefs";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
interface ITooltipEnergyProps {
|
||||
device: EnergyDevice;
|
||||
}
|
||||
|
||||
export const TooltipEnergy = ({ device }: ITooltipEnergyProps): React.ReactElement => (
|
||||
<Typography>
|
||||
{device.energy} / {device.maxEnergy} energy
|
||||
</Typography>
|
||||
);
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from "react";
|
||||
import { TieredDevice } from "@nsdefs";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
interface ITooltipTierProps {
|
||||
device: TieredDevice;
|
||||
}
|
||||
|
||||
export const TooltipTier = ({ device }: ITooltipTierProps): React.ReactElement => (
|
||||
<Typography>Tier: {device.tier}</Typography>
|
||||
);
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Component } from "@enums";
|
||||
|
||||
export const cellSize = 48;
|
||||
|
||||
export const defaultIconStyle = {
|
||||
width: cellSize + "px",
|
||||
height: cellSize + "px",
|
||||
color: "white",
|
||||
};
|
||||
|
||||
const ColorR = "red";
|
||||
const ColorG = "#7CFC00";
|
||||
const ColorB = "#1E90FF";
|
||||
const ColorY = "yellow";
|
||||
const ColorC = "cyan";
|
||||
const ColorM = "magenta";
|
||||
const ColorW = "white";
|
||||
|
||||
const componentColors: Record<string, string> = {
|
||||
r: ColorR,
|
||||
g: ColorG,
|
||||
b: ColorB,
|
||||
y: ColorY,
|
||||
c: ColorC,
|
||||
m: ColorM,
|
||||
w: ColorW,
|
||||
};
|
||||
|
||||
export const getComponentColor = (component: Component): string =>
|
||||
componentColors[component[0].toLowerCase()] ?? ColorW;
|
||||
@@ -1,109 +0,0 @@
|
||||
export const tutorial = `# Myrian
|
||||
|
||||
Myrian is the name of the OS that the BitNodes run on.
|
||||
|
||||
By gaining access to the OS directly you can start to break it apart by generating vulnerabilities (vulns).
|
||||
You do so by interracting with the various devices in the OS, represented by a grid.
|
||||
|
||||
## Devices
|
||||
|
||||
A device is a component that takes space on the Myrian, no 2 devices can be on the same tile.
|
||||
Devices can only interract when directly adjacent to each other. When one or more device is
|
||||
performing an action it will become busy and no other actions can be taken until the current one finishes.
|
||||
|
||||
Contrary to every other mechanic in the game. Async functions using myrian functions CAN run simultenaously.
|
||||
|
||||
|
||||
### Bus
|
||||
|
||||
The most important device is the Bus. Here's a few of the things it can do:
|
||||
|
||||
- move, only 1 tile at a time and that tile must be empty. No diagonal.
|
||||
- transfer content, most entity can store items called components, bus are the only device that can transfer components to and from other devices
|
||||
- use other devices, e.g. Use a Reducer device to create new components.
|
||||
|
||||
### ISocket
|
||||
|
||||
These devices produce basic components that can be used for other devices, [r0, g0, b0].
|
||||
They must be picked up by busses and will eventually produce another one after a certain cooldown has passed.
|
||||
|
||||
### OSocket
|
||||
|
||||
These devices request components and produce vulns in return, a bus simply needs to transfer a component into a OSocket content in order to fulfill the request
|
||||
|
||||
### Reducer
|
||||
|
||||
These devices can be used by a bus, when being used they will first check their content, if the content matches one of the recipe they will take some time to consume their content in order to produce a new, upgraded, more valuable component, e.g. r0 + r0 => r1
|
||||
|
||||
### Cache
|
||||
|
||||
These devices act as storage for components.
|
||||
|
||||
### Lock
|
||||
|
||||
These devices cannot be installed. They appear after various conditions are fulfilled in order to block certain tiles.
|
||||
|
||||
### Battery
|
||||
|
||||
These devices are only relevant when the Magnetism glitch is active. It recharges the energy of a bus.
|
||||
|
||||
## Installing
|
||||
|
||||
Bus can install new devices, when they do so a lock will appear over the tile that will eventually become the device. The cost of any device depends on the number of that type of device currently in the OS.
|
||||
|
||||
### Uninstalling
|
||||
|
||||
A bus can remove a device, there is no refund.
|
||||
|
||||
## Tiers
|
||||
|
||||
Upgrading a reducers tier allows it to reduce components of a higher tier and ONLY that higher tier.
|
||||
A tier 2 reducer can only tier 2 components like r1 + r1 => r2 and loses access to r0 + r0 => r1
|
||||
|
||||
Upgrading a batterys tier increases the amount of energy it produces.
|
||||
|
||||
## Glitches
|
||||
|
||||
glitches are optional difficulty modifiers that make the myrian more difficult BUT increase the amount of vulns gained.
|
||||
All glitches start at level 0 and must be activated when you chose. They also have a max level that differs from glitch to glitch.
|
||||
|
||||
### Magnetism
|
||||
|
||||
By default bus lose 0 energy when moving. But when this glitch is active they start losing energy, at 0 energy bus move much more slowly. Batteries must be installed and used to charge busses.
|
||||
|
||||
### Friction
|
||||
|
||||
When Friction is active busses move more slowly.
|
||||
|
||||
### Rust
|
||||
|
||||
When rust is active, hidden tiles are set on the Myrian. When a bus steps on one of these hidden tiles one of their upgrade lowers. Higher Rust level means lowers by a larger amount and more rust tiles are set.
|
||||
|
||||
### Isolation
|
||||
|
||||
When Isolation is active busses transfer and charge more slowly.
|
||||
|
||||
### Segmentation
|
||||
|
||||
When Segmentation is active random Locks will spawn on the Myrian. You have to remove these locks or the bord will be overrun with Locks and you won't be able to move.
|
||||
|
||||
### Virtualization
|
||||
|
||||
When Virtualization is active busses install and uninstall devices more slowly.
|
||||
|
||||
### Jamming
|
||||
|
||||
When Jamming is active busses use reducers more slowly.
|
||||
|
||||
### Roaming
|
||||
|
||||
When Roaming is active, isockets and osockets start to move around the map
|
||||
|
||||
### Encryption
|
||||
|
||||
Encryption is the only glitch that's always active. The level of Encryption determines the complexity of the requests made by osockets.
|
||||
|
||||
## Destabilization
|
||||
|
||||
As the number of total vulns increase the bitnode becomes unstable and it's multiplier become more favorable.
|
||||
`;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user