Skip to content

Commit ada903f

Browse files
authored
CODEBASE: Refactor ns.singularity.purchaseAugmentation (bitburner-official#1879)
1 parent 2965f51 commit ada903f

File tree

5 files changed

+196
-86
lines changed

5 files changed

+196
-86
lines changed

src/Augmentation/ui/PurchaseAugmentationModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function PurchaseAugmentationModal({ aug, faction, onClose, open }: IProp
4040
<Button
4141
autoFocus
4242
onClick={() => {
43-
purchaseAugmentation(aug, faction);
43+
purchaseAugmentation(faction, aug);
4444
onClose();
4545
}}
4646
>

src/Faction/FactionHelpers.tsx

Lines changed: 65 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { SFC32RNG } from "../Casino/RNG";
2020
import { isFactionWork } from "../Work/FactionWork";
2121
import { getAugCost } from "../Augmentation/AugmentationHelpers";
2222
import { getRecordKeys } from "../Types/Record";
23+
import type { Result } from "../types";
2324

2425
export function inviteToFaction(faction: Faction): void {
2526
if (faction.alreadyInvited || faction.isMember) return;
@@ -58,52 +59,76 @@ export function hasAugmentationPrereqs(aug: Augmentation): boolean {
5859
return aug.prereqs.every((aug) => Player.hasAugmentation(aug));
5960
}
6061

61-
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
62-
const hasPrereqs = hasAugmentationPrereqs(aug);
63-
const augCosts = getAugCost(aug);
64-
if (!hasPrereqs) {
65-
const txt = `You must first purchase or install ${aug.prereqs
66-
.filter((req) => !Player.hasAugmentation(req))
67-
.join(",")} before you can purchase this one.`;
68-
if (sing) {
69-
return txt;
70-
} else {
71-
dialogBoxCreate(txt);
72-
}
73-
} else if (augCosts.moneyCost !== 0 && Player.money < augCosts.moneyCost) {
74-
const txt = "You don't have enough money to purchase " + aug.name;
75-
if (sing) {
76-
return txt;
62+
function checkIfPlayerCanPurchaseAugmentation(faction: Faction, augmentation: Augmentation): Result {
63+
if (!Player.factions.includes(faction.name)) {
64+
return {
65+
success: false,
66+
message: `You can't purchase augmentations from '${faction.name}' because you aren't a member.`,
67+
};
68+
}
69+
70+
if (!getFactionAugmentationsFiltered(faction).includes(augmentation.name)) {
71+
return {
72+
success: false,
73+
message: `Faction '${faction.name}' does not have the '${augmentation.name}' augmentation.`,
74+
};
75+
}
76+
77+
if (augmentation.name !== AugmentationName.NeuroFluxGovernor) {
78+
for (const queuedAugmentation of Player.queuedAugmentations) {
79+
if (queuedAugmentation.name === augmentation.name) {
80+
return { success: false, message: `You already purchased the '${augmentation.name}' augmentation.` };
81+
}
7782
}
78-
dialogBoxCreate(txt);
79-
} else if (fac.playerReputation < augCosts.repCost) {
80-
const txt = "You don't have enough faction reputation to purchase " + aug.name;
81-
if (sing) {
82-
return txt;
83+
for (const installedAugmentation of Player.augmentations) {
84+
if (installedAugmentation.name === augmentation.name) {
85+
return { success: false, message: `You already installed the '${augmentation.name}' augmentation.` };
86+
}
8387
}
84-
dialogBoxCreate(txt);
85-
} else if (augCosts.moneyCost === 0 || Player.money >= augCosts.moneyCost) {
86-
Player.queueAugmentation(aug.name);
87-
88-
Player.loseMoney(augCosts.moneyCost, "augmentations");
89-
90-
if (sing) {
91-
return "You purchased " + aug.name;
92-
} else if (!Settings.SuppressBuyAugmentationConfirmation) {
93-
dialogBoxCreate(
94-
`You purchased ${aug.name}. Its enhancements will not take effect until they are installed. ` +
95-
"To install your augmentations, go to the 'Augmentations' tab on the left-hand navigation menu. " +
96-
"Purchasing additional augmentations will now be more expensive.",
97-
);
88+
}
89+
90+
if (!hasAugmentationPrereqs(augmentation)) {
91+
return {
92+
success: false,
93+
message: `You must first purchase or install ${augmentation.prereqs
94+
.filter((req) => !Player.hasAugmentation(req))
95+
.join(",")} before you can purchase this one.`,
96+
};
97+
}
98+
99+
const augCosts = getAugCost(augmentation);
100+
if (augCosts.moneyCost !== 0 && Player.money < augCosts.moneyCost) {
101+
return { success: false, message: `You don't have enough money to purchase ${augmentation.name}.` };
102+
}
103+
104+
if (faction.playerReputation < augCosts.repCost) {
105+
return { success: false, message: `You don't have enough faction reputation to purchase ${augmentation.name}.` };
106+
}
107+
108+
return { success: true };
109+
}
110+
111+
export function purchaseAugmentation(faction: Faction, augmentation: Augmentation, singularity = false): Result {
112+
const result = checkIfPlayerCanPurchaseAugmentation(faction, augmentation);
113+
if (!result.success) {
114+
if (!singularity) {
115+
dialogBoxCreate(result.message);
98116
}
99-
} else {
117+
return { success: false, message: result.message };
118+
}
119+
120+
const augCosts = getAugCost(augmentation);
121+
Player.queueAugmentation(augmentation.name);
122+
Player.loseMoney(augCosts.moneyCost, "augmentations");
123+
124+
if (!singularity && !Settings.SuppressBuyAugmentationConfirmation) {
100125
dialogBoxCreate(
101-
"Hmm, something went wrong when trying to purchase an Augmentation. " +
102-
"Please report this to the game developer with an explanation of how to " +
103-
"reproduce this.",
126+
`You purchased ${augmentation.name}. Its enhancements will not take effect until they are installed. ` +
127+
"To install your augmentations, go to the 'Augmentations' tab on the left-hand navigation menu. " +
128+
"Purchasing additional augmentations will now be more expensive.",
104129
);
105130
}
106-
return "";
131+
return { success: true };
107132
}
108133

109134
export function processPassiveFactionRepGain(numCycles: number): void {

src/Faction/ui/AugmentationsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export function AugmentationsPage({ faction }: { faction: Faction }): React.Reac
250250
if (!Settings.SuppressBuyAugmentationConfirmation) {
251251
showModal(true);
252252
} else {
253-
purchaseAugmentation(aug, faction);
253+
purchaseAugmentation(faction, aug);
254254
rerender();
255255
}
256256
}}

src/NetscriptFunctions/Singularity.ts

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Singularity as ISingularity } from "@nsdefs";
22

33
import { Player } from "@player";
4-
import { AugmentationName, CityName, FactionWorkType, GymType, LocationName, UniversityClassType } from "@enums";
4+
import { CityName, FactionWorkType, GymType, LocationName, UniversityClassType } from "@enums";
55
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
66
import { startWorkerScript } from "../NetscriptWorker";
77
import { Augmentations } from "../Augmentation/Augmentations";
@@ -163,50 +163,17 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
163163
helpers.checkSingularityAccess(ctx);
164164
const facName = getEnumHelper("FactionName").nsGetMember(ctx, _facName);
165165
const augName = getEnumHelper("AugmentationName").nsGetMember(ctx, _augName);
166-
const fac = Factions[facName];
167-
const aug = Augmentations[augName];
168-
169-
const factionAugs = getFactionAugmentationsFiltered(fac);
170-
171-
if (!Player.factions.includes(facName)) {
172-
helpers.log(ctx, () => `You can't purchase augmentations from '${facName}' because you aren't a member`);
173-
return false;
174-
}
175-
176-
if (!factionAugs.includes(augName)) {
177-
helpers.log(ctx, () => `Faction '${facName}' does not have the '${augName}' augmentation.`);
178-
return false;
179-
}
180-
181-
const isNeuroflux = aug.name === AugmentationName.NeuroFluxGovernor;
182-
if (!isNeuroflux) {
183-
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
184-
if (Player.queuedAugmentations[j].name === aug.name) {
185-
helpers.log(ctx, () => `You already have the '${augName}' augmentation.`);
186-
return false;
187-
}
188-
}
189-
for (let j = 0; j < Player.augmentations.length; ++j) {
190-
if (Player.augmentations[j].name === aug.name) {
191-
helpers.log(ctx, () => `You already have the '${augName}' augmentation.`);
192-
return false;
193-
}
194-
}
195-
}
196-
197-
if (fac.playerReputation < getAugCost(aug).repCost) {
198-
helpers.log(ctx, () => `You do not have enough reputation with '${fac.name}'.`);
199-
return false;
200-
}
166+
const faction = Factions[facName];
167+
const augmentation = Augmentations[augName];
201168

202-
const res = purchaseAugmentation(aug, fac, true);
203-
helpers.log(ctx, () => res);
204-
if (res.startsWith("You purchased")) {
205-
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 10);
206-
return true;
207-
} else {
169+
const result = purchaseAugmentation(faction, augmentation, true);
170+
if (!result.success) {
171+
helpers.log(ctx, () => result.message);
208172
return false;
209173
}
174+
helpers.log(ctx, () => `You purchased ${augName}.`);
175+
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 10);
176+
return true;
210177
},
211178
softReset: (ctx) => (_cbScript) => {
212179
helpers.checkSingularityAccess(ctx);

test/jest/Netscript/Singularity.test.ts

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { installAugmentations } from "../../../src/Augmentation/AugmentationHelpers";
22
import { blackOpsArray } from "../../../src/Bladeburner/data/BlackOperations";
3-
import { AugmentationName } from "../../../src/Enums";
4-
import { Player } from "../../../src/Player";
3+
import { AugmentationName, FactionName } from "@enums";
4+
import { Player } from "@player";
5+
import { prestigeSourceFile } from "../../../src/Prestige";
56
import { GetServerOrThrow } from "../../../src/Server/AllServers";
67
import { SpecialServers } from "../../../src/Server/data/SpecialServers";
8+
import { Factions } from "../../../src/Faction/Factions";
9+
import { PlayerOwnedAugmentation } from "../../../src/Augmentation/PlayerOwnedAugmentation";
710
import { getNS, initGameEnvironment, setupBasicTestingEnvironment } from "./Utilities";
811

912
function setNumBlackOpsComplete(value: number): void {
@@ -179,3 +182,118 @@ describe("destroyW0r1dD43m0n", () => {
179182
});
180183
});
181184
});
185+
186+
describe("purchaseAugmentation", () => {
187+
beforeEach(() => {
188+
setupBasicTestingEnvironment();
189+
prestigeSourceFile(true);
190+
Player.money = 1e100;
191+
Player.factions.push(FactionName.CyberSec);
192+
Factions[FactionName.CyberSec].playerReputation = 1e10;
193+
Player.factions.push(FactionName.Illuminati);
194+
});
195+
196+
describe("Success", () => {
197+
const expectQueuedAugmentation = (augmentationName: AugmentationName, level: number) => {
198+
expect(
199+
Player.queuedAugmentations.find((augmentation) => augmentation.name === augmentationName)?.level,
200+
).toStrictEqual(level);
201+
};
202+
test("NFG", () => {
203+
const ns = getNS();
204+
expect(
205+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.NeuroFluxGovernor),
206+
).toStrictEqual(true);
207+
expectQueuedAugmentation(AugmentationName.NeuroFluxGovernor, 1);
208+
});
209+
// Check if the level of NFG is increased properly.
210+
test("Upgrade NFG", () => {
211+
Player.augmentations.push(new PlayerOwnedAugmentation(AugmentationName.NeuroFluxGovernor));
212+
const ns = getNS();
213+
expect(
214+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.NeuroFluxGovernor),
215+
).toStrictEqual(true);
216+
expectQueuedAugmentation(AugmentationName.NeuroFluxGovernor, 2);
217+
});
218+
test("Normal augmentation", () => {
219+
const ns = getNS();
220+
expect(
221+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.CranialSignalProcessorsG1),
222+
).toStrictEqual(true);
223+
expectQueuedAugmentation(AugmentationName.CranialSignalProcessorsG1, 1);
224+
});
225+
test("Normal augmentation with prerequisite", () => {
226+
Player.augmentations.push(new PlayerOwnedAugmentation(AugmentationName.CranialSignalProcessorsG1));
227+
const ns = getNS();
228+
expect(
229+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.CranialSignalProcessorsG2),
230+
).toStrictEqual(true);
231+
expectQueuedAugmentation(AugmentationName.CranialSignalProcessorsG2, 1);
232+
});
233+
test("Buy 0-money-cost augmentation with negative money", () => {
234+
Player.money = -1000;
235+
Player.factions.push(FactionName.Daedalus);
236+
Factions[FactionName.Daedalus].playerReputation = 1e10;
237+
const ns = getNS();
238+
expect(ns.singularity.purchaseAugmentation(FactionName.Daedalus, AugmentationName.TheRedPill)).toStrictEqual(
239+
true,
240+
);
241+
expectQueuedAugmentation(AugmentationName.TheRedPill, 1);
242+
});
243+
});
244+
245+
describe("Failure", () => {
246+
const expectNoQueuedAugmentation = (augmentationName: AugmentationName) => {
247+
expect(Player.queuedAugmentations.find((augmentation) => augmentation.name === augmentationName)).toStrictEqual(
248+
undefined,
249+
);
250+
};
251+
test("Not a member of specified faction", () => {
252+
const ns = getNS();
253+
expect(
254+
ns.singularity.purchaseAugmentation(FactionName.Daedalus, AugmentationName.NeuroFluxGovernor),
255+
).toStrictEqual(false);
256+
expectNoQueuedAugmentation(AugmentationName.NeuroFluxGovernor);
257+
});
258+
test("Faction does not have specified augmentation", () => {
259+
const ns = getNS();
260+
expect(ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.QLink)).toStrictEqual(false);
261+
expectNoQueuedAugmentation(AugmentationName.QLink);
262+
});
263+
test("Purchase installed augmentation", () => {
264+
Player.augmentations.push(new PlayerOwnedAugmentation(AugmentationName.CranialSignalProcessorsG1));
265+
const ns = getNS();
266+
expect(
267+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.CranialSignalProcessorsG1),
268+
).toStrictEqual(false);
269+
expectNoQueuedAugmentation(AugmentationName.CranialSignalProcessorsG1);
270+
});
271+
test("Purchase queued augmentation", () => {
272+
Player.queuedAugmentations.push(new PlayerOwnedAugmentation(AugmentationName.CranialSignalProcessorsG1));
273+
const ns = getNS();
274+
expect(
275+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.CranialSignalProcessorsG1),
276+
).toStrictEqual(false);
277+
});
278+
test("Not have prerequisite augmentation", () => {
279+
const ns = getNS();
280+
expect(
281+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.CranialSignalProcessorsG2),
282+
).toStrictEqual(false);
283+
expectNoQueuedAugmentation(AugmentationName.CranialSignalProcessorsG2);
284+
});
285+
test("Not enough money", () => {
286+
Player.money = 1000;
287+
const ns = getNS();
288+
expect(
289+
ns.singularity.purchaseAugmentation(FactionName.CyberSec, AugmentationName.CranialSignalProcessorsG1),
290+
).toStrictEqual(false);
291+
expectNoQueuedAugmentation(AugmentationName.CranialSignalProcessorsG1);
292+
});
293+
test("Not enough reputation", () => {
294+
const ns = getNS();
295+
expect(ns.singularity.purchaseAugmentation(FactionName.Illuminati, AugmentationName.QLink)).toStrictEqual(false);
296+
expectNoQueuedAugmentation(AugmentationName.QLink);
297+
});
298+
});
299+
});

0 commit comments

Comments
 (0)