diff --git a/markdown/bitburner.basetask.cyclesworked.md b/markdown/bitburner.playerbasetask.cyclesworked.md
similarity index 52%
rename from markdown/bitburner.basetask.cyclesworked.md
rename to markdown/bitburner.playerbasetask.cyclesworked.md
index c267c7692..46c794558 100644
--- a/markdown/bitburner.basetask.cyclesworked.md
+++ b/markdown/bitburner.playerbasetask.cyclesworked.md
@@ -1,8 +1,8 @@
-[Home](./index.md) > [bitburner](./bitburner.md) > [BaseTask](./bitburner.basetask.md) > [cyclesWorked](./bitburner.basetask.cyclesworked.md)
+[Home](./index.md) > [bitburner](./bitburner.md) > [PlayerBaseTask](./bitburner.playerbasetask.md) > [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.
diff --git a/markdown/bitburner.playerbasetask.md b/markdown/bitburner.playerbasetask.md
new file mode 100644
index 000000000..1545eaacc
--- /dev/null
+++ b/markdown/bitburner.playerbasetask.md
@@ -0,0 +1,59 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [PlayerBaseTask](./bitburner.playerbasetask.md)
+
+## PlayerBaseTask interface
+
+Base interface of all player tasks.
+
+**Signature:**
+
+```typescript
+interface PlayerBaseTask extends BaseTask
+```
+**Extends:** [BaseTask](./bitburner.basetask.md)
+
+## Properties
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[cyclesWorked](./bitburner.playerbasetask.cyclesworked.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+The number of game engine cycles has passed since this task started. 1 engine cycle = 200ms.
+
+
+ |
+
+
diff --git a/markdown/bitburner.sleevebladeburnertask.actionname.md b/markdown/bitburner.sleevebladeburnertask.actionname.md
new file mode 100644
index 000000000..5381294f7
--- /dev/null
+++ b/markdown/bitburner.sleevebladeburnertask.actionname.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) > [actionName](./bitburner.sleevebladeburnertask.actionname.md)
+
+## SleeveBladeburnerTask.actionName property
+
+**Signature:**
+
+```typescript
+actionName: string;
+```
diff --git a/markdown/bitburner.sleevebladeburnertask.actiontype.md b/markdown/bitburner.sleevebladeburnertask.actiontype.md
new file mode 100644
index 000000000..8cec74324
--- /dev/null
+++ b/markdown/bitburner.sleevebladeburnertask.actiontype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) > [actionType](./bitburner.sleevebladeburnertask.actiontype.md)
+
+## SleeveBladeburnerTask.actionType property
+
+**Signature:**
+
+```typescript
+actionType: "General" | "Contracts";
+```
diff --git a/markdown/bitburner.sleevebladeburnertask.cyclesneeded.md b/markdown/bitburner.sleevebladeburnertask.cyclesneeded.md
new file mode 100644
index 000000000..ab79d26a2
--- /dev/null
+++ b/markdown/bitburner.sleevebladeburnertask.cyclesneeded.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) > [cyclesNeeded](./bitburner.sleevebladeburnertask.cyclesneeded.md)
+
+## SleeveBladeburnerTask.cyclesNeeded property
+
+**Signature:**
+
+```typescript
+cyclesNeeded: number;
+```
diff --git a/markdown/bitburner.sleevebladeburnertask.cyclesworked.md b/markdown/bitburner.sleevebladeburnertask.cyclesworked.md
new file mode 100644
index 000000000..bc69216b0
--- /dev/null
+++ b/markdown/bitburner.sleevebladeburnertask.cyclesworked.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) > [cyclesWorked](./bitburner.sleevebladeburnertask.cyclesworked.md)
+
+## SleeveBladeburnerTask.cyclesWorked property
+
+**Signature:**
+
+```typescript
+cyclesWorked: number;
+```
diff --git a/markdown/bitburner.sleevebladeburnertask.md b/markdown/bitburner.sleevebladeburnertask.md
index 7e3afde93..0be4fe465 100644
--- a/markdown/bitburner.sleevebladeburnertask.md
+++ b/markdown/bitburner.sleevebladeburnertask.md
@@ -2,19 +2,140 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [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;
- tasksCompleted: number;
-};
+interface SleeveBladeburnerTask extends BaseTask
```
+**Extends:** [BaseTask](./bitburner.basetask.md)
+
+## Properties
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[actionName](./bitburner.sleevebladeburnertask.actionname.md)
+
+
+ |
+
+
+ |
+
+string
+
+
+ |
+
+
+ |
+|
+
+[actionType](./bitburner.sleevebladeburnertask.actiontype.md)
+
+
+ |
+
+
+ |
+
+"General" \| "Contracts"
+
+
+ |
+
+
+ |
+|
+
+[cyclesNeeded](./bitburner.sleevebladeburnertask.cyclesneeded.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[cyclesWorked](./bitburner.sleevebladeburnertask.cyclesworked.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[tasksCompleted](./bitburner.sleevebladeburnertask.taskscompleted.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[type](./bitburner.sleevebladeburnertask.type.md)
+
+
+ |
+
+
+ |
+
+"BLADEBURNER"
+
+
+ |
+
+
+ |
+
+
diff --git a/markdown/bitburner.sleevebladeburnertask.taskscompleted.md b/markdown/bitburner.sleevebladeburnertask.taskscompleted.md
new file mode 100644
index 000000000..01a67cf0b
--- /dev/null
+++ b/markdown/bitburner.sleevebladeburnertask.taskscompleted.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) > [tasksCompleted](./bitburner.sleevebladeburnertask.taskscompleted.md)
+
+## SleeveBladeburnerTask.tasksCompleted property
+
+**Signature:**
+
+```typescript
+tasksCompleted: number;
+```
diff --git a/markdown/bitburner.sleevebladeburnertask.type.md b/markdown/bitburner.sleevebladeburnertask.type.md
new file mode 100644
index 000000000..f25e51568
--- /dev/null
+++ b/markdown/bitburner.sleevebladeburnertask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) > [type](./bitburner.sleevebladeburnertask.type.md)
+
+## SleeveBladeburnerTask.type property
+
+**Signature:**
+
+```typescript
+type: "BLADEBURNER";
+```
diff --git a/markdown/bitburner.sleeveclasstask.classtype.md b/markdown/bitburner.sleeveclasstask.classtype.md
new file mode 100644
index 000000000..64400a0bd
--- /dev/null
+++ b/markdown/bitburner.sleeveclasstask.classtype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveClassTask](./bitburner.sleeveclasstask.md) > [classType](./bitburner.sleeveclasstask.classtype.md)
+
+## SleeveClassTask.classType property
+
+**Signature:**
+
+```typescript
+classType: UniversityClassType | GymType;
+```
diff --git a/markdown/bitburner.sleeveclasstask.location.md b/markdown/bitburner.sleeveclasstask.location.md
new file mode 100644
index 000000000..3664e2288
--- /dev/null
+++ b/markdown/bitburner.sleeveclasstask.location.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveClassTask](./bitburner.sleeveclasstask.md) > [location](./bitburner.sleeveclasstask.location.md)
+
+## SleeveClassTask.location property
+
+**Signature:**
+
+```typescript
+location: LocationName;
+```
diff --git a/markdown/bitburner.sleeveclasstask.md b/markdown/bitburner.sleeveclasstask.md
index d0d47f5ed..19d22d2c1 100644
--- a/markdown/bitburner.sleeveclasstask.md
+++ b/markdown/bitburner.sleeveclasstask.md
@@ -2,17 +2,89 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [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
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[classType](./bitburner.sleeveclasstask.classtype.md)
+
+
+ |
+
+
+ |
+
+[UniversityClassType](./bitburner.universityclasstype.md) \| [GymType](./bitburner.gymtype.md)
+
+
+ |
+
+
+ |
+|
+
+[location](./bitburner.sleeveclasstask.location.md)
+
+
+ |
+
+
+ |
+
+[LocationName](./bitburner.locationname.md)
+
+
+ |
+
+
+ |
+|
+
+[type](./bitburner.sleeveclasstask.type.md)
+
+
+ |
+
+
+ |
+
+"CLASS"
+
+
+ |
+
+
+ |
+
diff --git a/markdown/bitburner.sleeveclasstask.type.md b/markdown/bitburner.sleeveclasstask.type.md
new file mode 100644
index 000000000..83176cf33
--- /dev/null
+++ b/markdown/bitburner.sleeveclasstask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveClassTask](./bitburner.sleeveclasstask.md) > [type](./bitburner.sleeveclasstask.type.md)
+
+## SleeveClassTask.type property
+
+**Signature:**
+
+```typescript
+type: "CLASS";
+```
diff --git a/markdown/bitburner.sleevecompanytask.companyname.md b/markdown/bitburner.sleevecompanytask.companyname.md
new file mode 100644
index 000000000..ab1b8fb06
--- /dev/null
+++ b/markdown/bitburner.sleevecompanytask.companyname.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveCompanyTask](./bitburner.sleevecompanytask.md) > [companyName](./bitburner.sleevecompanytask.companyname.md)
+
+## SleeveCompanyTask.companyName property
+
+**Signature:**
+
+```typescript
+companyName: CompanyName;
+```
diff --git a/markdown/bitburner.sleevecompanytask.md b/markdown/bitburner.sleevecompanytask.md
index f41d448ba..344ecd84d 100644
--- a/markdown/bitburner.sleevecompanytask.md
+++ b/markdown/bitburner.sleevecompanytask.md
@@ -2,13 +2,72 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [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
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[companyName](./bitburner.sleevecompanytask.companyname.md)
+
+
+ |
+
+
+ |
+
+[CompanyName](./bitburner.companyname.md)
+
+
+ |
+
+
+ |
+|
+
+[type](./bitburner.sleevecompanytask.type.md)
+
+
+ |
+
+
+ |
+
+"COMPANY"
+
+
+ |
+
+
+ |
+
diff --git a/markdown/bitburner.sleevecompanytask.type.md b/markdown/bitburner.sleevecompanytask.type.md
new file mode 100644
index 000000000..000331e77
--- /dev/null
+++ b/markdown/bitburner.sleevecompanytask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveCompanyTask](./bitburner.sleevecompanytask.md) > [type](./bitburner.sleevecompanytask.type.md)
+
+## SleeveCompanyTask.type property
+
+**Signature:**
+
+```typescript
+type: "COMPANY";
+```
diff --git a/markdown/bitburner.sleevecrimetask.crimetype.md b/markdown/bitburner.sleevecrimetask.crimetype.md
new file mode 100644
index 000000000..1e514d52f
--- /dev/null
+++ b/markdown/bitburner.sleevecrimetask.crimetype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveCrimeTask](./bitburner.sleevecrimetask.md) > [crimeType](./bitburner.sleevecrimetask.crimetype.md)
+
+## SleeveCrimeTask.crimeType property
+
+**Signature:**
+
+```typescript
+crimeType: CrimeType;
+```
diff --git a/markdown/bitburner.sleevecrimetask.cyclesneeded.md b/markdown/bitburner.sleevecrimetask.cyclesneeded.md
new file mode 100644
index 000000000..fca86b33b
--- /dev/null
+++ b/markdown/bitburner.sleevecrimetask.cyclesneeded.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveCrimeTask](./bitburner.sleevecrimetask.md) > [cyclesNeeded](./bitburner.sleevecrimetask.cyclesneeded.md)
+
+## SleeveCrimeTask.cyclesNeeded property
+
+**Signature:**
+
+```typescript
+cyclesNeeded: number;
+```
diff --git a/markdown/bitburner.sleevecrimetask.cyclesworked.md b/markdown/bitburner.sleevecrimetask.cyclesworked.md
new file mode 100644
index 000000000..0922ede5d
--- /dev/null
+++ b/markdown/bitburner.sleevecrimetask.cyclesworked.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveCrimeTask](./bitburner.sleevecrimetask.md) > [cyclesWorked](./bitburner.sleevecrimetask.cyclesworked.md)
+
+## SleeveCrimeTask.cyclesWorked property
+
+**Signature:**
+
+```typescript
+cyclesWorked: number;
+```
diff --git a/markdown/bitburner.sleevecrimetask.md b/markdown/bitburner.sleevecrimetask.md
index dc39b67ce..50d401aee 100644
--- a/markdown/bitburner.sleevecrimetask.md
+++ b/markdown/bitburner.sleevecrimetask.md
@@ -2,19 +2,123 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [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
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[crimeType](./bitburner.sleevecrimetask.crimetype.md)
+
+
+ |
+
+
+ |
+
+[CrimeType](./bitburner.crimetype.md)
+
+
+ |
+
+
+ |
+|
+
+[cyclesNeeded](./bitburner.sleevecrimetask.cyclesneeded.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[cyclesWorked](./bitburner.sleevecrimetask.cyclesworked.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[tasksCompleted](./bitburner.sleevecrimetask.taskscompleted.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[type](./bitburner.sleevecrimetask.type.md)
+
+
+ |
+
+
+ |
+
+"CRIME"
+
+
+ |
+
+
+ |
+
diff --git a/markdown/bitburner.sleevecrimetask.taskscompleted.md b/markdown/bitburner.sleevecrimetask.taskscompleted.md
new file mode 100644
index 000000000..4bb60aa83
--- /dev/null
+++ b/markdown/bitburner.sleevecrimetask.taskscompleted.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveCrimeTask](./bitburner.sleevecrimetask.md) > [tasksCompleted](./bitburner.sleevecrimetask.taskscompleted.md)
+
+## SleeveCrimeTask.tasksCompleted property
+
+**Signature:**
+
+```typescript
+tasksCompleted: number;
+```
diff --git a/markdown/bitburner.sleevecrimetask.type.md b/markdown/bitburner.sleevecrimetask.type.md
new file mode 100644
index 000000000..410ad0a1d
--- /dev/null
+++ b/markdown/bitburner.sleevecrimetask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveCrimeTask](./bitburner.sleevecrimetask.md) > [type](./bitburner.sleevecrimetask.type.md)
+
+## SleeveCrimeTask.type property
+
+**Signature:**
+
+```typescript
+type: "CRIME";
+```
diff --git a/markdown/bitburner.sleevefactiontask.factionname.md b/markdown/bitburner.sleevefactiontask.factionname.md
new file mode 100644
index 000000000..ddf15f90f
--- /dev/null
+++ b/markdown/bitburner.sleevefactiontask.factionname.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveFactionTask](./bitburner.sleevefactiontask.md) > [factionName](./bitburner.sleevefactiontask.factionname.md)
+
+## SleeveFactionTask.factionName property
+
+**Signature:**
+
+```typescript
+factionName: FactionName;
+```
diff --git a/markdown/bitburner.sleevefactiontask.factionworktype.md b/markdown/bitburner.sleevefactiontask.factionworktype.md
new file mode 100644
index 000000000..099f0eded
--- /dev/null
+++ b/markdown/bitburner.sleevefactiontask.factionworktype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveFactionTask](./bitburner.sleevefactiontask.md) > [factionWorkType](./bitburner.sleevefactiontask.factionworktype.md)
+
+## SleeveFactionTask.factionWorkType property
+
+**Signature:**
+
+```typescript
+factionWorkType: FactionWorkType;
+```
diff --git a/markdown/bitburner.sleevefactiontask.md b/markdown/bitburner.sleevefactiontask.md
index 84b2c503e..381f9eb68 100644
--- a/markdown/bitburner.sleevefactiontask.md
+++ b/markdown/bitburner.sleevefactiontask.md
@@ -2,17 +2,89 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [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
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[factionName](./bitburner.sleevefactiontask.factionname.md)
+
+
+ |
+
+
+ |
+
+[FactionName](./bitburner.factionname.md)
+
+
+ |
+
+
+ |
+|
+
+[factionWorkType](./bitburner.sleevefactiontask.factionworktype.md)
+
+
+ |
+
+
+ |
+
+[FactionWorkType](./bitburner.factionworktype.md)
+
+
+ |
+
+
+ |
+|
+
+[type](./bitburner.sleevefactiontask.type.md)
+
+
+ |
+
+
+ |
+
+"FACTION"
+
+
+ |
+
+
+ |
+
diff --git a/markdown/bitburner.sleevefactiontask.type.md b/markdown/bitburner.sleevefactiontask.type.md
new file mode 100644
index 000000000..cf41fa2ef
--- /dev/null
+++ b/markdown/bitburner.sleevefactiontask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveFactionTask](./bitburner.sleevefactiontask.md) > [type](./bitburner.sleevefactiontask.type.md)
+
+## SleeveFactionTask.type property
+
+**Signature:**
+
+```typescript
+type: "FACTION";
+```
diff --git a/markdown/bitburner.sleeveinfiltratetask.cyclesneeded.md b/markdown/bitburner.sleeveinfiltratetask.cyclesneeded.md
new file mode 100644
index 000000000..2684dac60
--- /dev/null
+++ b/markdown/bitburner.sleeveinfiltratetask.cyclesneeded.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md) > [cyclesNeeded](./bitburner.sleeveinfiltratetask.cyclesneeded.md)
+
+## SleeveInfiltrateTask.cyclesNeeded property
+
+**Signature:**
+
+```typescript
+cyclesNeeded: number;
+```
diff --git a/markdown/bitburner.sleeveinfiltratetask.cyclesworked.md b/markdown/bitburner.sleeveinfiltratetask.cyclesworked.md
new file mode 100644
index 000000000..282a22e9a
--- /dev/null
+++ b/markdown/bitburner.sleeveinfiltratetask.cyclesworked.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md) > [cyclesWorked](./bitburner.sleeveinfiltratetask.cyclesworked.md)
+
+## SleeveInfiltrateTask.cyclesWorked property
+
+**Signature:**
+
+```typescript
+cyclesWorked: number;
+```
diff --git a/markdown/bitburner.sleeveinfiltratetask.md b/markdown/bitburner.sleeveinfiltratetask.md
index 7f3d92dc8..8cf84aca7 100644
--- a/markdown/bitburner.sleeveinfiltratetask.md
+++ b/markdown/bitburner.sleeveinfiltratetask.md
@@ -2,16 +2,89 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md)
-## SleeveInfiltrateTask type
+## SleeveInfiltrateTask interface
**Signature:**
```typescript
-type SleeveInfiltrateTask = {
- type: "INFILTRATE";
- cyclesWorked: number;
- cyclesNeeded: number;
- nextCompletion: Promise;
-};
+interface SleeveInfiltrateTask extends BaseTask
```
+**Extends:** [BaseTask](./bitburner.basetask.md)
+
+## Properties
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[cyclesNeeded](./bitburner.sleeveinfiltratetask.cyclesneeded.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[cyclesWorked](./bitburner.sleeveinfiltratetask.cyclesworked.md)
+
+
+ |
+
+
+ |
+
+number
+
+
+ |
+
+
+ |
+|
+
+[type](./bitburner.sleeveinfiltratetask.type.md)
+
+
+ |
+
+
+ |
+
+"INFILTRATE"
+
+
+ |
+
+
+ |
+
+
diff --git a/markdown/bitburner.sleeveinfiltratetask.type.md b/markdown/bitburner.sleeveinfiltratetask.type.md
new file mode 100644
index 000000000..258a1cab2
--- /dev/null
+++ b/markdown/bitburner.sleeveinfiltratetask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveInfiltrateTask](./bitburner.sleeveinfiltratetask.md) > [type](./bitburner.sleeveinfiltratetask.type.md)
+
+## SleeveInfiltrateTask.type property
+
+**Signature:**
+
+```typescript
+type: "INFILTRATE";
+```
diff --git a/markdown/bitburner.sleeverecoverytask.md b/markdown/bitburner.sleeverecoverytask.md
index f846b6835..1a078907c 100644
--- a/markdown/bitburner.sleeverecoverytask.md
+++ b/markdown/bitburner.sleeverecoverytask.md
@@ -2,11 +2,55 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveRecoveryTask](./bitburner.sleeverecoverytask.md)
-## SleeveRecoveryTask type
+## SleeveRecoveryTask interface
**Signature:**
```typescript
-type SleeveRecoveryTask = { type: "RECOVERY" };
+interface SleeveRecoveryTask extends BaseTask
```
+**Extends:** [BaseTask](./bitburner.basetask.md)
+
+## Properties
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[type](./bitburner.sleeverecoverytask.type.md)
+
+
+ |
+
+
+ |
+
+"RECOVERY"
+
+
+ |
+
+
+ |
+
+
diff --git a/markdown/bitburner.sleeverecoverytask.type.md b/markdown/bitburner.sleeverecoverytask.type.md
new file mode 100644
index 000000000..7579d0b6c
--- /dev/null
+++ b/markdown/bitburner.sleeverecoverytask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveRecoveryTask](./bitburner.sleeverecoverytask.md) > [type](./bitburner.sleeverecoverytask.type.md)
+
+## SleeveRecoveryTask.type property
+
+**Signature:**
+
+```typescript
+type: "RECOVERY";
+```
diff --git a/markdown/bitburner.sleevesupporttask.md b/markdown/bitburner.sleevesupporttask.md
index 6507ecc30..f6bcd1719 100644
--- a/markdown/bitburner.sleevesupporttask.md
+++ b/markdown/bitburner.sleevesupporttask.md
@@ -2,11 +2,55 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveSupportTask](./bitburner.sleevesupporttask.md)
-## SleeveSupportTask type
+## SleeveSupportTask interface
**Signature:**
```typescript
-type SleeveSupportTask = { type: "SUPPORT" };
+interface SleeveSupportTask extends BaseTask
```
+**Extends:** [BaseTask](./bitburner.basetask.md)
+
+## Properties
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[type](./bitburner.sleevesupporttask.type.md)
+
+
+ |
+
+
+ |
+
+"SUPPORT"
+
+
+ |
+
+
+ |
+
+
diff --git a/markdown/bitburner.sleevesupporttask.type.md b/markdown/bitburner.sleevesupporttask.type.md
new file mode 100644
index 000000000..3ef6e6e1e
--- /dev/null
+++ b/markdown/bitburner.sleevesupporttask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveSupportTask](./bitburner.sleevesupporttask.md) > [type](./bitburner.sleevesupporttask.type.md)
+
+## SleeveSupportTask.type property
+
+**Signature:**
+
+```typescript
+type: "SUPPORT";
+```
diff --git a/markdown/bitburner.sleevesynchrotask.md b/markdown/bitburner.sleevesynchrotask.md
index 83e09684c..b08994c59 100644
--- a/markdown/bitburner.sleevesynchrotask.md
+++ b/markdown/bitburner.sleevesynchrotask.md
@@ -2,11 +2,55 @@
[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveSynchroTask](./bitburner.sleevesynchrotask.md)
-## SleeveSynchroTask type
+## SleeveSynchroTask interface
**Signature:**
```typescript
-type SleeveSynchroTask = { type: "SYNCHRO" };
+interface SleeveSynchroTask extends BaseTask
```
+**Extends:** [BaseTask](./bitburner.basetask.md)
+
+## Properties
+
+|
+
+Property
+
+
+ |
+
+Modifiers
+
+
+ |
+
+Type
+
+
+ |
+
+Description
+
+
+ |
+|
+
+[type](./bitburner.sleevesynchrotask.type.md)
+
+
+ |
+
+
+ |
+
+"SYNCHRO"
+
+
+ |
+
+
+ |
+
+
diff --git a/markdown/bitburner.sleevesynchrotask.type.md b/markdown/bitburner.sleevesynchrotask.type.md
new file mode 100644
index 000000000..af36f25d3
--- /dev/null
+++ b/markdown/bitburner.sleevesynchrotask.type.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [SleeveSynchroTask](./bitburner.sleevesynchrotask.md) > [type](./bitburner.sleevesynchrotask.type.md)
+
+## SleeveSynchroTask.type property
+
+**Signature:**
+
+```typescript
+type: "SYNCHRO";
+```
diff --git a/markdown/bitburner.studytask.md b/markdown/bitburner.studytask.md
index d259e79ee..fe480459d 100644
--- a/markdown/bitburner.studytask.md
+++ b/markdown/bitburner.studytask.md
@@ -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
diff --git a/src/Documentation/pages.ts b/src/Documentation/pages.ts
index 09581d0c9..043b73e55 100644
--- a/src/Documentation/pages.ts
+++ b/src/Documentation/pages.ts
@@ -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;
diff --git a/src/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts
index 639022570..22d8a0853 100644
--- a/src/NetscriptFunctions/Grafting.ts
+++ b/src/NetscriptFunctions/Grafting.ts
@@ -107,7 +107,7 @@ export function NetscriptGrafting(): InternalAPI {
`The current work is not a grafting work. Type of current work: ${Player.currentWork.type}.`,
);
}
- return Player.currentWork.completion;
+ return Player.currentWork.nextCompletion;
},
};
}
diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts
index d99b244ce..02e8a968c 100644
--- a/src/PersonObjects/Player/PlayerObject.ts
+++ b/src/PersonObjects/Player/PlayerObject.ts
@@ -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;
diff --git a/src/PersonObjects/Player/PlayerObjectWorkMethods.ts b/src/PersonObjects/Player/PlayerObjectWorkMethods.ts
index a7a9ee8f4..963ff9679 100644
--- a/src/PersonObjects/Player/PlayerObjectWorkMethods.ts
+++ b/src/PersonObjects/Player/PlayerObjectWorkMethods.ts
@@ -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);
}
diff --git a/src/PersonObjects/Sleeve/Work/SleeveBladeburnerWork.ts b/src/PersonObjects/Sleeve/Work/SleeveBladeburnerWork.ts
index 02d74c9bf..f08573a9b 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveBladeburnerWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveBladeburnerWork.ts
@@ -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 = { 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 {
- 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);
}
}
diff --git a/src/PersonObjects/Sleeve/Work/SleeveClassWork.ts b/src/PersonObjects/Sleeve/Work/SleeveClassWork.ts
index 8923bee96..7b505b499 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveClassWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveClassWork.ts
@@ -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. */
diff --git a/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts b/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts
index 4918bfb2c..b50c12092 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts
@@ -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,
};
}
diff --git a/src/PersonObjects/Sleeve/Work/SleeveCrimeWork.ts b/src/PersonObjects/Sleeve/Work/SleeveCrimeWork.ts
index ce17b3927..f6598a00b 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveCrimeWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveCrimeWork.ts
@@ -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,
};
}
diff --git a/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts b/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts
index 15b24b334..a50a3b0b6 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts
@@ -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,
};
}
diff --git a/src/PersonObjects/Sleeve/Work/SleeveInfiltrateWork.ts b/src/PersonObjects/Sleeve/Work/SleeveInfiltrateWork.ts
index d7e940906..89be7a35c 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveInfiltrateWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveInfiltrateWork.ts
@@ -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 = { 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 {
- 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);
}
}
diff --git a/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts b/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts
index 99f0e26fb..91d6532e4 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts
@@ -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. */
diff --git a/src/PersonObjects/Sleeve/Work/SleeveSupportWork.ts b/src/PersonObjects/Sleeve/Work/SleeveSupportWork.ts
index 308788eae..3e0ca0f14 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveSupportWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveSupportWork.ts
@@ -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. */
diff --git a/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts b/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts
index c348ba2cb..66b0a87a3 100644
--- a/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts
+++ b/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts
@@ -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. */
diff --git a/src/PersonObjects/Sleeve/Work/Work.ts b/src/PersonObjects/Sleeve/Work/Work.ts
index 4c7777a2e..36391fc52 100644
--- a/src/PersonObjects/Sleeve/Work/Work.ts
+++ b/src/PersonObjects/Sleeve/Work/Work.ts
@@ -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();
}
}
diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts
index 8348490dd..13e240c4f 100644
--- a/src/ScriptEditor/NetscriptDefinitions.d.ts
+++ b/src/ScriptEditor/NetscriptDefinitions.d.ts
@@ -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;
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;
-};
+}
/** @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;
}
+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;
+}
+
/**
- * 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;
}
/**
diff --git a/src/Work/ClassWork.tsx b/src/Work/ClassWork.tsx
index 1a8920ca1..da02414d0 100644
--- a/src/Work/ClassWork.tsx
+++ b/src/Work/ClassWork.tsx
@@ -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,
};
}
diff --git a/src/Work/CompanyWork.tsx b/src/Work/CompanyWork.tsx
index b2e27fd18..7b8cd9007 100644
--- a/src/Work/CompanyWork.tsx
+++ b/src/Work/CompanyWork.tsx
@@ -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,
};
}
diff --git a/src/Work/CreateProgramWork.ts b/src/Work/CreateProgramWork.ts
index a1b4099bf..5a3c487b1 100644
--- a/src/Work/CreateProgramWork.ts
+++ b/src/Work/CreateProgramWork.ts
@@ -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,
};
}
diff --git a/src/Work/CrimeWork.ts b/src/Work/CrimeWork.ts
index ad3165f55..5a9b5c851 100644
--- a/src/Work/CrimeWork.ts
+++ b/src/Work/CrimeWork.ts
@@ -6,7 +6,7 @@ import { CONSTANTS } from "../Constants";
import { determineCrimeSuccess } from "../Crime/CrimeHelpers";
import { Crimes } from "../Crime/Crimes";
import { dialogBoxCreate } from "../ui/React/DialogBox";
-import { Work, WorkType } from "./Work";
+import { PlayerBaseWork, WorkType } from "./Work";
import { scaleWorkStats, WorkStats } from "./WorkStats";
import { calculateCrimeWorkStats } from "./Formulas";
import { getEnumHelper } from "../utils/EnumHelper";
@@ -16,9 +16,9 @@ interface CrimeWorkParams {
singularity: boolean;
}
-export const isCrimeWork = (w: Work | null): w is CrimeWork => w !== null && w.type === WorkType.CRIME;
+export const isCrimeWork = (w: PlayerBaseWork | null): w is CrimeWork => w !== null && w.type === WorkType.CRIME;
-export class CrimeWork extends Work {
+export class CrimeWork extends PlayerBaseWork {
crimeType: CrimeType;
unitCompleted: number;
@@ -82,10 +82,7 @@ export class CrimeWork extends Work {
Player.gainAgilityExp(gains.agiExp);
Player.gainCharismaExp(gains.chaExp);
Player.karma -= karma * focusBonus;
- }
-
- finish(): void {
- /** nothing to do */
+ this.resolveNextCompletion();
}
APICopy() {
@@ -93,6 +90,7 @@ export class CrimeWork extends Work {
type: WorkType.CRIME as const,
cyclesWorked: this.cyclesWorked,
crimeType: this.crimeType,
+ nextCompletion: this.nextCompletion,
};
}
diff --git a/src/Work/FactionWork.tsx b/src/Work/FactionWork.tsx
index 5af6390e9..2ed53926f 100644
--- a/src/Work/FactionWork.tsx
+++ b/src/Work/FactionWork.tsx
@@ -1,7 +1,7 @@
import type { Faction } from "../Faction/Faction";
import React from "react";
-import { Work, WorkType } from "./Work";
+import { PlayerBaseWork, WorkType } from "./Work";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { Player } from "@player";
import { FactionName, FactionWorkType } from "@enums";
@@ -18,9 +18,9 @@ interface FactionWorkParams {
faction: FactionName;
}
-export const isFactionWork = (w: Work | null): w is FactionWork => w !== null && w.type === WorkType.FACTION;
+export const isFactionWork = (w: PlayerBaseWork | null): w is FactionWork => w !== null && w.type === WorkType.FACTION;
-export class FactionWork extends Work {
+export class FactionWork extends PlayerBaseWork {
factionWorkType: FactionWorkType;
factionName: FactionName;
@@ -55,7 +55,7 @@ export class FactionWork extends Work {
return false;
}
- finish(cancelled: boolean, suppressDialog?: boolean): void {
+ finish(__cancelled: boolean, suppressDialog?: boolean): void {
if (!this.singularity && !suppressDialog) {
dialogBoxCreate(
<>
@@ -65,6 +65,7 @@ export class FactionWork extends Work {
>,
);
}
+ this.resolveNextCompletion();
}
APICopy() {
@@ -73,6 +74,7 @@ export class FactionWork extends Work {
cyclesWorked: this.cyclesWorked,
factionWorkType: this.factionWorkType,
factionName: this.factionName,
+ nextCompletion: this.nextCompletion,
};
}
diff --git a/src/Work/GraftingWork.tsx b/src/Work/GraftingWork.tsx
index 28c47b88e..84eabc327 100644
--- a/src/Work/GraftingWork.tsx
+++ b/src/Work/GraftingWork.tsx
@@ -3,35 +3,26 @@ import { CONSTANTS } from "../Constants";
import { AugmentationName } from "@enums";
import { GraftableAugmentations } from "../PersonObjects/Grafting/ui/GraftingRoot";
import { Player } from "@player";
-import { Work, WorkType } from "./Work";
+import { PlayerBaseWork, WorkType } from "./Work";
import { graftingIntBonus } from "../PersonObjects/Grafting/GraftingHelpers";
import { applyAugmentation } from "../Augmentation/AugmentationHelpers";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation";
import { Augmentations } from "../Augmentation/Augmentations";
-import { PromisePair } from "../Types/Promises";
-import { getKeyList } from "../utils/helpers/getKeyList";
-export const isGraftingWork = (w: Work | null): w is GraftingWork => w !== null && w.type === WorkType.GRAFTING;
+export const isGraftingWork = (w: PlayerBaseWork | null): w is GraftingWork =>
+ w !== null && w.type === WorkType.GRAFTING;
interface GraftingWorkParams {
augmentation: AugmentationName;
singularity: boolean;
}
-export class GraftingWork extends Work {
+export class GraftingWork extends PlayerBaseWork {
augmentation: AugmentationName;
unitCompleted: number;
unitRate: number;
- completionPromisePair: PromisePair = { promise: null, resolve: null };
-
- get completion(): Promise {
- if (!this.completionPromisePair.promise) {
- this.completionPromisePair.promise = new Promise((r) => (this.completionPromisePair.resolve = r));
- }
- return this.completionPromisePair.promise;
- }
constructor(params?: GraftingWorkParams) {
super(WorkType.GRAFTING, params?.singularity ?? true);
@@ -98,11 +89,7 @@ export class GraftingWork extends Work {
);
}
- if (this.completionPromisePair.resolve) {
- this.completionPromisePair.resolve();
- this.completionPromisePair.resolve = null;
- this.completionPromisePair.promise = null;
- }
+ this.resolveNextCompletion();
}
APICopy() {
@@ -110,20 +97,18 @@ export class GraftingWork extends Work {
type: WorkType.GRAFTING as const,
cyclesWorked: this.cyclesWorked,
augmentation: this.augmentation,
- completion: this.completion,
+ nextCompletion: this.nextCompletion,
};
}
- static savedKeys = getKeyList(GraftingWork, { removedKeys: ["completionPromisePair"] });
-
/** Serialize the current object to a JSON save state. */
toJSON(): IReviverValue {
- return Generic_toJSON("GraftingWork", this, GraftingWork.savedKeys);
+ return Generic_toJSON("GraftingWork", this);
}
/** Initializes a GraftingWork object from a JSON save state. */
static fromJSON(value: IReviverValue): GraftingWork {
- return Generic_fromJSON(GraftingWork, value.data, GraftingWork.savedKeys);
+ return Generic_fromJSON(GraftingWork, value.data);
}
}
diff --git a/src/Work/InvalidWork.ts b/src/Work/InvalidWork.ts
index 92f7726be..3dd3505a3 100644
--- a/src/Work/InvalidWork.ts
+++ b/src/Work/InvalidWork.ts
@@ -2,12 +2,12 @@
import type { PlayerObject } from "../PersonObjects/Player/PlayerObject";
import type { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
import type { SleeveWork } from "../PersonObjects/Sleeve/Work/Work";
-import type { Work } from "./Work";
+import type { PlayerBaseWork } from "./Work";
// Type verifications to validate that Player.currentWork and sleeve.currentWork are allowed to be null.
const __canPlayerWorkBeNull: null extends PlayerObject["currentWork"] ? true : false = true;
const __canSleeveWorkBeNull: null extends Sleeve["currentWork"] ? true : false = true;
-export function invalidWork(): W {
+export function invalidWork(): W {
return null as unknown as W;
}
diff --git a/src/Work/Work.ts b/src/Work/Work.ts
index 9d00d82ee..47c7864e0 100644
--- a/src/Work/Work.ts
+++ b/src/Work/Work.ts
@@ -1,19 +1,46 @@
+import type { PromisePair } from "../Types/Promises";
import type { IReviverValue } from "../utils/JSONReviver";
import type { Task } from "@nsdefs";
-export abstract class Work {
+export abstract class BaseWork {
+ // Make this field private to ensure it's not included in the save data.
+ #nextCompletionPromisePair: PromisePair = { promise: null, resolve: null };
+
+ get nextCompletion(): Promise {
+ if (!this.#nextCompletionPromisePair.promise) {
+ this.#nextCompletionPromisePair.promise = new Promise((r) => (this.#nextCompletionPromisePair.resolve = r));
+ }
+ return this.#nextCompletionPromisePair.promise;
+ }
+
+ resolveNextCompletion(): void {
+ if (this.#nextCompletionPromisePair.resolve) {
+ this.#nextCompletionPromisePair.resolve();
+ this.#nextCompletionPromisePair.resolve = null;
+ this.#nextCompletionPromisePair.promise = null;
+ }
+ }
+}
+
+export abstract class PlayerBaseWork extends BaseWork {
type: WorkType;
singularity: boolean;
cyclesWorked: number;
-
constructor(type: WorkType, singularity: boolean) {
+ super();
this.type = type;
this.singularity = singularity;
this.cyclesWorked = 0;
}
abstract process(cycles: number): boolean;
- abstract finish(cancelled: boolean, suppressDialog?: boolean): void;
+ /**
+ * Child classes that override this function must call `this.resolveNextCompletion()` when appropriate to ensure the
+ * completion promise is resolved.
+ */
+ finish(__cancelled: boolean, __suppressDialog?: boolean): void {
+ this.resolveNextCompletion();
+ }
abstract APICopy(): Task;
abstract toJSON(): IReviverValue;
}
diff --git a/src/utils/APIBreaks/3.0.0.ts b/src/utils/APIBreaks/3.0.0.ts
index 097838697..c7d728095 100644
--- a/src/utils/APIBreaks/3.0.0.ts
+++ b/src/utils/APIBreaks/3.0.0.ts
@@ -639,5 +639,40 @@ export const breakingChanges300: VersionBreakingChange = {
"The function was renamed because it returns information about all gangs, including the player's own gang.",
showWarning: false,
},
+ {
+ brokenAPIs: [
+ {
+ name: "ns.singularity.getCurrentWork",
+ migration: {
+ searchValue: "completion",
+ /**
+ * "completion" is a common word, so we cannot replace all its instances.
+ * This migrator focuses on the most popular use cases of the "completion" promise. It intentionally does
+ * not support complex cases and `completion.then()`.
+ */
+ migrator: (line: string) => {
+ // Direct chaining from API
+ // Use \b:
+ // - The leading \b applies to getCurrentWork, preventing prefixes like foo_getCurrentWork
+ // - The trailing \b applies to completion, preventing suffixes like completionFoo.
+ // Use \s* to match `getCurrentWork ( ) .completion`
+ line = line.replace(/\b(getCurrentWork\s*\(\s*\))\s*\.completion\b/g, "$1.nextCompletion");
+
+ // Awaited property access (`await task.completion`). This is a bit risky, but it's still a common usage.
+ // [a-zA-Z0-9_$.[\]()?]+ is enough to catch common usages.
+ line = line.replace(/(\bawait\s+[a-zA-Z0-9_$.[\]()?]+)\s*\.completion\b/g, "$1.nextCompletion");
+
+ return line;
+ },
+ },
+ },
+ { name: "ns.sleeve.getTask" },
+ ],
+ info:
+ "Task objects returned from ns.singularity.getCurrentWork() and ns.sleeve.getTask() previously had an optional\n" +
+ `promise property named either "completion" or "nextCompletion", depending on the task.\n` +
+ `Now, these task objects always include this property, and it is consistently named "nextCompletion".`,
+ showWarning: false,
+ },
],
};
diff --git a/src/utils/SaveDataMigrationUtils.ts b/src/utils/SaveDataMigrationUtils.ts
index bfaf637f7..64b7c5677 100644
--- a/src/utils/SaveDataMigrationUtils.ts
+++ b/src/utils/SaveDataMigrationUtils.ts
@@ -634,14 +634,14 @@ Error: ${e}`,
person.overrideIntelligence();
}
}
- if (ver < 48) {
+ if (ver < 49) {
+ if (Player.sourceFileLvl(5) === 0 && Player.bitNodeN !== 5) {
+ for (const person of [Player, ...Player.sleeves]) {
+ person.persistentIntelligenceData.exp = 0;
+ person.exp.intelligence = 0;
+ person.skills.intelligence = 0;
+ }
+ }
showAPIBreaks("3.0.0", breakingChanges300);
}
- if (ver < 49 && Player.sourceFileLvl(5) === 0 && Player.bitNodeN !== 5) {
- for (const person of [Player, ...Player.sleeves]) {
- person.persistentIntelligenceData.exp = 0;
- person.exp.intelligence = 0;
- person.skills.intelligence = 0;
- }
- }
}
diff --git a/test/jest/Netscript/Singularity.test.ts b/test/jest/Netscript/Singularity.test.ts
index 23b0dc7f0..305d418f7 100644
--- a/test/jest/Netscript/Singularity.test.ts
+++ b/test/jest/Netscript/Singularity.test.ts
@@ -1,5 +1,21 @@
import { installAugmentations } from "../../../src/Augmentation/AugmentationHelpers";
-import { AugmentationName, CompanyName, CompletedProgramName, FactionName, JobField, JobName } from "@enums";
+import {
+ AugmentationName,
+ BladeburnerContractName,
+ BladeburnerGeneralActionName,
+ CityName,
+ CompanyName,
+ CompletedProgramName,
+ CrimeType,
+ FactionName,
+ FactionWorkType,
+ GymType,
+ JobField,
+ JobName,
+ LocationName,
+ SpecialBladeburnerActionTypeForSleeve,
+ UniversityClassType,
+} from "@enums";
import { Player } from "@player";
import { prestigeSourceFile } from "../../../src/Prestige";
import { disconnectServers, GetServerOrThrow } from "../../../src/Server/AllServers";
@@ -14,6 +30,7 @@ import { CompanyPositions } from "../../../src/Company/CompanyPositions";
import { getTorRouter } from "../../../src/Server/ServerHelpers";
import * as exceptionAlertModule from "../../../src/utils/helpers/exceptionAlert";
import { numberOfBlackOperations } from "../../../src/Bladeburner/data/BlackOperations";
+import type { SleeveTask, Task } from "@nsdefs";
const nextBN = 4;
@@ -844,3 +861,304 @@ describe("Intelligence", () => {
expectIntelligenceExp(50);
});
});
+
+const nextCompletionTestCases = [
+ {
+ action: () =>
+ expect(
+ getNS().singularity.universityCourse(LocationName.Sector12RothmanUniversity, UniversityClassType.algorithms),
+ ).toStrictEqual(true),
+ taskType: "CLASS",
+ isPlayerTask: true,
+ },
+ {
+ action: () =>
+ expect(getNS().singularity.gymWorkout(LocationName.Sector12PowerhouseGym, GymType.strength)).toStrictEqual(true),
+ taskType: "CLASS",
+ isPlayerTask: true,
+ },
+ {
+ action: () => {
+ const ns = getNS();
+ ns.singularity.applyToCompany(LocationName.Sector12JoesGuns, JobField.employee);
+ expect(ns.singularity.workForCompany(LocationName.Sector12JoesGuns)).toStrictEqual(true);
+ },
+ taskType: "COMPANY",
+ isPlayerTask: true,
+ },
+ {
+ action: () => expect(getNS().singularity.createProgram(CompletedProgramName.bruteSsh)).toStrictEqual(true),
+ taskType: "CREATE_PROGRAM",
+ isPlayerTask: true,
+ },
+ {
+ action: () => {
+ const ns = getNS();
+ ns.singularity.commitCrime(CrimeType.mug);
+ expect(ns.singularity.getCurrentWork()?.type === "CRIME").toStrictEqual(true);
+ },
+ taskType: "CRIME",
+ isPlayerTask: true,
+ },
+ {
+ action: () =>
+ expect(getNS().singularity.workForFaction(FactionName.Sector12, FactionWorkType.hacking)).toStrictEqual(true),
+ taskType: "FACTION",
+ isPlayerTask: true,
+ },
+ {
+ action: () => {
+ const ns = getNS();
+ ns.singularity.travelToCity(CityName.NewTokyo);
+ expect(ns.grafting.graftAugmentation(AugmentationName.Targeting1)).toStrictEqual(true);
+ },
+ taskType: "GRAFTING",
+ isPlayerTask: true,
+ },
+ {
+ action: () =>
+ expect(
+ getNS().sleeve.setToUniversityCourse(0, LocationName.Sector12RothmanUniversity, UniversityClassType.algorithms),
+ ).toStrictEqual(true),
+ taskType: "CLASS",
+ isPlayerTask: false,
+ },
+ {
+ action: () =>
+ expect(getNS().sleeve.setToGymWorkout(0, LocationName.Sector12PowerhouseGym, GymType.strength)).toStrictEqual(
+ true,
+ ),
+ taskType: "CLASS",
+ isPlayerTask: false,
+ },
+ {
+ action: () => {
+ const ns = getNS();
+ ns.singularity.applyToCompany(LocationName.Sector12JoesGuns, JobField.employee);
+ expect(ns.sleeve.setToCompanyWork(0, LocationName.Sector12JoesGuns)).toStrictEqual(true);
+ },
+ taskType: "COMPANY",
+ isPlayerTask: false,
+ },
+ {
+ action: () => expect(getNS().sleeve.setToCommitCrime(0, CrimeType.mug)).toStrictEqual(true),
+ taskType: "CRIME",
+ isPlayerTask: false,
+ },
+ {
+ action: () =>
+ expect(getNS().sleeve.setToFactionWork(0, FactionName.Sector12, FactionWorkType.hacking)).toStrictEqual(true),
+ taskType: "FACTION",
+ isPlayerTask: false,
+ },
+ {
+ action: () => expect(getNS().sleeve.setToShockRecovery(0)).toStrictEqual(true),
+ taskType: "RECOVERY",
+ isPlayerTask: false,
+ },
+ {
+ action: () => expect(getNS().sleeve.setToSynchronize(0)).toStrictEqual(true),
+ taskType: "SYNCHRO",
+ isPlayerTask: false,
+ },
+ {
+ action: () =>
+ expect(getNS().sleeve.setToBladeburnerAction(0, BladeburnerGeneralActionName.Training)).toStrictEqual(true),
+ taskType: "BLADEBURNER",
+ isPlayerTask: false,
+ },
+ {
+ action: () =>
+ expect(
+ getNS().sleeve.setToBladeburnerAction(0, SpecialBladeburnerActionTypeForSleeve.InfiltrateSynthoids),
+ ).toStrictEqual(true),
+ taskType: "INFILTRATE",
+ isPlayerTask: false,
+ },
+ {
+ action: () =>
+ expect(
+ getNS().sleeve.setToBladeburnerAction(0, SpecialBladeburnerActionTypeForSleeve.SupportMainSleeve),
+ ).toStrictEqual(true),
+ taskType: "SUPPORT",
+ isPlayerTask: false,
+ },
+ {
+ action: () =>
+ expect(
+ getNS().sleeve.setToBladeburnerAction(
+ 0,
+ SpecialBladeburnerActionTypeForSleeve.TakeOnContracts,
+ BladeburnerContractName.Tracking,
+ ),
+ ).toStrictEqual(true),
+ taskType: "BLADEBURNER",
+ isPlayerTask: false,
+ },
+] as const;
+
+function assertNoCurrentTask(): void {
+ expect(Player.currentWork).toBeNull();
+ expect(Player.sleeves[0].currentWork).toBeNull();
+}
+
+async function testNextCompletion(
+ action: () => void,
+ taskType: Task["type"] | SleeveTask["type"],
+ isPlayerTask: boolean,
+): Promise {
+ const ns = getNS();
+ let isCompletable;
+ let isRepeatable = false;
+ switch (taskType) {
+ case "CLASS":
+ isCompletable = false;
+ break;
+ case "COMPANY":
+ isCompletable = false;
+ break;
+ case "CREATE_PROGRAM":
+ isCompletable = true;
+ break;
+ case "CRIME":
+ isCompletable = true;
+ isRepeatable = true;
+ break;
+ case "FACTION":
+ isCompletable = false;
+ break;
+ case "GRAFTING":
+ isCompletable = true;
+ break;
+ case "BLADEBURNER":
+ isCompletable = true;
+ isRepeatable = true;
+ break;
+ case "INFILTRATE":
+ isCompletable = true;
+ isRepeatable = true;
+ break;
+ case "RECOVERY":
+ isCompletable = false;
+ break;
+ case "SUPPORT":
+ isCompletable = false;
+ break;
+ case "SYNCHRO":
+ isCompletable = false;
+ break;
+ default: {
+ // Verify type switch statement is exhaustive
+ const __a: never = taskType;
+ throw new Error(`Invalid taskType: ${taskType}`);
+ }
+ }
+
+ const processTask = async (cycles: number) => {
+ if (isPlayerTask) {
+ Player.processWork(cycles);
+ } else {
+ Player.sleeves[0].currentWork?.process(Player.sleeves[0], cycles);
+ }
+ // Yield to the microtask queue.
+ await Promise.resolve();
+ };
+
+ const cancelTask = async () => {
+ if (isPlayerTask) {
+ ns.singularity.stopAction();
+ } else {
+ ns.sleeve.setToIdle(0);
+ }
+ // Yield to the microtask queue.
+ await Promise.resolve();
+ };
+
+ const assertCurrentTask = () => {
+ if (isPlayerTask) {
+ expect(Player.currentWork).not.toBeNull();
+ } else {
+ expect(Player.sleeves[0].currentWork).not.toBeNull();
+ }
+ };
+
+ let isResolved = false;
+ const setUpNextCompletionPromise = () => {
+ if (isPlayerTask) {
+ void ns.singularity.getCurrentWork()?.nextCompletion.then(() => (isResolved = true));
+ } else {
+ void ns.sleeve.getTask(0)?.nextCompletion.then(() => (isResolved = true));
+ }
+ };
+
+ assertNoCurrentTask();
+ action();
+ setUpNextCompletionPromise();
+ expect(isResolved).toStrictEqual(false);
+
+ // The current task should remain incomplete after 1 cycle.
+ await processTask(1);
+ assertCurrentTask();
+ expect(isResolved).toStrictEqual(false);
+
+ // Run many cycles to ensure all completable tasks are completed.
+ await processTask(1e4);
+
+ if (isCompletable) {
+ // The nextCompletion promise should be resolved now.
+ expect(isResolved).toStrictEqual(true);
+ if (isRepeatable) {
+ assertCurrentTask();
+ // Create the promise again to verify cancellation.
+ isResolved = false;
+ setUpNextCompletionPromise();
+ } else {
+ assertNoCurrentTask();
+ // Run the action again. We will cancel it later to verify cancellation.
+ if (taskType !== "GRAFTING") {
+ // Delete the completed program before creating it again.
+ if (taskType === "CREATE_PROGRAM") {
+ ns.rm(CompletedProgramName.bruteSsh);
+ }
+ action();
+ } else {
+ // Graft a different augmentation.
+ ns.grafting.graftAugmentation(AugmentationName.BitWire);
+ }
+ // Create the promise again to verify cancellation.
+ isResolved = false;
+ setUpNextCompletionPromise();
+ }
+ }
+ expect(isResolved).toStrictEqual(false);
+ // Verify cancellation.
+ await cancelTask();
+ assertNoCurrentTask();
+ expect(isResolved).toStrictEqual(true);
+}
+
+describe("nextCompletion", () => {
+ beforeEach(() => {
+ setupBasicTestingEnvironment();
+ Player.sourceFiles.set(7, 3);
+ Player.sourceFiles.set(10, 3);
+ prestigeSourceFile(true);
+ Player.money = 1e15;
+ gainTonsOfExp();
+ const ns = getNS();
+ expect(ns.bladeburner.joinBladeburnerDivision()).toStrictEqual(true);
+ if (!Player.bladeburner) {
+ throw new Error("Bladeburner was not initialized");
+ }
+ Player.bladeburner.contracts[BladeburnerContractName.Tracking].count = 1e6;
+ ns.singularity.checkFactionInvitations();
+ expect(ns.singularity.joinFaction(FactionName.Sector12)).toStrictEqual(true);
+ ns.sleeve.setToIdle(0);
+ });
+ test.each(nextCompletionTestCases)(
+ "Task type: $taskType - Is player task: $isPlayerTask",
+ async ({ action, taskType, isPlayerTask }) => {
+ await testNextCompletion(action, taskType, isPlayerTask);
+ },
+ );
+});
|