diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts
index 8840ea1b8..154295e4b 100644
--- a/src/@types/global.d.ts
+++ b/src/@types/global.d.ts
@@ -1,8 +1,13 @@
// Defined by webpack on startup or compilation
-declare let __COMMIT_HASH__: string;
+declare const __COMMIT_HASH__: string;
// When using file-loader, we'll get a path to the resource
declare module "*.png" {
const value: string;
export default value;
}
+
+// Achievements communicated back to Electron shell for Steam.
+declare interface Document {
+ achievements: string[];
+}
diff --git a/src/Achievements/Achievements.ts b/src/Achievements/Achievements.ts
index c8b56065b..69cf75266 100644
--- a/src/Achievements/Achievements.ts
+++ b/src/Achievements/Achievements.ts
@@ -799,5 +799,5 @@ export function calculateAchievements(): void {
// Write all player's achievements to document for Steam/Electron
// This could be replaced by "availableAchievements"
// if we don't want to grant the save game achievements to steam but only currently available
- (document as any).achievements = [...Player.achievements.map((a) => a.ID)];
+ document.achievements = [...Player.achievements.map((a) => a.ID)];
}
diff --git a/src/Augmentation/data/AugmentationCreator.tsx b/src/Augmentation/data/AugmentationCreator.tsx
index 02ce0bdd2..6c3519df5 100644
--- a/src/Augmentation/data/AugmentationCreator.tsx
+++ b/src/Augmentation/data/AugmentationCreator.tsx
@@ -109,6 +109,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
rewards, reduced damage taken, etc.
>
),
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -121,6 +122,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
stats: (
<>This augmentation makes the Slash minigame easier by showing you via an indictor when the slash in coming.>
),
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -129,6 +131,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
moneyCost: 1e6,
info: "A connective brain implant to SASHA that focuses in pattern recognition and predictive templating.",
stats: <>This augmentation makes the Bracket minigame easier by removing all '[' ']'.>,
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -137,6 +140,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
moneyCost: 1e6,
info: "Opto-occipito implant to process visual signal before brain interpretation.",
stats: <>This augmentation makes the Backwards minigame easier by flipping the words.>,
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -147,6 +151,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
"Pheromone extruder injected in the thoracodorsal nerve. Emits pleasing scent guaranteed to " +
"make conversational partners more agreeable.",
stats: <>This augmentation makes the Bribe minigame easier by indicating the incorrect paths.>,
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -155,6 +160,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
moneyCost: 1e6,
info: "Penta-dynamo-neurovascular-valve inserted in the carpal ligament, enhances dexterity.",
stats: <>This augmentation makes the Cheat Code minigame easier by allowing the opposite character.>,
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -163,6 +169,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
moneyCost: 1e6,
info: "Transtinatium VVD reticulator used in optico-sterbing recognition.",
stats: <>This augmentation makes the Symbol matching minigame easier by indicating the correct choice.>,
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -176,6 +183,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
position.
>
),
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
new Augmentation({
@@ -184,6 +192,7 @@ export const initSoAAugmentations = (): Augmentation[] => [
moneyCost: 1e6,
info: "Neodynic retention fjengeln spoofer using -φ karmions, net positive effect on implantees delta wave.",
stats: <>This augmentation makes the Wire Cutting minigame easier by indicating the incorrect wires.>,
+ isSpecial: true,
factions: [FactionNames.ShadowsOfAnarchy],
}),
];
@@ -242,7 +251,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
moneyCost: 1.15e8,
repCost: 2.75e4,
info: "The latest version of the 'Augmented Targeting' implant adds the ability to lock-on and track threats.",
- prereqs: [AugmentationNames.Targeting2],
+ prereqs: [AugmentationNames.Targeting2, AugmentationNames.Targeting1],
dexterity_mult: 1.3,
factions: [
FactionNames.TheDarkArmy,
@@ -339,7 +348,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
info:
"The latest version of the 'Combat Rib' augmentation releases advanced anabolic steroids that " +
"improve muscle mass and physical performance while being safe and free of side effects.",
- prereqs: [AugmentationNames.CombatRib2],
+ prereqs: [AugmentationNames.CombatRib2, AugmentationNames.CombatRib1],
strength_mult: 1.18,
defense_mult: 1.18,
factions: [
@@ -673,7 +682,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
"This upgraded firmware allows the Embedded Netburner Module to control information on " +
"a network by re-routing traffic, spoofing IP addresses, and altering the data inside network " +
"packets.",
- prereqs: [AugmentationNames.ENMCore],
+ prereqs: [AugmentationNames.ENMCore, AugmentationNames.ENM],
hacking_speed_mult: 1.05,
hacking_money_mult: 1.3,
hacking_chance_mult: 1.05,
@@ -698,7 +707,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
"The Core V3 library is an implant that upgrades the firmware of the Embedded Netburner Module. " +
"This upgraded firmware allows the Embedded Netburner Module to seamlessly inject code into " +
"any device on a network.",
- prereqs: [AugmentationNames.ENMCoreV2],
+ prereqs: [AugmentationNames.ENMCoreV2, AugmentationNames.ENMCore, AugmentationNames.ENM],
hacking_speed_mult: 1.05,
hacking_money_mult: 1.4,
hacking_chance_mult: 1.1,
@@ -826,7 +835,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
"are a set of specialized microprocessors that are attached to " +
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
"so that the brain doesn't have to.",
- prereqs: [AugmentationNames.CranialSignalProcessorsG2],
+ prereqs: [AugmentationNames.CranialSignalProcessorsG2, AugmentationNames.CranialSignalProcessorsG1],
hacking_speed_mult: 1.02,
hacking_money_mult: 1.15,
hacking_mult: 1.09,
@@ -841,7 +850,11 @@ export const initGeneralAugmentations = (): Augmentation[] => [
"are a set of specialized microprocessors that are attached to " +
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
"so that the brain doesn't have to.",
- prereqs: [AugmentationNames.CranialSignalProcessorsG3],
+ prereqs: [
+ AugmentationNames.CranialSignalProcessorsG3,
+ AugmentationNames.CranialSignalProcessorsG2,
+ AugmentationNames.CranialSignalProcessorsG1,
+ ],
hacking_speed_mult: 1.02,
hacking_money_mult: 1.2,
hacking_grow_mult: 1.25,
@@ -856,7 +869,12 @@ export const initGeneralAugmentations = (): Augmentation[] => [
"are a set of specialized microprocessors that are attached to " +
"neurons in the brain. These chips process neural signals to quickly and automatically perform specific computations " +
"so that the brain doesn't have to.",
- prereqs: [AugmentationNames.CranialSignalProcessorsG4],
+ prereqs: [
+ AugmentationNames.CranialSignalProcessorsG4,
+ AugmentationNames.CranialSignalProcessorsG3,
+ AugmentationNames.CranialSignalProcessorsG2,
+ AugmentationNames.CranialSignalProcessorsG1,
+ ],
hacking_mult: 1.3,
hacking_money_mult: 1.25,
hacking_grow_mult: 1.75,
@@ -1254,6 +1272,7 @@ export const initGeneralAugmentations = (): Augmentation[] => [
moneyCost: 0,
info: "It's time to leave the cave.",
stats: null,
+ isSpecial: true,
factions: [FactionNames.Daedalus],
}),
new Augmentation({
@@ -1952,7 +1971,7 @@ export const initChurchOfTheMachineGodAugmentations = (): Augmentation[] => [
"You will become greater than the sum of our parts. As One. Embrace your gift " +
"fully and wholly free of it's accursed toll. Serenity brings tranquility the form " +
"of no longer suffering a stat penalty. ",
- prereqs: [AugmentationNames.StaneksGift2],
+ prereqs: [AugmentationNames.StaneksGift2, AugmentationNames.StaneksGift1],
isSpecial: true,
hacking_chance_mult: 1 / 0.95,
hacking_speed_mult: 1 / 0.95,
@@ -2003,6 +2022,7 @@ export function initNeuroFluxGovernor(): Augmentation {
multiplicatively.
>
),
+ isSpecial: true,
hacking_chance_mult: 1.01 + donationBonus,
hacking_speed_mult: 1.01 + donationBonus,
hacking_money_mult: 1.01 + donationBonus,
diff --git a/src/Augmentation/ui/PurchasableAugmentations.tsx b/src/Augmentation/ui/PurchasableAugmentations.tsx
new file mode 100644
index 000000000..488327f4b
--- /dev/null
+++ b/src/Augmentation/ui/PurchasableAugmentations.tsx
@@ -0,0 +1,264 @@
+/**
+ * React component for displaying a single augmentation for purchase through
+ * the faction UI
+ */
+import { CheckBox, CheckBoxOutlineBlank, CheckCircle, Info, NewReleases, Report } from "@mui/icons-material";
+import { Box, Button, Container, Paper, Tooltip, Typography } from "@mui/material";
+import React, { useState } from "react";
+import { getNextNeuroFluxLevel } from "../AugmentationHelpers";
+import { Faction } from "../../Faction/Faction";
+import { IPlayer } from "../../PersonObjects/IPlayer";
+import { Settings } from "../../Settings/Settings";
+import { numeralWrapper } from "../../ui/numeralFormat";
+import { Augmentation } from "../Augmentation";
+import { Augmentations } from "../Augmentations";
+import { AugmentationNames } from "../data/AugmentationNames";
+import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
+
+interface IPreReqsProps {
+ player: IPlayer;
+ aug: Augmentation;
+}
+
+const PreReqs = (props: IPreReqsProps): React.ReactElement => {
+ const ownedPreReqs = props.aug.prereqs.filter((aug) => props.player.hasAugmentation(aug));
+ const hasPreReqs = props.aug.prereqs.length > 0 && ownedPreReqs.length === props.aug.prereqs.length;
+
+ return (
+
+
+ This Augmentation has the following pre-requisite(s):
+
+ {props.aug.prereqs.map((preAug) => (
+
+ ))}
+ >
+ }
+ >
+
+ {hasPreReqs ? (
+ <>
+
+ Pre-requisites Owned
+ >
+ ) : (
+ <>
+
+ Missing {props.aug.prereqs.length - ownedPreReqs.length} pre-requisite(s)
+ >
+ )}
+
+
+ );
+};
+
+interface IExclusiveProps {
+ player: IPlayer;
+ aug: Augmentation;
+}
+
+const Exclusive = (props: IExclusiveProps): React.ReactElement => {
+ return (
+
+
+ This Augmentation can only be acquired from the following source(s):
+
+
+
+ -
+ {props.aug.factions[0]} faction
+
+ {props.player.canAccessGang() && !props.aug.isSpecial && (
+ -
+ Certain gangs
+
+ )}
+ {props.player.canAccessGrafting() &&
+ !props.aug.isSpecial &&
+ props.aug.name !== AugmentationNames.TheRedPill && (
+ -
+ Grafting
+
+ )}
+
+
+ >
+ }
+ >
+
+
+ );
+};
+
+interface IReqProps {
+ value: string;
+ color: string;
+ fulfilled: boolean;
+}
+
+const Requirement = (props: IReqProps): React.ReactElement => {
+ return (
+
+ {props.fulfilled ? : }
+ {props.value}
+
+ );
+};
+
+interface IPurchasableAugsProps {
+ augNames: string[];
+ ownedAugNames: string[];
+ player: IPlayer;
+
+ canPurchase: (player: IPlayer, aug: Augmentation) => boolean;
+ purchaseAugmentation: (player: IPlayer, aug: Augmentation, showModal: (open: boolean) => void) => void;
+
+ rep?: number;
+ sleeveAugs?: boolean;
+ faction?: Faction;
+}
+
+export const PurchasableAugmentations = (props: IPurchasableAugsProps): React.ReactElement => {
+ return (
+
+ {props.augNames.map((augName: string) => (
+
+ ))}
+ {props.ownedAugNames.map((augName: string) => (
+
+ ))}
+
+ );
+};
+
+interface IPurchasableAugProps {
+ parent: IPurchasableAugsProps;
+ augName: string;
+ owned: boolean;
+}
+
+export function PurchasableAugmentation(props: IPurchasableAugProps): React.ReactElement {
+ const [open, setOpen] = useState(false);
+
+ const aug = Augmentations[props.augName];
+
+ const cost = props.parent.sleeveAugs ? aug.startingCost : aug.baseCost;
+
+ const info = typeof aug.info === "string" ? {aug.info} : aug.info;
+ const description = (
+ <>
+ {info}
+
+
+ {aug.stats}
+ >
+ );
+
+ return (
+
+ <>
+
+
+
+
+
+
+
+ {props.augName}
+ {props.augName === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
+
+ {description}
+ >
+ }
+ >
+
+
+
+ {aug.name}
+ {aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
+
+ {aug.factions.length === 1 && !props.parent.sleeveAugs && (
+
+ )}
+
+
+ {aug.prereqs.length > 0 && !props.parent.sleeveAugs && }
+
+
+
+ {props.owned || (
+
+ cost}
+ value={numeralWrapper.formatMoney(cost)}
+ color={Settings.theme.money}
+ />
+ {props.parent.rep !== undefined && (
+ = aug.baseRepRequirement}
+ value={`${numeralWrapper.formatReputation(aug.baseRepRequirement)} rep`}
+ color={Settings.theme.rep}
+ />
+ )}
+
+ )}
+
+ {Settings.SuppressBuyAugmentationConfirmation || (
+ setOpen(false)}
+ faction={props.parent.faction}
+ aug={aug}
+ />
+ )}
+ >
+
+ );
+}
diff --git a/src/Faction/ui/PurchaseAugmentationModal.tsx b/src/Augmentation/ui/PurchaseAugmentationModal.tsx
similarity index 64%
rename from src/Faction/ui/PurchaseAugmentationModal.tsx
rename to src/Augmentation/ui/PurchaseAugmentationModal.tsx
index 658978aaf..0a6c2e938 100644
--- a/src/Faction/ui/PurchaseAugmentationModal.tsx
+++ b/src/Augmentation/ui/PurchaseAugmentationModal.tsx
@@ -1,9 +1,9 @@
import React from "react";
-import { Augmentation } from "../../Augmentation/Augmentation";
-import { Faction } from "../Faction";
-import { purchaseAugmentation } from "../FactionHelpers";
-import { isRepeatableAug } from "../../Augmentation/AugmentationHelpers";
+import { Augmentation } from "../Augmentation";
+import { Faction } from "../../Faction/Faction";
+import { purchaseAugmentation } from "../../Faction/FactionHelpers";
+import { isRepeatableAug } from "../AugmentationHelpers";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
@@ -13,21 +13,23 @@ import Button from "@mui/material/Button";
interface IProps {
open: boolean;
onClose: () => void;
- faction: Faction;
- aug: Augmentation;
- rerender: () => void;
+ faction?: Faction;
+ aug?: Augmentation;
}
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
+ if (typeof props.aug === "undefined" || typeof props.faction === "undefined") {
+ return <>>;
+ }
+
const player = use.Player();
function buy(): void {
- if (!isRepeatableAug(props.aug) && player.hasAugmentation(props.aug)) {
+ if (!isRepeatableAug(props.aug as Augmentation) && player.hasAugmentation(props.aug as Augmentation)) {
return;
}
- purchaseAugmentation(props.aug, props.faction);
- props.rerender();
+ purchaseAugmentation(props.aug as Augmentation, props.faction as Faction);
props.onClose();
}
diff --git a/src/Constants.ts b/src/Constants.ts
index d52f9a0dc..64159ada9 100644
--- a/src/Constants.ts
+++ b/src/Constants.ts
@@ -120,7 +120,7 @@ export const CONSTANTS: {
LatestUpdate: string;
} = {
VersionString: "1.6.4",
- VersionNumber: 15,
+ VersionNumber: 16,
// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
diff --git a/src/Electron.tsx b/src/Electron.tsx
index ff31fa3c9..2c5cd35fe 100644
--- a/src/Electron.tsx
+++ b/src/Electron.tsx
@@ -11,11 +11,41 @@ import { exportScripts } from "./Terminal/commands/download";
import { CONSTANTS } from "./Constants";
import { hash } from "./hash/hash";
+interface IReturnWebStatus extends IReturnStatus {
+ data?: Record;
+}
+
+declare global {
+ interface Window {
+ appNotifier: {
+ terminal: (message: string, type?: string) => void;
+ toast: (message: string, type: ToastVariant, duration?: number) => void;
+ };
+ appSaveFns: {
+ triggerSave: () => Promise;
+ triggerGameExport: () => void;
+ triggerScriptsExport: () => void;
+ getSaveData: () => { save: string; fileName: string };
+ getSaveInfo: (base64save: string) => Promise;
+ pushSaveData: (base64save: string, automatic?: boolean) => void;
+ };
+ electronBridge: {
+ send: (channel: string, data?: unknown) => void;
+ receive: (channel: string, func: (...args: any[]) => void) => void;
+ };
+ }
+ interface Document {
+ getFiles: () => IReturnWebStatus;
+ deleteFile: (filename: string) => IReturnWebStatus;
+ saveFile: (filename: string, code: string) => IReturnWebStatus;
+ }
+}
+
export function initElectron(): void {
const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.indexOf(" electron/") > -1) {
// Electron-specific code
- (document as any).achievements = [];
+ document.achievements = [];
initWebserver();
initAppNotifier();
initSaveFunctions();
@@ -24,11 +54,6 @@ export function initElectron(): void {
}
function initWebserver(): void {
- interface IReturnWebStatus extends IReturnStatus {
- data?: {
- [propName: string]: any;
- };
- }
function normalizeFileName(filename: string): string {
filename = filename.replace(/\/\/+/g, "/");
filename = removeLeadingSlash(filename);
@@ -38,7 +63,7 @@ function initWebserver(): void {
return filename;
}
- (document as any).getFiles = function (): IReturnWebStatus {
+ document.getFiles = function (): IReturnWebStatus {
const home = GetServer("home");
if (home === null) {
return {
@@ -58,7 +83,7 @@ function initWebserver(): void {
};
};
- (document as any).deleteFile = function (filename: string): IReturnWebStatus {
+ document.deleteFile = function (filename: string): IReturnWebStatus {
filename = normalizeFileName(filename);
const home = GetServer("home");
if (home === null) {
@@ -70,7 +95,7 @@ function initWebserver(): void {
return home.removeFile(filename);
};
- (document as any).saveFile = function (filename: string, code: string): IReturnWebStatus {
+ document.saveFile = function (filename: string, code: string): IReturnWebStatus {
filename = normalizeFileName(filename);
code = Buffer.from(code, "base64").toString();
@@ -115,7 +140,7 @@ function initAppNotifier(): void {
};
// Will be consumud by the electron wrapper.
- (window as any).appNotifier = funcs;
+ window.appNotifier = funcs;
}
function initSaveFunctions(): void {
@@ -149,38 +174,38 @@ function initSaveFunctions(): void {
};
// Will be consumud by the electron wrapper.
- (window as any).appSaveFns = funcs;
+ window.appSaveFns = funcs;
}
function initElectronBridge(): void {
- const bridge = (window as any).electronBridge as any;
+ const bridge = window.electronBridge;
if (!bridge) return;
bridge.receive("get-save-data-request", () => {
- const data = (window as any).appSaveFns.getSaveData();
+ const data = window.appSaveFns.getSaveData();
bridge.send("get-save-data-response", data);
});
bridge.receive("get-save-info-request", async (save: string) => {
- const data = await (window as any).appSaveFns.getSaveInfo(save);
+ const data = await window.appSaveFns.getSaveInfo(save);
bridge.send("get-save-info-response", data);
});
bridge.receive("push-save-request", ({ save, automatic = false }: { save: string; automatic: boolean }) => {
- (window as any).appSaveFns.pushSaveData(save, automatic);
+ window.appSaveFns.pushSaveData(save, automatic);
});
bridge.receive("trigger-save", () => {
- return (window as any).appSaveFns
+ return window.appSaveFns
.triggerSave()
.then(() => {
bridge.send("save-completed");
})
- .catch((error: any) => {
+ .catch((error: unknown) => {
console.log(error);
SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000);
});
});
bridge.receive("trigger-game-export", () => {
try {
- (window as any).appSaveFns.triggerGameExport();
+ window.appSaveFns.triggerGameExport();
} catch (error) {
console.log(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
@@ -188,7 +213,7 @@ function initElectronBridge(): void {
});
bridge.receive("trigger-scripts-export", () => {
try {
- (window as any).appSaveFns.triggerScriptsExport();
+ window.appSaveFns.triggerScriptsExport();
} catch (error) {
console.log(error);
SnackbarEvents.emit("Could not export scripts.", ToastVariant.ERROR, 2000);
@@ -197,14 +222,14 @@ function initElectronBridge(): void {
}
export function pushGameSaved(data: SaveData): void {
- const bridge = (window as any).electronBridge as any;
+ const bridge = window.electronBridge;
if (!bridge) return;
bridge.send("push-game-saved", data);
}
export function pushGameReady(): void {
- const bridge = (window as any).electronBridge as any;
+ const bridge = window.electronBridge;
if (!bridge) return;
// Send basic information to the electron wrapper
@@ -222,7 +247,7 @@ export function pushGameReady(): void {
}
export function pushImportResult(wasImported: boolean): void {
- const bridge = (window as any).electronBridge as any;
+ const bridge = window.electronBridge;
if (!bridge) return;
bridge.send("push-import-result", { wasImported });
@@ -230,7 +255,7 @@ export function pushImportResult(wasImported: boolean): void {
}
export function pushDisableRestore(): void {
- const bridge = (window as any).electronBridge as any;
+ const bridge = window.electronBridge;
if (!bridge) return;
bridge.send("push-disable-restore", { duration: 1000 * 60 });
diff --git a/src/Faction/FactionHelpers.tsx b/src/Faction/FactionHelpers.tsx
index 84519d5af..73a6e569a 100644
--- a/src/Faction/FactionHelpers.tsx
+++ b/src/Faction/FactionHelpers.tsx
@@ -54,36 +54,15 @@ export function joinFaction(faction: Faction): void {
//Returns a boolean indicating whether the player has the prerequisites for the
//specified Augmentation
export function hasAugmentationPrereqs(aug: Augmentation): boolean {
- let hasPrereqs = true;
- if (aug.prereqs && aug.prereqs.length > 0) {
- for (let i = 0; i < aug.prereqs.length; ++i) {
- const prereqAug = Augmentations[aug.prereqs[i]];
- if (prereqAug == null) {
- console.error(`Invalid prereq Augmentation ${aug.prereqs[i]}`);
- continue;
- }
-
- if (Player.hasAugmentation(prereqAug, true) === false) {
- hasPrereqs = false;
-
- // Check if the aug is purchased
- for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
- if (Player.queuedAugmentations[j].name === prereqAug.name) {
- hasPrereqs = true;
- break;
- }
- }
- }
- }
- }
-
- return hasPrereqs;
+ return aug.prereqs.every((aug) => Player.hasAugmentation(aug));
}
export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = false): string {
const hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
- const txt = `You must first purchase or install ${aug.prereqs.join(",")} before you can purchase this one.`;
+ const txt = `You must first purchase or install ${aug.prereqs
+ .filter((req) => !Player.hasAugmentation(req))
+ .join(",")} before you can purchase this one.`;
if (sing) {
return txt;
} else {
@@ -165,13 +144,11 @@ export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Factio
let augs = Object.values(Augmentations);
// Remove special augs
- augs = augs.filter((a) => !a.isSpecial);
+ augs = augs.filter((a) => !a.isSpecial || a.name != AugmentationNames.CongruityImplant);
- const blacklist: string[] = [AugmentationNames.NeuroFluxGovernor, AugmentationNames.CongruityImplant];
-
- if (player.bitNodeN !== 2) {
+ if (player.bitNodeN === 2) {
// TRP is not available outside of BN2 for Gangs
- blacklist.push(AugmentationNames.TheRedPill);
+ augs.push(Augmentations[AugmentationNames.TheRedPill]);
}
const rng = SFC32RNG(`BN${player.bitNodeN}.${player.sourceFileLvl(player.bitNodeN)}`);
@@ -190,9 +167,6 @@ export const getFactionAugmentationsFiltered = (player: IPlayer, faction: Factio
};
augs = augs.filter(uniqueFilter);
- // Remove blacklisted augs
- augs = augs.filter((a) => !blacklist.includes(a.name));
-
return augs.map((a) => a.name);
}
diff --git a/src/Faction/ui/AugmentationsPage.tsx b/src/Faction/ui/AugmentationsPage.tsx
index f9f3664b2..c169520fd 100644
--- a/src/Faction/ui/AugmentationsPage.tsx
+++ b/src/Faction/ui/AugmentationsPage.tsx
@@ -1,30 +1,21 @@
/**
* Root React Component for displaying a faction's "Purchase Augmentations" page
*/
+import { Box, Button, Tooltip, Typography, Paper, Container } from "@mui/material";
import React, { useState } from "react";
-
-import { PurchaseableAugmentation } from "./PurchaseableAugmentation";
-
+import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
-import { Faction } from "../Faction";
+import { PurchasableAugmentations } from "../../Augmentation/ui/PurchasableAugmentations";
import { PurchaseAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import { Settings } from "../../Settings/Settings";
-import { hasAugmentationPrereqs, getFactionAugmentationsFiltered } from "../FactionHelpers";
-
import { use } from "../../ui/Context";
-import { Reputation } from "../../ui/React/Reputation";
-import { Favor } from "../../ui/React/Favor";
import { numeralWrapper } from "../../ui/numeralFormat";
-
-import Box from "@mui/material/Box";
-import Button from "@mui/material/Button";
-import Typography from "@mui/material/Typography";
-import Tooltip from "@mui/material/Tooltip";
-import TableBody from "@mui/material/TableBody";
-import Table from "@mui/material/Table";
-import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers";
+import { Favor } from "../../ui/React/Favor";
+import { Reputation } from "../../ui/React/Reputation";
import { FactionNames } from "../data/FactionNames";
+import { Faction } from "../Faction";
+import { getFactionAugmentationsFiltered, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
type IProps = {
faction: Faction;
@@ -137,38 +128,12 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
aug === AugmentationNames.NeuroFluxGovernor ||
(!player.augmentations.some((a) => a.name === aug) && !player.queuedAugmentations.some((a) => a.name === aug)),
);
-
- const purchaseableAugmentation = (aug: string, owned = false): React.ReactNode => {
- return (
-
- );
- };
-
- const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
-
- let ownedElem = <>>;
const owned = augs.filter((aug: string) => !purchasable.includes(aug));
- if (owned.length !== 0) {
- ownedElem = (
- <>
-
- Purchased Augmentations
- This faction also offers these augmentations but you already own them.
- {owned.map((aug) => purchaseableAugmentation(aug, true))}
- >
- );
- }
+
const multiplierComponent =
props.faction.name !== FactionNames.ShadowsOfAnarchy ? (
- Price multiplier: x {numeralWrapper.formatMultiplier(getGenericAugmentationPriceMultiplier())}
+ Price multiplier: x {numeralWrapper.formatReallyBigNumber(getGenericAugmentationPriceMultiplier())}
) : (
<>>
@@ -176,42 +141,77 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
return (
<>
-
- Faction Augmentations
-
- These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are
- powerful upgrades that will enhance your abilities.
-
- Reputation: Favor:{" "}
-
-
-
-
+
+ Faction Augmentations
+
+
+ These are all of the Augmentations that are available to purchase from {props.faction.name}.
+ Augmentations are powerful upgrades that will enhance your abilities.
+
+
+
+
+ The price of every Augmentation increases for every queued Augmentation and it is reset when you
+ install them.
+
+ }
+ >
+ {multiplierComponent}
+
- The price of every Augmentation increases for every queued Augmentation and it is reset when you install
- them.
+ Reputation:
+
+ Favor:
+
+
+
+
+
+
+
+
+
+
+
+ {
+ return (
+ hasAugmentationPrereqs(aug) &&
+ props.faction.playerReputation >= aug.baseRepRequirement &&
+ (aug.baseCost === 0 || player.money > aug.baseCost)
+ );
+ }}
+ purchaseAugmentation={(player, aug, showModal) => {
+ if (!Settings.SuppressBuyAugmentationConfirmation) {
+ showModal(true);
+ } else {
+ purchaseAugmentation(aug, props.faction);
+ rerender();
}
- >
- {multiplierComponent}
-
-
-
-
-
-
-
-
-
-
-
+ }}
+ rep={props.faction.playerReputation}
+ faction={props.faction}
+ />
>
);
}
diff --git a/src/Faction/ui/PurchaseableAugmentation.tsx b/src/Faction/ui/PurchaseableAugmentation.tsx
deleted file mode 100644
index feebb29e0..000000000
--- a/src/Faction/ui/PurchaseableAugmentation.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * React component for displaying a single augmentation for purchase through
- * the faction UI
- */
-import React, { useState } from "react";
-
-import { hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
-import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
-
-import { Augmentations } from "../../Augmentation/Augmentations";
-import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
-import { Faction } from "../Faction";
-import { IPlayer } from "../../PersonObjects/IPlayer";
-import { Settings } from "../../Settings/Settings";
-import { Money } from "../../ui/React/Money";
-import { Reputation } from "../../ui/React/Reputation";
-
-import { Augmentation as AugFormat } from "../../ui/React/Augmentation";
-import Button from "@mui/material/Button";
-import Typography from "@mui/material/Typography";
-import Tooltip from "@mui/material/Tooltip";
-import Box from "@mui/material/Box";
-import { TableCell } from "../../ui/React/Table";
-import TableRow from "@mui/material/TableRow";
-import { getNextNeuroFluxLevel } from "../../Augmentation/AugmentationHelpers";
-
-interface IReqProps {
- augName: string;
- p: IPlayer;
- hasReq: boolean;
- rep: number;
- hasRep: boolean;
- cost: number;
- hasCost: boolean;
-}
-
-function Requirements(props: IReqProps): React.ReactElement {
- const aug = Augmentations[props.augName];
- if (!props.hasReq) {
- return (
-
-
- Requires{" "}
- {aug.prereqs.map((aug, i) => (
-
- ))}
-
-
- );
- }
-
- return (
-
-
-
-
-
-
-
-
- Requires faction reputation
-
-
-
- );
-}
-
-interface IProps {
- augName: string;
- faction: Faction;
- p: IPlayer;
- rerender: () => void;
- owned?: boolean;
-}
-
-export function PurchaseableAugmentation(props: IProps): React.ReactElement {
- const [open, setOpen] = useState(false);
- const aug = Augmentations[props.augName];
- if (aug == null) throw new Error(`aug ${props.augName} does not exists`);
-
- if (aug == null) {
- console.error(
- `Invalid Augmentation when trying to create PurchaseableAugmentation display element: ${props.augName}`,
- );
- return <>>;
- }
-
- const moneyCost = aug.baseCost;
- const repCost = aug.baseRepRequirement;
- const hasReq = hasAugmentationPrereqs(aug);
- const hasRep = props.faction.playerReputation >= repCost;
- const hasCost = aug.baseCost === 0 || props.p.money > aug.baseCost;
-
- // Determine UI properties
- const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";
-
- // Determine button txt
- let btnTxt = aug.name;
- if (aug.name === AugmentationNames.NeuroFluxGovernor) {
- btnTxt += ` - Level ${getNextNeuroFluxLevel()}`;
- }
-
- let tooltip = <>>;
- if (typeof aug.info === "string") {
- tooltip = (
- <>
- {aug.info}
-
-
- {aug.stats}
- >
- );
- } else
- tooltip = (
- <>
- {aug.info}
-
-
- {aug.stats}
- >
- );
-
- function handleClick(): void {
- if (color === "error") return;
- if (!Settings.SuppressBuyAugmentationConfirmation) {
- setOpen(true);
- } else {
- purchaseAugmentation(aug, props.faction);
- props.rerender();
- }
- }
-
- return (
-
- {!props.owned && (
-
-
- setOpen(false)}
- aug={aug}
- faction={props.faction}
- rerender={props.rerender}
- />
-
- )}
-
-
- {tooltip}} placement="top">
- {btnTxt}
-
-
-
- {!props.owned && (
-
- )}
-
- );
-}
diff --git a/src/Infiltration/formulas/victory.ts b/src/Infiltration/formulas/victory.ts
index 184cf15f9..2c781bef8 100644
--- a/src/Infiltration/formulas/victory.ts
+++ b/src/Infiltration/formulas/victory.ts
@@ -31,9 +31,9 @@ export function calculateTradeInformationRepReward(
const levelBonus = maxLevel * Math.pow(1.01, maxLevel);
return (
- Math.pow(reward + 1, 2) *
- Math.pow(difficulty, 3) *
- 3e3 *
+ Math.pow(reward + 1, 1.1) *
+ Math.pow(difficulty, 1.2) *
+ 30 *
levelBonus *
(player.hasAugmentation(AugmentationNames.WKSharmonizer, true) ? 1.5 : 1) *
BitNodeMultipliers.InfiltrationMoney
diff --git a/src/PersonObjects/Grafting/GraftingHelpers.ts b/src/PersonObjects/Grafting/GraftingHelpers.ts
index edf5ca798..62d3b72f4 100644
--- a/src/PersonObjects/Grafting/GraftingHelpers.ts
+++ b/src/PersonObjects/Grafting/GraftingHelpers.ts
@@ -1,5 +1,4 @@
import { Augmentations } from "../../Augmentation/Augmentations";
-import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { GraftableAugmentation } from "./GraftableAugmentation";
import { IPlayer } from "../IPlayer";
@@ -7,8 +6,7 @@ export const getGraftingAvailableAugs = (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 (aug.isSpecial) continue;
augs.push(augName);
}
diff --git a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx
index adc741589..3e7ddfd28 100644
--- a/src/PersonObjects/Grafting/ui/GraftingRoot.tsx
+++ b/src/PersonObjects/Grafting/ui/GraftingRoot.tsx
@@ -139,34 +139,37 @@ export const GraftingRoot = (): React.ReactElement => {
>
}
/>
-
- Time to Graft:{" "}
- {convertTimeMsToTimeElapsedString(
- calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
+
+
+ Time to Graft:{" "}
+ {convertTimeMsToTimeElapsedString(
+ calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
+ )}
+ {/* Use formula so the displayed creation time is accurate to player bonus */}
+
+
+ {Augmentations[selectedAug].prereqs.length > 0 && (
+
)}
- {/* Use formula so the displayed creation time is accurate to player bonus */}
-
- {Augmentations[selectedAug].prereqs.length > 0 && (
-
- )}
+
-
-
- {(() => {
- const aug = Augmentations[selectedAug];
+
+ {(() => {
+ const aug = Augmentations[selectedAug];
- const info = typeof aug.info === "string" ? {aug.info} : aug.info;
- const tooltip = (
- <>
- {info}
-
-
- {aug.stats}
- >
- );
- return tooltip;
- })()}
-
+ const info = typeof aug.info === "string" ? {aug.info} : aug.info;
+ const tooltip = (
+ <>
+ {info}
+
+
+ {aug.stats}
+ >
+ );
+ return tooltip;
+ })()}
+
+
) : (
diff --git a/src/PersonObjects/Sleeve/SleeveHelpers.ts b/src/PersonObjects/Sleeve/SleeveHelpers.ts
index 76bde04e8..324cd9aa6 100644
--- a/src/PersonObjects/Sleeve/SleeveHelpers.ts
+++ b/src/PersonObjects/Sleeve/SleeveHelpers.ts
@@ -5,7 +5,6 @@ import { IPlayer } from "../IPlayer";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Augmentations } from "../../Augmentation/Augmentations";
-import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Faction } from "../../Faction/Faction";
import { Factions } from "../../Faction/Factions";
@@ -22,9 +21,6 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat
// Helper function that helps filter out augs that are already owned
// and augs that aren't allowed for sleeves
function isAvailableForSleeve(aug: Augmentation): boolean {
- if (aug.name === AugmentationNames.NeuroFluxGovernor) {
- return false;
- }
if (ownedAugNames.includes(aug.name)) {
return false;
}
diff --git a/src/PersonObjects/Sleeve/ui/SleeveAugmentationsModal.tsx b/src/PersonObjects/Sleeve/ui/SleeveAugmentationsModal.tsx
index e17118e8c..71eaf08a4 100644
--- a/src/PersonObjects/Sleeve/ui/SleeveAugmentationsModal.tsx
+++ b/src/PersonObjects/Sleeve/ui/SleeveAugmentationsModal.tsx
@@ -1,20 +1,10 @@
-import React, { useState, useEffect } from "react";
+import { Container, Typography, Paper } from "@mui/material";
+import React, { useEffect, useState } from "react";
+import { PurchasableAugmentations } from "../../../Augmentation/ui/PurchasableAugmentations";
+import { use } from "../../../ui/Context";
+import { Modal } from "../../../ui/React/Modal";
import { Sleeve } from "../Sleeve";
import { findSleevePurchasableAugs } from "../SleeveHelpers";
-import { Augmentations } from "../../../Augmentation/Augmentations";
-import { Augmentation } from "../../../Augmentation/Augmentation";
-import { Money } from "../../../ui/React/Money";
-import { Modal } from "../../../ui/React/Modal";
-import { use } from "../../../ui/Context";
-import Typography from "@mui/material/Typography";
-import Tooltip from "@mui/material/Tooltip";
-import Paper from "@mui/material/Paper";
-import Box from "@mui/material/Box";
-import Button from "@mui/material/Button";
-import TableBody from "@mui/material/TableBody";
-import Table from "@mui/material/Table";
-import { TableCell } from "../../../ui/React/Table";
-import TableRow from "@mui/material/TableRow";
interface IProps {
open: boolean;
@@ -42,80 +32,34 @@ export function SleeveAugmentationsModal(props: IProps): React.ReactElement {
// and you must also have enough rep in that faction in order to purchase it.
const availableAugs = findSleevePurchasableAugs(props.sleeve, player);
- function purchaseAugmentation(aug: Augmentation): void {
- props.sleeve.tryBuyAugmentation(player, aug);
- rerender();
- }
-
return (
- <>
-
-
- 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.
-
-
-
-
- {availableAugs.map((aug) => {
- return (
-
-
-
-
-
-
-
- {aug.name}
-
-
-
-
-
-
-
- );
- })}
-
-
-
-
-
- {ownedAugNames.length > 0 && (
- <>
- Owned Augmentations:
-
- {ownedAugNames.map((augName) => {
- const aug = Augmentations[augName];
- const info = typeof aug.info === "string" ? {aug.info} : aug.info;
- const tooltip = (
- <>
- {info}
-
-
- {aug.stats}
- >
- );
-
- return (
- {tooltip}}>
-
- {augName}
-
-
- );
- })}
-
- >
- )}
- >
+
+
+ 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.
+
+
+ Augmentations will appear below as they become available.
+
+
+ aug.name)}
+ ownedAugNames={ownedAugNames}
+ player={player}
+ canPurchase={(player, aug) => {
+ return player.money > aug.startingCost;
+ }}
+ purchaseAugmentation={(player, aug, _showModal) => {
+ props.sleeve.tryBuyAugmentation(player, aug);
+ rerender();
+ }}
+ sleeveAugs
+ />
);
}
diff --git a/src/SaveObject.tsx b/src/SaveObject.tsx
index dafaca6aa..5367ad25c 100755
--- a/src/SaveObject.tsx
+++ b/src/SaveObject.tsx
@@ -400,8 +400,19 @@ function evaluateVersionCompatibility(ver: string | number): void {
if (ver < 15) {
(Settings as any).EditorTheme = { ...defaultMonacoTheme };
}
+ //Fix contract names
if (ver < 16) {
Factions[FactionNames.ShadowsOfAnarchy] = new Faction(FactionNames.ShadowsOfAnarchy);
+ //Iterate over all contracts on all servers
+ for (const server of GetAllServers()) {
+ for (const contract of server.contracts) {
+ //Rename old "HammingCodes: Integer to encoded Binary" contracts
+ //to "HammingCodes: Integer to Encoded Binary"
+ if (contract.type == "HammingCodes: Integer to encoded Binary") {
+ contract.type = "HammingCodes: Integer to Encoded Binary";
+ }
+ }
+ }
}
}
}
diff --git a/src/data/codingcontracttypes.ts b/src/data/codingcontracttypes.ts
index f9c11f4a2..a28f8d2d3 100644
--- a/src/data/codingcontracttypes.ts
+++ b/src/data/codingcontracttypes.ts
@@ -1250,7 +1250,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
},
},
{
- name: "HammingCodes: Integer to encoded Binary",
+ name: "HammingCodes: Integer to Encoded Binary",
numTries: 10,
difficulty: 5,
desc: (n: number): string => {