Compare commits

...

28 Commits

Author SHA1 Message Date
lucebac 9e85068cbb HACKNET: fix application of limits in cost functions (#2696) 2026-04-28 00:16:45 -07:00
The Gail ed6df3dfa7 MISC: Clarify what "backdoor" does in "help" command (#2694) 2026-04-28 00:14:39 -07:00
Michael Ficocelli 253b8b2672 DNET: Adjust charisma augmentation power (#2698) 2026-04-28 00:14:17 -07:00
catloversg 3146c50edc UI: Activate recovery mode on critical BN prestige bugs (#2699) 2026-04-28 00:11:23 -07:00
catloversg 4a91a71891 UI: Reload immediately after importing, deleting save data or killing all scripts (#2697) 2026-04-28 00:08:13 -07:00
catloversg 99afa3cd50 CORPORATION: Prevent duplicate processing of boost materials (#2695) 2026-04-28 00:06:12 -07:00
catloversg 750f79adf3 UI: Use "success" theme color for low infiltration difficulty instead of "primary" (#2693) 2026-04-27 23:55:35 -07:00
catloversg dbb20c1526 CODEBASE: Consistently check when to show intelligence skill (#2692) 2026-04-27 23:54:55 -07:00
catloversg 60fb303915 UI: Show effective amount of shared RAM when using UI (#2691) 2026-04-27 23:54:26 -07:00
catloversg 32fed9f142 BUGFIX: DarkscapeNavigator program is not granted on BN prestige when having SF15 (#2690) 2026-04-27 23:53:01 -07:00
catloversg 33ffc2107c DOCUMENTATION: Improve coding contract documentation (#2689) 2026-04-27 23:51:51 -07:00
catloversg 36e1adf2d2 API: Standardize "nextCompletion" promise in tasks (#2687) 2026-04-27 23:49:53 -07:00
David Walker ee3014b029 NETSCRIPT: Better error message for port serialization failure (#2688) 2026-04-24 13:55:39 -07:00
catloversg 112d317fd2 TOOLS: Ignore .DS_Store files when generating pages.ts (#2685) 2026-04-23 15:49:39 -07:00
catloversg 355f650367 CORPORATION: Remove max width of division list (#2686) 2026-04-23 15:42:33 -07:00
Michael Ficocelli a4b0f22a2e DNET: Update changelog (#2679) 2026-04-20 16:57:23 -07:00
catloversg 2aa5092d85 BLADEBURNER: Store BlackOp team count in save data (#2675) 2026-04-19 12:20:08 -07:00
catloversg a7409a01cc UI: Ensure intelligence override is a positive integer (#2673) 2026-04-19 12:09:11 -07:00
catloversg a99109d9c7 BUGFIX: Follow-up to #2660 (#2666) 2026-04-19 12:06:54 -07:00
catloversg 95af138c39 BLADEBURNER: Restrict team count of Ops/BlackOps to total team size (#2672) 2026-04-17 14:32:22 -07:00
Mathekatze f5bbc26495 MISC: Clear recent scripts when installing augmentations (#2670) 2026-04-17 14:20:07 -07:00
catloversg f8ec7f4294 CODEBASE: Fix passive event listener warning (#2671) 2026-04-17 14:09:12 -07:00
catloversg c06c6590c9 BUGFIX: calculateExp throws errors in edge cases (#2667) 2026-04-17 00:57:50 -07:00
catloversg 45bce6e45e MISC: Reduce achievements check interval (#2650) 2026-04-15 18:47:29 -07:00
catloversg c21d1f44b2 UI: Add button to open Faction page from Gang UI (#2655) 2026-04-14 15:53:40 -07:00
catloversg 956e00f789 BUGFIX: Intelligence data is incorrectly migrated when Intelligence is not unlocked (#2660) 2026-04-14 15:20:01 -07:00
catloversg c5536d252b MISC: Update "Last updated" date in changelog (#2658) 2026-04-13 13:10:59 +07:00
catloversg a99ca64455 MISC: Update changelog (#2657) 2026-04-13 11:32:39 +07:00
119 changed files with 3437 additions and 1321 deletions
+8 -6
View File
@@ -4,12 +4,10 @@
## BaseTask interface
Base interface of all tasks.
**Signature:**
```typescript
export interface BaseTask
interface BaseTask
```
## Properties
@@ -37,7 +35,7 @@ Description
</th></tr></thead>
<tbody><tr><td>
[cyclesWorked](./bitburner.basetask.cyclesworked.md)
[nextCompletion](./bitburner.basetask.nextcompletion.md)
</td><td>
@@ -45,12 +43,16 @@ Description
</td><td>
number
Promise&lt;void&gt;
</td><td>
The number of game engine cycles has passed since this task started. 1 engine cycle = 200ms.
This promise resolves when the task completes or is canceled.
Tasks that do not track progress, such as studying or working for a company, are non-completable, i.e., they continue indefinitely until canceled. The `nextCompletion` promise of these tasks resolves only when they are canceled.
Among completable tasks, some are repeatable, i.e., they automatically restart after completion. The `nextCompletion` promise of these tasks resolves on the next completion or when they are canceled.
</td></tr>
@@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [BaseTask](./bitburner.basetask.md) &gt; [nextCompletion](./bitburner.basetask.nextcompletion.md)
## BaseTask.nextCompletion property
This promise resolves when the task completes or is canceled.
Tasks that do not track progress, such as studying or working for a company, are non-completable, i.e., they continue indefinitely until canceled. The `nextCompletion` promise of these tasks resolves only when they are canceled.
Among completable tasks, some are repeatable, i.e., they automatically restart after completion. The `nextCompletion` promise of these tasks resolves on the next completion or when they are canceled.
**Signature:**
```typescript
nextCompletion: Promise<void>;
```
+1 -1
View File
@@ -12,7 +12,7 @@ Default value:
- All boolean options: false
If you specify intelligenceOverride, it must be a non-negative integer.
If you specify intelligenceOverride, it must be a positive integer.
**Signature:**
+2 -2
View File
@@ -9,9 +9,9 @@ Company Work
**Signature:**
```typescript
export interface CompanyWorkTask extends BaseTask
interface CompanyWorkTask extends PlayerBaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
**Extends:** [PlayerBaseTask](./bitburner.playerbasetask.md)
## Remarks
+2 -2
View File
@@ -9,9 +9,9 @@ Create Program
**Signature:**
```typescript
export interface CreateProgramWorkTask extends BaseTask
interface CreateProgramWorkTask extends PlayerBaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
**Extends:** [PlayerBaseTask](./bitburner.playerbasetask.md)
## Remarks
+2 -2
View File
@@ -9,9 +9,9 @@ Crime
**Signature:**
```typescript
export interface CrimeTask extends BaseTask
interface CrimeTask extends PlayerBaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
**Extends:** [PlayerBaseTask](./bitburner.playerbasetask.md)
## Remarks
+2 -2
View File
@@ -9,9 +9,9 @@ Faction Work
**Signature:**
```typescript
export interface FactionWorkTask extends BaseTask
interface FactionWorkTask extends PlayerBaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
**Extends:** [PlayerBaseTask](./bitburner.playerbasetask.md)
## Remarks
@@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [GraftingTask](./bitburner.graftingtask.md) &gt; [completion](./bitburner.graftingtask.completion.md)
## GraftingTask.completion property
This promise resolves when the task is complete.
**Signature:**
```typescript
completion: Promise<void>;
```
+2 -21
View File
@@ -9,9 +9,9 @@ Grafting Work
**Signature:**
```typescript
export interface GraftingTask extends BaseTask
interface GraftingTask extends PlayerBaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
**Extends:** [PlayerBaseTask](./bitburner.playerbasetask.md)
## Remarks
@@ -56,25 +56,6 @@ string
</td><td>
</td></tr>
<tr><td>
[completion](./bitburner.graftingtask.completion.md)
</td><td>
</td><td>
Promise&lt;void&gt;
</td><td>
This promise resolves when the task is complete.
</td></tr>
<tr><td>
+102 -93
View File
@@ -67,8 +67,6 @@ Player must have installed a backdoor on this server.
</td><td>
Base interface of all tasks.
</td></tr>
<tr><td>
@@ -123,7 +121,7 @@ Default value:
- All boolean options: false
If you specify intelligenceOverride, it must be a non-negative integer.
If you specify intelligenceOverride, it must be a positive integer.
</td></tr>
@@ -1111,6 +1109,17 @@ Player must have killed at least this many people.
</td></tr>
<tr><td>
[PlayerBaseTask](./bitburner.playerbasetask.md)
</td><td>
Base interface of all player tasks.
</td></tr>
<tr><td>
@@ -1272,6 +1281,66 @@ Skills formulas
Sleeve API
</td></tr>
<tr><td>
[SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveClassTask](./bitburner.sleeveclasstask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveCompanyTask](./bitburner.sleevecompanytask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveCrimeTask](./bitburner.sleevecrimetask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveFactionTask](./bitburner.sleevefactiontask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md)
</td><td>
</td></tr>
<tr><td>
@@ -1282,6 +1351,36 @@ Sleeve API
</td></tr>
<tr><td>
[SleeveRecoveryTask](./bitburner.sleeverecoverytask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveSupportTask](./bitburner.sleevesupporttask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveSynchroTask](./bitburner.sleevesynchrotask.md)
</td><td>
</td></tr>
<tr><td>
@@ -2206,96 +2305,6 @@ Use React.createElement to make the ReactElement type, see [creating an element
</td></tr>
<tr><td>
[SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveClassTask](./bitburner.sleeveclasstask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveCompanyTask](./bitburner.sleevecompanytask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveCrimeTask](./bitburner.sleevecrimetask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveFactionTask](./bitburner.sleevefactiontask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveRecoveryTask](./bitburner.sleeverecoverytask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveSupportTask](./bitburner.sleevesupporttask.md)
</td><td>
</td></tr>
<tr><td>
[SleeveSynchroTask](./bitburner.sleevesynchrotask.md)
</td><td>
</td></tr>
<tr><td>
@@ -1,8 +1,8 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [BaseTask](./bitburner.basetask.md) &gt; [cyclesWorked](./bitburner.basetask.cyclesworked.md)
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [PlayerBaseTask](./bitburner.playerbasetask.md) &gt; [cyclesWorked](./bitburner.playerbasetask.cyclesworked.md)
## BaseTask.cyclesWorked property
## PlayerBaseTask.cyclesWorked property
The number of game engine cycles has passed since this task started. 1 engine cycle = 200ms.
+59
View File
@@ -0,0 +1,59 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [PlayerBaseTask](./bitburner.playerbasetask.md)
## PlayerBaseTask interface
Base interface of all player tasks.
**Signature:**
```typescript
interface PlayerBaseTask extends BaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[cyclesWorked](./bitburner.playerbasetask.cyclesworked.md)
</td><td>
</td><td>
number
</td><td>
The number of game engine cycles has passed since this task started. 1 engine cycle = 200ms.
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) &gt; [actionName](./bitburner.sleevebladeburnertask.actionname.md)
## SleeveBladeburnerTask.actionName property
**Signature:**
```typescript
actionName: string;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) &gt; [actionType](./bitburner.sleevebladeburnertask.actiontype.md)
## SleeveBladeburnerTask.actionType property
**Signature:**
```typescript
actionType: "General" | "Contracts";
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) &gt; [cyclesNeeded](./bitburner.sleevebladeburnertask.cyclesneeded.md)
## SleeveBladeburnerTask.cyclesNeeded property
**Signature:**
```typescript
cyclesNeeded: number;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) &gt; [cyclesWorked](./bitburner.sleevebladeburnertask.cyclesworked.md)
## SleeveBladeburnerTask.cyclesWorked property
**Signature:**
```typescript
cyclesWorked: number;
```
+131 -10
View File
@@ -2,19 +2,140 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md)
## SleeveBladeburnerTask type
## SleeveBladeburnerTask interface
**Signature:**
```typescript
type SleeveBladeburnerTask = {
type: "BLADEBURNER";
actionType: "General" | "Contracts";
actionName: string;
cyclesWorked: number;
cyclesNeeded: number;
nextCompletion: Promise<void>;
tasksCompleted: number;
};
interface SleeveBladeburnerTask extends BaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[actionName](./bitburner.sleevebladeburnertask.actionname.md)
</td><td>
</td><td>
string
</td><td>
</td></tr>
<tr><td>
[actionType](./bitburner.sleevebladeburnertask.actiontype.md)
</td><td>
</td><td>
"General" \| "Contracts"
</td><td>
</td></tr>
<tr><td>
[cyclesNeeded](./bitburner.sleevebladeburnertask.cyclesneeded.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[cyclesWorked](./bitburner.sleevebladeburnertask.cyclesworked.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[tasksCompleted](./bitburner.sleevebladeburnertask.taskscompleted.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[type](./bitburner.sleevebladeburnertask.type.md)
</td><td>
</td><td>
"BLADEBURNER"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) &gt; [tasksCompleted](./bitburner.sleevebladeburnertask.taskscompleted.md)
## SleeveBladeburnerTask.tasksCompleted property
**Signature:**
```typescript
tasksCompleted: number;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) &gt; [type](./bitburner.sleevebladeburnertask.type.md)
## SleeveBladeburnerTask.type property
**Signature:**
```typescript
type: "BLADEBURNER";
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveClassTask](./bitburner.sleeveclasstask.md) &gt; [classType](./bitburner.sleeveclasstask.classtype.md)
## SleeveClassTask.classType property
**Signature:**
```typescript
classType: UniversityClassType | GymType;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveClassTask](./bitburner.sleeveclasstask.md) &gt; [location](./bitburner.sleeveclasstask.location.md)
## SleeveClassTask.location property
**Signature:**
```typescript
location: LocationName;
```
+79 -7
View File
@@ -2,17 +2,89 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveClassTask](./bitburner.sleeveclasstask.md)
## SleeveClassTask type
## SleeveClassTask interface
**Signature:**
```typescript
type SleeveClassTask = {
type: "CLASS";
classType: UniversityClassType | GymType;
location: LocationName;
};
interface SleeveClassTask extends BaseTask
```
**References:** [UniversityClassType](./bitburner.universityclasstype.md)<!-- -->, [GymType](./bitburner.gymtype.md)<!-- -->, [LocationName](./bitburner.locationname.md)
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[classType](./bitburner.sleeveclasstask.classtype.md)
</td><td>
</td><td>
[UniversityClassType](./bitburner.universityclasstype.md) \| [GymType](./bitburner.gymtype.md)
</td><td>
</td></tr>
<tr><td>
[location](./bitburner.sleeveclasstask.location.md)
</td><td>
</td><td>
[LocationName](./bitburner.locationname.md)
</td><td>
</td></tr>
<tr><td>
[type](./bitburner.sleeveclasstask.type.md)
</td><td>
</td><td>
"CLASS"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveClassTask](./bitburner.sleeveclasstask.md) &gt; [type](./bitburner.sleeveclasstask.type.md)
## SleeveClassTask.type property
**Signature:**
```typescript
type: "CLASS";
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCompanyTask](./bitburner.sleevecompanytask.md) &gt; [companyName](./bitburner.sleevecompanytask.companyname.md)
## SleeveCompanyTask.companyName property
**Signature:**
```typescript
companyName: CompanyName;
```
+62 -3
View File
@@ -2,13 +2,72 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCompanyTask](./bitburner.sleevecompanytask.md)
## SleeveCompanyTask type
## SleeveCompanyTask interface
**Signature:**
```typescript
type SleeveCompanyTask = { type: "COMPANY"; companyName: CompanyName };
interface SleeveCompanyTask extends BaseTask
```
**References:** [CompanyName](./bitburner.companyname.md)
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[companyName](./bitburner.sleevecompanytask.companyname.md)
</td><td>
</td><td>
[CompanyName](./bitburner.companyname.md)
</td><td>
</td></tr>
<tr><td>
[type](./bitburner.sleevecompanytask.type.md)
</td><td>
</td><td>
"COMPANY"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCompanyTask](./bitburner.sleevecompanytask.md) &gt; [type](./bitburner.sleevecompanytask.type.md)
## SleeveCompanyTask.type property
**Signature:**
```typescript
type: "COMPANY";
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCrimeTask](./bitburner.sleevecrimetask.md) &gt; [crimeType](./bitburner.sleevecrimetask.crimetype.md)
## SleeveCrimeTask.crimeType property
**Signature:**
```typescript
crimeType: CrimeType;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCrimeTask](./bitburner.sleevecrimetask.md) &gt; [cyclesNeeded](./bitburner.sleevecrimetask.cyclesneeded.md)
## SleeveCrimeTask.cyclesNeeded property
**Signature:**
```typescript
cyclesNeeded: number;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCrimeTask](./bitburner.sleevecrimetask.md) &gt; [cyclesWorked](./bitburner.sleevecrimetask.cyclesworked.md)
## SleeveCrimeTask.cyclesWorked property
**Signature:**
```typescript
cyclesWorked: number;
```
+113 -9
View File
@@ -2,19 +2,123 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCrimeTask](./bitburner.sleevecrimetask.md)
## SleeveCrimeTask type
## SleeveCrimeTask interface
**Signature:**
```typescript
type SleeveCrimeTask = {
type: "CRIME";
crimeType: CrimeType;
cyclesWorked: number;
cyclesNeeded: number;
tasksCompleted: number;
};
interface SleeveCrimeTask extends BaseTask
```
**References:** [CrimeType](./bitburner.crimetype.md)
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[crimeType](./bitburner.sleevecrimetask.crimetype.md)
</td><td>
</td><td>
[CrimeType](./bitburner.crimetype.md)
</td><td>
</td></tr>
<tr><td>
[cyclesNeeded](./bitburner.sleevecrimetask.cyclesneeded.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[cyclesWorked](./bitburner.sleevecrimetask.cyclesworked.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[tasksCompleted](./bitburner.sleevecrimetask.taskscompleted.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[type](./bitburner.sleevecrimetask.type.md)
</td><td>
</td><td>
"CRIME"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCrimeTask](./bitburner.sleevecrimetask.md) &gt; [tasksCompleted](./bitburner.sleevecrimetask.taskscompleted.md)
## SleeveCrimeTask.tasksCompleted property
**Signature:**
```typescript
tasksCompleted: number;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveCrimeTask](./bitburner.sleevecrimetask.md) &gt; [type](./bitburner.sleevecrimetask.type.md)
## SleeveCrimeTask.type property
**Signature:**
```typescript
type: "CRIME";
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveFactionTask](./bitburner.sleevefactiontask.md) &gt; [factionName](./bitburner.sleevefactiontask.factionname.md)
## SleeveFactionTask.factionName property
**Signature:**
```typescript
factionName: FactionName;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveFactionTask](./bitburner.sleevefactiontask.md) &gt; [factionWorkType](./bitburner.sleevefactiontask.factionworktype.md)
## SleeveFactionTask.factionWorkType property
**Signature:**
```typescript
factionWorkType: FactionWorkType;
```
+79 -7
View File
@@ -2,17 +2,89 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveFactionTask](./bitburner.sleevefactiontask.md)
## SleeveFactionTask type
## SleeveFactionTask interface
**Signature:**
```typescript
type SleeveFactionTask = {
type: "FACTION";
factionWorkType: FactionWorkType;
factionName: FactionName;
};
interface SleeveFactionTask extends BaseTask
```
**References:** [FactionWorkType](./bitburner.factionworktype.md)<!-- -->, [FactionName](./bitburner.factionname.md)
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[factionName](./bitburner.sleevefactiontask.factionname.md)
</td><td>
</td><td>
[FactionName](./bitburner.factionname.md)
</td><td>
</td></tr>
<tr><td>
[factionWorkType](./bitburner.sleevefactiontask.factionworktype.md)
</td><td>
</td><td>
[FactionWorkType](./bitburner.factionworktype.md)
</td><td>
</td></tr>
<tr><td>
[type](./bitburner.sleevefactiontask.type.md)
</td><td>
</td><td>
"FACTION"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveFactionTask](./bitburner.sleevefactiontask.md) &gt; [type](./bitburner.sleevefactiontask.type.md)
## SleeveFactionTask.type property
**Signature:**
```typescript
type: "FACTION";
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md) &gt; [cyclesNeeded](./bitburner.sleeveinfiltratetask.cyclesneeded.md)
## SleeveInfiltrateTask.cyclesNeeded property
**Signature:**
```typescript
cyclesNeeded: number;
```
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md) &gt; [cyclesWorked](./bitburner.sleeveinfiltratetask.cyclesworked.md)
## SleeveInfiltrateTask.cyclesWorked property
**Signature:**
```typescript
cyclesWorked: number;
```
+80 -7
View File
@@ -2,16 +2,89 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md)
## SleeveInfiltrateTask type
## SleeveInfiltrateTask interface
**Signature:**
```typescript
type SleeveInfiltrateTask = {
type: "INFILTRATE";
cyclesWorked: number;
cyclesNeeded: number;
nextCompletion: Promise<void>;
};
interface SleeveInfiltrateTask extends BaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[cyclesNeeded](./bitburner.sleeveinfiltratetask.cyclesneeded.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[cyclesWorked](./bitburner.sleeveinfiltratetask.cyclesworked.md)
</td><td>
</td><td>
number
</td><td>
</td></tr>
<tr><td>
[type](./bitburner.sleeveinfiltratetask.type.md)
</td><td>
</td><td>
"INFILTRATE"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md) &gt; [type](./bitburner.sleeveinfiltratetask.type.md)
## SleeveInfiltrateTask.type property
**Signature:**
```typescript
type: "INFILTRATE";
```
+46 -2
View File
@@ -2,11 +2,55 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveRecoveryTask](./bitburner.sleeverecoverytask.md)
## SleeveRecoveryTask type
## SleeveRecoveryTask interface
**Signature:**
```typescript
type SleeveRecoveryTask = { type: "RECOVERY" };
interface SleeveRecoveryTask extends BaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[type](./bitburner.sleeverecoverytask.type.md)
</td><td>
</td><td>
"RECOVERY"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveRecoveryTask](./bitburner.sleeverecoverytask.md) &gt; [type](./bitburner.sleeverecoverytask.type.md)
## SleeveRecoveryTask.type property
**Signature:**
```typescript
type: "RECOVERY";
```
+46 -2
View File
@@ -2,11 +2,55 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveSupportTask](./bitburner.sleevesupporttask.md)
## SleeveSupportTask type
## SleeveSupportTask interface
**Signature:**
```typescript
type SleeveSupportTask = { type: "SUPPORT" };
interface SleeveSupportTask extends BaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[type](./bitburner.sleevesupporttask.type.md)
</td><td>
</td><td>
"SUPPORT"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveSupportTask](./bitburner.sleevesupporttask.md) &gt; [type](./bitburner.sleevesupporttask.type.md)
## SleeveSupportTask.type property
**Signature:**
```typescript
type: "SUPPORT";
```
+46 -2
View File
@@ -2,11 +2,55 @@
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveSynchroTask](./bitburner.sleevesynchrotask.md)
## SleeveSynchroTask type
## SleeveSynchroTask interface
**Signature:**
```typescript
type SleeveSynchroTask = { type: "SYNCHRO" };
interface SleeveSynchroTask extends BaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
## Properties
<table><thead><tr><th>
Property
</th><th>
Modifiers
</th><th>
Type
</th><th>
Description
</th></tr></thead>
<tbody><tr><td>
[type](./bitburner.sleevesynchrotask.type.md)
</td><td>
</td><td>
"SYNCHRO"
</td><td>
</td></tr>
</tbody></table>
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [SleeveSynchroTask](./bitburner.sleevesynchrotask.md) &gt; [type](./bitburner.sleevesynchrotask.type.md)
## SleeveSynchroTask.type property
**Signature:**
```typescript
type: "SYNCHRO";
```
+2 -2
View File
@@ -9,9 +9,9 @@ Study
**Signature:**
```typescript
export interface StudyTask extends BaseTask
interface StudyTask extends PlayerBaseTask
```
**Extends:** [BaseTask](./bitburner.basetask.md)
**Extends:** [PlayerBaseTask](./bitburner.playerbasetask.md)
## Remarks
+1 -10
View File
@@ -4,19 +4,10 @@ import { AchievementList } from "./AchievementList";
import { achievements } from "./Achievements";
import { Box, Typography } from "@mui/material";
import { Player } from "@player";
import { makeStyles } from "tss-react/mui";
const useStyles = makeStyles()({
root: {
width: 50,
userSelect: "none",
},
});
export function AchievementsRoot(): JSX.Element {
const { classes } = useStyles();
return (
<div className={classes.root} style={{ width: "100%" }}>
<div style={{ width: "100%" }}>
<Typography variant="h4">Achievements</Typography>
<Box mx={2}>
<Typography>
+15 -19
View File
@@ -37,7 +37,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"triggers feelings of admiration, approval, and respect in others.",
company_rep: 1.2,
faction_rep: 1.2,
charisma: 1.2,
charisma: 1.1,
factions: [
FactionName.Silhouette,
FactionName.FourSigma,
@@ -517,7 +517,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"millions of nanobots capable of projecting high-density muon beams, " +
"creating an energy barrier around the user.",
defense: 1.4,
charisma: 1.1,
charisma: 1.05,
factions: [FactionName.Volhaven],
},
// === E === //
@@ -528,7 +528,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"A neural implant that enhances the user's ability to resonate with others. " +
"It is capable of analyzing and interpreting the emotions of those nearby, allowing " +
"the user to better understand and influence them.",
charisma: 1.1,
charisma: 1.05,
crime_success: 1.1,
work_money: 1.2,
factions: [FactionName.SpeakersForTheDead],
@@ -772,7 +772,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"An aural implant that enhances the user's ability to communicate and persuade others. " +
"The implant uses a predictive model that lets the user say precisely what their audience " +
"wants to hear. This implant is commonly used by many high-level executives and government officials.",
charisma: 1.2,
charisma: 1.1,
charisma_exp: 1.3,
factions: [FactionName.SpeakersForTheDead],
},
@@ -945,7 +945,6 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"Even though it contains no weapons, the advanced tungsten titanium " +
"alloy increases the user's strength to unbelievable levels.",
strength: 2.8,
charisma: 1.4,
factions: [FactionName.NWO],
},
[AugmentationName.HyperionV1]: {
@@ -985,7 +984,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
dexterity: 1.4,
hacking_speed: 1.03,
hacking_money: 1.1,
charisma: 1.05,
charisma: 1.03,
factions: [FactionName.BladeIndustries, FactionName.KuaiGongInternational],
},
// === I === //
@@ -1033,7 +1032,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"cells, when powered, have a negative refractive index. As a result, they bend light " +
"around the skin, making the user much harder to see with the naked eye.",
agility: 1.05,
charisma: 1.05,
charisma: 1.03,
crime_money: 1.1,
factions: [FactionName.SlumSnakes, FactionName.Tetrads],
},
@@ -1057,7 +1056,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
repCost: 1.5e4,
moneyCost: 2.5e8,
info: "A cranial implant that increases the attractive force of the wearer. (Even its inventor isn't quite sure how it works).",
charisma: 1.1,
charisma: 1.05,
company_rep: 1.1,
factions: [FactionName.TheBlackHand, FactionName.TheDarkArmy],
},
@@ -1081,7 +1080,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"which improves its regenerative and extracellular homeostasis abilities.",
strength: 1.2,
defense: 1.2,
charisma: 1.1,
charisma: 1.05,
factions: [
FactionName.TheDarkArmy,
FactionName.TheSyndicate,
@@ -1134,7 +1133,6 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
hacking_speed: 1.02,
hacking_chance: 1.1,
hacking_exp: 1.12,
charisma: 1.05,
factions: [
FactionName.TheBlackHand,
FactionName.Chongqing,
@@ -1294,7 +1292,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"the bloodstream to improve memory, increase focus, and provide other " +
"cognitive enhancements.",
company_rep: 1.2,
charisma: 1.05,
charisma: 1.03,
factions: [
FactionName.TianDiHui,
FactionName.Volhaven,
@@ -1432,7 +1430,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
strength: 1.4,
defense: 1.4,
agility: 1.4,
charisma: 1.4,
charisma: 1.2,
factions: [FactionName.KuaiGongInternational],
},
[AugmentationName.PowerRecirculator]: {
@@ -1462,7 +1460,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
info:
"A cutting-edge knowledgebase entirely built off of nanotech rod-logic, training the user on social engineering. " +
"Thought to be stolen technology, its existance has been a secret until recently.",
charisma: 1.2,
charisma: 1.1,
charisma_exp: 1.4,
factions: [FactionName.TheDarkArmy, FactionName.TheSyndicate],
},
@@ -1500,7 +1498,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
info:
"Makes the wearer a better leader and mentor by greatly increasing their awareness of social dynamics. " +
"Not actually a standard implant, but rather a series of training courses and seminars, led by a famous speaker named Denis.",
charisma: 1.3,
charisma: 1.1,
company_rep: 1.3,
factions: [FactionName.MegaCorp, FactionName.ECorp, FactionName.OmniTekIncorporated],
},
@@ -1528,7 +1526,6 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"criminal organizations and allows the user to project and control holographic " +
"simulacrums within a large radius. These simulacrums are commonly used for " +
"espionage and surveillance work.",
charisma: 1.15,
company_rep: 1.15,
faction_rep: 1.15,
factions: [FactionName.TheSyndicate, FactionName.TheDarkArmy, FactionName.SpeakersForTheDead],
@@ -1743,7 +1740,6 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"Scientists have named these artificially enhanced units 'synfibrils'.",
strength: 1.3,
defense: 1.3,
charisma: 1.15,
factions: [
FactionName.KuaiGongInternational,
FactionName.FulcrumSecretTechnologies,
@@ -1763,7 +1759,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
"more efficiently than an organic heart.",
agility: 1.5,
strength: 1.5,
charisma: 1.5,
charisma: 1.3,
factions: [
FactionName.KuaiGongInternational,
FactionName.FulcrumSecretTechnologies,
@@ -2042,8 +2038,8 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
info:
"A connective brain implant that greatly increases the user's speech reaction time. " +
"This allows the user to think faster and respond quicker in negotiations, and always have the last word.",
charisma: 1.08,
charisma_exp: 1.1,
charisma: 1.03,
charisma_exp: 1.05,
company_rep: 1.05,
factions: [FactionName.SlumSnakes, FactionName.BitRunners],
},
+3 -3
View File
@@ -271,13 +271,13 @@ function IntelligenceOverride({
disabled={!enabled}
value={intelligenceOverride !== undefined ? intelligenceOverride : ""}
onChange={(event) => {
// Empty string will be automatically changed to "0".
// Empty string will be automatically changed to "1".
if (event.target.value === "") {
callbacks.setIntelligenceOverride(0);
callbacks.setIntelligenceOverride(1);
return;
}
const value = Number.parseInt(event.target.value);
if (!Number.isInteger(value) || value < 0) {
if (!Number.isInteger(value) || value < 1) {
return;
}
callbacks.setIntelligenceOverride(value);
+25 -4
View File
@@ -6,6 +6,8 @@ import { ActionClass, ActionParams } from "./Action";
import { operationSkillSuccessBonus, operationTeamSuccessBonus } from "./Operation";
import { getEnumHelper } from "../../utils/EnumHelper";
import type { TeamActionWithCasualties } from "./TeamCasualties";
import { constructorsForReviver, Generic_fromJSON, type IReviverValue } from "../../utils/JSONReviver";
import { clampInteger } from "../../utils/helpers/clampNumber";
interface BlackOpParams {
name: BladeburnerBlackOpName;
@@ -32,11 +34,11 @@ export class BlackOperation extends ActionClass implements TeamActionWithCasualt
return getEnumHelper("BladeburnerBlackOpName").isMember(name);
}
constructor(params: ActionParams & BlackOpParams) {
constructor(params: (ActionParams & BlackOpParams) | null = null) {
super(params);
this.name = params.name;
this.reqdRank = params.reqdRank;
this.n = params.n;
this.name = params?.name ?? BladeburnerBlackOpName.OperationTyphoon;
this.reqdRank = params?.reqdRank ?? 0;
this.n = params?.n ?? 0;
}
getAvailability(bladeburner: Bladeburner): Availability {
@@ -65,4 +67,23 @@ export class BlackOperation extends ActionClass implements TeamActionWithCasualt
getTeamSuccessBonus = operationTeamSuccessBonus;
getActionTypeSkillSuccessBonus = operationSkillSuccessBonus;
toJSON(): IReviverValue {
return {
ctor: "BlackOperation",
data: {
teamCount: this.teamCount,
},
};
}
loadData(loadedObject: BlackOperation): void {
this.teamCount = clampInteger(loadedObject.teamCount, 0);
}
static fromJSON(value: IReviverValue): BlackOperation {
return Generic_fromJSON(BlackOperation, value.data);
}
}
constructorsForReviver.BlackOperation = BlackOperation;
+10 -4
View File
@@ -44,12 +44,18 @@ export function resolveTeamCasualties(action: TeamActionWithCasualties, team: Op
*/
const losses =
minCasualties <= maxCasualties ? team.getTeamCasualtiesRoll(minCasualties, maxCasualties) : minCasualties;
team.teamSize -= losses;
if (team.teamSize < team.sleeveSize) {
team.killRandomSupportingSleeves(team.sleeveSize - team.teamSize);
// Calculate the new teamSize in a temporary variable and call the setter team.teamSize ONCE.
// Note that it's important to call the setter only once; otherwise, the team count of each operation won't be reset
// correctly.
// For example, if _teamSize is 9 (1 team member + 8 support sleeves) and "losses" is 9, calling the setter with
// (team.teamSize - losses) will set teamCount of ops/blackOps to 0 while it should be 8.
let newTeamSize = team.teamSize - losses;
if (newTeamSize < team.sleeveSize) {
team.killRandomSupportingSleeves(team.sleeveSize - newTeamSize);
// If this happens, all team members died and some sleeves took damage. In this case, teamSize = sleeveSize.
team.teamSize = team.sleeveSize;
newTeamSize = team.sleeveSize;
}
team.teamSize = newTeamSize;
team.teamLost += losses;
return losses;
+50 -9
View File
@@ -47,7 +47,7 @@ import { createContracts, loadContractsData } from "./data/Contracts";
import { createOperations, loadOperationsData } from "./data/Operations";
import { clampInteger, clampNumber } from "../utils/helpers/clampNumber";
import { parseCommand } from "../Terminal/Parser";
import { BlackOperations } from "./data/BlackOperations";
import { createBlackOperations, loadBlackOperationsData } from "./data/BlackOperations";
import { GeneralActions } from "./data/GeneralActions";
import { PlayerObject } from "../PersonObjects/Player/PlayerObject";
import { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
@@ -72,7 +72,31 @@ export class Bladeburner implements OperationTeam {
skillPoints = 0;
totalSkillPoints = 0;
teamSize = 0;
/**
* Do NOT directly read and write this field. You must use the getter/setter.
* We use _teamSize instead of a private field #teamSize to reduce the complexity of saving/loading code.
*/
_teamSize = 0;
get teamSize() {
return this._teamSize;
}
set teamSize(value: number) {
// Ensure teamSize is a non-negative integer.
let newSize = value;
if (!Number.isInteger(newSize) || newSize < 0) {
newSize = 0;
}
// Early return if there is no change.
if (this._teamSize === newSize) {
return;
}
this._teamSize = newSize;
// Reduce teamCount of actions if it's greater than the team size.
for (const action of [...Object.values(this.operations), ...Object.values(this.blackOperations)]) {
action.teamCount = Math.min(action.teamCount, this._teamSize);
}
}
get sleeveSize() {
return Player.sleevesSupportingBladeburner().length;
}
@@ -96,9 +120,13 @@ export class Bladeburner implements OperationTeam {
staminaBonus = 0;
maxStamina = 1;
stamina = 1;
// Contracts and operations are stored on the Bladeburner object even though they are global so that they can utilize save/load of the main bladeburner object
// Contracts, operations and blackOps are stored on the Bladeburner object even though they are global so that they
// can utilize save/load of the main bladeburner object
contracts: Record<BladeburnerContractName, Contract>;
operations: Record<BladeburnerOperationName, Operation>;
blackOperations: Record<BladeburnerBlackOpName, BlackOperation>;
// Array for quick lookup by BlackOp number
blackOperationArray: BlackOperation[];
numBlackOpsComplete = 0;
logging = {
general: true,
@@ -119,6 +147,11 @@ export class Bladeburner implements OperationTeam {
constructor() {
this.contracts = createContracts();
this.operations = createOperations();
this.blackOperations = createBlackOperations();
this.blackOperationArray = Object.values(this.blackOperations).sort((a, b) => (a.n < b.n ? -1 : 1));
if (!this.blackOperationArray.every((blackOp, i) => blackOp.n === i)) {
throw new Error("blackOperationArray is not initialized with correct indices");
}
}
// Initialization code that is dependent on Player is here instead of in the constructor
@@ -1407,7 +1440,7 @@ export class Bladeburner implements OperationTeam {
case BladeburnerActionType.Operation:
return this.operations[actionId.name];
case BladeburnerActionType.BlackOp:
return BlackOperations[actionId.name];
return this.blackOperations[actionId.name];
case BladeburnerActionType.General:
return GeneralActions[actionId.name];
}
@@ -1426,7 +1459,7 @@ export class Bladeburner implements OperationTeam {
case BladeburnerActionType.Operation:
return this.operations[name as BladeburnerOperationName];
case BladeburnerActionType.BlackOp:
return BlackOperations[name as BladeburnerBlackOpName];
return this.blackOperations[name as BladeburnerBlackOpName];
}
}
@@ -1437,9 +1470,11 @@ export class Bladeburner implements OperationTeam {
return id ? this.getActionObject(id) : null;
}
static keysToSave = getKeyList(Bladeburner, { removedKeys: ["skillMultipliers"] });
static keysToSave = getKeyList(Bladeburner, { removedKeys: ["skillMultipliers", "blackOperationArray"] });
// Don't load contracts or operations because of the special loading method they use, see fromJSON
static keysToLoad = getKeyList(Bladeburner, { removedKeys: ["skillMultipliers", "contracts", "operations"] });
static keysToLoad = getKeyList(Bladeburner, {
removedKeys: ["skillMultipliers", "contracts", "operations", "blackOperations", "blackOperationArray"],
});
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
@@ -1449,9 +1484,10 @@ export class Bladeburner implements OperationTeam {
/** Initializes a Bladeburner object from a JSON save state. */
static fromJSON(value: IReviverValue): Bladeburner {
assertObject(value.data);
// operations and contracts are not loaded directly from the save, we load them in using a different method
// Contracts, operations, and black ops are not loaded directly from the save; they are loaded via a different method.
const contractsData = value.data.contracts;
const operationsData = value.data.operations;
const blackOperationsData = value.data.blackOperations;
const bladeburner = Generic_fromJSON(Bladeburner, value.data, Bladeburner.keysToLoad);
/**
@@ -1472,10 +1508,11 @@ export class Bladeburner implements OperationTeam {
bladeburner.automateActionLow = loadActionIdentifier(bladeburner.automateActionLow);
}
}
// Loading this way allows better typesafety and also allows faithfully reconstructing contracts/operations
// Loading this way allows better typesafety and also allows faithfully reconstructing contracts/operations/blackOps
// even from save data that is missing a lot of static info about the objects.
loadContractsData(contractsData, bladeburner.contracts);
loadOperationsData(operationsData, bladeburner.operations);
loadBlackOperationsData(blackOperationsData, bladeburner.blackOperations);
// Regenerate skill multiplier data, which is not included in savedata
bladeburner.updateSkillMultipliers();
// If stamina or maxStamina is invalid, we set both of them to 1 and recalculate them.
@@ -1488,6 +1525,10 @@ export class Bladeburner implements OperationTeam {
bladeburner.maxStamina = 1;
bladeburner.calculateMaxStamina();
}
// "_teamSize" was "teamSize" in pre-v3 versions.
if ("teamSize" in value.data && Number.isFinite(value.data.teamSize)) {
bladeburner.teamSize = value.data.teamSize as number;
}
return bladeburner;
}
}
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -113,7 +113,9 @@ export function createContracts(): Record<BladeburnerContractName, Contract> {
export function loadContractsData(data: unknown, contracts: Record<BladeburnerContractName, Contract>) {
// loading data as "unknown" and typechecking it down is probably not necessary
// but this will prevent crashes even with malformed savedata
if (!data || typeof data !== "object") return;
if (data == null || typeof data !== "object" || Array.isArray(data)) {
return;
}
assertLoadingType<Record<BladeburnerContractName, unknown>>(data);
for (const contractName of Object.values(BladeburnerContractName)) {
const loadedContract = data[contractName];
+3 -1
View File
@@ -230,7 +230,9 @@ export function createOperations(): Record<BladeburnerOperationName, Operation>
export function loadOperationsData(data: unknown, operations: Record<BladeburnerOperationName, Operation>) {
// loading data as "unknown" and typechecking it down is probably not necessary
// but this will prevent crashes even with malformed savedata
if (!data || typeof data !== "object") return;
if (data == null || typeof data !== "object" || Array.isArray(data)) {
return;
}
assertLoadingType<Record<BladeburnerOperationName, unknown>>(data);
for (const operationName of Object.values(BladeburnerOperationName)) {
const loadedOperation = data[operationName];
+4 -4
View File
@@ -7,7 +7,7 @@ import { BlackOpElem } from "./BlackOpElem";
import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { CorruptibleText } from "../../ui/React/CorruptibleText";
import { blackOpsArray } from "../data/BlackOperations";
import { numberOfBlackOperations } from "../data/BlackOperations";
import { finishBitNode } from "../../BitNode/BitNodeUtils";
import { Player } from "@player";
@@ -16,7 +16,7 @@ interface BlackOpPageProps {
}
export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactElement {
const blackOperations = blackOpsArray.slice(0, bladeburner.numBlackOpsComplete + 1).reverse();
const blackOperations = bladeburner.blackOperationArray.slice(0, bladeburner.numBlackOpsComplete + 1).reverse();
return (
<>
@@ -36,11 +36,11 @@ export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactEleme
Unaffected by Charisma.
</Typography>
{bladeburner.numBlackOpsComplete >= blackOpsArray.length && (
{bladeburner.numBlackOpsComplete >= numberOfBlackOperations && (
<Button
sx={{ my: 1, p: 1 }}
onClick={() => {
if (!Player.bladeburner || Player.bladeburner.numBlackOpsComplete < blackOpsArray.length) {
if (!Player.bladeburner || Player.bladeburner.numBlackOpsComplete < numberOfBlackOperations) {
return;
}
finishBitNode();
+11 -10
View File
@@ -15,25 +15,26 @@ interface TeamSizeModalProps {
}
export function TeamSizeModal({ bladeburner, action, open, onClose }: TeamSizeModalProps): React.ReactElement {
const [teamSize, setTeamSize] = useState<number | undefined>();
const [teamSize, setTeamSize] = useState(0);
function confirmTeamSize(event: React.FormEvent): void {
// Prevent reloading page when submitting form
event.preventDefault();
if (teamSize === undefined) return;
const num = Math.round(teamSize);
if (isNaN(num) || num < 0) {
dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric and non-negative)");
} else {
action.teamCount = num;
if (!Number.isInteger(teamSize) || teamSize < 0) {
dialogBoxCreate("Invalid value entered for number of Team Members (must be a non-negative integer)");
return;
}
action.teamCount = teamSize;
onClose();
}
function onTeamSize(event: React.ChangeEvent<HTMLInputElement>): void {
const x = parseFloat(event.target.value);
if (x > bladeburner.teamSize) setTeamSize(bladeburner.teamSize);
else setTeamSize(x);
const newTeamSize = Number(event.target.value);
if (newTeamSize > bladeburner.teamSize) {
setTeamSize(bladeburner.teamSize);
} else {
setTeamSize(newTeamSize);
}
}
return (
+26 -3
View File
@@ -7,7 +7,7 @@ export const CONSTANTS = {
VersionString: "3.0.0dev",
isDevBranch: true,
isInTestEnvironment: globalThis.process?.env?.JEST_WORKER_ID !== undefined,
VersionNumber: 48,
VersionNumber: 49,
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
@@ -111,7 +111,7 @@ export const CONSTANTS = {
// Also update Documentation/doc/en/changelog.md when appropriate (when doing a release)
LatestUpdate: `
## v3.0.0 development version: last updated 18 February 2026
## v3.0.0 development version: last updated 13 April 2026
### BREAKING CHANGES
@@ -143,13 +143,14 @@ export const CONSTANTS = {
- Cancel sleeve's current task when calling ns.sleeve.travel() (#2559) (@catloversg)
- Make ns.cloud.purchaseServer() and ns.cloud.deleteServer() use hostname as provided (#2560) (@catloversg)
- Make implicit string conversion consistent across all coding contracts (#2608) (@catloversg)
- Rename ns.gang.getOtherGangInformation to getAllGangInformation (#2635) (@lstutzman)
### MAJOR CHANGES
- Added Darknet, a new mechanic based on spreading through an unstable network and cracking passwords. Purchase DarkscapeNavigator.exe in the terminal to unlock access. (#2139) (@ficocelliguy)
- Balance change: IPvGO: Improve favor gain from wins to balance around the rep value of favor (#2131) (@ficocelliguy)
- Search and read NS API docs in editor tab and documentation tab (#2163) (@catloversg)
- Balance change: Infiltration: Rebalance rewards, add min stat requirement, add market demand (#2210) (@ficocelliguy, @d0sboots, @catloversg)
- Add Dark Net (#2139) (@ficocelliguy)
### UI
@@ -224,6 +225,10 @@ export const CONSTANTS = {
- Fix: Import save comparison popup shows wrong BN level (#2595) (@catloversg)
- Fix: Cannot type in text boxes rendered by players' scripts when terminal tab is shown (#2615, #2622) (@lstutzman, @catloversg)
- Navigate to gym/university instead of city when stopping focusing on gym/class work (#2613) (@lstutzman)
- Ensure prompts shown by ns.prompt do not lose focus in the terminal tab (#2631) (@catloversg)
- Remove unnecessary max-width of tab list in in-game editor (#2643) (@catloversg)
- Add hooks to sidebar for players to attach custom content (#2651) (@catloversg)
- Use exponential notation when formatting very small HP or thread values (#2656) (@catloversg)
### MISC
@@ -325,6 +330,12 @@ export const CONSTANTS = {
- Dnet: Remove packet capture (#2594) (@ficocelliguy)
- Generate more frequent and lower-reward coding contracts (#2603) (@ficocelliguy)
- Electron: Fix issues in edge cases of using --export-save (#2590) (@catloversg)
- Fix recursive alias detection causing infinite recursion (#2610) (@lstutzman)
- Add "hidden" mkdir command (#2646) (@catloversg)
- Dnet: Remove bonus time effect on authentication and heartbleed speed; fix ram rounding (#2627) (@ficocelliguy)
- Fix tab completion for multi-word quoted autocomplete options (#2612) (@lstutzman)
- Add weakenEffect to formulas.hacking namespace (#2626) (@lstutzman)
- Update description of "cat" in "help" command (#2654) (@catloversg)
### DOCUMENTATION
@@ -378,6 +389,9 @@ export const CONSTANTS = {
- Update mention of outdated getStockForecast API (#2578) (@catloversg)
- Fix newline issues in IPvGO docs and add missing RAM cost (#2602) (@catloversg)
- Document coding contract's generation and rewards (#2624) (@catloversg)
- Clarify scp and exec darknet permissions in API docs (#2634) (@lstutzman)
- Update RAM cost of hacknet APIs and remove unnecessary RAM cost docs (#2639) (@catloversg)
- Update tutorial script for buying cloud servers (#2653) (@catloversg)
### SPOILER CHANGES - UI
@@ -389,6 +403,8 @@ export const CONSTANTS = {
- Prevent ending BNs through reuse of Bladeburner UI event handler (#2574) (@catloversg)
- Always show Black Operations list (#2592) (@catloversg)
- Show hints of Sleeves mechanic in pre-endgame (#2605) (@catloversg)
- Consistently calculate BitNode "level" (#2645) (@catloversg)
- Add tooltips explaining why Bladeburner skill upgrades are disabled (#2648) (@catloversg)
### SPOILER CHANGES - MISC
@@ -416,6 +432,9 @@ export const CONSTANTS = {
- Add APIs to get rank gain and rank loss of an action (#2572) (@catloversg)
- Reduce RAM cost of inGang and inBladeburner APIs (#2582) (@catloversg)
- Fix skillMaxUpgradeCount returning 1 at extreme skill levels (#2611) (@lstutzman)
- API: Expose charged effects of Stanek's Gift active fragments (#2638) (@catloversg)
- Apply SF override to charisma calculations (#2642) (@catloversg)
- Update description of "BN9: Challenge" achievement (#2647) (@catloversg)
### SPOILER CHANGES - DOCUMENTATION
@@ -519,5 +538,9 @@ export const CONSTANTS = {
- Refactor and fix issues in db.ts (#2623) (@catloversg)
- Add dependency array to TerminalInput keydown useEffect (#2620) (@lstutzman)
- Add dependency array to GameRoot useEffect (#2617) (@lstutzman)
- Dev menu: Initialize dark net data when setting SF15 level (#2632) (@catloversg)
- Use type-only imports in ArrayHelpers.ts (#2630) (@catloversg)
- Remove redundant "$" from JS/TS regex in webpack config (#2649) (@catloversg)
- Allow specifying commit hash id when building artifacts (#2652) (@catloversg)
`,
} as const;
+16 -23
View File
@@ -215,32 +215,25 @@ export class Division {
}
}
// Process change in demand and competition for this industry's materials
// Process demand, competition, and market price changes for this division's materials
processMaterialMarket(): void {
//References to prodMats and reqMats
const reqMats = this.requiredMaterials,
prodMats = this.producedMaterials;
// Relevant materials:
// - All materials this division requires or produces
// - Boost materials
const materials = new Set([
...getRecordKeys(this.requiredMaterials),
...this.producedMaterials,
...corpConstants.boostMaterials,
]);
//Only 'process the market' for materials that this industry deals with
for (const city of Object.values(CityName)) {
//If this industry has a warehouse in this city, process the market
//for every material this industry requires or produces
if (this.warehouses[city]) {
const wh = this.warehouses[city];
for (const name of Object.keys(reqMats) as CorpMaterialName[]) {
if (Object.hasOwn(reqMats, name)) {
wh.materials[name].processMarket();
}
}
//Produced materials are stored in an array
for (const matName of prodMats) wh.materials[matName].processMarket();
//Process these twice because these boost production ??????
wh.materials.Hardware.processMarket();
wh.materials.Robots.processMarket();
wh.materials["AI Cores"].processMarket();
wh.materials["Real Estate"].processMarket();
const warehouse = this.warehouses[city];
// If this division has a warehouse in this city, process the relevant materials
if (warehouse == null) {
continue;
}
for (const materialName of materials) {
warehouse.materials[materialName].processMarket();
}
}
}
+3 -1
View File
@@ -19,7 +19,7 @@ import {
CorpBaseResearchName,
CorpProductResearchName,
} from "@enums";
import { PositiveInteger } from "../../types";
import type { PositiveInteger } from "../../types";
/** Names of all corporation game states */
export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "EXPORT", "SALE"],
@@ -30,6 +30,8 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
industryNames: CorpIndustryName[] = Object.values(IndustryType),
/** Names of all materials */
materialNames: APIMaterialName[] = Object.values(CorpMaterialName),
/** Names of all boost materials */
boostMaterials: CorpMaterialName[] = ["Hardware", "Robots", "AI Cores", "Real Estate"],
/** Names of all one-time corporation-wide unlocks */
unlockNames: APIUnlockName[] = Object.values(CorpUnlockName),
upgradeNames: APIUpgradeName[] = Object.values(CorpUpgradeName),
+1 -1
View File
@@ -27,7 +27,7 @@ export function CorporationRoot(): React.ReactElement {
return (
<Context.Corporation.Provider value={corporation}>
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65vw" }} scrollButtons>
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} scrollButtons>
<Tab label={corporation.name} value={"Overview"} />
{[...corporation.divisions.values()].map((div) => (
<Tab key={div.name} label={div.name} value={div.name} />
+2 -4
View File
@@ -1,15 +1,13 @@
import { CorpMaterialName } from "@nsdefs";
import { Division } from "../Division";
import { boostMaterials } from "../data/Constants";
// Returns a boolean indicating whether the given material is relevant for the
// current industry.
export function isRelevantMaterial(matName: CorpMaterialName, division: Division): boolean {
// Materials that affect Production multiplier
const prodMultiplierMats: CorpMaterialName[] = ["Hardware", "Robots", "AI Cores", "Real Estate"];
if (Object.keys(division.requiredMaterials).includes(matName)) return true;
if (division.producedMaterials.includes(matName)) return true;
if (prodMultiplierMats.includes(matName)) return true;
if (boostMaterials.includes(matName)) return true;
return false;
}
+1 -1
View File
@@ -77,7 +77,7 @@ export function NetworkDisplayWrapper(): React.ReactElement {
useEffect(() => {
const clearSubscription = DarknetEvents.subscribe(() => updateDisplay());
draggableBackground.current?.addEventListener("wheel", (e) => e.preventDefault());
draggableBackground.current?.addEventListener("wheel", (e) => e.preventDefault(), { passive: false });
scrollTo(DarknetState.netViewTopScroll, DarknetState.netViewLeftScroll);
updateDisplay();
@@ -142,8 +142,23 @@ same as the string `['foo', 'bar']`.
Internally, we use `JSON.parse` to convert the string answer, and `['foo', 'bar']` is not a valid string representation
of an array. In JSON, a string needs to be enclosed by double quotes. Using single quotes or backticks is not allowed.
This is another example of why you should not convert your answer to a string when not requested. If you submit your
array as it is, you do not need to care about the quote types.
This is one reason why you should not convert your answer to a string unless requested. If you submit your array as is,
you do not need to worry about quote types.
Let's check another example:
```js
const firstString = "foo";
const secondString = "bar";
const answer = [firstString, secondString];
ns.codingcontract.attempt(answer.toString(), "filename.cct");
ns.codingcontract.attempt(String(answer), "filename.cct");
```
Do NOT call toString() or use similar methods to convert your string array to a string. `["foo", "bar"]` will be
converted to `foo,bar`. For contracts that expect a string array, submitting this string causes it to be interpreted as
`[foo,bar]`, which is then passed to `JSON.parse`. However, `[foo,bar]` is not valid JSON (it lacks double quotes), so
your answer will be invalid.
## Rewards
+56 -4
View File
@@ -91,8 +91,8 @@ import nsDoc_bitburner_autocompletedata_txts_md from "../../markdown/bitburner.a
import nsDoc_bitburner_backdoorrequirement_md from "../../markdown/bitburner.backdoorrequirement.md?raw";
import nsDoc_bitburner_backdoorrequirement_server_md from "../../markdown/bitburner.backdoorrequirement.server.md?raw";
import nsDoc_bitburner_backdoorrequirement_type_md from "../../markdown/bitburner.backdoorrequirement.type.md?raw";
import nsDoc_bitburner_basetask_cyclesworked_md from "../../markdown/bitburner.basetask.cyclesworked.md?raw";
import nsDoc_bitburner_basetask_md from "../../markdown/bitburner.basetask.md?raw";
import nsDoc_bitburner_basetask_nextcompletion_md from "../../markdown/bitburner.basetask.nextcompletion.md?raw";
import nsDoc_bitburner_basichgwoptions_additionalmsec_md from "../../markdown/bitburner.basichgwoptions.additionalmsec.md?raw";
import nsDoc_bitburner_basichgwoptions_md from "../../markdown/bitburner.basichgwoptions.md?raw";
import nsDoc_bitburner_basichgwoptions_stock_md from "../../markdown/bitburner.basichgwoptions.stock.md?raw";
@@ -730,7 +730,6 @@ import nsDoc_bitburner_grafting_graftaugmentation_md from "../../markdown/bitbur
import nsDoc_bitburner_grafting_md from "../../markdown/bitburner.grafting.md?raw";
import nsDoc_bitburner_grafting_waitforongoinggrafting_md from "../../markdown/bitburner.grafting.waitforongoinggrafting.md?raw";
import nsDoc_bitburner_graftingtask_augmentation_md from "../../markdown/bitburner.graftingtask.augmentation.md?raw";
import nsDoc_bitburner_graftingtask_completion_md from "../../markdown/bitburner.graftingtask.completion.md?raw";
import nsDoc_bitburner_graftingtask_md from "../../markdown/bitburner.graftingtask.md?raw";
import nsDoc_bitburner_graftingtask_type_md from "../../markdown/bitburner.graftingtask.type.md?raw";
import nsDoc_bitburner_gymenumtype_md from "../../markdown/bitburner.gymenumtype.md?raw";
@@ -1172,6 +1171,8 @@ import nsDoc_bitburner_player_md from "../../markdown/bitburner.player.md?raw";
import nsDoc_bitburner_player_money_md from "../../markdown/bitburner.player.money.md?raw";
import nsDoc_bitburner_player_numpeoplekilled_md from "../../markdown/bitburner.player.numpeoplekilled.md?raw";
import nsDoc_bitburner_player_totalplaytime_md from "../../markdown/bitburner.player.totalplaytime.md?raw";
import nsDoc_bitburner_playerbasetask_cyclesworked_md from "../../markdown/bitburner.playerbasetask.cyclesworked.md?raw";
import nsDoc_bitburner_playerbasetask_md from "../../markdown/bitburner.playerbasetask.md?raw";
import nsDoc_bitburner_playerrequirement_md from "../../markdown/bitburner.playerrequirement.md?raw";
import nsDoc_bitburner_positionenumtype_md from "../../markdown/bitburner.positionenumtype.md?raw";
import nsDoc_bitburner_positiontype_md from "../../markdown/bitburner.positiontype.md?raw";
@@ -1384,20 +1385,45 @@ import nsDoc_bitburner_sleeve_settosynchronize_md from "../../markdown/bitburner
import nsDoc_bitburner_sleeve_settouniversitycourse_md from "../../markdown/bitburner.sleeve.settouniversitycourse.md?raw";
import nsDoc_bitburner_sleeve_travel_md from "../../markdown/bitburner.sleeve.travel.md?raw";
import nsDoc_bitburner_sleeve_upgradememory_md from "../../markdown/bitburner.sleeve.upgradememory.md?raw";
import nsDoc_bitburner_sleevebladeburnertask_actionname_md from "../../markdown/bitburner.sleevebladeburnertask.actionname.md?raw";
import nsDoc_bitburner_sleevebladeburnertask_actiontype_md from "../../markdown/bitburner.sleevebladeburnertask.actiontype.md?raw";
import nsDoc_bitburner_sleevebladeburnertask_cyclesneeded_md from "../../markdown/bitburner.sleevebladeburnertask.cyclesneeded.md?raw";
import nsDoc_bitburner_sleevebladeburnertask_cyclesworked_md from "../../markdown/bitburner.sleevebladeburnertask.cyclesworked.md?raw";
import nsDoc_bitburner_sleevebladeburnertask_md from "../../markdown/bitburner.sleevebladeburnertask.md?raw";
import nsDoc_bitburner_sleevebladeburnertask_taskscompleted_md from "../../markdown/bitburner.sleevebladeburnertask.taskscompleted.md?raw";
import nsDoc_bitburner_sleevebladeburnertask_type_md from "../../markdown/bitburner.sleevebladeburnertask.type.md?raw";
import nsDoc_bitburner_sleeveclasstask_classtype_md from "../../markdown/bitburner.sleeveclasstask.classtype.md?raw";
import nsDoc_bitburner_sleeveclasstask_location_md from "../../markdown/bitburner.sleeveclasstask.location.md?raw";
import nsDoc_bitburner_sleeveclasstask_md from "../../markdown/bitburner.sleeveclasstask.md?raw";
import nsDoc_bitburner_sleeveclasstask_type_md from "../../markdown/bitburner.sleeveclasstask.type.md?raw";
import nsDoc_bitburner_sleevecompanytask_companyname_md from "../../markdown/bitburner.sleevecompanytask.companyname.md?raw";
import nsDoc_bitburner_sleevecompanytask_md from "../../markdown/bitburner.sleevecompanytask.md?raw";
import nsDoc_bitburner_sleevecompanytask_type_md from "../../markdown/bitburner.sleevecompanytask.type.md?raw";
import nsDoc_bitburner_sleevecrimetask_crimetype_md from "../../markdown/bitburner.sleevecrimetask.crimetype.md?raw";
import nsDoc_bitburner_sleevecrimetask_cyclesneeded_md from "../../markdown/bitburner.sleevecrimetask.cyclesneeded.md?raw";
import nsDoc_bitburner_sleevecrimetask_cyclesworked_md from "../../markdown/bitburner.sleevecrimetask.cyclesworked.md?raw";
import nsDoc_bitburner_sleevecrimetask_md from "../../markdown/bitburner.sleevecrimetask.md?raw";
import nsDoc_bitburner_sleevecrimetask_taskscompleted_md from "../../markdown/bitburner.sleevecrimetask.taskscompleted.md?raw";
import nsDoc_bitburner_sleevecrimetask_type_md from "../../markdown/bitburner.sleevecrimetask.type.md?raw";
import nsDoc_bitburner_sleevefactiontask_factionname_md from "../../markdown/bitburner.sleevefactiontask.factionname.md?raw";
import nsDoc_bitburner_sleevefactiontask_factionworktype_md from "../../markdown/bitburner.sleevefactiontask.factionworktype.md?raw";
import nsDoc_bitburner_sleevefactiontask_md from "../../markdown/bitburner.sleevefactiontask.md?raw";
import nsDoc_bitburner_sleevefactiontask_type_md from "../../markdown/bitburner.sleevefactiontask.type.md?raw";
import nsDoc_bitburner_sleeveinfiltratetask_cyclesneeded_md from "../../markdown/bitburner.sleeveinfiltratetask.cyclesneeded.md?raw";
import nsDoc_bitburner_sleeveinfiltratetask_cyclesworked_md from "../../markdown/bitburner.sleeveinfiltratetask.cyclesworked.md?raw";
import nsDoc_bitburner_sleeveinfiltratetask_md from "../../markdown/bitburner.sleeveinfiltratetask.md?raw";
import nsDoc_bitburner_sleeveinfiltratetask_type_md from "../../markdown/bitburner.sleeveinfiltratetask.type.md?raw";
import nsDoc_bitburner_sleeveperson_md from "../../markdown/bitburner.sleeveperson.md?raw";
import nsDoc_bitburner_sleeveperson_memory_md from "../../markdown/bitburner.sleeveperson.memory.md?raw";
import nsDoc_bitburner_sleeveperson_shock_md from "../../markdown/bitburner.sleeveperson.shock.md?raw";
import nsDoc_bitburner_sleeveperson_storedcycles_md from "../../markdown/bitburner.sleeveperson.storedcycles.md?raw";
import nsDoc_bitburner_sleeveperson_sync_md from "../../markdown/bitburner.sleeveperson.sync.md?raw";
import nsDoc_bitburner_sleeverecoverytask_md from "../../markdown/bitburner.sleeverecoverytask.md?raw";
import nsDoc_bitburner_sleeverecoverytask_type_md from "../../markdown/bitburner.sleeverecoverytask.type.md?raw";
import nsDoc_bitburner_sleevesupporttask_md from "../../markdown/bitburner.sleevesupporttask.md?raw";
import nsDoc_bitburner_sleevesupporttask_type_md from "../../markdown/bitburner.sleevesupporttask.type.md?raw";
import nsDoc_bitburner_sleevesynchrotask_md from "../../markdown/bitburner.sleevesynchrotask.md?raw";
import nsDoc_bitburner_sleevesynchrotask_type_md from "../../markdown/bitburner.sleevesynchrotask.type.md?raw";
import nsDoc_bitburner_sleevetask_md from "../../markdown/bitburner.sleevetask.md?raw";
import nsDoc_bitburner_somerequirement_conditions_md from "../../markdown/bitburner.somerequirement.conditions.md?raw";
import nsDoc_bitburner_somerequirement_md from "../../markdown/bitburner.somerequirement.md?raw";
@@ -1688,8 +1714,8 @@ AllPages["nsDoc/bitburner.autocompletedata.txts.md"] = nsDoc_bitburner_autocompl
AllPages["nsDoc/bitburner.backdoorrequirement.md"] = nsDoc_bitburner_backdoorrequirement_md;
AllPages["nsDoc/bitburner.backdoorrequirement.server.md"] = nsDoc_bitburner_backdoorrequirement_server_md;
AllPages["nsDoc/bitburner.backdoorrequirement.type.md"] = nsDoc_bitburner_backdoorrequirement_type_md;
AllPages["nsDoc/bitburner.basetask.cyclesworked.md"] = nsDoc_bitburner_basetask_cyclesworked_md;
AllPages["nsDoc/bitburner.basetask.md"] = nsDoc_bitburner_basetask_md;
AllPages["nsDoc/bitburner.basetask.nextcompletion.md"] = nsDoc_bitburner_basetask_nextcompletion_md;
AllPages["nsDoc/bitburner.basichgwoptions.additionalmsec.md"] = nsDoc_bitburner_basichgwoptions_additionalmsec_md;
AllPages["nsDoc/bitburner.basichgwoptions.md"] = nsDoc_bitburner_basichgwoptions_md;
AllPages["nsDoc/bitburner.basichgwoptions.stock.md"] = nsDoc_bitburner_basichgwoptions_stock_md;
@@ -2327,7 +2353,6 @@ AllPages["nsDoc/bitburner.grafting.graftaugmentation.md"] = nsDoc_bitburner_graf
AllPages["nsDoc/bitburner.grafting.md"] = nsDoc_bitburner_grafting_md;
AllPages["nsDoc/bitburner.grafting.waitforongoinggrafting.md"] = nsDoc_bitburner_grafting_waitforongoinggrafting_md;
AllPages["nsDoc/bitburner.graftingtask.augmentation.md"] = nsDoc_bitburner_graftingtask_augmentation_md;
AllPages["nsDoc/bitburner.graftingtask.completion.md"] = nsDoc_bitburner_graftingtask_completion_md;
AllPages["nsDoc/bitburner.graftingtask.md"] = nsDoc_bitburner_graftingtask_md;
AllPages["nsDoc/bitburner.graftingtask.type.md"] = nsDoc_bitburner_graftingtask_type_md;
AllPages["nsDoc/bitburner.gymenumtype.md"] = nsDoc_bitburner_gymenumtype_md;
@@ -2769,6 +2794,8 @@ AllPages["nsDoc/bitburner.player.md"] = nsDoc_bitburner_player_md;
AllPages["nsDoc/bitburner.player.money.md"] = nsDoc_bitburner_player_money_md;
AllPages["nsDoc/bitburner.player.numpeoplekilled.md"] = nsDoc_bitburner_player_numpeoplekilled_md;
AllPages["nsDoc/bitburner.player.totalplaytime.md"] = nsDoc_bitburner_player_totalplaytime_md;
AllPages["nsDoc/bitburner.playerbasetask.cyclesworked.md"] = nsDoc_bitburner_playerbasetask_cyclesworked_md;
AllPages["nsDoc/bitburner.playerbasetask.md"] = nsDoc_bitburner_playerbasetask_md;
AllPages["nsDoc/bitburner.playerrequirement.md"] = nsDoc_bitburner_playerrequirement_md;
AllPages["nsDoc/bitburner.positionenumtype.md"] = nsDoc_bitburner_positionenumtype_md;
AllPages["nsDoc/bitburner.positiontype.md"] = nsDoc_bitburner_positiontype_md;
@@ -2981,20 +3008,45 @@ AllPages["nsDoc/bitburner.sleeve.settosynchronize.md"] = nsDoc_bitburner_sleeve_
AllPages["nsDoc/bitburner.sleeve.settouniversitycourse.md"] = nsDoc_bitburner_sleeve_settouniversitycourse_md;
AllPages["nsDoc/bitburner.sleeve.travel.md"] = nsDoc_bitburner_sleeve_travel_md;
AllPages["nsDoc/bitburner.sleeve.upgradememory.md"] = nsDoc_bitburner_sleeve_upgradememory_md;
AllPages["nsDoc/bitburner.sleevebladeburnertask.actionname.md"] = nsDoc_bitburner_sleevebladeburnertask_actionname_md;
AllPages["nsDoc/bitburner.sleevebladeburnertask.actiontype.md"] = nsDoc_bitburner_sleevebladeburnertask_actiontype_md;
AllPages["nsDoc/bitburner.sleevebladeburnertask.cyclesneeded.md"] = nsDoc_bitburner_sleevebladeburnertask_cyclesneeded_md;
AllPages["nsDoc/bitburner.sleevebladeburnertask.cyclesworked.md"] = nsDoc_bitburner_sleevebladeburnertask_cyclesworked_md;
AllPages["nsDoc/bitburner.sleevebladeburnertask.md"] = nsDoc_bitburner_sleevebladeburnertask_md;
AllPages["nsDoc/bitburner.sleevebladeburnertask.taskscompleted.md"] = nsDoc_bitburner_sleevebladeburnertask_taskscompleted_md;
AllPages["nsDoc/bitburner.sleevebladeburnertask.type.md"] = nsDoc_bitburner_sleevebladeburnertask_type_md;
AllPages["nsDoc/bitburner.sleeveclasstask.classtype.md"] = nsDoc_bitburner_sleeveclasstask_classtype_md;
AllPages["nsDoc/bitburner.sleeveclasstask.location.md"] = nsDoc_bitburner_sleeveclasstask_location_md;
AllPages["nsDoc/bitburner.sleeveclasstask.md"] = nsDoc_bitburner_sleeveclasstask_md;
AllPages["nsDoc/bitburner.sleeveclasstask.type.md"] = nsDoc_bitburner_sleeveclasstask_type_md;
AllPages["nsDoc/bitburner.sleevecompanytask.companyname.md"] = nsDoc_bitburner_sleevecompanytask_companyname_md;
AllPages["nsDoc/bitburner.sleevecompanytask.md"] = nsDoc_bitburner_sleevecompanytask_md;
AllPages["nsDoc/bitburner.sleevecompanytask.type.md"] = nsDoc_bitburner_sleevecompanytask_type_md;
AllPages["nsDoc/bitburner.sleevecrimetask.crimetype.md"] = nsDoc_bitburner_sleevecrimetask_crimetype_md;
AllPages["nsDoc/bitburner.sleevecrimetask.cyclesneeded.md"] = nsDoc_bitburner_sleevecrimetask_cyclesneeded_md;
AllPages["nsDoc/bitburner.sleevecrimetask.cyclesworked.md"] = nsDoc_bitburner_sleevecrimetask_cyclesworked_md;
AllPages["nsDoc/bitburner.sleevecrimetask.md"] = nsDoc_bitburner_sleevecrimetask_md;
AllPages["nsDoc/bitburner.sleevecrimetask.taskscompleted.md"] = nsDoc_bitburner_sleevecrimetask_taskscompleted_md;
AllPages["nsDoc/bitburner.sleevecrimetask.type.md"] = nsDoc_bitburner_sleevecrimetask_type_md;
AllPages["nsDoc/bitburner.sleevefactiontask.factionname.md"] = nsDoc_bitburner_sleevefactiontask_factionname_md;
AllPages["nsDoc/bitburner.sleevefactiontask.factionworktype.md"] = nsDoc_bitburner_sleevefactiontask_factionworktype_md;
AllPages["nsDoc/bitburner.sleevefactiontask.md"] = nsDoc_bitburner_sleevefactiontask_md;
AllPages["nsDoc/bitburner.sleevefactiontask.type.md"] = nsDoc_bitburner_sleevefactiontask_type_md;
AllPages["nsDoc/bitburner.sleeveinfiltratetask.cyclesneeded.md"] = nsDoc_bitburner_sleeveinfiltratetask_cyclesneeded_md;
AllPages["nsDoc/bitburner.sleeveinfiltratetask.cyclesworked.md"] = nsDoc_bitburner_sleeveinfiltratetask_cyclesworked_md;
AllPages["nsDoc/bitburner.sleeveinfiltratetask.md"] = nsDoc_bitburner_sleeveinfiltratetask_md;
AllPages["nsDoc/bitburner.sleeveinfiltratetask.type.md"] = nsDoc_bitburner_sleeveinfiltratetask_type_md;
AllPages["nsDoc/bitburner.sleeveperson.md"] = nsDoc_bitburner_sleeveperson_md;
AllPages["nsDoc/bitburner.sleeveperson.memory.md"] = nsDoc_bitburner_sleeveperson_memory_md;
AllPages["nsDoc/bitburner.sleeveperson.shock.md"] = nsDoc_bitburner_sleeveperson_shock_md;
AllPages["nsDoc/bitburner.sleeveperson.storedcycles.md"] = nsDoc_bitburner_sleeveperson_storedcycles_md;
AllPages["nsDoc/bitburner.sleeveperson.sync.md"] = nsDoc_bitburner_sleeveperson_sync_md;
AllPages["nsDoc/bitburner.sleeverecoverytask.md"] = nsDoc_bitburner_sleeverecoverytask_md;
AllPages["nsDoc/bitburner.sleeverecoverytask.type.md"] = nsDoc_bitburner_sleeverecoverytask_type_md;
AllPages["nsDoc/bitburner.sleevesupporttask.md"] = nsDoc_bitburner_sleevesupporttask_md;
AllPages["nsDoc/bitburner.sleevesupporttask.type.md"] = nsDoc_bitburner_sleevesupporttask_type_md;
AllPages["nsDoc/bitburner.sleevesynchrotask.md"] = nsDoc_bitburner_sleevesynchrotask_md;
AllPages["nsDoc/bitburner.sleevesynchrotask.type.md"] = nsDoc_bitburner_sleevesynchrotask_type_md;
AllPages["nsDoc/bitburner.sleevetask.md"] = nsDoc_bitburner_sleevetask_md;
AllPages["nsDoc/bitburner.somerequirement.conditions.md"] = nsDoc_bitburner_somerequirement_conditions_md;
AllPages["nsDoc/bitburner.somerequirement.md"] = nsDoc_bitburner_somerequirement_md;
+34 -18
View File
@@ -4,6 +4,8 @@ import { TextField } from "@mui/material";
import Button from "@mui/material/Button";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import InfoIcon from "@mui/icons-material/Info";
import { Player } from "@player";
import {
@@ -24,6 +26,7 @@ export function ShareOption({ rerender }: { rerender: () => void }): React.React
const home = Player.getHomeComputer();
const threads = Math.floor(ram / 4);
const ramUsage = roundToTwo(4 * threads);
function onShare(): void {
if (threads === 0) {
@@ -34,7 +37,6 @@ export function ShareOption({ rerender }: { rerender: () => void }): React.React
return;
}
const freeRAM = home.maxRam - home.ramUsed;
const ramUsage = roundToTwo(4 * threads);
if (ramUsage > freeRAM + 0.001) {
dialogBoxCreate("Not enough RAM.");
return;
@@ -50,6 +52,7 @@ export function ShareOption({ rerender }: { rerender: () => void }): React.React
rerender();
}, ShareBonusTime);
pendingUIShareJobIds.push(jobId);
rerender();
}
return (
@@ -62,27 +65,40 @@ export function ShareOption({ rerender }: { rerender: () => void }): React.React
<br />
Free RAM on home computer: {formatRam(home.maxRam - home.ramUsed)}.
<br />
Current bonus: {formatNumber(calculateCurrentShareBonus(), 6)}. Bonus with {formatRam(ram)}:{" "}
Current bonus: {formatNumber(calculateCurrentShareBonus(), 6)}. Bonus with {formatRam(ramUsage)}:{" "}
{formatNumber(calculateShareBonusWithAdditionalThreads(threads, home.cpuCores), 6)}
</Typography>
<TextField
value={ram}
onChange={(event) => {
if (event.target.value === "") {
setRam(0);
return;
<Typography component="div" style={{ display: "flex" }}>
<TextField
value={ram}
onChange={(event) => {
if (event.target.value === "") {
setRam(0);
return;
}
const value = Number.parseFloat(event.target.value);
if (!Number.isFinite(value) || value < 0) {
return;
}
setRam(value);
}}
/>
<Tooltip
title={
<Typography>
RAM shared via this tool is rounded down to the nearest multiple of 4.
<br />
For example, a value of 18 GB results in 16 GB.
</Typography>
}
const value = Number.parseFloat(event.target.value);
if (!Number.isFinite(value) || value < 0) {
return;
}
setRam(value);
}}
InputProps={{
endAdornment: <Button onClick={onShare}>Share</Button>,
}}
/>
>
<Typography component="div" style={{ display: "flex", alignItems: "center" }}>
<Button onClick={onShare}>Share</Button>
<InfoIcon sx={{ fontSize: "1.5em", marginLeft: "10px" }} />
</Typography>
</Tooltip>
</Typography>
</Paper>
);
}
+25 -6
View File
@@ -9,6 +9,10 @@ import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { useCycleRerender } from "../../ui/React/hooks";
import Button from "@mui/material/Button";
import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { Factions } from "../../Faction/Factions";
/** React Component for all the gang stuff. */
export function GangRoot(): React.ReactElement {
@@ -18,7 +22,7 @@ export function GangRoot(): React.ReactElement {
})();
const [value, setValue] = React.useState(0);
function handleChange(event: React.SyntheticEvent, tab: number): void {
function handleChange(__event: React.SyntheticEvent, tab: number): void {
setValue(tab);
}
@@ -26,11 +30,26 @@ export function GangRoot(): React.ReactElement {
return (
<Context.Gang.Provider value={gang}>
<Tabs variant="fullWidth" value={value} onChange={handleChange} sx={{ minWidth: "fit-content", maxWidth: "45%" }}>
<Tab label="Management" />
<Tab label="Equipment" />
<Tab label="Territory" />
</Tabs>
<div style={{ display: "flex" }}>
<Tabs
variant="fullWidth"
value={value}
onChange={handleChange}
sx={{ minWidth: "fit-content", maxWidth: "45%" }}
>
<Tab label="Management" />
<Tab label="Equipment" />
<Tab label="Territory" />
</Tabs>
<Button
style={{ marginLeft: "20px" }}
onClick={() => {
Router.toPage(Page.Faction, { faction: Factions[gang.facName] });
}}
>
Faction
</Button>
</div>
{value === 0 && <ManagementSubpage />}
{value === 1 && <EquipmentsSubpage />}
{value === 2 && <TerritorySubpage />}
+5 -5
View File
@@ -16,7 +16,7 @@ export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1
return 0;
}
if (startingLevel >= HacknetNodeConstants.MaxLevel) {
if (startingLevel + sanitizedLevels > HacknetNodeConstants.MaxLevel) {
return Infinity;
}
@@ -37,7 +37,7 @@ export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, co
return 0;
}
if (startingRam >= HacknetNodeConstants.MaxRam) {
if (startingRam * Math.pow(2, sanitizedLevels) > HacknetNodeConstants.MaxRam) {
return Infinity;
}
@@ -60,20 +60,20 @@ export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, co
return totalCost;
}
export function calculateCoreUpgradeCost(startingCore: number, extraLevels = 1, costMult = 1): number {
export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1, costMult = 1): number {
const sanitizedCores = Math.round(extraLevels);
if (isNaN(sanitizedCores) || sanitizedCores < 1) {
return 0;
}
if (startingCore >= HacknetNodeConstants.MaxCores) {
if (startingCores + sanitizedCores > HacknetNodeConstants.MaxCores) {
return Infinity;
}
const coreBaseCost = HacknetNodeConstants.CoreBaseCost;
const mult = HacknetNodeConstants.UpgradeCoreMult;
let totalCost = 0;
let currentCores = startingCore;
let currentCores = startingCores;
for (let i = 0; i < sanitizedCores; ++i) {
totalCost += coreBaseCost * Math.pow(mult, currentCores - 1);
++currentCores;
+4 -4
View File
@@ -22,7 +22,7 @@ export function calculateLevelUpgradeCost(startingLevel: number, extraLevels = 1
return 0;
}
if (startingLevel >= HacknetServerConstants.MaxLevel) {
if (startingLevel + sanitizedLevels > HacknetServerConstants.MaxLevel) {
return Infinity;
}
@@ -43,7 +43,7 @@ export function calculateRamUpgradeCost(startingRam: number, extraLevels = 1, co
return 0;
}
if (startingRam >= HacknetServerConstants.MaxRam) {
if (startingRam * Math.pow(2, sanitizedLevels) > HacknetServerConstants.MaxRam) {
return Infinity;
}
@@ -70,7 +70,7 @@ export function calculateCoreUpgradeCost(startingCores: number, extraLevels = 1,
return 0;
}
if (startingCores >= HacknetServerConstants.MaxCores) {
if (startingCores + sanitizedLevels > HacknetServerConstants.MaxCores) {
return Infinity;
}
@@ -93,7 +93,7 @@ export function calculateCacheUpgradeCost(startingCache: number, extraLevels = 1
return 0;
}
if (startingCache >= HacknetServerConstants.MaxCache) {
if (startingCache + sanitizedLevels > HacknetServerConstants.MaxCache) {
return Infinity;
}
+1 -1
View File
@@ -48,7 +48,7 @@ function coloredArrow(difficulty: number): JSX.Element {
} else {
return (
<>
{arrowPart(Settings.theme.primary, cappedDifficulty * 13)}
{arrowPart(Settings.theme.success, cappedDifficulty * 13)}
{arrowPart(Settings.theme.warning, (cappedDifficulty - 1) * 13)}
{arrowPart(Settings.theme.warning, (cappedDifficulty - 2) * 13)}
{arrowPart(Settings.theme.error, (cappedDifficulty - 3) * 26)}
+24 -24
View File
@@ -4,37 +4,37 @@ let pidCounter = 1;
/** Find and return the next available PID for a script */
export function generateNextPid(): number {
let tempCounter = pidCounter;
let pidCandidate = pidCounter;
// Cap the number of search iterations at some arbitrary value to avoid
// infinite loops. We'll assume that players wont have 1mil+ running scripts
let found = false;
for (let i = 0; i < 1e6; ) {
if (!workerScripts.has(tempCounter + i)) {
found = true;
tempCounter = tempCounter + i;
break;
// infinite loops. We'll assume that players won't have a million running scripts.
for (let attemptCounter = 0; attemptCounter < 1e6; ++attemptCounter, ++pidCandidate) {
// ensure the candidate PID is a safe integer
if (pidCandidate >= Number.MAX_SAFE_INTEGER) {
pidCandidate = 1;
}
if (i === Number.MAX_SAFE_INTEGER - 1) {
i = 1;
} else {
++i;
// ensure the PID is not in use
if (workerScripts.has(pidCandidate)) {
continue;
}
// found a PID that's not in use
pidCounter = pidCandidate + 1;
return pidCandidate;
}
if (found) {
pidCounter = tempCounter + 1;
if (pidCounter >= Number.MAX_SAFE_INTEGER) {
pidCounter = 1;
}
return tempCounter;
} else {
return -1;
}
// ran out of attempts without finding an unused PID :-(
return -1;
}
/**
* Reset the PID counter to 1.
*
* Note that the list of recently finished scripts has to be
* cleared (`recentScripts.splice(0)`) when resetting the PID counter.
* Otherwise scripts which re-use a PID that is still in the
* list of recent scripts do not show up there when they finish
* (if the previous script with that PID is still in the list at that point),
* because the function `AddRecentScript` de-duplicates scripts by PID.
*/
export function resetPidCounter(): void {
pidCounter = 1;
}
+7 -6
View File
@@ -16,7 +16,7 @@ import { helpers } from "../Netscript/NetscriptHelpers";
import { getEnumHelper } from "../utils/EnumHelper";
import { Skills } from "../Bladeburner/data/Skills";
import { assertStringWithNSContext } from "../Netscript/TypeAssertion";
import { BlackOperations, blackOpsArray } from "../Bladeburner/data/BlackOperations";
import { numberOfBlackOperations } from "../Bladeburner/data/BlackOperations";
import { checkSleeveAPIAccess, checkSleeveNumber } from "../NetscriptFunctions/Sleeve";
import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils";
import {
@@ -81,20 +81,21 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
return Object.values(BladeburnerOperationName);
},
getBlackOpNames: (ctx) => () => {
getBladeburner(ctx);
const bladeburner = getBladeburner(ctx);
// Ensures they are sent in the correct order
return blackOpsArray.map((blackOp) => blackOp.name);
return bladeburner.blackOperationArray.map((blackOp) => blackOp.name);
},
getNextBlackOp: (ctx) => () => {
const bladeburner = getBladeburner(ctx);
if (bladeburner.numBlackOpsComplete >= blackOpsArray.length) return null;
const blackOp = blackOpsArray[bladeburner.numBlackOpsComplete];
if (bladeburner.numBlackOpsComplete >= numberOfBlackOperations) return null;
const blackOp = bladeburner.blackOperationArray[bladeburner.numBlackOpsComplete];
return { name: blackOp.name, rank: blackOp.reqdRank };
},
getBlackOpRank: (ctx) => (_blackOpName) => {
checkBladeburnerAccess(ctx);
const blackOpName = getEnumHelper("BladeburnerBlackOpName").nsGetMember(ctx, _blackOpName);
return BlackOperations[blackOpName].reqdRank;
const bladeburner = getBladeburner(ctx);
return bladeburner.blackOperations[blackOpName].reqdRank;
},
getGeneralActionNames: (ctx) => () => {
getBladeburner(ctx);
+1 -1
View File
@@ -107,7 +107,7 @@ export function NetscriptGrafting(): InternalAPI<IGrafting> {
`The current work is not a grafting work. Type of current work: ${Player.currentWork.type}.`,
);
}
return Player.currentWork.completion;
return Player.currentWork.nextCompletion;
},
};
}
+2 -2
View File
@@ -45,7 +45,7 @@ import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
import { getRecordEntries } from "../Types/Record";
import { JobTracks } from "../Company/data/JobTracks";
import { ServerConstants } from "../Server/data/Constants";
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
import { numberOfBlackOperations } from "../Bladeburner/data/BlackOperations";
import { calculateEffectiveRequiredReputation } from "../Company/utils";
import { addRepToFavor } from "../Faction/formulas/favor";
import { validBitNodes } from "../BitNode/Constants";
@@ -1176,7 +1176,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
if (!Player.bladeburner) {
return false;
}
return Player.bladeburner.numBlackOpsComplete >= blackOpsArray.length;
return Player.bladeburner.numBlackOpsComplete >= numberOfBlackOperations;
};
if (!hackingRequirements() && !bladeburnerRequirements()) {
+13 -3
View File
@@ -27,7 +27,17 @@ export class Port {
resolver: Resolver | null = null;
promise: Promise<void> | null = null;
add(data: unknown) {
this.data.push(data);
let value = data;
if (isObjectLike(data)) {
try {
value = structuredClone(data);
} catch (ex) {
throw new Error("You can't send Functions, Promises, NS, or other unserializable data through ports!", {
cause: ex,
});
}
}
this.data.push(value);
if (!this.resolver) return;
this.resolver();
this.resolver = null;
@@ -45,7 +55,7 @@ export class PortHandle implements NetscriptPort {
write(value: unknown): unknown {
const port = getPort(this.n);
// Primitives don't need to be cloned.
port.add(isObjectLike(value) ? structuredClone(value) : value);
port.add(value);
if (port.data.length > Settings.MaxPortCapacity) return port.data.shift();
return null;
}
@@ -54,7 +64,7 @@ export class PortHandle implements NetscriptPort {
const port = getPort(this.n);
if (port.data.length >= Settings.MaxPortCapacity) return false;
// Primitives don't need to be cloned.
port.add(isObjectLike(value) ? structuredClone(value) : value);
port.add(value);
return true;
}
+11 -1
View File
@@ -147,6 +147,16 @@ export abstract class Person implements IPerson {
}
overrideIntelligence(): void {
// Reset intelligence data if the player has not unlocked Intelligence.
// Note that this check cannot reset intelligence data in some edge cases (e.g., bitflume from non-BN5 to BN5). This
// is an accepted limitation.
// For more information, please check https://github.com/bitburner-official/bitburner-src/pull/2666
if (Player.sourceFileLvl(5) === 0 && Player.bitNodeN !== 5) {
this.skills.intelligence = 0;
this.exp.intelligence = 0;
this.persistentIntelligenceData.exp = 0;
return;
}
const persistentIntelligenceSkill = this.calculateSkill(this.persistentIntelligenceData.exp, 1);
// Reset exp and skill to the persistent values if there is no limit (intelligenceOverride) or the limit is greater
// than or equal to the persistent skill.
@@ -172,7 +182,7 @@ export abstract class Person implements IPerson {
* Don't change sourceFileLvl to activeSourceFileLvl. When the player has int level, the ability to gain more int is
* a permanent benefit.
*/
if (Player.sourceFileLvl(5) > 0 || this.skills.intelligence > 0 || Player.bitNodeN === 5) {
if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) {
this.exp.intelligence += exp;
this.skills.intelligence = Math.floor(this.calculateSkill(this.exp.intelligence, 1));
this.persistentIntelligenceData.exp += exp;
+2 -2
View File
@@ -7,7 +7,7 @@ import type { Exploit } from "../../Exploits/Exploit";
import type { Gang } from "../../Gang/Gang";
import type { HacknetNode } from "../../Hacknet/HacknetNode";
import type { Sleeve } from "../Sleeve/Sleeve";
import type { Work } from "../../Work/Work";
import type { PlayerBaseWork } from "../../Work/Work";
import * as augmentationMethods from "./PlayerObjectAugmentationMethods";
import * as bladeburnerMethods from "./PlayerObjectBladeburnerMethods";
@@ -73,7 +73,7 @@ export class PlayerObject extends Person implements IPlayer {
lastSave = 0;
totalPlaytime = 0;
currentWork: Work | null = null;
currentWork: PlayerBaseWork | null = null;
focus = false;
entropy = 0;
@@ -135,12 +135,14 @@ export function prestigeAugmentation(this: PlayerObject): void {
this.hp.current = this.hp.max;
this.finishWork(true, true);
// We need to call overrideIntelligence here instead of prestigeSourceFile to reset intelligence data when installing
// augmentations.
this.overrideIntelligence();
}
export function prestigeSourceFile(this: PlayerObject): void {
this.entropy = 0;
this.prestigeAugmentation();
this.overrideIntelligence();
this.karma = 0;
// Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists)
this.sleeves.forEach((sleeve) => sleeve.prestige());
@@ -1,8 +1,8 @@
import { Work } from "../../Work/Work";
import type { PlayerBaseWork } from "../../Work/Work";
import type { PlayerObject } from "./PlayerObject";
export function startWork(this: PlayerObject, w: Work): void {
export function startWork(this: PlayerObject, w: PlayerBaseWork): void {
if (this.currentWork !== null) {
this.currentWork.finish(true);
}
@@ -1,13 +1,11 @@
import type { Sleeve } from "../Sleeve";
import type { ActionIdentifier } from "../../../Bladeburner/Types";
import type { PromisePair } from "../../../Types/Promises";
import { Player } from "@player";
import { BladeburnerActionType, BladeburnerGeneralActionName } from "@enums";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { CONSTANTS } from "../../../Constants";
import { scaleWorkStats } from "../../../Work/WorkStats";
import { getKeyList } from "../../../utils/helpers/getKeyList";
import { loadActionIdentifier } from "../../../Bladeburner/utils/loadActionIdentifier";
import { invalidWork } from "../../../Work/InvalidWork";
import { assertObject } from "../../../utils/TypeAssertion";
@@ -16,15 +14,14 @@ interface SleeveBladeburnerWorkParams {
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
}
export const isSleeveBladeburnerWork = (w: SleeveWorkClass | null): w is SleeveBladeburnerWork =>
export const isSleeveBladeburnerWork = (w: SleeveBaseWork | null): w is SleeveBladeburnerWork =>
w?.type === SleeveWorkType.BLADEBURNER;
export class SleeveBladeburnerWork extends SleeveWorkClass {
export class SleeveBladeburnerWork extends SleeveBaseWork {
type: SleeveWorkType.BLADEBURNER = SleeveWorkType.BLADEBURNER;
tasksCompleted = 0;
cyclesWorked = 0;
actionId: ActionIdentifier & { type: BladeburnerActionType.General | BladeburnerActionType.Contract };
nextCompletionPair: PromisePair<void> = { promise: null, resolve: null };
constructor(params?: SleeveBladeburnerWorkParams) {
super();
@@ -41,14 +38,6 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
return timeInMs / CONSTANTS.MilliPerCycle;
}
finish() {
if (this.nextCompletionPair.resolve) {
this.nextCompletionPair.resolve();
this.nextCompletionPair.resolve = null;
this.nextCompletionPair.promise = null;
}
}
process(sleeve: Sleeve, cycles: number) {
if (!Player.bladeburner) return sleeve.stopWork();
this.cyclesWorked += cycles;
@@ -67,17 +56,10 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
this.tasksCompleted++;
this.cyclesWorked -= this.cyclesNeeded(sleeve);
// Resolve and reset nextCompletion promise
this.finish();
this.resolveNextCompletion();
}
}
get nextCompletion(): Promise<void> {
if (!this.nextCompletionPair.promise)
this.nextCompletionPair.promise = new Promise((r) => (this.nextCompletionPair.resolve = r));
return this.nextCompletionPair.promise;
}
APICopy(sleeve: Sleeve) {
return {
type: SleeveWorkType.BLADEBURNER as const,
@@ -90,11 +72,9 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
};
}
static savedKeys = getKeyList(SleeveBladeburnerWork, { removedKeys: ["nextCompletionPair"] });
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
return Generic_toJSON("SleeveBladeburnerWork", this, SleeveBladeburnerWork.savedKeys);
return Generic_toJSON("SleeveBladeburnerWork", this);
}
/** Initializes a BladeburnerWork object from a JSON save state. */
@@ -115,7 +95,7 @@ export class SleeveBladeburnerWork extends SleeveWorkClass {
}
}
value.data.actionId = actionId;
return Generic_fromJSON(SleeveBladeburnerWork, value.data, SleeveBladeburnerWork.savedKeys);
return Generic_fromJSON(SleeveBladeburnerWork, value.data);
}
}
@@ -1,6 +1,6 @@
import { ClassType, LocationName, UniversityClassType } from "@enums";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { Classes } from "../../../Work/ClassWork";
import { calculateClassEarnings } from "../../../Work/Formulas";
import { Sleeve } from "../Sleeve";
@@ -9,7 +9,7 @@ import { Locations } from "../../../Locations/Locations";
import { isMember } from "../../../utils/EnumHelper";
import { assertObject } from "../../../utils/TypeAssertion";
export const isSleeveClassWork = (w: SleeveWorkClass | null): w is SleeveClassWork =>
export const isSleeveClassWork = (w: SleeveBaseWork | null): w is SleeveClassWork =>
w !== null && w.type === SleeveWorkType.CLASS;
interface ClassWorkParams {
@@ -17,7 +17,7 @@ interface ClassWorkParams {
location: LocationName;
}
export class SleeveClassWork extends SleeveWorkClass {
export class SleeveClassWork extends SleeveBaseWork {
type: SleeveWorkType.CLASS = SleeveWorkType.CLASS;
classType: ClassType;
location: LocationName;
@@ -46,6 +46,7 @@ export class SleeveClassWork extends SleeveWorkClass {
type: SleeveWorkType.CLASS as const,
classType: this.classType,
location: this.location,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
@@ -2,7 +2,7 @@ import { Player } from "@player";
import { CompanyName, JobName } from "@enums";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { Companies } from "../../../Company/Companies";
import { Company } from "../../../Company/Company";
import { calculateCompanyWorkStats } from "../../../Work/Formulas";
@@ -12,10 +12,10 @@ import { CompanyPositions } from "../../../Company/CompanyPositions";
import { isMember } from "../../../utils/EnumHelper";
import { invalidWork } from "../../../Work/InvalidWork";
export const isSleeveCompanyWork = (w: SleeveWorkClass | null): w is SleeveCompanyWork =>
export const isSleeveCompanyWork = (w: SleeveBaseWork | null): w is SleeveCompanyWork =>
w !== null && w.type === SleeveWorkType.COMPANY;
export class SleeveCompanyWork extends SleeveWorkClass {
export class SleeveCompanyWork extends SleeveBaseWork {
type: SleeveWorkType.COMPANY = SleeveWorkType.COMPANY;
companyName: CompanyName;
@@ -51,6 +51,7 @@ export class SleeveCompanyWork extends SleeveWorkClass {
return {
type: SleeveWorkType.COMPANY as const,
companyName: this.companyName,
nextCompletion: this.nextCompletion,
};
}
@@ -1,7 +1,7 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { CrimeType } from "@enums";
import { Crimes } from "../../../Crime/Crimes";
import { Crime } from "../../../Crime/Crime";
@@ -9,10 +9,10 @@ import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { CONSTANTS } from "../../../Constants";
import { calculateCrimeWorkStats } from "../../../Work/Formulas";
export const isSleeveCrimeWork = (w: SleeveWorkClass | null): w is SleeveCrimeWork =>
export const isSleeveCrimeWork = (w: SleeveBaseWork | null): w is SleeveCrimeWork =>
w !== null && w.type === SleeveWorkType.CRIME;
export class SleeveCrimeWork extends SleeveWorkClass {
export class SleeveCrimeWork extends SleeveBaseWork {
type: SleeveWorkType.CRIME = SleeveWorkType.CRIME;
crimeType: CrimeType;
tasksCompleted = 0;
@@ -49,6 +49,7 @@ export class SleeveCrimeWork extends SleeveWorkClass {
applySleeveGains(sleeve, gains, success ? 1 : 0.25);
this.tasksCompleted++;
this.cyclesWorked -= this.cyclesNeeded();
this.resolveNextCompletion();
}
}
@@ -59,6 +60,7 @@ export class SleeveCrimeWork extends SleeveWorkClass {
tasksCompleted: this.tasksCompleted,
cyclesWorked: this.cyclesWorked,
cyclesNeeded: this.cyclesNeeded(),
nextCompletion: this.nextCompletion,
};
}
@@ -1,7 +1,7 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work";
import { applySleeveGains, SleeveBaseWork, SleeveWorkType } from "./Work";
import { FactionName, FactionWorkType } from "@enums";
import { Factions } from "../../../Faction/Factions";
import { calculateFactionExp, calculateFactionRep } from "../../../Work/Formulas";
@@ -14,10 +14,10 @@ interface SleeveFactionWorkParams {
factionName: FactionName;
}
export const isSleeveFactionWork = (w: SleeveWorkClass | null): w is SleeveFactionWork =>
export const isSleeveFactionWork = (w: SleeveBaseWork | null): w is SleeveFactionWork =>
w !== null && w.type === SleeveWorkType.FACTION;
export class SleeveFactionWork extends SleeveWorkClass {
export class SleeveFactionWork extends SleeveBaseWork {
type: SleeveWorkType.FACTION = SleeveWorkType.FACTION;
factionWorkType: FactionWorkType;
factionName: FactionName;
@@ -56,6 +56,7 @@ export class SleeveFactionWork extends SleeveWorkClass {
type: SleeveWorkType.FACTION as const,
factionWorkType: this.factionWorkType,
factionName: this.factionName,
nextCompletion: this.nextCompletion,
};
}
@@ -1,20 +1,17 @@
import type { PromisePair } from "../../../Types/Promises";
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
import { CONSTANTS } from "../../../Constants";
import { getKeyList } from "../../../utils/helpers/getKeyList";
const infiltrateCycles = 60000 / CONSTANTS.MilliPerCycle;
export const isSleeveInfiltrateWork = (w: SleeveWorkClass | null): w is SleeveInfiltrateWork =>
export const isSleeveInfiltrateWork = (w: SleeveBaseWork | null): w is SleeveInfiltrateWork =>
w !== null && w.type === SleeveWorkType.INFILTRATE;
export class SleeveInfiltrateWork extends SleeveWorkClass {
export class SleeveInfiltrateWork extends SleeveBaseWork {
type: SleeveWorkType.INFILTRATE = SleeveWorkType.INFILTRATE;
cyclesWorked = 0;
nextCompletionPair: PromisePair<void> = { promise: null, resolve: null };
cyclesNeeded(): number {
return infiltrateCycles;
@@ -26,19 +23,7 @@ export class SleeveInfiltrateWork extends SleeveWorkClass {
if (this.cyclesWorked > this.cyclesNeeded()) {
this.cyclesWorked -= this.cyclesNeeded();
Player.bladeburner.infiltrateSynthoidCommunities();
this.finish();
}
}
get nextCompletion(): Promise<void> {
if (!this.nextCompletionPair.promise)
this.nextCompletionPair.promise = new Promise((r) => (this.nextCompletionPair.resolve = r));
return this.nextCompletionPair.promise;
}
finish() {
if (this.nextCompletionPair.resolve) {
this.nextCompletionPair.resolve();
this.nextCompletionPair.resolve = null;
this.nextCompletionPair.promise = null;
this.resolveNextCompletion();
}
}
@@ -51,16 +36,14 @@ export class SleeveInfiltrateWork extends SleeveWorkClass {
};
}
static savedKeys = getKeyList(SleeveInfiltrateWork, { removedKeys: ["nextCompletionPair"] });
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
return Generic_toJSON("SleeveInfiltrateWork", this, SleeveInfiltrateWork.savedKeys);
return Generic_toJSON("SleeveInfiltrateWork", this);
}
/** Initializes a BladeburnerWork object from a JSON save state. */
static fromJSON(value: IReviverValue): SleeveInfiltrateWork {
return Generic_fromJSON(SleeveInfiltrateWork, value.data, SleeveInfiltrateWork.savedKeys);
return Generic_fromJSON(SleeveInfiltrateWork, value.data);
}
}
@@ -1,12 +1,12 @@
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
import { calculateIntelligenceBonus } from "../../formulas/intelligence";
export const isSleeveRecoveryWork = (w: SleeveWorkClass | null): w is SleeveRecoveryWork =>
export const isSleeveRecoveryWork = (w: SleeveBaseWork | null): w is SleeveRecoveryWork =>
w !== null && w.type === SleeveWorkType.RECOVERY;
export class SleeveRecoveryWork extends SleeveWorkClass {
export class SleeveRecoveryWork extends SleeveBaseWork {
type: SleeveWorkType.RECOVERY = SleeveWorkType.RECOVERY;
process(sleeve: Sleeve, cycles: number) {
@@ -18,7 +18,10 @@ export class SleeveRecoveryWork extends SleeveWorkClass {
}
APICopy() {
return { type: SleeveWorkType.RECOVERY as const };
return {
type: SleeveWorkType.RECOVERY as const,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
@@ -1,11 +1,11 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
export const isSleeveSupportWork = (w: SleeveWorkClass | null): w is SleeveSupportWork =>
export const isSleeveSupportWork = (w: SleeveBaseWork | null): w is SleeveSupportWork =>
w !== null && w.type === SleeveWorkType.SUPPORT;
export class SleeveSupportWork extends SleeveWorkClass {
export class SleeveSupportWork extends SleeveBaseWork {
type: SleeveWorkType.SUPPORT = SleeveWorkType.SUPPORT;
constructor() {
super();
@@ -16,10 +16,14 @@ export class SleeveSupportWork extends SleeveWorkClass {
finish(): void {
Player.bladeburner?.sleeveSupport(false);
super.resolveNextCompletion();
}
APICopy() {
return { type: SleeveWorkType.SUPPORT as const };
return {
type: SleeveWorkType.SUPPORT as const,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
@@ -1,13 +1,13 @@
import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { SleeveWorkClass, SleeveWorkType } from "./Work";
import { SleeveBaseWork, SleeveWorkType } from "./Work";
import { calculateIntelligenceBonus } from "../../formulas/intelligence";
export const isSleeveSynchroWork = (w: SleeveWorkClass | null): w is SleeveSynchroWork =>
export const isSleeveSynchroWork = (w: SleeveBaseWork | null): w is SleeveSynchroWork =>
w !== null && w.type === SleeveWorkType.SYNCHRO;
export class SleeveSynchroWork extends SleeveWorkClass {
export class SleeveSynchroWork extends SleeveBaseWork {
type: SleeveWorkType.SYNCHRO = SleeveWorkType.SYNCHRO;
process(sleeve: Sleeve, cycles: number) {
@@ -19,7 +19,10 @@ export class SleeveSynchroWork extends SleeveWorkClass {
}
APICopy() {
return { type: SleeveWorkType.SYNCHRO as const };
return {
type: SleeveWorkType.SYNCHRO as const,
nextCompletion: this.nextCompletion,
};
}
/** Serialize the current object to a JSON save state. */
+7 -2
View File
@@ -12,6 +12,7 @@ import { SleeveSynchroWork } from "./SleeveSynchroWork";
import { SleeveBladeburnerWork } from "./SleeveBladeburnerWork";
import { SleeveInfiltrateWork } from "./SleeveInfiltrateWork";
import { SleeveSupportWork } from "./SleeveSupportWork";
import { BaseWork } from "../../../Work/Work";
export const applySleeveGains = (sleeve: Sleeve, shockedStats: WorkStats, mult = 1): void => {
applyWorkStatsExp(sleeve, shockedStats, mult);
@@ -23,13 +24,17 @@ export const applySleeveGains = (sleeve: Sleeve, shockedStats: WorkStats, mult =
Player.sleeves.forEach((s) => s !== sleeve && applyWorkStatsExp(s, shockedStats, mult * sync * s.shockBonus()));
};
export abstract class SleeveWorkClass {
export abstract class SleeveBaseWork extends BaseWork {
abstract type: SleeveWorkType;
abstract process(sleeve: Sleeve, cycles: number): void;
abstract APICopy(sleeve: Sleeve): SleeveTask;
abstract toJSON(): IReviverValue;
/**
* Child classes that override this function must call `this.resolveNextCompletion()` when appropriate to ensure the
* completion promise is resolved.
*/
finish(): void {
/* left for children to implement */
this.resolveNextCompletion();
}
}
+1 -2
View File
@@ -25,7 +25,6 @@ import { isSleeveClassWork } from "../Work/SleeveClassWork";
import { isSleeveFactionWork } from "../Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork";
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
import { canAccessBitNodeFeature } from "../../../BitNode/BitNodeUtils";
import { getKeyFromReactElements } from "../../../utils/StringHelperFunctions";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@@ -78,7 +77,7 @@ export function StatsElement(props: IProps): React.ReactElement {
color={Settings.theme.cha}
data={{ level: props.sleeve.skills.charisma, exp: props.sleeve.exp.charisma }}
/>
{canAccessBitNodeFeature(5) && (
{props.sleeve.skills.intelligence > 0 && (
<StatsRow
name="Intelligence"
color={Settings.theme.int}
+6 -1
View File
@@ -5,6 +5,11 @@ import { clampNumber } from "../../utils/helpers/clampNumber";
* stat level. Stat-agnostic (same formula for every stat)
*/
export function calculateSkill(exp: number, mult = 1): number {
// Mult can be 0 in BN12 when the player has a very high SF12 level. In this case, the skill level will never change
// from its initial value (1 for most stats, except intelligence).
if (mult === 0) {
return 1;
}
const value = Math.floor(mult * (32 * Math.log(exp + 534.6) - 200));
return clampNumber(value, 1);
}
@@ -12,7 +17,7 @@ export function calculateSkill(exp: number, mult = 1): number {
export function calculateExp(skill: number, mult = 1): number {
const floorSkill = Math.floor(skill);
let value = Math.exp((skill / mult + 200) / 32) - 534.6;
if (skill === floorSkill && Number.isFinite(skill)) {
if (skill === floorSkill && Number.isFinite(skill) && Number.isFinite(value)) {
// Check for floating point rounding issues that would cause the inverse
// operation to return the wrong result.
let calcSkill = calculateSkill(value, mult);
+6
View File
@@ -190,6 +190,8 @@ export function prestigeAugmentation(): void {
}
}
// clear recent scripts
recentScripts.splice(0);
resetPidCounter();
ProgramsSeen.clear();
InvitationsSeen.clear();
@@ -237,6 +239,10 @@ export function prestigeSourceFile(isFlume: boolean): void {
// Re-create foreign servers
initForeignServers(Player.getHomeComputer());
if (canAccessBitNodeFeature(15)) {
getDarkscapeNavigator();
}
if (Player.activeSourceFileLvl(9) >= 2) {
homeComp.setMaxRam(128);
} else if (Player.activeSourceFileLvl(1) > 0) {
+11 -11
View File
@@ -11,6 +11,7 @@ import { prestigeSourceFile } from "./Prestige";
import { getDefaultBitNodeOptions, setBitNodeOptions } from "./BitNode/BitNodeUtils";
import { prestigeWorkerScripts } from "./NetscriptWorker";
import { exceptionAlert } from "./utils/helpers/exceptionAlert";
import { ActivateRecoveryMode } from "./ui/React/RecoveryRoot";
function giveSourceFile(bitNodeNumber: number): void {
const sourceFileKey = "SourceFile" + bitNodeNumber.toString();
@@ -35,9 +36,6 @@ function giveSourceFile(bitNodeNumber: number): void {
}
} else {
Player.sourceFiles.set(bitNodeNumber, 1);
if (bitNodeNumber === 5 && Player.skills.intelligence === 0) {
Player.skills.intelligence = 1;
}
dialogBoxCreate(
<>
You received a Source-File for destroying a BitNode!
@@ -63,13 +61,6 @@ export function enterBitNode(
if (!isFlume) {
giveSourceFile(destroyedBitNode);
} else if (Player.sourceFileLvl(5) === 0 && newBitNode !== 5) {
Player.skills.intelligence = 0;
Player.exp.intelligence = 0;
Player.persistentIntelligenceData.exp = 0;
}
if (newBitNode === 5 && Player.skills.intelligence === 0) {
Player.skills.intelligence = 1;
}
// Set new Bit Node
Player.bitNodeN = newBitNode;
@@ -83,7 +74,16 @@ export function enterBitNode(
setBitNodeOptions(getDefaultBitNodeOptions());
}
prestigeSourceFile(isFlume);
try {
prestigeSourceFile(isFlume);
} catch (error) {
// prestigeSourceFile only throws on critical bugs. In these cases, we must activate recovery mode to ensure the
// player knows something went wrong and can notify us. Recovery mode also disables autosave, which may prevent the
// error from corrupting the save data.
ActivateRecoveryMode(error);
Router.toPage(Page.Recovery);
return;
}
if (newBitNode === 6) {
Router.toPage(Page.BladeburnerCinematic);
+1 -1
View File
@@ -332,7 +332,7 @@ class BitburnerSaveObject implements BitburnerSaveObjectType {
dialogBoxCreate(`Cannot import save data: ${error}`);
return;
}
setTimeout(() => location.reload(), 1000);
setTimeout(() => location.reload(), 0);
}
async getSaveDataFromFile(files: FileList | null): Promise<SaveData> {
+46 -29
View File
@@ -1100,58 +1100,65 @@ interface GangMemberAscension {
}
/** @public */
type SleeveBladeburnerTask = {
interface SleeveBladeburnerTask extends BaseTask {
type: "BLADEBURNER";
actionType: "General" | "Contracts";
actionName: string;
cyclesWorked: number;
cyclesNeeded: number;
nextCompletion: Promise<void>;
tasksCompleted: number;
};
}
/** @public */
type SleeveClassTask = {
interface SleeveClassTask extends BaseTask {
type: "CLASS";
classType: UniversityClassType | GymType;
location: LocationName;
};
}
/** @public */
type SleeveCompanyTask = { type: "COMPANY"; companyName: CompanyName };
interface SleeveCompanyTask extends BaseTask {
type: "COMPANY";
companyName: CompanyName;
}
/** @public */
type SleeveCrimeTask = {
interface SleeveCrimeTask extends BaseTask {
type: "CRIME";
crimeType: CrimeType;
cyclesWorked: number;
cyclesNeeded: number;
tasksCompleted: number;
};
}
/** @public */
type SleeveFactionTask = {
interface SleeveFactionTask extends BaseTask {
type: "FACTION";
factionWorkType: FactionWorkType;
factionName: FactionName;
};
}
/** @public */
type SleeveInfiltrateTask = {
interface SleeveInfiltrateTask extends BaseTask {
type: "INFILTRATE";
cyclesWorked: number;
cyclesNeeded: number;
nextCompletion: Promise<void>;
};
}
/** @public */
type SleeveRecoveryTask = { type: "RECOVERY" };
interface SleeveRecoveryTask extends BaseTask {
type: "RECOVERY";
}
/** @public */
type SleeveSupportTask = { type: "SUPPORT" };
interface SleeveSupportTask extends BaseTask {
type: "SUPPORT";
}
/** @public */
type SleeveSynchroTask = { type: "SYNCHRO" };
interface SleeveSynchroTask extends BaseTask {
type: "SYNCHRO";
}
/** Object representing a sleeve current task.
* @public */
@@ -1737,12 +1744,26 @@ export interface Stock {
nextUpdate(): Promise<number>;
}
interface BaseTask {
/**
* This promise resolves when the task completes or is canceled.
*
* Tasks that do not track progress, such as studying or working for a company, are non-completable, i.e., they
* continue indefinitely until canceled. The `nextCompletion` promise of these tasks resolves only when they are
* canceled.
*
* Among completable tasks, some are repeatable, i.e., they automatically restart after completion. The
* `nextCompletion` promise of these tasks resolves on the next completion or when they are canceled.
*/
nextCompletion: Promise<void>;
}
/**
* Base interface of all tasks.
* Base interface of all player tasks.
*
* @public
*/
export interface BaseTask {
interface PlayerBaseTask extends BaseTask {
/**
* The number of game engine cycles has passed since this task started. 1 engine cycle = 200ms.
*/
@@ -1757,7 +1778,7 @@ export interface BaseTask {
*
* @public
*/
export interface StudyTask extends BaseTask {
interface StudyTask extends PlayerBaseTask {
type: "CLASS";
classType: string;
location: LocationName;
@@ -1771,7 +1792,7 @@ export interface StudyTask extends BaseTask {
*
* @public
*/
export interface CompanyWorkTask extends BaseTask {
interface CompanyWorkTask extends PlayerBaseTask {
type: "COMPANY";
companyName: CompanyName;
}
@@ -1784,7 +1805,7 @@ export interface CompanyWorkTask extends BaseTask {
*
* @public
*/
export interface CreateProgramWorkTask extends BaseTask {
interface CreateProgramWorkTask extends PlayerBaseTask {
type: "CREATE_PROGRAM";
programName: ProgramName;
}
@@ -1797,7 +1818,7 @@ export interface CreateProgramWorkTask extends BaseTask {
*
* @public
*/
export interface CrimeTask extends BaseTask {
interface CrimeTask extends PlayerBaseTask {
type: "CRIME";
crimeType: CrimeType;
}
@@ -1810,7 +1831,7 @@ export interface CrimeTask extends BaseTask {
*
* @public
*/
export interface FactionWorkTask extends BaseTask {
interface FactionWorkTask extends PlayerBaseTask {
type: "FACTION";
factionWorkType: FactionWorkType;
factionName: FactionName;
@@ -1824,13 +1845,9 @@ export interface FactionWorkTask extends BaseTask {
*
* @public
*/
export interface GraftingTask extends BaseTask {
interface GraftingTask extends PlayerBaseTask {
type: "GRAFTING";
augmentation: string;
/**
* This promise resolves when the task is complete.
*/
completion: Promise<void>;
}
/**
@@ -1852,7 +1869,7 @@ export type Task = StudyTask | CompanyWorkTask | CreateProgramWorkTask | CrimeTa
*
* - All boolean options: false
*
* If you specify intelligenceOverride, it must be a non-negative integer.
* If you specify intelligenceOverride, it must be a positive integer.
*
* @public
*/
+2 -1
View File
@@ -121,7 +121,8 @@ export const HelpTexts: Record<string, string[]> = {
backdoor: [
"Usage: backdoor",
" ",
"Install a backdoor on the current machine, grants a secret bonus depending on the machine.",
"Installs a backdoor on the current server, which allows the player to connect to it from any ",
"server. It may also grant a secret bonus, such as store discounts, depending on the server.",
" ",
"Requires root access to run.",
" ",
+6 -4
View File
@@ -8,7 +8,7 @@ import { Money } from "../ui/React/Money";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { Player } from "@player";
import { calculateClassEarnings as calculateClassEarningsRate } from "./Formulas";
import { Work, WorkType } from "./Work";
import { PlayerBaseWork, WorkType } from "./Work";
import { applyWorkStats, newWorkStats, sumWorkStats, WorkStats } from "./WorkStats";
import { findEnumMember } from "../utils/helpers/enum";
import { isMember } from "../utils/EnumHelper";
@@ -78,8 +78,8 @@ interface ClassWorkParams {
singularity: boolean;
}
export const isClassWork = (w: Work | null): w is ClassWork => w !== null && w.type === WorkType.CLASS;
export class ClassWork extends Work {
export const isClassWork = (w: PlayerBaseWork | null): w is ClassWork => w !== null && w.type === WorkType.CLASS;
export class ClassWork extends PlayerBaseWork {
classType: ClassType;
location: LocationName;
earnings = newWorkStats();
@@ -110,7 +110,7 @@ export class ClassWork extends Work {
return false;
}
finish(cancelled: boolean, suppressDialog?: boolean): void {
finish(__cancelled: boolean, suppressDialog?: boolean): void {
if (!this.singularity && !suppressDialog) {
dialogBoxCreate(
<>
@@ -129,6 +129,7 @@ export class ClassWork extends Work {
</>,
);
}
this.resolveNextCompletion();
}
APICopy() {
@@ -137,6 +138,7 @@ export class ClassWork extends Work {
cyclesWorked: this.cyclesWorked,
classType: this.classType,
location: this.location,
nextCompletion: this.nextCompletion,
};
}
+6 -4
View File
@@ -1,7 +1,7 @@
import React from "react";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { Player } from "@player";
import { Work, WorkType } from "./Work";
import { PlayerBaseWork, WorkType } from "./Work";
import { influenceStockThroughCompanyWork } from "../StockMarket/PlayerInfluencing";
import { CompanyName, JobName } from "@enums";
import { calculateCompanyWorkStats } from "./Formulas";
@@ -19,9 +19,9 @@ interface CompanyWorkParams {
singularity: boolean;
}
export const isCompanyWork = (w: Work | null): w is CompanyWork => w !== null && w.type === WorkType.COMPANY;
export const isCompanyWork = (w: PlayerBaseWork | null): w is CompanyWork => w !== null && w.type === WorkType.COMPANY;
export class CompanyWork extends Work {
export class CompanyWork extends PlayerBaseWork {
companyName: CompanyName;
constructor(params?: CompanyWorkParams) {
super(WorkType.COMPANY, params?.singularity ?? false);
@@ -49,7 +49,7 @@ export class CompanyWork extends Work {
influenceStockThroughCompanyWork(company, gains.reputation, cycles);
return false;
}
finish(cancelled: boolean, suppressDialog?: boolean): void {
finish(__cancelled: boolean, suppressDialog?: boolean): void {
if (!this.singularity && !suppressDialog) {
dialogBoxCreate(
<>
@@ -59,6 +59,7 @@ export class CompanyWork extends Work {
</>,
);
}
this.resolveNextCompletion();
}
APICopy() {
@@ -66,6 +67,7 @@ export class CompanyWork extends Work {
type: WorkType.COMPANY as const,
cyclesWorked: this.cyclesWorked,
companyName: this.companyName,
nextCompletion: this.nextCompletion,
};
}
+5 -3
View File
@@ -4,12 +4,12 @@ import { CompletedProgramName } from "@enums";
import { CONSTANTS } from "../Constants";
import { Player } from "@player";
import { Programs } from "../Programs/Programs";
import { Work, WorkType } from "./Work";
import { PlayerBaseWork, WorkType } from "./Work";
import { Program } from "../Programs/Program";
import { calculateIntelligenceBonus } from "../PersonObjects/formulas/intelligence";
import { asProgramFilePath } from "../Paths/ProgramFilePath";
export const isCreateProgramWork = (w: Work | null): w is CreateProgramWork =>
export const isCreateProgramWork = (w: PlayerBaseWork | null): w is CreateProgramWork =>
w !== null && w.type === WorkType.CREATE_PROGRAM;
interface CreateProgramWorkParams {
@@ -17,7 +17,7 @@ interface CreateProgramWorkParams {
singularity: boolean;
}
export class CreateProgramWork extends Work {
export class CreateProgramWork extends PlayerBaseWork {
programName: CompletedProgramName;
// amount of effective work completed on the program (time boosted by skills).
unitCompleted: number;
@@ -94,6 +94,7 @@ export class CreateProgramWork extends Work {
const incompleteName = asProgramFilePath(programName + "-" + perc + "%-INC");
Player.getHomeComputer().pushProgram(incompleteName);
}
this.resolveNextCompletion();
}
APICopy() {
@@ -101,6 +102,7 @@ export class CreateProgramWork extends Work {
type: WorkType.CREATE_PROGRAM as const,
cyclesWorked: this.cyclesWorked,
programName: this.programName,
nextCompletion: this.nextCompletion,
};
}

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