WIP: Crimes streamlining. (#138)

* streamline crimes

* Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType.
* Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>.
* Added slums tooltip text as a crime property, to allow streamlining slums.
* Refactor slums location - removed repetitive code, rerenders 1/sec to update chances
* Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs").
* Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None.

* Add CrimeType enum in NetscriptDefinition.d.ts

* Also update broken ToastVariant type. Better support for enums in player scripts.
* Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts)

* Expect enum use for crime functions

Affected functions:
* ns.singularity.commitCrime
* ns.singularity.getCrimeChance
* ns.singularity.getCrimeStats
* ns.sleeve.setToCommitCrime
* formulas.work.crimeGains (param type only)

- Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members.
- Some documentation changes:
  * examples updated to use uppercase expected form.
  * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input).
  * note about needing to use isBusy to schedule crimes remove - crimes autoloop now.
  * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
This commit is contained in:
Snarling 2022-10-21 11:57:37 -04:00 committed by GitHub
parent 06a985bdf8
commit d74c380e42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 412 additions and 512 deletions

@ -25,22 +25,25 @@ interface IConstructorParams {
export class Crime {
// Number representing the difficulty of the crime. Used for success chance calculations
difficulty = 0;
difficulty: number;
// Amount of karma lost for SUCCESSFULLY committing this crime
karma = 0;
karma: number;
// How many people die as a result of this crime
kills = 0;
kills: number;
// How much money is given by the
money = 0;
money: number;
// Name of crime
name = "";
name: string;
// Name of crime as it appears on work screen: "You are attempting..."
workName = "";
workName: string;
// Tooltip text in slums ui
tooltipText: string;
// Milliseconds it takes to attempt the crime
time = 0;
@ -66,17 +69,19 @@ export class Crime {
intelligence_exp = 0;
constructor(
name = "",
workName = "",
name: string,
workName: string,
tooltipText: string,
type: CrimeType,
time = 0,
money = 0,
difficulty = 0,
karma = 0,
params: IConstructorParams = {},
time: number,
money: number,
difficulty: number,
karma: number,
params: IConstructorParams,
) {
this.name = name;
this.workName = workName;
this.tooltipText = tooltipText;
this.type = type;
this.time = time;
this.money = money;

@ -3,53 +3,46 @@ import { Crime } from "./Crime";
import { Player } from "@player";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { checkEnum } from "../utils/helpers/checkEnum";
import { CrimeType } from "../utils/WorkType";
//This is only used for the player
export function determineCrimeSuccess(type: string): boolean {
let chance = 0;
let found = false;
for (const i of Object.keys(Crimes)) {
const crime = Crimes[i];
if (crime.type === type) {
chance = crime.successRate(Player);
found = true;
break;
}
}
if (!found) {
if (!checkEnum(CrimeType, type)) {
dialogBoxCreate(`ERR: Unrecognized crime type: ${type} This is probably a bug please contact the developer`);
return false;
}
const crime = Crimes[type];
const chance = crime.successRate(Player);
return Math.random() <= chance;
}
export function findCrime(roughName: string): Crime | null {
roughName = roughName.toLowerCase();
if (roughName.includes("shoplift")) {
return Crimes.Shoplift;
return Crimes[CrimeType.SHOPLIFT];
} else if (roughName.includes("rob") && roughName.includes("store")) {
return Crimes.RobStore;
return Crimes[CrimeType.ROB_STORE];
} else if (roughName.includes("mug")) {
return Crimes.Mug;
return Crimes[CrimeType.MUG];
} else if (roughName.includes("larceny")) {
return Crimes.Larceny;
return Crimes[CrimeType.LARCENY];
} else if (roughName.includes("drugs")) {
return Crimes.DealDrugs;
return Crimes[CrimeType.DRUGS];
} else if (roughName.includes("bond") && roughName.includes("forge")) {
return Crimes.BondForgery;
return Crimes[CrimeType.BOND_FORGERY];
} else if ((roughName.includes("traffic") || roughName.includes("illegal")) && roughName.includes("arms")) {
return Crimes.TraffickArms;
return Crimes[CrimeType.TRAFFIC_ARMS];
} else if (roughName.includes("homicide")) {
return Crimes.Homicide;
return Crimes[CrimeType.HOMICIDE];
} else if (roughName.includes("grand") && roughName.includes("auto")) {
return Crimes.GrandTheftAuto;
return Crimes[CrimeType.GRAND_THEFT_AUTO];
} else if (roughName.includes("kidnap")) {
return Crimes.Kidnap;
return Crimes[CrimeType.KIDNAP];
} else if (roughName.includes("assassin")) {
return Crimes.Assassination;
return Crimes[CrimeType.ASSASSINATION];
} else if (roughName.includes("heist")) {
return Crimes.Heist;
return Crimes[CrimeType.HEIST];
}
return null;

@ -3,162 +3,282 @@ import { Crime } from "./Crime";
import { CONSTANTS } from "../Constants";
import { CrimeType } from "../utils/WorkType";
// TODO: What is the point of CrimeType using totally different strings than
export const Crimes: Record<CrimeType, Crime> = {
[CrimeType.SHOPLIFT]: new Crime(
"Shoplift",
"to shoplift",
"Attempt to shoplift from a low-end retailer",
CrimeType.SHOPLIFT,
2e3,
15e3,
1 / 20,
0.1,
{
dexterity_success_weight: 1,
agility_success_weight: 1,
export const Crimes: Record<string, Crime> = {
Shoplift: new Crime("Shoplift", "to shoplift", CrimeType.SHOPLIFT, 2e3, 15e3, 1 / 20, 0.1, {
dexterity_success_weight: 1,
agility_success_weight: 1,
dexterity_exp: 2,
agility_exp: 2,
},
),
dexterity_exp: 2,
agility_exp: 2,
}),
[CrimeType.ROB_STORE]: new Crime(
"Rob Store",
"to rob a store",
"Attempt to commit armed robbery on a high-end store",
CrimeType.ROB_STORE,
60e3,
400e3,
1 / 5,
0.5,
{
hacking_exp: 30,
dexterity_exp: 45,
agility_exp: 45,
RobStore: new Crime("Rob Store", "to rob a store", CrimeType.ROB_STORE, 60e3, 400e3, 1 / 5, 0.5, {
hacking_exp: 30,
dexterity_exp: 45,
agility_exp: 45,
hacking_success_weight: 0.5,
dexterity_success_weight: 2,
agility_success_weight: 1,
hacking_success_weight: 0.5,
dexterity_success_weight: 2,
agility_success_weight: 1,
intelligence_exp: 7.5 * CONSTANTS.IntelligenceCrimeBaseExpGain,
},
),
intelligence_exp: 7.5 * CONSTANTS.IntelligenceCrimeBaseExpGain,
}),
[CrimeType.MUG]: new Crime(
"Mug",
"to mug",
"Attempt to mug a random person on the street",
CrimeType.MUG,
4e3,
36e3,
1 / 5,
0.25,
{
strength_exp: 3,
defense_exp: 3,
dexterity_exp: 3,
agility_exp: 3,
Mug: new Crime("Mug", "to mug", CrimeType.MUG, 4e3, 36e3, 1 / 5, 0.25, {
strength_exp: 3,
defense_exp: 3,
dexterity_exp: 3,
agility_exp: 3,
strength_success_weight: 1.5,
defense_success_weight: 0.5,
dexterity_success_weight: 1.5,
agility_success_weight: 0.5,
},
),
strength_success_weight: 1.5,
defense_success_weight: 0.5,
dexterity_success_weight: 1.5,
agility_success_weight: 0.5,
}),
[CrimeType.LARCENY]: new Crime(
"Larceny",
"larceny",
"Attempt to rob property from someone's house",
CrimeType.LARCENY,
90e3,
800e3,
1 / 3,
1.5,
{
hacking_exp: 45,
dexterity_exp: 60,
agility_exp: 60,
Larceny: new Crime("Larceny", "larceny", CrimeType.LARCENY, 90e3, 800e3, 1 / 3, 1.5, {
hacking_exp: 45,
dexterity_exp: 60,
agility_exp: 60,
hacking_success_weight: 0.5,
dexterity_success_weight: 1,
agility_success_weight: 1,
hacking_success_weight: 0.5,
dexterity_success_weight: 1,
agility_success_weight: 1,
intelligence_exp: 15 * CONSTANTS.IntelligenceCrimeBaseExpGain,
},
),
intelligence_exp: 15 * CONSTANTS.IntelligenceCrimeBaseExpGain,
}),
[CrimeType.DRUGS]: new Crime(
"Deal Drugs",
"to deal drugs",
"Attempt to deal drugs",
CrimeType.DRUGS,
10e3,
120e3,
1,
0.5,
{
dexterity_exp: 5,
agility_exp: 5,
charisma_exp: 10,
DealDrugs: new Crime("Deal Drugs", "to deal drugs", CrimeType.DRUGS, 10e3, 120e3, 1, 0.5, {
dexterity_exp: 5,
agility_exp: 5,
charisma_exp: 10,
charisma_success_weight: 3,
dexterity_success_weight: 2,
agility_success_weight: 1,
},
),
charisma_success_weight: 3,
dexterity_success_weight: 2,
agility_success_weight: 1,
}),
[CrimeType.BOND_FORGERY]: new Crime(
"Bond Forgery",
"to forge bonds",
"Attempt to forge corporate bonds",
CrimeType.BOND_FORGERY,
300e3,
4.5e6,
1 / 2,
0.1,
{
hacking_exp: 100,
dexterity_exp: 150,
charisma_exp: 15,
BondForgery: new Crime("Bond Forgery", "to forge bonds", CrimeType.BOND_FORGERY, 300e3, 4.5e6, 1 / 2, 0.1, {
hacking_exp: 100,
dexterity_exp: 150,
charisma_exp: 15,
hacking_success_weight: 0.05,
dexterity_success_weight: 1.25,
hacking_success_weight: 0.05,
dexterity_success_weight: 1.25,
intelligence_exp: 60 * CONSTANTS.IntelligenceCrimeBaseExpGain,
},
),
intelligence_exp: 60 * CONSTANTS.IntelligenceCrimeBaseExpGain,
}),
[CrimeType.TRAFFIC_ARMS]: new Crime(
"Traffick Arms",
"to traffic arms",
"Attempt to smuggle illegal arms into the city",
CrimeType.TRAFFIC_ARMS,
40e3,
600e3,
2,
1,
{
strength_exp: 20,
defense_exp: 20,
dexterity_exp: 20,
agility_exp: 20,
charisma_exp: 40,
TraffickArms: new Crime("Traffick Arms", "to traffic arms", CrimeType.TRAFFIC_ARMS, 40e3, 600e3, 2, 1, {
strength_exp: 20,
defense_exp: 20,
dexterity_exp: 20,
agility_exp: 20,
charisma_exp: 40,
charisma_success_weight: 1,
strength_success_weight: 1,
defense_success_weight: 1,
dexterity_success_weight: 1,
agility_success_weight: 1,
},
),
charisma_success_weight: 1,
strength_success_weight: 1,
defense_success_weight: 1,
dexterity_success_weight: 1,
agility_success_weight: 1,
}),
[CrimeType.HOMICIDE]: new Crime(
"Homicide",
"homicide",
"Attempt to murder a random person on the street",
CrimeType.HOMICIDE,
3e3,
45e3,
1,
3,
{
strength_exp: 2,
defense_exp: 2,
dexterity_exp: 2,
agility_exp: 2,
Homicide: new Crime("Homicide", "homicide", CrimeType.HOMICIDE, 3e3, 45e3, 1, 3, {
strength_exp: 2,
defense_exp: 2,
dexterity_exp: 2,
agility_exp: 2,
strength_success_weight: 2,
defense_success_weight: 2,
dexterity_success_weight: 0.5,
agility_success_weight: 0.5,
strength_success_weight: 2,
defense_success_weight: 2,
dexterity_success_weight: 0.5,
agility_success_weight: 0.5,
kills: 1,
},
),
kills: 1,
}),
[CrimeType.GRAND_THEFT_AUTO]: new Crime(
"Grand Theft Auto",
"grand theft auto",
"Attempt to commit grand theft auto",
CrimeType.GRAND_THEFT_AUTO,
80e3,
1.6e6,
8,
5,
{
strength_exp: 20,
defense_exp: 20,
dexterity_exp: 20,
agility_exp: 80,
charisma_exp: 40,
GrandTheftAuto: new Crime("Grand Theft Auto", "grand theft auto", CrimeType.GRAND_THEFT_AUTO, 80e3, 1.6e6, 8, 5, {
strength_exp: 20,
defense_exp: 20,
dexterity_exp: 20,
agility_exp: 80,
charisma_exp: 40,
hacking_success_weight: 1,
strength_success_weight: 1,
dexterity_success_weight: 4,
agility_success_weight: 2,
charisma_success_weight: 2,
hacking_success_weight: 1,
strength_success_weight: 1,
dexterity_success_weight: 4,
agility_success_weight: 2,
charisma_success_weight: 2,
intelligence_exp: 16 * CONSTANTS.IntelligenceCrimeBaseExpGain,
},
),
intelligence_exp: 16 * CONSTANTS.IntelligenceCrimeBaseExpGain,
}),
[CrimeType.KIDNAP]: new Crime(
"Kidnap",
"to kidnap",
"Attempt to kidnap and ransom a high-profile-target",
CrimeType.KIDNAP,
120e3,
3.6e6,
5,
6,
{
strength_exp: 80,
defense_exp: 80,
dexterity_exp: 80,
agility_exp: 80,
charisma_exp: 80,
Kidnap: new Crime("Kidnap", "to kidnap", CrimeType.KIDNAP, 120e3, 3.6e6, 5, 6, {
strength_exp: 80,
defense_exp: 80,
dexterity_exp: 80,
agility_exp: 80,
charisma_exp: 80,
charisma_success_weight: 1,
strength_success_weight: 1,
dexterity_success_weight: 1,
agility_success_weight: 1,
charisma_success_weight: 1,
strength_success_weight: 1,
dexterity_success_weight: 1,
agility_success_weight: 1,
intelligence_exp: 26 * CONSTANTS.IntelligenceCrimeBaseExpGain,
},
),
intelligence_exp: 26 * CONSTANTS.IntelligenceCrimeBaseExpGain,
}),
[CrimeType.ASSASSINATION]: new Crime(
"Assassination",
"to assassinate",
"Attempt to assassinate a high-profile target",
CrimeType.ASSASSINATION,
300e3,
12e6,
8,
10,
{
strength_exp: 300,
defense_exp: 300,
dexterity_exp: 300,
agility_exp: 300,
Assassination: new Crime("Assassination", "to assassinate", CrimeType.ASSASSINATION, 300e3, 12e6, 8, 10, {
strength_exp: 300,
defense_exp: 300,
dexterity_exp: 300,
agility_exp: 300,
strength_success_weight: 1,
dexterity_success_weight: 2,
agility_success_weight: 1,
strength_success_weight: 1,
dexterity_success_weight: 2,
agility_success_weight: 1,
intelligence_exp: 65 * CONSTANTS.IntelligenceCrimeBaseExpGain,
intelligence_exp: 65 * CONSTANTS.IntelligenceCrimeBaseExpGain,
kills: 1,
},
),
kills: 1,
}),
[CrimeType.HEIST]: new Crime(
"Heist",
"a heist",
"Attempt to pull off the ultimate heist",
CrimeType.HEIST,
600e3,
120e6,
18,
15,
{
hacking_exp: 450,
strength_exp: 450,
defense_exp: 450,
dexterity_exp: 450,
agility_exp: 450,
charisma_exp: 450,
Heist: new Crime("Heist", "a heist", CrimeType.HEIST, 600e3, 120e6, 18, 15, {
hacking_exp: 450,
strength_exp: 450,
defense_exp: 450,
dexterity_exp: 450,
agility_exp: 450,
charisma_exp: 450,
hacking_success_weight: 1,
strength_success_weight: 1,
defense_success_weight: 1,
dexterity_success_weight: 1,
agility_success_weight: 1,
charisma_success_weight: 1,
hacking_success_weight: 1,
strength_success_weight: 1,
defense_success_weight: 1,
dexterity_success_weight: 1,
agility_success_weight: 1,
charisma_success_weight: 1,
intelligence_exp: 130 * CONSTANTS.IntelligenceCrimeBaseExpGain,
}),
intelligence_exp: 130 * CONSTANTS.IntelligenceCrimeBaseExpGain,
},
),
};

@ -3,7 +3,7 @@
*
* This subcomponent renders all of the buttons for committing crimes
*/
import * as React from "react";
import React, { useState, useEffect } from "react";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
@ -13,185 +13,33 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { Router } from "../../ui/GameRoot";
import { Player } from "@player";
import { Box } from "@mui/material";
import { Crime } from "../../Crime/Crime";
export function SlumsLocation(): React.ReactElement {
function shoplift(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.Shoplift.commit();
const setRerender = useState(false)[1];
const rerender = () => setRerender((o) => !o);
const crimes = Object.values(Crimes);
useEffect(() => {
const timerId = setInterval(() => rerender(), 1000);
return () => clearInterval(timerId);
});
function doCrime(e: React.MouseEvent<HTMLElement>, crime: Crime) {
if (!e.isTrusted) return;
crime.commit();
Router.toWork();
Player.focus = true;
}
function robStore(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.RobStore.commit();
Router.toWork();
Player.focus = true;
}
function mug(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.Mug.commit();
Router.toWork();
Player.focus = true;
}
function larceny(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.Larceny.commit();
Router.toWork();
Player.focus = true;
}
function dealDrugs(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.DealDrugs.commit();
Router.toWork();
Player.focus = true;
}
function bondForgery(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.BondForgery.commit();
Router.toWork();
Player.focus = true;
}
function traffickArms(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.TraffickArms.commit();
Router.toWork();
Player.focus = true;
}
function homicide(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.Homicide.commit();
Router.toWork();
Player.focus = true;
}
function grandTheftAuto(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.GrandTheftAuto.commit();
Router.toWork();
Player.focus = true;
}
function kidnap(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.Kidnap.commit();
Router.toWork();
Player.focus = true;
}
function assassinate(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.Assassination.commit();
Router.toWork();
Player.focus = true;
}
function heist(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) {
return;
}
Crimes.Heist.commit();
Router.toWork();
Player.focus = true;
}
const shopliftChance = Crimes.Shoplift.successRate(Player);
const robStoreChance = Crimes.RobStore.successRate(Player);
const mugChance = Crimes.Mug.successRate(Player);
const larcenyChance = Crimes.Larceny.successRate(Player);
const drugsChance = Crimes.DealDrugs.successRate(Player);
const bondChance = Crimes.BondForgery.successRate(Player);
const armsChance = Crimes.TraffickArms.successRate(Player);
const homicideChance = Crimes.Homicide.successRate(Player);
const gtaChance = Crimes.GrandTheftAuto.successRate(Player);
const kidnapChance = Crimes.Kidnap.successRate(Player);
const assassinateChance = Crimes.Assassination.successRate(Player);
const heistChance = Crimes.Heist.successRate(Player);
return (
<Box sx={{ display: "grid", width: "fit-content" }}>
<Tooltip title={<>Attempt to shoplift from a low-end retailer</>}>
<Button onClick={shoplift}>
Shoplift ({numeralWrapper.formatPercentage(shopliftChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to commit armed robbery on a high-end store</>}>
<Button onClick={robStore}>
Rob store ({numeralWrapper.formatPercentage(robStoreChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to mug a random person on the street</>}>
<Button onClick={mug}>Mug someone ({numeralWrapper.formatPercentage(mugChance)} chance of success)</Button>
</Tooltip>
<Tooltip title={<>Attempt to rob property from someone's house</>}>
<Button onClick={larceny}>Larceny ({numeralWrapper.formatPercentage(larcenyChance)} chance of success)</Button>
</Tooltip>
<Tooltip title={<>Attempt to deal drugs</>}>
<Button onClick={dealDrugs}>
Deal Drugs ({numeralWrapper.formatPercentage(drugsChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to forge corporate bonds</>}>
<Button onClick={bondForgery}>
Bond Forgery ({numeralWrapper.formatPercentage(bondChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to smuggle illegal arms into the city</>}>
<Button onClick={traffickArms}>
Traffick illegal Arms ({numeralWrapper.formatPercentage(armsChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to murder a random person on the street</>}>
<Button onClick={homicide}>
Homicide ({numeralWrapper.formatPercentage(homicideChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to commit grand theft auto</>}>
<Button onClick={grandTheftAuto}>
Grand theft Auto ({numeralWrapper.formatPercentage(gtaChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to kidnap and ransom a high-profile-target</>}>
<Button onClick={kidnap}>
Kidnap and Ransom ({numeralWrapper.formatPercentage(kidnapChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to assassinate a high-profile target</>}>
<Button onClick={assassinate}>
Assassinate ({numeralWrapper.formatPercentage(assassinateChance)} chance of success)
</Button>
</Tooltip>
<Tooltip title={<>Attempt to pull off the ultimate heist</>}>
<Button onClick={heist}>Heist ({numeralWrapper.formatPercentage(heistChance)} chance of success)</Button>
</Tooltip>
{crimes.map((crime) => (
<Tooltip title={crime.tooltipText}>
<Button onClick={(e) => doCrime(e, crime)}>
{crime.name} ({numeralWrapper.formatPercentage(crime.successRate(Player))} chance of success)
</Button>
</Tooltip>
))}
</Box>
);
}

@ -77,11 +77,12 @@ import { INetscriptExtra } from "./NetscriptFunctions/Extra";
import { ScriptDeath } from "./Netscript/ScriptDeath";
import { getBitNodeMultipliers } from "./BitNode/BitNode";
import { assert, arrayAssert, stringAssert, objectAssert } from "./utils/helpers/typeAssertion";
import { CrimeType } from "./utils/WorkType";
// "Enums" as object
export const enums = {
toast: ToastVariant,
} as const;
crimes: CrimeType,
};
export type NSFull = Readonly<NS & INetscriptExtra>;
export function NetscriptFunctions(workerScript: WorkerScript): NSFull {
@ -125,6 +126,7 @@ const base: InternalAPI<NS> = {
(ctx) =>
(_hostname, opts = {}) => {
const hostname = helpers.string(ctx, "hostname", _hostname);
// Todo: better type safety rework for functions using assertObjectType, then remove function.
const optsValidator: BasicHGWOptions = {};
assertObjectType(ctx, "opts", opts, optsValidator);
return helpers.hack(ctx, hostname, false, { threads: opts.threads, stock: opts.stock });

@ -48,6 +48,8 @@ import { calculateFactionExp, calculateFactionRep } from "../Work/formulas/Facti
import { FactionWorkType } from "../Work/data/FactionWorkType";
import { defaultMultipliers } from "../PersonObjects/Multipliers";
import { checkEnum } from "../utils/helpers/checkEnum";
import { CrimeType } from "../utils/WorkType";
export function NetscriptFormulas(): InternalAPI<IFormulas> {
const checkFormulasAccess = function (ctx: NetscriptContext): void {
@ -362,9 +364,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
work: {
crimeGains: (ctx) => (_crimeType) => {
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const crime = Object.values(Crimes).find((c) => String(c.type) === crimeType);
if (!crime) throw new Error(`Invalid crime type: ${crimeType}`);
return calculateCrimeWorkStats(crime);
if (!checkEnum(CrimeType, crimeType)) throw new Error(`Invalid crime type: ${crimeType}`);
return calculateCrimeWorkStats(Crimes[crimeType]);
},
classGains: (ctx) => (_person, _classType, _locationName) => {
const person = helpers.player(ctx, _person);

@ -50,6 +50,9 @@ import { CompanyWork } from "../Work/CompanyWork";
import { canGetBonus, onExport } from "../ExportBonus";
import { saveObject } from "../SaveObject";
import { calculateCrimeWorkStats } from "../Work/formulas/Crime";
import { checkEnum } from "../utils/helpers/checkEnum";
import { Crimes } from "../Crime/Crimes";
import { CrimeType } from "../utils/WorkType";
export function NetscriptSingularity(): InternalAPI<ISingularity> {
const getAugmentation = function (ctx: NetscriptContext, name: string): Augmentation {
@ -1115,56 +1118,47 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
helpers.log(ctx, () => `Began creating program: '${programName}'`);
return true;
},
commitCrime:
(ctx) =>
(_crimeRoughName, _focus = true) => {
helpers.checkSingularityAccess(ctx);
const crimeRoughName = helpers.string(ctx, "crimeRoughName", _crimeRoughName);
const focus = !!_focus;
const wasFocusing = Player.focus;
if (Player.currentWork !== null) {
Player.finishWork(true);
}
// Set Location to slums
Player.gotoLocation(LocationName.Slums);
const crime = findCrime(crimeRoughName.toLowerCase());
if (crime == null) {
// couldn't find crime
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeRoughName}'`);
}
helpers.log(ctx, () => `Attempting to commit ${crime.name}...`);
const crimeTime = crime.commit(1, ctx.workerScript);
if (focus) {
Player.startFocusing();
Router.toWork();
} else if (wasFocusing) {
Player.stopFocusing();
Router.toTerminal();
}
return crimeTime;
},
getCrimeChance: (ctx) => (_crimeRoughName) => {
commitCrime: (ctx) => (_crimeType, _focus) => {
helpers.checkSingularityAccess(ctx);
const crimeRoughName = helpers.string(ctx, "crimeRoughName", _crimeRoughName);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const focus = _focus === undefined ? true : !!_focus;
const wasFocusing = Player.focus;
const crime = findCrime(crimeRoughName.toLowerCase());
if (crime == null) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: ${crimeRoughName}`);
if (Player.currentWork !== null) Player.finishWork(true);
Player.gotoLocation(LocationName.Slums);
// If input isn't a crimeType, use search using roughname.
const crime = checkEnum(CrimeType, crimeType) ? Crimes[crimeType] : findCrime(crimeType);
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
helpers.log(ctx, () => `Attempting to commit ${crime.name}...`);
const crimeTime = crime.commit(1, ctx.workerScript);
if (focus) {
Player.startFocusing();
Router.toWork();
} else if (wasFocusing) {
Player.stopFocusing();
Router.toTerminal();
}
return crimeTime;
},
getCrimeChance: (ctx) => (_crimeType) => {
helpers.checkSingularityAccess(ctx);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
// If input isn't a crimeType, use search using roughname.
const crime = checkEnum(CrimeType, crimeType) ? Crimes[crimeType] : findCrime(crimeType);
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
return crime.successRate(Player);
},
getCrimeStats: (ctx) => (_crimeRoughName) => {
getCrimeStats: (ctx) => (_crimeType) => {
helpers.checkSingularityAccess(ctx);
const crimeRoughName = helpers.string(ctx, "crimeRoughName", _crimeRoughName);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const crime = findCrime(crimeRoughName.toLowerCase());
if (crime == null) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: ${crimeRoughName}`);
}
// If input isn't a crimeType, use search using roughname.
const crime = checkEnum(CrimeType, crimeType) ? Crimes[crimeType] : findCrime(crimeType);
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
const crimeStatsWithMultipliers = calculateCrimeWorkStats(crime);

@ -11,6 +11,8 @@ import { isSleeveBladeburnerWork } from "../PersonObjects/Sleeve/Work/SleeveBlad
import { isSleeveFactionWork } from "../PersonObjects/Sleeve/Work/SleeveFactionWork";
import { isSleeveCompanyWork } from "../PersonObjects/Sleeve/Work/SleeveCompanyWork";
import { helpers } from "../Netscript/NetscriptHelpers";
import { Crimes } from "../Crime/Crimes";
import { CrimeType } from "../utils/WorkType";
export function NetscriptSleeve(): InternalAPI<ISleeve> {
const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
@ -62,15 +64,13 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
checkSleeveNumber(ctx, sleeveNumber);
return Player.sleeves[sleeveNumber].synchronize();
},
setToCommitCrime: (ctx) => (_sleeveNumber, _crimeRoughName) => {
setToCommitCrime: (ctx) => (_sleeveNumber, _crimeType) => {
const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber);
const crimeRoughName = helpers.string(ctx, "crimeName", _crimeRoughName);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
checkSleeveAPIAccess(ctx);
checkSleeveNumber(ctx, sleeveNumber);
const crime = findCrime(crimeRoughName);
if (crime === null) {
return false;
}
const crime = checkEnum(CrimeType, crimeType) ? Crimes[crimeType] : findCrime(crimeType);
if (crime == null) return false;
return Player.sleeves[sleeveNumber].commitCrime(crime.name);
},
setToUniversityCourse: (ctx) => (_sleeveNumber, _universityName, _className) => {

@ -12,21 +12,18 @@ import { Person } from "../Person";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Crime } from "../../Crime/Crime";
import { Crimes } from "../../Crime/Crimes";
import { Companies } from "../../Company/Companies";
import { Company } from "../../Company/Company";
import { CompanyPosition } from "../../Company/CompanyPosition";
import { CompanyPositions } from "../../Company/CompanyPositions";
import { Contracts } from "../../Bladeburner/data/Contracts";
import { CONSTANTS } from "../../Constants";
import { checkEnum } from "../../utils/helpers/checkEnum";
import { CrimeType } from "../../utils/WorkType";
import { CityName } from "../../Locations/data/CityNames";
import { Factions } from "../../Faction/Factions";
import { CityName } from "../../Locations/data/CityNames";
import { LocationName } from "../../Locations/data/LocationNames";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../utils/JSONReviver";
@ -97,13 +94,9 @@ export class Sleeve extends Person {
}
/** Commit crimes */
commitCrime(crimeKey: string): boolean {
const crime: Crime | null = Crimes[crimeKey] || Object.values(Crimes).find((crime) => crime.name === crimeKey);
if (!crime) {
return false;
}
this.startWork(new SleeveCrimeWork(crime.type));
commitCrime(type: string): boolean {
if (!checkEnum(CrimeType, type)) return false;
this.startWork(new SleeveCrimeWork(type));
return true;
}

@ -8,6 +8,7 @@ import { Crime } from "../../../Crime/Crime";
import { newWorkStats, scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { CONSTANTS } from "../../../Constants";
import { BitNodeMultipliers } from "../../../BitNode/BitNodeMultipliers";
import { checkEnum } from "../../../utils/helpers/checkEnum";
export const isSleeveCrimeWork = (w: Work | null): w is SleeveCrimeWork => w !== null && w.type === WorkType.CRIME;
@ -20,9 +21,8 @@ export class SleeveCrimeWork extends Work {
}
getCrime(): Crime {
const crime = Object.values(Crimes).find((crime) => crime.type === this.crimeType);
if (!crime) throw new Error("crime should not be undefined");
return crime;
if (!checkEnum(CrimeType, this.crimeType)) throw new Error("crime should not be undefined");
return Crimes[this.crimeType];
}
getExp(sleeve: Sleeve): WorkStats {

@ -74,7 +74,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
const crime = w.getCrime();
desc = (
<>
This sleeve is currently attempting to {crime.type} (Success Rate:{" "}
This sleeve is currently attempting {crime.workName} (Success Rate:{" "}
{numeralWrapper.formatPercentage(crime.successRate(props.sleeve))}).
</>
);

@ -19,6 +19,8 @@ import { isSleeveSupportWork } from "../Work/SleeveSupportWork";
import { ClassType } from "../../../Work/ClassWork";
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
import { FactionWorkType } from "../../../Work/data/FactionWorkType";
import { checkEnum } from "../../../utils/helpers/checkEnum";
import { CrimeType } from "../../../utils/WorkType";
const universitySelectorOptions: string[] = [
"Study Computer Science",
@ -314,11 +316,7 @@ function getABC(sleeve: Sleeve): [string, string, string] {
}
}
if (isSleeveCrimeWork(w)) {
return [
"Commit Crime",
Object.values(Crimes).find((crime) => crime.type === w.crimeType)?.name ?? "Shoplift",
"------",
];
return ["Commit Crime", checkEnum(CrimeType, w.crimeType) ? Crimes[w.crimeType].name : "Shoplift", "------"];
}
if (isSleeveSupportWork(w)) {
return ["Perform Bladeburner Actions", "Support main sleeve", "------"];

@ -1,6 +1,3 @@
/** @public */
export type ValuesFrom<T> = T[keyof T];
/** @public */
export interface HP {
current: number;
@ -1979,19 +1976,13 @@ export interface Singularity {
*
* This function returns the number of milliseconds it takes to attempt the
* specified crime (e.g It takes 60 seconds to attempt the Rob Store crime,
* so running `commitCrime('rob store')` will return 60,000).
*
* Warning: I do not recommend using the time returned from this function to try
* and schedule your crime attempts. Instead, I would use the isBusy Singularity
* function to check whether you have finished attempting a crime. This is because
* although the game sets a certain crime to be X amount of seconds, there is no
* guarantee that your browser will follow that time limit.
* so running `commitCrime('ROBSTORE')` will return 60,000).
*
* @param crime - Name of crime to attempt.
* @param focus - Acquire player focus on this crime. Optional. Defaults to true.
* @returns The number of milliseconds it takes to attempt the specified crime.
*/
commitCrime(crime: string, focus?: boolean): number;
commitCrime(crime: CrimeType | CrimeNames, focus?: boolean): number;
/**
* Get chance to successfully commit a crime.
@ -2004,7 +1995,7 @@ export interface Singularity {
* @param crime - Name of crime.
* @returns Chance of success at committing the specified crime.
*/
getCrimeChance(crime: string): number;
getCrimeChance(crime: CrimeType | CrimeNames): number;
/**
* Get stats related to a crime.
@ -2014,10 +2005,10 @@ export interface Singularity {
*
* Returns the stats of the crime.
*
* @param crime - Name of crime. Not case-sensitive
* @param crime - Name of crime.
* @returns The stats of the crime.
*/
getCrimeStats(crime: string): CrimeStats;
getCrimeStats(crime: CrimeType | CrimeNames): CrimeStats;
/**
* Get a list of owned augmentation.
@ -2037,6 +2028,7 @@ export interface Singularity {
* @remarks
* RAM cost: 5 GB
*
*
* Returns an array of source files
*
* @returns Array containing an object with number and level of the source file.
@ -3651,32 +3643,13 @@ export interface Sleeve {
* @remarks
* RAM cost: 4 GB
*
* Return a boolean indicating whether or not this action was set successfully.
*
* Returns false if an invalid action is specified.
*
* You can set a sleeve to commit one of the following crimes. The crime names are not
* case sensitive. For example, you can pass in the crime name as `"Shoplift"`,
* `"shoplift"`, `"shopLift"`, or even `"SHOPLIFT"`.
*
* - Assassination
* - Bond forgery
* - Deal drugs
* - Grand theft auto
* - Heist
* - Homicide
* - Kidnap
* - Larceny
* - Mug
* - Rob store
* - Shoplift
* - Traffick arms
* Return a boolean indicating whether or not this action was set successfully (false if an invalid action is specified).
*
* @example
* ```ts
* // NS1
* // Assign the first 3 sleeves to commit various crimes.
* var crime = ["mug", "rob store", "shoplift"];
* var crime = ["MUG", "ROBSTORE", "SHOPLIFT"];
* for (var i = 0; i < crime.length; i++) {
* tprintf("Sleeve %d commits crime: %s", i, crime[i]);
* sleeve.setToCommitCrime(i, crime[i]);
@ -3686,7 +3659,7 @@ export interface Sleeve {
* ```ts
* // NS2
* // Assign the first 3 sleeves to commit various crimes.
* const crime = ["mug", "rob store", "shoplift"];
* const crime = ["MUG", "ROBSTORE", "SHOPLIFT"];
* for (let i = 0; i < crime.length; i++) {
* ns.tprintf("Sleeve %d commits crime: %s", i, crime[i]);
* ns.sleeve.setToCommitCrime(i, crime[i]);
@ -3694,10 +3667,10 @@ export interface Sleeve {
* ```
*
* @param sleeveNumber - Index of the sleeve to start committing crime. Sleeves are numbered starting from 0.
* @param name - Name of the crime. Must be an exact match. Refer to the list of crimes.
* @param name - Name of the crime.
* @returns True if this action was set successfully, false otherwise.
*/
setToCommitCrime(sleeveNumber: number, name: string): boolean;
setToCommitCrime(sleeveNumber: number, crimeType: CrimeType | CrimeNames): boolean;
/**
* Set a sleeve to work for a faction.
@ -3935,7 +3908,7 @@ export interface WorkStats {
* @public
*/
interface WorkFormulas {
crimeGains(crimeType: string): WorkStats;
crimeGains(crimeType: CrimeType | CrimeNames): WorkStats;
classGains(player: Player, classType: string, locationName: string): WorkStats;
factionGains(player: Player, workType: string, favor: number): WorkStats;
}
@ -6700,7 +6673,7 @@ export interface NS {
* @param variant - Type of toast, must be one of success, info, warning, error. Defaults to success.
* @param duration - Duration of toast in ms. Can also be `null` to create a persistent toast. Defaults to 2000
*/
toast(msg: string, variant?: ToastVariant, duration?: number | null): void;
toast(msg: string, variant?: ToastTypes | ToastVariant, duration?: number | null): void;
/**
* Download a file from the internet.
@ -6899,21 +6872,38 @@ export interface NS {
enums: NSEnums;
}
declare enum ToastVariant {
SUCCESS = "success",
WARNING = "warning",
ERROR = "error",
INFO = "info",
}
/** @public */
declare const enums = {
toast: {
SUCCESS: "success",
WARNING: "warning",
ERROR: "error",
INFO: "info",
},
export type ToastTypes = `${ToastVariant}`;
declare enum CrimeType {
SHOPLIFT = "SHOPLIFT",
ROB_STORE = "ROBSTORE",
MUG = "MUG",
LARCENY = "LARCENY",
DRUGS = "DRUGS",
BOND_FORGERY = "BONDFORGERY",
TRAFFIC_ARMS = "TRAFFICKARMS",
HOMICIDE = "HOMICIDE",
GRAND_THEFT_AUTO = "GRANDTHEFTAUTO",
KIDNAP = "KIDNAP",
ASSASSINATION = "ASSASSINATION",
HEIST = "HEIST",
}
/** @public */
type CrimeNames = `${CrimeType}`;
/** @public */
export type NSEnums = {
toast: typeof ToastVariant;
crimes: typeof CrimeType;
};
/** @public */
type ToastVariant = ValuesFrom<typeof enums.toast>;
/** @public */
export type NSEnums = typeof enums;
/**
* Corporation Office API
* @remarks

@ -9,51 +9,7 @@ import { CrimeType } from "../utils/WorkType";
import { Work, WorkType } from "./Work";
import { scaleWorkStats, WorkStats } from "./WorkStats";
import { calculateCrimeWorkStats } from "./formulas/Crime";
enum newCrimeType {
SHOPLIFT = "SHOPLIFT",
ROBSTORE = "ROBSTORE",
MUG = "MUG",
LARCENY = "LARCENY",
DRUGS = "DRUGS",
BONDFORGERY = "BONDFORGERY",
TRAFFICKARMS = "TRAFFICKARMS",
HOMICIDE = "HOMICIDE",
GRANDTHEFTAUTO = "GRANDTHEFTAUTO",
KIDNAP = "KIDNAP",
ASSASSINATION = "ASSASSINATION",
HEIST = "HEIST",
}
const convertCrimeType = (crimeType: CrimeType): newCrimeType => {
switch (crimeType) {
case CrimeType.SHOPLIFT:
return newCrimeType.SHOPLIFT;
case CrimeType.ROB_STORE:
return newCrimeType.ROBSTORE;
case CrimeType.MUG:
return newCrimeType.MUG;
case CrimeType.LARCENY:
return newCrimeType.LARCENY;
case CrimeType.DRUGS:
return newCrimeType.DRUGS;
case CrimeType.BOND_FORGERY:
return newCrimeType.BONDFORGERY;
case CrimeType.TRAFFIC_ARMS:
return newCrimeType.TRAFFICKARMS;
case CrimeType.HOMICIDE:
return newCrimeType.HOMICIDE;
case CrimeType.GRAND_THEFT_AUTO:
return newCrimeType.GRANDTHEFTAUTO;
case CrimeType.KIDNAP:
return newCrimeType.KIDNAP;
case CrimeType.ASSASSINATION:
return newCrimeType.ASSASSINATION;
case CrimeType.HEIST:
return newCrimeType.HEIST;
}
return newCrimeType.SHOPLIFT;
};
import { checkEnum } from "../utils/helpers/checkEnum";
interface CrimeWorkParams {
crimeType: CrimeType;
@ -73,9 +29,10 @@ export class CrimeWork extends Work {
}
getCrime(): Crime {
const crime = Object.values(Crimes).find((c) => c.type === this.crimeType);
if (!crime) throw new Error("CrimeWork object constructed with invalid crime type");
return crime;
if (!checkEnum(CrimeType, this.crimeType)) {
throw new Error("CrimeWork object constructed with invalid crime type");
}
return Crimes[this.crimeType];
}
process(cycles = 1): boolean {
@ -132,7 +89,7 @@ export class CrimeWork extends Work {
return {
type: this.type,
cyclesWorked: this.cyclesWorked,
crimeType: convertCrimeType(this.crimeType),
crimeType: checkEnum(CrimeType, this.crimeType) ? this.crimeType : CrimeType.SHOPLIFT,
};
}

@ -1,5 +1,4 @@
export enum CrimeType {
None = "",
SHOPLIFT = "SHOPLIFT", //"shoplift",
ROB_STORE = "ROBSTORE", //"rob a store",
MUG = "MUG", //"mug someone",