MISC: A bunch of enums stuff. (#212)

* Some game enums moved to utils/enums. Others can eventually be moved there as well.
* findEnumMember function for performing fuzzy matching of player input with enum members, without needing separate fuzzy functions for every enum.
* Also used findEnumMember for safely loading save games (allows case changes in enum values)
* Changed capitalization on some enums.
* BREAKING: removed classGains work formulas function
* Split ClassType enum into UniversityClassType and GymType.
* Added universityGains and gymGains work formulas functions
* Provided the new split enums to the player on ns.enums.
This commit is contained in:
Snarling 2022-11-20 08:37:11 -05:00 committed by GitHub
parent 629d610532
commit 88d51e9a7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 346 additions and 432 deletions

@ -14,7 +14,7 @@ import { Cities } from "../Locations/Cities";
import { EmployeePositions } from "./EmployeePositions";
import { ResearchMap } from "./ResearchMap";
import { isRelevantMaterial } from "./ui/Helpers";
import { checkEnum } from "../utils/helpers/checkEnum";
import { checkEnum } from "../utils/helpers/enum";
import { CityName } from "../Locations/data/CityNames";
export function NewIndustry(corporation: Corporation, industry: IndustryType, name: string): void {

@ -2,7 +2,7 @@ import { CONSTANTS } from "../Constants";
import { Player } from "@player";
import { Person as IPerson } from "../ScriptEditor/NetscriptDefinitions";
import { WorkerScript } from "../Netscript/WorkerScript";
import { CrimeType } from "../utils/WorkType";
import { CrimeType } from "../utils/enums";
import { CrimeWork } from "../Work/CrimeWork";
import { calculateIntelligenceBonus } from "../PersonObjects/formulas/intelligence";
@ -25,6 +25,9 @@ interface IConstructorParams {
}
export class Crime {
// Corresponding type, also the name of the crime.
type: CrimeType;
// Number representing the difficulty of the crime. Used for success chance calculations
difficulty: number;
@ -37,9 +40,6 @@ export class Crime {
// How much money is given by the
money: number;
// Name of crime
name: string;
// Name of crime as it appears on work screen: "You are attempting..."
workName: string;
@ -49,9 +49,6 @@ export class Crime {
// Milliseconds it takes to attempt the crime
time = 0;
// Corresponding type in CONSTANTS. Contains a description for the crime activity
type: CrimeType;
// Weighting factors that determine how stats affect the success rate of this crime
hacking_success_weight = 0;
strength_success_weight = 0;
@ -70,7 +67,6 @@ export class Crime {
intelligence_exp = 0;
constructor(
name: string,
workName: string,
tooltipText: string,
type: CrimeType,
@ -80,7 +76,6 @@ export class Crime {
karma: number,
params: IConstructorParams,
) {
this.name = name;
this.workName = workName;
this.tooltipText = tooltipText;
this.type = type;

@ -3,8 +3,8 @@ 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";
import { checkEnum } from "../utils/helpers/enum";
import { CrimeType } from "../utils/enums";
//This is only used for the player
export function determineCrimeSuccess(type: string): boolean {
@ -20,31 +20,18 @@ export function determineCrimeSuccess(type: string): boolean {
export function findCrime(roughName: string): Crime | null {
if (checkEnum(CrimeType, roughName)) return Crimes[roughName];
roughName = roughName.toLowerCase();
if (roughName.includes("shoplift")) {
return Crimes[CrimeType.SHOPLIFT];
} else if (roughName.includes("rob") && roughName.includes("store")) {
return Crimes[CrimeType.ROB_STORE];
} 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.DRUGS];
} else if (roughName.includes("bond") && roughName.includes("forge")) {
return Crimes[CrimeType.BOND_FORGERY];
} else if ((roughName.includes("traffic") || roughName.includes("illegal")) && roughName.includes("arms")) {
return Crimes[CrimeType.TRAFFIC_ARMS];
} else if (roughName.includes("homicide")) {
return Crimes[CrimeType.HOMICIDE];
} else if (roughName.includes("grand") && roughName.includes("auto")) {
return Crimes[CrimeType.GRAND_THEFT_AUTO];
} 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];
}
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;
}

@ -1,15 +1,12 @@
import { Crime } from "./Crime";
import { CONSTANTS } from "../Constants";
import { CrimeType } from "../utils/enums";
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",
[CrimeType.shoplift]: new Crime(
"to shoplift",
"Attempt to shoplift from a low-end retailer",
CrimeType.SHOPLIFT,
CrimeType.shoplift,
2e3,
15e3,
1 / 20,
@ -23,11 +20,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.ROB_STORE]: new Crime(
"Rob Store",
[CrimeType.robStore]: new Crime(
"to rob a store",
"Attempt to commit armed robbery on a high-end store",
CrimeType.ROB_STORE,
CrimeType.robStore,
60e3,
400e3,
1 / 5,
@ -45,11 +41,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.MUG]: new Crime(
"Mug",
[CrimeType.mug]: new Crime(
"to mug",
"Attempt to mug a random person on the street",
CrimeType.MUG,
CrimeType.mug,
4e3,
36e3,
1 / 5,
@ -67,11 +62,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.LARCENY]: new Crime(
"Larceny",
[CrimeType.larceny]: new Crime(
"larceny",
"Attempt to rob property from someone's house",
CrimeType.LARCENY,
CrimeType.larceny,
90e3,
800e3,
1 / 3,
@ -89,16 +83,7 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.DRUGS]: new Crime(
"Deal Drugs",
"to deal drugs",
"Attempt to deal drugs",
CrimeType.DRUGS,
10e3,
120e3,
1,
0.5,
{
[CrimeType.dealDrugs]: new Crime("to deal drugs", "Attempt to deal drugs", CrimeType.dealDrugs, 10e3, 120e3, 1, 0.5, {
dexterity_exp: 5,
agility_exp: 5,
charisma_exp: 10,
@ -106,14 +91,12 @@ export const Crimes: Record<CrimeType, Crime> = {
charisma_success_weight: 3,
dexterity_success_weight: 2,
agility_success_weight: 1,
},
),
}),
[CrimeType.BOND_FORGERY]: new Crime(
"Bond Forgery",
[CrimeType.bondForgery]: new Crime(
"to forge bonds",
"Attempt to forge corporate bonds",
CrimeType.BOND_FORGERY,
CrimeType.bondForgery,
300e3,
4.5e6,
1 / 2,
@ -130,11 +113,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.TRAFFIC_ARMS]: new Crime(
"Traffick Arms",
[CrimeType.traffickArms]: new Crime(
"to traffic arms",
"Attempt to smuggle illegal arms into the city",
CrimeType.TRAFFIC_ARMS,
CrimeType.traffickArms,
40e3,
600e3,
2,
@ -154,11 +136,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.HOMICIDE]: new Crime(
"Homicide",
[CrimeType.homicide]: new Crime(
"homicide",
"Attempt to murder a random person on the street",
CrimeType.HOMICIDE,
CrimeType.homicide,
3e3,
45e3,
1,
@ -178,11 +159,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.GRAND_THEFT_AUTO]: new Crime(
"Grand Theft Auto",
[CrimeType.grandTheftAuto]: new Crime(
"grand theft auto",
"Attempt to commit grand theft auto",
CrimeType.GRAND_THEFT_AUTO,
CrimeType.grandTheftAuto,
80e3,
1.6e6,
8,
@ -204,11 +184,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.KIDNAP]: new Crime(
"Kidnap",
[CrimeType.kidnap]: new Crime(
"to kidnap",
"Attempt to kidnap and ransom a high-profile-target",
CrimeType.KIDNAP,
CrimeType.kidnap,
120e3,
3.6e6,
5,
@ -229,11 +208,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.ASSASSINATION]: new Crime(
"Assassination",
[CrimeType.assassination]: new Crime(
"to assassinate",
"Attempt to assassinate a high-profile target",
CrimeType.ASSASSINATION,
CrimeType.assassination,
300e3,
12e6,
8,
@ -254,11 +232,10 @@ export const Crimes: Record<CrimeType, Crime> = {
},
),
[CrimeType.HEIST]: new Crime(
"Heist",
[CrimeType.heist]: new Crime(
"a heist",
"Attempt to pull off the ultimate heist",
CrimeType.HEIST,
CrimeType.heist,
600e3,
120e6,
18,

@ -23,7 +23,7 @@ import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPur
import { FactionNames } from "../data/FactionNames";
import { GangButton } from "./GangButton";
import { FactionWork } from "../../Work/FactionWork";
import { FactionWorkType } from "../../Work/data/FactionWorkType";
import { FactionWorkType } from "../../utils/enums";
type IProps = {
faction: Faction;
@ -72,7 +72,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.FIELD,
factionWorkType: FactionWorkType.field,
}),
);
startWork();
@ -83,7 +83,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.HACKING,
factionWorkType: FactionWorkType.hacking,
}),
);
startWork();
@ -94,7 +94,7 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.SECURITY,
factionWorkType: FactionWorkType.security,
}),
);
startWork();

@ -13,15 +13,16 @@ import { Player } from "@player";
import { Money } from "../../ui/React/Money";
import { Router } from "../../ui/GameRoot";
import { Box } from "@mui/material";
import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
import { ClassWork, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/Formulas";
import { GymType } from "../../utils/enums";
type IProps = {
loc: Location;
};
export function GymLocation(props: IProps): React.ReactElement {
function train(stat: ClassType): void {
function train(stat: GymType): void {
Player.startWork(
new ClassWork({
classType: stat,
@ -33,20 +34,20 @@ export function GymLocation(props: IProps): React.ReactElement {
Router.toWork();
}
const cost = calculateCost(Classes[ClassType.GymStrength], props.loc);
const cost = calculateCost(Classes[GymType.strength], props.loc);
return (
<Box sx={{ display: "grid", width: "fit-content" }}>
<Button onClick={() => train(ClassType.GymStrength)}>
<Button onClick={() => train(GymType.strength)}>
Train Strength (<Money money={cost} forPurchase={true} /> / sec)
</Button>
<Button onClick={() => train(ClassType.GymDefense)}>
<Button onClick={() => train(GymType.defense)}>
Train Defense (<Money money={cost} forPurchase={true} /> / sec)
</Button>
<Button onClick={() => train(ClassType.GymDexterity)}>
<Button onClick={() => train(GymType.dexterity)}>
Train Dexterity (<Money money={cost} forPurchase={true} /> / sec)
</Button>
<Button onClick={() => train(ClassType.GymAgility)}>
<Button onClick={() => train(GymType.agility)}>
Train Agility (<Money money={cost} forPurchase={true} /> / sec)
</Button>
</Box>

@ -36,7 +36,7 @@ export function SlumsLocation(): React.ReactElement {
{crimes.map((crime) => (
<Tooltip title={crime.tooltipText}>
<Button onClick={(e) => doCrime(e, crime)}>
{crime.name} ({numeralWrapper.formatPercentage(crime.successRate(Player))} chance of success)
{crime.type} ({numeralWrapper.formatPercentage(crime.successRate(Player))} chance of success)
</Button>
</Tooltip>
))}

@ -14,15 +14,16 @@ import { Router } from "../../ui/GameRoot";
import { Player } from "@player";
import { Box } from "@mui/material";
import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
import { ClassWork, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/Formulas";
import { UniversityClassType } from "../../utils/enums";
type IProps = {
loc: Location;
};
export function UniversityLocation(props: IProps): React.ReactElement {
function take(classType: ClassType): void {
function take(classType: UniversityClassType): void {
Player.startWork(
new ClassWork({
classType: classType,
@ -34,11 +35,11 @@ export function UniversityLocation(props: IProps): React.ReactElement {
Router.toWork();
}
const dataStructuresCost = calculateCost(Classes[ClassType.DataStructures], props.loc);
const networksCost = calculateCost(Classes[ClassType.Networks], props.loc);
const algorithmsCost = calculateCost(Classes[ClassType.Algorithms], props.loc);
const managementCost = calculateCost(Classes[ClassType.Management], props.loc);
const leadershipCost = calculateCost(Classes[ClassType.Leadership], props.loc);
const dataStructuresCost = calculateCost(Classes[UniversityClassType.dataStructures], props.loc);
const networksCost = calculateCost(Classes[UniversityClassType.networks], props.loc);
const algorithmsCost = calculateCost(Classes[UniversityClassType.algorithms], props.loc);
const managementCost = calculateCost(Classes[UniversityClassType.management], props.loc);
const leadershipCost = calculateCost(Classes[UniversityClassType.leadership], props.loc);
const earnHackingExpTooltip = `Gain hacking experience!`;
const earnCharismaExpTooltip = `Gain charisma experience!`;
@ -46,34 +47,34 @@ export function UniversityLocation(props: IProps): React.ReactElement {
return (
<Box sx={{ display: "grid", width: "fit-content" }}>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={() => take(ClassType.StudyComputerScience)}>Study Computer Science (free)</Button>
<Button onClick={() => take(UniversityClassType.computerScience)}>Study Computer Science (free)</Button>
</Tooltip>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={() => take(ClassType.DataStructures)}>
<Button onClick={() => take(UniversityClassType.dataStructures)}>
Take Data Structures course (
<Money money={dataStructuresCost} forPurchase={true} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={() => take(ClassType.Networks)}>
<Button onClick={() => take(UniversityClassType.networks)}>
Take Networks course (
<Money money={networksCost} forPurchase={true} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={() => take(ClassType.Algorithms)}>
<Button onClick={() => take(UniversityClassType.algorithms)}>
Take Algorithms course (
<Money money={algorithmsCost} forPurchase={true} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnCharismaExpTooltip}>
<Button onClick={() => take(ClassType.Management)}>
<Button onClick={() => take(UniversityClassType.management)}>
Take Management course (
<Money money={managementCost} forPurchase={true} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnCharismaExpTooltip}>
<Button onClick={() => take(ClassType.Leadership)}>
<Button onClick={() => take(UniversityClassType.leadership)}>
Take Leadership course (
<Money money={leadershipCost} forPurchase={true} /> / sec)
</Button>

@ -33,7 +33,7 @@ import { arrayToString } from "../utils/helpers/arrayToString";
import { HacknetServer } from "../Hacknet/HacknetServer";
import { BaseServer } from "../Server/BaseServer";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { checkEnum } from "../utils/helpers/checkEnum";
import { checkEnum } from "../utils/helpers/enum";
export const helpers = {
string,

@ -602,7 +602,8 @@ export const RamCosts: RamCostTree<NSFull> = {
work: {
crimeSuccessChance: 0,
crimeGains: 0,
classGains: 0,
gymGains: 0,
universityGains: 0,
factionGains: 0,
companyGains: 0,
},

@ -66,7 +66,7 @@ import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
import { dialogBoxCreate } from "./ui/React/DialogBox";
import { SnackbarEvents, ToastVariant } from "./ui/React/Snackbar";
import { checkEnum } from "./utils/helpers/checkEnum";
import { checkEnum } from "./utils/helpers/enum";
import { Flags } from "./NetscriptFunctions/Flags";
import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligence";
@ -77,16 +77,16 @@ 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 { CompanyPosNames, CrimeType } from "./utils/WorkType";
import { CompanyPosNames, CrimeType, GymType, UniversityClassType } from "./utils/enums";
import { cloneDeep } from "lodash";
import { FactionWorkType } from "./Work/data/FactionWorkType";
import { ClassType } from "./Work/ClassWork";
import { FactionWorkType } from "./utils/enums";
export const enums: NSEnums = {
toast: ToastVariant,
CrimeType,
FactionWorkType,
ClassType,
GymType,
UniversityClassType,
CompanyPosNames,
};

@ -58,7 +58,7 @@ import { Factions } from "../Faction/Factions";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { assertEnumMember, helpers } from "../Netscript/NetscriptHelpers";
import { checkEnum } from "../utils/helpers/checkEnum";
import { checkEnum } from "../utils/helpers/enum";
import { CityName } from "../Locations/data/CityNames";
export function NetscriptCorporation(): InternalAPI<NSCorporation> {

@ -40,19 +40,18 @@ import { repFromDonation } from "../Faction/formulas/donation";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers";
import { calculateCrimeWorkStats } from "../Work/Formulas";
import { Crimes } from "../Crime/Crimes";
import { calculateCompanyWorkStats } from "../Work/Formulas";
import { Companies } from "../Company/Companies";
import { calculateClassEarnings } from "../Work/Formulas";
import { ClassType } from "../Work/ClassWork";
import { LocationName } from "../Locations/data/LocationNames";
import { calculateFactionExp, calculateFactionRep } from "../Work/Formulas";
import { FactionWorkType } from "../Work/data/FactionWorkType";
import { FactionWorkType, GymType, UniversityClassType } from "../utils/enums";
import { defaultMultipliers } from "../PersonObjects/Multipliers";
import { checkEnum } from "../utils/helpers/checkEnum";
import { CompanyPosNames, CrimeType } from "../utils/WorkType";
import { checkEnum, findEnumMember } from "../utils/helpers/enum";
import { CompanyPosNames } from "../utils/enums";
import { CompanyPositions } from "../Company/CompanyPositions";
import { findCrime } from "../Crime/CrimeHelpers";
export function NetscriptFormulas(): InternalAPI<IFormulas> {
const checkFormulasAccess = function (ctx: NetscriptContext): void {
@ -356,22 +355,31 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
crimeSuccessChance: (ctx) => (_person, _crimeType) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
if (!checkEnum(CrimeType, crimeType)) throw new Error(`Invalid crime type: ${crimeType}`);
return Crimes[crimeType].successRate(person);
const crime = findCrime(helpers.string(ctx, "crimeType", _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 crimeType = helpers.string(ctx, "crimeType", _crimeType);
if (!checkEnum(CrimeType, crimeType)) throw new Error(`Invalid crime type: ${crimeType}`);
return calculateCrimeWorkStats(person, Crimes[crimeType]);
const crime = findCrime(helpers.string(ctx, "crimeType", _crimeType));
if (!crime) throw new Error(`Invalid crime type: ${_crimeType}`);
return calculateCrimeWorkStats(person, crime);
},
classGains: (ctx) => (_person, _classType, _locationName) => {
gymGains: (ctx) => (_person, _classType, _locationName) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const classType = helpers.string(ctx, "classType", _classType);
if (!checkEnum(ClassType, classType)) throw new Error(`Invalid class type: ${classType}`);
const classType = findEnumMember(GymType, helpers.string(ctx, "classType", _classType));
if (!classType) throw new Error(`Invalid gym training type: ${_classType}`);
const locationName = helpers.string(ctx, "locationName", _locationName);
if (!checkEnum(LocationName, locationName)) throw new Error(`Invalid location name: ${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 locationName = helpers.string(ctx, "locationName", _locationName);
if (!checkEnum(LocationName, locationName)) throw new Error(`Invalid location name: ${locationName}`);
return calculateClassEarnings(person, classType, locationName);
@ -379,8 +387,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
factionGains: (ctx) => (_player, _workType, _favor) => {
checkFormulasAccess(ctx);
const player = helpers.person(ctx, _player);
const workType = helpers.string(ctx, "_workType", _workType);
if (!checkEnum(FactionWorkType, workType)) throw new Error(`Invalid faction work type: ${workType}`);
const workType = findEnumMember(FactionWorkType, helpers.string(ctx, "_workType", _workType));
if (!workType) throw new Error(`Invalid faction work type: ${_workType}`);
const favor = helpers.number(ctx, "favor", _favor);
const exp = calculateFactionExp(player, workType);
const rep = calculateFactionRep(player, workType, favor);
@ -390,8 +398,8 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
companyGains: (ctx) => (_person, _companyName, _positionName, _favor) => {
checkFormulasAccess(ctx);
const person = helpers.person(ctx, _person);
const positionName = helpers.string(ctx, "_positionName", _positionName);
if (!checkEnum(CompanyPosNames, positionName)) throw new Error(`Invalid company position: ${positionName}`);
const positionName = findEnumMember(CompanyPosNames, helpers.string(ctx, "_positionName", _positionName));
if (!positionName) throw new Error(`Invalid company position: ${_positionName}`);
const position = CompanyPositions[positionName];
const companyName = helpers.string(ctx, "_companyName", _companyName);
const company = Object.values(Companies).find((c) => c.name === companyName);

@ -10,7 +10,7 @@ import {
import { FactionNames } from "../Faction/data/FactionNames";
import { Factions } from "../Faction/Factions";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { checkEnum } from "../utils/helpers/checkEnum";
import { checkEnum } from "../utils/helpers/enum";
import { LocationName } from "../Locations/data/LocationNames";
import { helpers } from "../Netscript/NetscriptHelpers";

@ -41,14 +41,15 @@ import { InternalAPI, NetscriptContext } from "src/Netscript/APIWrapper";
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
import { enterBitNode } from "../RedPill";
import { FactionNames } from "../Faction/data/FactionNames";
import { ClassWork, ClassType } from "../Work/ClassWork";
import { ClassWork } from "../Work/ClassWork";
import { CreateProgramWork, isCreateProgramWork } from "../Work/CreateProgramWork";
import { FactionWork } from "../Work/FactionWork";
import { FactionWorkType } from "../Work/data/FactionWorkType";
import { FactionWorkType, GymType, UniversityClassType } from "../utils/enums";
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";
export function NetscriptSingularity(): InternalAPI<ISingularity> {
const getAugmentation = function (ctx: NetscriptContext, name: string): Augmentation {
@ -253,7 +254,11 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
(_universityName, _className, _focus = true) => {
helpers.checkSingularityAccess(ctx);
const universityName = helpers.string(ctx, "universityName", _universityName);
const className = helpers.string(ctx, "className", _className);
const classType = findEnumMember(UniversityClassType, helpers.string(ctx, "className", _className));
if (!classType) {
helpers.log(ctx, () => `Invalid class name: ${_className}.`);
return false;
}
const focus = !!_focus;
const wasFocusing = Player.focus;
@ -293,33 +298,9 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
return false;
}
let task: ClassType;
switch (className.toLowerCase()) {
case "Study Computer Science".toLowerCase():
task = ClassType.StudyComputerScience;
break;
case "Data Structures".toLowerCase():
task = ClassType.DataStructures;
break;
case "Networks".toLowerCase():
task = ClassType.Networks;
break;
case "Algorithms".toLowerCase():
task = ClassType.Algorithms;
break;
case "Management".toLowerCase():
task = ClassType.Management;
break;
case "Leadership".toLowerCase():
task = ClassType.Leadership;
break;
default:
helpers.log(ctx, () => `Invalid class name: ${className}.`);
return false;
}
Player.startWork(
new ClassWork({
classType: task,
classType,
location: Player.location,
singularity: true,
}),
@ -331,7 +312,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
Player.stopFocusing();
Router.toTerminal();
}
helpers.log(ctx, () => `Started ${task} at ${universityName}`);
helpers.log(ctx, () => `Started ${classType} at ${universityName}`);
return true;
},
@ -340,7 +321,11 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
(_gymName, _stat, _focus = true) => {
helpers.checkSingularityAccess(ctx);
const gymName = helpers.string(ctx, "gymName", _gymName);
const stat = helpers.string(ctx, "stat", _stat);
const classType = findEnumMember(GymType, helpers.string(ctx, "stat", _stat));
if (!classType) {
helpers.log(ctx, () => `Invalid stat: ${_stat}.`);
return false;
}
const focus = !!_focus;
const wasFocusing = Player.focus;
@ -405,35 +390,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
return false;
}
switch (stat.toLowerCase()) {
case "strength".toLowerCase():
case "str".toLowerCase():
Player.startWork(
new ClassWork({ classType: ClassType.GymStrength, location: Player.location, singularity: true }),
);
break;
case "defense".toLowerCase():
case "def".toLowerCase():
Player.startWork(
new ClassWork({ classType: ClassType.GymDefense, location: Player.location, singularity: true }),
);
break;
case "dexterity".toLowerCase():
case "dex".toLowerCase():
Player.startWork(
new ClassWork({ classType: ClassType.GymDexterity, location: Player.location, singularity: true }),
);
break;
case "agility".toLowerCase():
case "agi".toLowerCase():
Player.startWork(
new ClassWork({ classType: ClassType.GymAgility, location: Player.location, singularity: true }),
);
break;
default:
helpers.log(ctx, () => `Invalid stat: ${stat}.`);
return false;
}
Player.startWork(new ClassWork({ classType, location: Player.location, singularity: true }));
if (focus) {
Player.startFocusing();
Router.toWork();
@ -441,7 +398,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
Player.stopFocusing();
Router.toTerminal();
}
helpers.log(ctx, () => `Started training ${stat} at ${gymName}`);
helpers.log(ctx, () => `Started training ${classType} at ${gymName}`);
return true;
},
@ -926,7 +883,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
Player.startWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.HACKING,
factionWorkType: FactionWorkType.hacking,
faction: faction.name,
}),
);
@ -949,7 +906,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
Player.startWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.FIELD,
factionWorkType: FactionWorkType.field,
faction: faction.name,
}),
);
@ -972,7 +929,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
Player.startWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.SECURITY,
factionWorkType: FactionWorkType.security,
faction: faction.name,
}),
);
@ -1118,7 +1075,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
const crime = findCrime(crimeType);
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
helpers.log(ctx, () => `Attempting to commit ${crime.name}...`);
helpers.log(ctx, () => `Attempting to commit ${crime.type}...`);
const crimeTime = crime.commit(1, ctx.workerScript);
if (focus) {
Player.startFocusing();

@ -5,7 +5,7 @@ import { findCrime } from "../Crime/CrimeHelpers";
import { Augmentation } from "../Augmentation/Augmentation";
import { sleeve } from "../ScriptEditor/NetscriptDefinitions";
import { checkEnum } from "../utils/helpers/checkEnum";
import { checkEnum } from "../utils/helpers/enum";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { isSleeveBladeburnerWork } from "../PersonObjects/Sleeve/Work/SleeveBladeburnerWork";
import { isSleeveFactionWork } from "../PersonObjects/Sleeve/Work/SleeveFactionWork";

@ -18,7 +18,7 @@ import { CompanyPosition } from "../../Company/CompanyPosition";
import { CompanyPositions } from "../../Company/CompanyPositions";
import { Contracts } from "../../Bladeburner/data/Contracts";
import { CONSTANTS } from "../../Constants";
import { CrimeType } from "../../utils/WorkType";
import { CrimeType, GymType, UniversityClassType } from "../../utils/enums";
import { CityName } from "../../Locations/data/CityNames";
import { Factions } from "../../Faction/Factions";
@ -27,7 +27,7 @@ import { LocationName } from "../../Locations/data/LocationNames";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../utils/JSONReviver";
import { numeralWrapper } from "../../ui/numeralFormat";
import { FactionWorkType } from "../../Work/data/FactionWorkType";
import { FactionWorkType } from "../../utils/enums";
import { Work } from "./Work/Work";
import { SleeveClassWork } from "./Work/SleeveClassWork";
import { ClassType } from "../../Work/ClassWork";
@ -221,22 +221,22 @@ export class Sleeve extends Person implements ISleeve {
let classType: ClassType | undefined;
switch (className.toLowerCase()) {
case "study computer science":
classType = ClassType.StudyComputerScience;
classType = UniversityClassType.computerScience;
break;
case "data structures":
classType = ClassType.DataStructures;
classType = UniversityClassType.dataStructures;
break;
case "networks":
classType = ClassType.Networks;
classType = UniversityClassType.networks;
break;
case "algorithms":
classType = ClassType.Algorithms;
classType = UniversityClassType.algorithms;
break;
case "management":
classType = ClassType.Management;
classType = UniversityClassType.management;
break;
case "leadership":
classType = ClassType.Leadership;
classType = UniversityClassType.leadership;
break;
}
if (!classType) return false;
@ -311,13 +311,13 @@ export class Sleeve extends Person implements ISleeve {
let factionWorkType: FactionWorkType;
if (sanitizedWorkType.includes("hack")) {
if (!factionInfo.offerHackingWork) return false;
factionWorkType = FactionWorkType.HACKING;
factionWorkType = FactionWorkType.hacking;
} else if (sanitizedWorkType.includes("field")) {
if (!factionInfo.offerFieldWork) return false;
factionWorkType = FactionWorkType.FIELD;
factionWorkType = FactionWorkType.field;
} else if (sanitizedWorkType.includes("security")) {
if (!factionInfo.offerSecurityWork) return false;
factionWorkType = FactionWorkType.SECURITY;
factionWorkType = FactionWorkType.security;
} else {
return false;
}
@ -372,16 +372,16 @@ export class Sleeve extends Person implements ISleeve {
// set stat to a default value.
let classType: ClassType | undefined;
if (sanitizedStat.includes("str")) {
classType = ClassType.GymStrength;
classType = GymType.strength;
}
if (sanitizedStat.includes("def")) {
classType = ClassType.GymDefense;
classType = GymType.defense;
}
if (sanitizedStat.includes("dex")) {
classType = ClassType.GymDexterity;
classType = GymType.dexterity;
}
if (sanitizedStat.includes("agi")) {
classType = ClassType.GymAgility;
classType = GymType.agility;
}
// if stat is still equals its default value, then validation has failed.
if (!classType) return false;

@ -5,6 +5,8 @@ import { LocationName } from "../../../Locations/data/LocationNames";
import { calculateClassEarnings } from "../../../Work/Formulas";
import { Sleeve } from "../Sleeve";
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { GymType, UniversityClassType } from "../../../utils/enums";
import { checkEnum } from "../../../utils/helpers/enum";
export const isSleeveClassWork = (w: Work | null): w is SleeveClassWork => w !== null && w.type === WorkType.CLASS;
@ -19,7 +21,7 @@ export class SleeveClassWork extends Work {
constructor(params?: ClassWorkParams) {
super(WorkType.CLASS);
this.classType = params?.classType ?? ClassType.StudyComputerScience;
this.classType = params?.classType ?? UniversityClassType.computerScience;
this.location = params?.location ?? LocationName.Sector12RothmanUniversity;
}
@ -28,9 +30,7 @@ export class SleeveClassWork extends Work {
}
isGym(): boolean {
return [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymStrength].includes(
this.classType,
);
return checkEnum(GymType, this.classType);
}
process(sleeve: Sleeve, cycles: number) {

@ -2,13 +2,14 @@ import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, Work, WorkType } from "./Work";
import { CrimeType } from "../../../utils/WorkType";
import { CrimeType } from "../../../utils/enums";
import { Crimes } from "../../../Crime/Crimes";
import { Crime } from "../../../Crime/Crime";
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { CONSTANTS } from "../../../Constants";
import { checkEnum } from "../../../utils/helpers/checkEnum";
import { checkEnum } from "../../../utils/helpers/enum";
import { calculateCrimeWorkStats } from "../../../Work/Formulas";
import { findCrime } from "../../../Crime/CrimeHelpers";
export const isSleeveCrimeWork = (w: Work | null): w is SleeveCrimeWork => w !== null && w.type === WorkType.CRIME;
@ -17,7 +18,7 @@ export class SleeveCrimeWork extends Work {
cyclesWorked = 0;
constructor(crimeType?: CrimeType) {
super(WorkType.CRIME);
this.crimeType = crimeType ?? CrimeType.SHOPLIFT;
this.crimeType = crimeType ?? CrimeType.shoplift;
}
getCrime(): Crime {
@ -60,7 +61,9 @@ export class SleeveCrimeWork extends Work {
/** Initializes a RecoveryWork object from a JSON save state. */
static fromJSON(value: IReviverValue): SleeveCrimeWork {
return Generic_fromJSON(SleeveCrimeWork, value.data);
const crimeWork = Generic_fromJSON(SleeveCrimeWork, value.data);
crimeWork.crimeType = findCrime(crimeWork.crimeType)?.type ?? CrimeType.shoplift;
return crimeWork;
}
}

@ -2,7 +2,7 @@ import { Player } from "@player";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver";
import { Sleeve } from "../Sleeve";
import { applySleeveGains, Work, WorkType } from "./Work";
import { FactionWorkType } from "../../../Work/data/FactionWorkType";
import { FactionWorkType } from "../../../utils/enums";
import { FactionNames } from "../../../Faction/data/FactionNames";
import { Factions } from "../../../Faction/Factions";
import { calculateFactionExp, calculateFactionRep } from "../../../Work/Formulas";
@ -23,7 +23,7 @@ export class SleeveFactionWork extends Work {
constructor(params?: SleeveFactionWorkParams) {
super(WorkType.FACTION);
this.factionWorkType = params?.factionWorkType ?? FactionWorkType.HACKING;
this.factionWorkType = params?.factionWorkType ?? FactionWorkType.hacking;
this.factionName = params?.factionName ?? FactionNames.Sector12;
}

@ -1,6 +1,6 @@
import { Box, Button, Paper, Tooltip, Typography } from "@mui/material";
import React, { useState } from "react";
import { FactionWorkType } from "../../../Work/data/FactionWorkType";
import { FactionWorkType } from "../../../utils/enums";
import { CONSTANTS } from "../../../Constants";
import { Player } from "@player";
import { numeralWrapper } from "../../../ui/numeralFormat";
@ -21,7 +21,7 @@ import { isSleeveSupportWork } from "../Work/SleeveSupportWork";
import { isSleeveBladeburnerWork } from "../Work/SleeveBladeburnerWork";
import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork";
import { findCrime } from "../../../Crime/CrimeHelpers";
import { CrimeType } from "../../../utils/WorkType";
import { CrimeType } from "../../../utils/enums";
interface IProps {
sleeve: Sleeve;
@ -46,7 +46,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
props.sleeve.workForFaction(abc[1], abc[2]);
break;
case "Commit Crime":
props.sleeve.commitCrime(findCrime(abc[1])?.type ?? CrimeType.SHOPLIFT);
props.sleeve.commitCrime(findCrime(abc[1])?.type ?? CrimeType.shoplift);
break;
case "Take University Course":
props.sleeve.takeUniversityCourse(abc[2], abc[1]);
@ -106,13 +106,13 @@ export function SleeveElem(props: IProps): React.ReactElement {
if (isSleeveFactionWork(props.sleeve.currentWork)) {
let doing = "nothing";
switch (props.sleeve.currentWork.factionWorkType) {
case FactionWorkType.FIELD:
case FactionWorkType.field:
doing = "Field work";
break;
case FactionWorkType.HACKING:
case FactionWorkType.hacking:
doing = "Hacking contracts";
break;
case FactionWorkType.SECURITY:
case FactionWorkType.security:
doing = "Security work";
break;
}

@ -16,11 +16,9 @@ import { isSleeveSynchroWork } from "../Work/SleeveSynchroWork";
import { isSleeveClassWork } from "../Work/SleeveClassWork";
import { isSleeveInfiltrateWork } from "../Work/SleeveInfiltrateWork";
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";
import { CrimeType, FactionWorkType, GymType, UniversityClassType } from "../../../utils/enums";
import { checkEnum } from "../../../utils/helpers/enum";
const universitySelectorOptions: string[] = [
"Study Computer Science",
@ -167,7 +165,7 @@ const tasks: {
};
},
"Commit Crime": (): ITaskDetails => {
return { first: Object.values(Crimes).map((crime) => crime.name), second: () => ["------"] };
return { first: Object.keys(Crimes), second: () => ["------"] };
},
"Take University Course": (sleeve: Sleeve): ITaskDetails => {
let universities: string[] = [];
@ -263,13 +261,13 @@ function getABC(sleeve: Sleeve): [string, string, string] {
if (isSleeveFactionWork(w)) {
let workType = "";
switch (w.factionWorkType) {
case FactionWorkType.HACKING:
case FactionWorkType.hacking:
workType = "Hacking Contracts";
break;
case FactionWorkType.FIELD:
case FactionWorkType.field:
workType = "Field Work";
break;
case FactionWorkType.SECURITY:
case FactionWorkType.security:
workType = "Security Work";
break;
}
@ -293,30 +291,30 @@ function getABC(sleeve: Sleeve): [string, string, string] {
if (isSleeveClassWork(w)) {
switch (w.classType) {
case ClassType.StudyComputerScience:
case UniversityClassType.computerScience:
return ["Take University Course", "Study Computer Science", w.location];
case ClassType.DataStructures:
case UniversityClassType.dataStructures:
return ["Take University Course", "Data Structures", w.location];
case ClassType.Networks:
case UniversityClassType.networks:
return ["Take University Course", "Networks", w.location];
case ClassType.Algorithms:
case UniversityClassType.algorithms:
return ["Take University Course", "Algorithms", w.location];
case ClassType.Management:
case UniversityClassType.management:
return ["Take University Course", "Management", w.location];
case ClassType.Leadership:
case UniversityClassType.leadership:
return ["Take University Course", "Leadership", w.location];
case ClassType.GymStrength:
case GymType.strength:
return ["Workout at Gym", "Train Strength", w.location];
case ClassType.GymDefense:
case GymType.defense:
return ["Workout at Gym", "Train Defense", w.location];
case ClassType.GymDexterity:
case GymType.dexterity:
return ["Workout at Gym", "Train Dexterity", w.location];
case ClassType.GymAgility:
case GymType.agility:
return ["Workout at Gym", "Train Agility", w.location];
}
}
if (isSleeveCrimeWork(w)) {
return ["Commit Crime", checkEnum(CrimeType, w.crimeType) ? Crimes[w.crimeType].name : "Shoplift", "------"];
return ["Commit Crime", checkEnum(CrimeType, w.crimeType) ? w.crimeType : "Shoplift", "------"];
}
if (isSleeveSupportWork(w)) {
return ["Perform Bladeburner Actions", "Support main sleeve", "------"];

@ -211,8 +211,6 @@ export interface CrimeStats {
kills: number;
/** How much money is given */
money: number;
/** Name of crime */
name: string;
/** Milliseconds it takes to attempt the crime */
time: number;
/** Description of the crime activity */
@ -3817,8 +3815,14 @@ interface WorkFormulas {
crimeSuccessChance(person: Person, crimeType: CrimeType | `${CrimeType}`): number;
/** @returns The WorkStats gained when completing one instance of the specified crime. */
crimeGains(person: Person, crimeType: CrimeType | `${CrimeType}`): WorkStats;
/** @returns The WorkStats applied every game cycle (200ms) by taking the specified class. */
classGains(person: Person, classType: ClassType | `${ClassType}`, locationName: string): WorkStats;
/** @returns The WorkStats applied every game cycle (200ms) by taking the specified gym class. */
gymGains(person: Person, gymType: GymType | `${GymType}`, locationName: string): WorkStats;
/** @returns The WorkStats applied every game cycle (200ms) by taking the specified university class. */
universityGains(
person: Person,
classType: UniversityClassType | `${UniversityClassType}`,
locationName: string,
): WorkStats;
/** @returns The WorkStats applied every game cycle (200ms) by performing the specified faction work. */
factionGains(person: Person, workType: FactionWorkType | `${FactionWorkType}`, favor: number): WorkStats;
/** @returns The WorkStats applied every game cycle (200ms) by performing the specified company work. */
@ -6815,42 +6819,46 @@ declare enum ToastVariant {
/** @public */
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",
shoplift = "Shoplift",
robStore = "Rob Store",
mug = "Mug",
larceny = "Larceny",
dealDrugs = "Deal Drugs",
bondForgery = "Bond Forgery",
traffickArms = "Traffick Arms",
homicide = "Homicide",
grandTheftAuto = "Grand Theft Auto",
kidnap = "Kidnap",
assassination = "Assassination",
heist = "Heist",
}
/** @public */
declare enum FactionWorkType {
HACKING = "HACKING",
FIELD = "FIELD",
SECURITY = "SECURITY",
hacking = "hacking",
field = "field",
security = "security",
}
// TODO: split ClassType enum into separate enums for gym and uni so they can actually be used for player input.
/** @public */
declare enum ClassType {
StudyComputerScience = "STUDYCOMPUTERSCIENCE",
DataStructures = "DATASTRUCTURES",
Networks = "NETWORKS",
Algorithms = "ALGORITHMS",
Management = "MANAGEMENT",
Leadership = "LEADERSHIP",
GymStrength = "GYMSTRENGTH",
GymDefense = "GYMDEFENSE",
GymDexterity = "GYMDEXTERITY",
GymAgility = "GYMAGILITY",
declare enum UniversityClassType {
computerScience = "Computer Science",
dataStructures = "Data Structures",
networks = "Networks",
algorithms = "Algorithms",
management = "Management",
leadership = "Leadership",
}
/** @public */
declare enum GymType {
strength = "str",
defense = "def",
dexterity = "dex",
agility = "agi",
}
/** @public */
declare enum CompanyPosNames {
sw0 = "Software Engineering Intern",
sw1 = "Junior Software Engineer",
@ -6927,7 +6935,8 @@ export type NSEnums = {
toast: typeof ToastVariant;
CrimeType: typeof CrimeType;
FactionWorkType: typeof FactionWorkType;
ClassType: typeof ClassType;
GymType: typeof GymType;
UniversityClassType: typeof UniversityClassType;
CompanyPosNames: typeof CompanyPosNames;
};

@ -3,7 +3,7 @@ import { BaseServer } from "../../Server/BaseServer";
import { MessageFilenames, showMessage } from "../../Message/MessageHelpers";
import { showLiterature } from "../../Literature/LiteratureHelpers";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { checkEnum } from "../../utils/helpers/checkEnum";
import { checkEnum } from "../../utils/helpers/enum";
export function cat(args: (string | number | boolean)[], server: BaseServer): void {
if (args.length !== 1) {

@ -10,110 +10,67 @@ import { Player } from "@player";
import { calculateClassEarnings as calculateClassEarningsRate } from "./Formulas";
import { Work, WorkType } from "./Work";
import { applyWorkStats, newWorkStats, sumWorkStats, WorkStats } from "./WorkStats";
import { GymType, UniversityClassType } from "../utils/enums";
import { checkEnum, findEnumMember } from "../utils/helpers/enum";
export enum ClassType {
StudyComputerScience = "STUDYCOMPUTERSCIENCE",
DataStructures = "DATASTRUCTURES",
Networks = "NETWORKS",
Algorithms = "ALGORITHMS",
Management = "MANAGEMENT",
Leadership = "LEADERSHIP",
GymStrength = "GYMSTRENGTH",
GymDefense = "GYMDEFENSE",
GymDexterity = "GYMDEXTERITY",
GymAgility = "GYMAGILITY",
}
export type ClassType = UniversityClassType | GymType;
export interface Class {
youAreCurrently: string;
type: ClassType;
youAreCurrently: string;
earnings: WorkStats;
}
export const Classes: Record<ClassType, Class> = {
[ClassType.StudyComputerScience]: {
youAreCurrently: "studying Computer Science",
type: ClassType.StudyComputerScience,
[UniversityClassType.computerScience]: {
type: UniversityClassType.computerScience,
youAreCurrently: `studying Computer Science`,
earnings: newWorkStats({ hackExp: 0.5, intExp: 0.01 }),
},
[ClassType.DataStructures]: {
[UniversityClassType.dataStructures]: {
type: UniversityClassType.dataStructures,
youAreCurrently: "taking a Data Structures course",
type: ClassType.DataStructures,
earnings: newWorkStats({
money: -40,
hackExp: 1,
intExp: 0.01,
}),
earnings: newWorkStats({ money: -40, hackExp: 1, intExp: 0.01 }),
},
[ClassType.Networks]: {
[UniversityClassType.networks]: {
type: UniversityClassType.networks,
youAreCurrently: "taking a Networks course",
type: ClassType.Networks,
earnings: newWorkStats({
money: -80,
hackExp: 2,
intExp: 0.01,
}),
earnings: newWorkStats({ money: -80, hackExp: 2, intExp: 0.01 }),
},
[ClassType.Algorithms]: {
[UniversityClassType.algorithms]: {
type: UniversityClassType.algorithms,
youAreCurrently: "taking an Algorithms course",
type: ClassType.Algorithms,
earnings: newWorkStats({
money: -320,
hackExp: 4,
intExp: 0.01,
}),
earnings: newWorkStats({ money: -320, hackExp: 4, intExp: 0.01 }),
},
[ClassType.Management]: {
[UniversityClassType.management]: {
type: UniversityClassType.management,
youAreCurrently: "taking a Management course",
type: ClassType.Management,
earnings: newWorkStats({
money: -160,
chaExp: 2,
intExp: 0.01,
}),
earnings: newWorkStats({ money: -160, chaExp: 2, intExp: 0.01 }),
},
[ClassType.Leadership]: {
[UniversityClassType.leadership]: {
type: UniversityClassType.leadership,
youAreCurrently: "taking a Leadership course",
type: ClassType.Leadership,
earnings: newWorkStats({
money: -320,
chaExp: 4,
intExp: 0.01,
}),
earnings: newWorkStats({ money: -320, chaExp: 4, intExp: 0.01 }),
},
[ClassType.GymStrength]: {
[GymType.strength]: {
type: GymType.strength,
youAreCurrently: "training your strength at a gym",
type: ClassType.GymStrength,
earnings: newWorkStats({
money: -120,
strExp: 1,
}),
earnings: newWorkStats({ money: -120, strExp: 1 }),
},
[ClassType.GymDefense]: {
[GymType.defense]: {
type: GymType.defense,
youAreCurrently: "training your defense at a gym",
type: ClassType.GymDefense,
earnings: newWorkStats({
money: -120,
defExp: 1,
}),
earnings: newWorkStats({ money: -120, defExp: 1 }),
},
[ClassType.GymDexterity]: {
[GymType.dexterity]: {
type: GymType.dexterity,
youAreCurrently: "training your dexterity at a gym",
type: ClassType.GymDexterity,
earnings: newWorkStats({
money: -120,
dexExp: 1,
}),
earnings: newWorkStats({ money: -120, dexExp: 1 }),
},
[ClassType.GymAgility]: {
[GymType.agility]: {
type: GymType.agility,
youAreCurrently: "training your agility at a gym",
type: ClassType.GymAgility,
earnings: newWorkStats({
money: -120,
agiExp: 1,
}),
earnings: newWorkStats({ money: -120, agiExp: 1 }),
},
};
@ -132,14 +89,12 @@ export class ClassWork extends Work {
constructor(params?: ClassWorkParams) {
super(WorkType.CLASS, params?.singularity ?? true);
this.classType = params?.classType ?? ClassType.StudyComputerScience;
this.classType = params?.classType ?? UniversityClassType.computerScience;
this.location = params?.location ?? LocationName.Sector12RothmanUniversity;
}
isGym(): boolean {
return [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymStrength].includes(
this.classType,
);
return checkEnum(GymType, this.classType);
}
getClass(): Class {
@ -195,7 +150,12 @@ export class ClassWork extends Work {
/** Initializes a ClassWork object from a JSON save state. */
static fromJSON(value: IReviverValue): ClassWork {
return Generic_fromJSON(ClassWork, value.data);
const classWork = Generic_fromJSON(ClassWork, value.data);
classWork.classType =
findEnumMember(UniversityClassType, classWork.classType) ??
findEnumMember(GymType, classWork.classType) ??
UniversityClassType.computerScience;
return classWork;
}
}

@ -5,11 +5,11 @@ import { determineCrimeSuccess } from "../Crime/CrimeHelpers";
import { Crimes } from "../Crime/Crimes";
import { Player } from "@player";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { CrimeType } from "../utils/WorkType";
import { CrimeType } from "../utils/enums";
import { Work, WorkType } from "./Work";
import { scaleWorkStats, WorkStats } from "./WorkStats";
import { calculateCrimeWorkStats } from "./Formulas";
import { checkEnum } from "../utils/helpers/checkEnum";
import { checkEnum } from "../utils/helpers/enum";
interface CrimeWorkParams {
crimeType: CrimeType;
@ -24,7 +24,7 @@ export class CrimeWork extends Work {
constructor(params?: CrimeWorkParams) {
super(WorkType.CRIME, params?.singularity ?? true);
this.crimeType = params?.crimeType ?? CrimeType.SHOPLIFT;
this.crimeType = params?.crimeType ?? CrimeType.shoplift;
this.unitCompleted = 0;
}
@ -89,7 +89,7 @@ export class CrimeWork extends Work {
return {
type: this.type,
cyclesWorked: this.cyclesWorked,
crimeType: checkEnum(CrimeType, this.crimeType) ? this.crimeType : CrimeType.SHOPLIFT,
crimeType: checkEnum(CrimeType, this.crimeType) ? this.crimeType : CrimeType.shoplift,
};
}

@ -11,7 +11,7 @@ import { Reputation } from "../ui/React/Reputation";
import { CONSTANTS } from "../Constants";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { calculateFactionExp, calculateFactionRep } from "./Formulas";
import { FactionWorkType } from "./data/FactionWorkType";
import { FactionWorkType } from "../utils/enums";
interface FactionWorkParams {
singularity: boolean;
@ -27,7 +27,7 @@ export class FactionWork extends Work {
constructor(params?: FactionWorkParams) {
super(WorkType.FACTION, params?.singularity ?? true);
this.factionWorkType = params?.factionWorkType ?? FactionWorkType.HACKING;
this.factionWorkType = params?.factionWorkType ?? FactionWorkType.hacking;
this.factionName = params?.faction ?? FactionNames.Sector12;
}
@ -92,7 +92,9 @@ export class FactionWork extends Work {
/** Initializes a FactionWork object from a JSON save state. */
static fromJSON(value: IReviverValue): FactionWork {
return Generic_fromJSON(FactionWork, value.data);
const factionWork = Generic_fromJSON(FactionWork, value.data);
factionWork.factionWorkType = factionWork.factionWorkType.toLowerCase() as FactionWorkType;
return factionWork;
}
}

@ -3,7 +3,7 @@ import { Crime } from "../Crime/Crime";
import { newWorkStats, scaleWorkStats, WorkStats, multWorkStats } from "./WorkStats";
import { Person as IPerson } from "../ScriptEditor/NetscriptDefinitions";
import { CONSTANTS } from "../Constants";
import { FactionWorkType } from "./data/FactionWorkType";
import { FactionWorkType, GymType } from "../utils/enums";
import {
getFactionFieldWorkRepGain,
getFactionSecurityWorkRepGain,
@ -19,11 +19,12 @@ import { serverMetadata } from "../Server/data/servers";
import { LocationName } from "../Locations/data/LocationNames";
import { Company } from "../Company/Company";
import { CompanyPosition } from "../Company/CompanyPosition";
import { checkEnum } from "../utils/helpers/enum";
const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second
export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
[FactionWorkType.HACKING]: newWorkStats({ hackExp: 15 }),
[FactionWorkType.FIELD]: newWorkStats({
[FactionWorkType.hacking]: newWorkStats({ hackExp: 15 }),
[FactionWorkType.field]: newWorkStats({
hackExp: 10,
strExp: 10,
defExp: 10,
@ -31,7 +32,7 @@ export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
agiExp: 10,
chaExp: 10,
}),
[FactionWorkType.SECURITY]: newWorkStats({
[FactionWorkType.security]: newWorkStats({
hackExp: 5,
strExp: 15,
defExp: 15,
@ -66,9 +67,9 @@ export function calculateCrimeWorkStats(person: IPerson, crime: Crime): WorkStat
/** @returns faction rep rate per cycle */
export const calculateFactionRep = (person: IPerson, type: FactionWorkType, favor: number): number => {
const repFormulas = {
[FactionWorkType.HACKING]: getHackingWorkRepGain,
[FactionWorkType.FIELD]: getFactionFieldWorkRepGain,
[FactionWorkType.SECURITY]: getFactionSecurityWorkRepGain,
[FactionWorkType.hacking]: getHackingWorkRepGain,
[FactionWorkType.field]: getFactionFieldWorkRepGain,
[FactionWorkType.security]: getFactionSecurityWorkRepGain,
};
return repFormulas[type](person, favor);
};
@ -95,11 +96,7 @@ export function calculateClassEarnings(person: IPerson, type: ClassType, locatio
const classs = Classes[type];
const location = Locations[locationName];
const hashMult = [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymStrength, ClassType.GymDexterity].includes(
type,
)
? hashManager.getTrainingMult()
: hashManager.getStudyMult();
const hashMult = checkEnum(GymType, type) ? hashManager.getTrainingMult() : hashManager.getStudyMult();
const earnings = multWorkStats(
scaleWorkStats(classs.earnings, (location.expMult / gameCPS) * hashMult, false),

@ -1,5 +0,0 @@
export enum FactionWorkType {
HACKING = "HACKING",
FIELD = "FIELD",
SECURITY = "SECURITY",
}

@ -1,20 +1,13 @@
/** Construct a type using the values from an object. Requires object to be defined "as const" */
export type ValuesFrom<T> = T[keyof T];
/**
* Status object for functions that return a boolean indicating success/failure
* and an optional message
*/
/** Status object for functions that return a boolean indicating success/failure
* and an optional message */
export interface IReturnStatus {
res: boolean;
msg?: string;
}
/**
* Defines the minimum and maximum values for a range.
/** Defines the minimum and maximum values for a range.
* It is up to the consumer if these values are inclusive or exclusive.
* It is up to the implementor to ensure max > min.
*/
* It is up to the implementor to ensure max > min. */
export interface IMinMaxRange {
/** Value by which the bounds are to be divided for the final range */
divisor?: number;

@ -24,7 +24,7 @@ import { WorkStats } from "../Work/WorkStats";
import { isCreateProgramWork } from "../Work/CreateProgramWork";
import { isGraftingWork } from "../Work/GraftingWork";
import { isFactionWork } from "../Work/FactionWork";
import { FactionWorkType } from "../Work/data/FactionWorkType";
import { FactionWorkType } from "../utils/enums";
import { isCompanyWork } from "../Work/CompanyWork";
const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle;
@ -394,9 +394,9 @@ export function WorkInProgressRoot(): React.ReactElement {
}
const description = {
[FactionWorkType.HACKING]: "carrying out hacking contracts",
[FactionWorkType.FIELD]: "carrying out field missions",
[FactionWorkType.SECURITY]: "performing security detail",
[FactionWorkType.hacking]: "carrying out hacking contracts",
[FactionWorkType.field]: "carrying out field missions",
[FactionWorkType.security]: "performing security detail",
};
const exp = Player.currentWork.getExpRates();

@ -1,18 +1,3 @@
export enum CrimeType {
SHOPLIFT = "SHOPLIFT", //"shoplift",
ROB_STORE = "ROBSTORE", //"rob a store",
MUG = "MUG", //"mug someone",
LARCENY = "LARCENY", //"commit larceny",
DRUGS = "DRUGS", //"deal drugs",
BOND_FORGERY = "BONDFORGERY", //"forge corporate bonds",
TRAFFIC_ARMS = "TRAFFICKARMS", //"traffick illegal arms",
HOMICIDE = "HOMICIDE", //"commit homicide",
GRAND_THEFT_AUTO = "GRANDTHEFTAUTO", //"commit grand theft auto",
KIDNAP = "KIDNAP", //"kidnap someone for ransom",
ASSASSINATION = "ASSASSINATION", //"assassinate a high-profile target",
HEIST = "HEIST", //"pull off the ultimate heist",
}
export enum CompanyPosNames {
sw0 = "Software Engineering Intern",
sw1 = "Junior Software Engineer",
@ -53,3 +38,41 @@ export enum CompanyPosNames {
waiterPT = "Part-time Waiter",
employeePT = "Part-time Employee",
}
export enum CrimeType {
shoplift = "Shoplift",
robStore = "Rob Store",
mug = "Mug",
larceny = "Larceny",
dealDrugs = "Deal Drugs",
bondForgery = "Bond Forgery",
traffickArms = "Traffick Arms",
homicide = "Homicide",
grandTheftAuto = "Grand Theft Auto",
kidnap = "Kidnap",
assassination = "Assassination",
heist = "Heist",
}
export enum FactionWorkType {
hacking = "hacking",
field = "field",
security = "security",
}
export enum UniversityClassType {
computerScience = "Computer Science",
dataStructures = "Data Structures",
networks = "Networks",
algorithms = "Algorithms",
management = "Management",
leadership = "Leadership",
}
//Uses skill short codes to allow easier fuzzy matching with player input
export enum GymType {
strength = "str",
defense = "def",
dexterity = "dex",
agility = "agi",
}

@ -1,4 +0,0 @@
/** Verifies that a supplied value is a member of the provided object/enum. Works for enums as well as enum-like objects (const {} as const). */
export function checkEnum<T extends Record<string, unknown>>(obj: T, value: unknown): value is T[keyof T] {
return Object.values(obj).includes(value);
}

11
src/utils/helpers/enum.ts Normal file

@ -0,0 +1,11 @@
/** Verifies that a supplied value is a member of the provided object/enum. Works for enums as well as enum-like objects (const {} as const). */
export function checkEnum<T extends Record<string, unknown>>(obj: T, value: unknown): value is T[keyof T] {
return Object.values(obj).includes(value);
}
export function findEnumMember<T extends Record<string, string>>(obj: T, value: string): T[keyof T] | undefined {
const lowerValue = value.toLowerCase().replace(/ /g, "");
for (const member of Object.values(obj) as T[keyof T][]) {
if (lowerValue.includes(member.toLowerCase().replace(/ /g, ""))) return member;
}
}