diff --git a/doc/source/v2.0.0_migration.rst b/doc/source/v2.0.0_migration.rst index e8bba2d80..97777cfe4 100644 --- a/doc/source/v2.0.0_migration.rst +++ b/doc/source/v2.0.0_migration.rst @@ -95,10 +95,10 @@ Singularity This means calls like 'ns.connect' need to be changed to 'ns.singularity.connect' -stock.buy and stock.sell ------------------------- +stock.buy, stock.sell, stock.short +---------------------------------- - These 2 functions were renamed to stock.buyStock and stock.sellStock because 'buy' and 'sell' + These functions were renamed to stock.buyStock, stock.sellStock, and stock.buyShort because 'buy', 'sell', and 'short' are very common tokens that would trick the ram calculation. corporation.bribe diff --git a/src/Bladeburner/Bladeburner.tsx b/src/Bladeburner/Bladeburner.tsx index 73b6c21b1..2b76fdde2 100644 --- a/src/Bladeburner/Bladeburner.tsx +++ b/src/Bladeburner/Bladeburner.tsx @@ -1452,7 +1452,7 @@ export class Bladeburner implements IBladeburner { } } } catch (e: unknown) { - exceptionAlert(e); + exceptionAlert(String(e)); } break; } diff --git a/src/Bladeburner/data/GeneralActions.tsx b/src/Bladeburner/data/GeneralActions.tsx index 3cc393612..1725b21cf 100644 --- a/src/Bladeburner/data/GeneralActions.tsx +++ b/src/Bladeburner/data/GeneralActions.tsx @@ -1,11 +1,13 @@ import React from "react"; +import { newWorkStats, WorkStats } from "../../Work/WorkStats"; -interface IContract { +interface IGeneral { desc: JSX.Element; + exp: WorkStats; } export const GeneralActions: { - [key: string]: IContract | undefined; + [key: string]: IGeneral | undefined; } = { Training: { desc: ( @@ -14,6 +16,12 @@ export const GeneralActions: { all combat stats and also increases your max stamina. ), + exp: newWorkStats({ + strExp: 30, + defExp: 30, + dexExp: 30, + agiExp: 30, + }), }, "Field Analysis": { @@ -27,6 +35,10 @@ export const GeneralActions: { Does NOT require stamina. ), + exp: newWorkStats({ + hackExp: 20, + chaExp: 20, + }), }, Recruitment: { @@ -38,6 +50,9 @@ export const GeneralActions: { Does NOT require stamina. ), + exp: newWorkStats({ + chaExp: 120, + }), }, Diplomacy: { @@ -50,6 +65,9 @@ export const GeneralActions: { Does NOT require stamina. ), + exp: newWorkStats({ + chaExp: 120, + }), }, "Hyperbolic Regeneration Chamber": { @@ -61,6 +79,7 @@ export const GeneralActions: {
), + exp: newWorkStats(), }, "Incite Violence": { desc: ( @@ -69,5 +88,12 @@ export const GeneralActions: { additional contracts and operations, at the cost of increased Chaos. ), + exp: newWorkStats({ + strExp: 10, + defExp: 10, + dexExp: 10, + agiExp: 10, + chaExp: 10, + }), }, }; diff --git a/src/Crime/Crimes.ts b/src/Crime/Crimes.ts index 66d100d62..407eb85ad 100644 --- a/src/Crime/Crimes.ts +++ b/src/Crime/Crimes.ts @@ -6,7 +6,7 @@ import { IMap } from "../types"; import { CrimeType } from "../utils/WorkType"; export const Crimes: IMap = { - Shoplift: new Crime("Shoplift", CrimeType.Shoplift, 2e3, 15e3, 1 / 20, 0.1, { + Shoplift: new Crime("Shoplift", CrimeType.SHOPLIFT, 2e3, 15e3, 1 / 20, 0.1, { dexterity_success_weight: 1, agility_success_weight: 1, @@ -14,7 +14,7 @@ export const Crimes: IMap = { agility_exp: 2, }), - RobStore: new Crime("Rob Store", CrimeType.RobStore, 60e3, 400e3, 1 / 5, 0.5, { + RobStore: new Crime("Rob Store", CrimeType.ROB_STORE, 60e3, 400e3, 1 / 5, 0.5, { hacking_exp: 30, dexterity_exp: 45, agility_exp: 45, @@ -26,7 +26,7 @@ export const Crimes: IMap = { intelligence_exp: 7.5 * CONSTANTS.IntelligenceCrimeBaseExpGain, }), - Mug: new Crime("Mug", CrimeType.Mug, 4e3, 36e3, 1 / 5, 0.25, { + Mug: new Crime("Mug", CrimeType.MUG, 4e3, 36e3, 1 / 5, 0.25, { strength_exp: 3, defense_exp: 3, dexterity_exp: 3, @@ -38,7 +38,7 @@ export const Crimes: IMap = { agility_success_weight: 0.5, }), - Larceny: new Crime("Larceny", CrimeType.Larceny, 90e3, 800e3, 1 / 3, 1.5, { + Larceny: new Crime("Larceny", CrimeType.LARCENY, 90e3, 800e3, 1 / 3, 1.5, { hacking_exp: 45, dexterity_exp: 60, agility_exp: 60, @@ -50,7 +50,7 @@ export const Crimes: IMap = { intelligence_exp: 15 * CONSTANTS.IntelligenceCrimeBaseExpGain, }), - DealDrugs: new Crime("Deal Drugs", CrimeType.Drugs, 10e3, 120e3, 1, 0.5, { + DealDrugs: new Crime("Deal Drugs", CrimeType.DRUGS, 10e3, 120e3, 1, 0.5, { dexterity_exp: 5, agility_exp: 5, charisma_exp: 10, @@ -60,7 +60,7 @@ export const Crimes: IMap = { agility_success_weight: 1, }), - BondForgery: new Crime("Bond Forgery", CrimeType.BondForgery, 300e3, 4.5e6, 1 / 2, 0.1, { + BondForgery: new Crime("Bond Forgery", CrimeType.BOND_FORGERY, 300e3, 4.5e6, 1 / 2, 0.1, { hacking_exp: 100, dexterity_exp: 150, charisma_exp: 15, @@ -71,7 +71,7 @@ export const Crimes: IMap = { intelligence_exp: 60 * CONSTANTS.IntelligenceCrimeBaseExpGain, }), - TraffickArms: new Crime("Traffick Arms", CrimeType.TraffickArms, 40e3, 600e3, 2, 1, { + TraffickArms: new Crime("Traffick Arms", CrimeType.TRAFFIC_ARMS, 40e3, 600e3, 2, 1, { strength_exp: 20, defense_exp: 20, dexterity_exp: 20, @@ -85,7 +85,7 @@ export const Crimes: IMap = { agility_success_weight: 1, }), - Homicide: new Crime("Homicide", CrimeType.Homicide, 3e3, 45e3, 1, 3, { + Homicide: new Crime("Homicide", CrimeType.HOMICIDE, 3e3, 45e3, 1, 3, { strength_exp: 2, defense_exp: 2, dexterity_exp: 2, @@ -99,7 +99,7 @@ export const Crimes: IMap = { kills: 1, }), - GrandTheftAuto: new Crime("Grand Theft Auto", CrimeType.GrandTheftAuto, 80e3, 1.6e6, 8, 5, { + GrandTheftAuto: new Crime("Grand Theft Auto", CrimeType.GRAND_THEFT_AUTO, 80e3, 1.6e6, 8, 5, { strength_exp: 20, defense_exp: 20, dexterity_exp: 20, @@ -115,7 +115,7 @@ export const Crimes: IMap = { intelligence_exp: 16 * CONSTANTS.IntelligenceCrimeBaseExpGain, }), - Kidnap: new Crime("Kidnap", CrimeType.Kidnap, 120e3, 3.6e6, 5, 6, { + Kidnap: new Crime("Kidnap", CrimeType.KIDNAP, 120e3, 3.6e6, 5, 6, { strength_exp: 80, defense_exp: 80, dexterity_exp: 80, @@ -130,7 +130,7 @@ export const Crimes: IMap = { intelligence_exp: 26 * CONSTANTS.IntelligenceCrimeBaseExpGain, }), - Assassination: new Crime("Assassination", CrimeType.Assassination, 300e3, 12e6, 8, 10, { + Assassination: new Crime("Assassination", CrimeType.ASSASSINATION, 300e3, 12e6, 8, 10, { strength_exp: 300, defense_exp: 300, dexterity_exp: 300, @@ -145,7 +145,7 @@ export const Crimes: IMap = { kills: 1, }), - Heist: new Crime("Heist", CrimeType.Heist, 600e3, 120e6, 18, 15, { + Heist: new Crime("Heist", CrimeType.HEIST, 600e3, 120e6, 18, 15, { hacking_exp: 450, strength_exp: 450, defense_exp: 450, diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index aac166b3e..80a854886 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -136,7 +136,7 @@ const stock = { getSaleGain: RamCostConstants.ScriptGetStockRamCost, buyStock: RamCostConstants.ScriptBuySellStockRamCost, sellStock: RamCostConstants.ScriptBuySellStockRamCost, - short: RamCostConstants.ScriptBuySellStockRamCost, + buyShort: RamCostConstants.ScriptBuySellStockRamCost, sellShort: RamCostConstants.ScriptBuySellStockRamCost, placeOrder: RamCostConstants.ScriptBuySellStockRamCost, cancelOrder: RamCostConstants.ScriptBuySellStockRamCost, diff --git a/src/NetscriptFunctions/Sleeve.ts b/src/NetscriptFunctions/Sleeve.ts index a6b88e580..001fb863c 100644 --- a/src/NetscriptFunctions/Sleeve.ts +++ b/src/NetscriptFunctions/Sleeve.ts @@ -180,20 +180,14 @@ export function NetscriptSleeve(player: IPlayer): InternalAPI { }, getTask: (ctx: NetscriptContext) => - (_sleeveNumber: unknown): SleeveTask => { + (_sleeveNumber: unknown): SleeveTask | null => { const sleeveNumber = ctx.helper.number("sleeveNumber", _sleeveNumber); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); const sl = player.sleeves[sleeveNumber]; - return { - task: SleeveTaskType[sl.currentTask], - crime: sl.crimeType, - location: sl.currentTaskLocation, - gymStatType: sl.gymStatType, - factionWorkType: FactionWorkType[sl.factionWorkType], - className: sl.className, - }; + if (sl.currentWork === null) return null; + return sl.currentWork.APICopy(); }, getInformation: (ctx: NetscriptContext) => diff --git a/src/NetscriptFunctions/StockMarket.ts b/src/NetscriptFunctions/StockMarket.ts index 26ab02e37..041450038 100644 --- a/src/NetscriptFunctions/StockMarket.ts +++ b/src/NetscriptFunctions/StockMarket.ts @@ -165,7 +165,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript return res ? stock.getBidPrice() : 0; }, - short: + buyShort: (ctx: NetscriptContext) => (_symbol: unknown, _shares: unknown): number => { const symbol = ctx.helper.string("symbol", _symbol); diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 0088a7acc..879d8593e 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -35,7 +35,7 @@ import { LocationName } from "../../Locations/data/LocationNames"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../utils/JSONReviver"; import { BladeburnerConstants } from "../../Bladeburner/data/Constants"; import { numeralWrapper } from "../../ui/numeralFormat"; -import { capitalizeFirstLetter, capitalizeEachWord } from "../../utils/StringHelperFunctions"; +import { capitalizeFirstLetter } from "../../utils/StringHelperFunctions"; import { FactionWorkType } from "../../Work/data/FactionWorkType"; import { Work } from "./Work/Work"; import { SleeveClassWork } from "./Work/SleeveClassWork"; @@ -44,8 +44,10 @@ import { SleeveSynchroWork } from "./Work/SleeveSynchroWork"; import { SleeveRecoveryWork } from "./Work/SleeveRecoveryWork"; import { SleeveFactionWork } from "./Work/SleeveFactionWork"; import { SleeveCompanyWork } from "./Work/SleeveCompanyWork"; -import { SleeveBladeburnerGeneralWork } from "./Work/SleeveBladeburnerGeneralActionWork"; import { SleeveInfiltrateWork } from "./Work/SleeveInfiltrateWork"; +import { SleeveSupportWork } from "./Work/SleeveSupportWork"; +import { SleeveBladeburnerWork } from "./Work/SleeveBladeburnerWork"; +import { SleeveCrimeWork } from "./Work/SleeveCrimeWork"; export class Sleeve extends Person { currentWork: Work | null = null; @@ -166,6 +168,16 @@ export class Sleeve extends Person { return this.sync / 100; } + startWork(player: IPlayer, w: Work): void { + if (this.currentWork) this.currentWork.finish(player); + this.currentWork = w; + } + + stopWork(player: IPlayer): void { + if (this.currentWork) this.currentWork.finish(player); + this.currentWork = null; + } + /** * Commit crimes */ @@ -175,26 +187,13 @@ export class Sleeve extends Person { return false; } - if (this.currentTask !== SleeveTaskType.Idle) { + if (this.currentTask !== SleeveTaskType.Idle || this.currentWork === null) { this.finishTask(p); } else { this.resetTaskStatus(p); } - this.gainRatesForTask.hack = crime.hacking_exp * this.mults.hacking_exp * BitNodeMultipliers.CrimeExpGain; - this.gainRatesForTask.str = crime.strength_exp * this.mults.strength_exp * BitNodeMultipliers.CrimeExpGain; - this.gainRatesForTask.def = crime.defense_exp * this.mults.defense_exp * BitNodeMultipliers.CrimeExpGain; - this.gainRatesForTask.dex = crime.dexterity_exp * this.mults.dexterity_exp * BitNodeMultipliers.CrimeExpGain; - this.gainRatesForTask.agi = crime.agility_exp * this.mults.agility_exp * BitNodeMultipliers.CrimeExpGain; - this.gainRatesForTask.cha = crime.charisma_exp * this.mults.charisma_exp * BitNodeMultipliers.CrimeExpGain; - this.gainRatesForTask.int = crime.intelligence_exp; - this.gainRatesForTask.money = crime.money * this.mults.crime_money * BitNodeMultipliers.CrimeMoney; - - this.currentTaskLocation = String(this.gainRatesForTask.money); - - this.crimeType = crime.name; - this.currentTaskMaxTime = crime.time; - this.currentTask = SleeveTaskType.Crime; + this.startWork(p, new SleeveCrimeWork(crime.type)); return true; } @@ -202,89 +201,7 @@ export class Sleeve extends Person { * Called to stop the current task */ finishTask(p: IPlayer): void { - this.currentWork = null; - if (this.currentTask === SleeveTaskType.Crime) { - // For crimes, all experience and money is gained at the end - if (this.currentTaskTime >= this.currentTaskMaxTime) { - const crime: Crime | undefined = Object.values(Crimes).find((crime) => crime.name === this.crimeType); - if (!crime) { - console.error(`Invalid data stored in sleeve.crimeType: ${this.crimeType}`); - this.resetTaskStatus(p); - return; - } - if (Math.random() < crime.successRate(this)) { - // Success - const successGainRates: ITaskTracker = createTaskTracker(); - - const keysForIteration: (keyof ITaskTracker)[] = Object.keys(successGainRates) as (keyof ITaskTracker)[]; - for (let i = 0; i < keysForIteration.length; ++i) { - const key = keysForIteration[i]; - successGainRates[key] = this.gainRatesForTask[key] * 2; - } - this.gainExperience(p, successGainRates); - this.gainMoney(p, this.gainRatesForTask); - - p.karma -= crime.karma * (this.sync / 100); - } else { - this.gainExperience(p, this.gainRatesForTask); - } - - // Do not reset task to IDLE - this.currentTaskTime = 0; - return; - } - } else if (this.currentTask === SleeveTaskType.Bladeburner) { - if (this.currentTaskMaxTime === 0) { - this.currentTaskTime = 0; - return; - } - // For bladeburner, all experience and money is gained at the end - const bb = p.bladeburner; - if (bb === null) { - const errorLogText = `bladeburner is null`; - console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`); - this.resetTaskStatus(p); - return; - } - - if (this.currentTaskTime >= this.currentTaskMaxTime) { - if (this.bbAction === "Infiltrate synthoids") { - bb.infiltrateSynthoidCommunities(p); - this.currentTaskTime = 0; - return; - } - let type: string; - let name: string; - if (this.bbAction === "Take on contracts") { - type = "Contracts"; - name = this.bbContract; - } else { - type = "General"; - name = this.bbAction; - } - - const actionIdent = bb.getActionIdFromTypeAndName(type, name); - if (actionIdent === null) { - const errorLogText = `Invalid action: type='${type}' name='${name}'`; - console.error(`Function: sleeves.finishTask; Message: '${errorLogText}'`); - this.resetTaskStatus(p); - return; - } - - const action = bb.getActionObject(actionIdent); - if ((action?.count ?? 0) > 0) { - const bbRetValue = bb.completeAction(p, this, actionIdent, false); - if (bbRetValue) { - this.gainExperience(p, bbRetValue); - this.gainMoney(p, bbRetValue); - - // Do not reset task to IDLE - this.currentTaskTime = 0; - return; - } - } - } - } + this.stopWork(p); this.resetTaskStatus(p); @@ -557,30 +474,6 @@ export class Sleeve extends Person { // Shock gradually goes towards 100 this.shock = Math.min(100, this.shock + 0.0001 * cyclesUsed); - switch (this.currentTask) { - case SleeveTaskType.Company: { - this.gainExperience(p, this.gainRatesForTask, cyclesUsed); - this.gainMoney(p, this.gainRatesForTask, cyclesUsed); - - const company: Company = Companies[this.currentTaskLocation]; - if (!(company instanceof Company)) { - console.error(`Invalid company for Sleeve task: ${this.currentTaskLocation}`); - break; - } - - company.playerReputation += this.getRepGain(p) * cyclesUsed; - break; - } - } - - if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) { - if (this.currentTask === SleeveTaskType.Crime || this.currentTask === SleeveTaskType.Bladeburner) { - this.finishTask(p); - } else { - this.finishTask(p); - } - } - this.updateStatLevels(); this.storedCycles -= cyclesUsed; @@ -592,7 +485,7 @@ export class Sleeve extends Person { * Resets all parameters used to keep information about the current task */ resetTaskStatus(p: IPlayer): void { - this.currentWork = null; + this.stopWork(p); if (this.bbAction == "Support main sleeve") { p.bladeburner?.sleeveSupport(false); } @@ -616,7 +509,7 @@ export class Sleeve extends Person { } else { this.resetTaskStatus(p); } - this.currentWork = new SleeveRecoveryWork(); + this.startWork(p, new SleeveRecoveryWork()); return true; } @@ -626,7 +519,7 @@ export class Sleeve extends Person { } else { this.resetTaskStatus(p); } - this.currentWork = new SleeveSynchroWork(); + this.startWork(p, new SleeveSynchroWork()); return true; } @@ -686,10 +579,13 @@ export class Sleeve extends Person { } if (!classType) return false; - this.currentWork = new SleeveClassWork({ - classType: classType, - location: loc, - }); + this.startWork( + p, + new SleeveClassWork({ + classType: classType, + location: loc, + }), + ); return true; } @@ -747,7 +643,7 @@ export class Sleeve extends Person { if (company == null) return false; if (companyPosition == null) return false; - this.currentWork = new SleeveCompanyWork({ companyName: companyName }); + this.startWork(p, new SleeveCompanyWork({ companyName: companyName })); return true; } @@ -786,10 +682,13 @@ export class Sleeve extends Person { return false; } - this.currentWork = new SleeveFactionWork({ - factionWorkType: factionWorkType, - factionName: faction.name, - }); + this.startWork( + p, + new SleeveFactionWork({ + factionWorkType: factionWorkType, + factionName: faction.name, + }), + ); return true; } @@ -856,10 +755,13 @@ export class Sleeve extends Person { // if stat is still equals its default value, then validation has failed. if (!classType) return false; - this.currentWork = new SleeveClassWork({ - classType: classType, - location: loc, - }); + this.startWork( + p, + new SleeveClassWork({ + classType: classType, + location: loc, + }), + ); return true; } @@ -883,7 +785,7 @@ export class Sleeve extends Person { this.gainRatesForTask.money = 0; this.currentTaskLocation = ""; - let time = 0; + const time = 0; this.bbContract = "------"; switch (action) { @@ -891,7 +793,7 @@ export class Sleeve extends Person { // time = this.getBladeburnerActionTime(p, "General", action); // this.gainRatesForTask.hack = 20 * this.mults.hacking_exp; // this.gainRatesForTask.cha = 20 * this.mults.charisma_exp; - this.currentWork = new SleeveBladeburnerGeneralWork("Field analysis"); + this.startWork(p, new SleeveBladeburnerWork({ type: "General", name: "Field Analysis" })); return true; case "Recruitment": // time = this.getBladeburnerActionTime(p, "General", action); @@ -900,25 +802,26 @@ export class Sleeve extends Person { // this.currentTaskLocation = `(Success Rate: ${numeralWrapper.formatPercentage( // this.recruitmentSuccessChance(p), // )})`; - this.currentWork = new SleeveBladeburnerGeneralWork("Recruitment"); - break; + this.startWork(p, new SleeveBladeburnerWork({ type: "General", name: "Recruitment" })); + return true; case "Diplomacy": // time = this.getBladeburnerActionTime(p, "General", action); - this.currentWork = new SleeveBladeburnerGeneralWork("Diplomacy"); - break; + this.startWork(p, new SleeveBladeburnerWork({ type: "General", name: "Diplomacy" })); + return true; case "Infiltrate synthoids": - this.currentWork = new SleeveInfiltrateWork(); - break; + this.startWork(p, new SleeveInfiltrateWork()); + return true; case "Support main sleeve": - p.bladeburner?.sleeveSupport(true); - time = 0; - break; + this.startWork(p, new SleeveSupportWork(p)); + return true; case "Take on contracts": - time = this.getBladeburnerActionTime(p, "Contracts", contract); - this.contractGainRates(p, "Contracts", contract); - this.currentTaskLocation = this.contractSuccessChance(p, "Contracts", contract); - this.bbContract = capitalizeEachWord(contract.toLowerCase()); - break; + this.startWork(p, new SleeveBladeburnerWork({ type: "Contracts", name: contract })); + return true; + // time = this.getBladeburnerActionTime(p, "Contracts", contract); + // this.contractGainRates(p, "Contracts", contract); + // this.currentTaskLocation = this.contractSuccessChance(p, "Contracts", contract); + // this.bbContract = capitalizeEachWord(contract.toLowerCase()); + // break; } this.bbAction = capitalizeFirstLetter(action.toLowerCase()); diff --git a/src/PersonObjects/Sleeve/Work/SleeveBladeburnerGeneralActionWork.ts b/src/PersonObjects/Sleeve/Work/SleeveBladeburnerGeneralActionWork.ts deleted file mode 100644 index 7297a3455..000000000 --- a/src/PersonObjects/Sleeve/Work/SleeveBladeburnerGeneralActionWork.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { IPlayer } from "../../IPlayer"; -import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver"; -import { Sleeve } from "../Sleeve"; -import { Work, WorkType } from "./Work"; -import { CONSTANTS } from "../../../Constants"; - -export const isSleeveBladeburnerGeneralWork = (w: Work | null): w is SleeveBladeburnerGeneralWork => - w !== null && w.type === WorkType.BLADEBURNER_GENERAL; - -export class SleeveBladeburnerGeneralWork extends Work { - cyclesWorked = 0; - action: string; - - constructor(action?: string) { - super(WorkType.BLADEBURNER_GENERAL); - this.action = action ?? "Field analysis"; - } - - cyclesNeeded(player: IPlayer, sleeve: Sleeve): number { - const ret = player.bladeburner?.getActionTimeNetscriptFn(sleeve, "General", this.action); - if (!ret || typeof ret === "string") throw new Error(`Error querying ${this.action} time`); - return ret / CONSTANTS._idleSpeed; - } - - process(player: IPlayer, sleeve: Sleeve, cycles: number): number { - if (!player.bladeburner) throw new Error("sleeve doing blade work without being a member"); - this.cyclesWorked += cycles; - while (this.cyclesWorked > this.cyclesNeeded(player, sleeve)) { - const actionIdent = player.bladeburner.getActionIdFromTypeAndName("General", this.action); - if (!actionIdent) throw new Error(`Error getting ${this.action} action`); - player.bladeburner.completeAction(player, sleeve, actionIdent, false); - this.cyclesWorked -= this.cyclesNeeded(player, sleeve); - } - return 0; - } - - APICopy(): Record { - return { - type: this.type, - action: this.action, - }; - } - - /** - * Serialize the current object to a JSON save state. - */ - toJSON(): IReviverValue { - return Generic_toJSON("SleeveBladeburnerGeneralWork", this); - } - - /** - * Initiatizes a BladeburnerWork object from a JSON save state. - */ - static fromJSON(value: IReviverValue): SleeveBladeburnerGeneralWork { - return Generic_fromJSON(SleeveBladeburnerGeneralWork, value.data); - } -} - -Reviver.constructors.SleeveBladeburnerGeneralWork = SleeveBladeburnerGeneralWork; diff --git a/src/PersonObjects/Sleeve/Work/SleeveBladeburnerWork.ts b/src/PersonObjects/Sleeve/Work/SleeveBladeburnerWork.ts new file mode 100644 index 000000000..62b74d908 --- /dev/null +++ b/src/PersonObjects/Sleeve/Work/SleeveBladeburnerWork.ts @@ -0,0 +1,75 @@ +import { IPlayer } from "../../IPlayer"; +import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver"; +import { Sleeve } from "../Sleeve"; +import { Work, WorkType } from "./Work"; +import { CONSTANTS } from "../../../Constants"; +import { GeneralActions } from "../../../Bladeburner/data/GeneralActions"; +import { applyWorkStatsExp, WorkStats } from "../../../Work/WorkStats"; +import { Contracts } from "src/Bladeburner/data/Contracts"; + +interface SleeveBladeburnerWorkParams { + type: string; + name: string; +} + +export const isSleeveBladeburnerWork = (w: Work | null): w is SleeveBladeburnerWork => + w !== null && w.type === WorkType.BLADEBURNER; + +export class SleeveBladeburnerWork extends Work { + cyclesWorked = 0; + actionType: string; + actionName: string; + + constructor(params?: SleeveBladeburnerWorkParams) { + super(WorkType.BLADEBURNER); + this.actionType = params?.type ?? "General"; + this.actionName = params?.name ?? "Field analysis"; + } + + cyclesNeeded(player: IPlayer, sleeve: Sleeve): number { + const ret = player.bladeburner?.getActionTimeNetscriptFn(sleeve, this.actionType, this.actionName); + if (!ret || typeof ret === "string") throw new Error(`Error querying ${this.actionName} time`); + return ret / CONSTANTS._idleSpeed; + } + + process(player: IPlayer, sleeve: Sleeve, cycles: number): number { + if (!player.bladeburner) throw new Error("sleeve doing blade work without being a member"); + this.cyclesWorked += cycles; + while (this.cyclesWorked > this.cyclesNeeded(player, sleeve)) { + const actionIdent = player.bladeburner.getActionIdFromTypeAndName(this.actionType, this.actionName); + if (!actionIdent) throw new Error(`Error getting ${this.actionName} action`); + player.bladeburner.completeAction(player, sleeve, actionIdent, false); + let exp: WorkStats | undefined; + if (this.actionType === "General") { + exp = GeneralActions[this.actionName]?.exp; + if (!exp) throw new Error(`Somehow there was no exp for action ${this.actionType} ${this.actionName}`); + applyWorkStatsExp(sleeve, exp, 1); + } + this.cyclesWorked -= this.cyclesNeeded(player, sleeve); + } + return 0; + } + + APICopy(): Record { + return { + actionType: this.actionType, + actionName: this.actionName, + }; + } + + /** + * Serialize the current object to a JSON save state. + */ + toJSON(): IReviverValue { + return Generic_toJSON("SleeveBladeburnerWork", this); + } + + /** + * Initiatizes a BladeburnerWork object from a JSON save state. + */ + static fromJSON(value: IReviverValue): SleeveBladeburnerWork { + return Generic_fromJSON(SleeveBladeburnerWork, value.data); + } +} + +Reviver.constructors.SleeveBladeburnerWork = SleeveBladeburnerWork; diff --git a/src/PersonObjects/Sleeve/Work/SleeveCrimeWork.ts b/src/PersonObjects/Sleeve/Work/SleeveCrimeWork.ts new file mode 100644 index 000000000..e86e7ab5d --- /dev/null +++ b/src/PersonObjects/Sleeve/Work/SleeveCrimeWork.ts @@ -0,0 +1,80 @@ +import { IPlayer } from "../../IPlayer"; +import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver"; +import { Sleeve } from "../Sleeve"; +import { Work, WorkType } from "./Work"; +import { CrimeType } from "../../../utils/WorkType"; +import { Crimes } from "../../../Crime/Crimes"; +import { Crime } from "../../../Crime/Crime"; +import { applyWorkStats, newWorkStats, scaleWorkStats, WorkStats } from "../../../Work/WorkStats"; +import { CONSTANTS } from "../../../Constants"; + +export const isSleeveCrimeWork = (w: Work | null): w is SleeveCrimeWork => w !== null && w.type === WorkType.CRIME; + +export class SleeveCrimeWork extends Work { + crimeType: CrimeType; + cyclesWorked = 0; + constructor(crimeType?: CrimeType) { + super(WorkType.CRIME); + this.crimeType = crimeType ?? CrimeType.SHOPLIFT; + } + + 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; + } + + getExp(): WorkStats { + const crime = this.getCrime(); + return newWorkStats({ + money: crime.money, + hackExp: crime.hacking_exp, + strExp: crime.strength_exp, + defExp: crime.defense_exp, + dexExp: crime.dexterity_exp, + agiExp: crime.agility_exp, + chaExp: crime.charisma_exp, + intExp: crime.intelligence_exp, + }); + } + + process(player: IPlayer, sleeve: Sleeve, cycles: number): number { + this.cyclesWorked += cycles; + + const crime = this.getCrime(); + const gains = this.getExp(); + if (this.cyclesWorked >= crime.time / CONSTANTS._idleSpeed) { + if (Math.random() < crime.successRate(sleeve)) { + applyWorkStats(player, sleeve, gains, 1, "sleeves"); + + player.karma -= crime.karma * sleeve.syncBonus(); + } else { + applyWorkStats(player, sleeve, scaleWorkStats(gains, 0.25), 1, "sleeves"); + } + this.cyclesWorked -= crime.time / CONSTANTS._idleSpeed; + } + return 0; + } + + APICopy(): Record { + return { + type: this.type, + }; + } + + /** + * Serialize the current object to a JSON save state. + */ + toJSON(): IReviverValue { + return Generic_toJSON("SleeveCrimeWork", this); + } + + /** + * Initiatizes a RecoveryWork object from a JSON save state. + */ + static fromJSON(value: IReviverValue): SleeveCrimeWork { + return Generic_fromJSON(SleeveCrimeWork, value.data); + } +} + +Reviver.constructors.SleeveCrimeWork = SleeveCrimeWork; diff --git a/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts b/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts index a372c3aaa..ec732cdb5 100644 --- a/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts +++ b/src/PersonObjects/Sleeve/Work/SleeveFactionWork.ts @@ -55,7 +55,7 @@ export class SleeveFactionWork extends Work { process(player: IPlayer, sleeve: Sleeve, cycles: number): number { if (player.gang) { if (this.factionName === player.gang.facName) { - sleeve.currentWork = null; + sleeve.stopWork(player); return 0; } } diff --git a/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts b/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts index db5a08a93..5f0de0a5d 100644 --- a/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts +++ b/src/PersonObjects/Sleeve/Work/SleeveRecoveryWork.ts @@ -13,7 +13,7 @@ export class SleeveRecoveryWork extends Work { process(player: IPlayer, sleeve: Sleeve, cycles: number): number { sleeve.shock = Math.min(100, sleeve.shock + 0.0002 * cycles); - if (sleeve.shock >= 100) sleeve.currentWork = null; + if (sleeve.shock >= 100) sleeve.stopWork(player); return 0; } diff --git a/src/PersonObjects/Sleeve/Work/SleeveSupportWork.ts b/src/PersonObjects/Sleeve/Work/SleeveSupportWork.ts new file mode 100644 index 000000000..5dfa02b7c --- /dev/null +++ b/src/PersonObjects/Sleeve/Work/SleeveSupportWork.ts @@ -0,0 +1,43 @@ +import { IPlayer } from "../../../PersonObjects/IPlayer"; +import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../../utils/JSONReviver"; +import { Work, WorkType } from "./Work"; + +export const isSleeveSupportWork = (w: Work | null): w is SleeveSupportWork => + w !== null && w.type === WorkType.SUPPORT; + +export class SleeveSupportWork extends Work { + constructor(player?: IPlayer) { + super(WorkType.SUPPORT); + if (player) player.bladeburner?.sleeveSupport(true); + } + + process(): number { + return 0; + } + + finish(player: IPlayer): void { + player.bladeburner?.sleeveSupport(false); + } + + APICopy(): Record { + return { + type: this.type, + }; + } + + /** + * Serialize the current object to a JSON save state. + */ + toJSON(): IReviverValue { + return Generic_toJSON("SleeveSupportWork", this); + } + + /** + * Initiatizes a BladeburnerWork object from a JSON save state. + */ + static fromJSON(value: IReviverValue): SleeveSupportWork { + return Generic_fromJSON(SleeveSupportWork, value.data); + } +} + +Reviver.constructors.SleeveSupportWork = SleeveSupportWork; diff --git a/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts b/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts index b70665401..09b5584d3 100644 --- a/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts +++ b/src/PersonObjects/Sleeve/Work/SleeveSynchroWork.ts @@ -13,7 +13,7 @@ export class SleeveSynchroWork extends Work { process(player: IPlayer, sleeve: Sleeve, cycles: number): number { sleeve.sync = Math.min(100, sleeve.sync + player.getIntelligenceBonus(0.5) * 0.0002 * cycles); - if (sleeve.sync >= 100) sleeve.currentWork = null; + if (sleeve.sync >= 100) sleeve.stopWork(player); return 0; } diff --git a/src/PersonObjects/Sleeve/Work/Work.ts b/src/PersonObjects/Sleeve/Work/Work.ts index 89bb26d14..37703df1a 100644 --- a/src/PersonObjects/Sleeve/Work/Work.ts +++ b/src/PersonObjects/Sleeve/Work/Work.ts @@ -12,6 +12,9 @@ export abstract class Work { abstract process(player: IPlayer, sleeve: Sleeve, cycles: number): number; abstract APICopy(): Record; abstract toJSON(): IReviverValue; + finish(_player: IPlayer): void { + /* left for children to implement */ + } } export enum WorkType { @@ -21,8 +24,7 @@ export enum WorkType { CLASS = "CLASS", RECOVERY = "RECOVERY", SYNCHRO = "SYNCHRO", - BLADEBURNER_GENERAL = "BLADEBURNER_GENERAL", + BLADEBURNER = "BLADEBURNER", INFILTRATE = "INFILTRATE", - BLADEBURNER_SUPPORT = "SUPPORT", - BLADEBURNER_CONTRACTS = "CONTRACTS", + SUPPORT = "SUPPORT", } diff --git a/src/PersonObjects/Sleeve/ui/SleeveElem.tsx b/src/PersonObjects/Sleeve/ui/SleeveElem.tsx index 1f961f5a8..de0bfd4d7 100644 --- a/src/PersonObjects/Sleeve/ui/SleeveElem.tsx +++ b/src/PersonObjects/Sleeve/ui/SleeveElem.tsx @@ -19,8 +19,10 @@ import { isSleeveSynchroWork } from "../Work/SleeveSynchroWork"; import { isSleeveRecoveryWork } from "../Work/SleeveRecoveryWork"; import { isSleeveFactionWork } from "../Work/SleeveFactionWork"; import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork"; -import { isSleeveBladeburnerGeneralWork } from "../Work/SleeveBladeburnerGeneralActionWork"; import { isSleeveInfiltrateWork } from "../Work/SleeveInfiltrateWork"; +import { isSleeveSupportWork } from "../Work/SleeveSupportWork"; +import { isSleeveBladeburnerWork } from "../Work/SleeveBladeburnerWork"; +import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork"; interface IProps { sleeve: Sleeve; @@ -71,42 +73,17 @@ export function SleeveElem(props: IProps): React.ReactElement { props.rerender(); } - let desc = <>; - switch (props.sleeve.currentTask) { - case SleeveTaskType.Idle: - desc = <>This sleeve is currently idle; - break; - case SleeveTaskType.Crime: { - const crime = Object.values(Crimes).find((crime) => crime.name === props.sleeve.crimeType); - if (!crime) throw new Error("crime should not be undefined"); - desc = ( - <> - This sleeve is currently attempting to {crime.type} (Success Rate:{" "} - {numeralWrapper.formatPercentage(crime.successRate(props.sleeve))}). - - ); - break; - } - case SleeveTaskType.Class: - desc = <>This sleeve is currently studying/taking a course at {props.sleeve.currentTaskLocation}.; - break; - case SleeveTaskType.Bladeburner: { - let message = ""; - if (props.sleeve.bbContract !== "------") { - message = ` - ${props.sleeve.bbContract} (Success Rate: ${props.sleeve.currentTaskLocation})`; - } else if (props.sleeve.currentTaskLocation !== "") { - message = props.sleeve.currentTaskLocation; - } - desc = ( - <> - This sleeve is currently attempting to {props.sleeve.bbAction}. {message} - - ); - break; - } + let desc = <>This sleeve is currently idle; - default: - console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${abc[0]}`); + if (isSleeveCrimeWork(props.sleeve.currentWork)) { + const w = props.sleeve.currentWork; + const crime = w.getCrime(); + desc = ( + <> + This sleeve is currently attempting to {crime.type} (Success Rate:{" "} + {numeralWrapper.formatPercentage(crime.successRate(props.sleeve))}). + + ); } if (isSleeveClassWork(props.sleeve.currentWork)) { @@ -153,11 +130,11 @@ export function SleeveElem(props: IProps): React.ReactElement { desc = <>This sleeve is currently working your job at {props.sleeve.currentWork.companyName}.; } - if (isSleeveBladeburnerGeneralWork(props.sleeve.currentWork)) { + if (isSleeveBladeburnerWork(props.sleeve.currentWork)) { const w = props.sleeve.currentWork; desc = ( <> - This sleeve is currently attempting to perform {w.action}. ( + This sleeve is currently attempting to perform {w.actionName}. ( {((100 * w.cyclesWorked) / w.cyclesNeeded(player, props.sleeve)).toFixed(2)}%) ); @@ -173,6 +150,10 @@ export function SleeveElem(props: IProps): React.ReactElement { ); } + if (isSleeveSupportWork(props.sleeve.currentWork)) { + desc = <>This sleeve is currently supporting you in your bladeburner activities.; + } + return ( <> diff --git a/src/PersonObjects/Sleeve/ui/StatsElement.tsx b/src/PersonObjects/Sleeve/ui/StatsElement.tsx index 8f5e6edac..4000a8348 100644 --- a/src/PersonObjects/Sleeve/ui/StatsElement.tsx +++ b/src/PersonObjects/Sleeve/ui/StatsElement.tsx @@ -16,6 +16,7 @@ import { SleeveTaskType } from "../SleeveTaskTypesEnum"; import { isSleeveClassWork } from "../Work/SleeveClassWork"; import { isSleeveFactionWork } from "../Work/SleeveFactionWork"; import { isSleeveCompanyWork } from "../Work/SleeveCompanyWork"; +import { isSleeveCrimeWork } from "../Work/SleeveCrimeWork"; interface IProps { sleeve: Sleeve; @@ -97,35 +98,17 @@ export function EarningsElement(props: IProps): React.ReactElement { const player = use.Player(); let data: (string | JSX.Element)[][] = []; - if (props.sleeve.currentTask === SleeveTaskType.Crime) { + if (isSleeveCrimeWork(props.sleeve.currentWork)) { + const gains = props.sleeve.currentWork.getExp(); data = [ - [ - `Money`, - <> - (on success) - , - ], - [`Hacking Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.hack)} (2x on success)`], - [`Strength Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.str)} (2x on success)`], - [`Defense Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.def)} (2x on success)`], - [`Dexterity Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.dex)} (2x on success)`], - [`Agility Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.agi)} (2x on success)`], - [`Charisma Exp`, `${numeralWrapper.formatExp(props.sleeve.gainRatesForTask.cha)} (2x on success)`], + [`Money:`, ], + [`Hacking Exp:`, `${numeralWrapper.formatExp(5 * gains.hackExp)}`], + [`Strength Exp:`, `${numeralWrapper.formatExp(5 * gains.strExp)}`], + [`Defense Exp:`, `${numeralWrapper.formatExp(5 * gains.defExp)}`], + [`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * gains.dexExp)}`], + [`Agility Exp:`, `${numeralWrapper.formatExp(5 * gains.agiExp)}`], + [`Charisma Exp:`, `${numeralWrapper.formatExp(5 * gains.chaExp)}`], ]; - } else { - data = [ - [`Money:`, ], - [`Hacking Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.hack)} / sec`], - [`Strength Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.str)} / sec`], - [`Defense Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.def)} / sec`], - [`Dexterity Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.dex)} / sec`], - [`Agility Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.agi)} / sec`], - [`Charisma Exp:`, `${numeralWrapper.formatExp(5 * props.sleeve.gainRatesForTask.cha)} / sec`], - ]; - if (props.sleeve.currentTask === SleeveTaskType.Company || props.sleeve.currentTask === SleeveTaskType.Faction) { - const repGain: number = props.sleeve.getRepGain(player); - data.push([`Reputation:`, ]); - } } if (isSleeveClassWork(props.sleeve.currentWork)) { const rates = props.sleeve.currentWork.calculateRates(player, props.sleeve); diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 65cc2b00d..b94170d26 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -1016,36 +1016,13 @@ export interface SleeveInformation { tor: boolean; /** Sleeve multipliers */ mult: CharacterMult; - /** Time spent on the current task in milliseconds */ - timeWorked: number; - /** Earnings synchronized to other sleeves */ - earningsForSleeves: SleeveWorkGains; - /** Earnings synchronized to the player */ - earningsForPlayer: SleeveWorkGains; - /** Earnings for this sleeve */ - earningsForTask: SleeveWorkGains; - /** Faction or company reputation gained for the current task */ - workRepGain: number; } /** * Object representing a sleeve current task. * @public */ -export interface SleeveTask { - /** Task type */ - task: string; - /** Crime currently attempting, if any */ - crime: string; - /** Location of the task, if any */ - location: string; - /** Stat being trained at the gym, if any */ - gymStatType: string; - /** Faction work type being performed, if any */ - factionWorkType: string; - /** Class being taken at university, if any */ - className: string; -} +export interface SleeveTask = any; /** * Object representing a port. A port is a serialized queue. @@ -1310,7 +1287,7 @@ export interface TIX { * @param shares - Number of shares to short. Must be positive. Will be rounded to nearest integer. * @returns The stock price at which each share was purchased, otherwise 0 if the shares weren't purchased. */ - short(sym: string, shares: number): number; + buyShort(sym: string, shares: number): number; /** * Sell short stock. diff --git a/src/Work/CrimeWork.ts b/src/Work/CrimeWork.ts index 356d932e1..3c7bc8933 100644 --- a/src/Work/CrimeWork.ts +++ b/src/Work/CrimeWork.ts @@ -26,29 +26,29 @@ enum newCrimeType { const convertCrimeType = (crimeType: CrimeType): newCrimeType => { switch (crimeType) { - case CrimeType.Shoplift: + case CrimeType.SHOPLIFT: return newCrimeType.SHOPLIFT; - case CrimeType.RobStore: + case CrimeType.ROB_STORE: return newCrimeType.ROBSTORE; - case CrimeType.Mug: + case CrimeType.MUG: return newCrimeType.MUG; - case CrimeType.Larceny: + case CrimeType.LARCENY: return newCrimeType.LARCENY; - case CrimeType.Drugs: + case CrimeType.DRUGS: return newCrimeType.DRUGS; - case CrimeType.BondForgery: + case CrimeType.BOND_FORGERY: return newCrimeType.BONDFORGERY; - case CrimeType.TraffickArms: + case CrimeType.TRAFFIC_ARMS: return newCrimeType.TRAFFICKARMS; - case CrimeType.Homicide: + case CrimeType.HOMICIDE: return newCrimeType.HOMICIDE; - case CrimeType.GrandTheftAuto: + case CrimeType.GRAND_THEFT_AUTO: return newCrimeType.GRANDTHEFTAUTO; - case CrimeType.Kidnap: + case CrimeType.KIDNAP: return newCrimeType.KIDNAP; - case CrimeType.Assassination: + case CrimeType.ASSASSINATION: return newCrimeType.ASSASSINATION; - case CrimeType.Heist: + case CrimeType.HEIST: return newCrimeType.HEIST; } return newCrimeType.SHOPLIFT; @@ -67,7 +67,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; } diff --git a/src/utils/WorkType.ts b/src/utils/WorkType.ts index 58c003fd7..f4cd1a285 100644 --- a/src/utils/WorkType.ts +++ b/src/utils/WorkType.ts @@ -1,15 +1,15 @@ export enum CrimeType { None = "", - Shoplift = "shoplift", - RobStore = "rob a store", - Mug = "mug someone", - Larceny = "commit larceny", - Drugs = "deal drugs", - BondForgery = "forge corporate bonds", - TraffickArms = "traffick illegal arms", - Homicide = "commit homicide", - GrandTheftAuto = "commit grand theft auto", - Kidnap = "kidnap someone for ransom", - Assassination = "assassinate a high-profile target", - Heist = "pull off the ultimate heist", + 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", } diff --git a/src/utils/v2APIBreak.ts b/src/utils/v2APIBreak.ts index 4285dd7ef..41a41abe6 100644 --- a/src/utils/v2APIBreak.ts +++ b/src/utils/v2APIBreak.ts @@ -185,6 +185,12 @@ export const v2APIBreak = () => { reason: "sell is a very common word so in order to avoid ram costs it was renamed ns.stock.sellStock", offenders: [], }, + { + matchJS: /ns\.stock\.short/g, + matchScript: /stock\.short/g, + reason: "short is a very common word so in order to avoid ram costs it was renamed ns.stock.buyShort", + offenders: [], + }, { matchJS: /ns\.corporation\.bribe/g, matchScript: /corporation\.bribe/g,