diff --git a/src/DarkWeb/DarkWeb.tsx b/src/DarkWeb/DarkWeb.tsx index 1857aece0..24b09f844 100644 --- a/src/DarkWeb/DarkWeb.tsx +++ b/src/DarkWeb/DarkWeb.tsx @@ -7,6 +7,7 @@ import { SpecialServers } from "../Server/data/SpecialServers"; import { numeralWrapper } from "../ui/numeralFormat"; import { Money } from "../ui/React/Money"; import { DarkWebItem } from "./DarkWebItem"; +import { isCreateProgramWork } from "../Work/CreateProgramWork"; //Posts a "help" message if connected to DarkWeb export function checkIfConnectedToDarkweb(): void { @@ -74,7 +75,7 @@ export function buyDarkwebItem(itemName: string): void { Player.getHomeComputer().pushProgram(item.program); // Cancel if the program is in progress of writing - if (Player.createProgramName === item.program) { + if (isCreateProgramWork(Player.currentWork) && Player.currentWork.programName === item.program) { Player.isWorking = false; Player.resetWorkStatus(); } diff --git a/src/DevMenu/ui/SaveFile.tsx b/src/DevMenu/ui/SaveFile.tsx index 584b0138f..ff0c81966 100644 --- a/src/DevMenu/ui/SaveFile.tsx +++ b/src/DevMenu/ui/SaveFile.tsx @@ -38,7 +38,6 @@ export function SaveFile(): React.ReactElement { function doRestore(): void { const save = JSON.parse(saveFile); - console.log(Object.keys(save)); // TODO: Continue here. } diff --git a/src/Electron.tsx b/src/Electron.tsx index 2c5cd35fe..5fb626794 100644 --- a/src/Electron.tsx +++ b/src/Electron.tsx @@ -150,7 +150,7 @@ function initSaveFunctions(): void { try { saveObject.exportGame(); } catch (error) { - console.log(error); + console.error(error); SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000); } }, @@ -199,7 +199,7 @@ function initElectronBridge(): void { bridge.send("save-completed"); }) .catch((error: unknown) => { - console.log(error); + console.error(error); SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000); }); }); @@ -207,7 +207,7 @@ function initElectronBridge(): void { try { window.appSaveFns.triggerGameExport(); } catch (error) { - console.log(error); + console.error(error); SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000); } }); @@ -215,7 +215,7 @@ function initElectronBridge(): void { try { window.appSaveFns.triggerScriptsExport(); } catch (error) { - console.log(error); + console.error(error); SnackbarEvents.emit("Could not export scripts.", ToastVariant.ERROR, 2000); } }); diff --git a/src/Gang/Gang.ts b/src/Gang/Gang.ts index fc1747663..688235a08 100644 --- a/src/Gang/Gang.ts +++ b/src/Gang/Gang.ts @@ -252,7 +252,6 @@ export class Gang implements IGang { const total = Object.values(AllGangs) .map((g) => g.territory) .reduce((p, c) => p + c, 0); - console.log(total); Object.values(AllGangs).forEach((g) => (g.territory /= total)); } } diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 9269ad71b..74565c368 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -2418,8 +2418,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { workChaExpGained: Player.workChaExpGained, workRepGained: Player.workRepGained, workMoneyGained: Player.workMoneyGained, - createProgramName: Player.createProgramName, - createProgramReqLvl: Player.createProgramReqLvl, 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/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts index 021f935a4..c27c68c51 100644 --- a/src/NetscriptFunctions/Grafting.ts +++ b/src/NetscriptFunctions/Grafting.ts @@ -7,6 +7,7 @@ import { getGraftingAvailableAugs, calculateGraftingTimeWithBonus } from "../Per import { IPlayer } from "../PersonObjects/IPlayer"; import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions"; import { Router } from "../ui/GameRoot"; +import { GraftingWork } from "../Work/GraftingWork"; export function NetscriptGrafting(player: IPlayer): InternalAPI { const checkGraftingAPIAccess = (ctx: NetscriptContext): void => { @@ -79,8 +80,13 @@ export function NetscriptGrafting(player: IPlayer): InternalAPI { return false; } - player.loseMoney(craftableAug.cost, "augmentations"); - player.startGraftAugmentationWork(augName, craftableAug.time); + player.startNEWWork( + new GraftingWork({ + singularity: true, + augmentation: augName, + player: player, + }), + ); if (focus) { player.startFocusing(); diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts index 7482982ec..1f0549dfc 100644 --- a/src/NetscriptFunctions/Singularity.ts +++ b/src/NetscriptFunctions/Singularity.ts @@ -51,6 +51,7 @@ import { enterBitNode } from "../RedPill"; import { FactionNames } from "../Faction/data/FactionNames"; import { WorkType } from "../utils/WorkType"; import { ClassWork, ClassType } from "../Work/ClassWork"; +import { CreateProgramWork, isCreateProgramWork } from "../Work/CreateProgramWork"; export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript): InternalAPI { const getAugmentation = function (_ctx: NetscriptContext, name: string): Augmentation { @@ -528,7 +529,7 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript player.getHomeComputer().pushProgram(item.program); // Cancel if the program is in progress of writing - if (player.createProgramName === item.program) { + if (isCreateProgramWork(player.currentWork) && player.currentWork.programName === item.program) { player.isWorking = false; player.resetWorkStatus(); } @@ -1182,7 +1183,13 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript return false; } - player.startCreateProgramWork(p.name, create.time, create.level); + player.startNEWWork( + new CreateProgramWork({ + programName: p.name, + singularity: true, + player: player, + }), + ); if (focus) { player.startFocusing(); Router.toWork(); diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 11a64b0fe..c6d137716 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -1,6 +1,7 @@ import { CheckBox, CheckBoxOutlineBlank, Construction } from "@mui/icons-material"; import { Box, Button, Container, List, ListItemButton, Paper, Typography } from "@mui/material"; import React, { useEffect, useState } from "react"; +import { GraftingWork } from "../../../Work/GraftingWork"; import { Augmentation } from "../../../Augmentation/Augmentation"; import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames"; import { StaticAugmentations } from "../../../Augmentation/StaticAugmentations"; @@ -19,7 +20,7 @@ import { IPlayer } from "../../IPlayer"; import { GraftableAugmentation } from "../GraftableAugmentation"; import { calculateGraftingTimeWithBonus, getGraftingAvailableAugs } from "../GraftingHelpers"; -const GraftableAugmentations: IMap = {}; +export const GraftableAugmentations: IMap = {}; const canGraft = (player: IPlayer, aug: GraftableAugmentation): boolean => { if (player.money < aug.cost) { @@ -154,9 +155,13 @@ export const GraftingRoot = (): React.ReactElement => { open={graftOpen} onClose={() => setGraftOpen(false)} onConfirm={() => { - const graftableAug = GraftableAugmentations[selectedAug]; - player.loseMoney(graftableAug.cost, "augmentations"); - player.startGraftAugmentationWork(selectedAug, graftableAug.time); + player.startNEWWork( + new GraftingWork({ + augmentation: selectedAug, + singularity: false, + player: player, + }), + ); player.startFocusing(); router.toWork(); }} diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index eae06b8ab..369f62e30 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -126,12 +126,7 @@ export interface IPlayer extends IPerson { bladeburner_success_chance_mult: number; currentWork: Work | null; - createProgramReqLvl: number; factionWorkType: string; - createProgramName: string; - timeWorkedCreateProgram: number; - graftAugmentationName: string; - timeWorkedGraftAugmentation: number; timeNeededToCompleteWork: number; focus: boolean; currentWorkFactionName: string; @@ -227,7 +222,6 @@ export interface IPlayer extends IPerson { quitJob(company: string, sing?: boolean): void; hasJob(): boolean; createHacknetServer(): HacknetServer; - startCreateProgramWork(programName: string, time: number, reqLevel: number): void; queueAugmentation(augmentationName: string): void; receiveInvite(factionName: string): void; updateSkillLevels(): void; @@ -237,7 +231,6 @@ export interface IPlayer extends IPerson { finishWork(cancelled: boolean, sing?: boolean): string; cancelationPenalty(): number; finishWorkPartTime(sing?: boolean): string; - finishCreateProgramWork(cancelled: boolean): string; resetMultipliers(): void; prestigeAugmentation(): void; prestigeSourceFile(): void; @@ -253,15 +246,11 @@ export interface IPlayer extends IPerson { getWorkMoneyGain(): number; processWorkEarnings(cycles: number): void; hospitalize(): void; - createProgramWork(numCycles: number): boolean; checkForFactionInvitations(): Faction[]; setBitNodeNumber(n: number): void; getMult(name: string): number; setMult(name: string, mult: number): void; canAccessCotMG(): boolean; sourceFileLvl(n: number): number; - startGraftAugmentationWork(augmentationName: string, time: number): void; - graftAugmentationWork(numCycles: number): boolean; - finishGraftAugmentationWork(cancelled: boolean, singularity?: boolean): string; applyEntropy(stacks?: number): void; } diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index b1e196bba..1afc0d70d 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -138,12 +138,7 @@ export class PlayerObject implements IPlayer { bladeburner_success_chance_mult: number; currentWork: Work | null; - createProgramReqLvl: number; factionWorkType: PlayerFactionWorkType; - createProgramName: string; - timeWorkedCreateProgram: number; - graftAugmentationName: string; - timeWorkedGraftAugmentation: number; timeNeededToCompleteWork: number; focus: boolean; currentWorkFactionName: string; @@ -252,7 +247,6 @@ export class PlayerObject implements IPlayer { hasJob: () => boolean; process: (router: IRouter, numCycles?: number) => void; createHacknetServer: () => HacknetServer; - startCreateProgramWork: (programName: string, time: number, reqLevel: number) => void; queueAugmentation: (augmentationName: string) => void; receiveInvite: (factionName: string) => void; updateSkillLevels: () => void; @@ -262,7 +256,6 @@ export class PlayerObject implements IPlayer { finishWork: (cancelled: boolean, sing?: boolean) => string; cancelationPenalty: () => number; finishWorkPartTime: (sing?: boolean) => string; - finishCreateProgramWork: (cancelled: boolean) => string; resetMultipliers: () => void; prestigeAugmentation: () => void; prestigeSourceFile: () => void; @@ -279,16 +272,12 @@ export class PlayerObject implements IPlayer { getWorkMoneyGain: () => number; processWorkEarnings: (cycles: number) => void; hospitalize: () => void; - createProgramWork: (numCycles: number) => boolean; checkForFactionInvitations: () => Faction[]; setBitNodeNumber: (n: number) => void; getMult: (name: string) => number; setMult: (name: string, mult: number) => void; canAccessCotMG: () => boolean; sourceFileLvl: (n: number) => number; - startGraftAugmentationWork: (augmentationName: string, time: number) => void; - graftAugmentationWork: (numCycles: number) => boolean; - finishGraftAugmentationWork: (cancelled: boolean, singularity?: boolean) => string; applyEntropy: (stacks?: number) => void; constructor() { @@ -410,14 +399,7 @@ export class PlayerObject implements IPlayer { this.workRepGained = 0; this.workMoneyGained = 0; - this.createProgramName = ""; - this.createProgramReqLvl = 0; - - this.graftAugmentationName = ""; - this.timeWorkedGraftAugmentation = 0; - this.timeWorked = 0; //in m; - this.timeWorkedCreateProgram = 0; this.timeNeededToCompleteWork = 0; this.work_money_mult = 1; @@ -538,12 +520,6 @@ export class PlayerObject implements IPlayer { this.getWorkChaExpGain = generalMethods.getWorkChaExpGain; this.getWorkRepGain = generalMethods.getWorkRepGain; this.process = generalMethods.process; - this.startCreateProgramWork = generalMethods.startCreateProgramWork; - this.createProgramWork = generalMethods.createProgramWork; - this.finishCreateProgramWork = generalMethods.finishCreateProgramWork; - this.startGraftAugmentationWork = generalMethods.startGraftAugmentationWork; - this.graftAugmentationWork = generalMethods.craftAugmentationWork; - this.finishGraftAugmentationWork = generalMethods.finishGraftAugmentationWork; this.singularityStopWork = generalMethods.singularityStopWork; this.takeDamage = generalMethods.takeDamage; this.regenerateHp = generalMethods.regenerateHp; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index d046e888d..e475cdba3 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -66,9 +66,8 @@ import { FactionNames } from "../../Faction/data/FactionNames"; import { ITaskTracker } from "../ITaskTracker"; import { IPerson } from "../IPerson"; import { Player } from "../../Player"; -import { graftingIntBonus } from "../Grafting/GraftingHelpers"; -import { WorkType, PlayerFactionWorkType, ClassType, CrimeType } from "../../utils/WorkType"; +import { WorkType, PlayerFactionWorkType } from "../../utils/WorkType"; export function init(this: IPlayer): void { /* Initialize Player's home computer */ @@ -143,7 +142,6 @@ export function prestigeAugmentation(this: PlayerObject): void { this.isWorking = false; this.currentWorkFactionName = ""; this.currentWorkFactionDescription = ""; - this.createProgramName = ""; this.workHackExpGainRate = 0; this.workStrExpGainRate = 0; @@ -537,13 +535,9 @@ export function resetWorkStatus(this: IPlayer, generalType?: WorkType, group?: s this.workMoneyGained = 0; this.timeWorked = 0; - this.timeWorkedCreateProgram = 0; - this.timeWorkedGraftAugmentation = 0; this.currentWorkFactionName = ""; this.currentWorkFactionDescription = ""; - this.createProgramName = ""; - this.graftAugmentationName = ""; this.workType = WorkType.None; } @@ -603,18 +597,10 @@ export function process(this: IPlayer, router: IRouter, numCycles = 1): void { if (this.workForFaction(numCycles)) { router.toFaction(Factions[this.currentWorkFactionName]); } - } else if (this.workType === WorkType.CreateProgram) { - if (this.createProgramWork(numCycles)) { - router.toTerminal(); - } } else if (this.workType === WorkType.CompanyPartTime) { if (this.workPartTime(numCycles)) { router.toCity(); } - } else if (this.workType === WorkType.GraftAugmentation) { - if (this.graftAugmentationWork(numCycles)) { - router.toGrafting(); - } } else if (this.work(numCycles)) { router.toCity(); } @@ -1257,143 +1243,6 @@ export function getWorkRepGain(this: IPlayer): number { // return t * this.faction_rep_mult; // } -/* Creating a Program */ -export function startCreateProgramWork(this: IPlayer, programName: string, time: number, reqLevel: number): void { - this.resetWorkStatus(); - this.isWorking = true; - this.workType = WorkType.CreateProgram; - - //Time needed to complete work affected by hacking skill (linearly based on - //ratio of (your skill - required level) to MAX skill) - //var timeMultiplier = (CONSTANTS.MaxSkillLevel - (this.hacking - reqLevel)) / CONSTANTS.MaxSkillLevel; - //if (timeMultiplier > 1) {timeMultiplier = 1;} - //if (timeMultiplier < 0.01) {timeMultiplier = 0.01;} - this.createProgramReqLvl = reqLevel; - - this.timeNeededToCompleteWork = time; - //Check for incomplete program - for (let i = 0; i < this.getHomeComputer().programs.length; ++i) { - const programFile = this.getHomeComputer().programs[i]; - if (programFile.startsWith(programName) && programFile.endsWith("%-INC")) { - const res = programFile.split("-"); - if (res.length != 3) { - break; - } - const percComplete = Number(res[1].slice(0, -1)); - if (isNaN(percComplete) || percComplete < 0 || percComplete >= 100) { - break; - } - this.timeWorkedCreateProgram = (percComplete / 100) * this.timeNeededToCompleteWork; - this.getHomeComputer().programs.splice(i, 1); - } - } - - this.createProgramName = programName; -} - -export function createProgramWork(this: IPlayer, numCycles: number): boolean { - let focusBonus = 1; - if (!this.hasAugmentation(AugmentationNames["NeuroreceptorManager"])) { - focusBonus = this.focus ? 1 : CONSTANTS.BaseFocusBonus; - } - //Higher hacking skill will allow you to create programs faster - const reqLvl = this.createProgramReqLvl; - let skillMult = (this.hacking / reqLvl) * this.getIntelligenceBonus(3); //This should always be greater than 1; - skillMult = 1 + (skillMult - 1) / 5; //The divider constant can be adjusted as necessary - skillMult *= focusBonus; - //Skill multiplier directly applied to "time worked" - this.timeWorked += CONSTANTS._idleSpeed * numCycles; - this.timeWorkedCreateProgram += CONSTANTS._idleSpeed * numCycles * skillMult; - - if (this.timeWorkedCreateProgram >= this.timeNeededToCompleteWork) { - this.finishCreateProgramWork(false); - return true; - } - return false; -} - -export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string { - const programName = this.createProgramName; - let message = ""; - 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."]; - dialogBoxCreate(lines.join("
")); - message = lines.join(" "); - - if (!this.getHomeComputer().programs.includes(programName)) { - this.getHomeComputer().programs.push(programName); - } - } else if (!this.getHomeComputer().programs.includes(programName)) { - //Incomplete case - const perc = (Math.floor((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 10000) / 100).toString(); - const incompleteName = programName + "-" + perc + "%-INC"; - this.getHomeComputer().programs.push(incompleteName); - message = `Cancelled creating program: ${programName} (${perc}% complete)`; - } - - this.isWorking = false; - this.resetWorkStatus(); - return message; -} - -export function startGraftAugmentationWork(this: IPlayer, augmentationName: string, time: number): void { - this.resetWorkStatus(); - this.isWorking = true; - this.workType = WorkType.GraftAugmentation; - - this.timeNeededToCompleteWork = time; - this.graftAugmentationName = augmentationName; -} - -export function craftAugmentationWork(this: IPlayer, numCycles: number): boolean { - let focusBonus = 1; - if (!this.hasAugmentation(AugmentationNames.NeuroreceptorManager)) { - focusBonus = this.focus ? 1 : CONSTANTS.BaseFocusBonus; - } - - let skillMult = graftingIntBonus(this); - skillMult *= focusBonus; - - this.timeWorked += CONSTANTS._idleSpeed * numCycles; - this.timeWorkedGraftAugmentation += CONSTANTS._idleSpeed * numCycles * skillMult; - - if (this.timeWorkedGraftAugmentation >= this.timeNeededToCompleteWork) { - this.finishGraftAugmentationWork(false); - return true; - } - return false; -} - -export function finishGraftAugmentationWork(this: IPlayer, cancelled: boolean, singularity = false): string { - const augName = this.graftAugmentationName; - if (cancelled === false) { - applyAugmentation({ name: augName, level: 1 }); - - if (!this.hasAugmentation(AugmentationNames.CongruityImplant)) { - this.entropy += 1; - this.applyEntropy(this.entropy); - } - - dialogBoxCreate( - `You've finished grafting ${augName}.
The augmentation has been applied to your body` + - (this.hasAugmentation(AugmentationNames.CongruityImplant) ? "." : ", but you feel a bit off."), - ); - } else if (cancelled && singularity === false) { - dialogBoxCreate(`You cancelled the grafting of ${augName}.
Your money was not returned to you.`); - } - - // Intelligence gain - if (!cancelled) { - this.gainIntelligenceExp((CONSTANTS.IntelligenceGraftBaseExpGain * this.timeWorked) / 10000); - } - - this.isWorking = false; - this.resetWorkStatus(); - return `Grafting of ${augName} has ended.`; -} - //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 { @@ -1414,12 +1263,6 @@ export function singularityStopWork(this: IPlayer): string { case WorkType.Faction: res = this.finishFactionWork(true, true); break; - case WorkType.CreateProgram: - res = this.finishCreateProgramWork(true); - break; - case WorkType.GraftAugmentation: - res = this.finishGraftAugmentationWork(true, true); - break; default: console.error(`Unrecognized work type (${this.workType})`); return ""; diff --git a/src/Programs/ui/ProgramsRoot.tsx b/src/Programs/ui/ProgramsRoot.tsx index 357a9e78a..0a4b8017c 100644 --- a/src/Programs/ui/ProgramsRoot.tsx +++ b/src/Programs/ui/ProgramsRoot.tsx @@ -8,6 +8,7 @@ import { use } from "../../ui/Context"; import { Settings } from "../../Settings/Settings"; import { Programs } from "../Programs"; +import { CreateProgramWork } from "../../Work/CreateProgramWork"; export const ProgramsSeen: string[] = []; @@ -96,7 +97,9 @@ export function ProgramsRoot(): React.ReactElement { sx={{ my: 1, width: "100%" }} onClick={(event) => { if (!event.isTrusted) return; - player.startCreateProgramWork(program.name, create.time, create.level); + player.startNEWWork( + new CreateProgramWork({ player: player, singularity: false, programName: program.name }), + ); player.startFocusing(); router.toWork(); }} diff --git a/src/SaveObject.tsx b/src/SaveObject.tsx index f4a269eed..22b9c206f 100755 --- a/src/SaveObject.tsx +++ b/src/SaveObject.tsx @@ -199,7 +199,7 @@ class BitburnerSaveObject { try { parsedSave = JSON.parse(newSave); } catch (error) { - console.log(error); // We'll handle below + console.error(error); // We'll handle below } if (!parsedSave || parsedSave.ctor !== "BitburnerSaveObject" || !parsedSave.data) { diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 75a67a456..3624f4ad1 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -68,8 +68,6 @@ interface Player { workChaExpGained: number; workRepGained: number; workMoneyGained: number; - createProgramName: string; - createProgramReqLvl: number; work_money_mult: number; hacknet_node_money_mult: number; hacknet_node_purchase_cost_mult: number; diff --git a/src/Server/AllServers.ts b/src/Server/AllServers.ts index cf6c7d694..47355e557 100644 --- a/src/Server/AllServers.ts +++ b/src/Server/AllServers.ts @@ -46,7 +46,6 @@ export function GetServer(s: string): BaseServer | null { const server = AllServers[s]; if (server) return server; } - console.log(AllServers); if (!isValidIPAddress(s)) { return GetServerByHostname(s); diff --git a/src/Work/ClassWork.tsx b/src/Work/ClassWork.tsx index 6c6b286e9..3e9eefef3 100644 --- a/src/Work/ClassWork.tsx +++ b/src/Work/ClassWork.tsx @@ -126,8 +126,6 @@ interface ClassWorkParams { export const isClassWork = (w: Work | null): w is ClassWork => w !== null && w.type === WorkType.CLASS; export class ClassWork extends Work { - type = WorkType.CLASS; - classType: ClassType; location: LocationName; cyclesWorked: number; @@ -135,7 +133,7 @@ export class ClassWork extends Work { earnings = newWorkStats(); constructor(params?: ClassWorkParams) { - super(); + super(WorkType.CLASS); this.classType = params?.classType ?? ClassType.StudyComputerScience; this.location = params?.location ?? LocationName.Sector12RothmanUniversity; this.singularity = params?.singularity ?? false; diff --git a/src/Work/CreateProgramWork.ts b/src/Work/CreateProgramWork.ts new file mode 100644 index 000000000..3b011c7d4 --- /dev/null +++ b/src/Work/CreateProgramWork.ts @@ -0,0 +1,125 @@ +import { dialogBoxCreate } from "../ui/React/DialogBox"; +import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; +import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; +import { CONSTANTS } from "../Constants"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { Programs } from "../Programs/Programs"; +import { Work, WorkType } from "./Work"; +import { Program } from "../Programs/Program"; + +export const isCreateProgramWork = (w: Work | null): w is CreateProgramWork => + w !== null && w.type === WorkType.CREATE_PROGRAM; + +interface CreateProgramWorkParams { + programName: string; + singularity: boolean; + player: IPlayer; +} + +export class CreateProgramWork extends Work { + programName: string; + // amount of cycles spent doing this task + cyclesWorked: number; + singularity: boolean; + // amount of effective work completed on the program (time boosted by skills). + unitCompleted: number; + + constructor(params?: CreateProgramWorkParams) { + super(WorkType.CREATE_PROGRAM); + this.cyclesWorked = 0; + this.unitCompleted = 0; + this.programName = params?.programName ?? ""; + this.singularity = params?.singularity ?? false; + + if (params?.player) { + const player = params.player; + for (let i = 0; i < player.getHomeComputer().programs.length; ++i) { + const programFile = player.getHomeComputer().programs[i]; + if (programFile.startsWith(this.programName) && programFile.endsWith("%-INC")) { + const res = programFile.split("-"); + if (res.length != 3) { + break; + } + const percComplete = Number(res[1].slice(0, -1)); + if (isNaN(percComplete) || percComplete < 0 || percComplete >= 100) { + break; + } + this.unitCompleted = (percComplete / 100) * this.unitNeeded(); + player.getHomeComputer().programs.splice(i, 1); + } + } + } + } + + unitNeeded(): number { + return this.getProgram().create?.time ?? 0; + } + + getProgram(): Program { + const p = Object.values(Programs).find((p) => p.name.toLowerCase() === this.programName.toLowerCase()); + if (!p) throw new Error("Create program work started with invalid program " + this.programName); + return p; + } + + process(player: IPlayer, cycles: number): boolean { + let focusBonus = 1; + if (!player.hasAugmentation(AugmentationNames["NeuroreceptorManager"])) { + focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus; + } + //Higher hacking skill will allow you to create programs faster + const reqLvl = this.getProgram().create?.level ?? 0; + let skillMult = (player.hacking / reqLvl) * player.getIntelligenceBonus(3); //This should always be greater than 1; + skillMult = 1 + (skillMult - 1) / 5; //The divider constant can be adjusted as necessary + skillMult *= focusBonus; + //Skill multiplier directly applied to "time worked" + this.cyclesWorked += cycles; + this.unitCompleted += CONSTANTS._idleSpeed * cycles * skillMult; + + if (this.unitCompleted >= this.unitNeeded()) { + return true; + } + return false; + } + finish(player: IPlayer, cancelled: boolean): void { + const programName = this.programName; + if (!cancelled) { + //Complete case + player.gainIntelligenceExp( + (CONSTANTS.IntelligenceProgramBaseExpGain * this.cyclesWorked * CONSTANTS._idleSpeed) / 1000, + ); + if (!this.singularity) { + const lines = [ + `You've finished creating ${programName}!`, + "The new program can be found on your home computer.", + ]; + dialogBoxCreate(lines.join("
")); + } + + if (!player.getHomeComputer().programs.includes(programName)) { + player.getHomeComputer().programs.push(programName); + } + } else if (!player.getHomeComputer().programs.includes(programName)) { + //Incomplete case + const perc = ((100 * this.unitCompleted) / this.unitNeeded()).toFixed(2); + const incompleteName = programName + "-" + perc + "%-INC"; + player.getHomeComputer().programs.push(incompleteName); + } + } + + /** + * Serialize the current object to a JSON save state. + */ + toJSON(): any { + return Generic_toJSON("CreateProgramWork", this); + } + + /** + * Initiatizes a CreateProgramWork object from a JSON save state. + */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + static fromJSON(value: any): CreateProgramWork { + return Generic_fromJSON(CreateProgramWork, value.data); + } +} + +Reviver.constructors.CreateProgramWork = CreateProgramWork; diff --git a/src/Work/CrimeWork.tsx b/src/Work/CrimeWork.tsx index 14b0be4e6..912eb8801 100644 --- a/src/Work/CrimeWork.tsx +++ b/src/Work/CrimeWork.tsx @@ -19,14 +19,12 @@ interface CrimeWorkParams { export const isCrimeWork = (w: Work | null): w is CrimeWork => w !== null && w.type === WorkType.CRIME; export class CrimeWork extends Work { - type = WorkType.CRIME; - crimeType: CrimeType; cyclesWorked: number; singularity: boolean; constructor(params?: CrimeWorkParams) { - super(); + super(WorkType.CRIME); this.crimeType = params?.crimeType ?? CrimeType.Shoplift; this.singularity = params?.singularity ?? false; this.cyclesWorked = 0; diff --git a/src/Work/GraftingWork.tsx b/src/Work/GraftingWork.tsx new file mode 100644 index 000000000..2ed9ddb4f --- /dev/null +++ b/src/Work/GraftingWork.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { CONSTANTS } from "../Constants"; +import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; +import { GraftableAugmentations } from "../PersonObjects/Grafting/ui/GraftingRoot"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { Work, WorkType } from "./Work"; +import { graftingIntBonus } from "../PersonObjects/Grafting/GraftingHelpers"; +import { applyAugmentation } from "../Augmentation/AugmentationHelpers"; +import { dialogBoxCreate } from "../ui/React/DialogBox"; +import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; +import { GraftableAugmentation } from "../PersonObjects/Grafting/GraftableAugmentation"; +import { StaticAugmentations } from "../Augmentation/StaticAugmentations"; + +export const isGraftingWork = (w: Work | null): w is GraftingWork => w !== null && w.type === WorkType.GRAFTING; + +interface GraftingWorkParams { + augmentation: string; + singularity: boolean; + player: IPlayer; +} + +export class GraftingWork extends Work { + augmentation: string; + singularity: boolean; + cyclesWorked: number; + unitCompleted: number; + + constructor(params?: GraftingWorkParams) { + super(WorkType.GRAFTING); + this.cyclesWorked = 0; + this.unitCompleted = 0; + this.augmentation = params?.augmentation ?? AugmentationNames.Targeting1; + this.singularity = params?.singularity ?? false; + if (params?.player) params.player.loseMoney(GraftableAugmentations[this.augmentation].cost, "augmentations"); + } + + unitNeeded(): number { + return new GraftableAugmentation(StaticAugmentations[this.augmentation]).time; + } + + process(player: IPlayer, cycles: number): boolean { + let focusBonus = 1; + if (!player.hasAugmentation(AugmentationNames.NeuroreceptorManager)) { + focusBonus = player.focus ? 1 : CONSTANTS.BaseFocusBonus; + } + + this.cyclesWorked += cycles; + this.unitCompleted += CONSTANTS._idleSpeed * cycles * graftingIntBonus(player) * focusBonus; + + return this.unitCompleted >= this.unitNeeded(); + } + + finish(player: IPlayer, cancelled: boolean): void { + const augName = this.augmentation; + if (!cancelled) { + applyAugmentation({ name: augName, level: 1 }); + + if (!player.hasAugmentation(AugmentationNames.CongruityImplant)) { + player.entropy += 1; + player.applyEntropy(player.entropy); + } + + if (!this.singularity) { + dialogBoxCreate( + <> + You've finished grafting {augName}.
+ The augmentation has been applied to your body{" "} + {player.hasAugmentation(AugmentationNames.CongruityImplant) ? "." : ", but you feel a bit off."} + , + ); + } + } else if (cancelled && !this.singularity) { + dialogBoxCreate( + <> + You cancelled the grafting of {augName}. +
+ Your money was not returned to you. + , + ); + } + + // Intelligence gain + if (!cancelled) { + player.gainIntelligenceExp( + (CONSTANTS.IntelligenceGraftBaseExpGain * this.cyclesWorked * CONSTANTS._idleSpeed) / 10000, + ); + } + } + + /** + * Serialize the current object to a JSON save state. + */ + toJSON(): any { + return Generic_toJSON("GraftingWork", this); + } + + /** + * Initiatizes a GraftingWork object from a JSON save state. + */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + static fromJSON(value: any): GraftingWork { + return Generic_fromJSON(GraftingWork, value.data); + } +} + +Reviver.constructors.GraftingWork = GraftingWork; diff --git a/src/Work/Work.ts b/src/Work/Work.ts index b04b09d0b..964643376 100644 --- a/src/Work/Work.ts +++ b/src/Work/Work.ts @@ -1,13 +1,20 @@ import { IPlayer } from "src/PersonObjects/IPlayer"; export abstract class Work { - abstract type: WorkType; + type: WorkType; + + constructor(type: WorkType) { + this.type = type; + } abstract process(player: IPlayer, cycles: number): boolean; abstract finish(player: IPlayer, cancelled: boolean): void; + abstract toJSON(): any; } export enum WorkType { CRIME = "CRIME", CLASS = "CLASS", + CREATE_PROGRAM = "CREATE_PROGRAM", + GRAFTING = "GRAFTING", } diff --git a/src/engine.tsx b/src/engine.tsx index 80672b636..e58df6b9a 100644 --- a/src/engine.tsx +++ b/src/engine.tsx @@ -306,15 +306,9 @@ const Engine: { case WorkType.Faction: Player.workForFaction(numCyclesOffline); break; - case WorkType.CreateProgram: - Player.createProgramWork(numCyclesOffline); - break; case WorkType.CompanyPartTime: Player.workPartTime(numCyclesOffline); break; - case WorkType.GraftAugmentation: - Player.graftAugmentationWork(numCyclesOffline); - break; default: Player.work(numCyclesOffline); } diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index edb6a8b19..e5db96ed7 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -294,7 +294,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme Router[fnName] = _wrap(Router[fnName], (func, ...args) => { if (!allowRoutingCalls) { // Let's just log to console. - console.log(`Routing is currently disabled - Attempted router.${fnName}()`); + console.error(`Routing is currently disabled - Attempted router.${fnName}()`); return; } diff --git a/src/ui/React/AlertManager.tsx b/src/ui/React/AlertManager.tsx index 0952d378c..f78a342d0 100644 --- a/src/ui/React/AlertManager.tsx +++ b/src/ui/React/AlertManager.tsx @@ -24,7 +24,6 @@ export function AlertManager(): React.ReactElement { setAlerts((old) => { const hash = getMessageHash(text); if (old.some((a) => a.hash === hash)) { - console.log("Duplicate message"); return old; } return [ diff --git a/src/ui/React/CharacterOverview.tsx b/src/ui/React/CharacterOverview.tsx index 27dad018a..2f9a82722 100644 --- a/src/ui/React/CharacterOverview.tsx +++ b/src/ui/React/CharacterOverview.tsx @@ -29,6 +29,8 @@ import { Box, Tooltip } from "@mui/material"; import { WorkType } from "../../utils/WorkType"; import { isClassWork } from "../../Work/ClassWork"; import { CONSTANTS } from "../../Constants"; +import { isCreateProgramWork } from "../../Work/CreateProgramWork"; +import { isGraftingWork } from "../../Work/GraftingWork"; interface IProps { save: () => void; @@ -150,6 +152,26 @@ function Work(): React.ReactElement { header = <>You are {player.currentWork.getClass().youAreCurrently}; innerText = <>{convertTimeMsToTimeElapsedString(player.currentWork.cyclesWorked * CONSTANTS._idleSpeed)}; } + if (isCreateProgramWork(player.currentWork)) { + const create = player.currentWork; + details = <>Coding {create.programName}; + header = <>Creating a program; + innerText = ( + <> + {create.programName} {((create.unitCompleted / create.unitNeeded()) * 100).toFixed(2)}% + + ); + } + if (isGraftingWork(player.currentWork)) { + const graft = player.currentWork; + details = <>Grafting {graft.augmentation}; + header = <>Grafting an Augmentation; + innerText = ( + <> + {((graft.unitCompleted / graft.unitNeeded()) * 100).toFixed(2)}% done + + ); + } switch (player.workType) { case WorkType.CompanyPartTime: case WorkType.Company: @@ -186,25 +208,6 @@ function Work(): React.ReactElement { ); break; - case WorkType.CreateProgram: - details = <>Coding {player.createProgramName}; - header = <>Creating a program; - innerText = ( - <> - {player.createProgramName}{" "} - {((player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100).toFixed(2)}% - - ); - break; - case WorkType.GraftAugmentation: - details = <>Grafting {player.graftAugmentationName}; - header = <>Grafting an Augmentation; - innerText = ( - <> - {((player.timeWorkedGraftAugmentation / player.timeNeededToCompleteWork) * 100).toFixed(2)}%{" "} - done - - ); } return ( diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index 84a27bcfb..2e7d03f64 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -22,7 +22,9 @@ import { StatsRow } from "./React/StatsRow"; import { WorkType, ClassType } from "../utils/WorkType"; import { isCrimeWork } from "../Work/CrimeWork"; import { isClassWork } from "../Work/ClassWork"; -import { WorkStats } from "src/Work/WorkStats"; +import { WorkStats } from "../Work/WorkStats"; +import { isCreateProgramWork } from "../Work/CreateProgramWork"; +import { isGraftingWork } from "../Work/GraftingWork"; const CYCLES_PER_SEC = 1000 / CONSTANTS.MilliPerCycle; @@ -301,6 +303,76 @@ export function WorkInProgressRoot(): React.ReactElement { stopText: stopText, }; } + + if (isCreateProgramWork(player.currentWork)) { + const create = player.currentWork; + function cancel(): void { + player.finishNEWWork(true); + router.toTerminal(); + } + function unfocus(): void { + router.toTerminal(); + player.stopFocusing(); + } + + const completion = (create.unitCompleted / create.unitNeeded()) * 100; + + workInfo = { + buttons: { + cancel: cancel, + unfocus: unfocus, + }, + title: ( + <> + You are currently working on coding {create.programName} + + ), + + progress: { + elapsed: create.cyclesWorked * CONSTANTS._idleSpeed, + percentage: completion, + }, + + stopText: "Stop creating program", + stopTooltip: "Your work will be saved and you can return to complete the program later.", + }; + } + + if (isGraftingWork(player.currentWork)) { + const graft = player.currentWork; + function cancel(): void { + player.finishNEWWork(true); + router.toTerminal(); + } + function unfocus(): void { + router.toTerminal(); + player.stopFocusing(); + } + + workInfo = { + buttons: { + cancel: cancel, + unfocus: unfocus, + }, + title: ( + <> + You are currently working on grafting {graft.augmentation} + + ), + + progress: { + elapsed: graft.cyclesWorked * CONSTANTS._idleSpeed, + percentage: (graft.unitCompleted / graft.unitNeeded()) * 100, + }, + + stopText: "Stop grafting", + stopTooltip: ( + <> + If you cancel, your work will not be saved, and the money you spent will not be returned + + ), + }; + } } switch (player.workType) { @@ -509,80 +581,6 @@ export function WorkInProgressRoot(): React.ReactElement { break; } - case WorkType.CreateProgram: { - function cancel(): void { - player.finishCreateProgramWork(true); - router.toTerminal(); - } - function unfocus(): void { - router.toTerminal(); - player.stopFocusing(); - } - - const completion = (player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100; - - workInfo = { - buttons: { - cancel: cancel, - unfocus: unfocus, - }, - title: ( - <> - You are currently working on coding {player.createProgramName} - - ), - - progress: { - elapsed: player.timeWorked, - percentage: completion, - }, - - stopText: "Stop creating program", - stopTooltip: "Your work will be saved and you can return to complete the program later.", - }; - - break; - } - - case WorkType.GraftAugmentation: { - function cancel(): void { - player.finishGraftAugmentationWork(true); - router.toTerminal(); - } - function unfocus(): void { - router.toTerminal(); - player.stopFocusing(); - } - - const completion = (player.timeWorkedGraftAugmentation / player.timeNeededToCompleteWork) * 100; - - workInfo = { - buttons: { - cancel: cancel, - unfocus: unfocus, - }, - title: ( - <> - You are currently working on grafting {player.graftAugmentationName} - - ), - - progress: { - elapsed: player.timeWorked, - percentage: completion, - }, - - stopText: "Stop grafting", - stopTooltip: ( - <> - If you cancel, your work will not be saved, and the money you spent will not be returned - - ), - }; - - break; - } - default: if (player.currentWork === null) { router.toTerminal();