From 919f8480dd469c79d42598b35cbb78af03c86f0d Mon Sep 17 00:00:00 2001 From: nickofolas Date: Fri, 18 Mar 2022 23:22:21 -0500 Subject: [PATCH 01/30] Grafting UI framework --- src/Locations/ui/SpecialLocation.tsx | 72 ++++++------ src/PersonObjects/Grafting/formulas/.gitkeep | 0 .../Grafting/ui/GraftingRoot.tsx | 103 ++++++++++++++++++ src/PersonObjects/IPlayer.ts | 2 +- src/PersonObjects/Player/PlayerObject.ts | 4 +- .../Player/PlayerObjectGeneralMethods.tsx | 2 +- src/Sidebar/ui/SidebarRoot.tsx | 2 +- src/ui/GameRoot.tsx | 10 +- src/ui/Router.ts | 4 +- 9 files changed, 151 insertions(+), 48 deletions(-) create mode 100644 src/PersonObjects/Grafting/formulas/.gitkeep create mode 100644 src/PersonObjects/Grafting/ui/GraftingRoot.tsx diff --git a/src/Locations/ui/SpecialLocation.tsx b/src/Locations/ui/SpecialLocation.tsx index d7be2c170..6e0d77d6e 100644 --- a/src/Locations/ui/SpecialLocation.tsx +++ b/src/Locations/ui/SpecialLocation.tsx @@ -54,7 +54,7 @@ export function SpecialLocation(props: IProps): React.ReactElement { router.toBladeburner(); } else if (p.strength >= 100 && p.defense >= 100 && p.dexterity >= 100 && p.agility >= 100) { // Apply for Bladeburner division - p.startBladeburner({new: true}); + p.startBladeburner({ new: true }); dialogBoxCreate("You have been accepted into the Bladeburner division!"); setRerender((old) => !old); @@ -71,8 +71,8 @@ export function SpecialLocation(props: IProps): React.ReactElement { /** * Click handler for Resleeving button at New Tokyo VitaLife */ - function handleResleeving(): void { - router.toResleeves(); + function handleGrafting(): void { + router.toGrafting(); } function renderBladeburner(): React.ReactElement { @@ -150,11 +150,11 @@ export function SpecialLocation(props: IProps): React.ReactElement { ); } - function renderResleeving(): React.ReactElement { - if (!player.canAccessResleeving()) { + function renderGrafting(): React.ReactElement { + if (!player.canAccessGrafting()) { return <>; } - return ; + return ; } function handleCotMG(): void { @@ -174,35 +174,35 @@ export function SpecialLocation(props: IProps): React.ReactElement { function renderCotMG(): React.ReactElement { // prettier-ignore - const symbol = - {" `` "}
- {" -odmmNmds: "}
- {" `hNmo:..-omNh. "}
- {" yMd` `hNh "}
- {" mMd oNm "}
- {" oMNo .mM/ "}
- {" `dMN+ -mM+ "}
- {" -mMNo -mN+ "}
- {" .+- :mMNo/mN/ "}
- {":yNMd. :NMNNN/ "}
- {"-mMMMh. /NMMh` "}
- {" .dMMMd. /NMMMy` "}
- {" `yMMMd. /NNyNMMh` "}
- {" `sMMMd. +Nm: +NMMh. "}
- {" oMMMm- oNm: /NMMd. "}
- {" +NMMmsMm- :mMMd. "}
- {" /NMMMm- -mMMd. "}
- {" /MMMm- -mMMd. "}
- {" `sMNMMm- .mMmo "}
- {" `sMd:hMMm. ./. "}
- {" `yMy` `yNMd` "}
- {" `hMs` oMMy "}
- {" `hMh sMN- "}
- {" /MM- .NMo "}
- {" +MM: :MM+ "}
- {" sNNo-.`.-omNy` "}
- {" -smNNNNmdo- "}
- {" `..` "}
+ const symbol = + {" `` "}
+ {" -odmmNmds: "}
+ {" `hNmo:..-omNh. "}
+ {" yMd` `hNh "}
+ {" mMd oNm "}
+ {" oMNo .mM/ "}
+ {" `dMN+ -mM+ "}
+ {" -mMNo -mN+ "}
+ {" .+- :mMNo/mN/ "}
+ {":yNMd. :NMNNN/ "}
+ {"-mMMMh. /NMMh` "}
+ {" .dMMMd. /NMMMy` "}
+ {" `yMMMd. /NNyNMMh` "}
+ {" `sMMMd. +Nm: +NMMh. "}
+ {" oMMMm- oNm: /NMMd. "}
+ {" +NMMmsMm- :mMMd. "}
+ {" /NMMMm- -mMMd. "}
+ {" /MMMm- -mMMd. "}
+ {" `sMNMMm- .mMmo "}
+ {" `sMd:hMMm. ./. "}
+ {" `yMy` `yNMd` "}
+ {" `hMs` oMMy "}
+ {" `hMh sMN- "}
+ {" /MM- .NMo "}
+ {" +MM: :MM+ "}
+ {" sNNo-.`.-omNy` "}
+ {" -smNNNNmdo- "}
+ {" `..` "}
if (player.hasAugmentation(AugmentationNames.StaneksGift3, true)) { return ( <> @@ -298,7 +298,7 @@ export function SpecialLocation(props: IProps): React.ReactElement { switch (props.loc.name) { case LocationName.NewTokyoVitaLife: { - return renderResleeving(); + return renderGrafting(); } case LocationName.Sector12CityHall: { return ; diff --git a/src/PersonObjects/Grafting/formulas/.gitkeep b/src/PersonObjects/Grafting/formulas/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx new file mode 100644 index 000000000..7efa09eca --- /dev/null +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -0,0 +1,103 @@ +import React, { useState } from "react"; + +import { + Typography, + Container, + Box, + Paper, + List, + ListItemButton, + Button +} from "@mui/material"; +import { + Construction +} from "@mui/icons-material"; + +import { use } from "../../../ui/Context"; +import { Augmentations } from "../../../Augmentation/Augmentations"; +import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames" +import { Settings } from "../../../Settings/Settings"; + +import { IPlayer } from "../../IPlayer"; + +const getAvailableAugs = (player: IPlayer): string[] => { + const augs: string[] = []; + + for (const [augName, aug] of Object.entries(Augmentations)) { + if ( + augName === AugmentationNames.NeuroFluxGovernor || + augName === AugmentationNames.TheRedPill || + aug.isSpecial + ) continue; + augs.push(augName); + } + + return augs.filter( + (augmentation: string) => !player.hasAugmentation(augmentation) + ); +} + +export const GraftingRoot = (): React.ReactElement => { + const player = use.Player(); + const [selectedAug, setSelectedAug] = useState(getAvailableAugs(player)[0]); + + return <> + + Grafting Laboratory + + blah blah blah exposition that isn't important right now
+ Lorem ipsum dolor sit amet et et sed et et sanctus duo vero. + Stet amet iriure consetetur elit in magna et diam dolores invidunt ipsum gubergren nihil. + Diam et et ipsum consectetuer voluptua et clita lorem sit. + Et et lorem id no suscipit wisi. + Illum velit takimata et aliquyam takimata labore vel dolor dolores duo amet lorem elitr facer invidunt. +
+ + + Craft Augmentations + + here goes a list with available augmentations with a purchase button (with price shown) to the side of it
+ getAvailableAugs function to the rescue +
+ + + {getAvailableAugs(player).map((k, i) => ( + setSelectedAug(k)} selected={selectedAug === k}> + + {k} + + + ))} + + + + {selectedAug} + + + + Time to Craft: bar + + + {(() => { + const aug = Augmentations[selectedAug]; + + const info = typeof aug.info === "string" ? {aug.info} : aug.info + const tooltip = (<>{info}

{aug.stats}); + return tooltip; + })()} +
+
+
+
+ + + name tbd + + probably some info about the cumulative negative effects here + + +
+ +} diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index 7b5053adc..24470271e 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -181,7 +181,7 @@ export interface IPlayer { canAccessBladeburner(): boolean; canAccessCorporation(): boolean; canAccessGang(): boolean; - canAccessResleeving(): boolean; + canAccessGrafting(): boolean; canAfford(cost: number): boolean; gainHackingExp(exp: number): void; gainStrengthExp(exp: number): void; diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index 0ecf6628c..4d1500ef1 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -190,7 +190,7 @@ export class PlayerObject implements IPlayer { canAccessBladeburner: () => boolean; canAccessCorporation: () => boolean; canAccessGang: () => boolean; - canAccessResleeving: () => boolean; + canAccessGrafting: () => boolean; canAfford: (cost: number) => boolean; gainHackingExp: (exp: number) => void; gainStrengthExp: (exp: number) => void; @@ -577,7 +577,7 @@ export class PlayerObject implements IPlayer { this.gainCodingContractReward = generalMethods.gainCodingContractReward; this.travel = generalMethods.travel; this.gotoLocation = generalMethods.gotoLocation; - this.canAccessResleeving = generalMethods.canAccessResleeving; + this.canAccessGrafting = generalMethods.canAccessGrafting; this.giveExploit = generalMethods.giveExploit; this.giveAchievement = generalMethods.giveAchievement; this.getIntelligenceBonus = generalMethods.getIntelligenceBonus; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index ee97fc982..238e2893f 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -2641,7 +2641,7 @@ export function gotoLocation(this: IPlayer, to: LocationName): boolean { return true; } -export function canAccessResleeving(this: IPlayer): boolean { +export function canAccessGrafting(this: IPlayer): boolean { return this.bitNodeN === 10 || SourceFileFlags[10] > 0; } diff --git a/src/Sidebar/ui/SidebarRoot.tsx b/src/Sidebar/ui/SidebarRoot.tsx index bd1a1fbf8..b1d98e2ea 100644 --- a/src/Sidebar/ui/SidebarRoot.tsx +++ b/src/Sidebar/ui/SidebarRoot.tsx @@ -617,7 +617,7 @@ export function SidebarRoot(props: IProps): React.ReactElement { key={"City"} className={clsx({ [classes.active]: - props.page === Page.City || props.page === Page.Resleeves || props.page === Page.Location, + props.page === Page.City || props.page === Page.Grafting || props.page === Page.Location, })} onClick={clickCity} > diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 40790ab3e..ed0114ccc 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -42,7 +42,7 @@ import { BladeburnerRoot } from "../Bladeburner/ui/BladeburnerRoot"; import { GangRoot } from "../Gang/ui/GangRoot"; import { CorporationRoot } from "../Corporation/ui/CorporationRoot"; import { InfiltrationRoot } from "../Infiltration/ui/InfiltrationRoot"; -import { ResleeveRoot } from "../PersonObjects/Resleeving/ui/ResleeveRoot"; +import { GraftingRoot } from "../PersonObjects/Grafting/ui/GraftingRoot"; import { WorkInProgressRoot } from "./WorkInProgressRoot"; import { GameOptionsRoot } from "./React/GameOptionsRoot"; import { SleeveRoot } from "../PersonObjects/Sleeve/ui/SleeveRoot"; @@ -135,7 +135,7 @@ export let Router: IRouter = { toInfiltration: uninitialized, toJob: uninitialized, toMilestones: uninitialized, - toResleeves: uninitialized, + toGrafting: uninitialized, toScriptEditor: uninitialized, toSleeves: uninitialized, toStockMarket: uninitialized, @@ -226,7 +226,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme toGang: () => setPage(Page.Gang), toHacknetNodes: () => setPage(Page.Hacknet), toMilestones: () => setPage(Page.Milestones), - toResleeves: () => setPage(Page.Resleeves), + toGrafting: () => setPage(Page.Grafting), toScriptEditor: (files: Record, options?: ScriptEditorRouteOptions) => { setEditorOptions({ files, @@ -429,8 +429,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme mainPage = ; break; } - case Page.Resleeves: { - mainPage = ; + case Page.Grafting: { + mainPage = ; break; } case Page.Travel: { diff --git a/src/ui/Router.ts b/src/ui/Router.ts index 2e4613611..e761d917c 100644 --- a/src/ui/Router.ts +++ b/src/ui/Router.ts @@ -23,7 +23,7 @@ export enum Page { Job, Milestones, Options, - Resleeves, + Grafting, Sleeves, Stats, StockMarket, @@ -74,7 +74,7 @@ export interface IRouter { toInfiltration(location: Location): void; toJob(): void; toMilestones(): void; - toResleeves(): void; + toGrafting(): void; toScriptEditor(files?: Record, options?: ScriptEditorRouteOptions): void; toSleeves(): void; toStockMarket(): void; From 06425d6d6374de9c07b868abad5b11995cd6f4a4 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 08:43:30 -0500 Subject: [PATCH 02/30] Implement new aug filtering for gangs --- src/Faction/ui/AugmentationsPage.tsx | 14 +++++++++----- src/Faction/ui/FactionsRoot.tsx | 6 +++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Faction/ui/AugmentationsPage.tsx b/src/Faction/ui/AugmentationsPage.tsx index 1d519b124..e086a4ee4 100644 --- a/src/Faction/ui/AugmentationsPage.tsx +++ b/src/Faction/ui/AugmentationsPage.tsx @@ -45,12 +45,16 @@ export function AugmentationsPage(props: IProps): React.ReactElement { if (isPlayersGang) { const augs: string[] = []; for (const augName of Object.keys(Augmentations)) { - if (augName === AugmentationNames.NeuroFluxGovernor) continue; - if (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) continue; const aug = Augmentations[augName]; - if (!aug.isSpecial) { - augs.push(augName); - } + if ( + augName === AugmentationNames.NeuroFluxGovernor || + augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2 || + // Special augs (i.e. Bladeburner augs) + aug.isSpecial || + // Exclusive augs (i.e. QLink) + (aug.factions.length <= 1 && !props.faction.augmentations.includes(augName)) + ) continue; + augs.push(augName); } return augs; diff --git a/src/Faction/ui/FactionsRoot.tsx b/src/Faction/ui/FactionsRoot.tsx index 45e750232..db039ef4e 100644 --- a/src/Faction/ui/FactionsRoot.tsx +++ b/src/Faction/ui/FactionsRoot.tsx @@ -64,10 +64,14 @@ export function FactionsRoot(props: IProps): React.ReactElement { if (isPlayersGang) { for (const augName of Object.keys(Augmentations)) { + const aug = Augmentations[augName]; if ( augName === AugmentationNames.NeuroFluxGovernor || augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2 || - Augmentations[augName].isSpecial + // Special augs (i.e. Bladeburner augs) + aug.isSpecial || + // Exclusive augs (i.e. QLink) + (aug.factions.length <= 1 && !faction.augmentations.includes(augName)) ) continue; augs.push(augName) } From 78f962318cb4a24c580874414143c88bcd06d853 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 08:56:02 -0500 Subject: [PATCH 03/30] Add router to grafting root --- src/PersonObjects/Grafting/ui/GraftingRoot.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 7efa09eca..123098804 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -39,6 +39,7 @@ const getAvailableAugs = (player: IPlayer): string[] => { export const GraftingRoot = (): React.ReactElement => { const player = use.Player(); + const router = use.Router(); const [selectedAug, setSelectedAug] = useState(getAvailableAugs(player)[0]); return <> From 4789455b9c96d93401555f714a35cba386283cc6 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 09:31:48 -0500 Subject: [PATCH 04/30] Implement basic crafting functionality --- src/Constants.ts | 2 + .../Grafting/ui/GraftingRoot.tsx | 11 +++- src/PersonObjects/IPlayer.ts | 5 ++ src/PersonObjects/Player/PlayerObject.ts | 11 ++++ .../Player/PlayerObjectGeneralMethods.tsx | 56 +++++++++++++++++++ src/engine.tsx | 2 + src/ui/WorkInProgressRoot.tsx | 32 +++++++++++ 7 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/Constants.ts b/src/Constants.ts index febc03e46..9688f0416 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -71,6 +71,7 @@ export const CONSTANTS: { WorkTypeCreateProgram: string; WorkTypeStudyClass: string; WorkTypeCrime: string; + WorkTypeCraftAugmentation: string; ClassStudyComputerScience: string; ClassDataStructures: string; ClassNetworks: string; @@ -224,6 +225,7 @@ export const CONSTANTS: { WorkTypeCreateProgram: "Working on Create a Program", WorkTypeStudyClass: "Studying or Taking a class at university", WorkTypeCrime: "Committing a crime", + WorkTypeCraftAugmentation: "Crafting an Augmentation", ClassStudyComputerScience: "studying Computer Science", ClassDataStructures: "taking a Data Structures course", diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 123098804..1f58b43d5 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -17,6 +17,7 @@ import { use } from "../../../ui/Context"; import { Augmentations } from "../../../Augmentation/Augmentations"; import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames" import { Settings } from "../../../Settings/Settings"; +import { CONSTANTS } from "../../../Constants"; import { IPlayer } from "../../IPlayer"; @@ -74,7 +75,15 @@ export const GraftingRoot = (): React.ReactElement => { {selectedAug} - diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index 24470271e..1a9e41762 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -130,6 +130,8 @@ export interface IPlayer { factionWorkType: string; createProgramName: string; timeWorkedCreateProgram: number; + craftAugmentationName: string; + timeWorkedCraftAugmentation: number; crimeType: string; committingCrimeThruSingFn: boolean; singFnCrimeWorkerScript: WorkerScript | null; @@ -286,4 +288,7 @@ export interface IPlayer { setMult(name: string, mult: number): void; canAccessCotMG(): boolean; sourceFileLvl(n: number): number; + startCraftAugmentationWork(augmentationName: string, time: number): void; + craftAugmentationWork(numCycles: number): boolean; + finishCraftAugmentationWork(cancelled: boolean): string; } diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index 4d1500ef1..dd5dbfc29 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -139,6 +139,8 @@ export class PlayerObject implements IPlayer { factionWorkType: string; createProgramName: string; timeWorkedCreateProgram: number; + craftAugmentationName: string; + timeWorkedCraftAugmentation: number; crimeType: string; committingCrimeThruSingFn: boolean; singFnCrimeWorkerScript: WorkerScript | null; @@ -296,6 +298,9 @@ export class PlayerObject implements IPlayer { setMult: (name: string, mult: number) => void; canAccessCotMG: () => boolean; sourceFileLvl: (n: number) => number; + startCraftAugmentationWork: (augmentationName: string, time: number) => void; + craftAugmentationWork: (numCycles: number) => boolean; + finishCraftAugmentationWork: (cancelled: boolean) => string; constructor() { //Skills and stats @@ -419,6 +424,9 @@ export class PlayerObject implements IPlayer { this.createProgramName = ""; this.createProgramReqLvl = 0; + this.craftAugmentationName = ""; + this.timeWorkedCraftAugmentation = 0; + this.className = ""; this.crimeType = ""; @@ -541,6 +549,9 @@ export class PlayerObject implements IPlayer { this.startCreateProgramWork = generalMethods.startCreateProgramWork; this.createProgramWork = generalMethods.createProgramWork; this.finishCreateProgramWork = generalMethods.finishCreateProgramWork; + this.startCraftAugmentationWork = generalMethods.startCraftAugmentationWork; + this.craftAugmentationWork = generalMethods.craftAugmentationWork; + this.finishCraftAugmentationWork = generalMethods.finishCraftAugmentationWork; this.startClass = generalMethods.startClass; this.takeClass = generalMethods.takeClass; this.finishClass = generalMethods.finishClass; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 238e2893f..bc90bcac1 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -528,10 +528,12 @@ export function resetWorkStatus(this: IPlayer, generalType?: string, group?: str this.timeWorked = 0; this.timeWorkedCreateProgram = 0; + this.timeWorkedCraftAugmentation = 0; this.currentWorkFactionName = ""; this.currentWorkFactionDescription = ""; this.createProgramName = ""; + this.craftAugmentationName = ""; this.className = ""; this.workType = ""; } @@ -608,6 +610,10 @@ export function process(this: IPlayer, router: IRouter, numCycles = 1): void { if (this.workPartTime(numCycles)) { router.toCity(); } + } else if (this.workType === CONSTANTS.WorkTypeCraftAugmentation) { + if (this.craftAugmentationWork(numCycles)) { + router.toGrafting(); + } } else if (this.work(numCycles)) { router.toCity(); } @@ -1331,6 +1337,56 @@ export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): stri this.resetWorkStatus(); return "You've finished creating " + programName + "! The new program can be found on your home computer."; } + +export function startCraftAugmentationWork( + this: IPlayer, + augmentationName: string, + time: number, +): void { + this.resetWorkStatus() + this.isWorking = true; + this.workType = CONSTANTS.WorkTypeCraftAugmentation; + + this.timeNeededToCompleteWork = time; + this.craftAugmentationName = augmentationName; +} + +export function craftAugmentationWork(this: IPlayer, numCycles: number): boolean { + let focusBonus = 1; + if (!this.hasAugmentation(AugmentationNames.NeuroreceptorManager)) { + focusBonus = this.focus ? 1 : CONSTANTS.BaseFocusBonus; + } + + // TODO: formula logic here (focus bonus and stuff) + let skillMult = 1; + skillMult *= focusBonus; + + this.timeWorked += CONSTANTS._idleSpeed * numCycles; + this.timeWorkedCraftAugmentation += CONSTANTS._idleSpeed * numCycles * skillMult; + + if (this.timeWorkedCraftAugmentation >= this.timeNeededToCompleteWork) { + this.finishCraftAugmentationWork(false); + return true; + } + return false; +} + +export function finishCraftAugmentationWork(this: IPlayer, cancelled: boolean): string { + const augName = this.craftAugmentationName; + if (cancelled === false) { + dialogBoxCreate(`You've finished crafting ${augName}.
The augmentation has been grafted to you, but you feel slightly lightheaded.`) + + applyAugmentation(Augmentations[augName]); + } else { + dialogBoxCreate(`You cancelled the crafting of ${augName}.
Your money was not returned to you.`) + } + + // TODO: intelligence EXP stuff here later + this.isWorking = false; + this.resetWorkStatus(); + return `Crafting of ${augName} has ended.` +} + /* Studying/Taking Classes */ export function startClass(this: IPlayer, costMult: number, expMult: number, className: string): void { this.resetWorkStatus(); diff --git a/src/engine.tsx b/src/engine.tsx index 18689a08f..6464d3309 100644 --- a/src/engine.tsx +++ b/src/engine.tsx @@ -298,6 +298,8 @@ const Engine: { Player.commitCrime(numCyclesOffline); } else if (Player.workType == CONSTANTS.WorkTypeCompanyPartTime) { Player.workPartTime(numCyclesOffline); + } else if (Player.workType === CONSTANTS.WorkTypeCraftAugmentation) { + Player.craftAugmentationWork(numCyclesOffline); } else { Player.work(numCyclesOffline); } diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index 7a4c1802a..810815ff1 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -494,6 +494,38 @@ export function WorkInProgressRoot(): React.ReactElement { ); } + if (player.craftAugmentationName !== "") { + function cancel(): void { + player.finishCraftAugmentationWork(true); + router.toTerminal(); + } + function unfocus(): void { + router.toTerminal(); + player.stopFocusing(); + } + return ( + + + + You are currently working on crafting {player.craftAugmentationName}. +

+ You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)} +

+ The augumentation if {((player.timeWorkedCraftAugmentation / player.timeNeededToCompleteWork) * 100).toFixed(2)} + % complete.
+ If you cancel, your work will not be saved, and the money you spent will not be returned. +
+
+ + + + +
+ ) + } + if (!player.workType) router.toTerminal(); return <>; From fef5ab31b2c7ca2d2cf307f647452d4c3c6ba5e4 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 11:03:18 -0500 Subject: [PATCH 05/30] Implement `CraftableAugmentation` --- src/Constants.ts | 7 +++++ .../Grafting/CraftableAugmentation.ts | 26 ++++++++++++++++++ .../Grafting/ui/GraftingRoot.tsx | 27 ++++++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 src/PersonObjects/Grafting/CraftableAugmentation.ts diff --git a/src/Constants.ts b/src/Constants.ts index 9688f0416..7dd447c7c 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -109,6 +109,8 @@ export const CONSTANTS: { CodingContractBaseFactionRepGain: number; CodingContractBaseCompanyRepGain: number; CodingContractBaseMoneyGain: number; + AugmentationCraftingCostMult: number; + AugmentationCraftingTimeMult: number; TotalNumBitNodes: number; LatestUpdate: string; } = { @@ -271,6 +273,11 @@ export const CONSTANTS: { CodingContractBaseCompanyRepGain: 4000, CodingContractBaseMoneyGain: 75e6, + // Augmentation crafting multipliers + // TODO: Get these right + AugmentationCraftingCostMult: 1.2, + AugmentationCraftingTimeMult: 1, + // BitNode/Source-File related stuff TotalNumBitNodes: 24, diff --git a/src/PersonObjects/Grafting/CraftableAugmentation.ts b/src/PersonObjects/Grafting/CraftableAugmentation.ts new file mode 100644 index 000000000..5f7785c16 --- /dev/null +++ b/src/PersonObjects/Grafting/CraftableAugmentation.ts @@ -0,0 +1,26 @@ +import { Augmentation } from "../../Augmentation/Augmentation"; +import { CONSTANTS } from "../../Constants"; + +export interface IConstructorParams { + augmentation: Augmentation; + readonly cost: number; + readonly time: number; +} + +export class CraftableAugmentation { + // The augmentation that this craftable corresponds to + augmentation: Augmentation; + + constructor(augmentation: Augmentation) { + this.augmentation = augmentation; + } + + get cost(): number { + return this.augmentation.startingCost * CONSTANTS.AugmentationCraftingCostMult; + } + + get time(): number { + // CONSTANTS.AugmentationCraftingTimeMult + return 15000; + } +} diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 1f58b43d5..9a3b05534 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -14,13 +14,19 @@ import { } from "@mui/icons-material"; import { use } from "../../../ui/Context"; +import { Money } from "../../../ui/React/Money"; import { Augmentations } from "../../../Augmentation/Augmentations"; import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames" import { Settings } from "../../../Settings/Settings"; -import { CONSTANTS } from "../../../Constants"; +import { IMap } from "../../../types"; +import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; import { IPlayer } from "../../IPlayer"; +import { CraftableAugmentation } from "../CraftableAugmentation"; + +const CraftableAugmentations: IMap = {} + const getAvailableAugs = (player: IPlayer): string[] => { const augs: string[] = []; @@ -41,6 +47,13 @@ const getAvailableAugs = (player: IPlayer): string[] => { export const GraftingRoot = (): React.ReactElement => { const player = use.Player(); const router = use.Router(); + + for (const aug of Object.values(Augmentations)) { + const name = aug.name; + const craftableAug = new CraftableAugmentation(aug); + CraftableAugmentations[name] = craftableAug; + } + const [selectedAug, setSelectedAug] = useState(getAvailableAugs(player)[0]); return <> @@ -78,16 +91,22 @@ export const GraftingRoot = (): React.ReactElement => { - Time to Craft: bar + Time to Craft: {convertTimeMsToTimeElapsedString(CraftableAugmentations[selectedAug].time)} {(() => { From d2dd88c5cad11a95b5d55b6a9dc5590cd629e1e4 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 11:04:15 -0500 Subject: [PATCH 06/30] Polish some UI --- .../Player/PlayerObjectGeneralMethods.tsx | 2 +- src/ui/React/CharacterOverview.tsx | 105 ++++++++++-------- src/ui/WorkInProgressRoot.tsx | 3 +- 3 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 7aa9bd41d..71c51205c 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -1372,7 +1372,7 @@ export function craftAugmentationWork(this: IPlayer, numCycles: number): boolean export function finishCraftAugmentationWork(this: IPlayer, cancelled: boolean): string { const augName = this.craftAugmentationName; if (cancelled === false) { - dialogBoxCreate(`You've finished crafting ${augName}.
The augmentation has been grafted to you, but you feel slightly lightheaded.`) + dialogBoxCreate(`You've finished crafting ${augName}.
The augmentation has been grafted to your body, but you feel a bit off.`) applyAugmentation(Augmentations[augName]); } else { diff --git a/src/ui/React/CharacterOverview.tsx b/src/ui/React/CharacterOverview.tsx index f57f3b1bb..34e7b81eb 100644 --- a/src/ui/React/CharacterOverview.tsx +++ b/src/ui/React/CharacterOverview.tsx @@ -143,51 +143,66 @@ function Work(): React.ReactElement { let details = <>; let header = <>; let innerText = <>; - if (player.workType === CONSTANTS.WorkTypeCompanyPartTime || player.workType === CONSTANTS.WorkTypeCompany) { - details = ( - <> - {player.jobs[player.companyName]} at {player.companyName} - - ); - header = ( - <> - Working at {player.companyName} - - ); - innerText = ( - <> - + rep - - ); - } else if (player.workType === CONSTANTS.WorkTypeFaction) { - details = ( - <> - {player.factionWorkType} for {player.currentWorkFactionName} - - ); - header = ( - <> - Working for {player.currentWorkFactionName} - - ); - innerText = ( - <> - + rep - - ); - } else if (player.workType === CONSTANTS.WorkTypeStudyClass) { - details = <>{player.workType}; - header = <>You are {player.className}; - innerText = <>{convertTimeMsToTimeElapsedString(player.timeWorked)}; - } else if (player.workType === CONSTANTS.WorkTypeCreateProgram) { - details = <>Coding {player.createProgramName}; - header = <>Creating a program; - innerText = ( - <> - {player.createProgramName}{" "} - {((player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100).toFixed(2)}% - - ); + switch (player.workType) { + case CONSTANTS.WorkTypeCompanyPartTime: + case CONSTANTS.WorkTypeCompany: + details = ( + <> + {player.jobs[player.companyName]} at {player.companyName} + + ); + header = ( + <> + Working at {player.companyName} + + ); + innerText = ( + <> + + rep + + ); + break; + case CONSTANTS.WorkTypeFaction: + details = ( + <> + {player.factionWorkType} for {player.currentWorkFactionName} + + ); + header = ( + <> + Working for {player.currentWorkFactionName} + + ); + innerText = ( + <> + + rep + + ); + break; + case CONSTANTS.WorkTypeStudyClass: + details = <>{player.workType}; + header = <>You are {player.className}; + innerText = <>{convertTimeMsToTimeElapsedString(player.timeWorked)}; + break; + case CONSTANTS.WorkTypeCreateProgram: + details = <>Coding {player.createProgramName}; + header = <>Creating a program; + innerText = ( + <> + {player.createProgramName}{" "} + {((player.timeWorkedCreateProgram / player.timeNeededToCompleteWork) * 100).toFixed(2)}% + + ); + break; + case CONSTANTS.WorkTypeCraftAugmentation: + details = <>Crafting {player.craftAugmentationName}; + header = <>Crafting an Augmentation; + innerText = ( + <> + {convertTimeMsToTimeElapsedString(player.timeNeededToCompleteWork - player.timeWorkedCraftAugmentation)} + {" "}remaining + + ); } return ( diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index 810815ff1..8738d76e9 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -511,8 +511,7 @@ export function WorkInProgressRoot(): React.ReactElement {

You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)}

- The augumentation if {((player.timeWorkedCraftAugmentation / player.timeNeededToCompleteWork) * 100).toFixed(2)} - % complete.
+ The augmentation will be done in {convertTimeMsToTimeElapsedString(player.timeNeededToCompleteWork - player.timeWorkedCraftAugmentation)}.
If you cancel, your work will not be saved, and the money you spent will not be returned.
From a70969df144d9667bf49e3b42035c271e4f1c821 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 11:17:28 -0500 Subject: [PATCH 07/30] Add back button to GraftingRoot --- src/PersonObjects/Grafting/ui/GraftingRoot.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 9a3b05534..15e4f4652 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -20,6 +20,8 @@ import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames" import { Settings } from "../../../Settings/Settings"; import { IMap } from "../../../types"; import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; +import { LocationName } from "../../../Locations/data/LocationNames"; +import { Locations } from "../../../Locations/Locations"; import { IPlayer } from "../../IPlayer"; @@ -58,6 +60,9 @@ export const GraftingRoot = (): React.ReactElement => { return <> + Grafting Laboratory blah blah blah exposition that isn't important right now
From c33c23700eeff2677e155126a97250598d77a37e Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 11:43:58 -0500 Subject: [PATCH 08/30] Remove resleeving from code --- src/PersonObjects/IPlayer.ts | 2 - src/PersonObjects/Player/PlayerObject.ts | 3 - .../Player/PlayerObjectGeneralMethods.tsx | 2 - src/PersonObjects/Resleeving/README.md | 10 -- src/PersonObjects/Resleeving/Resleeve.ts | 63 ------- src/PersonObjects/Resleeving/Resleeving.ts | 133 -------------- .../Resleeving/ui/ResleeveElem.tsx | 166 ------------------ .../Resleeving/ui/ResleeveRoot.tsx | 124 ------------- 8 files changed, 503 deletions(-) delete mode 100644 src/PersonObjects/Resleeving/README.md delete mode 100644 src/PersonObjects/Resleeving/Resleeve.ts delete mode 100644 src/PersonObjects/Resleeving/Resleeving.ts delete mode 100644 src/PersonObjects/Resleeving/ui/ResleeveElem.tsx delete mode 100644 src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index 1a9e41762..ed15ce028 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -3,7 +3,6 @@ * Used because at the time of implementation, the PlayerObject * cant be converted to TypeScript. */ -import { Resleeve } from "./Resleeving/Resleeve"; import { Sleeve } from "./Sleeve/Sleeve"; import { IMap } from "../types"; @@ -65,7 +64,6 @@ export interface IPlayer { playtimeSinceLastBitnode: number; purchasedServers: any[]; queuedAugmentations: IPlayerOwnedAugmentation[]; - resleeves: Resleeve[]; scriptProdSinceLastAug: number; sleeves: Sleeve[]; sleevesFromCovenant: number; diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index dd5dbfc29..211131f30 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -6,7 +6,6 @@ import * as generalMethods from "./PlayerObjectGeneralMethods"; import * as serverMethods from "./PlayerObjectServerMethods"; import { IMap } from "../../types"; -import { Resleeve } from "../Resleeving/Resleeve"; import { Sleeve } from "../Sleeve/Sleeve"; import { IPlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile"; import { Exploit } from "../../Exploits/Exploit"; @@ -72,7 +71,6 @@ export class PlayerObject implements IPlayer { playtimeSinceLastBitnode: number; purchasedServers: any[]; queuedAugmentations: IPlayerOwnedAugmentation[]; - resleeves: Resleeve[]; scriptProdSinceLastAug: number; sleeves: Sleeve[]; sleevesFromCovenant: number; @@ -465,7 +463,6 @@ export class PlayerObject implements IPlayer { // Sleeves & Re-sleeving this.sleeves = []; - this.resleeves = []; this.sleevesFromCovenant = 0; // # of Duplicate sleeves purchased from the covenan; //bitnode this.bitNodeN = 1; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 71c51205c..03bb85edb 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -121,8 +121,6 @@ export function prestigeAugmentation(this: PlayerObject): void { this.queuedAugmentations = []; - this.resleeves = []; - const numSleeves = Math.min(3, SourceFileFlags[10] + (this.bitNodeN === 10 ? 1 : 0)) + this.sleevesFromCovenant; if (this.sleeves.length > numSleeves) this.sleeves.length = numSleeves; for (let i = this.sleeves.length; i < numSleeves; i++) { diff --git a/src/PersonObjects/Resleeving/README.md b/src/PersonObjects/Resleeving/README.md deleted file mode 100644 index ba3116b94..000000000 --- a/src/PersonObjects/Resleeving/README.md +++ /dev/null @@ -1,10 +0,0 @@ -Implements the Re-sleeving feature, which allows players to purchase a new body -that comes with pre-existing Augmentations and experience. Note that purchasing -a new body causes you to lose all of your old Augmentations and experience - -This feature is introduced in BitNode-10, and destroying BitNode-10 allows -the user to use it in other BitNodes (provided that they purchase the required -cortical stack Augmentation) - -While they are based on the same concept, this feature is different than the -"Duplicate Sleeve" mechanic (which is referred to as just "Sleeve" in the source code). diff --git a/src/PersonObjects/Resleeving/Resleeve.ts b/src/PersonObjects/Resleeving/Resleeve.ts deleted file mode 100644 index 165ab2bda..000000000 --- a/src/PersonObjects/Resleeving/Resleeve.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Implements the Resleeve class, which defines a new body - * that the player can "re-sleeve" into. - */ -import { Person } from "../Person"; - -import { Augmentation } from "../../Augmentation/Augmentation"; -import { Augmentations } from "../../Augmentation/Augmentations"; - -import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver"; - -export class Resleeve extends Person { - constructor() { - super(); - } - - getCost(): number { - // Each experience point adds this to the cost - const CostPerExp = 25e3; - - // Final cost is multiplied by this constant ^ # Augs - const NumAugsExponent = 1.2; - - // Get total exp in this re-sleeve - const totalExp: number = - this.hacking_exp + - this.strength_exp + - this.defense_exp + - this.dexterity_exp + - this.agility_exp + - this.charisma_exp; - - // Get total base Augmentation cost for this re-sleeve - let totalAugmentationCost = 0; - for (let i = 0; i < this.augmentations.length; ++i) { - const aug: Augmentation | null = Augmentations[this.augmentations[i].name]; - if (aug == null) { - console.error(`Could not find Augmentation ${this.augmentations[i].name}`); - continue; - } - totalAugmentationCost += aug.startingCost; - } - - return totalExp * CostPerExp + totalAugmentationCost * Math.pow(NumAugsExponent, this.augmentations.length); - } - - /** - * Serialize the current object to a JSON save state. - */ - toJSON(): any { - return Generic_toJSON("Resleeve", this); - } - - /** - * Initiatizes a Resleeve object from a JSON save state. - */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - static fromJSON(value: any): Resleeve { - return Generic_fromJSON(Resleeve, value.data); - } -} - -Reviver.constructors.Resleeve = Resleeve; diff --git a/src/PersonObjects/Resleeving/Resleeving.ts b/src/PersonObjects/Resleeving/Resleeving.ts deleted file mode 100644 index 2d3f2daa6..000000000 --- a/src/PersonObjects/Resleeving/Resleeving.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Implements the Re-sleeving mechanic for BitNode-10. - * This allows the player to purchase and "use" new sleeves at VitaLife. - * These new sleeves come with different starting experience and Augmentations - * The cost of these new sleeves scales based on the exp and Augs. - * - * Note that this is different from the "Sleeve mechanic". The "Sleeve" mechanic - * provides new sleeves, essentially clones. This Re-sleeving mechanic lets - * the player purchase a new body with pre-existing Augmentations and experience - * - * As of right now, this feature is only available in BitNode 10 - */ -import { Resleeve } from "./Resleeve"; -import { IPlayer } from "../IPlayer"; - -import { Augmentation } from "../../Augmentation/Augmentation"; -import { Augmentations } from "../../Augmentation/Augmentations"; -import { IPlayerOwnedAugmentation, PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation"; -import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; - -import { getRandomInt } from "../../utils/helpers/getRandomInt"; - -// Executes the actual re-sleeve when one is purchased -export function purchaseResleeve(r: Resleeve, p: IPlayer): boolean { - const cost: number = r.getCost(); - if (!p.canAfford(cost)) { - return false; - } - p.loseMoney(cost, "other"); - - // Set the player's exp - p.hacking_exp = r.hacking_exp; - p.strength_exp = r.strength_exp; - p.defense_exp = r.defense_exp; - p.dexterity_exp = r.dexterity_exp; - p.agility_exp = r.agility_exp; - p.charisma_exp = r.charisma_exp; - - // Reset Augmentation "owned" data - for (const augKey of Object.keys(Augmentations)) { - Augmentations[augKey].owned = false; - } - - // Clear all of the player's augmentations, except the NeuroFlux Governor - // which is kept - for (let i = p.augmentations.length - 1; i >= 0; --i) { - if (p.augmentations[i].name !== AugmentationNames.NeuroFluxGovernor) { - p.augmentations.splice(i, 1); - } else { - // NeuroFlux Governor - Augmentations[AugmentationNames.NeuroFluxGovernor].owned = true; - } - } - - for (let i = 0; i < r.augmentations.length; ++i) { - p.augmentations.push(new PlayerOwnedAugmentation(r.augmentations[i].name)); - Augmentations[r.augmentations[i].name].owned = true; - } - - // The player's purchased Augmentations should remain the same, but any purchased - // Augmentations that are given by the resleeve should be removed so there are no duplicates - for (let i = p.queuedAugmentations.length - 1; i >= 0; --i) { - const name: string = p.queuedAugmentations[i].name; - - if ( - p.augmentations.filter((e: IPlayerOwnedAugmentation) => { - return e.name !== AugmentationNames.NeuroFluxGovernor && e.name === name; - }).length >= 1 - ) { - p.queuedAugmentations.splice(i, 1); - } - } - - p.reapplyAllAugmentations(true); - p.reapplyAllSourceFiles(); //Multipliers get reset, so have to re-process source files too - return true; -} - -// Creates all of the Re-sleeves that will be available for purchase at VitaLife -export function generateResleeves(): Resleeve[] { - const NumResleeves = 40; // Total number of Resleeves to generate - - const ret: Resleeve[] = []; - for (let i = 0; i < NumResleeves; ++i) { - // i will be a number indicating how "powerful" the Re-sleeve should be - const r: Resleeve = new Resleeve(); - - // Generate experience - const expMult: number = 5 * i + 1; - r.hacking_exp = expMult * getRandomInt(1000, 5000); - r.strength_exp = expMult * getRandomInt(1000, 5000); - r.defense_exp = expMult * getRandomInt(1000, 5000); - r.dexterity_exp = expMult * getRandomInt(1000, 5000); - r.agility_exp = expMult * getRandomInt(1000, 5000); - r.charisma_exp = expMult * getRandomInt(1000, 5000); - - // Generate Augs - // Augmentation prequisites will be ignored for this - const baseNumAugs: number = Math.max(2, Math.ceil((i + 3) / 2)); - const numAugs: number = getRandomInt(baseNumAugs, baseNumAugs + 2); - const augKeys: string[] = Object.keys(Augmentations); - for (let a = 0; a < numAugs; ++a) { - // Get a random aug - const randIndex: number = getRandomInt(0, augKeys.length - 1); - const randKey: string = augKeys[randIndex]; - - // Forbidden augmentations - const forbidden = [ - AugmentationNames.TheRedPill, - AugmentationNames.NeuroFluxGovernor, - AugmentationNames.StaneksGift1, - AugmentationNames.StaneksGift2, - AugmentationNames.StaneksGift3, - ]; - if (forbidden.includes(randKey)) { - continue; - } - - const randAug: Augmentation | null = Augmentations[randKey]; - if (randAug === null) throw new Error(`null augmentation: ${randKey}`); - r.augmentations.push({ name: randAug.name, level: 1 }); - r.applyAugmentation(Augmentations[randKey]); - r.updateStatLevels(); - - // Remove Augmentation so that there are no duplicates - augKeys.splice(randIndex, 1); - } - - ret.push(r); - } - - return ret; -} diff --git a/src/PersonObjects/Resleeving/ui/ResleeveElem.tsx b/src/PersonObjects/Resleeving/ui/ResleeveElem.tsx deleted file mode 100644 index 69d58961b..000000000 --- a/src/PersonObjects/Resleeving/ui/ResleeveElem.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import React, { useState } from "react"; -import { IPlayer } from "../../IPlayer"; -import { Resleeve } from "../Resleeve"; -import { Augmentations } from "../../../Augmentation/Augmentations"; -import { purchaseResleeve } from "../Resleeving"; -import { Money } from "../../../ui/React/Money"; - -import { numeralWrapper } from "../../../ui/numeralFormat"; -import { dialogBoxCreate } from "../../../ui/React/DialogBox"; - -import Typography from "@mui/material/Typography"; -import Paper from "@mui/material/Paper"; -import Button from "@mui/material/Button"; -import Select, { SelectChangeEvent } from "@mui/material/Select"; -import MenuItem from "@mui/material/MenuItem"; -import Grid from "@mui/material/Grid"; - -interface IProps { - resleeve: Resleeve; - player: IPlayer; -} - -export function ResleeveElem(props: IProps): React.ReactElement { - const [aug, setAug] = useState(props.resleeve.augmentations[0].name); - - function openStats(): void { - dialogBoxCreate( - <> - - Total Multipliers: - - - Hacking Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_mult)} -
- Hacking Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_exp_mult)} -
- Strength Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_mult)} -
- Strength Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_exp_mult)} -
- Defense Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_mult)} -
- Defense Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_exp_mult)} -
- Dexterity Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_mult)} -
- Dexterity Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_exp_mult)} -
- Agility Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_mult)} -
- Agility Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_exp_mult)} -
- Charisma Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_mult)} -
- Charisma Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_exp_mult)} -
- Hacking Chance multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_chance_mult)} -
- Hacking Speed multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_speed_mult)} -
- Hacking Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_money_mult)} -
- Hacking Growth multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_grow_mult)} -
- Salary multiplier: {numeralWrapper.formatPercentage(props.resleeve.work_money_mult)} -
- Company Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.company_rep_mult)} -
- Faction Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.faction_rep_mult)} -
- Crime Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_money_mult)} -
- Crime Success multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_success_mult)} -
- Hacknet Income multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_money_mult)} -
- Hacknet Purchase Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_purchase_cost_mult)} -
- Hacknet Level Upgrade Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_level_cost_mult)} -
- Hacknet Ram Upgrade Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_ram_cost_mult)} -
- Hacknet Core Upgrade Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_core_cost_mult)} -
- Bladeburner Max Stamina multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_max_stamina_mult)} -
- Bladeburner Stamina Gain multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_stamina_gain_mult)} -
- Bladeburner Field Analysis multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_analysis_mult)} -
- Bladeburner Success Chance multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_success_chance_mult)} -
- , - ); - } - - function onAugChange(event: SelectChangeEvent): void { - setAug(event.target.value); - } - - const currentAug = Augmentations[aug]; - const cost = props.resleeve.getCost(); - - function purchase(): void { - if (!purchaseResleeve(props.resleeve, props.player)) return; - dialogBoxCreate( - <> - You re-sleeved for ! - , - ); - } - - return ( - - - - - Hacking: {numeralWrapper.formatSkill(props.resleeve.hacking)} ( - {numeralWrapper.formatExp(props.resleeve.hacking_exp)} exp) -
- Strength: {numeralWrapper.formatSkill(props.resleeve.strength)} ( - {numeralWrapper.formatExp(props.resleeve.strength_exp)} exp) -
- Defense: {numeralWrapper.formatSkill(props.resleeve.defense)} ( - {numeralWrapper.formatExp(props.resleeve.defense_exp)} exp) -
- Dexterity: {numeralWrapper.formatSkill(props.resleeve.dexterity)} ( - {numeralWrapper.formatExp(props.resleeve.dexterity_exp)} exp) -
- Agility: {numeralWrapper.formatSkill(props.resleeve.agility)} ( - {numeralWrapper.formatExp(props.resleeve.agility_exp)} exp) -
- Charisma: {numeralWrapper.formatSkill(props.resleeve.charisma)} ( - {numeralWrapper.formatExp(props.resleeve.charisma_exp)} exp) -
# Augmentations: {props.resleeve.augmentations.length} -
- -
- - - {currentAug !== undefined && currentAug.info} - - - - It costs to purchase this Sleeve. - - - -
-
- ); -} diff --git a/src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx b/src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx deleted file mode 100644 index 5e0a7d139..000000000 --- a/src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { useState } from "react"; - -import { generateResleeves } from "../Resleeving"; -import { Resleeve } from "../Resleeve"; -import { ResleeveElem } from "./ResleeveElem"; -import { use } from "../../../ui/Context"; -import Typography from "@mui/material/Typography"; -import Select, { SelectChangeEvent } from "@mui/material/Select"; -import MenuItem from "@mui/material/MenuItem"; -import Box from "@mui/material/Box"; - -const SortOption: { - [key: string]: string | undefined; - Cost: string; - Hacking: string; - Strength: string; - Defense: string; - Dexterity: string; - Agility: string; - Charisma: string; - AverageCombatStats: string; - AverageAllStats: string; - TotalNumAugmentations: string; -} = { - Cost: "Cost", - Hacking: "Hacking Level", - Strength: "Strength Level", - Defense: "Defense Level", - Dexterity: "Dexterity Level", - Agility: "Agility Level", - Charisma: "Charisma Level", - AverageCombatStats: "Average Combat Stats", - AverageAllStats: "Average Stats", - TotalNumAugmentations: "Number of Augmentations", -}; - -// Helper function for averaging -function getAverage(...values: number[]): number { - let sum = 0; - for (let i = 0; i < values.length; ++i) { - sum += values[i]; - } - - return sum / values.length; -} - -const SortFunctions: { - [key: string]: ((a: Resleeve, b: Resleeve) => number) | undefined; - Cost: (a: Resleeve, b: Resleeve) => number; - Hacking: (a: Resleeve, b: Resleeve) => number; - Strength: (a: Resleeve, b: Resleeve) => number; - Defense: (a: Resleeve, b: Resleeve) => number; - Dexterity: (a: Resleeve, b: Resleeve) => number; - Agility: (a: Resleeve, b: Resleeve) => number; - Charisma: (a: Resleeve, b: Resleeve) => number; - AverageCombatStats: (a: Resleeve, b: Resleeve) => number; - AverageAllStats: (a: Resleeve, b: Resleeve) => number; - TotalNumAugmentations: (a: Resleeve, b: Resleeve) => number; -} = { - Cost: (a: Resleeve, b: Resleeve): number => a.getCost() - b.getCost(), - Hacking: (a: Resleeve, b: Resleeve): number => a.hacking - b.hacking, - Strength: (a: Resleeve, b: Resleeve): number => a.strength - b.strength, - Defense: (a: Resleeve, b: Resleeve): number => a.defense - b.defense, - Dexterity: (a: Resleeve, b: Resleeve): number => a.dexterity - b.dexterity, - Agility: (a: Resleeve, b: Resleeve): number => a.agility - b.agility, - Charisma: (a: Resleeve, b: Resleeve): number => a.charisma - b.charisma, - AverageCombatStats: (a: Resleeve, b: Resleeve): number => - getAverage(a.strength, a.defense, a.dexterity, a.agility) - - getAverage(b.strength, b.defense, b.dexterity, b.agility), - AverageAllStats: (a: Resleeve, b: Resleeve): number => - getAverage(a.hacking, a.strength, a.defense, a.dexterity, a.agility, a.charisma) - - getAverage(b.hacking, b.strength, b.defense, b.dexterity, b.agility, b.charisma), - TotalNumAugmentations: (a: Resleeve, b: Resleeve): number => a.augmentations.length - b.augmentations.length, -}; - -export function ResleeveRoot(): React.ReactElement { - const player = use.Player(); - const [sort, setSort] = useState(SortOption.Cost); - // Randomly create all Resleeves if they dont already exist - if (player.resleeves.length === 0) { - player.resleeves = generateResleeves(); - } - - function onSortChange(event: SelectChangeEvent): void { - setSort(event.target.value); - } - - const sortFunction = SortFunctions[sort]; - if (sortFunction === undefined) throw new Error(`sort function '${sort}' is undefined`); - player.resleeves.sort(sortFunction); - - return ( - <> - - Re-sleeving is the process of digitizing and transferring your consciousness into a new human body, or 'sleeve'. - Here at VitaLife, you can purchase new specially-engineered bodies for the re-sleeve process. Many of these - bodies even come with genetic and cybernetic Augmentations! -
-
- Re-sleeving will change your experience for every stat. It will also REMOVE all of your currently-installed - Augmentations, and replace them with the ones provided by the purchased sleeve. However, Augmentations that you - have purchased but not installed will NOT be removed. If you have purchased an Augmentation and then re-sleeve - into a body which already has that Augmentation, it will be removed (since you cannot have duplicate - Augmentations). -
-
- NOTE: The stats and multipliers displayed on this page do NOT include your bonuses from Source-File. -
- - Sort By: - - - {player.resleeves.map((resleeve, i) => ( - - ))} - - ); -} From c92b15958063a8cb8d8fe65dc774df9e2e266408 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 13:15:31 -0500 Subject: [PATCH 09/30] Implement entropy accumulation system --- src/Constants.ts | 5 +- .../Grafting/EntropyAccumulation.ts | 52 ++++++++++++++++++ .../Grafting/ui/GraftingRoot.tsx | 5 +- src/PersonObjects/IPlayer.ts | 3 + src/PersonObjects/Player/PlayerObject.ts | 7 +++ .../Player/PlayerObjectAugmentationMethods.ts | 9 +++ .../Player/PlayerObjectGeneralMethods.tsx | 55 ++++++++++--------- src/Prestige.ts | 3 + src/engine.tsx | 3 + 9 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 src/PersonObjects/Grafting/EntropyAccumulation.ts diff --git a/src/Constants.ts b/src/Constants.ts index 7dd447c7c..e6f97865a 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -111,6 +111,7 @@ export const CONSTANTS: { CodingContractBaseMoneyGain: number; AugmentationCraftingCostMult: number; AugmentationCraftingTimeMult: number; + EntropyEffect: number; TotalNumBitNodes: number; LatestUpdate: string; } = { @@ -276,7 +277,9 @@ export const CONSTANTS: { // Augmentation crafting multipliers // TODO: Get these right AugmentationCraftingCostMult: 1.2, - AugmentationCraftingTimeMult: 1, + AugmentationCraftingTimeMult: 0.01, + + EntropyEffect: 0.99, // BitNode/Source-File related stuff TotalNumBitNodes: 24, diff --git a/src/PersonObjects/Grafting/EntropyAccumulation.ts b/src/PersonObjects/Grafting/EntropyAccumulation.ts new file mode 100644 index 000000000..e894127f7 --- /dev/null +++ b/src/PersonObjects/Grafting/EntropyAccumulation.ts @@ -0,0 +1,52 @@ +import { IMap } from "../../types"; +import { CONSTANTS } from "../../Constants"; + +import { IPlayer } from "../IPlayer"; + +export const applyEntropy = (player: IPlayer, stacks = 1): IMap => { + const multipliers: IMap = { + hacking_chance_mult: player.hacking_chance_mult, + hacking_speed_mult: player.hacking_speed_mult, + hacking_money_mult: player.hacking_money_mult, + hacking_grow_mult: player.hacking_grow_mult, + + hacking_mult: player.hacking_mult, + strength_mult: player.strength_mult, + defense_mult: player.defense_mult, + dexterity_mult: player.dexterity_mult, + agility_mult: player.agility_mult, + charisma_mult: player.charisma_mult, + + hacking_exp_mult: player.hacking_exp_mult, + strength_exp_mult: player.strength_exp_mult, + defense_exp_mult: player.defense_exp_mult, + dexterity_exp_mult: player.dexterity_exp_mult, + agility_exp_mult: player.agility_exp_mult, + charisma_exp_mult: player.charisma_exp_mult, + + company_rep_mult: player.company_rep_mult, + faction_rep_mult: player.faction_rep_mult, + + crime_money_mult: player.crime_money_mult, + crime_success_mult: player.crime_success_mult, + + hacknet_node_money_mult: player.hacknet_node_money_mult, + hacknet_node_purchase_cost_mult: player.hacknet_node_purchase_cost_mult, + hacknet_node_ram_cost_mult: player.hacknet_node_ram_cost_mult, + hacknet_node_core_cost_mult: player.hacknet_node_core_cost_mult, + hacknet_node_level_cost_mult: player.hacknet_node_level_cost_mult, + + work_money_mult: player.work_money_mult, + + bladeburner_max_stamina_mult: player.bladeburner_max_stamina_mult, + bladeburner_stamina_gain_mult: player.bladeburner_stamina_gain_mult, + bladeburner_analysis_mult: player.bladeburner_analysis_mult, + bladeburner_success_chance_mult: player.bladeburner_success_chance_mult, + } + + for (const [mult, val] of Object.entries(multipliers)) { + multipliers[mult] = val * (CONSTANTS.EntropyEffect ** stacks); + } + + return multipliers; +} diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 15e4f4652..91c84e7b7 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -127,9 +127,10 @@ export const GraftingRoot = (): React.ReactElement => { - name tbd + Entropy Accumulation - probably some info about the cumulative negative effects here + probably some info about the cumulative negative effects here
+ {player.entropyStacks} accumulated entropy
diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index ed15ce028..41881462a 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -160,6 +160,8 @@ export interface IPlayer { workChaExpGainRate: number; workMoneyLossRate: number; + entropyStacks: number; + // Methods work(numCycles: number): boolean; workPartTime(numCycles: number): boolean; @@ -289,4 +291,5 @@ export interface IPlayer { startCraftAugmentationWork(augmentationName: string, time: number): void; craftAugmentationWork(numCycles: number): boolean; finishCraftAugmentationWork(cancelled: boolean): string; + applyEntropy(stacks?: number): void; } diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index 211131f30..65603c3af 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -169,6 +169,8 @@ export class PlayerObject implements IPlayer { workChaExpGainRate: number; workMoneyLossRate: number; + entropyStacks: number; + // Methods work: (numCycles: number) => boolean; workPartTime: (numCycles: number) => boolean; @@ -299,6 +301,7 @@ export class PlayerObject implements IPlayer { startCraftAugmentationWork: (augmentationName: string, time: number) => void; craftAugmentationWork: (numCycles: number) => boolean; finishCraftAugmentationWork: (cancelled: boolean) => string; + applyEntropy: (stacks?: number) => void; constructor() { //Skills and stats @@ -467,6 +470,8 @@ export class PlayerObject implements IPlayer { //bitnode this.bitNodeN = 1; + this.entropyStacks = 0; + //Used to store the last update time. this.lastUpdate = 0; this.lastSave = 0; @@ -619,6 +624,8 @@ export class PlayerObject implements IPlayer { this.canAccessCotMG = generalMethods.canAccessCotMG; this.sourceFileLvl = generalMethods.sourceFileLvl; + + this.applyEntropy = augmentationMethods.applyEntropy; } /** diff --git a/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts b/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts index acccb38df..ed68a0251 100644 --- a/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts @@ -5,6 +5,8 @@ import { IPlayer } from "../IPlayer"; import { Augmentation } from "../../Augmentation/Augmentation"; +import { applyEntropy as calculateEntropy } from "../Grafting/EntropyAccumulation"; + export function hasAugmentation(this: IPlayer, aug: string | Augmentation, installed = false): boolean { const augName: string = aug instanceof Augmentation ? aug.name : aug; @@ -24,3 +26,10 @@ export function hasAugmentation(this: IPlayer, aug: string | Augmentation, insta return false; } + +export function applyEntropy(this: IPlayer, stacks = 1): void { + const newMultipliers = calculateEntropy(this, stacks); + for (const [mult, val] of Object.entries(newMultipliers)) { + this.setMult(mult, val); + } +} diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 03bb85edb..30d7a4731 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -180,6 +180,7 @@ export function prestigeAugmentation(this: PlayerObject): void { } export function prestigeSourceFile(this: IPlayer): void { + this.entropyStacks = 0; this.prestigeAugmentation(); this.karma = 0; // Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists) @@ -1373,6 +1374,8 @@ export function finishCraftAugmentationWork(this: IPlayer, cancelled: boolean): dialogBoxCreate(`You've finished crafting ${augName}.
The augmentation has been grafted to your body, but you feel a bit off.`) applyAugmentation(Augmentations[augName]); + this.entropyStacks += 1; + this.applyEntropy(); } else { dialogBoxCreate(`You cancelled the crafting of ${augName}.
Your money was not returned to you.`) } @@ -1561,20 +1564,20 @@ export function finishCrime(this: IPlayer, cancelled: boolean): string { 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.", + 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 { @@ -1613,18 +1616,18 @@ export function finishCrime(this: IPlayer, cancelled: boolean): string { 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.", + 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 { diff --git a/src/Prestige.ts b/src/Prestige.ts index ab8641a85..066fb1d59 100755 --- a/src/Prestige.ts +++ b/src/Prestige.ts @@ -108,6 +108,9 @@ export function prestigeAugmentation(): void { // Messages initMessages(); + // Apply entropy from grafting + Player.applyEntropy(Player.entropyStacks); + // Gang const gang = Player.gang; if (Player.inGang() && gang !== null) { diff --git a/src/engine.tsx b/src/engine.tsx index 6464d3309..d2c28aba7 100644 --- a/src/engine.tsx +++ b/src/engine.tsx @@ -259,6 +259,9 @@ const Engine: { initSymbolToStockMap(); } + // Apply penalty for entropy accumulation + Player.applyEntropy(Player.entropyStacks); + // Calculate the number of cycles have elapsed while offline Engine._lastUpdate = new Date().getTime(); const lastUpdate = Player.lastUpdate; From 4c422347d421c4eabebecec9066a4e46271f47df Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 13:59:34 -0500 Subject: [PATCH 10/30] Polish entropy - Also adds DevMenu entry --- src/DevMenu.tsx | 2 + src/DevMenu/ui/Entropy.tsx | 50 +++++++++++++++++++ .../Grafting/ui/GraftingRoot.tsx | 34 +++++++++---- .../Player/PlayerObjectAugmentationMethods.ts | 4 ++ 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 src/DevMenu/ui/Entropy.tsx diff --git a/src/DevMenu.tsx b/src/DevMenu.tsx index 62300a430..2c9611941 100644 --- a/src/DevMenu.tsx +++ b/src/DevMenu.tsx @@ -23,6 +23,7 @@ import { Sleeves } from "./DevMenu/ui/Sleeves"; import { Stanek } from "./DevMenu/ui/Stanek"; import { TimeSkip } from "./DevMenu/ui/TimeSkip"; import { Achievements } from "./DevMenu/ui/Achievements"; +import { Entropy } from "./DevMenu/ui/Entropy"; import Typography from "@mui/material/Typography"; import { Exploit } from "./Exploits/Exploit"; @@ -63,6 +64,7 @@ export function DevMenuRoot(props: IProps): React.ReactElement { + ); } diff --git a/src/DevMenu/ui/Entropy.tsx b/src/DevMenu/ui/Entropy.tsx new file mode 100644 index 000000000..93c63df71 --- /dev/null +++ b/src/DevMenu/ui/Entropy.tsx @@ -0,0 +1,50 @@ +import React from "react"; + +import Accordion from "@mui/material/Accordion"; +import AccordionSummary from "@mui/material/AccordionSummary"; +import AccordionDetails from "@mui/material/AccordionDetails"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; + +import Typography from "@mui/material/Typography"; +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { Adjuster } from "./Adjuster"; +import { IEngine } from "../../IEngine"; + +// Update as additional BitNodes get implemented + +interface IProps { + player: IPlayer; + engine: IEngine; +} + +export function Entropy(props: IProps): React.ReactElement { + return ( + + }> + Entropy + + + { + props.player.entropyStacks += num; + props.player.applyEntropy(props.player.entropyStacks); + }} + subtract={num => { + props.player.entropyStacks -= num; + props.player.applyEntropy(props.player.entropyStacks); + }} + tons={() => { + props.player.entropyStacks += 1e12; + props.player.applyEntropy(props.player.entropyStacks); + }} + reset={() => { + props.player.entropyStacks = 0; + props.player.applyEntropy(props.player.entropyStacks); + }} + /> + + + ); +} diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 91c84e7b7..531dffa70 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -19,9 +19,13 @@ import { Augmentations } from "../../../Augmentation/Augmentations"; import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames" import { Settings } from "../../../Settings/Settings"; import { IMap } from "../../../types"; -import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; +import { + convertTimeMsToTimeElapsedString, + formatNumber +} from "../../../utils/StringHelperFunctions"; import { LocationName } from "../../../Locations/data/LocationNames"; import { Locations } from "../../../Locations/Locations"; +import { CONSTANTS } from "../../../Constants"; import { IPlayer } from "../../IPlayer"; @@ -73,12 +77,8 @@ export const GraftingRoot = (): React.ReactElement => { Illum velit takimata et aliquyam takimata labore vel dolor dolores duo amet lorem elitr facer invidunt.
- + Craft Augmentations - - here goes a list with available augmentations with a purchase button (with price shown) to the side of it
- getAvailableAugs function to the rescue -
{getAvailableAugs(player).map((k, i) => ( @@ -126,12 +126,28 @@ export const GraftingRoot = (): React.ReactElement => {
- + Entropy Accumulation + + + + Accumulated Entropy: {player.entropyStacks} +
+ All multipliers decreased by: {formatNumber((1 - (CONSTANTS.EntropyEffect ** player.entropyStacks)) * 100, 3)}% +
+
+ - probably some info about the cumulative negative effects here
- {player.entropyStacks} accumulated entropy + Augmenting causes signficant physical and psychological changes to the affected + individual, and the best way for the body to deal with these effects is to undergo + a full shutdown process. +

+ Grafting Augmentations to a semi-conscious host is extremely experimental and dangerous. +

+ Scientists don't fully understand the consequences, but some have hypothesized that there's + a buildup of unknown irregularities that impair the user...
+
diff --git a/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts b/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts index ed68a0251..9de4cdb03 100644 --- a/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts @@ -28,6 +28,10 @@ export function hasAugmentation(this: IPlayer, aug: string | Augmentation, insta } export function applyEntropy(this: IPlayer, stacks = 1): void { + // Re-apply all multipliers + this.reapplyAllAugmentations(); + this.reapplyAllSourceFiles(); + const newMultipliers = calculateEntropy(this, stacks); for (const [mult, val] of Object.entries(newMultipliers)) { this.setMult(mult, val); From 1f4f13c8ef8b93abd1610bb28d8eb0cd5b46c78f Mon Sep 17 00:00:00 2001 From: nickofolas Date: Sat, 19 Mar 2022 14:12:58 -0500 Subject: [PATCH 11/30] Main grafting exposition --- .../Grafting/ui/GraftingRoot.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 531dffa70..05ef91bd6 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -69,12 +69,13 @@ export const GraftingRoot = (): React.ReactElement => { Grafting Laboratory - blah blah blah exposition that isn't important right now
- Lorem ipsum dolor sit amet et et sed et et sanctus duo vero. - Stet amet iriure consetetur elit in magna et diam dolores invidunt ipsum gubergren nihil. - Diam et et ipsum consectetuer voluptua et clita lorem sit. - Et et lorem id no suscipit wisi. - Illum velit takimata et aliquyam takimata labore vel dolor dolores duo amet lorem elitr facer invidunt. + You find yourself in a secret laboratory, owned by a mysterious researcher.
+ The scientist explains that they've been studying Augmentation grafting, the process + of applying Augmentations without requiring a body reset. +

+ Through legally questionable connections, the scientist has access to a vast array of + Augmentation blueprints, even private designs. They offer to build and graft + the Augmentations to you, in exchange for both a hefty sum of money, and being a lab rat.
@@ -134,6 +135,7 @@ export const GraftingRoot = (): React.ReactElement => { Accumulated Entropy: {player.entropyStacks}
All multipliers decreased by: {formatNumber((1 - (CONSTANTS.EntropyEffect ** player.entropyStacks)) * 100, 3)}% + {" "}(multiplicative) @@ -141,11 +143,11 @@ export const GraftingRoot = (): React.ReactElement => { Augmenting causes signficant physical and psychological changes to the affected individual, and the best way for the body to deal with these effects is to undergo a full shutdown process. -

+
Grafting Augmentations to a semi-conscious host is extremely experimental and dangerous.

- Scientists don't fully understand the consequences, but some have hypothesized that there's - a buildup of unknown irregularities that impair the user... + Scientists don't fully understand the consequences, but some have hypothesized + a buildup of irregularities that impair the user...
From d8206257213f622fcf78d8445b2d53eb4a39aed5 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Mon, 21 Mar 2022 11:17:06 -0500 Subject: [PATCH 12/30] Remove fragment and fix typo --- src/PersonObjects/Grafting/ui/GraftingRoot.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 05ef91bd6..619bb3044 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -62,7 +62,7 @@ export const GraftingRoot = (): React.ReactElement => { const [selectedAug, setSelectedAug] = useState(getAvailableAugs(player)[0]); - return <> + return ( + {player.entropyStacks > 0 && + (() => { + const [open, setOpen] = useState(false); + + return ( + + setOpen((old) => !old)}> + + Entropy ({player.entropyStacks} accumulated) + + } + /> + {open ? ( + + ) : ( + + )} + + + + + All multipliers decreased by:{" "} + {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropyStacks) * 100, 3)}% (multiplicative) + + + + + ); + })()} + {sourceAugs.map((e) => { const aug = Augmentations[e.name]; From 010a4f1aa3c9bfdc93710d42fa761ba3119466ce Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Mon, 21 Mar 2022 14:30:03 -0500 Subject: [PATCH 15/30] Fix Entropy application after grafting --- src/PersonObjects/Grafting/EntropyAccumulation.ts | 2 +- src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts | 2 +- src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PersonObjects/Grafting/EntropyAccumulation.ts b/src/PersonObjects/Grafting/EntropyAccumulation.ts index e894127f7..4ea0ea169 100644 --- a/src/PersonObjects/Grafting/EntropyAccumulation.ts +++ b/src/PersonObjects/Grafting/EntropyAccumulation.ts @@ -3,7 +3,7 @@ import { CONSTANTS } from "../../Constants"; import { IPlayer } from "../IPlayer"; -export const applyEntropy = (player: IPlayer, stacks = 1): IMap => { +export const calculateEntropy = (player: IPlayer, stacks = 1): IMap => { const multipliers: IMap = { hacking_chance_mult: player.hacking_chance_mult, hacking_speed_mult: player.hacking_speed_mult, diff --git a/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts b/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts index 9de4cdb03..999689eef 100644 --- a/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectAugmentationMethods.ts @@ -5,7 +5,7 @@ import { IPlayer } from "../IPlayer"; import { Augmentation } from "../../Augmentation/Augmentation"; -import { applyEntropy as calculateEntropy } from "../Grafting/EntropyAccumulation"; +import { calculateEntropy } from "../Grafting/EntropyAccumulation"; export function hasAugmentation(this: IPlayer, aug: string | Augmentation, installed = false): boolean { const augName: string = aug instanceof Augmentation ? aug.name : aug; diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 30d7a4731..60384f82a 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -1375,7 +1375,7 @@ export function finishCraftAugmentationWork(this: IPlayer, cancelled: boolean): applyAugmentation(Augmentations[augName]); this.entropyStacks += 1; - this.applyEntropy(); + this.applyEntropy(this.entropyStacks); } else { dialogBoxCreate(`You cancelled the crafting of ${augName}.
Your money was not returned to you.`) } From 720e2112c6766b581e43e1f9e712c47300b22dcd Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Mon, 21 Mar 2022 14:40:17 -0500 Subject: [PATCH 16/30] prettier and remove formulas dir --- .../Grafting/EntropyAccumulation.ts | 6 +- src/PersonObjects/Grafting/formulas/.gitkeep | 0 .../Grafting/ui/GraftingRoot.tsx | 110 ++++++++---------- 3 files changed, 51 insertions(+), 65 deletions(-) delete mode 100644 src/PersonObjects/Grafting/formulas/.gitkeep diff --git a/src/PersonObjects/Grafting/EntropyAccumulation.ts b/src/PersonObjects/Grafting/EntropyAccumulation.ts index 4ea0ea169..8887df5a0 100644 --- a/src/PersonObjects/Grafting/EntropyAccumulation.ts +++ b/src/PersonObjects/Grafting/EntropyAccumulation.ts @@ -42,11 +42,11 @@ export const calculateEntropy = (player: IPlayer, stacks = 1): IMap => { bladeburner_stamina_gain_mult: player.bladeburner_stamina_gain_mult, bladeburner_analysis_mult: player.bladeburner_analysis_mult, bladeburner_success_chance_mult: player.bladeburner_success_chance_mult, - } + }; for (const [mult, val] of Object.entries(multipliers)) { - multipliers[mult] = val * (CONSTANTS.EntropyEffect ** stacks); + multipliers[mult] = val * CONSTANTS.EntropyEffect ** stacks; } return multipliers; -} +}; diff --git a/src/PersonObjects/Grafting/formulas/.gitkeep b/src/PersonObjects/Grafting/formulas/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 852a94c9b..a59e32608 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -1,28 +1,15 @@ import React, { useState } from "react"; -import { - Typography, - Container, - Box, - Paper, - List, - ListItemButton, - Button -} from "@mui/material"; -import { - Construction -} from "@mui/icons-material"; +import { Typography, Container, Box, Paper, List, ListItemButton, Button } from "@mui/material"; +import { Construction } from "@mui/icons-material"; import { use } from "../../../ui/Context"; import { Money } from "../../../ui/React/Money"; import { Augmentations } from "../../../Augmentation/Augmentations"; -import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames" +import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames"; import { Settings } from "../../../Settings/Settings"; import { IMap } from "../../../types"; -import { - convertTimeMsToTimeElapsedString, - formatNumber -} from "../../../utils/StringHelperFunctions"; +import { convertTimeMsToTimeElapsedString, formatNumber } from "../../../utils/StringHelperFunctions"; import { LocationName } from "../../../Locations/data/LocationNames"; import { Locations } from "../../../Locations/Locations"; import { CONSTANTS } from "../../../Constants"; @@ -31,24 +18,19 @@ import { IPlayer } from "../../IPlayer"; import { CraftableAugmentation } from "../CraftableAugmentation"; -const CraftableAugmentations: IMap = {} +const CraftableAugmentations: IMap = {}; const getAvailableAugs = (player: IPlayer): string[] => { const augs: string[] = []; for (const [augName, aug] of Object.entries(Augmentations)) { - if ( - augName === AugmentationNames.NeuroFluxGovernor || - augName === AugmentationNames.TheRedPill || - aug.isSpecial - ) continue; + if (augName === AugmentationNames.NeuroFluxGovernor || augName === AugmentationNames.TheRedPill || aug.isSpecial) + continue; augs.push(augName); } - return augs.filter( - (augmentation: string) => !player.hasAugmentation(augmentation) - ); -} + return augs.filter((augmentation: string) => !player.hasAugmentation(augmentation)); +}; export const GraftingRoot = (): React.ReactElement => { const player = use.Player(); @@ -64,38 +46,36 @@ export const GraftingRoot = (): React.ReactElement => { return ( - + Grafting Laboratory - You find yourself in a secret laboratory, owned by a mysterious researcher.
- The scientist explains that they've been studying Augmentation grafting, the process - of applying Augmentations without requiring a body reset. -

- Through legally questionable connections, the scientist has access to a vast array of - Augmentation blueprints, even private designs. They offer to build and graft - the Augmentations to you, in exchange for both a hefty sum of money, and being a lab rat. + You find yourself in a secret laboratory, owned by a mysterious researcher. +
+ The scientist explains that they've been studying Augmentation grafting, the process of applying Augmentations + without requiring a body reset. +
+
+ Through legally questionable connections, the scientist has access to a vast array of Augmentation blueprints, + even private designs. They offer to build and graft the Augmentations to you, in exchange for both a hefty sum + of money, and being a lab rat.
Craft Augmentations - - + + {getAvailableAugs(player).map((k, i) => ( setSelectedAug(k)} selected={selectedAug === k}> - - {k} - + {k} ))} - + {selectedAug} Time to Craft: {convertTimeMsToTimeElapsedString(CraftableAugmentations[selectedAug].time)} - + {(() => { const aug = Augmentations[selectedAug]; - const info = typeof aug.info === "string" ? {aug.info} : aug.info - const tooltip = (<>{info}

{aug.stats}); + const info = typeof aug.info === "string" ? {aug.info} : aug.info; + const tooltip = ( + <> + {info} +
+
+ {aug.stats} + + ); return tooltip; })()}
@@ -130,26 +118,24 @@ export const GraftingRoot = (): React.ReactElement => { Entropy Accumulation - + Accumulated Entropy: {player.entropyStacks}
- All multipliers decreased by: {formatNumber((1 - (CONSTANTS.EntropyEffect ** player.entropyStacks)) * 100, 3)}% - {" "}(multiplicative) + All multipliers decreased by:{" "} + {formatNumber((1 - CONSTANTS.EntropyEffect ** player.entropyStacks) * 100, 3)}% (multiplicative)
- When installed on an unconscious individual, Augmentations are scanned by the body - on awakening, eliminating hidden malware. However, grafted Augmentations do not - provide this security measure. -

- Individuals who tested Augmentation grafting have reported symptoms of an unknown - virus, which they've dubbed "Entropy". This virus seems to grow more potent with - each grafted Augmentation... + When installed on an unconscious individual, Augmentations are scanned by the body on awakening, eliminating + hidden malware. However, grafted Augmentations do not provide this security measure. +
+
+ Individuals who tested Augmentation grafting have reported symptoms of an unknown virus, which they've dubbed + "Entropy". This virus seems to grow more potent with each grafted Augmentation...
-
- ) -} + ); +}; From 16fa4c8d7ffa854440aefda9975cb250bf9b1313 Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Mon, 21 Mar 2022 14:44:50 -0500 Subject: [PATCH 17/30] Update BN10 description --- src/BitNode/BitNode.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BitNode/BitNode.tsx b/src/BitNode/BitNode.tsx index b4a7e515d..95dc6cdc5 100644 --- a/src/BitNode/BitNode.tsx +++ b/src/BitNode/BitNode.tsx @@ -425,7 +425,7 @@ BitNodes["BitNode10"] = new BitNode( This BitNode unlocks Sleeve technology. Sleeve technology allows you to:

- 1. Re-sleeve: Purchase and transfer your consciousness into a new body + 1. Grafting: Visit VitaLife in New Tokyo to be able to obtain Augmentations without needing to install
2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously From d6e01801161d25ca7686bd905e580035ceb050d7 Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Mon, 21 Mar 2022 14:54:39 -0500 Subject: [PATCH 18/30] Add confirmation modal to crafting --- .../Grafting/ui/GraftingRoot.tsx | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index a59e32608..055ed5d6e 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -5,6 +5,7 @@ import { Construction } from "@mui/icons-material"; import { use } from "../../../ui/Context"; import { Money } from "../../../ui/React/Money"; +import { ConfirmationModal } from "../../../ui/React/ConfirmationModal"; import { Augmentations } from "../../../Augmentation/Augmentations"; import { AugmentationNames } from "../../../Augmentation/data/AugmentationNames"; import { Settings } from "../../../Settings/Settings"; @@ -43,6 +44,7 @@ export const GraftingRoot = (): React.ReactElement => { } const [selectedAug, setSelectedAug] = useState(getAvailableAugs(player)[0]); + const [craftOpen, setCraftOpen] = useState(false); return ( @@ -75,14 +77,7 @@ export const GraftingRoot = (): React.ReactElement => { {selectedAug} + setCraftOpen(false)} + onConfirm={() => { + const craftableAug = CraftableAugmentations[selectedAug]; + player.loseMoney(craftableAug.cost, "augmentations"); + player.startCraftAugmentationWork(selectedAug, craftableAug.time); + player.startFocusing(); + router.toWork(); + }} + confirmationText={ + <> + Cancelling crafting will not save crafting progress, and the money you spend will not be + returned. +
+
+ Additionally, grafting an Augmentation will increase the potency of the Entropy virus. + + } + /> Time to Craft: {convertTimeMsToTimeElapsedString(CraftableAugmentations[selectedAug].time)} From 3df3e04f9c9dd94653077dd8b34790ca009321dd Mon Sep 17 00:00:00 2001 From: nickofolas Date: Tue, 22 Mar 2022 17:53:57 -0500 Subject: [PATCH 19/30] Formulas --- src/Constants.ts | 6 ++++-- .../Grafting/CraftableAugmentation.ts | 9 +++++++-- src/PersonObjects/Grafting/ui/GraftingRoot.tsx | 6 +++++- .../Player/PlayerObjectGeneralMethods.tsx | 9 ++++++--- src/ui/React/CharacterOverview.tsx | 4 ++-- src/ui/WorkInProgressRoot.tsx | 17 +++++++++++------ 6 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/Constants.ts b/src/Constants.ts index e6f97865a..1a93ec942 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -41,6 +41,7 @@ export const CONSTANTS: { IntelligenceInfiltrationWeight: number; IntelligenceCrimeBaseExpGain: number; IntelligenceProgramBaseExpGain: number; + IntelligenceCraftBaseExpGain: number; IntelligenceTerminalHackBaseExpGain: number; IntelligenceSingFnBaseExpGain: number; IntelligenceClassBaseExpGain: number; @@ -110,7 +111,7 @@ export const CONSTANTS: { CodingContractBaseCompanyRepGain: number; CodingContractBaseMoneyGain: number; AugmentationCraftingCostMult: number; - AugmentationCraftingTimeMult: number; + AugmentationCraftingTimeBase: number; EntropyEffect: number; TotalNumBitNodes: number; LatestUpdate: string; @@ -184,6 +185,7 @@ export const CONSTANTS: { IntelligenceInfiltrationWeight: 0.1, // Weight for how much int affects infiltration success rates IntelligenceCrimeBaseExpGain: 0.05, IntelligenceProgramBaseExpGain: 0.1, // Program required hack level divided by this to determine int exp gain + IntelligenceCraftBaseExpGain: 0.05, IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain IntelligenceSingFnBaseExpGain: 1.5, IntelligenceClassBaseExpGain: 0.01, @@ -277,7 +279,7 @@ export const CONSTANTS: { // Augmentation crafting multipliers // TODO: Get these right AugmentationCraftingCostMult: 1.2, - AugmentationCraftingTimeMult: 0.01, + AugmentationCraftingTimeBase: 3600000, EntropyEffect: 0.99, diff --git a/src/PersonObjects/Grafting/CraftableAugmentation.ts b/src/PersonObjects/Grafting/CraftableAugmentation.ts index 5f7785c16..0d365ebd7 100644 --- a/src/PersonObjects/Grafting/CraftableAugmentation.ts +++ b/src/PersonObjects/Grafting/CraftableAugmentation.ts @@ -1,3 +1,5 @@ +import { sum } from "lodash"; + import { Augmentation } from "../../Augmentation/Augmentation"; import { CONSTANTS } from "../../Constants"; @@ -20,7 +22,10 @@ export class CraftableAugmentation { } get time(): number { - // CONSTANTS.AugmentationCraftingTimeMult - return 15000; + // Time = 1 hour * log_2(sum(aug multipliers) || 1) + 30 minutes + const antiLog = Math.max(sum(Object.values(this.augmentation.mults)), 1); + + const mult = Math.log2(antiLog); + return CONSTANTS.AugmentationCraftingTimeBase * mult + CONSTANTS.MillisecondsPerHalfHour; } } diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index 055ed5d6e..eefde9c2f 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -108,7 +108,11 @@ export const GraftingRoot = (): React.ReactElement => { } /> - Time to Craft: {convertTimeMsToTimeElapsedString(CraftableAugmentations[selectedAug].time)} + Time to Craft:{" "} + {convertTimeMsToTimeElapsedString( + CraftableAugmentations[selectedAug].time / (1 + (player.getIntelligenceBonus(3) - 1) / 3), + )} + {/* Use formula so the displayed creation time is accurate to player bonus */} {(() => { diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx index 60384f82a..2a862085e 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.tsx @@ -1354,8 +1354,7 @@ export function craftAugmentationWork(this: IPlayer, numCycles: number): boolean focusBonus = this.focus ? 1 : CONSTANTS.BaseFocusBonus; } - // TODO: formula logic here (focus bonus and stuff) - let skillMult = 1; + let skillMult = 1 + (this.getIntelligenceBonus(3) - 1) / 3; skillMult *= focusBonus; this.timeWorked += CONSTANTS._idleSpeed * numCycles; @@ -1380,7 +1379,11 @@ export function finishCraftAugmentationWork(this: IPlayer, cancelled: boolean): dialogBoxCreate(`You cancelled the crafting of ${augName}.
Your money was not returned to you.`) } - // TODO: intelligence EXP stuff here later + // Intelligence gain + if (!cancelled) { + this.gainIntelligenceExp((CONSTANTS.IntelligenceCraftBaseExpGain * this.timeWorked) / 10000); + } + this.isWorking = false; this.resetWorkStatus(); return `Crafting of ${augName} has ended.` diff --git a/src/ui/React/CharacterOverview.tsx b/src/ui/React/CharacterOverview.tsx index 34e7b81eb..5b08dbebe 100644 --- a/src/ui/React/CharacterOverview.tsx +++ b/src/ui/React/CharacterOverview.tsx @@ -199,8 +199,8 @@ function Work(): React.ReactElement { header = <>Crafting an Augmentation; innerText = ( <> - {convertTimeMsToTimeElapsedString(player.timeNeededToCompleteWork - player.timeWorkedCraftAugmentation)} - {" "}remaining + {((player.timeWorkedCraftAugmentation / player.timeNeededToCompleteWork) * 100).toFixed(2)}% + {" "}done ); } diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index fe9978bb1..72a91efa1 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -41,8 +41,8 @@ export function WorkInProgressRoot(): React.ReactElement { return ( <> - You have not joined {player.currentWorkFactionName || "(Faction not found)"} yet or cannot work at this time, - please try again if you think this should have worked + You have not joined {player.currentWorkFactionName || "(Faction not found)"} yet or cannot work at this + time, please try again if you think this should have worked @@ -497,10 +497,15 @@ export function WorkInProgressRoot(): React.ReactElement { You are currently working on crafting {player.craftAugmentationName}. -

+
+
You have been working for {convertTimeMsToTimeElapsedString(player.timeWorked)} -

- The augmentation will be done in {convertTimeMsToTimeElapsedString(player.timeNeededToCompleteWork - player.timeWorkedCraftAugmentation)}.
+
+
+ The augmentation is{" "} + {((player.timeWorkedCraftAugmentation / player.timeNeededToCompleteWork) * 100).toFixed(2)}% done being + crafted. +
If you cancel, your work will not be saved, and the money you spent will not be returned.
@@ -511,7 +516,7 @@ export function WorkInProgressRoot(): React.ReactElement { - ) + ); } if (!player.workType) router.toTerminal(); From 6a8426abd236f325dc1a8fc036954af2bc4f3e33 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Wed, 23 Mar 2022 11:17:03 -0500 Subject: [PATCH 20/30] Allow every aug in gangs in BN2 --- src/Faction/ui/AugmentationsPage.tsx | 7 +++--- src/Faction/ui/FactionsRoot.tsx | 32 ++++++++++------------------ 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/Faction/ui/AugmentationsPage.tsx b/src/Faction/ui/AugmentationsPage.tsx index e086a4ee4..d29f239d7 100644 --- a/src/Faction/ui/AugmentationsPage.tsx +++ b/src/Faction/ui/AugmentationsPage.tsx @@ -48,12 +48,13 @@ export function AugmentationsPage(props: IProps): React.ReactElement { const aug = Augmentations[augName]; if ( augName === AugmentationNames.NeuroFluxGovernor || - augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2 || + (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) || // Special augs (i.e. Bladeburner augs) aug.isSpecial || // Exclusive augs (i.e. QLink) - (aug.factions.length <= 1 && !props.faction.augmentations.includes(augName)) - ) continue; + (aug.factions.length <= 1 && !props.faction.augmentations.includes(augName) && player.bitNodeN !== 2) + ) + continue; augs.push(augName); } diff --git a/src/Faction/ui/FactionsRoot.tsx b/src/Faction/ui/FactionsRoot.tsx index ffe255510..e5c5b292f 100644 --- a/src/Faction/ui/FactionsRoot.tsx +++ b/src/Faction/ui/FactionsRoot.tsx @@ -1,14 +1,6 @@ import React, { useEffect, useState } from "react"; -import { - Box, - Button, - Container, - Paper, - TableBody, - TableRow, - Typography -} from "@mui/material"; +import { Box, Button, Container, Paper, TableBody, TableRow, Typography } from "@mui/material"; import { Augmentations } from "../../Augmentation/Augmentations"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; @@ -68,27 +60,25 @@ export function FactionsRoot(props: IProps): React.ReactElement { const aug = Augmentations[augName]; if ( augName === AugmentationNames.NeuroFluxGovernor || - augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2 || + (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) || // Special augs (i.e. Bladeburner augs) aug.isSpecial || // Exclusive augs (i.e. QLink) - (aug.factions.length <= 1 && !faction.augmentations.includes(augName)) - ) continue; - augs.push(augName) + (aug.factions.length <= 1 && !faction.augmentations.includes(augName) && player.bitNodeN !== 2) + ) + continue; + augs.push(augName); } } else { augs = faction.augmentations.slice(); } - return augs.filter( - (augmentation: string) => !player.hasAugmentation(augmentation) - ).length; - } + return augs.filter((augmentation: string) => !player.hasAugmentation(augmentation)).length; + }; - const allFactions = Object.values(FactionNames).map(faction => faction as string) + const allFactions = Object.values(FactionNames).map((faction) => faction as string); const allJoinedFactions = props.player.factions.slice(0); - allJoinedFactions.sort((a, b) => - allFactions.indexOf(a) - allFactions.indexOf(b)); + allJoinedFactions.sort((a, b) => allFactions.indexOf(a) - allFactions.indexOf(b)); return ( @@ -120,7 +110,7 @@ export function FactionsRoot(props: IProps): React.ReactElement { - From 96dd059f2800f6a3662f026d1bcf85fb6893b7b5 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Wed, 23 Mar 2022 11:35:56 -0500 Subject: [PATCH 21/30] Update BN10 docs --- doc/source/advancedgameplay/intelligence.rst | 1 + doc/source/advancedgameplay/sleeves.rst | 27 ++++++++++---------- doc/source/advancedgameplay/sourcefiles.rst | 1 + 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/doc/source/advancedgameplay/intelligence.rst b/doc/source/advancedgameplay/intelligence.rst index 0bdcc15f1..7ca645387 100644 --- a/doc/source/advancedgameplay/intelligence.rst +++ b/doc/source/advancedgameplay/intelligence.rst @@ -16,3 +16,4 @@ Intelligence will boost your production for many actions in the game, including: * Crime success rate * Bladeburner * Reputation gain for companies & factions +* Augmentation crafting speed diff --git a/doc/source/advancedgameplay/sleeves.rst b/doc/source/advancedgameplay/sleeves.rst index e7097866d..27b5d9d1b 100644 --- a/doc/source/advancedgameplay/sleeves.rst +++ b/doc/source/advancedgameplay/sleeves.rst @@ -92,18 +92,19 @@ and above, and is only available after defeating BitNode-10 at least once. Memory is a persistent stat, meaning it never gets reset back to 1. The maximum possible value for a sleeve's memory is 100. -Re-sleeving -^^^^^^^^^^^ -Re-sleeving is the process of digitizing and transferring your consciousness into a -new human body, or "sleeve". When you re-sleeve into a new body, your stat experience -and Augmentations get replaced with those of the new body. +Grafting +^^^^^^^^ +Grafting is an experimental process through which you can obtain the benefits of +Augmentations, without needing to install them. -In order to re-sleeve, you must purchase new bodies. This can be done at VitaLife in -New Tokyo. Once you purchase a body to re-sleeve into, the effects will take -place immediately. +In order to graft, you must first purchase a blueprint for and craft the Augmentation. +This can be done at VitaLife in New Tokyo, where you'll find a shady researcher with +questionable connections. Once you purchase a blueprint, you will start crafting the +Augmentation, and it will be grafted to your body once complete. -Note that resleeving **REMOVES** all of your currently-installed Augmentations, -and replaces them with the ones provided by the purchased sleeve. However, -Augmentations that are purchased but not installed will **not** be removed. If you have purchased -an Augmentation and then re-sleeve into a body which already has that Augmentation, -it will be removed since you cannot have duplicate Augmentations. +Be warned, some who have tested grafting have reported an unidentified malware. Dubbed +"Entropy", this virus seems to grow in potency as more Augmentations are grafted, +causing unpredictable affects to the victim. + +Note that when crafting an Augmentation, cancelling will **not** save your progress, +and the money spent will **not** be returned. diff --git a/doc/source/advancedgameplay/sourcefiles.rst b/doc/source/advancedgameplay/sourcefiles.rst index 150b0689f..fd05b6f56 100644 --- a/doc/source/advancedgameplay/sourcefiles.rst +++ b/doc/source/advancedgameplay/sourcefiles.rst @@ -53,6 +53,7 @@ List of all Source-Files +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ || BitNode-10: Digital Carbon || * Each level of this grants a Duplicate Sleeve. | || || * Allows the player to access the `Sleeve API `_ in other BitNodes. | +|| || * Grants the player access to the VitaLife grafting laboratory in other BitNodes. | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ || BitNode-11: The Big Crash || * Company favor increases both the player's salary and reputation gain at that | || || company by 1% per favor (rather than just the reputation gain). | From 1c696b551858254a43f9d22a8ebea0e2e7432da6 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Wed, 23 Mar 2022 12:04:49 -0500 Subject: [PATCH 22/30] Update purchaseAugmentation rules --- src/NetscriptFunctions/Singularity.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts index 26fbd760d..3c9abf987 100644 --- a/src/NetscriptFunctions/Singularity.ts +++ b/src/NetscriptFunctions/Singularity.ts @@ -167,12 +167,17 @@ export function NetscriptSingularity( let augs = []; if (player.hasGangWith(faction)) { for (const augName of Object.keys(Augmentations)) { - if (augName === AugmentationNames.NeuroFluxGovernor) continue; - if (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) continue; - const tempAug = Augmentations[augName]; - if (!tempAug.isSpecial) { - augs.push(augName); - } + const aug = Augmentations[augName]; + if ( + augName === AugmentationNames.NeuroFluxGovernor || + (augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2) || + // Special augs (i.e. Bladeburner augs) + aug.isSpecial || + // Exclusive augs (i.e. QLink) + (aug.factions.length <= 1 && !fac.augmentations.includes(augName) && player.bitNodeN !== 2) + ) + continue; + augs.push(augName); } } else { augs = fac.augmentations; From 3476f0f0ebbc82efecce0a625ba658522f440c5a Mon Sep 17 00:00:00 2001 From: nickofolas Date: Wed, 23 Mar 2022 12:35:15 -0500 Subject: [PATCH 23/30] Grafting API (no RAM costs yet) --- src/NetscriptFunctions.ts | 4 + src/NetscriptFunctions/Grafting.ts | 84 +++++++++++++++++++ .../Grafting/ui/GraftingRoot.tsx | 2 +- src/ScriptEditor/NetscriptDefinitions.d.ts | 41 +++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/NetscriptFunctions/Grafting.ts diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 65a4b3d2f..7b292a209 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -70,6 +70,7 @@ import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract"; import { NetscriptCorporation } from "./NetscriptFunctions/Corporation"; import { NetscriptFormulas } from "./NetscriptFunctions/Formulas"; import { NetscriptStockMarket } from "./NetscriptFunctions/StockMarket"; +import { NetscriptGrafting } from "./NetscriptFunctions/Grafting"; import { IPort } from "./NetscriptPort"; import { @@ -480,6 +481,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { const singularity = NetscriptSingularity(Player, workerScript, helper); const stockmarket = NetscriptStockMarket(Player, workerScript, helper); const ui = NetscriptUserInterface(Player, workerScript, helper); + const grafting = NetscriptGrafting(Player, workerScript, helper); const base: INS = { ...singularity, @@ -493,6 +495,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { ui: ui, formulas: formulas, stock: stockmarket, + grafting: grafting, args: workerScript.args, hacknet: hacknet, sprintf: sprintf, @@ -2315,6 +2318,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { tor: Player.hasTorRouter(), inBladeburner: Player.inBladeburner(), hasCorporation: Player.hasCorporation(), + entropyStacks: Player.entropyStacks, }; Object.assign(data.jobs, Player.jobs); return data; diff --git a/src/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts new file mode 100644 index 000000000..124ab0206 --- /dev/null +++ b/src/NetscriptFunctions/Grafting.ts @@ -0,0 +1,84 @@ +import { CityName } from "../Locations/data/CityNames"; +import { Augmentations } from "../Augmentation/Augmentations"; +import { getRamCost } from "../Netscript/RamCostGenerator"; +import { WorkerScript } from "../Netscript/WorkerScript"; +import { CraftableAugmentation } from "../PersonObjects/Grafting/CraftableAugmentation"; +import { getAvailableAugs } from "../PersonObjects/Grafting/ui/GraftingRoot"; +import { IPlayer } from "../PersonObjects/IPlayer"; +import { Grafting as IGrafting } from "../ScriptEditor/NetscriptDefinitions"; +import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; +import { Router } from "../ui/GameRoot"; +import { INetscriptHelper } from "./INetscriptHelper"; + +export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IGrafting { + const checkGraftingAPIAccess = (func: any): void => { + if (player.bitNodeN !== 10 && !SourceFileFlags[10]) { + throw helper.makeRuntimeErrorMsg( + `grafting.${func}`, + "You do not currently have access to the Grafting API. This is either because you are not in BitNode 10 or because you do not have Source-File 10", + ); + } + }; + + return { + getAugmentationCraftPrice: (augName: string): number => { + helper.updateDynamicRam("getAugmentationCraftPrice", getRamCost(player, "grafting", "getAugmentationCraftPrice")); + checkGraftingAPIAccess("getAugmentationCraftPrice"); + if (!Augmentations.hasOwnProperty(augName)) { + throw helper.makeRuntimeErrorMsg("grafting.getAugmentationCraftPrice", `Invalid aug: ${augName}`); + } + const craftableAug = new CraftableAugmentation(Augmentations[augName]); + return craftableAug.cost; + }, + + getAugmentationCraftTime: (augName: string): number => { + helper.updateDynamicRam("getAugmentationCraftTime", getRamCost(player, "grafting", "getAugmentationCraftTime")); + checkGraftingAPIAccess("getAugmentationCraftTime"); + if (!Augmentations.hasOwnProperty(augName)) { + throw helper.makeRuntimeErrorMsg("grafting.getAugmentationCraftTime", `Invalid aug: ${augName}`); + } + const craftableAug = new CraftableAugmentation(Augmentations[augName]); + return craftableAug.time; + }, + + craftAugmentation: (augName: string, focus = true): boolean => { + helper.updateDynamicRam("craftAugmentation", getRamCost(player, "grafting", "craftAugmentation")); + checkGraftingAPIAccess("craftAugmentation"); + if (player.city !== CityName.NewTokyo) { + throw helper.makeRuntimeErrorMsg( + "grafting.craftAugmentation", + "You must be in New Tokyo to begin crafting an Augmentation.", + ); + } + if (!getAvailableAugs(player).includes(augName)) { + workerScript.log("grafting.craftAugmentation", () => `Invalid aug: ${augName}`); + return false; + } + + const wasFocusing = player.focus; + if (player.isWorking) { + const txt = player.singularityStopWork(); + workerScript.log("craftAugmentation", () => txt); + } + + const craftableAug = new CraftableAugmentation(Augmentations[augName]); + if (player.money < craftableAug.cost) { + workerScript.log("grafting.craftAugmentation", () => `You don't have enough money to craft ${augName}`); + } + + player.loseMoney(craftableAug.cost, "augmentations"); + player.startCraftAugmentationWork(augName, craftableAug.time); + + if (focus) { + player.startFocusing(); + Router.toWork(); + } else if (wasFocusing) { + player.stopFocusing(); + Router.toTerminal(); + } + + workerScript.log("grafting.craftAugmentation", () => `Began crafting Augmentation ${augName}.`); + return true; + }, + }; +} diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx index eefde9c2f..838fc8f7e 100644 --- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx +++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx @@ -21,7 +21,7 @@ import { CraftableAugmentation } from "../CraftableAugmentation"; const CraftableAugmentations: IMap = {}; -const getAvailableAugs = (player: IPlayer): string[] => { +export const getAvailableAugs = (player: IPlayer): string[] => { const augs: string[] = []; for (const [augName, aug] of Object.entries(Augmentations)) { diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index a062ddd29..f871c296e 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -95,6 +95,7 @@ interface Player { tor: boolean; hasCorporation: boolean; inBladeburner: boolean; + entropyStacks: number; } /** @@ -3719,6 +3720,39 @@ export interface Sleeve { purchaseSleeveAug(sleeveNumber: number, augName: string): boolean; } +export interface Grafting { + /** + * Retrieve the crafting cost of an aug. + * @remarks + * RAM cost: TODO + * + * @param augName - Name of the aug to check the price of. Must be an exact match. + * @returns The cost required to craft the named augmentation. + */ + getAugmentationCraftPrice(augName: string): number; + + /** + * Retrieves the time required to craft an aug. + * @remarks + * RAM cost: TODO + * + * @param augName - Name of the aug to check the crafting time of. Must be an exact match. + * @returns The time required, in millis, to craft the named augmentation. + */ + getAugmentationCraftTime(augName: string): number; + + /** + * Begins crafting the named aug. You must be in New Tokyo to use this. + * @remarks + * RAM cost: TODO + * + * @param augName - The name of the aug to begin crafting. Must be an exact match. + * @param focus - Acquire player focus on this Augmentation crafting. Optional. Defaults to true. + * @returns True if the aug successfully began crafting, false otherwise. + */ + craftAugmentation(augName: string, focus?: boolean): boolean; +} + /** * Skills formulas * @public @@ -4280,6 +4314,13 @@ export interface NS extends Singularity { */ readonly ui: UserInterface; + /** + * Namespace for grafting functions. + * @remarks + * RAM cost: 0 GB + */ + readonly grafting: Grafting; + /** * Arguments passed into the script. * From 973ff5da7a5deeb187bf4f32be9174422fdf9cd1 Mon Sep 17 00:00:00 2001 From: nickofolas Date: Wed, 23 Mar 2022 12:40:02 -0500 Subject: [PATCH 24/30] Update RTD to mention Grafting API --- doc/source/advancedgameplay/sourcefiles.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/advancedgameplay/sourcefiles.rst b/doc/source/advancedgameplay/sourcefiles.rst index fd05b6f56..dba38b763 100644 --- a/doc/source/advancedgameplay/sourcefiles.rst +++ b/doc/source/advancedgameplay/sourcefiles.rst @@ -53,7 +53,7 @@ List of all Source-Files +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ || BitNode-10: Digital Carbon || * Each level of this grants a Duplicate Sleeve. | || || * Allows the player to access the `Sleeve API `_ in other BitNodes. | -|| || * Grants the player access to the VitaLife grafting laboratory in other BitNodes. | +|| || * Grants the player access to the VitaLife grafting laboratory in other BitNodes. Also grants access to the Grafting API. | +-------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ || BitNode-11: The Big Crash || * Company favor increases both the player's salary and reputation gain at that | || || company by 1% per favor (rather than just the reputation gain). | From b437adb16ceac75016363d832cd6429a8f08112e Mon Sep 17 00:00:00 2001 From: nickofolas Date: Wed, 23 Mar 2022 15:48:39 -0500 Subject: [PATCH 25/30] Simplify API check --- src/NetscriptFunctions/Grafting.ts | 2 +- src/SourceFile/SourceFiles.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts index 124ab0206..8101b3f1c 100644 --- a/src/NetscriptFunctions/Grafting.ts +++ b/src/NetscriptFunctions/Grafting.ts @@ -12,7 +12,7 @@ import { INetscriptHelper } from "./INetscriptHelper"; export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IGrafting { const checkGraftingAPIAccess = (func: any): void => { - if (player.bitNodeN !== 10 && !SourceFileFlags[10]) { + if (!player.canAccessGrafting()) { throw helper.makeRuntimeErrorMsg( `grafting.${func}`, "You do not currently have access to the Grafting API. This is either because you are not in BitNode 10 or because you do not have Source-File 10", diff --git a/src/SourceFile/SourceFiles.tsx b/src/SourceFile/SourceFiles.tsx index 20cc9f452..a8bd75811 100644 --- a/src/SourceFile/SourceFiles.tsx +++ b/src/SourceFile/SourceFiles.tsx @@ -167,8 +167,8 @@ SourceFiles["SourceFile10"] = new SourceFile( 10, ( <> - This Source-File unlocks Sleeve technology in other BitNodes. Each level of this Source-File also grants you a - Duplicate Sleeve + This Source-File unlocks Sleeve technology, and the Grafting API in other BitNodes. + Each level of this Source-File also grants you a Duplicate Sleeve ), ); From 2aaf880e27d292759eb2b88855bf2ce9b9df0d69 Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Sun, 27 Mar 2022 15:23:35 -0400 Subject: [PATCH 26/30] Fix oversight in WIPRoot --- src/ui/WorkInProgressRoot.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index 72a91efa1..e263790e4 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -511,7 +511,7 @@ export function WorkInProgressRoot(): React.ReactElement { From 9f6c827dee100ed03c7e13e6870b0828cee0f26a Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Sun, 27 Mar 2022 15:33:03 -0400 Subject: [PATCH 27/30] Properly return false from craftAug --- src/NetscriptFunctions/Grafting.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NetscriptFunctions/Grafting.ts b/src/NetscriptFunctions/Grafting.ts index 8101b3f1c..fa1e2339d 100644 --- a/src/NetscriptFunctions/Grafting.ts +++ b/src/NetscriptFunctions/Grafting.ts @@ -64,6 +64,7 @@ export function NetscriptGrafting(player: IPlayer, workerScript: WorkerScript, h const craftableAug = new CraftableAugmentation(Augmentations[augName]); if (player.money < craftableAug.cost) { workerScript.log("grafting.craftAugmentation", () => `You don't have enough money to craft ${augName}`); + return false; } player.loseMoney(craftableAug.cost, "augmentations"); From 3b32811f5e7876d8c2f84867f2dfe67ddac9802d Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Sun, 27 Mar 2022 15:33:31 -0400 Subject: [PATCH 28/30] Grafting API RAM cost & doc update --- src/Netscript/RamCostGenerator.ts | 6 ++++++ src/Script/RamCalculations.ts | 3 +++ src/ScriptEditor/NetscriptDefinitions.d.ts | 10 ++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index af50c8c5c..31524d5b4 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -386,6 +386,12 @@ export const RamCosts: IMap = { getGameInfo: 0, }, + grafting: { + getAugmentationCraftPrice: 3.75, + getAugmentationCraftTime: 3.75, + craftAugmentation: 7.5, + }, + heart: { // Easter egg function break: 0, diff --git a/src/Script/RamCalculations.ts b/src/Script/RamCalculations.ts index 0bb432581..94f25aaea 100644 --- a/src/Script/RamCalculations.ts +++ b/src/Script/RamCalculations.ts @@ -225,6 +225,9 @@ async function parseOnlyRamCalculate( } else if (ref in workerScript.env.vars.ui) { func = workerScript.env.vars.ui[ref]; refDetail = `ui.${ref}`; + } else if (ref in workerScript.env.vars.grafting) { + func = workerScript.env.vars.grafting[ref]; + refDetail = `grafting.${ref}`; } else { func = workerScript.env.vars[ref]; refDetail = `${ref}`; diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index f871c296e..7a0d53f6e 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -3724,7 +3724,7 @@ export interface Grafting { /** * Retrieve the crafting cost of an aug. * @remarks - * RAM cost: TODO + * RAM cost: 3.75 GB * * @param augName - Name of the aug to check the price of. Must be an exact match. * @returns The cost required to craft the named augmentation. @@ -3734,7 +3734,7 @@ export interface Grafting { /** * Retrieves the time required to craft an aug. * @remarks - * RAM cost: TODO + * RAM cost: 3.75 GB * * @param augName - Name of the aug to check the crafting time of. Must be an exact match. * @returns The time required, in millis, to craft the named augmentation. @@ -3744,11 +3744,13 @@ export interface Grafting { /** * Begins crafting the named aug. You must be in New Tokyo to use this. * @remarks - * RAM cost: TODO + * RAM cost: 7.5 GB * * @param augName - The name of the aug to begin crafting. Must be an exact match. * @param focus - Acquire player focus on this Augmentation crafting. Optional. Defaults to true. - * @returns True if the aug successfully began crafting, false otherwise. + * @returns True if the aug successfully began crafting, false otherwise (e.g. not enough money, or + * invalid Augmentation name provided). + * @throws Will error if called while you are not in New Tokyo. */ craftAugmentation(augName: string, focus?: boolean): boolean; } From 5890ce0cf12ad9f9194bd0254cbfc20f644d0c6c Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Sun, 27 Mar 2022 15:40:24 -0400 Subject: [PATCH 29/30] Cleanup comments in Constants.ts --- src/Constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Constants.ts b/src/Constants.ts index 1a93ec942..8a3716783 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -277,10 +277,10 @@ export const CONSTANTS: { CodingContractBaseMoneyGain: 75e6, // Augmentation crafting multipliers - // TODO: Get these right AugmentationCraftingCostMult: 1.2, AugmentationCraftingTimeBase: 3600000, + // Value raised to the number of entropy stacks, then multiplied to player multipliers EntropyEffect: 0.99, // BitNode/Source-File related stuff From bd3f256536fe4c8e1692f46246625e087762d658 Mon Sep 17 00:00:00 2001 From: nickofolas <60761231+nickofolas@users.noreply.github.com> Date: Sun, 27 Mar 2022 15:44:25 -0400 Subject: [PATCH 30/30] Update doc for some grafting API funcs --- src/ScriptEditor/NetscriptDefinitions.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 7a0d53f6e..8a89ad0eb 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -3728,6 +3728,7 @@ export interface Grafting { * * @param augName - Name of the aug to check the price of. Must be an exact match. * @returns The cost required to craft the named augmentation. + * @throws Will error if an invalid Augmentation name is provided. */ getAugmentationCraftPrice(augName: string): number; @@ -3738,6 +3739,7 @@ export interface Grafting { * * @param augName - Name of the aug to check the crafting time of. Must be an exact match. * @returns The time required, in millis, to craft the named augmentation. + * @throws Will error if an invalid Augmentation name is provided. */ getAugmentationCraftTime(augName: string): number;