diff --git a/src/Crime/Crime.ts b/src/Crime/Crime.ts index c7584ff05..21158376f 100644 --- a/src/Crime/Crime.ts +++ b/src/Crime/Crime.ts @@ -4,6 +4,7 @@ import { IPerson } from "../PersonObjects/IPerson"; import { IRouter } from "../ui/Router"; import { WorkerScript } from "../Netscript/WorkerScript"; import { CrimeType } from "../utils/WorkType"; +import { CrimeWork } from "../Work/CrimeWork"; interface IConstructorParams { hacking_success_weight?: number; @@ -100,19 +101,13 @@ export class Crime { if (div <= 0) { div = 1; } - p.startCrime( - router, - this.type, - this.hacking_exp / div, - this.strength_exp / div, - this.defense_exp / div, - this.dexterity_exp / div, - this.agility_exp / div, - this.charisma_exp / div, - this.money / div, - this.time, - workerScript, + p.startNEWWork( + new CrimeWork({ + crimeType: this.type, + singularity: workerScript !== null, + }), ); + router.toWork(); return this.time; } diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 02d3a74ba..c7c04e5dd 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -2421,7 +2421,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { createProgramName: Player.createProgramName, createProgramReqLvl: Player.createProgramReqLvl, className: Player.className, - crimeType: Player.crimeType, work_money_mult: Player.work_money_mult, hacknet_node_money_mult: Player.hacknet_node_money_mult, hacknet_node_purchase_cost_mult: Player.hacknet_node_purchase_cost_mult, diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index be62e8283..4382ceb26 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -32,6 +32,7 @@ import { ISkillProgress } from "./formulas/skill"; import { PlayerAchievement } from "../Achievements/Achievements"; import { IPerson } from "./IPerson"; import { WorkType, ClassType, CrimeType } from "../utils/WorkType"; +import { Work } from "src/Work/Work"; export interface IPlayer extends IPerson { bitNodeN: number; @@ -124,15 +125,13 @@ export interface IPlayer extends IPerson { bladeburner_analysis_mult: number; bladeburner_success_chance_mult: number; + currentWork: Work | null; createProgramReqLvl: number; factionWorkType: string; createProgramName: string; timeWorkedCreateProgram: number; graftAugmentationName: string; timeWorkedGraftAugmentation: number; - crimeType: CrimeType; - committingCrimeThruSingFn: boolean; - singFnCrimeWorkerScript: WorkerScript | null; timeNeededToCompleteWork: number; focus: boolean; className: ClassType; @@ -163,6 +162,9 @@ export interface IPlayer extends IPerson { entropy: number; // Methods + startNEWWork(w: Work): void; + processNEWWork(cycles: number): void; + finishNEWWork(cancelled: boolean): void; work(numCycles: number): boolean; workPartTime(numCycles: number): boolean; workForFaction(numCycles: number): boolean; @@ -213,19 +215,6 @@ export interface IPlayer extends IPerson { startFactionWork(faction: Faction): void; startClass(costMult: number, expMult: number, className: ClassType): void; startCorporation(corpName: string, additionalShares?: number): void; - startCrime( - router: IRouter, - crimeType: CrimeType, - hackExp: number, - strExp: number, - defExp: number, - dexExp: number, - agiExp: number, - chaExp: number, - money: number, - time: number, - singParams: any, - ): void; startFactionFieldWork(faction: Faction): void; startFactionHackWork(faction: Faction): void; startFactionSecurityWork(faction: Faction): void; @@ -251,7 +240,6 @@ export interface IPlayer extends IPerson { finishWork(cancelled: boolean, sing?: boolean): string; cancelationPenalty(): number; finishWorkPartTime(sing?: boolean): string; - finishCrime(cancelled: boolean): string; finishCreateProgramWork(cancelled: boolean): string; resetMultipliers(): void; prestigeAugmentation(): void; @@ -270,7 +258,6 @@ export interface IPlayer extends IPerson { hospitalize(): void; createProgramWork(numCycles: number): boolean; takeClass(numCycles: number): boolean; - commitCrime(numCycles: number): boolean; checkForFactionInvitations(): Faction[]; setBitNodeNumber(n: number): void; getMult(name: string): number; diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index aae9bdd61..ce138db20 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -4,6 +4,7 @@ import * as corporationMethods from "./PlayerObjectCorporationMethods"; import * as gangMethods from "./PlayerObjectGangMethods"; import * as generalMethods from "./PlayerObjectGeneralMethods"; import * as serverMethods from "./PlayerObjectServerMethods"; +import * as workMethods from "./PlayerObjectWorkMethods"; import { IMap } from "../../types"; import { Sleeve } from "../Sleeve/Sleeve"; @@ -40,6 +41,7 @@ import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { ITaskTracker } from "../ITaskTracker"; import { CONSTANTS } from "../../Constants"; import { WorkType, ClassType, CrimeType, PlayerFactionWorkType } from "../../utils/WorkType"; +import { Work } from "src/Work/Work"; export class PlayerObject implements IPlayer { // Class members @@ -136,15 +138,13 @@ export class PlayerObject implements IPlayer { bladeburner_analysis_mult: number; bladeburner_success_chance_mult: number; + currentWork: Work | null; createProgramReqLvl: number; factionWorkType: PlayerFactionWorkType; createProgramName: string; timeWorkedCreateProgram: number; graftAugmentationName: string; timeWorkedGraftAugmentation: number; - crimeType: CrimeType; - committingCrimeThruSingFn: boolean; - singFnCrimeWorkerScript: WorkerScript | null; timeNeededToCompleteWork: number; focus: boolean; className: ClassType; @@ -175,6 +175,9 @@ export class PlayerObject implements IPlayer { entropy: number; // Methods + startNEWWork: (w: Work) => void; + processNEWWork: (cycles: number) => void; + finishNEWWork: (cancelled: boolean) => void; work: (numCycles: number) => boolean; workPartTime: (numCycles: number) => boolean; workForFaction: (numCycles: number) => boolean; @@ -234,19 +237,6 @@ export class PlayerObject implements IPlayer { startFactionWork: (faction: Faction) => void; startClass: (costMult: number, expMult: number, className: ClassType) => void; startCorporation: (corpName: string, additionalShares?: number) => void; - startCrime: ( - router: IRouter, - crimeType: CrimeType, - hackExp: number, - strExp: number, - defExp: number, - dexExp: number, - agiExp: number, - chaExp: number, - money: number, - time: number, - singParams: any, - ) => void; startFactionFieldWork: (faction: Faction) => void; startFactionHackWork: (faction: Faction) => void; startFactionSecurityWork: (faction: Faction) => void; @@ -276,7 +266,6 @@ export class PlayerObject implements IPlayer { finishWork: (cancelled: boolean, sing?: boolean) => string; cancelationPenalty: () => number; finishWorkPartTime: (sing?: boolean) => string; - finishCrime: (cancelled: boolean) => string; finishCreateProgramWork: (cancelled: boolean) => string; resetMultipliers: () => void; prestigeAugmentation: () => void; @@ -296,7 +285,6 @@ export class PlayerObject implements IPlayer { hospitalize: () => void; createProgramWork: (numCycles: number) => boolean; takeClass: (numCycles: number) => boolean; - commitCrime: (numCycles: number) => boolean; checkForFactionInvitations: () => Faction[]; setBitNodeNumber: (n: number) => void; getMult: (name: string) => number; @@ -435,8 +423,6 @@ export class PlayerObject implements IPlayer { this.className = ClassType.None; - this.crimeType = CrimeType.None; - this.timeWorked = 0; //in m; this.timeWorkedCreateProgram = 0; this.timeNeededToCompleteWork = 0; @@ -495,6 +481,8 @@ export class PlayerObject implements IPlayer { this.achievements = []; this.terminalCommandHistory = []; + this.currentWork = null; + // Let's get a hash of some semi-random stuff so we have something unique. this.identifier = cyrb53( "I-" + @@ -532,6 +520,9 @@ export class PlayerObject implements IPlayer { this.processWorkEarnings = generalMethods.processWorkEarnings; this.startWork = generalMethods.startWork; this.cancelationPenalty = generalMethods.cancelationPenalty; + this.startNEWWork = workMethods.start; + this.processNEWWork = workMethods.process; + this.finishNEWWork = workMethods.finish; this.work = generalMethods.work; this.finishWork = generalMethods.finishWork; this.startWorkPartTime = generalMethods.startWorkPartTime; @@ -563,9 +554,6 @@ export class PlayerObject implements IPlayer { this.startClass = generalMethods.startClass; this.takeClass = generalMethods.takeClass; this.finishClass = generalMethods.finishClass; - this.startCrime = generalMethods.startCrime; - this.commitCrime = generalMethods.commitCrime; - this.finishCrime = generalMethods.finishCrime; this.singularityStopWork = generalMethods.singularityStopWork; this.takeDamage = generalMethods.takeDamage; this.regenerateHp = generalMethods.regenerateHp; @@ -623,8 +611,6 @@ export class PlayerObject implements IPlayer { this.getUpgradeHomeCoresCost = serverMethods.getUpgradeHomeCoresCost; this.createHacknetServer = serverMethods.createHacknetServer; this.factionWorkType = PlayerFactionWorkType.None; - this.committingCrimeThruSingFn = false; - this.singFnCrimeWorkerScript = null; this.getMult = generalMethods.getMult; this.setMult = generalMethods.setMult; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 4ae471894..dc18de70f 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -146,7 +146,6 @@ export function prestigeAugmentation(this: PlayerObject): void { this.currentWorkFactionDescription = ""; this.createProgramName = ""; this.className = ClassType.None; - this.crimeType = CrimeType.None; this.workHackExpGainRate = 0; this.workStrExpGainRate = 0; @@ -615,10 +614,6 @@ export function process(this: IPlayer, router: IRouter, numCycles = 1): void { if (this.takeClass(numCycles)) { router.toCity(); } - } else if (this.workType === WorkType.Crime) { - if (this.commitCrime(numCycles)) { - router.toLocation(Locations[LocationName.Slums]); - } } else if (this.workType === WorkType.CompanyPartTime) { if (this.workPartTime(numCycles)) { router.toCity(); @@ -1330,10 +1325,7 @@ export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): stri if (!cancelled) { //Complete case this.gainIntelligenceExp((CONSTANTS.IntelligenceProgramBaseExpGain * this.timeWorked) / 1000); - const lines = [ - `You've finished creating ${programName}!`, - "The new program can be found on your home computer.", - ]; + const lines = [`You've finished creating ${programName}!`, "The new program can be found on your home computer."]; dialogBoxCreate(lines.join("
")); message = lines.join(" "); @@ -1501,196 +1493,6 @@ export function finishClass(this: IPlayer, sing = false): string { return ""; } -//The EXP and $ gains are hardcoded. Time is in ms -export function startCrime( - this: IPlayer, - router: IRouter, - crimeType: CrimeType, - hackExp: number, - strExp: number, - defExp: number, - dexExp: number, - agiExp: number, - chaExp: number, - money: number, - time: number, - workerscript: WorkerScript | null = null, -): void { - this.crimeType = crimeType; - - this.resetWorkStatus(); - this.isWorking = true; - this.focus = true; - this.workType = WorkType.Crime; - - if (workerscript !== null) { - this.committingCrimeThruSingFn = true; - this.singFnCrimeWorkerScript = workerscript; - } - - this.workHackExpGained = hackExp * this.hacking_exp_mult * BitNodeMultipliers.CrimeExpGain; - this.workStrExpGained = strExp * this.strength_exp_mult * BitNodeMultipliers.CrimeExpGain; - this.workDefExpGained = defExp * this.defense_exp_mult * BitNodeMultipliers.CrimeExpGain; - this.workDexExpGained = dexExp * this.dexterity_exp_mult * BitNodeMultipliers.CrimeExpGain; - this.workAgiExpGained = agiExp * this.agility_exp_mult * BitNodeMultipliers.CrimeExpGain; - this.workChaExpGained = chaExp * this.charisma_exp_mult * BitNodeMultipliers.CrimeExpGain; - this.workMoneyGained = money * this.crime_money_mult * BitNodeMultipliers.CrimeMoney; - - this.timeNeededToCompleteWork = time; - router.toWork(); -} - -export function commitCrime(this: IPlayer, numCycles: number): boolean { - this.timeWorked += CONSTANTS._idleSpeed * numCycles; - - if (this.timeWorked >= this.timeNeededToCompleteWork) { - this.finishCrime(false); - return true; - } - return false; -} - -export function finishCrime(this: IPlayer, cancelled: boolean): string { - //Determine crime success/failure - if (!cancelled) { - if (determineCrimeSuccess(this, this.crimeType)) { - //Handle Karma and crime statistics - let crime = null; - for (const i of Object.keys(Crimes)) { - if (Crimes[i].type == this.crimeType) { - crime = Crimes[i]; - break; - } - } - if (crime == null) { - dialogBoxCreate( - `ERR: Unrecognized crime type (${this.crimeType}). This is probably a bug please contact the developer`, - ); - return ""; - } - this.gainMoney(this.workMoneyGained, "crime"); - this.karma -= crime.karma; - this.numPeopleKilled += crime.kills; - if (crime.intelligence_exp > 0) { - this.gainIntelligenceExp(crime.intelligence_exp); - } - - //On a crime success, gain 2x exp - this.workHackExpGained *= 2; - this.workStrExpGained *= 2; - this.workDefExpGained *= 2; - this.workDexExpGained *= 2; - this.workAgiExpGained *= 2; - this.workChaExpGained *= 2; - const ws = this.singFnCrimeWorkerScript; - if (this.committingCrimeThruSingFn && ws !== null) { - if (ws.disableLogs.ALL == null && ws.disableLogs.commitCrime == null) { - ws.scriptRef.log( - "SUCCESS: Crime successful! Gained " + - numeralWrapper.formatMoney(this.workMoneyGained) + - ", " + - numeralWrapper.formatExp(this.workHackExpGained) + - " hack exp, " + - numeralWrapper.formatExp(this.workStrExpGained) + - " str exp, " + - numeralWrapper.formatExp(this.workDefExpGained) + - " def exp, " + - numeralWrapper.formatExp(this.workDexExpGained) + - " dex exp, " + - numeralWrapper.formatExp(this.workAgiExpGained) + - " agi exp, " + - numeralWrapper.formatExp(this.workChaExpGained) + - " cha exp.", - ); - } - } else { - dialogBoxCreate( - <> - Crime successful! -
-
- You gained: -
- -
- {numeralWrapper.formatExp(this.workHackExpGained)} hacking experience
- {numeralWrapper.formatExp(this.workStrExpGained)} strength experience -
- {numeralWrapper.formatExp(this.workDefExpGained)} defense experience -
- {numeralWrapper.formatExp(this.workDexExpGained)} dexterity experience -
- {numeralWrapper.formatExp(this.workAgiExpGained)} agility experience -
- {numeralWrapper.formatExp(this.workChaExpGained)} charisma experience - , - ); - } - } else { - //Exp halved on failure - this.workHackExpGained /= 2; - this.workStrExpGained /= 2; - this.workDefExpGained /= 2; - this.workDexExpGained /= 2; - this.workAgiExpGained /= 2; - this.workChaExpGained /= 2; - const ws = this.singFnCrimeWorkerScript; - if (this.committingCrimeThruSingFn && ws !== null) { - if (ws.disableLogs.ALL == null && ws.disableLogs.commitCrime == null) { - ws.scriptRef.log( - "FAIL: Crime failed! Gained " + - numeralWrapper.formatExp(this.workHackExpGained) + - " hack exp, " + - numeralWrapper.formatExp(this.workStrExpGained) + - " str exp, " + - numeralWrapper.formatExp(this.workDefExpGained) + - " def exp, " + - numeralWrapper.formatExp(this.workDexExpGained) + - " dex exp, " + - numeralWrapper.formatExp(this.workAgiExpGained) + - " agi exp, " + - numeralWrapper.formatExp(this.workChaExpGained) + - " cha exp.", - ); - } - } else { - dialogBoxCreate( - <> - Crime failed! -
-
- You gained: -
- {numeralWrapper.formatExp(this.workHackExpGained)} hacking experience
- {numeralWrapper.formatExp(this.workStrExpGained)} strength experience -
- {numeralWrapper.formatExp(this.workDefExpGained)} defense experience -
- {numeralWrapper.formatExp(this.workDexExpGained)} dexterity experience -
- {numeralWrapper.formatExp(this.workAgiExpGained)} agility experience -
- {numeralWrapper.formatExp(this.workChaExpGained)} charisma experience - , - ); - } - } - - this.gainHackingExp(this.workHackExpGained); - this.gainStrengthExp(this.workStrExpGained); - this.gainDefenseExp(this.workDefExpGained); - this.gainDexterityExp(this.workDexExpGained); - this.gainAgilityExp(this.workAgiExpGained); - this.gainCharismaExp(this.workChaExpGained); - } - this.committingCrimeThruSingFn = false; - this.singFnCrimeWorkerScript = null; - this.isWorking = false; - this.crimeType = CrimeType.None; - this.resetWorkStatus(); - return ""; -} - //Cancels the player's current "work" assignment and gives the proper rewards //Used only for Singularity functions, so no popups are created export function singularityStopWork(this: IPlayer): string { @@ -1714,9 +1516,6 @@ export function singularityStopWork(this: IPlayer): string { case WorkType.CreateProgram: res = this.finishCreateProgramWork(true); break; - case WorkType.Crime: - res = this.finishCrime(true); - break; case WorkType.GraftAugmentation: res = this.finishGraftAugmentationWork(true, true); break; diff --git a/src/PersonObjects/Player/PlayerObjectWorkMethods.ts b/src/PersonObjects/Player/PlayerObjectWorkMethods.ts new file mode 100644 index 000000000..6ef8557b2 --- /dev/null +++ b/src/PersonObjects/Player/PlayerObjectWorkMethods.ts @@ -0,0 +1,21 @@ +import { Work } from "../../Work/Work"; +import { IPlayer } from "../IPlayer"; + +export function start(this: IPlayer, w: Work): void { + if (this.currentWork !== null) { + this.currentWork.finish(this, true); + } + this.currentWork = w; +} +export function process(this: IPlayer, cycles = 1): void { + if (this.currentWork === null) return; + const finished = this.currentWork.process(this, cycles); + if (finished) { + this.finishNEWWork(false); + } +} +export function finish(this: IPlayer, cancelled: boolean): void { + if (this.currentWork === null) return; + this.currentWork.finish(this, cancelled); + this.currentWork = null; +} diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 0177aee7e..d099e6170 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -71,7 +71,6 @@ interface Player { createProgramName: string; createProgramReqLvl: number; className: string; - crimeType: string; work_money_mult: number; hacknet_node_money_mult: number; hacknet_node_purchase_cost_mult: number; diff --git a/src/Work/CrimeWork.tsx b/src/Work/CrimeWork.tsx new file mode 100644 index 000000000..9bd2bba11 --- /dev/null +++ b/src/Work/CrimeWork.tsx @@ -0,0 +1,139 @@ +import React from "react"; +import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; +import { Crime } from "../Crime/Crime"; +import { CONSTANTS } from "../Constants"; +import { determineCrimeSuccess } from "../Crime/CrimeHelpers"; +import { Crimes } from "../Crime/Crimes"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { numeralWrapper } from "../ui/numeralFormat"; +import { dialogBoxCreate } from "../ui/React/DialogBox"; +import { Money } from "../ui/React/Money"; +import { CrimeType, WorkType } from "../utils/WorkType"; +import { Work } from "./Work"; + +interface CrimeWorkParams { + crimeType: CrimeType; + singularity: boolean; +} + +export const isCrimeWork = (w: Work): w is CrimeWork => w.type === WorkType.Crime; + +export class CrimeWork extends Work { + type = WorkType.Crime; + + crimeType: CrimeType; + cyclesWorked: number; + singularity: boolean; + + constructor(params?: CrimeWorkParams) { + super(); + this.crimeType = params?.crimeType ?? CrimeType.Shoplift; + this.singularity = params?.singularity ?? false; + this.cyclesWorked = 0; + } + + getCrime(): Crime { + const crime = Object.values(Crimes).find((c) => c.type === this.crimeType); + if (!crime) throw new Error("CrimeWork object constructed with invalid crime type"); + return crime; + } + + process(player: IPlayer, cycles = 1): boolean { + this.cyclesWorked += cycles; + const time = Object.values(Crimes).find((c) => c.type === this.crimeType)?.time ?? 0; + return this.cyclesWorked * CONSTANTS._idleSpeed >= time; + } + + finish(player: IPlayer, cancelled: boolean): void { + if (cancelled) return; + let crime = null; + for (const i of Object.keys(Crimes)) { + if (Crimes[i].type == this.crimeType) { + crime = Crimes[i]; + break; + } + } + if (crime == null) { + dialogBoxCreate( + `ERR: Unrecognized crime type (${this.crimeType}). This is probably a bug please contact the developer`, + ); + return; + } + // exp times 2 because were trying to maintain the same numbers as before the conversion + // Technically the definition of Crimes should have the success numbers and failure should divide by 4 + let hackExp = crime.hacking_exp * 2; + let StrExp = crime.strength_exp * 2; + let DefExp = crime.defense_exp * 2; + let DexExp = crime.dexterity_exp * 2; + let AgiExp = crime.agility_exp * 2; + let ChaExp = crime.charisma_exp * 2; + let karma = crime.karma; + const success = determineCrimeSuccess(player, crime.type); + if (success) { + player.gainMoney(crime.money, "crime"); + player.numPeopleKilled += crime.kills; + player.gainIntelligenceExp(crime.intelligence_exp); + } else { + hackExp /= 4; + StrExp /= 4; + DefExp /= 4; + DexExp /= 4; + AgiExp /= 4; + ChaExp /= 4; + karma /= 4; + } + + player.gainHackingExp(hackExp); + player.gainStrengthExp(StrExp); + player.gainDefenseExp(DefExp); + player.gainDexterityExp(DexExp); + player.gainAgilityExp(AgiExp); + player.gainCharismaExp(ChaExp); + player.karma -= karma; + + if (!this.singularity) { + dialogBoxCreate( + <> + Crime {success ? "successful" : "failed"}! +
+
+ You gained: + {success && ( + <> +
+ + + )} +
+ {numeralWrapper.formatExp(hackExp)} hacking experience
+ {numeralWrapper.formatExp(StrExp)} strength experience +
+ {numeralWrapper.formatExp(DefExp)} defense experience +
+ {numeralWrapper.formatExp(DexExp)} dexterity experience +
+ {numeralWrapper.formatExp(AgiExp)} agility experience +
+ {numeralWrapper.formatExp(ChaExp)} charisma experience + , + ); + } + } + + /** + * Serialize the current object to a JSON save state. + */ + toJSON(): any { + return Generic_toJSON("CrimeWork", this); + } + + /** + * Initiatizes a CrimeWork object from a JSON save state. + */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + static fromJSON(value: any): CrimeWork { + return Generic_fromJSON(CrimeWork, value.data); + } +} + +Reviver.constructors.CrimeWork = CrimeWork; diff --git a/src/Work/Work.ts b/src/Work/Work.ts new file mode 100644 index 000000000..0b9d6f63c --- /dev/null +++ b/src/Work/Work.ts @@ -0,0 +1,9 @@ +import { IPlayer } from "src/PersonObjects/IPlayer"; +import { WorkType } from "src/utils/WorkType"; + +export abstract class Work { + abstract type: WorkType; + + abstract process(player: IPlayer, cycles: number): boolean; + abstract finish(player: IPlayer, cancelled: boolean): void; +} diff --git a/src/engine.tsx b/src/engine.tsx index 5a16e7f54..c29aa7769 100644 --- a/src/engine.tsx +++ b/src/engine.tsx @@ -96,7 +96,11 @@ const Engine: { Terminal.process(Router, Player, numCycles); - Player.process(Router, numCycles); + if (Player.currentWork !== null) { + Player.processNEWWork(numCycles); + } else { + Player.process(Router, numCycles); + } // Update stock prices if (Player.hasWseAccount) { @@ -293,7 +297,9 @@ const Engine: { Player.gainMoney(offlineHackingIncome, "hacking"); // Process offline progress loadAllRunningScripts(Player); // This also takes care of offline production for those scripts - if (Player.isWorking) { + if (Player.currentWork !== null) { + Player.processNEWWork(numCyclesOffline); + } else if (Player.isWorking) { Player.focus = true; switch (Player.workType) { case WorkType.Faction: @@ -305,9 +311,6 @@ const Engine: { case WorkType.StudyClass: Player.takeClass(numCyclesOffline); break; - case WorkType.Crime: - Player.commitCrime(numCyclesOffline); - break; case WorkType.CompanyPartTime: Player.workPartTime(numCyclesOffline); break; diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index ad6d73580..edb6a8b19 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -154,7 +154,7 @@ export let Router: IRouter = { function determineStartPage(player: IPlayer): Page { if (RecoveryMode) return Page.Recovery; - if (player.isWorking) return Page.Work; + if (player.isWorking || player.currentWork !== null) return Page.Work; return Page.Terminal; } diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index a94d6776a..811ce7b97 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -20,6 +20,7 @@ import { Reputation } from "./React/Reputation"; import { ReputationRate } from "./React/ReputationRate"; import { StatsRow } from "./React/StatsRow"; import { WorkType, ClassType } from "../utils/WorkType"; +import { isCrimeWork } from "../Work/CrimeWork"; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; @@ -137,7 +138,37 @@ export function WorkInProgressRoot(): React.ReactElement { ), ]; - let workInfo: IWorkInfo | null; + let workInfo: IWorkInfo = { + buttons: { + cancel: () => undefined, + }, + title: "", + stopText: "", + }; + + if (player.currentWork !== null) { + if (isCrimeWork(player.currentWork)) { + const crime = player.currentWork.getCrime(); + const completion = Math.round(((player.currentWork.cyclesWorked * CONSTANTS._idleSpeed) / crime.time) * 100); + + workInfo = { + buttons: { + cancel: () => { + router.toLocation(Locations[LocationName.Slums]); + player.finishNEWWork(true); + }, + }, + title: `You are attempting to ${crime.type}`, + + progress: { + remaining: crime.time - player.currentWork.cyclesWorked * CONSTANTS._idleSpeed, + percentage: completion, + }, + + stopText: "Cancel crime", + }; + } + } switch (player.workType) { case WorkType.Faction: { @@ -399,29 +430,6 @@ export function WorkInProgressRoot(): React.ReactElement { break; } - case WorkType.Crime: { - const completion = Math.round((player.timeWorked / player.timeNeededToCompleteWork) * 100); - - workInfo = { - buttons: { - cancel: () => { - router.toLocation(Locations[LocationName.Slums]); - player.finishCrime(true); - }, - }, - title: `You are attempting to ${player.crimeType}`, - - progress: { - remaining: player.timeNeededToCompleteWork - player.timeWorked, - percentage: completion, - }, - - stopText: "Cancel crime", - }; - - break; - } - case WorkType.CreateProgram: { function cancel(): void { player.finishCreateProgramWork(true); @@ -497,11 +505,12 @@ export function WorkInProgressRoot(): React.ReactElement { } default: - router.toTerminal(); - workInfo = null; + if (player.currentWork === null) { + router.toTerminal(); + } } - if (workInfo === null) { + if (workInfo.title === "") { return <>; }