MISC: Remove fuzzy matching when checking params (#2091)

This commit is contained in:
catloversg
2025-05-19 06:08:19 +07:00
committed by GitHub
parent f4e70720a6
commit 24b31975e7
26 changed files with 281 additions and 395 deletions

View File

@@ -122,8 +122,7 @@
| [SpawnOptions](./bitburner.spawnoptions.md) | |
| [Stanek](./bitburner.stanek.md) | Stanek's Gift API. |
| [StockMarketConstants](./bitburner.stockmarketconstants.md) | Constants used for the stock market game mechanic. |
| [StockOrder](./bitburner.stockorder.md) | <p>Return value of [getOrders](./bitburner.tix.getorders.md)</p><p>Keys are stock symbols, properties are arrays of [StockOrderObject](./bitburner.stockorderobject.md)</p> |
| [StockOrderObject](./bitburner.stockorderobject.md) | Value in map of [StockOrder](./bitburner.stockorder.md) |
| [StockOrder](./bitburner.stockorder.md) | |
| [StudyTask](./bitburner.studytask.md) | Study |
| [TailProperties](./bitburner.tailproperties.md) | |
| [TIX](./bitburner.tix.md) | Stock market API |

View File

@@ -4,12 +4,19 @@
## StockOrder interface
Return value of [getOrders](./bitburner.tix.getorders.md)
Keys are stock symbols, properties are arrays of [StockOrderObject](./bitburner.stockorderobject.md)
**Signature:**
```typescript
interface StockOrder
```
## Properties
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [position](./bitburner.stockorder.position.md) | | [PositionType](./bitburner.positiontype.md) | Order position |
| [price](./bitburner.stockorder.price.md) | | number | Price per share |
| [shares](./bitburner.stockorder.shares.md) | | number | Number of shares |
| [type](./bitburner.stockorder.type.md) | | [OrderType](./bitburner.ordertype.md) | Order type |

View File

@@ -1,8 +1,8 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrderObject](./bitburner.stockorderobject.md) &gt; [position](./bitburner.stockorderobject.position.md)
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrder](./bitburner.stockorder.md) &gt; [position](./bitburner.stockorder.position.md)
## StockOrderObject.position property
## StockOrder.position property
Order position

View File

@@ -1,8 +1,8 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrderObject](./bitburner.stockorderobject.md) &gt; [price](./bitburner.stockorderobject.price.md)
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrder](./bitburner.stockorder.md) &gt; [price](./bitburner.stockorder.price.md)
## StockOrderObject.price property
## StockOrder.price property
Price per share

View File

@@ -1,8 +1,8 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrderObject](./bitburner.stockorderobject.md) &gt; [shares](./bitburner.stockorderobject.shares.md)
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrder](./bitburner.stockorder.md) &gt; [shares](./bitburner.stockorder.shares.md)
## StockOrderObject.shares property
## StockOrder.shares property
Number of shares

View File

@@ -1,8 +1,8 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrderObject](./bitburner.stockorderobject.md) &gt; [type](./bitburner.stockorderobject.type.md)
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrder](./bitburner.stockorder.md) &gt; [type](./bitburner.stockorder.type.md)
## StockOrderObject.type property
## StockOrder.type property
Order type

View File

@@ -1,23 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [StockOrderObject](./bitburner.stockorderobject.md)
## StockOrderObject interface
Value in map of [StockOrder](./bitburner.stockorder.md)
**Signature:**
```typescript
interface StockOrderObject
```
## Properties
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [position](./bitburner.stockorderobject.position.md) | | [PositionType](./bitburner.positiontype.md) | Order position |
| [price](./bitburner.stockorderobject.price.md) | | number | Price per share |
| [shares](./bitburner.stockorderobject.shares.md) | | number | Number of shares |
| [type](./bitburner.stockorderobject.type.md) | | [OrderType](./bitburner.ordertype.md) | Order type |

View File

@@ -9,7 +9,7 @@ Cancel order for stocks.
**Signature:**
```typescript
cancelOrder(sym: string, shares: number, price: number, type: string, pos: string): void;
cancelOrder(sym: string, shares: number, price: number, orderType: OrderType, positionType: PositionType): void;
```
## Parameters
@@ -19,8 +19,8 @@ cancelOrder(sym: string, shares: number, price: number, type: string, pos: strin
| sym | string | Stock symbol. |
| shares | number | Number of shares for order. Must be positive. Will be rounded to the nearest integer. |
| price | number | Execution price for the order. |
| type | string | Type of order. |
| pos | string | Specifies whether the order is a Long or Short position. |
| orderType | [OrderType](./bitburner.ordertype.md) | Type of order. |
| positionType | [PositionType](./bitburner.positiontype.md) | Specifies whether the order is a Long ("L") or Short ("S") position. |
**Returns:**

View File

@@ -9,17 +9,17 @@ Returns your order book for the stock market.
**Signature:**
```typescript
getOrders(): StockOrder;
getOrders(): Record<string, StockOrder[]>;
```
**Returns:**
[StockOrder](./bitburner.stockorder.md)
Record&lt;string, [StockOrder](./bitburner.stockorder.md)<!-- -->\[\]&gt;
Object containing information for all the Limit and Stop Orders you have in the stock market.
Object containing information for all the Limit and Stop Orders you have in the stock market. Keys are stock symbols, and properties are arrays of [StockOrder](./bitburner.stockorder.md)
## Remarks
RAM cost: 2.5 GB This is an object containing information for all the Limit and Stop Orders you have in the stock market. For each symbol you have a position in, the returned object will have a key with that symbol's name. The object's properties are each an array of [StockOrderObject](./bitburner.stockorderobject.md) The object has the following structure:
RAM cost: 2.5 GB This is an object containing information for all the Limit and Stop Orders you have in the stock market. For each symbol you have a position in, the returned object will have a key with that symbol's name. The object's properties are each an array of [StockOrder](./bitburner.stockorder.md) The object has the following structure:
```js
{

View File

@@ -9,7 +9,7 @@ Calculates cost of buying stocks.
**Signature:**
```typescript
getPurchaseCost(sym: string, shares: number, posType: string): number;
getPurchaseCost(sym: string, shares: number, positionType: PositionType): number;
```
## Parameters
@@ -18,7 +18,7 @@ getPurchaseCost(sym: string, shares: number, posType: string): number;
| --- | --- | --- |
| sym | string | Stock symbol. |
| shares | number | Number of shares to purchase. |
| posType | string | Specifies whether the order is a Long or Short position. |
| positionType | [PositionType](./bitburner.positiontype.md) | Specifies whether the order is a Long ("L") or Short ("S") position. |
**Returns:**

View File

@@ -9,7 +9,7 @@ Calculate profit of selling stocks.
**Signature:**
```typescript
getSaleGain(sym: string, shares: number, posType: string): number;
getSaleGain(sym: string, shares: number, positionType: PositionType): number;
```
## Parameters
@@ -18,7 +18,7 @@ getSaleGain(sym: string, shares: number, posType: string): number;
| --- | --- | --- |
| sym | string | Stock symbol. |
| shares | number | Number of shares to sell. |
| posType | string | Specifies whether the order is a Long or Short position. |
| positionType | [PositionType](./bitburner.positiontype.md) | Specifies whether the order is a Long ("L") or Short ("S") position. |
**Returns:**

View File

@@ -18,7 +18,7 @@ export interface TIX
| --- | --- |
| [buyShort(sym, shares)](./bitburner.tix.buyshort.md) | Short stocks. |
| [buyStock(sym, shares)](./bitburner.tix.buystock.md) | Buy stocks. |
| [cancelOrder(sym, shares, price, type, pos)](./bitburner.tix.cancelorder.md) | Cancel order for stocks. |
| [cancelOrder(sym, shares, price, orderType, positionType)](./bitburner.tix.cancelorder.md) | Cancel order for stocks. |
| [getAskPrice(sym)](./bitburner.tix.getaskprice.md) | Returns the ask price of that stock. |
| [getBidPrice(sym)](./bitburner.tix.getbidprice.md) | Returns the bid price of that stock. |
| [getBonusTime()](./bitburner.tix.getbonustime.md) | Get Stock Market bonus time. |
@@ -29,8 +29,8 @@ export interface TIX
| [getOrganization(sym)](./bitburner.tix.getorganization.md) | Returns the organization associated with a stock symbol. |
| [getPosition(sym)](./bitburner.tix.getposition.md) | Returns the players position in a stock. |
| [getPrice(sym)](./bitburner.tix.getprice.md) | Returns the price of a stock. |
| [getPurchaseCost(sym, shares, posType)](./bitburner.tix.getpurchasecost.md) | Calculates cost of buying stocks. |
| [getSaleGain(sym, shares, posType)](./bitburner.tix.getsalegain.md) | Calculate profit of selling stocks. |
| [getPurchaseCost(sym, shares, positionType)](./bitburner.tix.getpurchasecost.md) | Calculates cost of buying stocks. |
| [getSaleGain(sym, shares, positionType)](./bitburner.tix.getsalegain.md) | Calculate profit of selling stocks. |
| [getSymbols()](./bitburner.tix.getsymbols.md) | Returns an array of the symbols of the tradable stocks |
| [getVolatility(sym)](./bitburner.tix.getvolatility.md) | Returns the volatility of the specified stock. |
| [has4SData()](./bitburner.tix.has4sdata.md) | Returns true if the player has access to the 4S Data |
@@ -38,7 +38,7 @@ export interface TIX
| [hasTIXAPIAccess()](./bitburner.tix.hastixapiaccess.md) | Returns true if the player has access to the TIX API |
| [hasWSEAccount()](./bitburner.tix.haswseaccount.md) | Returns true if the player has access to a WSE Account |
| [nextUpdate()](./bitburner.tix.nextupdate.md) | Sleep until the next Stock Market price update has happened. |
| [placeOrder(sym, shares, price, type, pos)](./bitburner.tix.placeorder.md) | Place order for stocks. |
| [placeOrder(sym, shares, price, orderType, positionType)](./bitburner.tix.placeorder.md) | Place order for stocks. |
| [purchase4SMarketData()](./bitburner.tix.purchase4smarketdata.md) | Purchase 4S Market Data Access. |
| [purchase4SMarketDataTixApi()](./bitburner.tix.purchase4smarketdatatixapi.md) | Purchase 4S Market Data TIX API Access. |
| [purchaseTixApi()](./bitburner.tix.purchasetixapi.md) | Purchase TIX API Access |

View File

@@ -9,7 +9,7 @@ Place order for stocks.
**Signature:**
```typescript
placeOrder(sym: string, shares: number, price: number, type: string, pos: string): boolean;
placeOrder(sym: string, shares: number, price: number, orderType: OrderType, positionType: PositionType): boolean;
```
## Parameters
@@ -19,8 +19,8 @@ placeOrder(sym: string, shares: number, price: number, type: string, pos: string
| sym | string | Stock symbol. |
| shares | number | Number of shares for order. Must be positive. Will be rounded to the nearest integer. |
| price | number | Execution price for the order. |
| type | string | Type of order. |
| pos | string | Specifies whether the order is a Long or Short position. |
| orderType | [OrderType](./bitburner.ordertype.md) | Type of order. |
| positionType | [PositionType](./bitburner.positiontype.md) | Specifies whether the order is a Long ("L") or Short ("S") position. |
**Returns:**

View File

@@ -7,6 +7,7 @@ import type { Skills as PersonSkills } from "../PersonObjects/Skills";
import {
AugmentationName,
BladeburnerActionType,
type BladeburnerBlackOpName,
BladeburnerContractName,
BladeburnerGeneralActionName,
BladeburnerMultName,
@@ -246,7 +247,7 @@ export class Bladeburner implements OperationTeam {
}
const type = args[1];
const name = args[2];
const action = this.getActionFromTypeAndName(type, name);
const action = this.guessActionFromTypeAndName(type, name);
if (!action) {
this.postToConsole(`Invalid action type / name specified: type: ${type}, name: ${name}`);
return;
@@ -1406,8 +1407,25 @@ export class Bladeburner implements OperationTeam {
}
}
/** Fuzzy matching for action identifiers. Should be removed in 3.0 */
getActionFromTypeAndName(type: string, name: string): Action | null {
getActionFromTypeAndName(type: BladeburnerActionType, name: string): Action | undefined {
/**
* Typecasting "name" instead of checking it with getEnumHelper().isMember() is intentional. The callers will handle
* the undefined value if "name" is invalid.
*/
switch (type) {
case BladeburnerActionType.General:
return GeneralActions[name as BladeburnerGeneralActionName];
case BladeburnerActionType.Contract:
return this.contracts[name as BladeburnerContractName];
case BladeburnerActionType.Operation:
return this.operations[name as BladeburnerOperationName];
case BladeburnerActionType.BlackOp:
return BlackOperations[name as BladeburnerBlackOpName];
}
}
/** Fuzzy matching for action identifiers. Do not use this function for anything except BB console. */
guessActionFromTypeAndName(type: string, name: string): Action | null {
if (!type || !name) return null;
const id = autoCompleteTypeShorthand(type, name);
return id ? this.getActionObject(id) : null;

View File

@@ -14,6 +14,12 @@ export enum BladeburnerGeneralActionName {
InciteViolence = "Incite Violence",
}
export enum SpecialBladeburnerActionTypeForSleeve {
InfiltrateSynthoids = "Infiltrate Synthoids",
SupportMainSleeve = "Support main sleeve",
TakeOnContracts = "Take on contracts",
}
export enum BladeburnerContractName {
Tracking = "Tracking",
BountyHunter = "Bounty Hunter",

View File

@@ -1,8 +1,6 @@
import { Crimes } from "./Crimes";
import { Crime } from "./Crime";
import { Player } from "@player";
import { getEnumHelper } from "../utils/EnumHelper";
import { CrimeType } from "@enums";
//This is only used for the player
@@ -11,25 +9,3 @@ export function determineCrimeSuccess(type: CrimeType): boolean {
const chance = crime.successRate(Player);
return Math.random() <= chance;
}
export function findCrime(roughName: string): Crime | null {
const matchedName = getEnumHelper("CrimeType").getMember(roughName, { fuzzy: true });
if (matchedName) return Crimes[matchedName];
// This can probably all be removed
roughName = roughName.toLowerCase();
if (roughName.includes("shoplift")) return Crimes[CrimeType.shoplift];
else if (roughName.includes("rob") && roughName.includes("store")) return Crimes[CrimeType.robStore];
else if (roughName.includes("mug")) return Crimes[CrimeType.mug];
else if (roughName.includes("larceny")) return Crimes[CrimeType.larceny];
else if (roughName.includes("drugs")) return Crimes[CrimeType.dealDrugs];
else if (roughName.includes("bond") && roughName.includes("forge")) return Crimes[CrimeType.bondForgery];
else if ((roughName.includes("traffic") || roughName.includes("illegal")) && roughName.includes("arms")) {
return Crimes[CrimeType.traffickArms];
} else if (roughName.includes("homicide")) return Crimes[CrimeType.homicide];
else if (roughName.includes("grand") && roughName.includes("auto")) return Crimes[CrimeType.grandTheftAuto];
else if (roughName.includes("kidnap")) return Crimes[CrimeType.kidnap];
else if (roughName.includes("assassin")) return Crimes[CrimeType.assassination];
else if (roughName.includes("heist")) return Crimes[CrimeType.heist];
//
return null;
}

View File

@@ -35,13 +35,17 @@ export function NetscriptBladeburner(): InternalAPI<INetscriptBladeburner> {
throw helpers.errorMessage(ctx, "You must be a member of the Bladeburner division to use this API.");
return bladeburner;
};
function getAction(ctx: NetscriptContext, type: unknown, name: unknown): Action {
function getAction(ctx: NetscriptContext, _type: unknown, name: unknown): Action {
const bladeburner = Player.bladeburner;
assertStringWithNSContext(ctx, "type", type);
const type = getEnumHelper("BladeburnerActionType").nsGetMember(ctx, _type);
assertStringWithNSContext(ctx, "name", name);
if (bladeburner === null) throw new Error("Must have joined bladeburner");
if (bladeburner === null) {
throw new Error("Must have joined bladeburner");
}
const action = bladeburner.getActionFromTypeAndName(type, name);
if (!action) throw helpers.errorMessage(ctx, `Invalid action type='${type}', name='${name}'`);
if (!action) {
throw helpers.errorMessage(ctx, `Invalid action type='${_type}', name='${name}'`);
}
return action;
}

View File

@@ -26,7 +26,7 @@ import {
calculateGrowTime,
calculateWeakenTime,
} from "../Hacking";
import { CityName, CompletedProgramName, FactionWorkType, GymType, LocationName, UniversityClassType } from "@enums";
import { CityName, CompletedProgramName, LocationName } from "@enums";
import { Formulas as IFormulas, Player as IPlayer, Person as IPerson } from "@nsdefs";
import {
calculateRespectGain,
@@ -47,12 +47,11 @@ import { calculateClassEarnings } from "../Work/Formulas";
import { calculateFactionExp, calculateFactionRep } from "../Work/Formulas";
import { defaultMultipliers } from "../PersonObjects/Multipliers";
import { findEnumMember } from "../utils/helpers/enum";
import { getEnumHelper } from "../utils/EnumHelper";
import { CompanyPositions } from "../Company/CompanyPositions";
import { findCrime } from "../Crime/CrimeHelpers";
import { Skills } from "../Bladeburner/data/Skills";
import type { PositiveNumber } from "../types";
import { Crimes } from "../Crime/Crimes";
import { calculateEffectiveSharedThreads, calculateShareBonus } from "../NetworkShare/Share";
export function NetscriptFormulas(): InternalAPI<IFormulas> {
@@ -395,38 +394,39 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
crimeSuccessChance: (ctx) => (_person, _crimeType) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const crime = findCrime(helpers.string(ctx, "crimeType", _crimeType));
if (!crime) throw new Error(`Invalid crime type: ${_crimeType}`);
const crime = Crimes[getEnumHelper("CrimeType").nsGetMember(ctx, _crimeType)];
if (!crime) {
throw new Error(`Invalid crime type: ${_crimeType}`);
}
return crime.successRate(person);
},
crimeGains: (ctx) => (_person, _crimeType) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const crime = findCrime(helpers.string(ctx, "crimeType", _crimeType));
if (!crime) throw new Error(`Invalid crime type: ${_crimeType}`);
const crime = Crimes[getEnumHelper("CrimeType").nsGetMember(ctx, _crimeType)];
if (!crime) {
throw new Error(`Invalid crime type: ${_crimeType}`);
}
return calculateCrimeWorkStats(person, crime);
},
gymGains: (ctx) => (_person, _classType, _locationName) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const classType = findEnumMember(GymType, helpers.string(ctx, "classType", _classType));
if (!classType) throw new Error(`Invalid gym training type: ${_classType}`);
const classType = getEnumHelper("GymType").nsGetMember(ctx, _classType);
const locationName = getEnumHelper("LocationName").nsGetMember(ctx, _locationName);
return calculateClassEarnings(person, classType, locationName);
},
universityGains: (ctx) => (_person, _classType, _locationName) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const classType = findEnumMember(UniversityClassType, helpers.string(ctx, "classType", _classType));
if (!classType) throw new Error(`Invalid university class type: ${_classType}`);
const classType = getEnumHelper("UniversityClassType").nsGetMember(ctx, _classType);
const locationName = getEnumHelper("LocationName").nsGetMember(ctx, _locationName);
return calculateClassEarnings(person, classType, locationName);
},
factionGains: (ctx) => (_player, _workType, _favor) => {
checkFormulasAccess(ctx);
const player = helpers.person(ctx, _player);
const workType = findEnumMember(FactionWorkType, helpers.string(ctx, "_workType", _workType));
if (!workType) throw new Error(`Invalid faction work type: ${_workType}`);
const workType = getEnumHelper("FactionWorkType").nsGetMember(ctx, _workType);
const favor = helpers.number(ctx, "favor", _favor);
const exp = calculateFactionExp(player, workType);
const rep = calculateFactionRep(player, workType, favor);

View File

@@ -1,7 +1,7 @@
import type { Singularity as ISingularity } from "@nsdefs";
import { Player } from "@player";
import { CityName, FactionWorkType, GymType, LocationName, UniversityClassType } from "@enums";
import { CityName, FactionWorkType, LocationName } from "@enums";
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
import { startWorkerScript } from "../NetscriptWorker";
import { Augmentations } from "../Augmentation/Augmentations";
@@ -9,7 +9,6 @@ import { getAugCost, installAugmentations } from "../Augmentation/AugmentationHe
import { CONSTANTS } from "../Constants";
import { RunningScript } from "../Script/RunningScript";
import { calculateAchievements } from "../Achievements/Achievements";
import { findCrime } from "../Crime/CrimeHelpers";
import { CompanyPositions } from "../Company/CompanyPositions";
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
import { Router } from "../ui/GameRoot";
@@ -40,7 +39,6 @@ import { CompanyWork } from "../Work/CompanyWork";
import { canGetBonus, onExport } from "../ExportBonus";
import { saveObject } from "../SaveObject";
import { calculateCrimeWorkStats } from "../Work/Formulas";
import { findEnumMember } from "../utils/helpers/enum";
import { Engine } from "../engine";
import { getEnumHelper } from "../utils/EnumHelper";
import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath";
@@ -53,6 +51,7 @@ import { addRepToFavor } from "../Faction/formulas/favor";
import { validBitNodes } from "../BitNode/BitNodeUtils";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { cat } from "../Terminal/commands/cat";
import { Crimes } from "../Crime/Crimes";
export function NetscriptSingularity(): InternalAPI<ISingularity> {
const runAfterReset = function (cbScript: ScriptFilePath) {
@@ -231,17 +230,13 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
(_universityName, _className, _focus = true) => {
helpers.checkSingularityAccess(ctx);
const universityName = helpers.string(ctx, "universityName", _universityName);
const classType = findEnumMember(UniversityClassType, helpers.string(ctx, "className", _className));
if (!classType) {
helpers.log(ctx, () => `Invalid class name: ${_className}.`);
return false;
}
const classType = getEnumHelper("UniversityClassType").nsGetMember(ctx, _className);
const focus = !!_focus;
const wasFocusing = Player.focus;
switch (universityName.toLowerCase()) {
case LocationName.AevumSummitUniversity.toLowerCase():
if (Player.city != CityName.Aevum) {
switch (universityName) {
case LocationName.AevumSummitUniversity:
if (Player.city !== CityName.Aevum) {
helpers.log(
ctx,
() => `You cannot study at 'Summit University' because you are not in '${CityName.Aevum}'.`,
@@ -250,8 +245,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
Player.gotoLocation(LocationName.AevumSummitUniversity);
break;
case LocationName.Sector12RothmanUniversity.toLowerCase():
if (Player.city != CityName.Sector12) {
case LocationName.Sector12RothmanUniversity:
if (Player.city !== CityName.Sector12) {
helpers.log(
ctx,
() => `You cannot study at 'Rothman University' because you are not in '${CityName.Sector12}'.`,
@@ -260,8 +255,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
Player.gotoLocation(LocationName.Sector12RothmanUniversity);
break;
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
if (Player.city != CityName.Volhaven) {
case LocationName.VolhavenZBInstituteOfTechnology:
if (Player.city !== CityName.Volhaven) {
helpers.log(
ctx,
() => `You cannot study at 'ZB Institute of Technology' because you are not in '${CityName.Volhaven}'.`,
@@ -298,17 +293,13 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
(_gymName, _stat, _focus = true) => {
helpers.checkSingularityAccess(ctx);
const gymName = helpers.string(ctx, "gymName", _gymName);
const classType = findEnumMember(GymType, helpers.string(ctx, "stat", _stat));
if (!classType) {
helpers.log(ctx, () => `Invalid stat: ${_stat}.`);
return false;
}
const classType = getEnumHelper("GymType").nsGetMember(ctx, _stat);
const focus = !!_focus;
const wasFocusing = Player.focus;
switch (gymName.toLowerCase()) {
case LocationName.AevumCrushFitnessGym.toLowerCase():
if (Player.city != CityName.Aevum) {
switch (gymName) {
case LocationName.AevumCrushFitnessGym:
if (Player.city !== CityName.Aevum) {
helpers.log(
ctx,
() =>
@@ -318,8 +309,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
Player.gotoLocation(LocationName.AevumCrushFitnessGym);
break;
case LocationName.AevumSnapFitnessGym.toLowerCase():
if (Player.city != CityName.Aevum) {
case LocationName.AevumSnapFitnessGym:
if (Player.city !== CityName.Aevum) {
helpers.log(
ctx,
() =>
@@ -329,8 +320,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
Player.gotoLocation(LocationName.AevumSnapFitnessGym);
break;
case LocationName.Sector12IronGym.toLowerCase():
if (Player.city != CityName.Sector12) {
case LocationName.Sector12IronGym:
if (Player.city !== CityName.Sector12) {
helpers.log(
ctx,
() =>
@@ -340,8 +331,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
Player.gotoLocation(LocationName.Sector12IronGym);
break;
case LocationName.Sector12PowerhouseGym.toLowerCase():
if (Player.city != CityName.Sector12) {
case LocationName.Sector12PowerhouseGym:
if (Player.city !== CityName.Sector12) {
helpers.log(
ctx,
() =>
@@ -351,8 +342,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
Player.gotoLocation(LocationName.Sector12PowerhouseGym);
break;
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
if (Player.city != CityName.Volhaven) {
case LocationName.VolhavenMilleniumFitnessGym:
if (Player.city !== CityName.Volhaven) {
helpers.log(
ctx,
() =>
@@ -719,7 +710,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
applyToCompany: (ctx) => (_companyName, _field) => {
helpers.checkSingularityAccess(ctx);
const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName);
const field = getEnumHelper("JobField").nsGetMember(ctx, _field, "field", { fuzzy: true });
const field = getEnumHelper("JobField").nsGetMember(ctx, _field, "field");
const company = Companies[companyName];
const entryPos = CompanyPositions[JobTracks[field][0]];
@@ -796,7 +787,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
(_facName, _type, _focus = true) => {
helpers.checkSingularityAccess(ctx);
const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
const type = helpers.string(ctx, "type", _type);
const type = getEnumHelper("FactionWorkType").nsGetMember(ctx, _type);
const focus = !!_focus;
const faction = Factions[facName];
@@ -813,10 +804,8 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
const wasFocusing = Player.focus;
switch (type.toLowerCase()) {
case "hacking":
case "hacking contracts":
case "hackingcontracts":
switch (type) {
case FactionWorkType.hacking:
if (!FactionInfos[faction.name].offerHackingWork) {
helpers.log(ctx, () => `Faction '${faction.name}' do not need help with hacking contracts.`);
return false;
@@ -837,9 +826,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
helpers.log(ctx, () => `Started carrying out hacking contracts for '${faction.name}'`);
return true;
case "field":
case "fieldwork":
case "field work":
case FactionWorkType.field:
if (!FactionInfos[faction.name].offerFieldWork) {
helpers.log(ctx, () => `Faction '${faction.name}' do not need help with field missions.`);
return false;
@@ -860,9 +847,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
}
helpers.log(ctx, () => `Started carrying out field missions for '${faction.name}'`);
return true;
case "security":
case "securitywork":
case "security work":
case FactionWorkType.security:
if (!FactionInfos[faction.name].offerSecurityWork) {
helpers.log(ctx, () => `Faction '${faction.name}' do not need help with security work.`);
return false;
@@ -1019,16 +1004,19 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
},
commitCrime: (ctx) => (_crimeType, _focus) => {
helpers.checkSingularityAccess(ctx);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const crimeType = getEnumHelper("CrimeType").nsGetMember(ctx, _crimeType);
const focus = _focus === undefined ? true : !!_focus;
const wasFocusing = Player.focus;
if (Player.currentWork !== null) Player.finishWork(true);
if (Player.currentWork !== null) {
Player.finishWork(true);
}
Player.gotoLocation(LocationName.Slums);
// If input isn't a crimeType, use search using roughname.
const crime = findCrime(crimeType);
if (crime == null) throw helpers.errorMessage(ctx, `Invalid crime: '${crimeType}'`);
const crime = Crimes[crimeType];
if (crime == null) {
throw helpers.errorMessage(ctx, `Invalid crime: '${crimeType}'`);
}
helpers.log(ctx, () => `Attempting to commit ${crime.type}...`);
const crimeTime = crime.commit(1, ctx.workerScript);
@@ -1043,21 +1031,23 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
},
getCrimeChance: (ctx) => (_crimeType) => {
helpers.checkSingularityAccess(ctx);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const crimeType = getEnumHelper("CrimeType").nsGetMember(ctx, _crimeType);
// If input isn't a crimeType, use search using roughname.
const crime = findCrime(crimeType);
if (crime == null) throw helpers.errorMessage(ctx, `Invalid crime: '${crimeType}'`);
const crime = Crimes[crimeType];
if (crime == null) {
throw helpers.errorMessage(ctx, `Invalid crime: '${crimeType}'`);
}
return crime.successRate(Player);
},
getCrimeStats: (ctx) => (_crimeType) => {
helpers.checkSingularityAccess(ctx);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const crimeType = getEnumHelper("CrimeType").nsGetMember(ctx, _crimeType);
// If input isn't a crimeType, use search using roughname.
const crime = findCrime(crimeType);
if (crime == null) throw helpers.errorMessage(ctx, `Invalid crime: '${crimeType}'`);
const crime = Crimes[crimeType];
if (crime == null) {
throw helpers.errorMessage(ctx, `Invalid crime: '${crimeType}'`);
}
const crimeStatsWithMultipliers = calculateCrimeWorkStats(Player, crime);

View File

@@ -3,9 +3,8 @@ import type { Sleeve as NetscriptSleeve } from "@nsdefs";
import type { ActionIdentifier } from "../Bladeburner/Types";
import { Player } from "@player";
import { BladeburnerActionType, type BladeburnerContractName } from "@enums";
import { BladeburnerActionType, SpecialBladeburnerActionTypeForSleeve, type BladeburnerContractName } from "@enums";
import { Augmentations } from "../Augmentation/Augmentations";
import { findCrime } from "../Crime/CrimeHelpers";
import { getEnumHelper } from "../utils/EnumHelper";
import { InternalAPI, NetscriptContext, setRemovedFunctions } from "../Netscript/APIWrapper";
import { isSleeveFactionWork } from "../PersonObjects/Sleeve/Work/SleeveFactionWork";
@@ -15,6 +14,7 @@ import { getAugCost } from "../Augmentation/AugmentationHelpers";
import { Factions } from "../Faction/Factions";
import { SleeveWorkType } from "../PersonObjects/Sleeve/Work/Work";
import { canAccessBitNodeFeature } from "../BitNode/BitNodeUtils";
import { Crimes } from "../Crime/Crimes";
export const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
/**
@@ -79,17 +79,17 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
},
setToCommitCrime: (ctx) => (_sleeveNumber, _crimeType) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const crimeType = getEnumHelper("CrimeType").nsGetMember(ctx, _crimeType);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const crime = findCrime(crimeType);
const crime = Crimes[crimeType];
if (crime == null) return false;
return Player.sleeves[sleeveNumber].commitCrime(crime.type);
},
setToUniversityCourse: (ctx) => (_sleeveNumber, _universityName, _className) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const universityName = helpers.string(ctx, "universityName", _universityName);
const className = helpers.string(ctx, "className", _className);
const className = getEnumHelper("UniversityClassType").nsGetMember(ctx, _className);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
return Player.sleeves[sleeveNumber].takeUniversityCourse(universityName, className);
@@ -130,7 +130,7 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
setToFactionWork: (ctx) => (_sleeveNumber, _factionName, _workType) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const factionName = getEnumHelper("FactionName").nsGetMember(ctx, _factionName);
const workType = helpers.string(ctx, "workType", _workType);
const workType = getEnumHelper("FactionWorkType").nsGetMember(ctx, _workType);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
@@ -164,7 +164,7 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
setToGymWorkout: (ctx) => (_sleeveNumber, _gymName, _stat) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const gymName = helpers.string(ctx, "gymName", _gymName);
const stat = helpers.string(ctx, "stat", _stat);
const stat = getEnumHelper("GymType").nsGetMember(ctx, _stat);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
@@ -267,7 +267,7 @@ export function NetscriptSleeve(): InternalAPI<NetscriptSleeve> {
return false;
}
let contract: BladeburnerContractName | undefined = undefined;
if (action === "Take on contracts") {
if (action === SpecialBladeburnerActionTypeForSleeve.TakeOnContracts) {
contract = getEnumHelper("BladeburnerContractName").nsGetMember(ctx, _contract);
for (let i = 0; i < Player.sleeves.length; ++i) {
if (i === sleeveNumber) {

View File

@@ -9,7 +9,7 @@ import {
StockMarketPromise,
} from "../StockMarket/StockMarket";
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
import { PositionType, OrderType, StockSymbol } from "@enums";
import { StockSymbol } from "@enums";
import {
getStockMarket4SDataCost,
getStockMarket4STixApiCost,
@@ -21,6 +21,7 @@ import { StockOrder, TIX } from "@nsdefs";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers";
import { StockMarketConstants } from "../StockMarket/data/Constants";
import { getEnumHelper } from "../utils/EnumHelper";
export function NetscriptStockMarket(): InternalAPI<TIX> {
/** Checks if the player has TIX API access. Throws an error if the player does not */
@@ -99,22 +100,12 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
getPurchaseCost: (ctx) => (_symbol, _shares, _posType) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
let shares = helpers.number(ctx, "shares", _shares);
const posType = helpers.string(ctx, "posType", _posType);
const posType = getEnumHelper("PositionType").nsGetMember(ctx, _posType);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
shares = Math.round(shares);
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionType.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionType.Short;
} else {
return Infinity;
}
const res = getBuyTransactionCost(stock, shares, pos);
const res = getBuyTransactionCost(stock, shares, posType);
if (res == null) {
return Infinity;
}
@@ -124,22 +115,12 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
getSaleGain: (ctx) => (_symbol, _shares, _posType) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
let shares = helpers.number(ctx, "shares", _shares);
const posType = helpers.string(ctx, "posType", _posType);
const posType = getEnumHelper("PositionType").nsGetMember(ctx, _posType);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
shares = Math.round(shares);
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionType.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionType.Short;
} else {
return 0;
}
const res = getSellTransactionGain(stock, shares, pos);
const res = getSellTransactionGain(stock, shares, posType);
if (res == null) {
return 0;
}
@@ -191,46 +172,22 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
const price = helpers.number(ctx, "price", _price);
const type = helpers.string(ctx, "type", _type);
const pos = helpers.string(ctx, "pos", _pos);
const type = getEnumHelper("OrderType").nsGetMember(ctx, _type);
const pos = getEnumHelper("PositionType").nsGetMember(ctx, _pos);
checkTixApiAccess(ctx);
if (Player.bitNodeN !== 8 && Player.activeSourceFileLvl(8) <= 2) {
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
}
const stock = getStockFromSymbol(ctx, symbol);
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderType.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderType.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderType.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderType.StopSell;
} else {
throw helpers.errorMessage(ctx, `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionType.Long;
} else if (lpos.includes("s")) {
orderPos = PositionType.Short;
} else {
throw helpers.errorMessage(ctx, `Invalid position type: ${pos}`);
}
return placeOrder(stock, shares, price, orderType, orderPos, ctx);
return placeOrder(stock, shares, price, type, pos, ctx);
},
cancelOrder: (ctx) => (_symbol, _shares, _price, _type, _pos) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
const price = helpers.number(ctx, "price", _price);
const type = helpers.string(ctx, "type", _type);
const pos = helpers.string(ctx, "pos", _pos);
const type = getEnumHelper("OrderType").nsGetMember(ctx, _type);
const pos = getEnumHelper("PositionType").nsGetMember(ctx, _pos);
checkTixApiAccess(ctx);
if (Player.bitNodeN !== 8 && Player.activeSourceFileLvl(8) <= 2) {
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or you must have Source-File 8 Level 3.");
@@ -239,37 +196,8 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
if (isNaN(shares) || isNaN(price)) {
throw helpers.errorMessage(ctx, `Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`);
}
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderType.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderType.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderType.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderType.StopSell;
} else {
throw helpers.errorMessage(ctx, `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionType.Long;
} else if (lpos.includes("s")) {
orderPos = PositionType.Short;
} else {
throw helpers.errorMessage(ctx, `Invalid position type: ${pos}`);
}
const params = {
stock: stock,
shares: shares,
price: price,
type: orderType,
pos: orderPos,
};
return cancelOrder(params, ctx);
return cancelOrder({ stock, shares, price, type, pos }, ctx);
},
getOrders: (ctx) => () => {
checkTixApiAccess(ctx);
@@ -277,7 +205,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
throw helpers.errorMessage(ctx, "You must either be in BitNode-8 or have Source-File 8 Level 3.");
}
const orders: StockOrder = {};
const orders: Record<string, StockOrder[]> = {};
const stockMarketOrders = StockMarket.Orders;
for (const symbol of Object.keys(stockMarketOrders)) {

View File

@@ -28,6 +28,7 @@ import {
BladeburnerActionType,
BladeburnerGeneralActionName,
AugmentationName,
SpecialBladeburnerActionTypeForSleeve,
} from "@enums";
import { Factions } from "../../Faction/Factions";
@@ -286,22 +287,22 @@ export class Sleeve extends Person implements SleevePerson {
}
/** Take a course at a university */
takeUniversityCourse(universityName: string, className: string): boolean {
takeUniversityCourse(universityName: string, className: UniversityClassType): boolean {
// Set exp/money multipliers based on which university.
// Also check that the sleeve is in the right city
let loc: LocationName | undefined;
switch (universityName.toLowerCase()) {
case LocationName.AevumSummitUniversity.toLowerCase(): {
switch (universityName) {
case LocationName.AevumSummitUniversity: {
if (this.city !== CityName.Aevum) return false;
loc = LocationName.AevumSummitUniversity;
break;
}
case LocationName.Sector12RothmanUniversity.toLowerCase(): {
case LocationName.Sector12RothmanUniversity: {
if (this.city !== CityName.Sector12) return false;
loc = LocationName.Sector12RothmanUniversity;
break;
}
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase(): {
case LocationName.VolhavenZBInstituteOfTechnology: {
if (this.city !== CityName.Volhaven) return false;
loc = LocationName.VolhavenZBInstituteOfTechnology;
break;
@@ -311,25 +312,23 @@ export class Sleeve extends Person implements SleevePerson {
// Set experience/money gains based on class
let classType: ClassType | undefined;
// TODO: why lower case??? It's not effecient, not typesafe and in general a bad idea
switch (className.toLowerCase()) {
case "study computer science": // deprecated, leave it here for backwards compatibility
case ClassType.computerScience.toLowerCase():
switch (className) {
case ClassType.computerScience:
classType = UniversityClassType.computerScience;
break;
case ClassType.dataStructures.toLowerCase():
case ClassType.dataStructures:
classType = UniversityClassType.dataStructures;
break;
case ClassType.networks.toLowerCase():
case ClassType.networks:
classType = UniversityClassType.networks;
break;
case ClassType.algorithms.toLowerCase():
case ClassType.algorithms:
classType = UniversityClassType.algorithms;
break;
case ClassType.management.toLowerCase():
case ClassType.management:
classType = UniversityClassType.management;
break;
case ClassType.leadership.toLowerCase():
case ClassType.leadership:
classType = UniversityClassType.leadership;
break;
}
@@ -376,17 +375,8 @@ export class Sleeve extends Person implements SleevePerson {
return true;
}
/** TODO 2.4: Make this take in type correct data */
workForFaction(factionName: FactionName, _workType: string): boolean {
const workTypeConversion: Record<string, string> = {
"Hacking Contracts": "hacking",
"Field Work": "field",
"Security Work": "security",
};
if (workTypeConversion[_workType]) _workType = workTypeConversion[_workType];
workForFaction(factionName: FactionName, workType: FactionWorkType): boolean {
const faction = Factions[factionName];
const workType = getEnumHelper("FactionWorkType").getMember(_workType, { fuzzy: true });
if (!workType) return false;
const factionInfo = faction.getInfo();
switch (workType) {
@@ -412,62 +402,41 @@ export class Sleeve extends Person implements SleevePerson {
}
/** Begin a gym workout task */
workoutAtGym(gymName: string, stat: string): boolean {
// Set exp/money multipliers based on which university.
// Also check that the sleeve is in the right city
workoutAtGym(gymName: string, stat: GymType): boolean {
// Check that the sleeve is in the right city
let loc: LocationName | undefined;
switch (gymName.toLowerCase()) {
case LocationName.AevumCrushFitnessGym.toLowerCase(): {
if (this.city != CityName.Aevum) return false;
switch (gymName) {
case LocationName.AevumCrushFitnessGym: {
if (this.city !== CityName.Aevum) return false;
loc = LocationName.AevumCrushFitnessGym;
break;
}
case LocationName.AevumSnapFitnessGym.toLowerCase(): {
if (this.city != CityName.Aevum) return false;
case LocationName.AevumSnapFitnessGym: {
if (this.city !== CityName.Aevum) return false;
loc = LocationName.AevumSnapFitnessGym;
break;
}
case LocationName.Sector12IronGym.toLowerCase(): {
if (this.city != CityName.Sector12) return false;
case LocationName.Sector12IronGym: {
if (this.city !== CityName.Sector12) return false;
loc = LocationName.Sector12IronGym;
break;
}
case LocationName.Sector12PowerhouseGym.toLowerCase(): {
if (this.city != CityName.Sector12) return false;
case LocationName.Sector12PowerhouseGym: {
if (this.city !== CityName.Sector12) return false;
loc = LocationName.Sector12PowerhouseGym;
break;
}
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase(): {
if (this.city != CityName.Volhaven) return false;
case LocationName.VolhavenMilleniumFitnessGym: {
if (this.city !== CityName.Volhaven) return false;
loc = LocationName.VolhavenMilleniumFitnessGym;
break;
}
}
if (!loc) return false;
// Set experience/money gains based on class
const sanitizedStat: string = stat.toLowerCase();
// set stat to a default value.
let classType: ClassType | undefined;
if (sanitizedStat.includes("str")) {
classType = GymType.strength;
}
if (sanitizedStat.includes("def")) {
classType = GymType.defense;
}
if (sanitizedStat.includes("dex")) {
classType = GymType.dexterity;
}
if (sanitizedStat.includes("agi")) {
classType = GymType.agility;
}
// if stat is still equals its default value, then validation has failed.
if (!classType) return false;
this.startWork(
new SleeveClassWork({
classType: classType,
classType: stat,
location: loc,
}),
);
@@ -479,50 +448,48 @@ export class Sleeve extends Person implements SleevePerson {
bladeburner(action: string, contract?: string): boolean {
if (!Player.bladeburner) return false;
switch (action) {
case "Training":
case BladeburnerGeneralActionName.Training:
this.startWork(
new SleeveBladeburnerWork({
actionId: { type: BladeburnerActionType.General, name: BladeburnerGeneralActionName.Training },
}),
);
return true;
case "Field analysis":
case "Field Analysis":
case BladeburnerGeneralActionName.FieldAnalysis:
this.startWork(
new SleeveBladeburnerWork({
actionId: { type: BladeburnerActionType.General, name: BladeburnerGeneralActionName.FieldAnalysis },
}),
);
return true;
case "Recruitment":
case BladeburnerGeneralActionName.Recruitment:
this.startWork(
new SleeveBladeburnerWork({
actionId: { type: BladeburnerActionType.General, name: BladeburnerGeneralActionName.Recruitment },
}),
);
return true;
case "Diplomacy":
case BladeburnerGeneralActionName.Diplomacy:
this.startWork(
new SleeveBladeburnerWork({
actionId: { type: BladeburnerActionType.General, name: BladeburnerGeneralActionName.Diplomacy },
}),
);
return true;
case "Hyperbolic Regeneration Chamber":
case BladeburnerGeneralActionName.HyperbolicRegen:
this.startWork(
new SleeveBladeburnerWork({
actionId: { type: BladeburnerActionType.General, name: BladeburnerGeneralActionName.HyperbolicRegen },
}),
);
return true;
case "Infiltrate synthoids":
case "Infiltrate Synthoids":
case SpecialBladeburnerActionTypeForSleeve.InfiltrateSynthoids:
this.startWork(new SleeveInfiltrateWork());
return true;
case "Support main sleeve":
case SpecialBladeburnerActionTypeForSleeve.SupportMainSleeve:
this.startWork(new SleeveSupportWork());
return true;
case "Take on contracts":
case SpecialBladeburnerActionTypeForSleeve.TakeOnContracts:
if (!getEnumHelper("BladeburnerContractName").isMember(contract)) return false;
this.startWork(
new SleeveBladeburnerWork({ actionId: { type: BladeburnerActionType.Contract, name: contract } }),

View File

@@ -8,7 +8,6 @@ import { Crime } from "../../../Crime/Crime";
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { CONSTANTS } from "../../../Constants";
import { calculateCrimeWorkStats } from "../../../Work/Formulas";
import { findCrime } from "../../../Crime/CrimeHelpers";
export const isSleeveCrimeWork = (w: SleeveWorkClass | null): w is SleeveCrimeWork =>
w !== null && w.type === SleeveWorkType.CRIME;
@@ -68,10 +67,12 @@ export class SleeveCrimeWork extends SleeveWorkClass {
return Generic_toJSON("SleeveCrimeWork", this);
}
/** Initializes a RecoveryWork object from a JSON save state. */
/** Initializes an object from a JSON save state. */
static fromJSON(value: IReviverValue): SleeveCrimeWork {
const crimeWork = Generic_fromJSON(SleeveCrimeWork, value.data);
crimeWork.crimeType = findCrime(crimeWork.crimeType)?.type ?? CrimeType.shoplift;
if (!(crimeWork.crimeType in Crimes)) {
crimeWork.crimeType = CrimeType.shoplift;
}
return crimeWork;
}
}

View File

@@ -1,6 +1,6 @@
import { Box, Button, Paper, Tooltip, Typography } from "@mui/material";
import React, { useEffect, useState } from "react";
import { BladeburnerActionType, CrimeType, FactionWorkType, GymType } from "@enums";
import { BladeburnerActionType, FactionWorkType, GymType, SpecialBladeburnerActionTypeForSleeve } from "@enums";
import { CONSTANTS } from "../../../Constants";
import { Player } from "@player";
import { formatPercent, formatInt } from "../../../ui/formatNumber";
@@ -11,9 +11,22 @@ import { SleeveAugmentationsModal } from "./SleeveAugmentationsModal";
import { EarningsElement, StatsElement } from "./StatsElement";
import { TaskSelector } from "./TaskSelector";
import { TravelModal } from "./TravelModal";
import { findCrime } from "../../../Crime/CrimeHelpers";
import { type SleeveWork, SleeveWorkType } from "../Work/Work";
import { getEnumHelper } from "../../../utils/EnumHelper";
import { getRecordEntries } from "../../../Types/Record";
const factionWorkTypeDescriptions = {
[FactionWorkType.field]: "Field Work",
[FactionWorkType.hacking]: "Hacking Contracts",
[FactionWorkType.security]: "Security Work",
};
const gymTypeDescriptions: Record<GymType, string> = {
[GymType.strength]: "Train Strength",
[GymType.defense]: "Train Defense",
[GymType.dexterity]: "Train Dexterity",
[GymType.agility]: "Train Agility",
};
function getWorkDescription(sleeve: Sleeve, progress: number): string {
const work = sleeve.currentWork;
@@ -57,14 +70,9 @@ function getWorkDescription(sleeve: Sleeve, progress: number): string {
);
}
case SleeveWorkType.FACTION: {
// This isn't the way this should be handled...
const workNames = {
[FactionWorkType.field]: "Field Work",
[FactionWorkType.hacking]: "Hacking Contracts",
[FactionWorkType.security]: "Security Work",
};
const doing = workNames[work.factionWorkType] ?? "nothing";
return `This sleeve is currently doing ${doing} for ${work.factionName}.`;
return `This sleeve is currently doing ${factionWorkTypeDescriptions[work.factionWorkType]} for ${
work.factionName
}.`;
}
case SleeveWorkType.INFILTRATE:
return (
@@ -82,36 +90,29 @@ function calculateABC(work: SleeveWork | null): [string, string, string] {
case SleeveWorkType.COMPANY:
return ["Work for Company", work.companyName, "------"];
case SleeveWorkType.FACTION: {
const workNames = {
[FactionWorkType.field]: "Field Work",
[FactionWorkType.hacking]: "Hacking Contracts",
[FactionWorkType.security]: "Security Work",
};
return ["Work for Faction", work.factionName, workNames[work.factionWorkType] ?? ""];
return ["Work for Faction", work.factionName, factionWorkTypeDescriptions[work.factionWorkType]];
}
case SleeveWorkType.BLADEBURNER:
if (work.actionId.type === BladeburnerActionType.Contract) {
return ["Perform Bladeburner Actions", "Take on contracts", work.actionId.name];
return [
"Perform Bladeburner Actions",
SpecialBladeburnerActionTypeForSleeve.TakeOnContracts,
work.actionId.name,
];
}
return ["Perform Bladeburner Actions", work.actionId.name, "------"];
case SleeveWorkType.CLASS: {
if (!work.isGym()) {
return ["Take University Course", work.classType, work.location];
}
const gymNames: Record<GymType, string> = {
[GymType.strength]: "Train Strength",
[GymType.defense]: "Train Defense",
[GymType.dexterity]: "Train Dexterity",
[GymType.agility]: "Train Agility",
};
return ["Workout at Gym", gymNames[work.classType as GymType], work.location];
return ["Workout at Gym", gymTypeDescriptions[work.classType as GymType], work.location];
}
case SleeveWorkType.CRIME:
return ["Commit Crime", getEnumHelper("CrimeType").getMember(work.crimeType, { alwaysMatch: true }), "------"];
case SleeveWorkType.SUPPORT:
return ["Perform Bladeburner Actions", "Support main sleeve", "------"];
return ["Perform Bladeburner Actions", SpecialBladeburnerActionTypeForSleeve.SupportMainSleeve, "------"];
case SleeveWorkType.INFILTRATE:
return ["Perform Bladeburner Actions", "Infiltrate Synthoids", "------"];
return ["Perform Bladeburner Actions", SpecialBladeburnerActionTypeForSleeve.InfiltrateSynthoids, "------"];
case SleeveWorkType.RECOVERY:
return ["Shock Recovery", "------", "------"];
case SleeveWorkType.SYNCHRO:
@@ -148,21 +149,41 @@ export function SleeveElem(props: SleeveElemProps): React.ReactElement {
props.sleeve.stopWork();
break;
case "Work for Company":
if (getEnumHelper("CompanyName").isMember(abc[1])) props.sleeve.workForCompany(abc[1]);
else console.error(`Invalid company name in setSleeveTask: ${abc[1]}`);
if (getEnumHelper("CompanyName").isMember(abc[1])) {
props.sleeve.workForCompany(abc[1]);
} else {
console.error(`Invalid company name in setSleeveTask: ${abc[1]}`);
}
break;
case "Work for Faction":
if (getEnumHelper("FactionName").isMember(abc[1])) props.sleeve.workForFaction(abc[1], abc[2]);
else console.error(`Invalid faction name in setSleeveTask: ${abc[1]}`);
if (getEnumHelper("FactionName").isMember(abc[1])) {
for (const [factionWorkType, description] of getRecordEntries(factionWorkTypeDescriptions)) {
if (description === abc[2]) {
props.sleeve.workForFaction(abc[1], factionWorkType);
break;
}
}
} else {
console.error(`Invalid faction name in setSleeveTask: ${abc[1]}`);
}
break;
case "Commit Crime":
props.sleeve.commitCrime(findCrime(abc[1])?.type ?? CrimeType.shoplift);
if (getEnumHelper("CrimeType").isMember(abc[1])) {
props.sleeve.commitCrime(abc[1]);
}
break;
case "Take University Course":
if (getEnumHelper("UniversityClassType").isMember(abc[1])) {
props.sleeve.takeUniversityCourse(abc[2], abc[1]);
}
break;
case "Workout at Gym":
props.sleeve.workoutAtGym(abc[2], abc[1]);
for (const [gymType, description] of getRecordEntries(gymTypeDescriptions)) {
if (description === abc[1]) {
props.sleeve.workoutAtGym(abc[2], gymType);
break;
}
}
break;
case "Perform Bladeburner Actions":
props.sleeve.bladeburner(abc[1], abc[2]);

View File

@@ -4,33 +4,35 @@ import React from "react";
import { MenuItem, Select, SelectChangeEvent } from "@mui/material";
import { Player } from "@player";
import { BladeburnerActionType, BladeburnerContractName, CityName, FactionName, LocationName } from "@enums";
import {
BladeburnerActionType,
BladeburnerContractName,
BladeburnerGeneralActionName,
CityName,
FactionName,
LocationName,
SpecialBladeburnerActionTypeForSleeve,
UniversityClassType,
} from "@enums";
import { Crimes } from "../../../Crime/Crimes";
import { Factions } from "../../../Faction/Factions";
import { getEnumHelper } from "../../../utils/EnumHelper";
import { SleeveWorkType } from "../Work/Work";
import { getRecordKeys } from "../../../Types/Record";
const universitySelectorOptions: string[] = [
"Computer Science",
"Data Structures",
"Networks",
"Algorithms",
"Management",
"Leadership",
];
const universitySelectorOptions = Object.values(UniversityClassType);
const gymSelectorOptions: string[] = ["Train Strength", "Train Defense", "Train Dexterity", "Train Agility"];
const bladeburnerSelectorOptions: string[] = [
"Training",
"Field Analysis",
"Recruitment",
"Diplomacy",
"Hyperbolic Regeneration Chamber",
"Infiltrate Synthoids",
"Support main sleeve",
"Take on contracts",
BladeburnerGeneralActionName.Training,
BladeburnerGeneralActionName.FieldAnalysis,
BladeburnerGeneralActionName.Recruitment,
BladeburnerGeneralActionName.Diplomacy,
BladeburnerGeneralActionName.HyperbolicRegen,
SpecialBladeburnerActionTypeForSleeve.InfiltrateSynthoids,
SpecialBladeburnerActionTypeForSleeve.SupportMainSleeve,
SpecialBladeburnerActionTypeForSleeve.TakeOnContracts,
];
interface IProps {
@@ -201,7 +203,7 @@ const tasks: {
return {
first: bladeburnerSelectorOptions,
second: (s1: string) => {
if (s1 === "Take on contracts") {
if (s1 === SpecialBladeburnerActionTypeForSleeve.TakeOnContracts) {
return possibleContracts(sleeve);
} else {
return ["------"];

View File

@@ -430,10 +430,9 @@ type OrderEnumType = {
type OrderType = _ValueOf<OrderEnumType>;
/**
* Value in map of {@link StockOrder}
* @public
*/
interface StockOrderObject {
interface StockOrder {
/** Number of shares */
shares: number;
/** Price per share */
@@ -444,16 +443,6 @@ interface StockOrderObject {
position: PositionType;
}
/**
* Return value of {@link TIX.getOrders | getOrders}
*
* Keys are stock symbols, properties are arrays of {@link StockOrderObject}
* @public
*/
interface StockOrder {
[key: string]: StockOrderObject[];
}
/** Constants used for the stock market game mechanic.
* @public */
interface StockMarketConstants {
@@ -1381,10 +1370,10 @@ export interface TIX {
*
* @param sym - Stock symbol.
* @param shares - Number of shares to purchase.
* @param posType - Specifies whether the order is a Long or Short position.
* @param positionType - Specifies whether the order is a Long ("L") or Short ("S") position.
* @returns Cost to buy a given number of shares of a stock.
*/
getPurchaseCost(sym: string, shares: number, posType: string): number;
getPurchaseCost(sym: string, shares: number, positionType: PositionType): number;
/**
* Calculate profit of selling stocks.
@@ -1395,10 +1384,10 @@ export interface TIX {
*
* @param sym - Stock symbol.
* @param shares - Number of shares to sell.
* @param posType - Specifies whether the order is a Long or Short position.
* @param positionType - Specifies whether the order is a Long ("L") or Short ("S") position.
* @returns Gain from selling a given number of shares of a stock.
*/
getSaleGain(sym: string, shares: number, posType: string): number;
getSaleGain(sym: string, shares: number, positionType: PositionType): number;
/**
* Buy stocks.
@@ -1501,11 +1490,11 @@ export interface TIX {
* @param sym - Stock symbol.
* @param shares - Number of shares for order. Must be positive. Will be rounded to the nearest integer.
* @param price - Execution price for the order.
* @param type - Type of order.
* @param pos - Specifies whether the order is a Long or Short position.
* @param orderType - Type of order.
* @param positionType - Specifies whether the order is a Long ("L") or Short ("S") position.
* @returns True if the order is successfully placed, and false otherwise.
*/
placeOrder(sym: string, shares: number, price: number, type: string, pos: string): boolean;
placeOrder(sym: string, shares: number, price: number, orderType: OrderType, positionType: PositionType): boolean;
/**
* Cancel order for stocks.
@@ -1519,10 +1508,10 @@ export interface TIX {
* @param sym - Stock symbol.
* @param shares - Number of shares for order. Must be positive. Will be rounded to the nearest integer.
* @param price - Execution price for the order.
* @param type - Type of order.
* @param pos - Specifies whether the order is a Long or Short position.
* @param orderType - Type of order.
* @param positionType - Specifies whether the order is a Long ("L") or Short ("S") position.
*/
cancelOrder(sym: string, shares: number, price: number, type: string, pos: string): void;
cancelOrder(sym: string, shares: number, price: number, orderType: OrderType, positionType: PositionType): void;
/**
* Returns your order book for the stock market.
@@ -1530,7 +1519,7 @@ export interface TIX {
* RAM cost: 2.5 GB
* This is an object containing information for all the Limit and Stop Orders you have in the stock market.
* For each symbol you have a position in, the returned object will have a key with that symbol's name.
* The object's properties are each an array of {@link StockOrderObject}
* The object's properties are each an array of {@link StockOrder}
* The object has the following structure:
*
* ```js
@@ -1585,9 +1574,10 @@ export interface TIX {
* ],
* }
* ```
* @returns Object containing information for all the Limit and Stop Orders you have in the stock market.
* @returns Object containing information for all the Limit and Stop Orders you have in the stock market. Keys are
* stock symbols, and properties are arrays of {@link StockOrder}
*/
getOrders(): StockOrder;
getOrders(): Record<string, StockOrder[]>;
/**
* Returns the volatility of the specified stock.