mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-26 09:33:49 +01:00
Merge branch 'dev' into feature-catch-errors-and-softreset-recovery
This commit is contained in:
commit
a454e21977
8
.github/ISSUE_TEMPLATE
vendored
Normal file
8
.github/ISSUE_TEMPLATE
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# DELETE THIS AFTER READING
|
||||||
|
|
||||||
|
# include (where relevant)
|
||||||
|
|
||||||
|
- [ ] Save file
|
||||||
|
- [ ] Minimal scripts to reproduce the issue
|
||||||
|
- [ ] Steps to reproduce
|
||||||
|
- [ ] Version of the game, e.g. Bitburner v1.3.0 (216bf616)
|
11
.github/PULL_REQUEST_TEMPLATE
vendored
Normal file
11
.github/PULL_REQUEST_TEMPLATE
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# DELETE THIS AFTER READING
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
- DO NOT CHANGE any markdown/\*.md, these files are autogenerated from NetscriptDefinitions.d.ts and will be overwritten
|
||||||
|
- DO NOT re-generate the documentation, makes it harder to review.
|
||||||
|
|
||||||
|
# Bug fix
|
||||||
|
|
||||||
|
- Include how it was tested
|
||||||
|
- Include screenshot / gif (if possible)
|
6
dist/bitburner.d.ts
vendored
6
dist/bitburner.d.ts
vendored
@ -3848,7 +3848,7 @@ export declare interface NS extends Singularity {
|
|||||||
* @param host - Host of target server.
|
* @param host - Host of target server.
|
||||||
* @returns Returns the amount of time in milliseconds it takes to execute the weaken Netscript function. Returns Infinity if called on a Hacknet Server.
|
* @returns Returns the amount of time in milliseconds it takes to execute the weaken Netscript function. Returns Infinity if called on a Hacknet Server.
|
||||||
*/
|
*/
|
||||||
getWeakenTime(host: string): number;
|
getWeakenTime(host?: string): number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the income of a script.
|
* Get the income of a script.
|
||||||
@ -3916,7 +3916,7 @@ export declare interface NS extends Singularity {
|
|||||||
* @param args - Formating arguments.
|
* @param args - Formating arguments.
|
||||||
* @returns Formated text.
|
* @returns Formated text.
|
||||||
*/
|
*/
|
||||||
sprintf(format: string, ...args: string[]): string;
|
sprintf(format: string, ...args: any[]): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a string with an array of arguments.
|
* Format a string with an array of arguments.
|
||||||
@ -3928,7 +3928,7 @@ export declare interface NS extends Singularity {
|
|||||||
* @param args - Formating arguments.
|
* @param args - Formating arguments.
|
||||||
* @returns Formated text.
|
* @returns Formated text.
|
||||||
*/
|
*/
|
||||||
vsprintf(format: string, args: string[]): string;
|
vsprintf(format: string, args: any[]): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a number
|
* Format a number
|
||||||
|
36
dist/vendor.bundle.js
vendored
36
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -39,8 +39,7 @@ There are two methods of obtaining Duplicate Sleeves:
|
|||||||
|
|
||||||
1. Destroy BitNode-10. Each completion give you one additional Duplicate Sleeve
|
1. Destroy BitNode-10. Each completion give you one additional Duplicate Sleeve
|
||||||
2. Purchase Duplicate Sleeves from :ref:`the faction The Covenant <gameplay_factions>`.
|
2. Purchase Duplicate Sleeves from :ref:`the faction The Covenant <gameplay_factions>`.
|
||||||
This is only available in BitNodes-10 and above, and is only available after defeating
|
This is only available in BitNodes-10. Sleeves purchased this way are **permanent** (they persist
|
||||||
BitNode-10 at least once. Sleeves purchased this way are **permanent** (they persist
|
|
||||||
through BitNodes). You can purchase up to 5 Duplicate Sleeves from The Covenant.
|
through BitNodes). You can purchase up to 5 Duplicate Sleeves from The Covenant.
|
||||||
|
|
||||||
Synchronization
|
Synchronization
|
||||||
|
@ -113,7 +113,7 @@ The list contains the name of (i.e. the value returned by
|
|||||||
| | | to any position from i to i+n. |
|
| | | to any position from i to i+n. |
|
||||||
| | | |
|
| | | |
|
||||||
| | | Assuming you are initially positioned at the start of the array, determine |
|
| | | Assuming you are initially positioned at the start of the array, determine |
|
||||||
| | | whether you are able to reach the last index of the array EXACTLY. |
|
| | | whether you are able to reach the last index of the array. |
|
||||||
+------------------------------------+------------------------------------------------------------------------------------------+
|
+------------------------------------+------------------------------------------------------------------------------------------+
|
||||||
| Merge Overlapping Intervals | | Given an array of intervals, merge all overlapping intervals. An interval |
|
| Merge Overlapping Intervals | | Given an array of intervals, merge all overlapping intervals. An interval |
|
||||||
| | | is an array with two numbers, where the first number is always less than |
|
| | | is an array with two numbers, where the first number is always less than |
|
||||||
|
@ -17,9 +17,16 @@ says 'Infiltrate Company'.
|
|||||||
When infiltrating a company you will be presented with short active challenges.
|
When infiltrating a company you will be presented with short active challenges.
|
||||||
None of the challenges use the mouse.
|
None of the challenges use the mouse.
|
||||||
|
|
||||||
The difficulty at the top lowers with better combat stats. It is not recommended
|
The difficulty at the top lowers with better combat stats and charisma. It is not recommended
|
||||||
to attempt infiltrations above mid-normal.
|
to attempt infiltrations above mid-normal.
|
||||||
|
|
||||||
|
The "maximum level" is the number of challenges you will need to pass to receive
|
||||||
|
the infiltration reward.
|
||||||
|
|
||||||
|
Every time you fail an infiltration challenge, you will take damage based on the
|
||||||
|
difficulty of the infiltration. If you are reduced to 0 hp or below, the
|
||||||
|
infiltration will immediately end.
|
||||||
|
|
||||||
* Most use spacebar as "action"
|
* Most use spacebar as "action"
|
||||||
* Some use WASD or arrows interchangeably.
|
* Some use WASD or arrows interchangeably.
|
||||||
* A few others use the rest of the keyboard.
|
* A few others use the rest of the keyboard.
|
||||||
|
@ -16,7 +16,6 @@ Affects:
|
|||||||
* Chance to successfully hack a server
|
* Chance to successfully hack a server
|
||||||
* Percent money stolen when hacking a server
|
* Percent money stolen when hacking a server
|
||||||
* Success rate of certain crimes
|
* Success rate of certain crimes
|
||||||
* Success rate of Hacking option during Infiltration
|
|
||||||
* Time it takes to create a program
|
* Time it takes to create a program
|
||||||
* Faction reputation gain when carrying out Hacking Contracts or Field Work
|
* Faction reputation gain when carrying out Hacking Contracts or Field Work
|
||||||
* Company reputation gain for certain jobs
|
* Company reputation gain for certain jobs
|
||||||
@ -26,7 +25,6 @@ Gain experience by:
|
|||||||
* Manually hacking servers through Terminal
|
* Manually hacking servers through Terminal
|
||||||
* Executing hack(), grow(), or weaken() through a script
|
* Executing hack(), grow(), or weaken() through a script
|
||||||
* Committing certain crimes
|
* Committing certain crimes
|
||||||
* Infiltration
|
|
||||||
* Carrying out Hacking Contracts or doing Field work for Factions
|
* Carrying out Hacking Contracts or doing Field work for Factions
|
||||||
* Working certain jobs at a company
|
* Working certain jobs at a company
|
||||||
* Studying at a university
|
* Studying at a university
|
||||||
@ -38,14 +36,12 @@ Represents the player's physical offensive power
|
|||||||
Affects:
|
Affects:
|
||||||
|
|
||||||
* Success rate of certain crimes
|
* Success rate of certain crimes
|
||||||
* Success rate of Combat options during Infiltration
|
|
||||||
* Faction reputation gain for Security and Field Work
|
* Faction reputation gain for Security and Field Work
|
||||||
* Company reputation gain for certain jobs
|
* Company reputation gain for certain jobs
|
||||||
|
|
||||||
Gain experience by:
|
Gain experience by:
|
||||||
|
|
||||||
* Committing certain crimes
|
* Committing certain crimes
|
||||||
* Infiltration
|
|
||||||
* Working out at a gym
|
* Working out at a gym
|
||||||
* Doing Security/Field Work for a faction
|
* Doing Security/Field Work for a faction
|
||||||
* Working certain jobs at a company
|
* Working certain jobs at a company
|
||||||
@ -58,15 +54,12 @@ Affects:
|
|||||||
|
|
||||||
* Success rate of certain crimes
|
* Success rate of certain crimes
|
||||||
* The player's HP
|
* The player's HP
|
||||||
* Success rate of Combat options during Infiltration
|
|
||||||
* How much damage the player takes during Infiltration
|
|
||||||
* Faction reputation gain for Security and Field Work
|
* Faction reputation gain for Security and Field Work
|
||||||
* Company reputation gain for certain jobs
|
* Company reputation gain for certain jobs
|
||||||
|
|
||||||
Gain experience by:
|
Gain experience by:
|
||||||
|
|
||||||
* Committing certain crimes
|
* Committing certain crimes
|
||||||
* Infiltration
|
|
||||||
* Working out at a gym
|
* Working out at a gym
|
||||||
* Doing Security/Field Work for a faction
|
* Doing Security/Field Work for a faction
|
||||||
* Working certain jobs at a company
|
* Working certain jobs at a company
|
||||||
@ -78,14 +71,12 @@ Represents the player's skill and adeptness in performing certain tasks
|
|||||||
Affects:
|
Affects:
|
||||||
|
|
||||||
* Success rate of certain crimes
|
* Success rate of certain crimes
|
||||||
* Success rate of Combat, Lockpick, and Escape options during Infiltration
|
|
||||||
* Faction reputation gain for Security and Field Work
|
* Faction reputation gain for Security and Field Work
|
||||||
* Company reputation gain for certain jobs
|
* Company reputation gain for certain jobs
|
||||||
|
|
||||||
Gain experience by:
|
Gain experience by:
|
||||||
|
|
||||||
* Committing certain crimes
|
* Committing certain crimes
|
||||||
* Infiltration
|
|
||||||
* Working out at a gym
|
* Working out at a gym
|
||||||
* Doing Security/Field Work for a faction
|
* Doing Security/Field Work for a faction
|
||||||
* Working certain jobs at a company
|
* Working certain jobs at a company
|
||||||
@ -97,14 +88,12 @@ Represents the player's speed and ability to move
|
|||||||
Affects:
|
Affects:
|
||||||
|
|
||||||
* Success rate of certain crimes
|
* Success rate of certain crimes
|
||||||
* Success rate of Combat, Sneak, and Escape options during Infiltration
|
|
||||||
* Faction reputation gain for Security and Field Work
|
* Faction reputation gain for Security and Field Work
|
||||||
* Company reputation gain for certain jobs
|
* Company reputation gain for certain jobs
|
||||||
|
|
||||||
Gain experience by:
|
Gain experience by:
|
||||||
|
|
||||||
* Committing certain crimes
|
* Committing certain crimes
|
||||||
* Infiltration
|
|
||||||
* Working out at a gym
|
* Working out at a gym
|
||||||
* Doing Security/Field Work for a faction
|
* Doing Security/Field Work for a faction
|
||||||
* Working certain jobs at a company
|
* Working certain jobs at a company
|
||||||
@ -116,14 +105,12 @@ Represents the player's social abilities
|
|||||||
Affects:
|
Affects:
|
||||||
|
|
||||||
* Success rate of certain crimes
|
* Success rate of certain crimes
|
||||||
* Success rate of Bribe option during Infiltration
|
|
||||||
* Faction reputation gain for Field Work
|
* Faction reputation gain for Field Work
|
||||||
* Company reputation gain for most jobs
|
* Company reputation gain for most jobs
|
||||||
|
|
||||||
Gain experience by:
|
Gain experience by:
|
||||||
|
|
||||||
* Committing certain crimes
|
* Committing certain crimes
|
||||||
* Infiltration
|
|
||||||
* Studying at a university
|
* Studying at a university
|
||||||
* Working a relevant job at a company
|
* Working a relevant job at a company
|
||||||
* Doing Field work for a Faction
|
* Doing Field work for a Faction
|
||||||
|
@ -253,7 +253,6 @@ Here's what mine showed at the time I made this::
|
|||||||
|
|
||||||
Take note of the following servers:
|
Take note of the following servers:
|
||||||
|
|
||||||
* |n00dles|
|
|
||||||
* |sigma-cosmetics|
|
* |sigma-cosmetics|
|
||||||
* |joesguns|
|
* |joesguns|
|
||||||
* |nectar-net|
|
* |nectar-net|
|
||||||
@ -807,8 +806,7 @@ startup script. Feel free to adjust it to your liking.
|
|||||||
|
|
||||||
// Array of all servers that don't need any ports opened
|
// Array of all servers that don't need any ports opened
|
||||||
// to gain root access. These have 16 GB of RAM
|
// to gain root access. These have 16 GB of RAM
|
||||||
var servers0Port = ["n00dles",
|
var servers0Port = ["sigma-cosmetics",
|
||||||
"sigma-cosmetics",
|
|
||||||
"joesguns",
|
"joesguns",
|
||||||
"nectar-net",
|
"nectar-net",
|
||||||
"hong-fang-tea",
|
"hong-fang-tea",
|
||||||
|
@ -66,8 +66,7 @@ And the data in port 1 will look like::
|
|||||||
WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1`
|
WARNING: Port Handles only work in :ref:`netscriptjs`. They do not work in :ref:`netscript1`
|
||||||
|
|
||||||
The :js:func:`getPortHandle` Netscript function can be used to get a handle to a Netscript Port.
|
The :js:func:`getPortHandle` Netscript function can be used to get a handle to a Netscript Port.
|
||||||
This handle allows you to access several new port-related functions and the
|
This handle allows you to access several new port-related functions. The functions are:
|
||||||
port's underlying data structure, which is just a JavaScript array. The functions are:
|
|
||||||
|
|
||||||
.. js:method:: NetscriptPort.writePort(data)
|
.. js:method:: NetscriptPort.writePort(data)
|
||||||
|
|
||||||
@ -111,22 +110,11 @@ port's underlying data structure, which is just a JavaScript array. The function
|
|||||||
|
|
||||||
Clears all data from the port. Works the same as the Netscript function `clear`
|
Clears all data from the port. Works the same as the Netscript function `clear`
|
||||||
|
|
||||||
.. js:attribute:: NetscriptPort.data
|
|
||||||
|
|
||||||
The Netscript port underlying data structure, which is just a Javascript array. All
|
|
||||||
valid Javascript Array methods can be called on this.
|
|
||||||
|
|
||||||
Port Handle Example::
|
Port Handle Example::
|
||||||
|
|
||||||
port = getPortHandle(5);
|
port = getPortHandle(5);
|
||||||
back = port.data.pop(); //Get and remove last element in port
|
back = port.data.pop(); //Get and remove last element in port
|
||||||
|
|
||||||
//Remove an element from the port
|
|
||||||
i = port.data.findIndex("foo");
|
|
||||||
if (i != -1) {
|
|
||||||
port.data.slice(i, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Wait for port data before reading
|
//Wait for port data before reading
|
||||||
while(port.empty()) {
|
while(port.empty()) {
|
||||||
sleep(10000);
|
sleep(10000);
|
||||||
|
@ -3,6 +3,9 @@ const greenworks = require("./greenworks");
|
|||||||
const log = require("electron-log");
|
const log = require("electron-log");
|
||||||
|
|
||||||
function enableAchievementsInterval(window) {
|
function enableAchievementsInterval(window) {
|
||||||
|
// If the Steam API could not be initialized on game start, we'll abort this.
|
||||||
|
if (global.greenworksError) return;
|
||||||
|
|
||||||
// This is backward but the game fills in an array called `document.achievements` and we retrieve it from
|
// This is backward but the game fills in an array called `document.achievements` and we retrieve it from
|
||||||
// here. Hey if it works it works.
|
// here. Hey if it works it works.
|
||||||
const steamAchievements = greenworks.getAchievementNames();
|
const steamAchievements = greenworks.getAchievementNames();
|
||||||
|
@ -16,10 +16,18 @@ process.on('uncaughtException', function () {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// We want to fail gracefully if we cannot connect to Steam
|
||||||
|
try {
|
||||||
if (greenworks.init()) {
|
if (greenworks.init()) {
|
||||||
log.info("Steam API has been initialized.");
|
log.info("Steam API has been initialized.");
|
||||||
} else {
|
} else {
|
||||||
log.warn("Steam API has failed to initialize.");
|
const error = "Steam API has failed to initialize.";
|
||||||
|
log.warn(error);
|
||||||
|
global.greenworksError = error;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
log.warn(ex.message);
|
||||||
|
global.greenworksError = ex.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStopProcessHandler(app, window, enabled) {
|
function setStopProcessHandler(app, window, enabled) {
|
||||||
@ -113,4 +121,13 @@ app.whenReady().then(async () => {
|
|||||||
} else {
|
} else {
|
||||||
startWindow(process.argv.includes("--no-scripts"));
|
startWindow(process.argv.includes("--no-scripts"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (global.greenworksError) {
|
||||||
|
dialog.showMessageBox({
|
||||||
|
title: 'Bitburner',
|
||||||
|
message: 'Could not connect to Steam',
|
||||||
|
detail: `${global.greenworksError}\n\nYou won't be able to receive achievements until this is resolved and you restart the game.`,
|
||||||
|
type: 'warning', buttons: ['OK']
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -9,15 +9,15 @@ Format a string.
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
sprintf(format: string, ...args: string[]): string;
|
sprintf(format: string, ...args: any[]): string;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --- | --- | --- |
|
| --------- | ------- | -------------------- |
|
||||||
| format | string | String to format. |
|
| format | string | String to format. |
|
||||||
| args | string\[\] | Formating arguments. |
|
| args | any\[\] | Formating arguments. |
|
||||||
|
|
||||||
<b>Returns:</b>
|
<b>Returns:</b>
|
||||||
|
|
||||||
@ -30,4 +30,3 @@ Formated text.
|
|||||||
RAM cost: 0 GB
|
RAM cost: 0 GB
|
||||||
|
|
||||||
see: https://github.com/alexei/sprintf.js
|
see: https://github.com/alexei/sprintf.js
|
||||||
|
|
||||||
|
@ -9,15 +9,15 @@ Format a string with an array of arguments.
|
|||||||
<b>Signature:</b>
|
<b>Signature:</b>
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
vsprintf(format: string, args: string[]): string;
|
vsprintf(format: string, args: any[]): string;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --- | --- | --- |
|
| --------- | ------- | -------------------- |
|
||||||
| format | string | String to format. |
|
| format | string | String to format. |
|
||||||
| args | string\[\] | Formating arguments. |
|
| args | any\[\] | Formating arguments. |
|
||||||
|
|
||||||
<b>Returns:</b>
|
<b>Returns:</b>
|
||||||
|
|
||||||
@ -30,4 +30,3 @@ Formated text.
|
|||||||
RAM cost: 0 GB
|
RAM cost: 0 GB
|
||||||
|
|
||||||
see: https://github.com/alexei/sprintf.js
|
see: https://github.com/alexei/sprintf.js
|
||||||
|
|
||||||
|
23
markdown/bitburner.userinterface.getstyles.md
Normal file
23
markdown/bitburner.userinterface.getstyles.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) > [UserInterface](./bitburner.userinterface.md) > [getStyles](./bitburner.userinterface.getstyles.md)
|
||||||
|
|
||||||
|
## UserInterface.getStyles() method
|
||||||
|
|
||||||
|
Get the current styles
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
getStyles(): IStyleSettings;
|
||||||
|
```
|
||||||
|
<b>Returns:</b>
|
||||||
|
|
||||||
|
IStyleSettings
|
||||||
|
|
||||||
|
An object containing the player's styles
|
||||||
|
|
||||||
|
## Remarks
|
||||||
|
|
||||||
|
RAM cost: cost: 0 GB
|
||||||
|
|
@ -16,7 +16,10 @@ interface UserInterface
|
|||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
| [getStyles()](./bitburner.userinterface.getstyles.md) | Get the current styles |
|
||||||
| [getTheme()](./bitburner.userinterface.gettheme.md) | Get the current theme |
|
| [getTheme()](./bitburner.userinterface.gettheme.md) | Get the current theme |
|
||||||
|
| [resetStyles()](./bitburner.userinterface.resetstyles.md) | Resets the player's styles to the default values |
|
||||||
| [resetTheme()](./bitburner.userinterface.resettheme.md) | Resets the player's theme to the default values |
|
| [resetTheme()](./bitburner.userinterface.resettheme.md) | Resets the player's theme to the default values |
|
||||||
|
| [setStyles(newStyles)](./bitburner.userinterface.setstyles.md) | Sets the current styles |
|
||||||
| [setTheme(newTheme)](./bitburner.userinterface.settheme.md) | Sets the current theme |
|
| [setTheme(newTheme)](./bitburner.userinterface.settheme.md) | Sets the current theme |
|
||||||
|
|
||||||
|
21
markdown/bitburner.userinterface.resetstyles.md
Normal file
21
markdown/bitburner.userinterface.resetstyles.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [UserInterface](./bitburner.userinterface.md) > [resetStyles](./bitburner.userinterface.resetstyles.md)
|
||||||
|
|
||||||
|
## UserInterface.resetStyles() method
|
||||||
|
|
||||||
|
Resets the player's styles to the default values
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
resetStyles(): void;
|
||||||
|
```
|
||||||
|
<b>Returns:</b>
|
||||||
|
|
||||||
|
void
|
||||||
|
|
||||||
|
## Remarks
|
||||||
|
|
||||||
|
RAM cost: cost: 0 GB
|
||||||
|
|
38
markdown/bitburner.userinterface.setstyles.md
Normal file
38
markdown/bitburner.userinterface.setstyles.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||||
|
|
||||||
|
[Home](./index.md) > [bitburner](./bitburner.md) > [UserInterface](./bitburner.userinterface.md) > [setStyles](./bitburner.userinterface.setstyles.md)
|
||||||
|
|
||||||
|
## UserInterface.setStyles() method
|
||||||
|
|
||||||
|
Sets the current styles
|
||||||
|
|
||||||
|
<b>Signature:</b>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
setStyles(newStyles: IStyleSettings): void;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| newStyles | IStyleSettings | |
|
||||||
|
|
||||||
|
<b>Returns:</b>
|
||||||
|
|
||||||
|
void
|
||||||
|
|
||||||
|
## Remarks
|
||||||
|
|
||||||
|
RAM cost: cost: 0 GB
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Usage example (NS2)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const styles = ns.ui.getStyles();
|
||||||
|
styles.fontFamily = 'Comic Sans Ms';
|
||||||
|
ns.ui.setStyles(styles);
|
||||||
|
```
|
||||||
|
|
@ -209,7 +209,7 @@
|
|||||||
},
|
},
|
||||||
"STOCK_1q": {
|
"STOCK_1q": {
|
||||||
"ID": "STOCK_1q",
|
"ID": "STOCK_1q",
|
||||||
"Name": "Wolf of wall stree.",
|
"Name": "Wolf of wall street.",
|
||||||
"Description": "Make 1q on the stock market."
|
"Description": "Make 1q on the stock market."
|
||||||
},
|
},
|
||||||
"DISCOUNT": {
|
"DISCOUNT": {
|
||||||
@ -484,3 +484,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
|||||||
|
|
||||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { clearObject } from "../utils/helpers/clearObject";
|
import { clearObject } from "../utils/helpers/clearObject";
|
||||||
import { Router } from "../ui/GameRoot";
|
|
||||||
|
|
||||||
import { WHRNG } from "../Casino/RNG";
|
import { WHRNG } from "../Casino/RNG";
|
||||||
|
|
||||||
@ -2583,7 +2582,6 @@ function installAugmentations(): boolean {
|
|||||||
augmentationList += aug.name + level + "<br>";
|
augmentationList += aug.name + level + "<br>";
|
||||||
}
|
}
|
||||||
Player.queuedAugmentations = [];
|
Player.queuedAugmentations = [];
|
||||||
|
|
||||||
dialogBoxCreate(
|
dialogBoxCreate(
|
||||||
"You slowly drift to sleep as scientists put you under in order " +
|
"You slowly drift to sleep as scientists put you under in order " +
|
||||||
"to install the following Augmentations:<br>" +
|
"to install the following Augmentations:<br>" +
|
||||||
|
@ -20,13 +20,15 @@ export const ConsoleHelpText: {
|
|||||||
" skill [action] [name] Level or display info about your Bladeburner skills",
|
" skill [action] [name] Level or display info about your Bladeburner skills",
|
||||||
" start [type] [name] Start a Bladeburner action/task",
|
" start [type] [name] Start a Bladeburner action/task",
|
||||||
" stop Stops your current Bladeburner action/task",
|
" stop Stops your current Bladeburner action/task",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
automate: [
|
automate: [
|
||||||
"automate [var] [val] [hi/low]",
|
"Usage: automate [var] [val] [hi/low]",
|
||||||
" ",
|
" ",
|
||||||
"A simple way to automate your Bladeburner actions. This console command can be used " +
|
"A simple way to automate your Bladeburner actions. This console command can be used " +
|
||||||
"to automatically start an action when your stamina rises above a certain threshold, and " +
|
"to automatically start an action when your stamina rises above a certain threshold, and " +
|
||||||
"automatically switch to another action when your stamina drops below another threshold.",
|
"automatically switch to another action when your stamina drops below another threshold.",
|
||||||
|
" ",
|
||||||
" automate status - Check the current status of your automation and get a brief description of what it'll do",
|
" automate status - Check the current status of your automation and get a brief description of what it'll do",
|
||||||
" automate en - Enable the automation feature",
|
" automate en - Enable the automation feature",
|
||||||
" automate dis - Disable the automation feature",
|
" automate dis - Disable the automation feature",
|
||||||
@ -42,11 +44,12 @@ export const ConsoleHelpText: {
|
|||||||
"if your stamina is 100 or higher, and then switch to Field Analysis if your stamina drops below " +
|
"if your stamina is 100 or higher, and then switch to Field Analysis if your stamina drops below " +
|
||||||
"50. Note that when setting the action, the name of the action is CASE-SENSITIVE. It must " +
|
"50. Note that when setting the action, the name of the action is CASE-SENSITIVE. It must " +
|
||||||
"exactly match whatever the name is in the UI.",
|
"exactly match whatever the name is in the UI.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
clear: ["clear", "", "Clears the console"],
|
clear: ["Usage: clear", " ", "Clears the console", " "],
|
||||||
cls: ["cls", "", "Clears the console"],
|
cls: ["Usage: cls", " ", "Clears the console", " "],
|
||||||
help: [
|
help: [
|
||||||
"help [command]",
|
"Usage: help [command]",
|
||||||
" ",
|
" ",
|
||||||
"Running 'help' with no arguments displays the general help text, which lists all console commands " +
|
"Running 'help' with no arguments displays the general help text, which lists all console commands " +
|
||||||
"and a brief description of what they do. A command can be specified to get more specific help text " +
|
"and a brief description of what they do. A command can be specified to get more specific help text " +
|
||||||
@ -55,9 +58,10 @@ export const ConsoleHelpText: {
|
|||||||
" help automate",
|
" help automate",
|
||||||
" ",
|
" ",
|
||||||
"will display specific information about using the automate console command",
|
"will display specific information about using the automate console command",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
log: [
|
log: [
|
||||||
"log [en/dis] [type]",
|
"Usage: log [en/dis] [type]",
|
||||||
" ",
|
" ",
|
||||||
"Enable or disable logging. By default, the results of completing actions such as contracts/operations are logged " +
|
"Enable or disable logging. By default, the results of completing actions such as contracts/operations are logged " +
|
||||||
"in the console. There are also random events that are logged in the console as well. The five categories of " +
|
"in the console. There are also random events that are logged in the console as well. The five categories of " +
|
||||||
@ -75,9 +79,10 @@ export const ConsoleHelpText: {
|
|||||||
" ",
|
" ",
|
||||||
" log dis all",
|
" log dis all",
|
||||||
" log en all",
|
" log en all",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
skill: [
|
skill: [
|
||||||
"skill [action] [name]",
|
"Usage: skill [action] [name]",
|
||||||
" ",
|
" ",
|
||||||
"Level or display information about your skills.",
|
"Level or display information about your skills.",
|
||||||
" ",
|
" ",
|
||||||
@ -95,9 +100,10 @@ export const ConsoleHelpText: {
|
|||||||
"This console command can also be used to level up skills:",
|
"This console command can also be used to level up skills:",
|
||||||
" ",
|
" ",
|
||||||
" skill level [skill name]",
|
" skill level [skill name]",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
start: [
|
start: [
|
||||||
"start [type] [name]",
|
"Usage: start [type] [name]",
|
||||||
" ",
|
" ",
|
||||||
"Start an action. An action is specified by its type and its name. The " +
|
"Start an action. An action is specified by its type and its name. The " +
|
||||||
"name is case-sensitive. It must appear exactly as it does in the UI. If " +
|
"name is case-sensitive. It must appear exactly as it does in the UI. If " +
|
||||||
@ -110,6 +116,7 @@ export const ConsoleHelpText: {
|
|||||||
" ",
|
" ",
|
||||||
" start contract Tracking",
|
" start contract Tracking",
|
||||||
" start op 'Undercover Operation'",
|
" start op 'Undercover Operation'",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
stop: ["stop", "", "Stop your current action and go idle."],
|
stop: ["Usage: stop", " ", "Stop your current action and go idle.", " "],
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,6 @@ import { Console } from "./Console";
|
|||||||
import { AllPages } from "./AllPages";
|
import { AllPages } from "./AllPages";
|
||||||
|
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import Grid from "@mui/material/Grid";
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
export function BladeburnerRoot(): React.ReactElement {
|
export function BladeburnerRoot(): React.ReactElement {
|
||||||
@ -24,14 +23,10 @@ export function BladeburnerRoot(): React.ReactElement {
|
|||||||
if (bladeburner === null) return <></>;
|
if (bladeburner === null) return <></>;
|
||||||
return (
|
return (
|
||||||
<Box display="flex" flexDirection="column">
|
<Box display="flex" flexDirection="column">
|
||||||
<Grid container>
|
<Box sx={{ display: "grid", gridTemplateColumns: "4fr 8fr", p: 1 }}>
|
||||||
<Grid item xs={6}>
|
|
||||||
<Stats bladeburner={bladeburner} player={player} router={router} />
|
<Stats bladeburner={bladeburner} player={player} router={router} />
|
||||||
</Grid>
|
|
||||||
<Grid item xs={6}>
|
|
||||||
<Console bladeburner={bladeburner} player={player} />
|
<Console bladeburner={bladeburner} player={player} />
|
||||||
</Grid>
|
</Box>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<AllPages bladeburner={bladeburner} player={player} />
|
<AllPages bladeburner={bladeburner} player={player} />
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -23,7 +23,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
backgroundColor: "#000",
|
backgroundColor: theme.colors.backgroundsecondary,
|
||||||
},
|
},
|
||||||
nopadding: {
|
nopadding: {
|
||||||
padding: theme.spacing(0),
|
padding: theme.spacing(0),
|
||||||
@ -56,6 +56,7 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [command, setCommand] = useState("");
|
const [command, setCommand] = useState("");
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
const consoleInput = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
setCommand(event.target.value);
|
setCommand(event.target.value);
|
||||||
@ -131,15 +132,21 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleClick(): void {
|
||||||
|
if (!consoleInput.current) return;
|
||||||
|
consoleInput.current.focus();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper>
|
<Paper sx={{ p: 1 }}>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
height: '60vh',
|
height: '60vh',
|
||||||
paddingBottom: '8px',
|
paddingBottom: '8px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'stretch',
|
alignItems: 'stretch',
|
||||||
whiteSpace: 'pre-wrap',
|
whiteSpace: 'pre-wrap',
|
||||||
}}>
|
}}
|
||||||
|
onClick={handleClick}>
|
||||||
<Box>
|
<Box>
|
||||||
<Logs entries={[...props.bladeburner.consoleLogs]} />
|
<Logs entries={[...props.bladeburner.consoleLogs]} />
|
||||||
</Box>
|
</Box>
|
||||||
@ -149,6 +156,7 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
autoFocus
|
autoFocus
|
||||||
tabIndex={1}
|
tabIndex={1}
|
||||||
type="text"
|
type="text"
|
||||||
|
inputRef={consoleInput}
|
||||||
value={command}
|
value={command}
|
||||||
onChange={handleCommandChange}
|
onChange={handleCommandChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
@ -182,7 +190,7 @@ function Logs({entries}: ILogProps): React.ReactElement {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, [entries]);
|
}, [entries.length]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}>
|
<List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}>
|
||||||
|
@ -3,7 +3,6 @@ import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/Stri
|
|||||||
import { BladeburnerConstants } from "../data/Constants";
|
import { BladeburnerConstants } from "../data/Constants";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { StatsTable } from "../../ui/React/StatsTable";
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { Factions } from "../../Faction/Factions";
|
import { Factions } from "../../Faction/Factions";
|
||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
@ -44,7 +43,19 @@ export function Stats(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper sx={{ p: 1 }}>
|
<Paper sx={{ p: 1, overflowY: 'auto', overflowX: 'hidden', wordBreak: 'break-all' }}>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, maxHeight: '60vh' }}>
|
||||||
|
<Box sx={{ alignSelf: 'flex-start', width: '100%' }}>
|
||||||
|
<Button onClick={() => setTravelOpen(true)} sx={{ width: '50%' }}>Travel</Button>
|
||||||
|
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
||||||
|
<span>
|
||||||
|
<Button disabled={!inFaction} onClick={openFaction} sx={{ width: '50%' }}>
|
||||||
|
Faction
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
||||||
|
</Box>
|
||||||
<Box display="flex">
|
<Box display="flex">
|
||||||
<Tooltip title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
<Tooltip title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
||||||
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
||||||
@ -158,24 +169,16 @@ export function Stats(props: IProps): React.ReactElement {
|
|||||||
)}
|
)}
|
||||||
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<StatsTable
|
<Typography>
|
||||||
rows={[
|
Aug. Success Chance mult: {formatNumber(props.player.bladeburner_success_chance_mult * 100, 1)}%
|
||||||
["Aug. Success Chance mult: ", formatNumber(props.player.bladeburner_success_chance_mult * 100, 1) + "%"],
|
|
||||||
["Aug. Max Stamina mult: ", formatNumber(props.player.bladeburner_max_stamina_mult * 100, 1) + "%"],
|
|
||||||
["Aug. Stamina Gain mult: ", formatNumber(props.player.bladeburner_stamina_gain_mult * 100, 1) + "%"],
|
|
||||||
["Aug. Field Analysis mult: ", formatNumber(props.player.bladeburner_analysis_mult * 100, 1) + "%"],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<br />
|
<br />
|
||||||
<Button onClick={() => setTravelOpen(true)}>Travel</Button>
|
Aug. Max Stamina mult: {formatNumber(props.player.bladeburner_max_stamina_mult * 100, 1)}%
|
||||||
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
<br />
|
||||||
<span>
|
Aug. Stamina Gain mult: {formatNumber(props.player.bladeburner_stamina_gain_mult * 100, 1)}%
|
||||||
<Button disabled={!inFaction} onClick={openFaction}>
|
<br />
|
||||||
Faction
|
Aug. Field Analysis mult: {formatNumber(props.player.bladeburner_analysis_mult * 100, 1)}%
|
||||||
</Button>
|
</Typography>
|
||||||
</span>
|
</Box>
|
||||||
</Tooltip>
|
|
||||||
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { CompanyPosition } from "./CompanyPosition";
|
import { CompanyPosition } from "./CompanyPosition";
|
||||||
import * as posNames from "./data/companypositionnames";
|
import * as posNames from "./data/companypositionnames";
|
||||||
|
import { favorToRep, repToFavor } from "../Faction/formulas/favor";
|
||||||
|
|
||||||
import { CONSTANTS } from "../Constants";
|
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
@ -71,7 +71,6 @@ export class Company {
|
|||||||
isPlayerEmployed: boolean;
|
isPlayerEmployed: boolean;
|
||||||
playerReputation: number;
|
playerReputation: number;
|
||||||
favor: number;
|
favor: number;
|
||||||
rolloverRep: number;
|
|
||||||
|
|
||||||
constructor(p: IConstructorParams = DefaultConstructorParams) {
|
constructor(p: IConstructorParams = DefaultConstructorParams) {
|
||||||
this.name = p.name;
|
this.name = p.name;
|
||||||
@ -84,7 +83,6 @@ export class Company {
|
|||||||
this.isPlayerEmployed = false;
|
this.isPlayerEmployed = false;
|
||||||
this.playerReputation = 1;
|
this.playerReputation = 1;
|
||||||
this.favor = 0;
|
this.favor = 0;
|
||||||
this.rolloverRep = 0;
|
|
||||||
this.isMegacorp = false;
|
this.isMegacorp = false;
|
||||||
if (p.isMegacorp) this.isMegacorp = true;
|
if (p.isMegacorp) this.isMegacorp = true;
|
||||||
}
|
}
|
||||||
@ -137,39 +135,17 @@ export class Company {
|
|||||||
if (this.favor == null) {
|
if (this.favor == null) {
|
||||||
this.favor = 0;
|
this.favor = 0;
|
||||||
}
|
}
|
||||||
if (this.rolloverRep == null) {
|
this.favor += this.getFavorGain();
|
||||||
this.rolloverRep = 0;
|
|
||||||
}
|
|
||||||
const res = this.getFavorGain();
|
|
||||||
if (res.length != 2) {
|
|
||||||
console.error("Invalid result from getFavorGain() function");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.favor += res[0];
|
getFavorGain(): number {
|
||||||
this.rolloverRep = res[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
getFavorGain(): number[] {
|
|
||||||
if (this.favor == null) {
|
if (this.favor == null) {
|
||||||
this.favor = 0;
|
this.favor = 0;
|
||||||
}
|
}
|
||||||
if (this.rolloverRep == null) {
|
const storedRep = Math.max(0, favorToRep(this.favor));
|
||||||
this.rolloverRep = 0;
|
const totalRep = storedRep + this.playerReputation;
|
||||||
}
|
const newFavor = repToFavor(totalRep);
|
||||||
let favorGain = 0,
|
return newFavor - this.favor;
|
||||||
rep = this.playerReputation + this.rolloverRep;
|
|
||||||
let reqdRep = CONSTANTS.CompanyReputationToFavorBase * Math.pow(CONSTANTS.CompanyReputationToFavorMult, this.favor);
|
|
||||||
while (rep > 0) {
|
|
||||||
if (rep >= reqdRep) {
|
|
||||||
++favorGain;
|
|
||||||
rep -= reqdRep;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
reqdRep *= CONSTANTS.FactionReputationToFavorMult;
|
|
||||||
}
|
|
||||||
return [favorGain, rep];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,7 @@ import { hasAugmentationPrereqs } from "../FactionHelpers";
|
|||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { Reputation } from "../../ui/React/Reputation";
|
import { Reputation } from "../../ui/React/Reputation";
|
||||||
import { Favor } from "../../ui/React/Favor";
|
import { Favor } from "../../ui/React/Favor";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
@ -203,7 +204,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Typography>Price multiplier: x {mult.toFixed(3)}</Typography>
|
<Typography>Price multiplier: x {numeralWrapper.formatMultiplier(mult)}</Typography>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
||||||
|
@ -18,8 +18,18 @@ import { HacknetNodeConstants } from "./data/Constants";
|
|||||||
|
|
||||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
import { ObjectValidator, minMax } from "../utils/Validator";
|
||||||
|
|
||||||
export class HacknetNode implements IHacknetNode {
|
export class HacknetNode implements IHacknetNode {
|
||||||
|
|
||||||
|
static validationData: ObjectValidator<HacknetNode> = {
|
||||||
|
cores: minMax(1, 1, HacknetNodeConstants.MaxCores),
|
||||||
|
level: minMax(1, 1, HacknetNodeConstants.MaxLevel),
|
||||||
|
ram: minMax(1, 1, HacknetNodeConstants.MaxRam),
|
||||||
|
onlineTimeSeconds: minMax(0, 0, Infinity),
|
||||||
|
totalMoneyGenerated: minMax(0, 0, Infinity)
|
||||||
|
}
|
||||||
|
|
||||||
// Node's number of cores
|
// Node's number of cores
|
||||||
cores = 1;
|
cores = 1;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import { GetServer } from "../../Server/AllServers";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
@ -136,7 +137,7 @@ export function HacknetRoot(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
{hasHacknetServers(props.player) && <Button onClick={() => setOpen(true)}>Spend Hashes on Upgrades</Button>}
|
{hasHacknetServers(props.player) && <Button onClick={() => setOpen(true)}>Spend Hashes on Upgrades</Button>}
|
||||||
|
|
||||||
<Grid container>{nodes}</Grid>
|
<Box sx={{ display: 'grid', width: 'fit-content', gridTemplateColumns: 'repeat(3, 1fr)' }}>{nodes}</Box>
|
||||||
<HashUpgradeModal open={open} onClose={() => setOpen(false)} />
|
<HashUpgradeModal open={open} onClose={() => setOpen(false)} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -114,7 +114,7 @@ export const Literatures: IMap<Literature> = {};
|
|||||||
"as working for any military/defense organization or conducting any bioengineering, computing, or robotics related research.<br><br>" +
|
"as working for any military/defense organization or conducting any bioengineering, computing, or robotics related research.<br><br>" +
|
||||||
"Unfortunately, many believe that not all of the rogue MK-VI Synthoids from the Uprising were found and destroyed, " +
|
"Unfortunately, many believe that not all of the rogue MK-VI Synthoids from the Uprising were found and destroyed, " +
|
||||||
"and that many of them are blending in as normal humans in society today. In response, many nations have created " +
|
"and that many of them are blending in as normal humans in society today. In response, many nations have created " +
|
||||||
"Bladeburner divisions, special military branches that are tasked with investigating and dealing with any Synthoid threads.<br><br>" +
|
"Bladeburner divisions, special military branches that are tasked with investigating and dealing with any Synthoid threats.<br><br>" +
|
||||||
"To this day, tensions still exist between the remaining Synthoids and humans as a result of the Uprising.<br><br>" +
|
"To this day, tensions still exist between the remaining Synthoids and humans as a result of the Uprising.<br><br>" +
|
||||||
"Nobody knows what happened to the terrorist group Ascendis Totalis.";
|
"Nobody knows what happened to the terrorist group Ascendis Totalis.";
|
||||||
Literatures[fn] = new Literature(title, fn, txt);
|
Literatures[fn] = new Literature(title, fn, txt);
|
||||||
|
@ -10,6 +10,7 @@ import { CoinFlip } from "../../Casino/CoinFlip";
|
|||||||
import { Roulette } from "../../Casino/Roulette";
|
import { Roulette } from "../../Casino/Roulette";
|
||||||
import { SlotMachine } from "../../Casino/SlotMachine";
|
import { SlotMachine } from "../../Casino/SlotMachine";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
enum GameType {
|
enum GameType {
|
||||||
None = "none",
|
None = "none",
|
||||||
@ -33,15 +34,12 @@ export function CasinoLocation(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{game === GameType.None && (
|
{game === GameType.None && (
|
||||||
<>
|
<Box sx={{ display: 'grid', width: 'fit-content' }}>
|
||||||
<Button onClick={() => updateGame(GameType.Coin)}>Play coin flip</Button>
|
<Button onClick={() => updateGame(GameType.Coin)}>Play coin flip</Button>
|
||||||
<br />
|
|
||||||
<Button onClick={() => updateGame(GameType.Slots)}>Play slots</Button>
|
<Button onClick={() => updateGame(GameType.Slots)}>Play slots</Button>
|
||||||
<br />
|
|
||||||
<Button onClick={() => updateGame(GameType.Roulette)}>Play roulette</Button>
|
<Button onClick={() => updateGame(GameType.Roulette)}>Play roulette</Button>
|
||||||
<br />
|
|
||||||
<Button onClick={() => updateGame(GameType.Blackjack)}>Play blackjack</Button>
|
<Button onClick={() => updateGame(GameType.Blackjack)}>Play blackjack</Button>
|
||||||
</>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{game !== GameType.None && (
|
{game !== GameType.None && (
|
||||||
<>
|
<>
|
||||||
|
@ -198,7 +198,7 @@ export function CompanyLocation(props: IProps): React.ReactElement {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
You will have <Favor favor={company.favor + favorGain[0]} /> company favor upon resetting after
|
You will have <Favor favor={company.favor + favorGain} /> company favor upon resetting after
|
||||||
installing Augmentations
|
installing Augmentations
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@ -226,8 +226,12 @@ export function CompanyLocation(props: IProps): React.ReactElement {
|
|||||||
</Box>
|
</Box>
|
||||||
<Typography>-------------------------</Typography>
|
<Typography>-------------------------</Typography>
|
||||||
<br />
|
<br />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Box sx={{ display: 'grid', width: 'fit-content' }}>
|
||||||
|
{isEmployedHere && (
|
||||||
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
|
||||||
<Button onClick={work}>Work</Button>
|
<Button onClick={work}>Work</Button>
|
||||||
|
|
||||||
<Button onClick={() => setQuitOpen(true)}>Quit</Button>
|
<Button onClick={() => setQuitOpen(true)}>Quit</Button>
|
||||||
<QuitJobModal
|
<QuitJobModal
|
||||||
locName={props.locName}
|
locName={props.locName}
|
||||||
@ -236,9 +240,10 @@ export function CompanyLocation(props: IProps): React.ReactElement {
|
|||||||
open={quitOpen}
|
open={quitOpen}
|
||||||
onClose={() => setQuitOpen(false)}
|
onClose={() => setQuitOpen(false)}
|
||||||
/>
|
/>
|
||||||
</>
|
</Box>
|
||||||
)}
|
)
|
||||||
<br />
|
|
||||||
|
}
|
||||||
{company.hasAgentPositions() && (
|
{company.hasAgentPositions() && (
|
||||||
<ApplyToJobButton
|
<ApplyToJobButton
|
||||||
company={company}
|
company={company}
|
||||||
@ -328,6 +333,7 @@ export function CompanyLocation(props: IProps): React.ReactElement {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{location.infiltrationData != null && <Button onClick={startInfiltration}>Infiltrate Company</Button>}
|
{location.infiltrationData != null && <Button onClick={startInfiltration}>Infiltrate Company</Button>}
|
||||||
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import { Server } from "../../Server/Server";
|
|||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
import { serverMetadata } from "../../Server/data/servers";
|
import { serverMetadata } from "../../Server/data/servers";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
loc: Location;
|
loc: Location;
|
||||||
@ -56,7 +57,7 @@ export function GymLocation(props: IProps): React.ReactElement {
|
|||||||
const cost = CONSTANTS.ClassGymBaseCost * calculateCost();
|
const cost = CONSTANTS.ClassGymBaseCost * calculateCost();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box sx={{ display: 'grid', width: 'fit-content' }}>
|
||||||
<Button onClick={trainStrength}>
|
<Button onClick={trainStrength}>
|
||||||
Train Strength (<Money money={cost} player={props.p} /> / sec)
|
Train Strength (<Money money={cost} player={props.p} /> / sec)
|
||||||
</Button>
|
</Button>
|
||||||
@ -72,6 +73,6 @@ export function GymLocation(props: IProps): React.ReactElement {
|
|||||||
<Button onClick={trainAgility}>
|
<Button onClick={trainAgility}>
|
||||||
Train Agility (<Money money={cost} player={props.p} /> / sec)
|
Train Agility (<Money money={cost} player={props.p} /> / sec)
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import { Crimes } from "../../Crime/Crimes";
|
|||||||
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
export function SlumsLocation(): React.ReactElement {
|
export function SlumsLocation(): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
@ -113,73 +114,61 @@ export function SlumsLocation(): React.ReactElement {
|
|||||||
const heistChance = Crimes.Heist.successRate(player);
|
const heistChance = Crimes.Heist.successRate(player);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box sx={{ display: 'grid', width: 'fit-content' }}>
|
||||||
<Tooltip title={<>Attempt to shoplift from a low-end retailer</>}>
|
<Tooltip title={<>Attempt to shoplift from a low-end retailer</>}>
|
||||||
<Button onClick={shoplift}>
|
<Button onClick={shoplift}>
|
||||||
Shoplift ({numeralWrapper.formatPercentage(shopliftChance)} chance of success)
|
Shoplift ({numeralWrapper.formatPercentage(shopliftChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to commit armed robbery on a high-end store</>}>
|
<Tooltip title={<>Attempt to commit armed robbery on a high-end store</>}>
|
||||||
<Button onClick={robStore}>
|
<Button onClick={robStore}>
|
||||||
Rob store ({numeralWrapper.formatPercentage(robStoreChance)} chance of success)
|
Rob store ({numeralWrapper.formatPercentage(robStoreChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to mug a random person on the street</>}>
|
<Tooltip title={<>Attempt to mug a random person on the street</>}>
|
||||||
<Button onClick={mug}>Mug someone ({numeralWrapper.formatPercentage(mugChance)} chance of success)</Button>
|
<Button onClick={mug}>Mug someone ({numeralWrapper.formatPercentage(mugChance)} chance of success)</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to rob property from someone's house</>}>
|
<Tooltip title={<>Attempt to rob property from someone's house</>}>
|
||||||
<Button onClick={larceny}>Larceny ({numeralWrapper.formatPercentage(larcenyChance)} chance of success)</Button>
|
<Button onClick={larceny}>Larceny ({numeralWrapper.formatPercentage(larcenyChance)} chance of success)</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to deal drugs</>}>
|
<Tooltip title={<>Attempt to deal drugs</>}>
|
||||||
<Button onClick={dealDrugs}>
|
<Button onClick={dealDrugs}>
|
||||||
Deal Drugs ({numeralWrapper.formatPercentage(drugsChance)} chance of success)
|
Deal Drugs ({numeralWrapper.formatPercentage(drugsChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to forge corporate bonds</>}>
|
<Tooltip title={<>Attempt to forge corporate bonds</>}>
|
||||||
<Button onClick={bondForgery}>
|
<Button onClick={bondForgery}>
|
||||||
Bond Forgery ({numeralWrapper.formatPercentage(bondChance)} chance of success)
|
Bond Forgery ({numeralWrapper.formatPercentage(bondChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to smuggle illegal arms into the city</>}>
|
<Tooltip title={<>Attempt to smuggle illegal arms into the city</>}>
|
||||||
<Button onClick={traffickArms}>
|
<Button onClick={traffickArms}>
|
||||||
Traffick illegal Arms ({numeralWrapper.formatPercentage(armsChance)} chance of success)
|
Traffick illegal Arms ({numeralWrapper.formatPercentage(armsChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to murder a random person on the street</>}>
|
<Tooltip title={<>Attempt to murder a random person on the street</>}>
|
||||||
<Button onClick={homicide}>
|
<Button onClick={homicide}>
|
||||||
Homicide ({numeralWrapper.formatPercentage(homicideChance)} chance of success)
|
Homicide ({numeralWrapper.formatPercentage(homicideChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to commit grand theft auto</>}>
|
<Tooltip title={<>Attempt to commit grand theft auto</>}>
|
||||||
<Button onClick={grandTheftAuto}>
|
<Button onClick={grandTheftAuto}>
|
||||||
Grand theft Auto ({numeralWrapper.formatPercentage(gtaChance)} chance of success)
|
Grand theft Auto ({numeralWrapper.formatPercentage(gtaChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to kidnap and ransom a high-profile-target</>}>
|
<Tooltip title={<>Attempt to kidnap and ransom a high-profile-target</>}>
|
||||||
<Button onClick={kidnap}>
|
<Button onClick={kidnap}>
|
||||||
Kidnap and Ransom ({numeralWrapper.formatPercentage(kidnapChance)} chance of success)
|
Kidnap and Ransom ({numeralWrapper.formatPercentage(kidnapChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to assassinate a high-profile target</>}>
|
<Tooltip title={<>Attempt to assassinate a high-profile target</>}>
|
||||||
<Button onClick={assassinate}>
|
<Button onClick={assassinate}>
|
||||||
Assassinate ({numeralWrapper.formatPercentage(assassinateChance)} chance of success)
|
Assassinate ({numeralWrapper.formatPercentage(assassinateChance)} chance of success)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={<>Attempt to pull off the ultimate heist</>}>
|
<Tooltip title={<>Attempt to pull off the ultimate heist</>}>
|
||||||
<Button onClick={heist}>Heist ({numeralWrapper.formatPercentage(heistChance)} chance of success)</Button>
|
<Button onClick={heist}>Heist ({numeralWrapper.formatPercentage(heistChance)} chance of success)</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
</Box>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
|||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
const text = inBladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
|
const text = inBladeburner ? "Enter Bladeburner Headquarters" : "Apply to Bladeburner Division";
|
||||||
return <Button onClick={handleBladeburner}>{text}</Button>;
|
return <><br/><Button onClick={handleBladeburner}>{text}</Button></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNoodleBar(): React.ReactElement {
|
function renderNoodleBar(): React.ReactElement {
|
||||||
|
@ -18,6 +18,7 @@ import { Money } from "../../ui/React/Money";
|
|||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { PurchaseServerModal } from "./PurchaseServerModal";
|
import { PurchaseServerModal } from "./PurchaseServerModal";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
interface IServerProps {
|
interface IServerProps {
|
||||||
ram: number;
|
ram: number;
|
||||||
@ -70,7 +71,9 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br />
|
||||||
|
<Box sx={{ display: 'grid', width: 'fit-content' }}>
|
||||||
{purchaseServerButtons}
|
{purchaseServerButtons}
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<Typography>
|
<Typography>
|
||||||
<i>"You can order bigger servers via scripts. We don't take custom orders in person."</i>
|
<i>"You can order bigger servers via scripts. We don't take custom orders in person."</i>
|
||||||
|
@ -15,6 +15,7 @@ import { Server } from "../../Server/Server";
|
|||||||
|
|
||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
loc: Location;
|
loc: Location;
|
||||||
@ -72,45 +73,40 @@ export function UniversityLocation(props: IProps): React.ReactElement {
|
|||||||
const earnCharismaExpTooltip = `Gain charisma experience!`;
|
const earnCharismaExpTooltip = `Gain charisma experience!`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box sx={{ display: 'grid', width: 'fit-content' }}>
|
||||||
<Tooltip title={earnHackingExpTooltip}>
|
<Tooltip title={earnHackingExpTooltip}>
|
||||||
<Button onClick={study}>Study Computer Science (free)</Button>
|
<Button onClick={study}>Study Computer Science (free)</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={earnHackingExpTooltip}>
|
<Tooltip title={earnHackingExpTooltip}>
|
||||||
<Button onClick={dataStructures}>
|
<Button onClick={dataStructures}>
|
||||||
Take Data Structures course (
|
Take Data Structures course (
|
||||||
<Money money={dataStructuresCost} player={player} /> / sec)
|
<Money money={dataStructuresCost} player={player} /> / sec)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={earnHackingExpTooltip}>
|
<Tooltip title={earnHackingExpTooltip}>
|
||||||
<Button onClick={networks}>
|
<Button onClick={networks}>
|
||||||
Take Networks course (
|
Take Networks course (
|
||||||
<Money money={networksCost} player={player} /> / sec)
|
<Money money={networksCost} player={player} /> / sec)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={earnHackingExpTooltip}>
|
<Tooltip title={earnHackingExpTooltip}>
|
||||||
<Button onClick={algorithms}>
|
<Button onClick={algorithms}>
|
||||||
Take Algorithms course (
|
Take Algorithms course (
|
||||||
<Money money={algorithmsCost} player={player} /> / sec)
|
<Money money={algorithmsCost} player={player} /> / sec)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={earnCharismaExpTooltip}>
|
<Tooltip title={earnCharismaExpTooltip}>
|
||||||
<Button onClick={management}>
|
<Button onClick={management}>
|
||||||
Take Management course (
|
Take Management course (
|
||||||
<Money money={managementCost} player={player} /> / sec)
|
<Money money={managementCost} player={player} /> / sec)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
<Tooltip title={earnCharismaExpTooltip}>
|
<Tooltip title={earnCharismaExpTooltip}>
|
||||||
<Button onClick={leadership}>
|
<Button onClick={leadership}>
|
||||||
Take Leadership course (
|
Take Leadership course (
|
||||||
<Money money={leadershipCost} player={player} /> / sec)
|
<Money money={leadershipCost} player={player} /> / sec)
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -364,6 +364,9 @@ export const RamCosts: IMap<any> = {
|
|||||||
getTheme: 0,
|
getTheme: 0,
|
||||||
setTheme: 0,
|
setTheme: 0,
|
||||||
resetTheme: 0,
|
resetTheme: 0,
|
||||||
|
getStyles: 0,
|
||||||
|
setStyles: 0,
|
||||||
|
resetStyles: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
heart: {
|
heart: {
|
||||||
|
@ -33,9 +33,9 @@ export class WorkerScript {
|
|||||||
delay: number | null = null;
|
delay: number | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the Promise resolve() function for when the script is "blocked" by an async op
|
* Holds the Promise reject() function while the script is "blocked" by an async op
|
||||||
*/
|
*/
|
||||||
delayResolve?: () => void;
|
delayReject?: (reason?: any) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores names of all functions that have logging disabled
|
* Stores names of all functions that have logging disabled
|
||||||
|
@ -138,8 +138,8 @@ function killNetscriptDelay(workerScript: WorkerScript): void {
|
|||||||
if (workerScript instanceof WorkerScript) {
|
if (workerScript instanceof WorkerScript) {
|
||||||
if (workerScript.delay) {
|
if (workerScript.delay) {
|
||||||
clearTimeout(workerScript.delay);
|
clearTimeout(workerScript.delay);
|
||||||
if (workerScript.delayResolve) {
|
if (workerScript.delayReject) {
|
||||||
workerScript.delayResolve();
|
workerScript.delayReject(workerScript);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,17 @@ import { GetServer } from "./Server/AllServers";
|
|||||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||||
|
|
||||||
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
|
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve, reject) {
|
||||||
workerScript.delay = window.setTimeout(() => {
|
workerScript.delay = window.setTimeout(() => {
|
||||||
workerScript.delay = null;
|
workerScript.delay = null;
|
||||||
|
workerScript.delayReject = undefined;
|
||||||
|
|
||||||
|
if (workerScript.env.stopFlag)
|
||||||
|
reject(workerScript);
|
||||||
|
else
|
||||||
resolve();
|
resolve();
|
||||||
}, time);
|
}, time);
|
||||||
workerScript.delayResolve = resolve;
|
workerScript.delayReject = reject;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,16 +335,13 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
workerScript.log(
|
workerScript.log(
|
||||||
"hack",
|
"hack",
|
||||||
() =>
|
() =>
|
||||||
`Executing ${hostname} in ${convertTimeMsToTimeElapsedString(
|
`Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(
|
||||||
hackingTime * 1000,
|
hackingTime * 1000,
|
||||||
true,
|
true,
|
||||||
)} (t=${numeralWrapper.formatThreads(threads)})`,
|
)} (t=${numeralWrapper.formatThreads(threads)})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return netscriptDelay(hackingTime * 1000, workerScript).then(function () {
|
return netscriptDelay(hackingTime * 1000, workerScript).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
const hackChance = calculateHackingChance(server, Player);
|
const hackChance = calculateHackingChance(server, Player);
|
||||||
const rand = Math.random();
|
const rand = Math.random();
|
||||||
let expGainedOnSuccess = calculateHackingExpGain(server, Player) * threads;
|
let expGainedOnSuccess = calculateHackingExpGain(server, Player) * threads;
|
||||||
@ -352,7 +349,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
if (rand < hackChance) {
|
if (rand < hackChance) {
|
||||||
// Success!
|
// Success!
|
||||||
const percentHacked = calculatePercentMoneyHacked(server, Player);
|
const percentHacked = calculatePercentMoneyHacked(server, Player);
|
||||||
let maxThreadNeeded = Math.ceil((1 / percentHacked) * (server.moneyAvailable / server.moneyMax));
|
let maxThreadNeeded = Math.ceil(1 / percentHacked);
|
||||||
if (isNaN(maxThreadNeeded)) {
|
if (isNaN(maxThreadNeeded)) {
|
||||||
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value
|
// Server has a 'max money' of 0 (probably). We'll set this to an arbitrarily large value
|
||||||
maxThreadNeeded = 1e6;
|
maxThreadNeeded = 1e6;
|
||||||
@ -432,8 +429,10 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
throw makeRuntimeErrorMsg(funcName, `${argName} should be a string`);
|
throw makeRuntimeErrorMsg(funcName, `${argName} should be a string`);
|
||||||
},
|
},
|
||||||
number: (funcName: string, argName: string, v: any): number => {
|
number: (funcName: string, argName: string, v: any): number => {
|
||||||
|
if (!isNaN(v)) {
|
||||||
if (typeof v === "number") return v;
|
if (typeof v === "number") return v;
|
||||||
if (!isNaN(v) && !isNaN(parseFloat(v))) return parseFloat(v);
|
if (!isNaN(parseFloat(v))) return parseFloat(v);
|
||||||
|
}
|
||||||
throw makeRuntimeErrorMsg(funcName, `${argName} should be a number`);
|
throw makeRuntimeErrorMsg(funcName, `${argName} should be a number`);
|
||||||
},
|
},
|
||||||
boolean: (v: any): boolean => {
|
boolean: (v: any): boolean => {
|
||||||
@ -613,9 +612,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
)} (t=${numeralWrapper.formatThreads(threads)}).`,
|
)} (t=${numeralWrapper.formatThreads(threads)}).`,
|
||||||
);
|
);
|
||||||
return netscriptDelay(growTime * 1000, workerScript).then(function () {
|
return netscriptDelay(growTime * 1000, workerScript).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
|
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
|
||||||
processSingleServerGrowth(server, threads, Player, host.cpuCores);
|
processSingleServerGrowth(server, threads, Player, host.cpuCores);
|
||||||
const moneyAfter = server.moneyAvailable;
|
const moneyAfter = server.moneyAvailable;
|
||||||
@ -684,7 +680,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
)} (t=${numeralWrapper.formatThreads(threads)})`,
|
)} (t=${numeralWrapper.formatThreads(threads)})`,
|
||||||
);
|
);
|
||||||
return netscriptDelay(weakenTime * 1000, workerScript).then(function () {
|
return netscriptDelay(weakenTime * 1000, workerScript).then(function () {
|
||||||
if (workerScript.env.stopFlag) return Promise.reject(workerScript);
|
|
||||||
const host = GetServer(workerScript.hostname);
|
const host = GetServer(workerScript.hostname);
|
||||||
if (host === null) {
|
if (host === null) {
|
||||||
workerScript.log("weaken", () => "Server is null, did it die?");
|
workerScript.log("weaken", () => "Server is null, did it die?");
|
||||||
@ -697,8 +692,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
workerScript.log(
|
workerScript.log(
|
||||||
"weaken",
|
"weaken",
|
||||||
() =>
|
() =>
|
||||||
`'${server.hostname}' security level weakened to ${
|
`'${server.hostname}' security level weakened to ${server.hackDifficulty
|
||||||
server.hackDifficulty
|
|
||||||
}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`,
|
}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`,
|
||||||
);
|
);
|
||||||
workerScript.scriptRef.onlineExpGained += expGain;
|
workerScript.scriptRef.onlineExpGained += expGain;
|
||||||
@ -2011,7 +2005,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
|
|
||||||
return calculateGrowTime(server, Player) * 1000;
|
return calculateGrowTime(server, Player) * 1000;
|
||||||
},
|
},
|
||||||
getWeakenTime: function (hostname: any): any {
|
getWeakenTime: function (hostname: any = workerScript.hostname): any {
|
||||||
updateDynamicRam("getWeakenTime", getRamCost(Player, "getWeakenTime"));
|
updateDynamicRam("getWeakenTime", getRamCost(Player, "getWeakenTime"));
|
||||||
const server = safeGetServer(hostname, "getWeakenTime");
|
const server = safeGetServer(hostname, "getWeakenTime");
|
||||||
if (!(server instanceof Server)) {
|
if (!(server instanceof Server)) {
|
||||||
|
@ -53,23 +53,22 @@ export function NetscriptCodingContract(
|
|||||||
const serv = helper.getServer(hostname, "codingcontract.attempt");
|
const serv = helper.getServer(hostname, "codingcontract.attempt");
|
||||||
if (contract.isSolution(answer)) {
|
if (contract.isSolution(answer)) {
|
||||||
const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
|
const reward = player.gainCodingContractReward(creward, contract.getDifficulty());
|
||||||
workerScript.log("attempt", () => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
|
workerScript.log("codingcontract.attempt", () => `Successfully completed Coding Contract '${filename}'. Reward: ${reward}`);
|
||||||
serv.removeContract(filename);
|
serv.removeContract(filename);
|
||||||
return returnReward ? reward : true;
|
return returnReward ? reward : true;
|
||||||
} else {
|
} else {
|
||||||
++contract.tries;
|
++contract.tries;
|
||||||
if (contract.tries >= contract.getMaxNumTries()) {
|
if (contract.tries >= contract.getMaxNumTries()) {
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
"attempt",
|
"codingcontract.attempt",
|
||||||
() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`,
|
() => `Coding Contract attempt '${filename}' failed. Contract is now self-destructing`,
|
||||||
);
|
);
|
||||||
serv.removeContract(filename);
|
serv.removeContract(filename);
|
||||||
} else {
|
} else {
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
"attempt",
|
"codingcontract.attempt",
|
||||||
() =>
|
() =>
|
||||||
`Coding Contract attempt '${filename}' failed. ${
|
`Coding Contract attempt '${filename}' failed. ${contract.getMaxNumTries() - contract.tries
|
||||||
contract.getMaxNumTries() - contract.tries
|
|
||||||
} attempts remaining.`,
|
} attempts remaining.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -311,9 +311,6 @@ export function NetscriptCorporation(
|
|||||||
const job = helper.string("assignJob", "job", ajob);
|
const job = helper.string("assignJob", "job", ajob);
|
||||||
const employee = getEmployee(divisionName, cityName, employeeName);
|
const employee = getEmployee(divisionName, cityName, employeeName);
|
||||||
return netscriptDelay(1000, workerScript).then(function () {
|
return netscriptDelay(1000, workerScript).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
return Promise.resolve(AssignJob(employee, job));
|
return Promise.resolve(AssignJob(employee, job));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -344,9 +341,6 @@ export function NetscriptCorporation(
|
|||||||
(60 * 1000) / (player.hacking_speed_mult * calculateIntelligenceBonus(player.intelligence, 1)),
|
(60 * 1000) / (player.hacking_speed_mult * calculateIntelligenceBonus(player.intelligence, 1)),
|
||||||
workerScript,
|
workerScript,
|
||||||
).then(function () {
|
).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
return Promise.resolve(ThrowParty(corporation, office, costPerEmployee));
|
return Promise.resolve(ThrowParty(corporation, office, costPerEmployee));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -359,9 +353,6 @@ export function NetscriptCorporation(
|
|||||||
(60 * 1000) / (player.hacking_speed_mult * calculateIntelligenceBonus(player.intelligence, 1)),
|
(60 * 1000) / (player.hacking_speed_mult * calculateIntelligenceBonus(player.intelligence, 1)),
|
||||||
workerScript,
|
workerScript,
|
||||||
).then(function () {
|
).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
return Promise.resolve(BuyCoffee(corporation, getDivision(divisionName), getOffice(divisionName, cityName)));
|
return Promise.resolve(BuyCoffee(corporation, getDivision(divisionName), getOffice(divisionName, cityName)));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -111,7 +111,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
|
|||||||
}
|
}
|
||||||
const node = getHacknetNode(i, "upgradeCache");
|
const node = getHacknetNode(i, "upgradeCache");
|
||||||
if (!(node instanceof HacknetServer)) {
|
if (!(node instanceof HacknetServer)) {
|
||||||
workerScript.log("upgradeCache", () => "Can only be called on hacknet servers");
|
workerScript.log("hacknet.upgradeCache", () => "Can only be called on hacknet servers");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const res = purchaseCacheUpgrade(player, node, n);
|
const res = purchaseCacheUpgrade(player, node, n);
|
||||||
@ -138,7 +138,7 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
|
|||||||
}
|
}
|
||||||
const node = getHacknetNode(i, "upgradeCache");
|
const node = getHacknetNode(i, "upgradeCache");
|
||||||
if (!(node instanceof HacknetServer)) {
|
if (!(node instanceof HacknetServer)) {
|
||||||
workerScript.log("getCacheUpgradeCost", () => "Can only be called on hacknet servers");
|
workerScript.log("hacknet.getCacheUpgradeCost", () => "Can only be called on hacknet servers");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return node.calculateCacheUpgradeCost(n);
|
return node.calculateCacheUpgradeCost(n);
|
||||||
|
@ -611,9 +611,6 @@ export function NetscriptSingularity(
|
|||||||
);
|
);
|
||||||
|
|
||||||
return netscriptDelay(installTime, workerScript).then(function () {
|
return netscriptDelay(installTime, workerScript).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
workerScript.log("installBackdoor", () => `Successfully installed backdoor on '${server.hostname}'`);
|
workerScript.log("installBackdoor", () => `Successfully installed backdoor on '${server.hostname}'`);
|
||||||
|
|
||||||
server.backdoorInstalled = true;
|
server.backdoorInstalled = true;
|
||||||
|
@ -38,9 +38,6 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
|
|||||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.charge", `No fragment with root (${rootX}, ${rootY}).`);
|
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.charge", `No fragment with root (${rootX}, ${rootY}).`);
|
||||||
const time = staneksGift.inBonus() ? 200 : 1000;
|
const time = staneksGift.inBonus() ? 200 : 1000;
|
||||||
return netscriptDelay(time, workerScript).then(function () {
|
return netscriptDelay(time, workerScript).then(function () {
|
||||||
if (workerScript.env.stopFlag) {
|
|
||||||
return Promise.reject(workerScript);
|
|
||||||
}
|
|
||||||
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
const charge = staneksGift.charge(player, fragment, workerScript.scriptRef.threads);
|
||||||
workerScript.log("stanek.charge", () => `Charged fragment for ${charge} charge.`);
|
workerScript.log("stanek.charge", () => `Charged fragment for ${charge} charge.`);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
@ -2,10 +2,11 @@ import { INetscriptHelper } from "./INetscriptHelper";
|
|||||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { getRamCost } from "../Netscript/RamCostGenerator";
|
import { getRamCost } from "../Netscript/RamCostGenerator";
|
||||||
import { UserInterface as IUserInterface, UserInterfaceTheme } from "../ScriptEditor/NetscriptDefinitions";
|
import { IStyleSettings, UserInterface as IUserInterface, UserInterfaceTheme } from "../ScriptEditor/NetscriptDefinitions";
|
||||||
import { Settings } from "../Settings/Settings";
|
import { Settings } from "../Settings/Settings";
|
||||||
import { ThemeEvents } from "../ui/React/Theme";
|
import { ThemeEvents } from "../ui/React/Theme";
|
||||||
import { defaultTheme } from "../Settings/Themes";
|
import { defaultTheme } from "../Settings/Themes";
|
||||||
|
import { defaultStyles } from "../Settings/Styles";
|
||||||
|
|
||||||
export function NetscriptUserInterface(
|
export function NetscriptUserInterface(
|
||||||
player: IPlayer,
|
player: IPlayer,
|
||||||
@ -18,6 +19,11 @@ export function NetscriptUserInterface(
|
|||||||
return { ...Settings.theme };
|
return { ...Settings.theme };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getStyles: function (): IStyleSettings {
|
||||||
|
helper.updateDynamicRam("getStyles", getRamCost(player, "ui", "getStyles"));
|
||||||
|
return { ...Settings.styles };
|
||||||
|
},
|
||||||
|
|
||||||
setTheme: function (newTheme: UserInterfaceTheme): void {
|
setTheme: function (newTheme: UserInterfaceTheme): void {
|
||||||
helper.updateDynamicRam("setTheme", getRamCost(player, "ui", "setTheme"));
|
helper.updateDynamicRam("setTheme", getRamCost(player, "ui", "setTheme"));
|
||||||
const hex = /^(#)((?:[A-Fa-f0-9]{3}){1,2})$/;
|
const hex = /^(#)((?:[A-Fa-f0-9]{3}){1,2})$/;
|
||||||
@ -43,11 +49,41 @@ export function NetscriptUserInterface(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setStyles: function (newStyles: IStyleSettings): void {
|
||||||
|
helper.updateDynamicRam("setStyles", getRamCost(player, "ui", "setStyles"));
|
||||||
|
|
||||||
|
const currentStyles = {...Settings.styles}
|
||||||
|
const errors: string[] = [];
|
||||||
|
for (const key of Object.keys(newStyles)) {
|
||||||
|
if (!((currentStyles as any)[key])) {
|
||||||
|
// Invalid key
|
||||||
|
errors.push(`Invalid key "${key}"`);
|
||||||
|
} else {
|
||||||
|
(currentStyles as any)[key] = (newStyles as any)[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length === 0) {
|
||||||
|
Object.assign(Settings.styles, currentStyles);
|
||||||
|
ThemeEvents.emit();
|
||||||
|
workerScript.log("ui.setStyles", () => `Successfully set styles`);
|
||||||
|
} else {
|
||||||
|
workerScript.log("ui.setStyles", () => `Failed to set styles. Errors: ${errors.join(', ')}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
resetTheme: function (): void {
|
resetTheme: function (): void {
|
||||||
helper.updateDynamicRam("resetTheme", getRamCost(player, "ui", "resetTheme"));
|
helper.updateDynamicRam("resetTheme", getRamCost(player, "ui", "resetTheme"));
|
||||||
Settings.theme = defaultTheme;
|
Settings.theme = { ...defaultTheme };
|
||||||
ThemeEvents.emit();
|
ThemeEvents.emit();
|
||||||
workerScript.log("ui.resetTheme", () => `Reinitialized theme to default`);
|
workerScript.log("ui.resetTheme", () => `Reinitialized theme to default`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
resetStyles: function (): void {
|
||||||
|
helper.updateDynamicRam("resetStyles", getRamCost(player, "ui", "resetStyles"));
|
||||||
|
Settings.styles = { ...defaultStyles };
|
||||||
|
ThemeEvents.emit();
|
||||||
|
workerScript.log("ui.resetStyles", () => `Reinitialized styles to default`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -946,11 +946,6 @@ export function workForFaction(this: IPlayer, numCycles: number): boolean {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let favorMult = 1 + faction.favor / 100;
|
|
||||||
if (isNaN(favorMult)) {
|
|
||||||
favorMult = 1;
|
|
||||||
}
|
|
||||||
this.workRepGainRate *= favorMult;
|
|
||||||
this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain;
|
this.workRepGainRate *= BitNodeMultipliers.FactionWorkRepGain;
|
||||||
|
|
||||||
//Cap the number of cycles being processed to whatever would put you at limit (20 hours)
|
//Cap the number of cycles being processed to whatever would put you at limit (20 hours)
|
||||||
|
@ -161,8 +161,6 @@ export function prestigeAugmentation(): void {
|
|||||||
resetPidCounter();
|
resetPidCounter();
|
||||||
ProgramsSeen.splice(0, ProgramsSeen.length);
|
ProgramsSeen.splice(0, ProgramsSeen.length);
|
||||||
InvitationsSeen.splice(0, InvitationsSeen.length);
|
InvitationsSeen.splice(0, InvitationsSeen.length);
|
||||||
|
|
||||||
Router.clearHistory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prestige by destroying Bit Node and gaining a Source File
|
// Prestige by destroying Bit Node and gaining a Source File
|
||||||
@ -308,7 +306,5 @@ export function prestigeSourceFile(flume: boolean): void {
|
|||||||
// Gain int exp
|
// Gain int exp
|
||||||
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300);
|
if (SourceFileFlags[5] !== 0 && !flume) Player.gainIntelligenceExp(300);
|
||||||
|
|
||||||
Router.clearHistory();
|
|
||||||
|
|
||||||
resetPidCounter();
|
resetPidCounter();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { getAvailableCreatePrograms } from "../ProgramHelpers";
|
import { getAvailableCreatePrograms } from "../ProgramHelpers";
|
||||||
|
|
||||||
import { Tooltip, Typography } from "@mui/material";
|
import { Box, Tooltip, Typography } from "@mui/material";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
export const ProgramsSeen: string[] = [];
|
export const ProgramsSeen: string[] = [];
|
||||||
@ -38,6 +38,7 @@ export function ProgramsRoot(): React.ReactElement {
|
|||||||
time. Your progress will be saved and you can continue later.
|
time. Your progress will be saved and you can continue later.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'grid', width: 'fit-content' }}>
|
||||||
{programs.map((program) => {
|
{programs.map((program) => {
|
||||||
const create = program.create;
|
const create = program.create;
|
||||||
if (create === null) return <></>;
|
if (create === null) return <></>;
|
||||||
@ -55,10 +56,10 @@ export function ProgramsRoot(): React.ReactElement {
|
|||||||
{program.name}
|
{program.name}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<br />
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ export class Script {
|
|||||||
*/
|
*/
|
||||||
saveScript(player: IPlayer, filename: string, code: string, hostname: string, otherScripts: Script[]): void {
|
saveScript(player: IPlayer, filename: string, code: string, hostname: string, otherScripts: Script[]): void {
|
||||||
// Update code and filename
|
// Update code and filename
|
||||||
this.code = code.replace(/^\s+|\s+$/g, "");
|
this.code = Script.formatCode(code);
|
||||||
|
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.server = hostname;
|
this.server = hostname;
|
||||||
@ -158,6 +158,15 @@ export class Script {
|
|||||||
s.rehash();
|
s.rehash();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats code: Removes the starting & trailing whitespace
|
||||||
|
* @param {string} code - The code to format
|
||||||
|
* @returns The formatted code
|
||||||
|
*/
|
||||||
|
static formatCode(code: string): string {
|
||||||
|
return code.replace(/^\s+|\s+$/g, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reviver.constructors.Script = Script;
|
Reviver.constructors.Script = Script;
|
||||||
|
230
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
230
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -109,32 +109,6 @@ interface RunningScript {
|
|||||||
threads: number;
|
threads: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface of a netscript port
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface IPort {
|
|
||||||
/** write data to the port and removes and returns first element if full */
|
|
||||||
write: (value: any) => any;
|
|
||||||
/** add data to port if not full.
|
|
||||||
* @returns true if added and false if full and not added */
|
|
||||||
tryWrite: (value: any) => boolean;
|
|
||||||
/** reads and removes first element from port
|
|
||||||
* if no data in port returns "NULL PORT DATA"
|
|
||||||
*/
|
|
||||||
read: () => any;
|
|
||||||
/** reads first element without removing it from port
|
|
||||||
* if no data in port returns "NULL PORT DATA"
|
|
||||||
*/
|
|
||||||
peek: () => any;
|
|
||||||
/** check if port is full */
|
|
||||||
full: () => boolean;
|
|
||||||
/** check if port is empty */
|
|
||||||
empty: () => boolean;
|
|
||||||
/** removes all data from port */
|
|
||||||
clear: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data representing the internal values of a crime.
|
* Data representing the internal values of a crime.
|
||||||
* @public
|
* @public
|
||||||
@ -280,6 +254,24 @@ export interface AugmentPair {
|
|||||||
cost: number;
|
cost: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export enum PositionTypes {
|
||||||
|
Long = "L",
|
||||||
|
Short = "S",
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export enum OrderTypes {
|
||||||
|
LimitBuy = "Limit Buy Order",
|
||||||
|
LimitSell = "Limit Sell Order",
|
||||||
|
StopBuy = "Stop Buy Order",
|
||||||
|
StopSell = "Stop Sell Order",
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value in map of {@link StockOrder}
|
* Value in map of {@link StockOrder}
|
||||||
* @public
|
* @public
|
||||||
@ -290,17 +282,18 @@ export interface StockOrderObject {
|
|||||||
/** Price per share */
|
/** Price per share */
|
||||||
price: number;
|
price: number;
|
||||||
/** Order type */
|
/** Order type */
|
||||||
type: string;
|
type: OrderTypes;
|
||||||
/** Order position */
|
/** Order position */
|
||||||
position: string;
|
position: PositionTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return value of {@link TIX.getOrders | getOrders}
|
* Return value of {@link TIX.getOrders | getOrders}
|
||||||
|
*
|
||||||
|
* Keys are stock symbols, properties are arrays of {@link StockOrderObject}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface StockOrder {
|
export interface StockOrder {
|
||||||
/** Stock Symbol */
|
|
||||||
[key: string]: StockOrderObject[];
|
[key: string]: StockOrderObject[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,6 +481,8 @@ export interface BitNodeMultipliers {
|
|||||||
FourSigmaMarketDataApiCost: number;
|
FourSigmaMarketDataApiCost: number;
|
||||||
/** Influences how much it costs to unlock the stock market's 4S Market Data (NOT API) */
|
/** Influences how much it costs to unlock the stock market's 4S Market Data (NOT API) */
|
||||||
FourSigmaMarketDataCost: number;
|
FourSigmaMarketDataCost: number;
|
||||||
|
/** Influences the respect gain and money gain of your gang. */
|
||||||
|
GangSoftcap: number;
|
||||||
/** Influences the experienced gained when hacking a server. */
|
/** Influences the experienced gained when hacking a server. */
|
||||||
HackExpGain: number;
|
HackExpGain: number;
|
||||||
/** Influences how quickly the player's hacking level (not experience) scales */
|
/** Influences how quickly the player's hacking level (not experience) scales */
|
||||||
@ -508,10 +503,14 @@ export interface BitNodeMultipliers {
|
|||||||
PurchasedServerLimit: number;
|
PurchasedServerLimit: number;
|
||||||
/** Influences the maximum allowed RAM for a purchased server */
|
/** Influences the maximum allowed RAM for a purchased server */
|
||||||
PurchasedServerMaxRam: number;
|
PurchasedServerMaxRam: number;
|
||||||
|
/** Influences cost of any purchased server at or above 128GB */
|
||||||
|
PurchasedServerSoftCap: number;
|
||||||
/** Influences the minimum favor the player must have with a faction before they can donate to gain rep. */
|
/** Influences the minimum favor the player must have with a faction before they can donate to gain rep. */
|
||||||
RepToDonateToFaction: number;
|
RepToDonateToFaction: number;
|
||||||
/** Influences how much money can be stolen from a server when a script performs a hack against it. */
|
/** Influences how much the money on a server can be reduced when a script performs a hack against it. */
|
||||||
ScriptHackMoney: number;
|
ScriptHackMoney: number;
|
||||||
|
/** Influences how much of the money stolen by a scripted hack will be added to the player's money. */
|
||||||
|
ScriptHackMoneyGain: number;
|
||||||
/** Influences the growth percentage per cycle against a server. */
|
/** Influences the growth percentage per cycle against a server. */
|
||||||
ServerGrowthRate: number;
|
ServerGrowthRate: number;
|
||||||
/** Influences the maxmimum money that a server can grow to. */
|
/** Influences the maxmimum money that a server can grow to. */
|
||||||
@ -524,6 +523,12 @@ export interface BitNodeMultipliers {
|
|||||||
ServerWeakenRate: number;
|
ServerWeakenRate: number;
|
||||||
/** Influences how quickly the player's strength level (not exp) scales */
|
/** Influences how quickly the player's strength level (not exp) scales */
|
||||||
StrengthLevelMultiplier: number;
|
StrengthLevelMultiplier: number;
|
||||||
|
/** Influences the power of the gift */
|
||||||
|
StaneksGiftPowerMultiplier: number;
|
||||||
|
/** Influences the size of the gift */
|
||||||
|
StaneksGiftExtraSize: number;
|
||||||
|
/** Influences the hacking skill required to backdoor the world daemon. */
|
||||||
|
WorldDaemonDifficulty: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -535,9 +540,9 @@ export interface NodeStats {
|
|||||||
name: string;
|
name: string;
|
||||||
/** Node's level */
|
/** Node's level */
|
||||||
level: number;
|
level: number;
|
||||||
/** Node's RAM */
|
/** Node's RAM (GB) */
|
||||||
ram: number;
|
ram: number;
|
||||||
/** Node's used RAM */
|
/** Node's used RAM (GB) */
|
||||||
ramUsed: number;
|
ramUsed: number;
|
||||||
/** Node's number of cores */
|
/** Node's number of cores */
|
||||||
cores: number;
|
cores: number;
|
||||||
@ -956,6 +961,78 @@ export interface SleeveTask {
|
|||||||
factionWorkType: string;
|
factionWorkType: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object representing a port. A port is a serialized queue.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface NetscriptPort {
|
||||||
|
/**
|
||||||
|
* Write data to a port.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
*
|
||||||
|
* @returns The data popped off the queue if it was full.
|
||||||
|
*/
|
||||||
|
write(value: string|number): null|string|number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to write data to the port.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
*
|
||||||
|
* @returns True if the data was added to the port, false if the port was full
|
||||||
|
*/
|
||||||
|
tryWrite(value: string|number): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shift an element out of the port.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
*
|
||||||
|
* This function will remove the first element from the port and return it.
|
||||||
|
* If the port is empty, then the string “NULL PORT DATA” will be returned.
|
||||||
|
* @returns the data read.
|
||||||
|
*/
|
||||||
|
read(): string|number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the first element from the port without removing it.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
*
|
||||||
|
* This function is used to peek at the data from a port. It returns the
|
||||||
|
* first element in the specified port without removing that element. If
|
||||||
|
* the port is empty, the string “NULL PORT DATA” will be returned.
|
||||||
|
* @returns the data read
|
||||||
|
*/
|
||||||
|
peek(): string|number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the port is full.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
*
|
||||||
|
* @returns true if the port is full, otherwise false
|
||||||
|
*/
|
||||||
|
full(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the port is empty.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
*
|
||||||
|
* @returns true if the port is empty, otherwise false
|
||||||
|
*/
|
||||||
|
empty(): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empties all data from the port.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0 GB
|
||||||
|
*/
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stock market API
|
* Stock market API
|
||||||
* @public
|
* @public
|
||||||
@ -1213,6 +1290,8 @@ export interface TIX {
|
|||||||
* @remarks
|
* @remarks
|
||||||
* RAM cost: 2.5 GB
|
* RAM cost: 2.5 GB
|
||||||
* This is an object containing information for all the Limit and Stop Orders you have in the stock market.
|
* This is an object containing information for all the Limit and Stop Orders you have in the stock market.
|
||||||
|
* For each symbol you have a position in, the returned object will have a key with that symbol's name.
|
||||||
|
* The object's properties are each an array of {@link StockOrderObject}
|
||||||
* The object has the following structure:
|
* The object has the following structure:
|
||||||
*
|
*
|
||||||
* ```ts
|
* ```ts
|
||||||
@ -1320,7 +1399,7 @@ export interface TIX {
|
|||||||
/**
|
/**
|
||||||
* Singularity API
|
* Singularity API
|
||||||
* @remarks
|
* @remarks
|
||||||
* This API requires Source-File 4 level 1 to use. The RAM cost of all these functions is multiplied by 16/4/1 based on Source-File 4 levels.
|
* This API requires Source-File 4 to use. The RAM cost of all these functions is multiplied by 16/4/1 based on Source-File 4 levels.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface Singularity {
|
export interface Singularity {
|
||||||
@ -3880,6 +3959,36 @@ interface UserInterface {
|
|||||||
* RAM cost: cost: 0 GB
|
* RAM cost: cost: 0 GB
|
||||||
*/
|
*/
|
||||||
resetTheme(): void;
|
resetTheme(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current styles
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: cost: 0 GB
|
||||||
|
*
|
||||||
|
* @returns An object containing the player's styles
|
||||||
|
*/
|
||||||
|
getStyles(): IStyleSettings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current styles
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: cost: 0 GB
|
||||||
|
* @example
|
||||||
|
* Usage example (NS2)
|
||||||
|
* ```ts
|
||||||
|
* const styles = ns.ui.getStyles();
|
||||||
|
* styles.fontFamily = 'Comic Sans Ms';
|
||||||
|
* ns.ui.setStyles(styles);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
setStyles(newStyles: IStyleSettings): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the player's styles to the default values
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: cost: 0 GB
|
||||||
|
*/
|
||||||
|
resetStyles(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4410,11 +4519,11 @@ export interface NS extends Singularity {
|
|||||||
* //Get logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
|
* //Get logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
|
||||||
* ns.tail("foo.script", "foodnstuff", 1, "test");
|
* ns.tail("foo.script", "foodnstuff", 1, "test");
|
||||||
* ```
|
* ```
|
||||||
* @param fn - Optional. Filename of the script being tailed. If omitted, the current script is tailed.
|
* @param fn - Optional. Filename or PID of the script being tailed. If omitted, the current script is tailed.
|
||||||
* @param host - Optional. Hostname of the script being tailed. Defaults to the server this script is running on. If args are specified, this is not optional.
|
* @param host - Optional. Hostname of the script being tailed. Defaults to the server this script is running on. If args are specified, this is not optional.
|
||||||
* @param args - Arguments for the script being tailed.
|
* @param args - Arguments for the script being tailed.
|
||||||
*/
|
*/
|
||||||
tail(fn?: string, host?: string, ...args: any[]): void;
|
tail(fn?: FilenameOrPID, host?: string, ...args: any[]): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of servers connected to a server.
|
* Get the list of servers connected to a server.
|
||||||
@ -5064,7 +5173,7 @@ export interface NS extends Singularity {
|
|||||||
* const [totalRam, ramUsed] = ns.getServerRam("helios");
|
* const [totalRam, ramUsed] = ns.getServerRam("helios");
|
||||||
* ```
|
* ```
|
||||||
* @param host - Host of target server.
|
* @param host - Host of target server.
|
||||||
* @returns Array with total and used memory on the specified server.
|
* @returns Array with total and used memory on the specified server, in GB.
|
||||||
*/
|
*/
|
||||||
getServerRam(host: string): [number, number];
|
getServerRam(host: string): [number, number];
|
||||||
|
|
||||||
@ -5074,7 +5183,7 @@ export interface NS extends Singularity {
|
|||||||
* RAM cost: 0.05 GB
|
* RAM cost: 0.05 GB
|
||||||
*
|
*
|
||||||
* @param host - Hostname of the target server.
|
* @param host - Hostname of the target server.
|
||||||
* @returns max ram
|
* @returns max ram (GB)
|
||||||
*/
|
*/
|
||||||
getServerMaxRam(host: string): number;
|
getServerMaxRam(host: string): number;
|
||||||
/**
|
/**
|
||||||
@ -5083,7 +5192,7 @@ export interface NS extends Singularity {
|
|||||||
* RAM cost: 0.05 GB
|
* RAM cost: 0.05 GB
|
||||||
*
|
*
|
||||||
* @param host - Hostname of the target server.
|
* @param host - Hostname of the target server.
|
||||||
* @returns used ram
|
* @returns used ram (GB)
|
||||||
*/
|
*/
|
||||||
getServerUsedRam(host: string): number;
|
getServerUsedRam(host: string): number;
|
||||||
|
|
||||||
@ -5157,6 +5266,7 @@ export interface NS extends Singularity {
|
|||||||
* RAM cost: 0.1 GB
|
* RAM cost: 0.1 GB
|
||||||
*
|
*
|
||||||
* Returns a boolean indicating whether the specified script is running on the target server.
|
* Returns a boolean indicating whether the specified script is running on the target server.
|
||||||
|
* If you use a PID instead of a filename, the hostname and args parameters are unnecessary.
|
||||||
* Remember that a script is uniquely identified by both its name and its arguments.
|
* Remember that a script is uniquely identified by both its name and its arguments.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
@ -5183,12 +5293,12 @@ export interface NS extends Singularity {
|
|||||||
* //The function call will return true if there is a script named foo.script running with the arguments 1, 5, and “test” (in that order) on the joesguns server, and false otherwise:
|
* //The function call will return true if there is a script named foo.script running with the arguments 1, 5, and “test” (in that order) on the joesguns server, and false otherwise:
|
||||||
* ns.isRunning("foo.script", "joesguns", 1, 5, "test");
|
* ns.isRunning("foo.script", "joesguns", 1, 5, "test");
|
||||||
* ```
|
* ```
|
||||||
* @param script - Filename of script to check. This is case-sensitive.
|
* @param script - Filename or PID of script to check. This is case-sensitive.
|
||||||
* @param host - Host of target server.
|
* @param host - Host of target server.
|
||||||
* @param args - Arguments to specify/identify which scripts to search for.
|
* @param args - Arguments to specify/identify which scripts to search for.
|
||||||
* @returns True if specified script is running on the target server, and false otherwise.
|
* @returns True if specified script is running on the target server, and false otherwise.
|
||||||
*/
|
*/
|
||||||
isRunning(script: string, host: string, ...args: string[]): boolean;
|
isRunning(script: FilenameOrPID, host: string, ...args: string[]): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get general info about a running script.
|
* Get general info about a running script.
|
||||||
@ -5196,10 +5306,14 @@ export interface NS extends Singularity {
|
|||||||
* RAM cost: 0.3 GB
|
* RAM cost: 0.3 GB
|
||||||
*
|
*
|
||||||
* Running with no args returns curent script.
|
* Running with no args returns curent script.
|
||||||
|
* If you use a PID as the first parameter, the hostname and args parameters are unnecessary.
|
||||||
*
|
*
|
||||||
|
* @param filename - Optional. Filename or PID of the script.
|
||||||
|
* @param hostname - Optional. Name of host server the script is running on.
|
||||||
|
* @param args - Arguments to identify the script
|
||||||
* @returns info about a running script
|
* @returns info about a running script
|
||||||
*/
|
*/
|
||||||
getRunningScript(filename?: string | number, hostname?: string, ...args: (string | number)[]): RunningScript;
|
getRunningScript(filename?: FilenameOrPID, hostname?: string, ...args: (string | number)[]): RunningScript;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get cost of purchasing a server.
|
* Get cost of purchasing a server.
|
||||||
@ -5222,7 +5336,7 @@ export interface NS extends Singularity {
|
|||||||
* ns.tprint(i + " -- " + ns.getPurchasedServerCost(Math.pow(2, i)));
|
* ns.tprint(i + " -- " + ns.getPurchasedServerCost(Math.pow(2, i)));
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @param ram - Amount of RAM of a potential purchased server. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20).
|
* @param ram - Amount of RAM of a potential purchased server, in GB. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20).
|
||||||
* @returns The cost to purchase a server with the specified amount of ram.
|
* @returns The cost to purchase a server with the specified amount of ram.
|
||||||
*/
|
*/
|
||||||
getPurchasedServerCost(ram: number): number;
|
getPurchasedServerCost(ram: number): number;
|
||||||
@ -5270,7 +5384,7 @@ export interface NS extends Singularity {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @param hostname - Host of the purchased server.
|
* @param hostname - Host of the purchased server.
|
||||||
* @param ram - Amount of RAM of the purchased server. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20).
|
* @param ram - Amount of RAM of the purchased server, in GB. Must be a power of 2 (2, 4, 8, 16, etc.). Maximum value of 1048576 (2^20).
|
||||||
* @returns The hostname of the newly purchased server.
|
* @returns The hostname of the newly purchased server.
|
||||||
*/
|
*/
|
||||||
purchaseServer(hostname: string, ram: number): string;
|
purchaseServer(hostname: string, ram: number): string;
|
||||||
@ -5311,7 +5425,7 @@ export interface NS extends Singularity {
|
|||||||
* Returns the maximum RAM that a purchased server can have.
|
* Returns the maximum RAM that a purchased server can have.
|
||||||
*
|
*
|
||||||
* @remarks RAM cost: 0.05 GB
|
* @remarks RAM cost: 0.05 GB
|
||||||
* @returns Returns the maximum RAM that a purchased server can have.
|
* @returns Returns the maximum RAM (in GB) that a purchased server can have.
|
||||||
*/
|
*/
|
||||||
getPurchasedServerMaxRam(): number;
|
getPurchasedServerMaxRam(): number;
|
||||||
|
|
||||||
@ -5320,7 +5434,7 @@ export interface NS extends Singularity {
|
|||||||
* @remarks
|
* @remarks
|
||||||
* RAM cost: 0 GB
|
* RAM cost: 0 GB
|
||||||
*
|
*
|
||||||
* This function can be used to either write data to a text file (.txt).
|
* This function can be used to write data to a text file (.txt).
|
||||||
*
|
*
|
||||||
* This function will write data to that text file. If the specified text file does not exist,
|
* This function will write data to that text file. If the specified text file does not exist,
|
||||||
* then it will be created. The third argument mode, defines how the data will be written to
|
* then it will be created. The third argument mode, defines how the data will be written to
|
||||||
@ -5329,7 +5443,7 @@ export interface NS extends Singularity {
|
|||||||
* then the data will be written in “append” mode which means that the data will be added at the
|
* then the data will be written in “append” mode which means that the data will be added at the
|
||||||
* end of the text file.
|
* end of the text file.
|
||||||
*
|
*
|
||||||
* @param handle - Port or text file that will be written to.
|
* @param handle - Filename of the text file that will be written to.
|
||||||
* @param data - Data to write.
|
* @param data - Data to write.
|
||||||
* @param mode - Defines the write mode. Only valid when writing to text files.
|
* @param mode - Defines the write mode. Only valid when writing to text files.
|
||||||
*/
|
*/
|
||||||
@ -5355,13 +5469,13 @@ export interface NS extends Singularity {
|
|||||||
* @remarks
|
* @remarks
|
||||||
* RAM cost: 0 GB
|
* RAM cost: 0 GB
|
||||||
*
|
*
|
||||||
* This function is used to read data from a port or from a text file (.txt).
|
* This function is used to read data from a text file (.txt).
|
||||||
*
|
*
|
||||||
* This function will return the data in the specified text
|
* This function will return the data in the specified text
|
||||||
* file. If the text file does not exist, an empty string will be returned.
|
* file. If the text file does not exist, an empty string will be returned.
|
||||||
*
|
*
|
||||||
* @param handle - Port or text file to read from.
|
* @param handle - Filename to read from.
|
||||||
* @returns Data in the specified text file or port.
|
* @returns Data in the specified text file.
|
||||||
*/
|
*/
|
||||||
read(handle: string): any;
|
read(handle: string): any;
|
||||||
|
|
||||||
@ -5433,9 +5547,8 @@ export interface NS extends Singularity {
|
|||||||
*
|
*
|
||||||
* @see https://bitburner.readthedocs.io/en/latest/netscript/netscriptmisc.html#netscript-ports
|
* @see https://bitburner.readthedocs.io/en/latest/netscript/netscriptmisc.html#netscript-ports
|
||||||
* @param port - Port number. Must be an integer between 1 and 20.
|
* @param port - Port number. Must be an integer between 1 and 20.
|
||||||
* @returns Data in the specified port.
|
|
||||||
*/
|
*/
|
||||||
getPortHandle(port: number): IPort;
|
getPortHandle(port: number): NetscriptPort;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a file.
|
* Delete a file.
|
||||||
@ -5518,7 +5631,7 @@ export interface NS extends Singularity {
|
|||||||
*
|
*
|
||||||
* @param script - Filename of script. This is case-sensitive.
|
* @param script - Filename of script. This is case-sensitive.
|
||||||
* @param host - Host of target server the script is located on. This is optional, If it is not specified then the function will se the current server as the target server.
|
* @param host - Host of target server the script is located on. This is optional, If it is not specified then the function will se the current server as the target server.
|
||||||
* @returns Amount of RAM required to run the specified script on the target server, and 0 if the script does not exist.
|
* @returns Amount of RAM (in GB) required to run the specified script on the target server, and 0 if the script does not exist.
|
||||||
*/
|
*/
|
||||||
getScriptRam(script: string, host?: string): number;
|
getScriptRam(script: string, host?: string): number;
|
||||||
|
|
||||||
@ -5630,7 +5743,7 @@ export interface NS extends Singularity {
|
|||||||
* @param args - Formating arguments.
|
* @param args - Formating arguments.
|
||||||
* @returns Formated text.
|
* @returns Formated text.
|
||||||
*/
|
*/
|
||||||
sprintf(format: string, ...args: string[]): string;
|
sprintf(format: string, ...args: any[]): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a string with an array of arguments.
|
* Format a string with an array of arguments.
|
||||||
@ -5642,7 +5755,7 @@ export interface NS extends Singularity {
|
|||||||
* @param args - Formating arguments.
|
* @param args - Formating arguments.
|
||||||
* @returns Formated text.
|
* @returns Formated text.
|
||||||
*/
|
*/
|
||||||
vsprintf(format: string, args: string[]): string;
|
vsprintf(format: string, args: any[]): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a number
|
* Format a number
|
||||||
@ -6354,3 +6467,12 @@ interface UserInterfaceTheme {
|
|||||||
backgroundsecondary: string;
|
backgroundsecondary: string;
|
||||||
button: string;
|
button: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface Styles
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
interface IStyleSettings {
|
||||||
|
fontFamily: string;
|
||||||
|
lineHeight: number;
|
||||||
|
}
|
||||||
|
@ -686,7 +686,9 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
const serverScript = server.scripts.find((s) => s.filename === openScript.fileName);
|
const serverScript = server.scripts.find((s) => s.filename === openScript.fileName);
|
||||||
if (serverScript === undefined) return " *";
|
if (serverScript === undefined) return " *";
|
||||||
|
|
||||||
return serverScript.code !== openScript.code ? " *" : "";
|
// The server code is stored with its starting & trailing whitespace removed
|
||||||
|
const openScriptFormatted = Script.formatCode(openScript.code);
|
||||||
|
return serverScript.code !== openScriptFormatted ? " *" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toolbars are roughly 112px:
|
// Toolbars are roughly 112px:
|
||||||
@ -846,7 +848,7 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
<span style={{ color: Settings.theme.primary, fontSize: "20px", textAlign: "center" }}>
|
<span style={{ color: Settings.theme.primary, fontSize: "20px", textAlign: "center" }}>
|
||||||
<Typography variant="h4">No open files</Typography>
|
<Typography variant="h4">No open files</Typography>
|
||||||
<Typography variant="h5">
|
<Typography variant="h5">
|
||||||
Use `nano FILENAME` in
|
Use <code>nano FILENAME</code> in
|
||||||
<br />
|
<br />
|
||||||
the terminal to open files
|
the terminal to open files
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { ISelfInitializer, ISelfLoading } from "../types";
|
import { ISelfInitializer, ISelfLoading } from "../types";
|
||||||
import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums";
|
import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums";
|
||||||
import { defaultTheme, ITheme } from "./Themes";
|
import { defaultTheme, ITheme } from "./Themes";
|
||||||
import { defaultStyles, IStyleSettings } from "./Styles";
|
import { defaultStyles } from "./Styles";
|
||||||
import { WordWrapOptions } from "../ScriptEditor/ui/Options";
|
import { WordWrapOptions } from "../ScriptEditor/ui/Options";
|
||||||
import { OverviewSettings } from "../ui/React/Overview";
|
import { OverviewSettings } from "../ui/React/Overview";
|
||||||
|
import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the default settings the player could customize.
|
* Represents the default settings the player could customize.
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import React from "react";
|
import { IStyleSettings } from "../ScriptEditor/NetscriptDefinitions";
|
||||||
|
|
||||||
export interface IStyleSettings {
|
|
||||||
fontFamily: React.CSSProperties["fontFamily"];
|
|
||||||
lineHeight: React.CSSProperties["lineHeight"];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultStyles: IStyleSettings = {
|
export const defaultStyles: IStyleSettings = {
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
|
@ -29,7 +29,7 @@ export const TerminalHelpText: string[] = [
|
|||||||
"lscpu Displays the number of CPU cores on the machine",
|
"lscpu Displays the number of CPU cores on the machine",
|
||||||
"mem [script] [-t n] Displays the amount of RAM required to run the script",
|
"mem [script] [-t n] Displays the amount of RAM required to run the script",
|
||||||
"mv [src] [dest] Move/rename a text or script file",
|
"mv [src] [dest] Move/rename a text or script file",
|
||||||
"nano [file ...] Text editor - Open up and edit one or more scripts or text files",
|
"nano [file ...] | [glob] Text editor - Open up and edit one or more scripts or text files",
|
||||||
"ps Display all scripts that are currently running",
|
"ps Display all scripts that are currently running",
|
||||||
"rm [file] Delete a file from the server",
|
"rm [file] Delete a file from the server",
|
||||||
"run [name] [-t n] [--tail] [args...] Execute a program or script",
|
"run [name] [-t n] [--tail] [args...] Execute a program or script",
|
||||||
@ -40,14 +40,36 @@ export const TerminalHelpText: string[] = [
|
|||||||
"tail [script] [args...] Displays dynamic logs for the specified script",
|
"tail [script] [args...] Displays dynamic logs for the specified script",
|
||||||
"top Displays all running scripts and their RAM usage",
|
"top Displays all running scripts and their RAM usage",
|
||||||
"unalias [alias name] Deletes the specified alias",
|
"unalias [alias name] Deletes the specified alias",
|
||||||
"vim [file ...] Text editor - Open up and edit one or more scripts or text files in vim mode",
|
"vim [file ...] | [glob] Text editor - Open up and edit one or more scripts or text files in vim mode",
|
||||||
"weaken Reduce the security of the current machine",
|
"weaken Reduce the security of the current machine",
|
||||||
"wget [url] [target file] Retrieves code/text from a web server",
|
"wget [url] [target file] Retrieves code/text from a web server",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const TemplatedHelpTexts: IMap<(command: string) => string[]> = {
|
||||||
|
scriptEditor: (command) => {
|
||||||
|
return [
|
||||||
|
`${command} [file ...] | [glob]`,
|
||||||
|
` `,
|
||||||
|
`Opens up the specified file(s) in the Script Editor. Only scripts (.js, .ns, .script) or text files (.txt) `,
|
||||||
|
`can be edited using the Script Editor. If a file does not exist a new one will be created`,
|
||||||
|
` `,
|
||||||
|
`If provided a glob as the only argument, ${command} can spider directories and open all matching `,
|
||||||
|
`files at once. ${command} cannot create files using globs, so your scripts must already exist.`,
|
||||||
|
` `,
|
||||||
|
`Examples:`,
|
||||||
|
` `,
|
||||||
|
`${command} test.js`,
|
||||||
|
`${command} test.js test2.js`,
|
||||||
|
` `,
|
||||||
|
`${command} test.*`,
|
||||||
|
`${command} /my-dir/*.js`,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const HelpTexts: IMap<string[]> = {
|
export const HelpTexts: IMap<string[]> = {
|
||||||
alias: [
|
alias: [
|
||||||
'alias [-g] [name="value"] ',
|
'Usage: alias [-g] [name="value"] ',
|
||||||
" ",
|
" ",
|
||||||
"Create or display aliases. An alias enables a replacement of a word with another string. ",
|
"Create or display aliases. An alias enables a replacement of a word with another string. ",
|
||||||
"It can be used to abbreviate a commonly used command, or commonly used parts of a command. The NAME ",
|
"It can be used to abbreviate a commonly used command, or commonly used parts of a command. The NAME ",
|
||||||
@ -80,15 +102,16 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
analyze: [
|
analyze: [
|
||||||
"analyze",
|
"Usage: analyze",
|
||||||
" ",
|
" ",
|
||||||
"Prints details and statistics about the current server. The information that is printed includes basic ",
|
"Prints details and statistics about the current server. The information that is printed includes basic ",
|
||||||
"server details such as the hostname, whether the player has root access, what ports are opened/closed, and also ",
|
"server details such as the hostname, whether the player has root access, what ports are opened/closed, and also ",
|
||||||
"hacking-related information such as an estimated chance to successfully hack, an estimate of how much money is ",
|
"hacking-related information such as an estimated chance to successfully hack, an estimate of how much money is ",
|
||||||
"available on the server, etc.",
|
"available on the server, etc.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
backdoor: [
|
backdoor: [
|
||||||
"backdoor",
|
"Usage: backdoor",
|
||||||
" ",
|
" ",
|
||||||
"Install a backdoor on the current machine, grants a secret bonus depending on the machine.",
|
"Install a backdoor on the current machine, grants a secret bonus depending on the machine.",
|
||||||
" ",
|
" ",
|
||||||
@ -96,7 +119,7 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
buy: [
|
buy: [
|
||||||
"buy [-l / -a / program]",
|
"Usage: buy [-l / -a / program]",
|
||||||
" ",
|
" ",
|
||||||
"Purchase a program through the Dark Web. Requires a TOR router to use.",
|
"Purchase a program through the Dark Web. Requires a TOR router to use.",
|
||||||
" ",
|
" ",
|
||||||
@ -106,9 +129,10 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
"If this command is ran with the '-a' flag, it will attempt to purchase all unowned programs.",
|
"If this command is ran with the '-a' flag, it will attempt to purchase all unowned programs.",
|
||||||
" ",
|
" ",
|
||||||
"Otherwise, the name of the program must be passed in as a parameter. This name is NOT case-sensitive.",
|
"Otherwise, the name of the program must be passed in as a parameter. This name is NOT case-sensitive.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
cat: [
|
cat: [
|
||||||
"cat [file]",
|
"Usage: cat [file]",
|
||||||
" ",
|
" ",
|
||||||
"Display message (.msg), literature (.lit), or text (.txt) files. Examples:",
|
"Display message (.msg), literature (.lit), or text (.txt) files. Examples:",
|
||||||
" ",
|
" ",
|
||||||
@ -117,9 +141,10 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" cat foo.lit",
|
" cat foo.lit",
|
||||||
" ",
|
" ",
|
||||||
" cat servers.txt",
|
" cat servers.txt",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
cd: [
|
cd: [
|
||||||
"cd [dir]",
|
"Usage: cd [dir]",
|
||||||
" ",
|
" ",
|
||||||
"Change to the specified directory. Note that this works even for directories that don't exist. If you ",
|
"Change to the specified directory. Note that this works even for directories that don't exist. If you ",
|
||||||
"change to a directory that does not exist, it will not be 'created'. Examples:",
|
"change to a directory that does not exist, it will not be 'created'. Examples:",
|
||||||
@ -129,9 +154,10 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" cd /logs",
|
" cd /logs",
|
||||||
" ",
|
" ",
|
||||||
" cd ../",
|
" cd ../",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
check: [
|
check: [
|
||||||
"check [script name] [args...]",
|
"Usage: check [script name] [args...]",
|
||||||
" ",
|
" ",
|
||||||
"Print the logs of the script specified by the script name and arguments to the Terminal. Each argument must be separated by ",
|
"Print the logs of the script specified by the script name and arguments to the Terminal. Each argument must be separated by ",
|
||||||
"a space. Remember that a running script is uniquely ",
|
"a space. Remember that a running script is uniquely ",
|
||||||
@ -142,29 +168,33 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
"Then to run the 'check' command on this script you would have to pass the same arguments in: ",
|
"Then to run the 'check' command on this script you would have to pass the same arguments in: ",
|
||||||
" ",
|
" ",
|
||||||
" check foo.script 1 2 foodnstuff",
|
" check foo.script 1 2 foodnstuff",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
clear: [
|
clear: [
|
||||||
"clear",
|
"Usage: clear",
|
||||||
" ",
|
" ",
|
||||||
"Clear the Terminal screen, deleting all of the text. Note that this does not delete the user's command history, so using the up ",
|
"Clear the Terminal screen, deleting all of the text. Note that this does not delete the user's command history, so using the up ",
|
||||||
"and down arrow keys is still valid. Also note that this is permanent and there is no way to undo this. Synonymous with 'cls' command",
|
"and down arrow keys is still valid. Also note that this is permanent and there is no way to undo this. Synonymous with 'cls' command",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
cls: [
|
cls: [
|
||||||
"cls",
|
"Usage: cls",
|
||||||
" ",
|
" ",
|
||||||
"Clear the Terminal screen, deleting all of the text. Note that this does not delete the user's command history, so using the up ",
|
"Clear the Terminal screen, deleting all of the text. Note that this does not delete the user's command history, so using the up ",
|
||||||
"and down arrow keys is still valid. Also note that this is permanent and there is no way to undo this. Synonymous with 'clear' command",
|
"and down arrow keys is still valid. Also note that this is permanent and there is no way to undo this. Synonymous with 'clear' command",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
connect: [
|
connect: [
|
||||||
"connect [hostname]",
|
"Usage: connect [hostname]",
|
||||||
" ",
|
" ",
|
||||||
"Connect to a remote server. The hostname or IP address of the remote server must be given as the argument ",
|
"Connect to a remote server. The hostname or IP address of the remote server must be given as the argument ",
|
||||||
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
|
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
|
||||||
"see which servers can be connected to, use the 'scan' command.",
|
"see which servers can be connected to, use the 'scan' command.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
cp: ["cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp."],
|
cp: ["Usage: cp [src] [dst]", " ", "Copy a file on this server. To copy a file to another server use scp.", " "],
|
||||||
download: [
|
download: [
|
||||||
"download [script/text file]",
|
"Usage: download [script/text file]",
|
||||||
" ",
|
" ",
|
||||||
"Downloads a script or text file to your computer (like your real life computer).",
|
"Downloads a script or text file to your computer (like your real life computer).",
|
||||||
" ",
|
" ",
|
||||||
@ -178,7 +208,7 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
expr: [
|
expr: [
|
||||||
"expr [mathematical expression]",
|
"Usage: expr [mathematical expression]",
|
||||||
" ",
|
" ",
|
||||||
"Evaluate a simple mathematical expression. Supports native JavaScript operators:",
|
"Evaluate a simple mathematical expression. Supports native JavaScript operators:",
|
||||||
" ",
|
" ",
|
||||||
@ -189,27 +219,30 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" expr 25 * 2 ** 10",
|
" expr 25 * 2 ** 10",
|
||||||
" ",
|
" ",
|
||||||
"Note that letters (non-digits) are not allowed and will be removed from the input.",
|
"Note that letters (non-digits) are not allowed and will be removed from the input.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
free: [
|
free: [
|
||||||
"free",
|
"Usage: free",
|
||||||
" ",
|
" ",
|
||||||
"Displays the memory usage on the current machine. Print the amount of RAM that is available on the current server as well as ",
|
"Displays the memory usage on the current machine. Print the amount of RAM that is available on the current server as well as ",
|
||||||
"how much of it is being used.",
|
"how much of it is being used.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
grow: [
|
grow: [
|
||||||
"grow",
|
"Usage: grow",
|
||||||
" ",
|
" ",
|
||||||
"Spoof transactions in the current server. Increasing the money available by hacking. Requires root access.",
|
"Spoof transactions in the current server. Increasing the money available by hacking. Requires root access.",
|
||||||
"See the wiki page for hacking mechanics.",
|
"See the wiki page for hacking mechanics.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
hack: [
|
hack: [
|
||||||
"hack",
|
"Usage: hack",
|
||||||
" ",
|
" ",
|
||||||
"Attempt to hack the current server. Requires root access in order to be run. See the wiki page for hacking mechanics",
|
"Attempt to hack the current server. Requires root access in order to be run. See the wiki page for hacking mechanics",
|
||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
help: [
|
help: [
|
||||||
"help [command]",
|
"Usage: help [command]",
|
||||||
" ",
|
" ",
|
||||||
"Display Terminal help information. Without arguments, 'help' prints a list of all valid Terminal commands and a brief ",
|
"Display Terminal help information. Without arguments, 'help' prints a list of all valid Terminal commands and a brief ",
|
||||||
"description of their functionality. You can also pass the name of a Terminal command as an argument to 'help' to print ",
|
"description of their functionality. You can also pass the name of a Terminal command as an argument to 'help' to print ",
|
||||||
@ -218,15 +251,14 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" help alias",
|
" help alias",
|
||||||
" ",
|
" ",
|
||||||
" help scan-analyze",
|
" help scan-analyze",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
home: [
|
home: [
|
||||||
"home" + "Connect to your home computer. This will work no matter what server you are currently connected to.",
|
"Usage: home", " ", "Connect to your home computer. This will work no matter what server you are currently connected to.", " ",
|
||||||
],
|
],
|
||||||
hostname: ["hostname", " ", "Prints the hostname of the current server"],
|
hostname: ["Usage: hostname", " ", "Prints the hostname of the current server", " "],
|
||||||
kill: [
|
kill: [
|
||||||
"kill [script name] [args...]",
|
"Usage: kill [script name] [args...] or kill [pid",
|
||||||
" ",
|
|
||||||
"kill [pid]",
|
|
||||||
" ",
|
" ",
|
||||||
"Kill the script specified by the script name and arguments OR by its PID.",
|
"Kill the script specified by the script name and arguments OR by its PID.",
|
||||||
" ",
|
" ",
|
||||||
@ -242,17 +274,19 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" kill foo.script 1 sigma-cosmetics",
|
" kill foo.script 1 sigma-cosmetics",
|
||||||
" ",
|
" ",
|
||||||
"If you are killing the script using its PID, then the PID argument must be numeric",
|
"If you are killing the script using its PID, then the PID argument must be numeric",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
killall: [
|
killall: [
|
||||||
"killall",
|
"Usage: killall",
|
||||||
" ",
|
" ",
|
||||||
"Kills all scripts on the current server. ",
|
"Kills all scripts on the current server. ",
|
||||||
"Note that after the 'kill' command is issued for a script, it may take a while for the script to actually stop running. ",
|
"Note that after the 'kill' command is issued for a script, it may take a while for the script to actually stop running. ",
|
||||||
"This will happen if the script is in the middle of a command such as grow() or weaken() that takes time to execute. ",
|
"This will happen if the script is in the middle of a command such as grow() or weaken() that takes time to execute. ",
|
||||||
"The script will not be stopped/killed until after that time has elapsed.",
|
"The script will not be stopped/killed until after that time has elapsed.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
ls: [
|
ls: [
|
||||||
"ls [dir] [| grep pattern]",
|
"Usage: ls [dir] [| grep pattern]",
|
||||||
" ",
|
" ",
|
||||||
"The ls command, with no arguments, prints all files and directories on the current server's directory to the Terminal screen. ",
|
"The ls command, with no arguments, prints all files and directories on the current server's directory to the Terminal screen. ",
|
||||||
"The files will be displayed in alphabetical order. ",
|
"The files will be displayed in alphabetical order. ",
|
||||||
@ -274,11 +308,12 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
"List all files with the word 'purchase' in the filename, in the 'scripts' directory:",
|
"List all files with the word 'purchase' in the filename, in the 'scripts' directory:",
|
||||||
" ",
|
" ",
|
||||||
" ls scripts | grep purchase",
|
" ls scripts | grep purchase",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
lscpu: ["lscpu", " ", "Prints the number of CPU Cores the current server has"],
|
lscpu: ["Usage: lscpu", " ", "Prints the number of CPU Cores the current server has", " "],
|
||||||
|
|
||||||
mem: [
|
mem: [
|
||||||
"mem [script name] [-t num_threads]",
|
"Usage: mem [script name] [-t num_threads]",
|
||||||
" ",
|
" ",
|
||||||
"Displays the amount of RAM needed to run the specified script with a single thread. The command can also be used to print ",
|
"Displays the amount of RAM needed to run the specified script with a single thread. The command can also be used to print ",
|
||||||
"the amount of RAM needed to run a script with multiple threads using the '-t' flag. If the '-t' flag is specified, then ",
|
"the amount of RAM needed to run a script with multiple threads using the '-t' flag. If the '-t' flag is specified, then ",
|
||||||
@ -290,9 +325,10 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" ",
|
" ",
|
||||||
"The first example above will print the amount of RAM needed to run 'foo.script' with a single thread. The second example ",
|
"The first example above will print the amount of RAM needed to run 'foo.script' with a single thread. The second example ",
|
||||||
"above will print the amount of RAM needed to run 'foo.script' with 50 threads.",
|
"above will print the amount of RAM needed to run 'foo.script' with 50 threads.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
mv: [
|
mv: [
|
||||||
"mv [src] [dest]",
|
"Usage: mv [src] [dest]",
|
||||||
" ",
|
" ",
|
||||||
"Move the source file to the specified destination. This can also be used to rename files. ",
|
"Move the source file to the specified destination. This can also be used to rename files. ",
|
||||||
"This command only works for scripts and text files (.txt). This command CANNOT be used to ",
|
"This command only works for scripts and text files (.txt). This command CANNOT be used to ",
|
||||||
@ -305,25 +341,20 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" mv hacking-controller.script scripts/hacking-controller.script",
|
" mv hacking-controller.script scripts/hacking-controller.script",
|
||||||
" ",
|
" ",
|
||||||
" mv myScript.js myOldScript.js",
|
" mv myScript.js myOldScript.js",
|
||||||
],
|
|
||||||
nano: [
|
|
||||||
"nano [file ...]",
|
|
||||||
" ",
|
" ",
|
||||||
"Opens up the specified file(s) in the Text Editor. Only scripts (.script) or text files (.txt) can be ",
|
|
||||||
"edited using the Text Editor. If the file does not already exist, then a new, empty one ",
|
|
||||||
"will be created",
|
|
||||||
],
|
],
|
||||||
ps: ["ps", " ", "Prints all scripts that are running on the current server"],
|
nano: TemplatedHelpTexts.scriptEditor('nano'),
|
||||||
|
ps: ["Usage: ps", " ", "Prints all scripts that are running on the current server", " "],
|
||||||
rm: [
|
rm: [
|
||||||
"rm [file]",
|
"Usage: rm [file]",
|
||||||
" ",
|
" ",
|
||||||
"Removes the specified file from the current server. A file can be a script, a program, or a message file. ",
|
"Removes the specified file from the current server. A file can be a script, a program, or a message file. ",
|
||||||
" ",
|
" ",
|
||||||
"WARNING: This is permanent and cannot be undone",
|
"WARNING: This is permanent and cannot be undone",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
run: [
|
run: [
|
||||||
"run [file name] [-t] [num threads] [args...]",
|
"Usage: run [file name] [-t] [num threads] [args...]",
|
||||||
" ",
|
" ",
|
||||||
"Execute a program, script or coding contract.",
|
"Execute a program, script or coding contract.",
|
||||||
" ",
|
" ",
|
||||||
@ -338,13 +369,14 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
scan: [
|
scan: [
|
||||||
"scan",
|
"Usage: scan",
|
||||||
" ",
|
" ",
|
||||||
"Prints all immediately-available network connection. This will print a list of all servers that you can currently connect ",
|
"Prints all immediately-available network connection. This will print a list of all servers that you can currently connect ",
|
||||||
"to using the 'connect' Terminal command.",
|
"to using the 'connect' Terminal command.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
"scan-analyze": [
|
"scan-analyze": [
|
||||||
"scan-analyze [depth] [-a]",
|
"Usage: scan-analyze [depth] [-a]",
|
||||||
" ",
|
" ",
|
||||||
"Prints detailed information about all servers up to [depth] nodes away on the network. Calling ",
|
"Prints detailed information about all servers up to [depth] nodes away on the network. Calling ",
|
||||||
"'scan-analyze 1' will display information for the same servers that are shown by the 'scan' Terminal ",
|
"'scan-analyze 1' will display information for the same servers that are shown by the 'scan' Terminal ",
|
||||||
@ -360,9 +392,10 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" ",
|
" ",
|
||||||
"By default, this command will not display servers that you have purchased. However, you can pass in the ",
|
"By default, this command will not display servers that you have purchased. However, you can pass in the ",
|
||||||
"-a flag at the end of the command if you would like to enable that.",
|
"-a flag at the end of the command if you would like to enable that.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
scp: [
|
scp: [
|
||||||
"scp [filename ...] [target server]",
|
"Usage: scp [filename ...] [target server]",
|
||||||
" ",
|
" ",
|
||||||
"Copies the specified file(s) from the current server to the target server. ",
|
"Copies the specified file(s) from the current server to the target server. ",
|
||||||
"This command only works for script files (.script or .js extension), literature files (.lit extension), ",
|
"This command only works for script files (.script or .js extension), literature files (.lit extension), ",
|
||||||
@ -374,10 +407,10 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" scp foo.script bar.script n00dles",
|
" scp foo.script bar.script n00dles",
|
||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
sudov: ["sudov", " ", "Prints whether or not you have root access to the current machine"],
|
sudov: ["Usage: sudov", " ", "Prints whether or not you have root access to the current machine", " "],
|
||||||
|
|
||||||
tail: [
|
tail: [
|
||||||
"tail [script name] [args...]",
|
"Usage: tail [script name] [args...]",
|
||||||
" ",
|
" ",
|
||||||
"Displays dynamic logs for the script specified by the script name and arguments. Each argument must be separated ",
|
"Displays dynamic logs for the script specified by the script name and arguments. Each argument must be separated ",
|
||||||
"by a space. Remember that a running script is uniquely identified by both its name and the arguments that were used ",
|
"by a space. Remember that a running script is uniquely identified by both its name and the arguments that were used ",
|
||||||
@ -388,15 +421,17 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
"Then in order to check its logs with 'tail' the same arguments must be used: ",
|
"Then in order to check its logs with 'tail' the same arguments must be used: ",
|
||||||
" ",
|
" ",
|
||||||
" tail foo.script 10 50000",
|
" tail foo.script 10 50000",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
top: [
|
top: [
|
||||||
"top",
|
"Usage: top",
|
||||||
" ",
|
" ",
|
||||||
"Prints a list of all scripts running on the current server as well as their thread count and how much ",
|
"Prints a list of all scripts running on the current server as well as their thread count and how much ",
|
||||||
"RAM they are using in total.",
|
"RAM they are using in total.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
unalias: [
|
unalias: [
|
||||||
"unalias [alias name]",
|
"Usage: unalias [alias name]",
|
||||||
" ",
|
" ",
|
||||||
"Deletes the specified alias. Note that the double quotation marks are required. ",
|
"Deletes the specified alias. Note that the double quotation marks are required. ",
|
||||||
" ",
|
" ",
|
||||||
@ -409,22 +444,18 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" unalias r",
|
" unalias r",
|
||||||
" ",
|
" ",
|
||||||
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
|
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
|
||||||
],
|
|
||||||
vim: [
|
|
||||||
"vim [file ...]",
|
|
||||||
" ",
|
" ",
|
||||||
"Opens up the specified file(s) in the Text Editor in vim mode. Only scripts (.script) or text files (.txt) can be ",
|
|
||||||
"edited using the Text Editor. If the file does not already exist, then a new, empty one ",
|
|
||||||
"will be created",
|
|
||||||
],
|
],
|
||||||
|
vim: TemplatedHelpTexts.scriptEditor('vim'),
|
||||||
weaken: [
|
weaken: [
|
||||||
"weaken",
|
"Usage: weaken",
|
||||||
" ",
|
" ",
|
||||||
"Reduces the security level of the current server. Decreasing the time it takes for all operations on this server.",
|
"Reduces the security level of the current server. Decreasing the time it takes for all operations on this server.",
|
||||||
"Requires root access. See the wiki page for hacking mechanics.",
|
"Requires root access. See the wiki page for hacking mechanics.",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
wget: [
|
wget: [
|
||||||
"wget [url] [target file]",
|
"Usage: wget [url] [target file]",
|
||||||
" ",
|
" ",
|
||||||
"Retrieves data from a URL and downloads it to a file on the current server. The data can only ",
|
"Retrieves data from a URL and downloads it to a file on the current server. The data can only ",
|
||||||
"be downloaded to a script (.script, .ns, .js) or a text file (.txt). If the file already exists, ",
|
"be downloaded to a script (.script, .ns, .js) or a text file (.txt). If the file already exists, ",
|
||||||
@ -434,5 +465,6 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
"cross-origin resource sharing (CORS). Example:",
|
"cross-origin resource sharing (CORS). Example:",
|
||||||
" ",
|
" ",
|
||||||
" wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt",
|
" wget https://raw.githubusercontent.com/danielyxie/bitburner/master/README.md game_readme.txt",
|
||||||
|
" ",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { ITerminal } from "../../ITerminal";
|
import { ITerminal } from "../../ITerminal";
|
||||||
|
import { removeLeadingSlash, removeTrailingSlash } from '../../DirectoryHelpers'
|
||||||
import { IRouter, ScriptEditorRouteOptions } from "../../../ui/Router";
|
import { IRouter, ScriptEditorRouteOptions } from "../../../ui/Router";
|
||||||
import { IPlayer } from "../../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../../PersonObjects/IPlayer";
|
||||||
import { BaseServer } from "../../../Server/BaseServer";
|
import { BaseServer } from "../../../Server/BaseServer";
|
||||||
import { isScriptFilename } from "../../../Script/isScriptFilename";
|
import { isScriptFilename } from "../../../Script/isScriptFilename";
|
||||||
import { CursorPositions } from "../../../ScriptEditor/CursorPositions";
|
import { CursorPositions } from "../../../ScriptEditor/CursorPositions";
|
||||||
|
import { Script } from "../../../Script/Script";
|
||||||
|
import { isEmpty } from "lodash";
|
||||||
|
|
||||||
interface EditorParameters {
|
interface EditorParameters {
|
||||||
terminal: ITerminal;
|
terminal: ITerminal;
|
||||||
@ -22,6 +25,74 @@ export async function main(ns) {
|
|||||||
|
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
interface ISimpleScriptGlob {
|
||||||
|
glob: string;
|
||||||
|
preGlob: string;
|
||||||
|
postGlob: string;
|
||||||
|
globError: string;
|
||||||
|
globMatches: string[];
|
||||||
|
globAgainst: Script[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsSimpleGlob(filename: string): boolean {
|
||||||
|
return filename.includes("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectSimpleScriptGlob(
|
||||||
|
args: EditorParameters["args"],
|
||||||
|
player: IPlayer,
|
||||||
|
terminal: ITerminal,
|
||||||
|
): ISimpleScriptGlob | null {
|
||||||
|
if (args.length == 1 && containsSimpleGlob(`${args[0]}`)) {
|
||||||
|
const filename = `${args[0]}`;
|
||||||
|
const scripts = player.getCurrentServer().scripts;
|
||||||
|
const parsedGlob = parseSimpleScriptGlob(filename, scripts, terminal);
|
||||||
|
return parsedGlob;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSimpleScriptGlob(globString: string, globDatabase: Script[], terminal: ITerminal): ISimpleScriptGlob {
|
||||||
|
const parsedGlob: ISimpleScriptGlob = {
|
||||||
|
glob: globString,
|
||||||
|
preGlob: "",
|
||||||
|
postGlob: "",
|
||||||
|
globError: "",
|
||||||
|
globMatches: [],
|
||||||
|
globAgainst: globDatabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure deep globs are minified to simple globs, which act as deep globs in this impl
|
||||||
|
globString = globString.replace("**", "*");
|
||||||
|
|
||||||
|
// Ensure only a single glob is present
|
||||||
|
if (globString.split("").filter((c) => c == "*").length !== 1) {
|
||||||
|
parsedGlob.globError = "Only a single glob is supported per command.\nexample: `nano my-dir/*.js`";
|
||||||
|
return parsedGlob;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split arg around glob, normalize preGlob path
|
||||||
|
[parsedGlob.preGlob, parsedGlob.postGlob] = globString.split("*");
|
||||||
|
parsedGlob.preGlob = removeLeadingSlash(parsedGlob.preGlob);
|
||||||
|
|
||||||
|
// Add CWD to preGlob path
|
||||||
|
const cwd = removeTrailingSlash(terminal.cwd())
|
||||||
|
parsedGlob.preGlob = `${cwd}/${parsedGlob.preGlob}`
|
||||||
|
|
||||||
|
// For every script on the current server, filter matched scripts per glob values & persist
|
||||||
|
globDatabase.forEach((script) => {
|
||||||
|
const filename = script.filename.startsWith('/') ? script.filename : `/${script.filename}`
|
||||||
|
if (filename.startsWith(parsedGlob.preGlob) && filename.endsWith(parsedGlob.postGlob)) {
|
||||||
|
parsedGlob.globMatches.push(filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rebuild glob for potential error reporting
|
||||||
|
parsedGlob.glob = `${parsedGlob.preGlob}*${parsedGlob.postGlob}`
|
||||||
|
|
||||||
|
return parsedGlob;
|
||||||
|
}
|
||||||
|
|
||||||
export function commonEditor(
|
export function commonEditor(
|
||||||
command: string,
|
command: string,
|
||||||
{ terminal, router, player, args }: EditorParameters,
|
{ terminal, router, player, args }: EditorParameters,
|
||||||
@ -32,8 +103,15 @@ export function commonEditor(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let filesToLoadOrCreate = args;
|
||||||
try {
|
try {
|
||||||
const files = args.map((arg) => {
|
const globSearch = detectSimpleScriptGlob(args, player, terminal);
|
||||||
|
if (globSearch) {
|
||||||
|
if (isEmpty(globSearch.globError) === false) throw new Error(globSearch.globError);
|
||||||
|
filesToLoadOrCreate = globSearch.globMatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = filesToLoadOrCreate.map((arg) => {
|
||||||
const filename = `${arg}`;
|
const filename = `${arg}`;
|
||||||
|
|
||||||
if (isScriptFilename(filename)) {
|
if (isScriptFilename(filename)) {
|
||||||
@ -55,12 +133,18 @@ export function commonEditor(
|
|||||||
if (filename.endsWith(".txt")) {
|
if (filename.endsWith(".txt")) {
|
||||||
const filepath = terminal.getFilepath(filename);
|
const filepath = terminal.getFilepath(filename);
|
||||||
const txt = terminal.getTextFile(player, filename);
|
const txt = terminal.getTextFile(player, filename);
|
||||||
return [filepath, txt == null ? "" : txt.text];
|
return [filepath, txt === null ? "" : txt.text];
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Invalid file. Only scripts (.script, .ns, .js), or text files (.txt) can be edited with ${command}`);
|
throw new Error(
|
||||||
|
`Invalid file. Only scripts (.script, .ns, .js), or text files (.txt) can be edited with ${command}`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (globSearch && files.length === 0) {
|
||||||
|
throw new Error(`Could not find any valid files to open with ${command} using glob: \`${globSearch.glob}\``)
|
||||||
|
}
|
||||||
|
|
||||||
router.toScriptEditor(Object.fromEntries(files), scriptEditorRouteOptions);
|
router.toScriptEditor(Object.fromEntries(files), scriptEditorRouteOptions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
terminal.error(`${e}`);
|
terminal.error(`${e}`);
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
|
import { Theme } from "@mui/material/styles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
import makeStyles from "@mui/styles/makeStyles";
|
||||||
|
import { toString } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ITerminal } from "../ITerminal";
|
|
||||||
import { IRouter } from "../../ui/Router";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { BaseServer } from "../../Server/BaseServer";
|
import { BaseServer } from "../../Server/BaseServer";
|
||||||
import { getFirstParentDirectory, isValidDirectoryPath, evaluateDirectoryPath } from "../../Terminal/DirectoryHelpers";
|
import { evaluateDirectoryPath, getFirstParentDirectory, isValidDirectoryPath } from "../../Terminal/DirectoryHelpers";
|
||||||
|
import { IRouter } from "../../ui/Router";
|
||||||
|
import { ITerminal } from "../ITerminal";
|
||||||
|
|
||||||
export function ls(
|
export function ls(
|
||||||
terminal: ITerminal,
|
terminal: ITerminal,
|
||||||
@ -113,7 +117,55 @@ export function ls(
|
|||||||
allMessages.sort();
|
allMessages.sort();
|
||||||
folders.sort();
|
folders.sort();
|
||||||
|
|
||||||
function postSegments(segments: string[], style?: any): void {
|
interface ClickableScriptRowProps {
|
||||||
|
row: string;
|
||||||
|
prefix: string;
|
||||||
|
hostname: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ClickableScriptRow({ row, prefix, hostname }: ClickableScriptRowProps): React.ReactElement {
|
||||||
|
const classes = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
scriptLinksWrap: {
|
||||||
|
display: "flex",
|
||||||
|
color: theme.palette.warning.main,
|
||||||
|
},
|
||||||
|
scriptLink: {
|
||||||
|
cursor: "pointer",
|
||||||
|
textDecorationLine: "underline",
|
||||||
|
paddingRight: "1.15em",
|
||||||
|
"&:last-child": { padding: 0 },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)();
|
||||||
|
|
||||||
|
const rowSplit = row
|
||||||
|
.split(" ")
|
||||||
|
.map((x) => x.trim())
|
||||||
|
.filter((x) => !!x);
|
||||||
|
|
||||||
|
function onScriptLinkClick(filename: string): void {
|
||||||
|
if (player.getCurrentServer().hostname !== hostname) {
|
||||||
|
return terminal.error(`File is not on this server, connect to ${hostname} and try again`);
|
||||||
|
}
|
||||||
|
if (filename.startsWith("/")) filename = filename.slice(1);
|
||||||
|
const filepath = terminal.getFilepath(`${prefix}${filename}`);
|
||||||
|
const code = toString(terminal.getScript(player, filepath)?.code);
|
||||||
|
router.toScriptEditor({ [filepath]: code });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={classes.scriptLinksWrap}>
|
||||||
|
{rowSplit.map((rowItem) => (
|
||||||
|
<span key={rowItem} className={classes.scriptLink} onClick={() => onScriptLinkClick(rowItem)}>
|
||||||
|
{rowItem}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function postSegments(segments: string[], style?: any, linked?: boolean): void {
|
||||||
const maxLength = Math.max(...segments.map((s) => s.length)) + 1;
|
const maxLength = Math.max(...segments.map((s) => s.length)) + 1;
|
||||||
const filesPerRow = Math.floor(80 / maxLength);
|
const filesPerRow = Math.floor(80 / maxLength);
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
@ -127,11 +179,15 @@ export function ls(
|
|||||||
i--;
|
i--;
|
||||||
if (!style) {
|
if (!style) {
|
||||||
terminal.print(row);
|
terminal.print(row);
|
||||||
|
} else {
|
||||||
|
if (linked) {
|
||||||
|
terminal.printRaw(<ClickableScriptRow row={row} prefix={prefix} hostname={server.hostname} />);
|
||||||
} else {
|
} else {
|
||||||
terminal.printRaw(<span style={style}>{row}</span>);
|
terminal.printRaw(<span style={style}>{row}</span>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const groups = [
|
const groups = [
|
||||||
{ segments: folders, style: { color: "cyan" } },
|
{ segments: folders, style: { color: "cyan" } },
|
||||||
@ -139,9 +195,9 @@ export function ls(
|
|||||||
{ segments: allTextFiles },
|
{ segments: allTextFiles },
|
||||||
{ segments: allPrograms },
|
{ segments: allPrograms },
|
||||||
{ segments: allContracts },
|
{ segments: allContracts },
|
||||||
{ segments: allScripts, style: { color: "yellow", fontStyle: "bold" } },
|
{ segments: allScripts, style: { color: "yellow", fontStyle: "bold" }, linked: true },
|
||||||
].filter((g) => g.segments.length > 0);
|
].filter((g) => g.segments.length > 0);
|
||||||
for (let i = 0; i < groups.length; i++) {
|
for (let i = 0; i < groups.length; i++) {
|
||||||
postSegments(groups[i].segments, groups[i].style);
|
postSegments(groups[i].segments, groups[i].style, groups[i].linked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { DarkWebItems } from "../DarkWeb/DarkWebItems";
|
|||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { GetServer, GetAllServers } from "../Server/AllServers";
|
import { GetServer, GetAllServers } from "../Server/AllServers";
|
||||||
import { ParseCommand, ParseCommands } from "./Parser";
|
import { ParseCommand, ParseCommands } from "./Parser";
|
||||||
|
import { HelpTexts } from "./HelpText";
|
||||||
import { isScriptFilename } from "../Script/isScriptFilename";
|
import { isScriptFilename } from "../Script/isScriptFilename";
|
||||||
import { compile } from "../NetscriptJSEvaluator";
|
import { compile } from "../NetscriptJSEvaluator";
|
||||||
import { Flags } from "../NetscriptFunctions/Flags";
|
import { Flags } from "../NetscriptFunctions/Flags";
|
||||||
@ -178,31 +179,8 @@ export async function determineAllPossibilitiesForTabCompletion(
|
|||||||
return input.startsWith(t_cmd);
|
return input.startsWith(t_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If the command starts with './' and the index == -1, then the user
|
|
||||||
* has input ./partialexecutablename so autocomplete the script or program.
|
|
||||||
* Put './' in front of each script/executable
|
|
||||||
*/
|
|
||||||
if (input.startsWith("./") && index == -1) {
|
|
||||||
//All programs and scripts
|
|
||||||
for (let i = 0; i < currServ.scripts.length; ++i) {
|
|
||||||
allPos.push("./" + currServ.scripts[i].filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Programs are on home computer
|
|
||||||
for (let i = 0; i < homeComputer.programs.length; ++i) {
|
|
||||||
allPos.push("./" + homeComputer.programs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Contracts on current server
|
|
||||||
for (let i = 0; i < currServ.contracts.length; ++i) {
|
|
||||||
allPos.push("./" + currServ.contracts[i].fn)
|
|
||||||
}
|
|
||||||
return allPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Autocomplete the command
|
// Autocomplete the command
|
||||||
if (index === -1) {
|
if (index === -1 && !input.startsWith('./')) {
|
||||||
return commands.concat(Object.keys(Aliases)).concat(Object.keys(GlobalAliases));
|
return commands.concat(Object.keys(Aliases)).concat(Object.keys(GlobalAliases));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,14 +270,21 @@ export async function determineAllPossibilitiesForTabCompletion(
|
|||||||
|
|
||||||
async function scriptAutocomplete(): Promise<string[] | undefined> {
|
async function scriptAutocomplete(): Promise<string[] | undefined> {
|
||||||
if (!isCommand("run") && !isCommand("tail") && !isCommand("kill") && !input.startsWith("./")) return;
|
if (!isCommand("run") && !isCommand("tail") && !isCommand("kill") && !input.startsWith("./")) return;
|
||||||
if (input.startsWith("./")) input = "run " + input.slice(2);
|
let copy = input;
|
||||||
const commands = ParseCommands(input);
|
if (input.startsWith("./")) copy = "run " + input.slice(2);
|
||||||
|
const commands = ParseCommands(copy);
|
||||||
if (commands.length === 0) return;
|
if (commands.length === 0) return;
|
||||||
const command = ParseCommand(commands[commands.length - 1]);
|
const command = ParseCommand(commands[commands.length - 1]);
|
||||||
const filename = command[1] + "";
|
const filename = command[1] + "";
|
||||||
if (!isScriptFilename(filename)) return; // Not a script.
|
if (!isScriptFilename(filename)) return; // Not a script.
|
||||||
if (filename.endsWith(".script")) return; // Doesn't work with ns1.
|
if (filename.endsWith(".script")) return; // Doesn't work with ns1.
|
||||||
const script = currServ.scripts.find((script) => script.filename === filename || script.filename === '/' + filename);
|
// Use regex to remove any leading './', and then check if it matches against
|
||||||
|
// the output of processFilepath or if it matches with a '/' prepended,
|
||||||
|
// this way autocomplete works inside of directories
|
||||||
|
const script = currServ.scripts.find((script) => {
|
||||||
|
const fn = filename.replace(/^\.\//g, '');
|
||||||
|
return (processFilepath(script.filename) === fn || script.filename === '/' + fn);
|
||||||
|
})
|
||||||
if (!script) return; // Doesn't exist.
|
if (!script) return; // Doesn't exist.
|
||||||
if (!script.module) {
|
if (!script.module) {
|
||||||
await compile(p, script, currServ.scripts);
|
await compile(p, script, currServ.scripts);
|
||||||
@ -341,6 +326,35 @@ export async function determineAllPossibilitiesForTabCompletion(
|
|||||||
const pos = await scriptAutocomplete();
|
const pos = await scriptAutocomplete();
|
||||||
if (pos) return pos;
|
if (pos) return pos;
|
||||||
|
|
||||||
|
// If input starts with './', essentially treat it as a slimmer
|
||||||
|
// invocation of `run`.
|
||||||
|
if (input.startsWith("./")) {
|
||||||
|
// All programs and scripts
|
||||||
|
for (const script of currServ.scripts) {
|
||||||
|
const res = processFilepath(script.filename);
|
||||||
|
if (res) {
|
||||||
|
allPos.push(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const program of currServ.programs) {
|
||||||
|
const res = processFilepath(program);
|
||||||
|
if (res) {
|
||||||
|
allPos.push(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All coding contracts
|
||||||
|
for (const cct of currServ.contracts) {
|
||||||
|
const res = processFilepath(cct.fn);
|
||||||
|
if (res) {
|
||||||
|
allPos.push(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allPos;
|
||||||
|
}
|
||||||
|
|
||||||
if (isCommand("run")) {
|
if (isCommand("run")) {
|
||||||
addAllScripts();
|
addAllScripts();
|
||||||
addAllPrograms();
|
addAllPrograms();
|
||||||
@ -383,5 +397,11 @@ export async function determineAllPossibilitiesForTabCompletion(
|
|||||||
addAllDirectories();
|
addAllDirectories();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCommand("help")) {
|
||||||
|
// Get names from here instead of commands array because some
|
||||||
|
// undocumented/nonexistent commands are in the array
|
||||||
|
return Object.keys(HelpTexts);
|
||||||
|
}
|
||||||
|
|
||||||
return allPos;
|
return allPos;
|
||||||
}
|
}
|
||||||
|
@ -95,20 +95,42 @@ export function TerminalRoot({ terminal, router, player }: IProps): React.ReactE
|
|||||||
setKey((key) => key + 1);
|
setKey((key) => key + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => TerminalEvents.subscribe(_.debounce(async () => rerender(), 25, { maxWait: 50 })), []);
|
useEffect(() => {
|
||||||
useEffect(() => TerminalClearEvents.subscribe(_.debounce(async () => clear(), 25, { maxWait: 50 })), []);
|
const debounced = _.debounce(async () => rerender(), 25, { maxWait: 50 });
|
||||||
|
const unsubscribe = TerminalEvents.subscribe(debounced);
|
||||||
|
return () => {
|
||||||
|
debounced.cancel();
|
||||||
|
unsubscribe();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
function doScroll(): void {
|
useEffect(() => {
|
||||||
|
const debounced = _.debounce(async () => clear(), 25, { maxWait: 50 });
|
||||||
|
const unsubscribe = TerminalClearEvents.subscribe(debounced);
|
||||||
|
return () => {
|
||||||
|
debounced.cancel();
|
||||||
|
unsubscribe();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function doScroll(): number | undefined {
|
||||||
const hook = scrollHook.current;
|
const hook = scrollHook.current;
|
||||||
if (hook !== null) {
|
if (hook !== null) {
|
||||||
setTimeout(() => hook.scrollIntoView(true), 50);
|
return window.setTimeout(() => hook.scrollIntoView(true), 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doScroll();
|
doScroll();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(doScroll, 50);
|
let scrollId: number;
|
||||||
|
const id = setTimeout(() => {
|
||||||
|
scrollId = doScroll() ?? 0;
|
||||||
|
}, 50);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(id);
|
||||||
|
clearTimeout(scrollId);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function lineClass(s: string): string {
|
function lineClass(s: string): string {
|
||||||
|
@ -279,7 +279,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
|||||||
"i to i+n.",
|
"i to i+n.",
|
||||||
"\n\nAssuming you are initially positioned",
|
"\n\nAssuming you are initially positioned",
|
||||||
"at the start of the array, determine whether you are",
|
"at the start of the array, determine whether you are",
|
||||||
"able to reach the last index exactly.\n\n",
|
"able to reach the last index.\n\n",
|
||||||
"Your answer should be submitted as 1 or 0, representing true and false respectively",
|
"Your answer should be submitted as 1 or 0, representing true and false respectively",
|
||||||
].join(" ");
|
].join(" ");
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { cloneDeep } from "lodash";
|
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { IEngine } from "../IEngine";
|
import { IEngine } from "../IEngine";
|
||||||
import { ITerminal } from "../Terminal/ITerminal";
|
import { ITerminal } from "../Terminal/ITerminal";
|
||||||
@ -87,11 +87,6 @@ interface IProps {
|
|||||||
engine: IEngine;
|
engine: IEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PageHistoryEntry {
|
|
||||||
page: Page;
|
|
||||||
args: any[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
root: {
|
root: {
|
||||||
@ -111,15 +106,6 @@ export let Router: IRouter = {
|
|||||||
page: () => {
|
page: () => {
|
||||||
throw new Error("Router called before initialization");
|
throw new Error("Router called before initialization");
|
||||||
},
|
},
|
||||||
previousPage: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
clearHistory: () => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toPreviousPage: (): boolean => {
|
|
||||||
throw new Error("Router called before initialization");
|
|
||||||
},
|
|
||||||
toActiveScripts: () => {
|
toActiveScripts: () => {
|
||||||
throw new Error("Router called before initialization");
|
throw new Error("Router called before initialization");
|
||||||
},
|
},
|
||||||
@ -218,9 +204,7 @@ function determineStartPage(player: IPlayer): Page {
|
|||||||
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
|
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
|
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
|
||||||
const startPage = determineStartPage(player);
|
const [page, setPage] = useState(determineStartPage(player));
|
||||||
const [page, setPage] = useState(startPage);
|
|
||||||
const [pageHistory, setPageHistory] = useState<PageHistoryEntry[]>([{ page: startPage, args: [] }]);
|
|
||||||
const setRerender = useState(0)[1];
|
const setRerender = useState(0)[1];
|
||||||
const [faction, setFaction] = useState<Faction>(
|
const [faction, setFaction] = useState<Faction>(
|
||||||
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction),
|
player.currentWorkFactionName ? Factions[player.currentWorkFactionName] : (undefined as unknown as Faction),
|
||||||
@ -256,131 +240,70 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
setTimeout(() => htmlLocation.reload(), 2000);
|
setTimeout(() => htmlLocation.reload(), 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCurrentPage(page: Page, ...args: any): void {
|
|
||||||
const history = [
|
|
||||||
{ page, args: cloneDeep(args) },
|
|
||||||
...pageHistory
|
|
||||||
].slice(0, 20);
|
|
||||||
setPageHistory(history)
|
|
||||||
setPage(page)
|
|
||||||
}
|
|
||||||
|
|
||||||
function goBack(fallback: (...args: any[]) => void): void {
|
|
||||||
const [ , previousPage ] = pageHistory;
|
|
||||||
if (previousPage) {
|
|
||||||
const handler = pageToRouterMap[previousPage?.page];
|
|
||||||
handler(...previousPage.args);
|
|
||||||
} else {
|
|
||||||
if (fallback) fallback();
|
|
||||||
}
|
|
||||||
const [ , ...history] = pageHistory;
|
|
||||||
setPageHistory(cloneDeep(history));
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageToRouterMap: { [key: number] : (...args: any[]) => void } = {
|
|
||||||
[Page.ActiveScripts]: Router.toActiveScripts,
|
|
||||||
[Page.Augmentations]: Router.toAugmentations,
|
|
||||||
[Page.Bladeburner]: Router.toBladeburner,
|
|
||||||
[Page.Stats]: Router.toStats,
|
|
||||||
[Page.Corporation]: Router.toCorporation,
|
|
||||||
[Page.CreateProgram]: Router.toCreateProgram,
|
|
||||||
[Page.DevMenu]: Router.toDevMenu,
|
|
||||||
[Page.Faction]: Router.toFaction,
|
|
||||||
[Page.Factions]: Router.toFactions,
|
|
||||||
[Page.Options]: Router.toGameOptions,
|
|
||||||
[Page.Gang]: Router.toGang,
|
|
||||||
[Page.Hacknet]: Router.toHacknetNodes,
|
|
||||||
[Page.Milestones]: Router.toMilestones,
|
|
||||||
[Page.Resleeves]: Router.toResleeves,
|
|
||||||
[Page.ScriptEditor]: Router.toScriptEditor,
|
|
||||||
[Page.Sleeves]: Router.toSleeves,
|
|
||||||
[Page.StockMarket]: Router.toStockMarket,
|
|
||||||
[Page.Terminal]: Router.toTerminal,
|
|
||||||
[Page.Tutorial]: Router.toTutorial,
|
|
||||||
[Page.Job]: Router.toJob,
|
|
||||||
[Page.City]: Router.toCity,
|
|
||||||
[Page.Travel]: Router.toTravel,
|
|
||||||
[Page.BitVerse]: Router.toBitVerse,
|
|
||||||
[Page.Infiltration]: Router.toInfiltration,
|
|
||||||
[Page.Work]: Router.toWork,
|
|
||||||
[Page.BladeburnerCinematic]: Router.toBladeburnerCinematic,
|
|
||||||
[Page.Location]: Router.toLocation,
|
|
||||||
[Page.StaneksGift]: Router.toStaneksGift,
|
|
||||||
[Page.Achievements]: Router.toAchievements,
|
|
||||||
}
|
|
||||||
|
|
||||||
Router = {
|
Router = {
|
||||||
page: () => page,
|
page: () => page,
|
||||||
previousPage: () => {
|
toActiveScripts: () => setPage(Page.ActiveScripts),
|
||||||
const [ , previousPage] = pageHistory;
|
toAugmentations: () => setPage(Page.Augmentations),
|
||||||
return previousPage?.page ?? -1;
|
toBladeburner: () => setPage(Page.Bladeburner),
|
||||||
},
|
toStats: () => setPage(Page.Stats),
|
||||||
clearHistory: () => setPageHistory([]),
|
toCorporation: () => setPage(Page.Corporation),
|
||||||
toPreviousPage: goBack,
|
toCreateProgram: () => setPage(Page.CreateProgram),
|
||||||
toActiveScripts: () => setCurrentPage(Page.ActiveScripts),
|
toDevMenu: () => setPage(Page.DevMenu),
|
||||||
toAugmentations: () => setCurrentPage(Page.Augmentations),
|
|
||||||
toBladeburner: () => setCurrentPage(Page.Bladeburner),
|
|
||||||
toStats: () => setCurrentPage(Page.Stats),
|
|
||||||
toCorporation: () => setCurrentPage(Page.Corporation),
|
|
||||||
toCreateProgram: () => setCurrentPage(Page.CreateProgram),
|
|
||||||
toDevMenu: () => setCurrentPage(Page.DevMenu),
|
|
||||||
toFaction: (faction?: Faction) => {
|
toFaction: (faction?: Faction) => {
|
||||||
setCurrentPage(Page.Faction, faction);
|
setPage(Page.Faction);
|
||||||
if (faction) setFaction(faction);
|
if (faction) setFaction(faction);
|
||||||
},
|
},
|
||||||
toFactions: () => setCurrentPage(Page.Factions),
|
toFactions: () => setPage(Page.Factions),
|
||||||
toGameOptions: () => setCurrentPage(Page.Options),
|
toGameOptions: () => setPage(Page.Options),
|
||||||
toGang: () => setCurrentPage(Page.Gang),
|
toGang: () => setPage(Page.Gang),
|
||||||
toHacknetNodes: () => setCurrentPage(Page.Hacknet),
|
toHacknetNodes: () => setPage(Page.Hacknet),
|
||||||
toMilestones: () => setCurrentPage(Page.Milestones),
|
toMilestones: () => setPage(Page.Milestones),
|
||||||
toResleeves: () => setCurrentPage(Page.Resleeves),
|
toResleeves: () => setPage(Page.Resleeves),
|
||||||
toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => {
|
toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => {
|
||||||
setEditorOptions({
|
setEditorOptions({
|
||||||
files,
|
files,
|
||||||
vim: !!options?.vim,
|
vim: !!options?.vim,
|
||||||
});
|
});
|
||||||
setCurrentPage(Page.ScriptEditor, files, options);
|
setPage(Page.ScriptEditor);
|
||||||
},
|
},
|
||||||
toSleeves: () => setCurrentPage(Page.Sleeves),
|
toSleeves: () => setPage(Page.Sleeves),
|
||||||
toStockMarket: () => setCurrentPage(Page.StockMarket),
|
toStockMarket: () => setPage(Page.StockMarket),
|
||||||
toTerminal: () => setCurrentPage(Page.Terminal),
|
toTerminal: () => setPage(Page.Terminal),
|
||||||
toTutorial: () => setCurrentPage(Page.Tutorial),
|
toTutorial: () => setPage(Page.Tutorial),
|
||||||
toJob: () => {
|
toJob: () => {
|
||||||
setLocation(Locations[player.companyName]);
|
setLocation(Locations[player.companyName]);
|
||||||
setCurrentPage(Page.Job);
|
setPage(Page.Job);
|
||||||
},
|
},
|
||||||
toCity: () => {
|
toCity: () => {
|
||||||
setCurrentPage(Page.City);
|
setPage(Page.City);
|
||||||
},
|
},
|
||||||
toTravel: () => {
|
toTravel: () => {
|
||||||
player.gotoLocation(LocationName.TravelAgency);
|
player.gotoLocation(LocationName.TravelAgency);
|
||||||
setCurrentPage(Page.Travel);
|
setPage(Page.Travel);
|
||||||
},
|
},
|
||||||
toBitVerse: (flume: boolean, quick: boolean) => {
|
toBitVerse: (flume: boolean, quick: boolean) => {
|
||||||
setFlume(flume);
|
setFlume(flume);
|
||||||
setQuick(quick);
|
setQuick(quick);
|
||||||
setCurrentPage(Page.BitVerse, flume, quick);
|
setPage(Page.BitVerse);
|
||||||
},
|
},
|
||||||
toInfiltration: (location: Location) => {
|
toInfiltration: (location: Location) => {
|
||||||
setLocation(location);
|
setLocation(location);
|
||||||
setCurrentPage(Page.Infiltration, location);
|
setPage(Page.Infiltration);
|
||||||
},
|
|
||||||
toWork: () => {
|
|
||||||
setCurrentPage(Page.Work);
|
|
||||||
},
|
},
|
||||||
|
toWork: () => setPage(Page.Work),
|
||||||
toBladeburnerCinematic: () => {
|
toBladeburnerCinematic: () => {
|
||||||
setCurrentPage(Page.BladeburnerCinematic);
|
setPage(Page.BladeburnerCinematic);
|
||||||
setCinematicText(cinematicText);
|
setCinematicText(cinematicText);
|
||||||
},
|
},
|
||||||
toLocation: (location: Location) => {
|
toLocation: (location: Location) => {
|
||||||
setLocation(location);
|
setLocation(location);
|
||||||
setCurrentPage(Page.Location, location);
|
setPage(Page.Location);
|
||||||
},
|
},
|
||||||
toStaneksGift: () => {
|
toStaneksGift: () => {
|
||||||
setCurrentPage(Page.StaneksGift);
|
setPage(Page.StaneksGift);
|
||||||
},
|
},
|
||||||
toAchievements: () => {
|
toAchievements: () => {
|
||||||
setCurrentPage(Page.Achievements);
|
setPage(Page.Achievements);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -587,20 +510,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
|||||||
<SnackbarProvider>
|
<SnackbarProvider>
|
||||||
<Overview mode={ITutorial.isRunning ? "tutorial" : "overview"}>
|
<Overview mode={ITutorial.isRunning ? "tutorial" : "overview"}>
|
||||||
{!ITutorial.isRunning ? (
|
{!ITutorial.isRunning ? (
|
||||||
<CharacterOverview
|
<CharacterOverview save={() => saveObject.saveGame()} killScripts={killAllScripts} />
|
||||||
save={() => saveObject.saveGame()}
|
|
||||||
killScripts={killAllScripts}
|
|
||||||
router={Router}
|
|
||||||
allowBackButton={withSidebar} />
|
|
||||||
) : (
|
|
||||||
<InteractiveTutorialRoot />
|
|
||||||
)}
|
|
||||||
</Overview>
|
|
||||||
{withSidebar ? (
|
|
||||||
<Box display="flex" flexDirection="row" width="100%">
|
|
||||||
<SidebarRoot player={player} router={Router} page={page} />
|
|
||||||
<Box className={classes.root}>{mainPage}</Box>
|
|
||||||
</Box>
|
|
||||||
) : (
|
) : (
|
||||||
<Box className={classes.root}>{mainPage}</Box>
|
<Box className={classes.root}>{mainPage}</Box>
|
||||||
)}
|
)}
|
||||||
|
@ -7,6 +7,7 @@ import createStyles from "@mui/styles/createStyles";
|
|||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { Reputation } from "./Reputation";
|
import { Reputation } from "./Reputation";
|
||||||
import { KillScriptsModal } from "./KillScriptsModal";
|
import { KillScriptsModal } from "./KillScriptsModal";
|
||||||
|
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
|
|
||||||
import Table from "@mui/material/Table";
|
import Table from "@mui/material/Table";
|
||||||
import TableBody from "@mui/material/TableBody";
|
import TableBody from "@mui/material/TableBody";
|
||||||
@ -16,21 +17,19 @@ import Typography from "@mui/material/Typography";
|
|||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import SaveIcon from "@mui/icons-material/Save";
|
import SaveIcon from "@mui/icons-material/Save";
|
||||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|
||||||
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
import ClearAllIcon from "@mui/icons-material/ClearAll";
|
||||||
|
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { use } from "../Context";
|
import { use } from "../Context";
|
||||||
import { StatsProgressOverviewCell } from "./StatsProgressBar";
|
import { StatsProgressOverviewCell } from "./StatsProgressBar";
|
||||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||||
import { IRouter, Page } from "../Router";
|
|
||||||
import { Box, Tooltip } from "@mui/material";
|
import { Box, Tooltip } from "@mui/material";
|
||||||
|
import { CONSTANTS } from "../../Constants";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
save: () => void;
|
save: () => void;
|
||||||
killScripts: () => void;
|
killScripts: () => void;
|
||||||
router: IRouter;
|
|
||||||
allowBackButton: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Intelligence(): React.ReactElement {
|
function Intelligence(): React.ReactElement {
|
||||||
@ -79,105 +78,132 @@ function Bladeburner(): React.ReactElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WorkInProgressOverviewProps {
|
||||||
|
tooltip: React.ReactNode;
|
||||||
|
header: React.ReactNode;
|
||||||
|
children: React.ReactNode;
|
||||||
|
onClickFocus: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WorkInProgressOverview({
|
||||||
|
tooltip,
|
||||||
|
children,
|
||||||
|
onClickFocus,
|
||||||
|
header,
|
||||||
|
}: WorkInProgressOverviewProps): React.ReactElement {
|
||||||
|
const classes = useStyles();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.workCell }}>
|
||||||
|
<Tooltip title={<>{tooltip}</>}>
|
||||||
|
<Typography className={classes.workHeader} sx={{ pt: 1, pb: 0.5 }}>
|
||||||
|
{header}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.workCell }}>
|
||||||
|
<Typography className={classes.workSubtitles}>{children}</Typography>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell component="th" scope="row" align="center" colSpan={2} classes={{ root: classes.cellNone }}>
|
||||||
|
<Button sx={{ mt: 1 }} onClick={onClickFocus}>
|
||||||
|
Focus
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function Work(): React.ReactElement {
|
function Work(): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
const router = use.Router();
|
const router = use.Router();
|
||||||
const classes = useStyles();
|
const onClickFocus = (): void => {
|
||||||
|
player.startFocusing();
|
||||||
|
router.toWork();
|
||||||
|
};
|
||||||
|
|
||||||
if (!player.isWorking || player.focus) return <></>;
|
if (!player.isWorking || player.focus) return <></>;
|
||||||
|
|
||||||
if (player.className !== "") {
|
let details = <></>;
|
||||||
return (
|
let header = <></>;
|
||||||
|
let innerText = <></>;
|
||||||
|
if (player.workType === CONSTANTS.WorkTypeCompanyPartTime || player.workType === CONSTANTS.WorkTypeCompany) {
|
||||||
|
details = (
|
||||||
<>
|
<>
|
||||||
<TableRow>
|
{player.jobs[player.companyName]} at <strong>{player.companyName}</strong>
|
||||||
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Typography>Work in progress:</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Typography>{player.className}</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell component="th" scope="row" align="center" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
player.startFocusing();
|
|
||||||
router.toWork();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Focus
|
|
||||||
</Button>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
header = (
|
||||||
|
<>
|
||||||
if (player.createProgramName !== "") {
|
Working at <strong>{player.companyName}</strong>
|
||||||
return (
|
</>
|
||||||
|
);
|
||||||
|
innerText = (
|
||||||
|
<>
|
||||||
|
+<Reputation reputation={player.workRepGained} /> rep
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (player.workType === CONSTANTS.WorkTypeFaction) {
|
||||||
|
details = (
|
||||||
|
<>
|
||||||
|
{player.factionWorkType} for <strong>{player.currentWorkFactionName}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
header = (
|
||||||
|
<>
|
||||||
|
Working for <strong>{player.currentWorkFactionName}</strong>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
innerText = (
|
||||||
|
<>
|
||||||
|
+<Reputation reputation={player.workRepGained} /> rep
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (player.workType === CONSTANTS.WorkTypeStudyClass) {
|
||||||
|
details = <>{player.workType}</>;
|
||||||
|
header = <>You are {player.className}</>;
|
||||||
|
innerText = <>{convertTimeMsToTimeElapsedString(player.timeWorked)}</>;
|
||||||
|
} else if (player.workType === CONSTANTS.WorkTypeCreateProgram) {
|
||||||
|
details = <>Coding {player.createProgramName}</>;
|
||||||
|
header = <>Creating a program</>;
|
||||||
|
innerText = (
|
||||||
<>
|
<>
|
||||||
<TableRow>
|
|
||||||
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Typography>Work in progress:</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Typography>
|
|
||||||
{player.createProgramName}{" "}
|
{player.createProgramName}{" "}
|
||||||
{((player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100).toFixed(2)}%
|
{((player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100).toFixed(2)}%
|
||||||
</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell component="th" scope="row" align="center" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
player.startFocusing();
|
|
||||||
router.toWork();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Focus
|
|
||||||
</Button>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<WorkInProgressOverview tooltip={details} header={header} onClickFocus={onClickFocus}>
|
||||||
<TableRow>
|
{innerText}
|
||||||
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
|
</WorkInProgressOverview>
|
||||||
<Typography>Work in progress:</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Typography>
|
|
||||||
+<Reputation reputation={player.workRepGained} /> rep
|
|
||||||
</Typography>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell component="th" scope="row" align="center" colSpan={2} classes={{ root: classes.cellNone }}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
player.startFocusing();
|
|
||||||
router.toWork();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Focus
|
|
||||||
</Button>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
workCell: {
|
||||||
|
textAlign: "center",
|
||||||
|
maxWidth: "200px",
|
||||||
|
borderBottom: "none",
|
||||||
|
padding: 0,
|
||||||
|
margin: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
workHeader: {
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
},
|
||||||
|
|
||||||
|
workSubtitles: {
|
||||||
|
fontSize: "0.8rem",
|
||||||
|
},
|
||||||
|
|
||||||
cellNone: {
|
cellNone: {
|
||||||
borderBottom: "none",
|
borderBottom: "none",
|
||||||
padding: 0,
|
padding: 0,
|
||||||
@ -210,7 +236,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
|
|
||||||
export { useStyles as characterOverviewStyles };
|
export { useStyles as characterOverviewStyles };
|
||||||
|
|
||||||
export function CharacterOverview({ save, killScripts, router, allowBackButton }: IProps): React.ReactElement {
|
export function CharacterOverview({ save, killScripts }: IProps): React.ReactElement {
|
||||||
const [killOpen, setKillOpen] = useState(false);
|
const [killOpen, setKillOpen] = useState(false);
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
|
|
||||||
@ -249,9 +275,6 @@ export function CharacterOverview({ save, killScripts, router, allowBackButton }
|
|||||||
player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
|
player.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier,
|
||||||
);
|
);
|
||||||
|
|
||||||
const previousPageName = router.previousPage() < 0
|
|
||||||
? '' : Page[router.previousPage() ?? 0].replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table sx={{ display: "block", m: 1 }}>
|
<Table sx={{ display: "block", m: 1 }}>
|
||||||
@ -428,24 +451,15 @@ export function CharacterOverview({ save, killScripts, router, allowBackButton }
|
|||||||
<Bladeburner />
|
<Bladeburner />
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<Box sx={{ display: 'flex', borderTop: `1px solid ${Settings.theme.welllight}` }}>
|
<Box sx={{ display: "flex", borderTop: `1px solid ${Settings.theme.welllight}` }}>
|
||||||
<Box sx={{ display: 'flex', flex: 1, justifyContent: 'flex-start', alignItems: 'center' }}>
|
<Box sx={{ display: "flex", flex: 1, justifyContent: "flex-start", alignItems: "center" }}>
|
||||||
<IconButton onClick={save}>
|
<IconButton onClick={save}>
|
||||||
<Tooltip title="Save game">
|
<Tooltip title="Save game">
|
||||||
<SaveIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
|
<SaveIcon color={Settings.AutosaveInterval !== 0 ? "primary" : "error"} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{allowBackButton && (
|
|
||||||
<IconButton
|
|
||||||
disabled={!previousPageName}
|
|
||||||
onClick={() => router.toPreviousPage()}>
|
|
||||||
<Tooltip title={previousPageName ? `Go back to "${previousPageName}"` : ''}>
|
|
||||||
<ArrowBackIcon />
|
|
||||||
</Tooltip>
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: 'flex', flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>
|
<Box sx={{ display: "flex", flex: 1, justifyContent: "flex-end", alignItems: "center" }}>
|
||||||
<IconButton onClick={() => setKillOpen(true)}>
|
<IconButton onClick={() => setKillOpen(true)}>
|
||||||
<Tooltip title="Kill all running scripts">
|
<Tooltip title="Kill all running scripts">
|
||||||
<ClearAllIcon color="error" />
|
<ClearAllIcon color="error" />
|
||||||
|
@ -25,20 +25,22 @@ export function CorruptableText(props: IProps): JSX.Element {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let counter = 5;
|
let counter = 5;
|
||||||
const id = setInterval(() => {
|
const timers: number[] = [];
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
counter--;
|
counter--;
|
||||||
if (counter > 0) return;
|
if (counter > 0) return;
|
||||||
counter = Math.random() * 5;
|
counter = Math.random() * 5;
|
||||||
const index = Math.random() * content.length;
|
const index = Math.random() * content.length;
|
||||||
const letter = content.charAt(index);
|
const letter = content.charAt(index);
|
||||||
setContent((content) => replace(content, index, randomize(letter)));
|
setContent((content) => replace(content, index, randomize(letter)));
|
||||||
setTimeout(() => {
|
timers.push(window.setTimeout(() => {
|
||||||
setContent((content) => replace(content, index, letter));
|
setContent((content) => replace(content, index, letter));
|
||||||
}, 500);
|
}, 500));
|
||||||
}, 20);
|
}, 20);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(id);
|
clearInterval(intervalId);
|
||||||
|
timers.forEach((timerId) => clearTimeout(timerId));
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -529,12 +529,12 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={6}>
|
<Box sx={{ display: 'grid', width: 'fit-content', height: 'fit-content' }}>
|
||||||
<Box>
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
|
||||||
<Button onClick={() => props.save()}>Save Game</Button>
|
<Button onClick={() => props.save()}>Save Game</Button>
|
||||||
<Button onClick={() => setDeleteOpen(true)}>Delete Game</Button>
|
<Button onClick={() => setDeleteOpen(true)}>Delete Game</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
|
||||||
<Tooltip title={<Typography>Export your game to a text file.</Typography>}>
|
<Tooltip title={<Typography>Export your game to a text file.</Typography>}>
|
||||||
<Button onClick={() => props.export()}>
|
<Button onClick={() => props.export()}>
|
||||||
<DownloadIcon color="primary" />
|
<DownloadIcon color="primary" />
|
||||||
@ -571,7 +571,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box sx={{ display: 'grid' }}>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<Typography>
|
||||||
@ -586,7 +586,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
<Button onClick={() => props.forceKill()}>Force kill all active scripts</Button>
|
<Button onClick={() => props.forceKill()}>Force kill all active scripts</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<Typography>
|
<Typography>
|
||||||
@ -613,7 +613,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
<Button onClick={() => setDiagnosticOpen(true)}>Diagnose files</Button>
|
<Button onClick={() => setDiagnosticOpen(true)}>Diagnose files</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr' }}>
|
||||||
<Button onClick={() => setThemeEditorOpen(true)}>Theme editor</Button>
|
<Button onClick={() => setThemeEditorOpen(true)}>Theme editor</Button>
|
||||||
<Button onClick={() => setStyleEditorOpen(true)}>Style editor</Button>
|
<Button onClick={() => setStyleEditorOpen(true)}>Style editor</Button>
|
||||||
</Box>
|
</Box>
|
||||||
@ -637,7 +637,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
|||||||
<Typography>Incremental game plaza</Typography>
|
<Typography>Incremental game plaza</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
<FileDiagnosticModal open={diagnosticOpen} onClose={() => setDiagnosticOpen(false)} />
|
<FileDiagnosticModal open={diagnosticOpen} onClose={() => setDiagnosticOpen(false)} />
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
|
@ -39,12 +39,14 @@ export function LogBoxManager(): React.ReactElement {
|
|||||||
() =>
|
() =>
|
||||||
LogBoxEvents.subscribe((script: RunningScript) => {
|
LogBoxEvents.subscribe((script: RunningScript) => {
|
||||||
const id = script.server + "-" + script.filename + script.args.map((x: any): string => `${x}`).join("-");
|
const id = script.server + "-" + script.filename + script.args.map((x: any): string => `${x}`).join("-");
|
||||||
if (logs.find((l) => l.id === id)) return;
|
if (logs.find((l) => l.id === id)) close(id);
|
||||||
|
Promise.resolve().then(() => {
|
||||||
logs.push({
|
logs.push({
|
||||||
id: id,
|
id: id,
|
||||||
script: script,
|
script: script,
|
||||||
});
|
});
|
||||||
rerender();
|
rerender();
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
@ -7,12 +7,13 @@ import Typography from "@mui/material/Typography";
|
|||||||
import Paper from "@mui/material/Paper";
|
import Paper from "@mui/material/Paper";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import ReplyIcon from "@mui/icons-material/Reply";
|
import ReplyIcon from "@mui/icons-material/Reply";
|
||||||
import SaveIcon from '@mui/icons-material/Save';
|
import SaveIcon from "@mui/icons-material/Save";
|
||||||
|
|
||||||
import { ThemeEvents } from "./Theme";
|
import { ThemeEvents } from "./Theme";
|
||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import { IStyleSettings, defaultStyles } from "../../Settings/Styles";
|
import { defaultStyles } from "../../Settings/Styles";
|
||||||
import { Tooltip } from "@mui/material";
|
import { Tooltip } from "@mui/material";
|
||||||
|
import { IStyleSettings } from "../../ScriptEditor/NetscriptDefinitions";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -32,9 +33,9 @@ function FontFamilyField({ value, onChange, refreshId } : FontFamilyProps): Reac
|
|||||||
function update(newValue: React.CSSProperties["fontFamily"]): void {
|
function update(newValue: React.CSSProperties["fontFamily"]): void {
|
||||||
setFontFamily(newValue);
|
setFontFamily(newValue);
|
||||||
if (!newValue) {
|
if (!newValue) {
|
||||||
setErrorText('Must have a value');
|
setErrorText("Must have a value");
|
||||||
} else {
|
} else {
|
||||||
setErrorText('');
|
setErrorText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ function FontFamilyField({ value, onChange, refreshId } : FontFamilyProps): Reac
|
|||||||
onChange={onTextChange}
|
onChange={onTextChange}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LineHeightProps {
|
interface LineHeightProps {
|
||||||
@ -71,11 +72,11 @@ function LineHeightField({ value, onChange, refreshId } : LineHeightProps): Reac
|
|||||||
function update(newValue: React.CSSProperties["lineHeight"]): void {
|
function update(newValue: React.CSSProperties["lineHeight"]): void {
|
||||||
setLineHeight(newValue);
|
setLineHeight(newValue);
|
||||||
if (!newValue) {
|
if (!newValue) {
|
||||||
setErrorText('Must have a value');
|
setErrorText("Must have a value");
|
||||||
} else if (isNaN(Number(newValue))) {
|
} else if (isNaN(Number(newValue))) {
|
||||||
setErrorText('Must be a number');
|
setErrorText("Must be a number");
|
||||||
} else {
|
} else {
|
||||||
setErrorText('');
|
setErrorText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ function LineHeightField({ value, onChange, refreshId } : LineHeightProps): Reac
|
|||||||
helperText={errorText}
|
helperText={errorText}
|
||||||
onChange={onTextChange}
|
onChange={onTextChange}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StyleEditorModal(props: IProps): React.ReactElement {
|
export function StyleEditorModal(props: IProps): React.ReactElement {
|
||||||
@ -115,7 +116,7 @@ export function StyleEditorModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setDefaults(): void {
|
function setDefaults(): void {
|
||||||
const styles = {...defaultStyles}
|
const styles = { ...defaultStyles };
|
||||||
setCustomStyle(styles);
|
setCustomStyle(styles);
|
||||||
persistToSettings(styles);
|
persistToSettings(styles);
|
||||||
setRefreshId(refreshId + 1);
|
setRefreshId(refreshId + 1);
|
||||||
@ -132,14 +133,21 @@ export function StyleEditorModal(props: IProps): React.ReactElement {
|
|||||||
<Modal open={props.open} onClose={props.onClose}>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<Typography variant="h6">Styles Editor</Typography>
|
<Typography variant="h6">Styles Editor</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
WARNING: Changing styles <strong>may mess up</strong> the interface. Drastic changes are <strong>NOT recommended</strong>.
|
WARNING: Changing styles <strong>may mess up</strong> the interface. Drastic changes are{" "}
|
||||||
|
<strong>NOT recommended</strong>.
|
||||||
</Typography>
|
</Typography>
|
||||||
<Paper sx={{ p: 2, my: 2 }}>
|
<Paper sx={{ p: 2, my: 2 }}>
|
||||||
<FontFamilyField value={customStyle.fontFamily} refreshId={refreshId}
|
<FontFamilyField
|
||||||
onChange={(value, error) => update({ ...customStyle, fontFamily: value }, error)} />
|
value={customStyle.fontFamily}
|
||||||
|
refreshId={refreshId}
|
||||||
|
onChange={(value, error) => update({ ...customStyle, fontFamily: value as any }, error)}
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
<LineHeightField value={customStyle.lineHeight} refreshId={refreshId}
|
<LineHeightField
|
||||||
onChange={(value, error) => update({ ...customStyle, lineHeight: value }, error)} />
|
value={customStyle.lineHeight}
|
||||||
|
refreshId={refreshId}
|
||||||
|
onChange={(value, error) => update({ ...customStyle, lineHeight: value as any }, error)}
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
<ButtonGroup sx={{ my: 1 }}>
|
<ButtonGroup sx={{ my: 1 }}>
|
||||||
<Button onClick={setDefaults} startIcon={<ReplyIcon />} color="secondary" variant="outlined">
|
<Button onClick={setDefaults} startIcon={<ReplyIcon />} color="secondary" variant="outlined">
|
||||||
|
@ -53,9 +53,6 @@ export interface IRouter {
|
|||||||
// toRedPill(): void;
|
// toRedPill(): void;
|
||||||
// toworkInProgress(): void;
|
// toworkInProgress(): void;
|
||||||
page(): Page;
|
page(): Page;
|
||||||
previousPage(): Page;
|
|
||||||
clearHistory(): void;
|
|
||||||
toPreviousPage(fallback?: (...args: any[]) => void): void;
|
|
||||||
toActiveScripts(): void;
|
toActiveScripts(): void;
|
||||||
toAugmentations(): void;
|
toAugmentations(): void;
|
||||||
toBitVerse(flume: boolean, quick: boolean): void;
|
toBitVerse(flume: boolean, quick: boolean): void;
|
||||||
|
@ -36,12 +36,12 @@ export function WorkInProgressRoot(): React.ReactElement {
|
|||||||
const faction = Factions[player.currentWorkFactionName];
|
const faction = Factions[player.currentWorkFactionName];
|
||||||
if (player.workType == CONSTANTS.WorkTypeFaction) {
|
if (player.workType == CONSTANTS.WorkTypeFaction) {
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
|
router.toFaction(faction);
|
||||||
player.finishFactionWork(true);
|
player.finishFactionWork(true);
|
||||||
router.toPreviousPage(() => router.toFaction(faction));
|
|
||||||
}
|
}
|
||||||
function unfocus(): void {
|
function unfocus(): void {
|
||||||
|
router.toFaction(faction);
|
||||||
player.stopFocusing();
|
player.stopFocusing();
|
||||||
router.toPreviousPage(() => router.toFaction(faction));
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
||||||
@ -120,12 +120,13 @@ export function WorkInProgressRoot(): React.ReactElement {
|
|||||||
if (player.className !== "") {
|
if (player.className !== "") {
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
player.finishClass(true);
|
player.finishClass(true);
|
||||||
router.toPreviousPage(() => router.toCity());
|
router.toCity();
|
||||||
}
|
}
|
||||||
|
|
||||||
function unfocus(): void {
|
function unfocus(): void {
|
||||||
|
router.toFaction(faction);
|
||||||
|
router.toCity();
|
||||||
player.stopFocusing();
|
player.stopFocusing();
|
||||||
router.toPreviousPage(() => router.toCity());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let stopText = "";
|
let stopText = "";
|
||||||
@ -211,11 +212,11 @@ export function WorkInProgressRoot(): React.ReactElement {
|
|||||||
|
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
player.finishWork(true);
|
player.finishWork(true);
|
||||||
router.toPreviousPage(() => router.toJob());
|
router.toJob();
|
||||||
}
|
}
|
||||||
function unfocus(): void {
|
function unfocus(): void {
|
||||||
player.stopFocusing();
|
player.stopFocusing();
|
||||||
router.toPreviousPage(() => router.toJob());
|
router.toJob();
|
||||||
}
|
}
|
||||||
|
|
||||||
const position = player.jobs[player.companyName];
|
const position = player.jobs[player.companyName];
|
||||||
@ -303,11 +304,11 @@ export function WorkInProgressRoot(): React.ReactElement {
|
|||||||
if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
|
if (player.workType == CONSTANTS.WorkTypeCompanyPartTime) {
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
player.finishWorkPartTime(true);
|
player.finishWorkPartTime(true);
|
||||||
router.toPreviousPage(() => router.toJob());
|
router.toJob();
|
||||||
}
|
}
|
||||||
function unfocus(): void {
|
function unfocus(): void {
|
||||||
player.stopFocusing();
|
player.stopFocusing();
|
||||||
router.toPreviousPage(() => router.toJob());
|
router.toJob();
|
||||||
}
|
}
|
||||||
const comp = Companies[player.companyName];
|
const comp = Companies[player.companyName];
|
||||||
let companyRep = 0;
|
let companyRep = 0;
|
||||||
@ -439,11 +440,11 @@ export function WorkInProgressRoot(): React.ReactElement {
|
|||||||
if (player.createProgramName !== "") {
|
if (player.createProgramName !== "") {
|
||||||
function cancel(): void {
|
function cancel(): void {
|
||||||
player.finishCreateProgramWork(true);
|
player.finishCreateProgramWork(true);
|
||||||
router.toPreviousPage(() => router.toTerminal());
|
router.toTerminal();
|
||||||
}
|
}
|
||||||
function unfocus(): void {
|
function unfocus(): void {
|
||||||
|
router.toTerminal();
|
||||||
player.stopFocusing();
|
player.stopFocusing();
|
||||||
router.toPreviousPage(() => router.toTerminal());
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
|
/* Generic Reviver, toJSON, and fromJSON functions used for saving and loading objects */
|
||||||
|
|
||||||
|
import { validateObject } from "./Validator";
|
||||||
|
|
||||||
interface IReviverValue {
|
interface IReviverValue {
|
||||||
ctor: string;
|
ctor: string;
|
||||||
data: any;
|
data: any;
|
||||||
@ -26,7 +28,11 @@ export function Reviver(key: string, value: IReviverValue | null): any {
|
|||||||
const ctor = Reviver.constructors[value.ctor];
|
const ctor = Reviver.constructors[value.ctor];
|
||||||
|
|
||||||
if (typeof ctor === "function" && typeof ctor.fromJSON === "function") {
|
if (typeof ctor === "function" && typeof ctor.fromJSON === "function") {
|
||||||
return ctor.fromJSON(value);
|
const obj = ctor.fromJSON(value);
|
||||||
|
if (ctor.validationData !== undefined) {
|
||||||
|
validateObject(obj, ctor.validationData);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
78
src/utils/Validator.ts
Normal file
78
src/utils/Validator.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
export type ObjectValidator<T> = {
|
||||||
|
[key in keyof T]?: ParameterValidator<T, keyof T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParameterValidatorObject<Type, Key extends keyof Type> {
|
||||||
|
default?: any;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
func?: (obj: Type, validator: ObjectValidator<Type>, key: Key) => void;
|
||||||
|
}
|
||||||
|
type ParameterValidatorFunction<Type, Key extends keyof Type> = (obj: Type, key: Key) => void;
|
||||||
|
type ParameterValidator<Type, Key extends keyof Type> = ParameterValidatorObject<Type, Key> | ParameterValidatorFunction<Type, Key>
|
||||||
|
|
||||||
|
export function validateObject<Type extends Record<string, unknown>, Key extends keyof Type>(obj: Type, validator: ObjectValidator<Type>): void {
|
||||||
|
for (const key of Object.keys(validator) as Key[]) {
|
||||||
|
const paramValidator = validator[key];
|
||||||
|
if (paramValidator !== undefined) {
|
||||||
|
if (typeof paramValidator === 'function') {
|
||||||
|
paramValidator(obj, key);
|
||||||
|
} else {
|
||||||
|
if (paramValidator.func !== undefined) {
|
||||||
|
paramValidator.func(obj, validator, key);
|
||||||
|
} else {
|
||||||
|
if ((typeof obj[key]) !== (typeof paramValidator.default)) {
|
||||||
|
obj[key] = paramValidator.default
|
||||||
|
}
|
||||||
|
if (typeof obj[key] === 'number' && paramValidator.min !== undefined) {
|
||||||
|
if (obj[key] < paramValidator.min) obj[key] = paramValidator.min as Type[Key];
|
||||||
|
}
|
||||||
|
if (typeof obj[key] === 'number' && paramValidator.max !== undefined) {
|
||||||
|
if (obj[key] > paramValidator.max) obj[key] = paramValidator.max as Type[Key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function minMax<Type, Key extends keyof Type>(def: number, min: number, max: number): (obj: Type, key: Key & keyof Type) => void {
|
||||||
|
return (obj, key) => {
|
||||||
|
if (typeof obj[key] !== 'number') {
|
||||||
|
obj[key] = def as unknown as Type[Key];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((obj[key] as unknown as number) < min) {
|
||||||
|
obj[key] = min as unknown as Type[Key];
|
||||||
|
}
|
||||||
|
if ((obj[key] as unknown as number) > max) {
|
||||||
|
obj[key] = max as unknown as Type[Key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function oneOf<Type, Key extends keyof Type, Value>(def: Value, options: Value[]): (obj: Type, key: Key & keyof Type) => void {
|
||||||
|
return (obj, key) => {
|
||||||
|
if (typeof obj[key] !== typeof def) {
|
||||||
|
obj[key] = def as unknown as Type[Key];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!options.includes(obj[key] as unknown as Value)) {
|
||||||
|
obj[key] = def as unknown as Type[Key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function subsetOf<Type, Key extends keyof Type, Value>(options: Value[]): (obj: Type, key: Key & keyof Type) => void {
|
||||||
|
return (obj, key) => {
|
||||||
|
if (typeof obj[key] !== 'object' || !Array.isArray(obj[key])) {
|
||||||
|
obj[key] = [] as unknown as Type[Key];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const validValues: Value[] = [];
|
||||||
|
for (const value of obj[key] as unknown as Value[]) {
|
||||||
|
if (options.includes(value)) validValues.push(value);
|
||||||
|
}
|
||||||
|
obj[key] = validValues as unknown as Type[Key];
|
||||||
|
};
|
||||||
|
}
|
29
test/Script/Script.test.ts
Normal file
29
test/Script/Script.test.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import { jest, describe, expect, test } from "@jest/globals";
|
||||||
|
|
||||||
|
import { Script } from "../../src/Script/Script";
|
||||||
|
import { Player } from "../../src/Player";
|
||||||
|
|
||||||
|
jest.mock(`!!raw-loader!../NetscriptDefinitions.d.ts`, () => "", {
|
||||||
|
virtual: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const code = `/** @param {NS} ns **/
|
||||||
|
export async function main(ns) {
|
||||||
|
ns.print(ns.getWeakenTime('n00dles'));
|
||||||
|
}`;
|
||||||
|
|
||||||
|
describe("Validate Save Script Works", function () {
|
||||||
|
|
||||||
|
it("Save", function () {
|
||||||
|
const server = "home";
|
||||||
|
const filename = "test.js";
|
||||||
|
const player = Player;
|
||||||
|
const script = new Script();
|
||||||
|
script.saveScript(player, filename, code, server, []);
|
||||||
|
|
||||||
|
expect(script.filename).toEqual(filename)
|
||||||
|
expect(script.code).toEqual(code)
|
||||||
|
expect(script.server).toEqual(server)
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user