diff --git a/src/DevMenu.tsx b/src/DevMenu.tsx index 291be09d3..1072dff21 100644 --- a/src/DevMenu.tsx +++ b/src/DevMenu.tsx @@ -27,19 +27,23 @@ import { AchievementsDev } from "./DevMenu/ui/AchievementsDev"; import { EntropyDev } from "./DevMenu/ui/EntropyDev"; import { Exploit } from "./Exploits/Exploit"; +import { useRerender } from "./ui/React/hooks"; export function DevMenuRoot(): React.ReactElement { useEffect(() => { Player.giveExploit(Exploit.YoureNotMeantToAccessThis); }, []); + // Pass rerender to certain subpages in case certain tabs are now valid/invalid due to changes made on those pages + // Rerender periodically in case game state changes (e.g. player starts gang or buys wse account through a script) + const rerender = useRerender(400); return ( <> Development Menu - Only meant to be used for testing/debugging - + - + diff --git a/src/DevMenu/ui/General.tsx b/src/DevMenu/ui/General.tsx index 8f70c150f..a3e5b8101 100644 --- a/src/DevMenu/ui/General.tsx +++ b/src/DevMenu/ui/General.tsx @@ -23,11 +23,10 @@ import { Page } from "../../ui/Router"; import { Bladeburner } from "../../Bladeburner/Bladeburner"; import { GangConstants } from "../../Gang/data/Constants"; import { checkForMessagesToSend } from "../../Message/MessageHelpers"; -import { ThemeEvents } from "../../Themes/ui/Theme"; import { getEnumHelper } from "../../utils/EnumHelper"; import { formatRam } from "../../ui/formatNumber"; -export function General(): React.ReactElement { +export function General({ parentRerender }: { parentRerender: () => void }): React.ReactElement { const rerender = useRerender(400); const [error, setError] = useState(false); const [corporationName, setCorporationName] = useState(""); @@ -64,38 +63,32 @@ export function General(): React.ReactElement { // Corp functions const createCorporation = () => { Player.startCorporation(corporationName, false); - // Rerender so the corp menu option will show up immediately on the devmenu page selection - ThemeEvents.emit(); + parentRerender(); }; const destroyCorporation = () => { Player.corporation = null; - // Rerender so the corp menu option will be removed immediately on the devmenu page selection - ThemeEvents.emit(); + parentRerender(); }; // Blade functions const joinBladeburner = () => { Player.bladeburner = new Bladeburner(); - // Rerender so the blade menu option will show up immediately on the devmenu page selection - ThemeEvents.emit(); + parentRerender(); }; const leaveBladeburner = () => { Player.bladeburner = null; - // Rerender so the blade menu option will be removed immediately on the devmenu page selection - ThemeEvents.emit(); + parentRerender(); }; // Gang functions const startGang = () => { const isHacking = gangFaction === FactionName.NiteSec || gangFaction === FactionName.TheBlackHand; Player.startGang(gangFaction, isHacking); - // Rerender so the gang menu option will show up immediately on the devmenu page selection - ThemeEvents.emit(); + parentRerender(); }; const stopGang = () => { Player.gang = null; - // Rerender so the gang menu option will be removed immediately on the devmenu page selection - ThemeEvents.emit(); + parentRerender(); }; const setGangFactionDropdown = (event: SelectChangeEvent) => { // Todo: Make this a more specific check when a GangName enumlike is added diff --git a/src/DevMenu/ui/SleevesDev.tsx b/src/DevMenu/ui/SleevesDev.tsx index 66eb1bfd2..19dbad80e 100644 --- a/src/DevMenu/ui/SleevesDev.tsx +++ b/src/DevMenu/ui/SleevesDev.tsx @@ -71,6 +71,11 @@ export function SleevesDev(): React.ReactElement { + + + Total: + + void }): React.ReactElement { const classes = useStyles(); const setSF = useCallback( @@ -30,18 +32,34 @@ export function SourceFilesDev(): React.ReactElement { } if (sfLvl === 0) { Player.sourceFiles.delete(sfN); - rerender(); + if (sfN === 10) Sleeve.recalculateNumOwned(); + parentRerender(); return; } Player.sourceFiles.set(sfN, sfLvl); - rerender(); + if (sfN === 10) Sleeve.recalculateNumOwned(); + parentRerender(); }, - [rerender], + [parentRerender], ); const setAllSF = useCallback((sfLvl: number) => () => validSFN.forEach((sfN) => setSF(sfN, sfLvl)()), [setSF]); const clearExploits = () => (Player.exploits = []); + const addSleeve = useCallback(() => { + if (Player.sleevesFromCovenant >= 10) return; + Player.sleevesFromCovenant += 1; + Sleeve.recalculateNumOwned(); + parentRerender(); + }, [parentRerender]); + + const removeSleeve = useCallback(() => { + if (Player.sleevesFromCovenant <= 0) return; + Player.sleevesFromCovenant -= 1; + Sleeve.recalculateNumOwned(); + parentRerender(); + }, [parentRerender]); + const devLvls = [0, 1, 2, 3]; const buttonRow = (sfN?: number) => { @@ -66,6 +84,23 @@ export function SourceFilesDev(): React.ReactElement { ))} {sfN && {`Level: ${level}`}} + {sfN === 10 && ( + <> + + -1 sleeve + + = MaxSleevesFromCovenant ? "Already at maximum" : ""} + onClick={addSleeve} + > + +1 sleeve + + Extra sleeves: {Player.sleevesFromCovenant} + + )} diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts index 5a3290bf4..e95f8c560 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts @@ -32,7 +32,7 @@ import { resetGangs } from "../../Gang/AllGangs"; import { Cities } from "../../Locations/Cities"; import { Locations } from "../../Locations/Locations"; import { Sleeve } from "../Sleeve/Sleeve"; -import { isSleeveCompanyWork } from "../Sleeve/Work/SleeveCompanyWork"; +import { SleeveWorkType } from "../Sleeve/Work/Work"; import { calculateSkillProgress as calculateSkillProgressF, ISkillProgress } from "../formulas/skill"; import { AddToAllServers, createUniqueRandomIp } from "../../Server/AllServers"; import { safelyCreateUniqueServer } from "../../Server/ServerHelpers"; @@ -109,11 +109,7 @@ export function prestigeAugmentation(this: PlayerObject): void { this.queuedAugmentations = []; - const numSleeves = Math.min(3, this.sourceFileLvl(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++) { - this.sleeves.push(new Sleeve()); - } + Sleeve.recalculateNumOwned(); this.sleeves.forEach((sleeve) => (sleeve.shock <= 0 ? sleeve.synchronize() : sleeve.shockRecovery())); @@ -369,7 +365,7 @@ export function quitJob(this: PlayerObject, company: CompanyName): void { this.finishWork(true); } for (const sleeve of this.sleeves) { - if (isSleeveCompanyWork(sleeve.currentWork) && sleeve.currentWork.companyName === company) { + if (sleeve.currentWork?.type === SleeveWorkType.COMPANY && sleeve.currentWork.companyName === company) { sleeve.stopWork(); dialogBoxCreate(`You quit ${company} while one of your sleeves was working there. The sleeve is now idle.`); } diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 98da1958d..1649eaeb9 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -464,6 +464,19 @@ export class Sleeve extends Person implements SleevePerson { } } + static recalculateNumOwned() { + const numSleeves = + Math.min(3, Player.sourceFileLvl(10) + (Player.bitNodeN === 10 ? 1 : 0)) + Player.sleevesFromCovenant; + while (Player.sleeves.length > numSleeves) { + const destroyedSleeve = Player.sleeves.pop(); + // This should not happen, but avoid an infinite loop in case sleevesFromCovenent or sf10 level are somehow negative + if (!destroyedSleeve) return; + // Stop work, to prevent destroyed sleeves from continuing their tasks in the void + destroyedSleeve.stopWork(); + } + while (Player.sleeves.length < numSleeves) Player.sleeves.push(new Sleeve()); + } + whoAmI(): string { return "Sleeve"; } diff --git a/src/PersonObjects/Sleeve/ui/CovenantPurchasesRoot.tsx b/src/PersonObjects/Sleeve/ui/CovenantPurchasesRoot.tsx index aa4b7881d..2f5c64e15 100644 --- a/src/PersonObjects/Sleeve/ui/CovenantPurchasesRoot.tsx +++ b/src/PersonObjects/Sleeve/ui/CovenantPurchasesRoot.tsx @@ -47,7 +47,7 @@ export function CovenantPurchasesRoot(props: IProps): React.ReactElement { if (Player.canAfford(purchaseCost())) { Player.loseMoney(purchaseCost(), "sleeves"); Player.sleevesFromCovenant += 1; - Player.sleeves.push(new Sleeve()); + Sleeve.recalculateNumOwned(); rerender(); } else { dialogBoxCreate(`You cannot afford to purchase a Duplicate Sleeve`);