diff --git a/src/DevMenu.js b/src/DevMenu.js index ceadc51cf..03b72f108 100644 --- a/src/DevMenu.js +++ b/src/DevMenu.js @@ -51,6 +51,15 @@ export function createDevMenu() { innerText: "Add $1000t", }); + const addMoney2 = createElement("button", { + class: "std-button", + clickListener: () => { + Player.gainMoney(1e12); + }, + display: "block", + innerText: "Add $1t", + }) + const addRam = createElement("button", { class: "std-button", clickListener: () => { @@ -588,6 +597,20 @@ export function createDevMenu() { innerText: "View Stock Price Caps", }); + // Sleeves + const sleevesHeader = createElement("h2", { innerText: "Sleeves" }); + + const sleevesRemoveAllShockRecovery = createElement("button", { + class: "std-button", + display: "block", + innerText: "Set Shock Recovery of All Sleeves to 0", + clickListener: () => { + for (let i = 0; i < Player.sleeves.length; ++i) { + Player.sleeves[i].shock = 100; + } + } + }); + // Add everything to container, then append to main menu const devMenuContainer = createElement("div", { class: "generic-menupage-container", @@ -597,6 +620,7 @@ export function createDevMenu() { devMenuContainer.appendChild(devMenuText); devMenuContainer.appendChild(genericHeader); devMenuContainer.appendChild(addMoney); + devMenuContainer.appendChild(addMoney2); devMenuContainer.appendChild(addRam); devMenuContainer.appendChild(triggerBitflume); devMenuContainer.appendChild(destroyCurrentBitnode); @@ -674,6 +698,8 @@ export function createDevMenu() { devMenuContainer.appendChild(stockPriceChangeBtn); devMenuContainer.appendChild(createElement("br")); devMenuContainer.appendChild(stockViewPriceCapBtn); + devMenuContainer.appendChild(sleevesHeader); + devMenuContainer.appendChild(sleevesRemoveAllShockRecovery); const entireGameContainer = document.getElementById("entire-game-container"); if (entireGameContainer == null) { diff --git a/src/PersonObjects/Person.ts b/src/PersonObjects/Person.ts index 6a80dce2f..9bc208a0a 100644 --- a/src/PersonObjects/Person.ts +++ b/src/PersonObjects/Person.ts @@ -5,7 +5,6 @@ import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentatio import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { Cities } from "../Locations/Cities"; import { CONSTANTS } from "../Constants"; -import { IMap } from "../types"; // Interface that defines a generic object used to track experience/money // earnings for tasks diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index a634a8d2f..a02842560 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -13,6 +13,8 @@ import { Person, ITaskTracker, createTaskTracker } from "../Person"; +import { Augmentation } from "../../Augmentation/Augmentation"; + import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { Crime } from "../../Crime/Crime"; @@ -136,8 +138,11 @@ export class Sleeve extends Person { */ sync: number = 1; - constructor() { + constructor(p: IPlayer | null = null) { super(); + if (p != null) { + this.shockRecovery(p); + } } /** @@ -381,6 +386,17 @@ export class Sleeve extends Person { } } + installAugmentation(aug: Augmentation): void { + this.hacking_exp = 0; + this.strength_exp = 0; + this.defense_exp = 0; + this.dexterity_exp = 0; + this.agility_exp = 0; + this.charisma_exp = 0; + this.applyAugmentation(aug); + this.updateStatLevels(); + } + log(entry: string): void { const MaxLogSize: number = 50; this.logs.push(entry); @@ -452,10 +468,10 @@ export class Sleeve extends Person { company!.playerReputation += (this.getRepGain(p) * cyclesUsed); break; case SleeveTaskType.Recovery: - this.shock = Math.min(100, this.shock + (0.0001 * cyclesUsed)); + this.shock = Math.min(100, this.shock + (0.0002 * cyclesUsed)); break; case SleeveTaskType.Sync: - this.sync = Math.min(100, this.sync + (0.0001 * cyclesUsed)); + this.sync = Math.min(100, this.sync + (0.0002 * cyclesUsed)); break; default: break; @@ -492,6 +508,28 @@ export class Sleeve extends Person { this.gymStatType = ""; } + shockRecovery(p: IPlayer): boolean { + if (this.currentTask !== SleeveTaskType.Idle) { + this.finishTask(p); + } else { + this.resetTaskStatus(); + } + + this.currentTask = SleeveTaskType.Recovery; + return true; + } + + synchronize(p: IPlayer): boolean { + if (this.currentTask !== SleeveTaskType.Idle) { + this.finishTask(p); + } else { + this.resetTaskStatus(); + } + + this.currentTask = SleeveTaskType.Sync; + return true; + } + /** * Take a course at a university */ diff --git a/src/PersonObjects/Sleeve/SleeveAugmentationsUI.ts b/src/PersonObjects/Sleeve/SleeveAugmentationsUI.ts new file mode 100644 index 000000000..280b8b37b --- /dev/null +++ b/src/PersonObjects/Sleeve/SleeveAugmentationsUI.ts @@ -0,0 +1,91 @@ +/** + * Module for handling the UI for purchasing Sleeve Augmentations + * This UI is a popup, not a full page + */ +import { Sleeve } from "./Sleeve"; + +import { IPlayer } from "../IPlayer"; + +import { Augmentation } from "../../Augmentation/Augmentation"; +import { Augmentations } from "../../Augmentation/Augmentations"; + +import { Faction } from "../../Faction/Faction"; +import { Factions } from "../../Faction/Factions"; + +import { numeralWrapper } from "../../ui/numeralFormat"; + +import { dialogBoxCreate } from "../../../utils/DialogBox"; + +import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners"; +import { createElement } from "../../../utils/uiHelpers/createElement"; +import { createPopup } from "../../../utils/uiHelpers/createPopup"; +import { createPopupCloseButton } from "../../../utils/uiHelpers/createPopupCloseButton"; +import { getSelectValue } from "../../../utils/uiHelpers/getSelectData"; +import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement"; +import { removeElement } from "../../../utils/uiHelpers/removeElement"; +import { removeElementById } from "../../../utils/uiHelpers/removeElementById"; + +export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) { + // You can only purchase Augmentations that are actually available from + // your factions. I.e. you must be in a faction that has the Augmentation + // and you must also have enough rep in that faction in order to purchase it. + const availableAugs: Augmentation[] = []; + + for (const facName of p.factions) { + const fac: Faction | null = Factions[facName]; + if (fac == null) { continue; } + + for (const augName of fac.augmentations) { + const aug: Augmentation | null = Augmentations[augName]; + + if (fac.playerReputation > aug.baseRepRequirement && !availableAugs.includes(aug)) { + availableAugs.push(aug); + } + } + } + + + // General info about buying Augmentations + const info = createElement("p", { + innerHTML: + [ + `You can purchase Augmentations for your Duplicate Sleeves. These Augmentations`, + `have the same effect as they would for you. You can only purchase Augmentations`, + `that you have unlocked through Factions.

`, + `When purchasing an Augmentation for a Duplicate Sleeve, they are immediately`, + `installed. This means that the Duplicate Sleeve will immediately lose all of`, + `its stat experience.` + ].join(" "), + }); + + const popupId = "purchase-sleeve-augs-popup"; + const popupElems: HTMLElement[] = [info]; + + for (const aug of availableAugs) { + const div = createElement("div", { + class: "cmpy-mgmt-upgrade-div", // We'll reuse this CSS class + }); + + div.appendChild(createElement("p", { + innerHTML: + [ + `

${aug.name}


`, + `Cost: ${numeralWrapper.formatMoney(aug.baseCost)}

`, + `${aug.info}` + ].join(" "), + clickListener: () => { + if (p.canAfford(aug.baseCost)) { + p.loseMoney(aug.baseCost); + sleeve.installAugmentation(aug); + dialogBoxCreate(`Installed ${aug.name} on Duplicate Sleeve!`, false) + } else { + dialogBoxCreate(`You cannot afford ${aug.name}`, false); + } + } + })); + + popupElems.push(div); + } + + createPopup(popupId, popupElems); +} diff --git a/src/PersonObjects/Sleeve/SleeveCovenantPurchases.ts b/src/PersonObjects/Sleeve/SleeveCovenantPurchases.ts index b05eb80bf..496641415 100644 --- a/src/PersonObjects/Sleeve/SleeveCovenantPurchases.ts +++ b/src/PersonObjects/Sleeve/SleeveCovenantPurchases.ts @@ -29,7 +29,7 @@ export function createPurchaseSleevesFromCovenantPopup(p: IPlayer) { if (p.canAfford(cost)) { p.loseMoney(cost); p.sleevesFromCovenant += 1; - p.sleeves.push(new Sleeve()); + p.sleeves.push(new Sleeve(p)); yesNoBoxClose(); } else { dialogBoxCreate("You cannot afford to purchase a Duplicate Sleeve", false); diff --git a/src/PersonObjects/Sleeve/SleeveUI.ts b/src/PersonObjects/Sleeve/SleeveUI.ts index 486e510ea..a9a0197fd 100644 --- a/src/PersonObjects/Sleeve/SleeveUI.ts +++ b/src/PersonObjects/Sleeve/SleeveUI.ts @@ -10,6 +10,8 @@ import { IPlayer } from "../IPlayer"; import { CONSTANTS } from "../../Constants"; import { Locations } from "../../Locations"; +import { Augmentations } from "../../Augmentation/Augmentations"; + import { Faction } from "../../Faction/Faction"; import { Factions } from "../../Faction/Factions"; import { FactionWorkType } from "../../Faction/FactionWorkTypeEnum"; @@ -44,6 +46,7 @@ interface ISleeveUIElems { stats: HTMLElement | null; moreStatsButton: HTMLElement | null; travelButton: HTMLElement | null; + purchaseAugsButton: HTMLElement | null; taskPanel: HTMLElement | null; taskSelector: HTMLSelectElement | null; taskDetailsSelector: HTMLSelectElement | null; @@ -171,6 +174,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems { stats: null, moreStatsButton: null, travelButton: null, + purchaseAugsButton: null, taskPanel: null, taskSelector: null, taskDetailsSelector: null, @@ -267,10 +271,22 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems { createPopup(popupId, popupArguments); } - }) + }); + elems.purchaseAugsButton = createElement("button", { + class: "std-button", + display: "block", + innerText: "Purchase Augmentations", + clickListener: () => { + + } + }); elems.statsPanel.appendChild(elems.stats); elems.statsPanel.appendChild(elems.moreStatsButton); elems.statsPanel.appendChild(elems.travelButton); + if (sleeve.shock >= 100) { + // You can only buy augs when shock recovery is 0 + elems.statsPanel.appendChild(elems.purchaseAugsButton); + } elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" }); elems.taskSelector = createElement("select") as HTMLSelectElement; @@ -655,14 +671,11 @@ function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): boolean { res = sleeve.workoutAtGym(playerRef!, detailValue2, detailValue); break; case "Shock Recovery": - sleeve.finishTask(playerRef!); sleeve.currentTask = SleeveTaskType.Recovery; - res = true; + res = sleeve.shockRecovery(playerRef!); break; case "Synchronize": - sleeve.finishTask(playerRef!); - sleeve.currentTask = SleeveTaskType.Sync; - res = true; + res = sleeve.synchronize(playerRef!); break; default: console.error(`Invalid/Unrecognized taskValue in setSleeveTask(): ${taskValue}`); diff --git a/src/Player.js b/src/Player.js index 9a47e79f1..5c48555ee 100644 --- a/src/Player.js +++ b/src/Player.js @@ -281,7 +281,7 @@ PlayerObject.prototype.prestigeAugmentation = function() { for (let i = 0; i < this.sleeves.length; ++i) { if (this.sleeves[i] instanceof Sleeve) { - this.sleeves[i].resetTaskStatus(); + this.sleeves[i].shockRecovery(this); } } @@ -372,7 +372,7 @@ PlayerObject.prototype.prestigeSourceFile = function() { // Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists) this.sleeves.length = SourceFileFlags[10] + this.sleevesFromCovenant; for (let i = 0; i < this.sleeves.length; ++i) { - this.sleeves[i] = new Sleeve(); + this.sleeves[i] = new Sleeve(this); } this.isWorking = false;