mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-24 18:22:58 +02:00
Fixed merge conflicts. Rebalanced new Hacknet Node mechanics. Adjusted Hacknet API so that it'll work with hacknet Servers. Fixed Corporation bug with Issuing new Shares
This commit is contained in:
@@ -12,6 +12,8 @@ import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugment
|
||||
import { Company } from "../Company/Company";
|
||||
import { CompanyPosition } from "../Company/CompanyPosition";
|
||||
import { CityName } from "../Locations/data/CityNames";
|
||||
import { HacknetNode } from "../Hacknet/HacknetNode";
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
import { LocationName } from "../Locations/data/LocationNames";
|
||||
import { IPlayerOwnedSourceFile } from "../SourceFile/PlayerOwnedSourceFile";
|
||||
import { MoneySourceTracker } from "../utils/MoneySourceTracker";
|
||||
@@ -26,7 +28,7 @@ export interface IPlayer {
|
||||
corporation: any;
|
||||
currentServer: string;
|
||||
factions: string[];
|
||||
hacknetNodes: any[];
|
||||
hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server
|
||||
hasWseAccount: boolean;
|
||||
jobs: IMap<string>;
|
||||
karma: number;
|
||||
|
||||
@@ -105,7 +105,7 @@ export abstract class Person {
|
||||
/**
|
||||
* City that the person is in
|
||||
*/
|
||||
city: string = CityName.Sector12;
|
||||
city: CityName = CityName.Sector12;
|
||||
|
||||
constructor() {}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ export function createResleevesPage(p: IPlayer) {
|
||||
display: "inline-block",
|
||||
innerText: "Sort By: "
|
||||
});
|
||||
UIElems.sortSelector = createElement("select") as HTMLSelectElement;
|
||||
UIElems.sortSelector = createElement("select", { class: "dropdown" }) as HTMLSelectElement;
|
||||
|
||||
enum SortOption {
|
||||
Cost = "Cost",
|
||||
@@ -309,7 +309,7 @@ function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
|
||||
elems.statsPanel.appendChild(elems.multipliersButton);
|
||||
|
||||
elems.augPanel = createElement("div", { class: "resleeve-panel", width: "50%" });
|
||||
elems.augSelector = createElement("select", { class: "resleeve-aug-selector" }) as HTMLSelectElement;
|
||||
elems.augSelector = createElement("select", { class: "resleeve-aug-selector dropdown" }) as HTMLSelectElement;
|
||||
elems.augDescription = createElement("p");
|
||||
for (let i = 0; i < resleeve.augmentations.length; ++i) {
|
||||
elems.augSelector.add(createOptionElement(resleeve.augmentations[i].name));
|
||||
|
||||
@@ -14,6 +14,8 @@ import { Person,
|
||||
createTaskTracker } from "../Person";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
@@ -112,13 +114,12 @@ export class Sleeve extends Person {
|
||||
logs: string[] = [];
|
||||
|
||||
/**
|
||||
* Clone retains memory% of exp upon prestige. If exp would be lower than previously
|
||||
* kept exp, nothing happens
|
||||
* Clone retains 'memory' synchronization (and maybe exp?) upon prestige/installing Augs
|
||||
*/
|
||||
memory: number = 0;
|
||||
memory: number = 1;
|
||||
|
||||
/**
|
||||
* Sleeve shock. Number between 1 and 100
|
||||
* Sleeve shock. Number between 0 and 100
|
||||
* Trauma/shock that comes with being in a sleeve. Experience earned
|
||||
* is multipled by shock%. This gets applied before synchronization
|
||||
*
|
||||
@@ -337,6 +338,31 @@ export class Sleeve extends Person {
|
||||
p.gainMoney(gain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cost of upgrading this sleeve's memory by a certain amount
|
||||
*/
|
||||
getMemoryUpgradeCost(n: number): number {
|
||||
const amt = Math.round(n);
|
||||
if (amt < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.memory + amt > 100) {
|
||||
return this.getMemoryUpgradeCost(100 - this.memory);
|
||||
}
|
||||
|
||||
const mult = 1.02;
|
||||
const baseCost = 1e12;
|
||||
let currCost = 0;
|
||||
let currMemory = this.memory-1;
|
||||
for (let i = 0; i < n; ++i) {
|
||||
currCost += (Math.pow(mult, currMemory));
|
||||
++currMemory;
|
||||
}
|
||||
|
||||
return currCost * baseCost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reputation gain for the current task
|
||||
* Only applicable when working for company or faction
|
||||
@@ -406,6 +432,20 @@ export class Sleeve extends Person {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on every sleeve for a Source File prestige
|
||||
*/
|
||||
prestige(p: IPlayer) {
|
||||
this.resetTaskStatus();
|
||||
this.earningsForSleeves = createTaskTracker();
|
||||
this.earningsForPlayer = createTaskTracker();
|
||||
this.logs = [];
|
||||
this.shock = 1;
|
||||
this.storedCycles = 0;
|
||||
this.sync = Math.max(this.memory, 1);
|
||||
this.shockRecovery(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process loop
|
||||
* Returns an object containing the amount of experience that should be
|
||||
@@ -612,12 +652,7 @@ export class Sleeve extends Person {
|
||||
/**
|
||||
* Travel to another City. Costs money from player
|
||||
*/
|
||||
travel(p: IPlayer, newCity: string): boolean {
|
||||
if (CityName[newCity] == null) {
|
||||
console.error(`Invalid city ${newCity} passed into Sleeve.travel()`);
|
||||
return false;
|
||||
}
|
||||
|
||||
travel(p: IPlayer, newCity: CityName): boolean {
|
||||
p.loseMoney(CONSTANTS.TravelCost);
|
||||
this.city = newCity;
|
||||
|
||||
@@ -641,8 +676,8 @@ export class Sleeve extends Person {
|
||||
|
||||
const company: Company | null = Companies[companyName];
|
||||
const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]];
|
||||
if (company == null) { throw new Error(`Invalid company name specified in Sleeve.workForCompany(): ${companyName}`); }
|
||||
if (companyPosition == null) { throw new Error(`Invalid CompanyPosition data in Sleeve.workForCompany(): ${companyName}`); }
|
||||
if (company == null) { return false; }
|
||||
if (companyPosition == null) { return false; }
|
||||
this.gainRatesForTask.money = companyPosition.baseSalary *
|
||||
company.salaryMultiplier *
|
||||
this.work_money_mult *
|
||||
@@ -684,8 +719,8 @@ export class Sleeve extends Person {
|
||||
* Returns boolean indicating success
|
||||
*/
|
||||
workForFaction(p: IPlayer, factionName: string, workType: string): boolean {
|
||||
if (factionName === "") { return false; }
|
||||
if (!(Factions[factionName] instanceof Faction) || !p.factions.includes(factionName)) {
|
||||
throw new Error(`Invalid Faction specified for Sleeve.workForFaction(): ${factionName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -807,6 +842,25 @@ export class Sleeve extends Person {
|
||||
return true;
|
||||
}
|
||||
|
||||
tryBuyAugmentation(p: IPlayer, aug: Augmentation): boolean {
|
||||
if (!p.canAfford(aug.startingCost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
p.loseMoney(aug.startingCost);
|
||||
this.installAugmentation(aug);
|
||||
return true;
|
||||
}
|
||||
|
||||
upgradeMemory(n: number): void {
|
||||
if (n < 0) {
|
||||
console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.memory = Math.min(100, Math.round(this.memory + n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
@@ -815,4 +869,31 @@ export class Sleeve extends Person {
|
||||
}
|
||||
}
|
||||
|
||||
export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentation[] {
|
||||
// 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 ownedAugNames: string[] = sleeve.augmentations.map((e) => {return e.name});
|
||||
const availableAugs: Augmentation[] = [];
|
||||
|
||||
for (const facName of p.factions) {
|
||||
if (facName === "Bladeburners") { continue; }
|
||||
const fac: Faction | null = Factions[facName];
|
||||
if (fac == null) { continue; }
|
||||
|
||||
for (const augName of fac.augmentations) {
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) { continue; }
|
||||
if (ownedAugNames.includes(augName)) { continue; }
|
||||
const aug: Augmentation | null = Augmentations[augName];
|
||||
|
||||
if (fac.playerReputation > aug.baseRepRequirement && !availableAugs.includes(aug)) {
|
||||
availableAugs.push(aug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableAugs;
|
||||
}
|
||||
|
||||
Reviver.constructors.Sleeve = Sleeve;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Module for handling the UI for purchasing Sleeve Augmentations
|
||||
* This UI is a popup, not a full page
|
||||
*/
|
||||
import { Sleeve } from "./Sleeve";
|
||||
import { Sleeve, findSleevePurchasableAugs } from "./Sleeve";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
@@ -29,23 +29,7 @@ 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) {
|
||||
if (facName === "Bladeburners") { continue; }
|
||||
const fac: Faction | null = Factions[facName];
|
||||
if (fac == null) { continue; }
|
||||
|
||||
for (const augName of fac.augmentations) {
|
||||
if (augName === AugmentationNames.NeuroFluxGovernor) { continue; }
|
||||
if (ownedAugNames.includes(augName)) { continue; }
|
||||
const aug: Augmentation | null = Augmentations[augName];
|
||||
|
||||
if (fac.playerReputation > aug.baseRepRequirement && !availableAugs.includes(aug)) {
|
||||
availableAugs.push(aug);
|
||||
}
|
||||
}
|
||||
}
|
||||
const availableAugs = findSleevePurchasableAugs(sleeve, p);
|
||||
|
||||
// Create popup
|
||||
const popupId = "purchase-sleeve-augs-popup";
|
||||
@@ -105,15 +89,13 @@ export function createSleevePurchaseAugsPopup(sleeve: Sleeve, p: IPlayer) {
|
||||
innerHTML:
|
||||
[
|
||||
`<h2>${aug.name}</h2><br>`,
|
||||
`Cost: ${numeralWrapper.formatMoney(aug.baseCost)}<br><br>`,
|
||||
`Cost: ${numeralWrapper.formatMoney(aug.startingCost)}<br><br>`,
|
||||
`${aug.info}`
|
||||
].join(" "),
|
||||
padding: "2px",
|
||||
clickListener: () => {
|
||||
if (p.canAfford(aug.baseCost)) {
|
||||
p.loseMoney(aug.baseCost);
|
||||
sleeve.installAugmentation(aug);
|
||||
dialogBoxCreate(`Installed ${aug.name} on Duplicate Sleeve!`, false)
|
||||
if (sleeve.tryBuyAugmentation(p, aug)) {
|
||||
dialogBoxCreate(`Installed ${aug.name} on Duplicate Sleeve!`, false);
|
||||
removeElementById(popupId);
|
||||
createSleevePurchaseAugsPopup(sleeve, p);
|
||||
} else {
|
||||
|
||||
@@ -1,48 +1,18 @@
|
||||
/**
|
||||
* Implements the purchasing of extra Duplicate Sleeves from The Covenant
|
||||
* Implements the purchasing of extra Duplicate Sleeves from The Covenant,
|
||||
* as well as the purchasing of upgrades (memory)
|
||||
*/
|
||||
import { Sleeve } from "./Sleeve";
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { yesNoBoxCreate,
|
||||
yesNoBoxClose,
|
||||
yesNoBoxGetYesButton,
|
||||
yesNoBoxGetNoButton } from "../../../utils/YesNoBox";
|
||||
import { CovenantPurchasesRoot } from "./ui/CovenantPurchasesRoot";
|
||||
import { createPopup,
|
||||
removePopup } from "../../ui/React/createPopup";
|
||||
|
||||
export const MaxSleevesFromCovenant: number = 5;
|
||||
export const BaseCostPerSleeve: number = 10e12;
|
||||
export const PopupId: string = "covenant-sleeve-purchases-popup";
|
||||
|
||||
export function createPurchaseSleevesFromCovenantPopup(p: IPlayer) {
|
||||
if (p.sleevesFromCovenant >= MaxSleevesFromCovenant) { return; }
|
||||
|
||||
// First sleeve purchased costs the base amount. Then, the price of
|
||||
// each successive one increases by the same amount
|
||||
const baseCostPerExtraSleeve: number = 10e12;
|
||||
const cost: number = (p.sleevesFromCovenant + 1) * baseCostPerExtraSleeve;
|
||||
|
||||
const yesBtn = yesNoBoxGetYesButton();
|
||||
const noBtn = yesNoBoxGetNoButton();
|
||||
|
||||
yesBtn!.addEventListener("click", () => {
|
||||
if (p.canAfford(cost)) {
|
||||
p.loseMoney(cost);
|
||||
p.sleevesFromCovenant += 1;
|
||||
p.sleeves.push(new Sleeve(p));
|
||||
yesNoBoxClose();
|
||||
} else {
|
||||
dialogBoxCreate("You cannot afford to purchase a Duplicate Sleeve", false);
|
||||
}
|
||||
});
|
||||
|
||||
noBtn!.addEventListener("click", () => {
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
const txt = `Would you like to purchase an additional Duplicate Sleeve from The Covenant for ` +
|
||||
`${numeralWrapper.formatMoney(cost)}?<br><br>` +
|
||||
`These Duplicate Sleeves are permanent. You can purchase a total of 5 Duplicate ` +
|
||||
`Sleeves from The Covenant`;
|
||||
yesNoBoxCreate(txt);
|
||||
export function createSleevePurchasesFromCovenantPopup(p: IPlayer) {
|
||||
const removePopupFn = removePopup.bind(null, PopupId);
|
||||
createPopup(PopupId, CovenantPurchasesRoot, { p: p, closeFn: removePopupFn });
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
}
|
||||
|
||||
elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" });
|
||||
elems.taskSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskSelector = createElement("select", { class: "dropdown" }) as HTMLSelectElement;
|
||||
elems.taskSelector.add(createOptionElement("------"));
|
||||
elems.taskSelector.add(createOptionElement("Work for Company"));
|
||||
elems.taskSelector.add(createOptionElement("Work for Faction"));
|
||||
@@ -297,8 +297,8 @@ function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
elems.taskSelector.add(createOptionElement("Workout at Gym"));
|
||||
elems.taskSelector.add(createOptionElement("Shock Recovery"));
|
||||
elems.taskSelector.add(createOptionElement("Synchronize"));
|
||||
elems.taskDetailsSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDetailsSelector = createElement("select", { class: "dropdown" }) as HTMLSelectElement;
|
||||
elems.taskDetailsSelector2 = createElement("select", { class: "dropdown" }) as HTMLSelectElement;
|
||||
elems.taskDescription = createElement("p");
|
||||
elems.taskProgressBar = createElement("p");
|
||||
elems.taskSelector.addEventListener("change", () => {
|
||||
@@ -383,7 +383,8 @@ function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
|
||||
`HP: ${numeralWrapper.format(sleeve.hp, "0,0")} / ${numeralWrapper.format(sleeve.max_hp, "0,0")}`,
|
||||
`City: ${sleeve.city}`,
|
||||
`Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0.000")}`,
|
||||
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`].join("<br>");
|
||||
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`,
|
||||
`Memory: ${numeralWrapper.format(sleeve.memory, "0")}`].join("<br>");
|
||||
|
||||
let repGainText: string = "";
|
||||
if (sleeve.currentTask === SleeveTaskType.Company || sleeve.currentTask === SleeveTaskType.Faction) {
|
||||
|
||||
@@ -45,5 +45,13 @@ export const SleeveFaq: string =
|
||||
"are not available for sleeves.<br><br>",
|
||||
|
||||
"<strong><u>Do sleeves get reset when installing Augmentations or switching BitNodes?</u></strong><br>",
|
||||
"Sleeves are reset when switching BitNodes, but not when installing Augmentations."
|
||||
"Sleeves are reset when switching BitNodes, but not when installing Augmentations.<br><br>",
|
||||
|
||||
"<strong><u>What is Memory?</u></strong><br>",
|
||||
"Sleeve memory dictates what a sleeve's synchronization will be",
|
||||
"when its reset by switching BitNodes. For example, if a sleeve has a memory of 25,",
|
||||
"then when you switch BitNodes its synchronization will initially be set to 25, rather than 1.<br><br>",
|
||||
"Memory can only be increased by purchasing upgrades from The Covenant. It is a",
|
||||
"persistent stat, meaning it never gets resets back to 1. The maximum possible",
|
||||
"value for a sleeve's memory is 100."
|
||||
].join(" ");
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Root React component for the popup that lets player purchase Duplicate
|
||||
* Sleeves and Sleeve-related upgrades from The Covenant
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CovenantSleeveUpgrades } from "./CovenantSleeveUpgrades";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { BaseCostPerSleeve,
|
||||
MaxSleevesFromCovenant,
|
||||
PopupId } from "../SleeveCovenantPurchases";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
|
||||
import { PopupCloseButton } from "../../../ui/React/PopupCloseButton";
|
||||
import { StdButton } from "../../../ui/React/StdButton";
|
||||
|
||||
import { dialogBoxCreate } from "../../../../utils/DialogBox";
|
||||
|
||||
interface IProps {
|
||||
closeFn: () => void;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
update: number;
|
||||
}
|
||||
|
||||
export class CovenantPurchasesRoot extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
update: 0,
|
||||
}
|
||||
|
||||
this.rerender = this.rerender.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cost to purchase a new Duplicate Sleeve
|
||||
*/
|
||||
purchaseCost(): number {
|
||||
return (this.props.p.sleevesFromCovenant + 1) * BaseCostPerSleeve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a rerender by just changing an arbitrary state value
|
||||
*/
|
||||
rerender() {
|
||||
this.setState((state: IState) => ({
|
||||
update: state.update + 1,
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
// Purchasing a new Duplicate Sleeve
|
||||
let purchaseDisabled = false;
|
||||
if (!this.props.p.canAfford(this.purchaseCost())) {
|
||||
purchaseDisabled = true;
|
||||
}
|
||||
if (this.props.p.sleevesFromCovenant >= MaxSleevesFromCovenant) {
|
||||
purchaseDisabled = true;
|
||||
}
|
||||
const purchaseOnClick = () => {
|
||||
if (this.props.p.sleevesFromCovenant >= MaxSleevesFromCovenant) { return; }
|
||||
|
||||
if (this.props.p.canAfford(this.purchaseCost())) {
|
||||
this.props.p.loseMoney(this.purchaseCost());
|
||||
this.props.p.sleevesFromCovenant += 1;
|
||||
this.props.p.sleeves.push(new Sleeve(this.props.p));
|
||||
this.rerender();
|
||||
} else {
|
||||
dialogBoxCreate(`You cannot afford to purchase a Duplicate Sleeve`, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Purchasing Upgrades for Sleeves
|
||||
const upgradePanels = [];
|
||||
for (let i = 0; i < this.props.p.sleeves.length; ++i) {
|
||||
const sleeve = this.props.p.sleeves[i];
|
||||
upgradePanels.push(
|
||||
<CovenantSleeveUpgrades {...this.props} sleeve={sleeve} index={i} rerender={this.rerender} key={i} />
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PopupCloseButton popup={PopupId} text={"Close"} />
|
||||
<p>
|
||||
Would you like to purchase an additional Duplicate Sleeve from The Covenant
|
||||
for {numeralWrapper.formatMoney(this.purchaseCost())}?
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
These Duplicate Sleeves are permanent (they persist through BitNodes). You can
|
||||
purchase a total of {MaxSleevesFromCovenant} from The Covenant.
|
||||
</p>
|
||||
<StdButton disabled={purchaseDisabled} onClick={purchaseOnClick} text={"Purchase"} />
|
||||
<br /><br />
|
||||
<p>
|
||||
Here, you can also purchase upgrades for your Duplicate Sleeves. These upgrades
|
||||
are also permanent, meaning they persist across BitNodes.
|
||||
</p>
|
||||
{upgradePanels}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* React component for a panel that lets you purchase upgrades for a Duplicate
|
||||
* Sleeve's Memory (through The Covenant)
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../../ui/numeralFormat";
|
||||
import { StdButton } from "../../../ui/React/StdButton";
|
||||
|
||||
interface IProps {
|
||||
index: number;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
amt: number;
|
||||
}
|
||||
|
||||
export class CovenantSleeveMemoryUpgrade extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
amt: 1,
|
||||
}
|
||||
|
||||
this.changePurchaseAmount = this.changePurchaseAmount.bind(this);
|
||||
this.purchaseMemory = this.purchaseMemory.bind(this);
|
||||
}
|
||||
|
||||
changePurchaseAmount(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
const n: number = parseInt(e.target.value);
|
||||
|
||||
this.setState({
|
||||
amt: n,
|
||||
});
|
||||
}
|
||||
|
||||
getPurchaseCost(): number {
|
||||
if (isNaN(this.state.amt)) { return Infinity; }
|
||||
|
||||
const maxMemory = 100 - this.props.sleeve.memory;
|
||||
if (this.state.amt > maxMemory) { return Infinity; }
|
||||
|
||||
return this.props.sleeve.getMemoryUpgradeCost(this.state.amt);
|
||||
}
|
||||
|
||||
purchaseMemory(): void {
|
||||
const cost = this.getPurchaseCost();
|
||||
if (this.props.p.canAfford(cost)) {
|
||||
this.props.sleeve.upgradeMemory(this.state.amt);
|
||||
this.props.p.loseMoney(cost);
|
||||
this.props.rerender();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const inputId = `sleeve-${this.props.index}-memory-upgrade-input`;
|
||||
|
||||
// Memory cannot go above 100
|
||||
const maxMemory = 100 - this.props.sleeve.memory;
|
||||
|
||||
// Purchase button props
|
||||
const cost = this.getPurchaseCost();
|
||||
const purchaseBtnDisabled = !this.props.p.canAfford(cost);
|
||||
let purchaseBtnText;
|
||||
if (this.state.amt > maxMemory) {
|
||||
purchaseBtnText = `Memory cannot exceed 100`;
|
||||
} else if (isNaN(this.state.amt)) {
|
||||
purchaseBtnText = "Invalid value";
|
||||
} else {
|
||||
purchaseBtnText = `Purchase ${this.state.amt} memory - ${numeralWrapper.formatMoney(cost)}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2><u>Upgrade Memory</u></h2>
|
||||
<p>
|
||||
Purchase a memory upgrade for your sleeve. Note that a sleeve's max memory
|
||||
is 100 (current: {numeralWrapper.format(this.props.sleeve.memory, "0")})
|
||||
</p>
|
||||
|
||||
<label htmlFor={inputId}>
|
||||
Amount of memory to purchase (must be an integer):
|
||||
</label>
|
||||
<input id={inputId} onChange={this.changePurchaseAmount} type={"number"} value={this.state.amt} />
|
||||
<br />
|
||||
<StdButton disabled={purchaseBtnDisabled} onClick={this.purchaseMemory} text={purchaseBtnText} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* React Component for a panel that lets you purchase upgrades for a single
|
||||
* Duplicate Sleeve through The Covenant
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CovenantSleeveMemoryUpgrade } from "./CovenantSleeveMemoryUpgrade";
|
||||
|
||||
import { Sleeve } from "../Sleeve";
|
||||
import { IPlayer } from "../../IPlayer";
|
||||
|
||||
interface IProps {
|
||||
index: number;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
sleeve: Sleeve;
|
||||
}
|
||||
|
||||
export class CovenantSleeveUpgrades extends React.Component<IProps, any> {
|
||||
render() {
|
||||
return (
|
||||
<div className={"bladeburner-action"}>
|
||||
<h1>Duplicate Sleeve {this.props.index}</h1>
|
||||
<CovenantSleeveMemoryUpgrade {...this.props} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user