mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-11 10:13:52 +01:00
Merge branch 'danielyxie:dev' into dev
This commit is contained in:
commit
2f320d00a2
7
src/@types/global.d.ts
vendored
7
src/@types/global.d.ts
vendored
@ -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[];
|
||||
}
|
||||
|
@ -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)];
|
||||
}
|
||||
|
@ -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,
|
||||
|
264
src/Augmentation/ui/PurchasableAugmentations.tsx
Normal file
264
src/Augmentation/ui/PurchasableAugmentations.tsx
Normal file
@ -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 (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
This Augmentation has the following pre-requisite(s):
|
||||
</Typography>
|
||||
{props.aug.prereqs.map((preAug) => (
|
||||
<Requirement
|
||||
fulfilled={props.player.hasAugmentation(preAug)}
|
||||
value={preAug}
|
||||
color={Settings.theme.money}
|
||||
key={preAug}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
color: hasPreReqs ? Settings.theme.successlight : Settings.theme.error,
|
||||
}}
|
||||
>
|
||||
{hasPreReqs ? (
|
||||
<>
|
||||
<CheckCircle fontSize="small" sx={{ mr: 1 }} />
|
||||
Pre-requisites Owned
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Report fontSize="small" sx={{ mr: 1 }} />
|
||||
Missing {props.aug.prereqs.length - ownedPreReqs.length} pre-requisite(s)
|
||||
</>
|
||||
)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface IExclusiveProps {
|
||||
player: IPlayer;
|
||||
aug: Augmentation;
|
||||
}
|
||||
|
||||
const Exclusive = (props: IExclusiveProps): React.ReactElement => {
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
This Augmentation can only be acquired from the following source(s):
|
||||
</Typography>
|
||||
<ul>
|
||||
<Typography sx={{ color: Settings.theme.money }}>
|
||||
<li>
|
||||
<b>{props.aug.factions[0]}</b> faction
|
||||
</li>
|
||||
{props.player.canAccessGang() && !props.aug.isSpecial && (
|
||||
<li>
|
||||
Certain <b>gangs</b>
|
||||
</li>
|
||||
)}
|
||||
{props.player.canAccessGrafting() &&
|
||||
!props.aug.isSpecial &&
|
||||
props.aug.name !== AugmentationNames.TheRedPill && (
|
||||
<li>
|
||||
<b>Grafting</b>
|
||||
</li>
|
||||
)}
|
||||
</Typography>
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<NewReleases sx={{ ml: 1, color: Settings.theme.money, transform: "rotate(180deg)" }} />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface IReqProps {
|
||||
value: string;
|
||||
color: string;
|
||||
fulfilled: boolean;
|
||||
}
|
||||
|
||||
const Requirement = (props: IReqProps): React.ReactElement => {
|
||||
return (
|
||||
<Typography sx={{ display: "flex", alignItems: "center", color: props.color }}>
|
||||
{props.fulfilled ? <CheckBox sx={{ mr: 1 }} /> : <CheckBoxOutlineBlank sx={{ mr: 1 }} />}
|
||||
{props.value}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<Container
|
||||
maxWidth="lg"
|
||||
disableGutters
|
||||
sx={{ mx: 0, display: "grid", gridTemplateColumns: "repeat(1, 1fr)", gap: 1 }}
|
||||
>
|
||||
{props.augNames.map((augName: string) => (
|
||||
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={false} />
|
||||
))}
|
||||
{props.ownedAugNames.map((augName: string) => (
|
||||
<PurchasableAugmentation key={augName} parent={props} augName={augName} owned={true} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
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" ? <span>{aug.info}</span> : aug.info;
|
||||
const description = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
p: 1,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "minmax(0, 4fr) 1fr",
|
||||
gap: 1,
|
||||
opacity: props.owned ? 0.75 : 1,
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
props.parent.purchaseAugmentation(props.parent.player, aug, (open): void => {
|
||||
setOpen(open);
|
||||
})
|
||||
}
|
||||
disabled={!props.parent.canPurchase(props.parent.player, aug) || props.owned}
|
||||
sx={{ width: "48px", height: "48px", float: "left", clear: "none", mr: 1 }}
|
||||
>
|
||||
{props.owned ? "Owned" : "Buy"}
|
||||
</Button>
|
||||
|
||||
<Box sx={{ maxWidth: props.owned ? "100%" : "85%" }}>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography variant="h5">
|
||||
{props.augName}
|
||||
{props.augName === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
|
||||
</Typography>
|
||||
<Typography>{description}</Typography>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Info sx={{ mr: 1 }} color="info" />
|
||||
</Tooltip>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{aug.name}
|
||||
{aug.name === AugmentationNames.NeuroFluxGovernor && ` - Level ${getNextNeuroFluxLevel()}`}
|
||||
</Typography>
|
||||
{aug.factions.length === 1 && !props.parent.sleeveAugs && (
|
||||
<Exclusive player={props.parent.player} aug={aug} />
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{aug.prereqs.length > 0 && !props.parent.sleeveAugs && <PreReqs player={props.parent.player} aug={aug} />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{props.owned || (
|
||||
<Box sx={{ display: "grid", alignItems: "center", justifyItems: "left" }}>
|
||||
<Requirement
|
||||
fulfilled={aug.baseCost === 0 || props.parent.player.money > cost}
|
||||
value={numeralWrapper.formatMoney(cost)}
|
||||
color={Settings.theme.money}
|
||||
/>
|
||||
{props.parent.rep !== undefined && (
|
||||
<Requirement
|
||||
fulfilled={props.parent.rep >= aug.baseRepRequirement}
|
||||
value={`${numeralWrapper.formatReputation(aug.baseRepRequirement)} rep`}
|
||||
color={Settings.theme.rep}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{Settings.SuppressBuyAugmentationConfirmation || (
|
||||
<PurchaseAugmentationModal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
faction={props.parent.faction}
|
||||
aug={aug}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</Paper>
|
||||
);
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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<string, unknown>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
appNotifier: {
|
||||
terminal: (message: string, type?: string) => void;
|
||||
toast: (message: string, type: ToastVariant, duration?: number) => void;
|
||||
};
|
||||
appSaveFns: {
|
||||
triggerSave: () => Promise<void>;
|
||||
triggerGameExport: () => void;
|
||||
triggerScriptsExport: () => void;
|
||||
getSaveData: () => { save: string; fileName: string };
|
||||
getSaveInfo: (base64save: string) => Promise<ImportPlayerData | undefined>;
|
||||
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 });
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 (
|
||||
<PurchaseableAugmentation
|
||||
augName={aug}
|
||||
faction={props.faction}
|
||||
key={aug}
|
||||
p={player}
|
||||
rerender={rerender}
|
||||
owned={owned}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
|
||||
|
||||
let ownedElem = <></>;
|
||||
const owned = augs.filter((aug: string) => !purchasable.includes(aug));
|
||||
if (owned.length !== 0) {
|
||||
ownedElem = (
|
||||
<>
|
||||
<br />
|
||||
<Typography variant="h4">Purchased Augmentations</Typography>
|
||||
<Typography>This faction also offers these augmentations but you already own them.</Typography>
|
||||
{owned.map((aug) => purchaseableAugmentation(aug, true))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const multiplierComponent =
|
||||
props.faction.name !== FactionNames.ShadowsOfAnarchy ? (
|
||||
<Typography>
|
||||
Price multiplier: x {numeralWrapper.formatMultiplier(getGenericAugmentationPriceMultiplier())}
|
||||
<b>Price multiplier:</b> x {numeralWrapper.formatReallyBigNumber(getGenericAugmentationPriceMultiplier())}
|
||||
</Typography>
|
||||
) : (
|
||||
<></>
|
||||
@ -176,42 +141,77 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={props.routeToMainPage}>Back</Button>
|
||||
<Typography variant="h4">Faction Augmentations</Typography>
|
||||
<Typography>
|
||||
These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are
|
||||
powerful upgrades that will enhance your abilities.
|
||||
<br />
|
||||
Reputation: <Reputation reputation={props.faction.playerReputation} /> Favor:{" "}
|
||||
<Favor favor={Math.floor(props.faction.favor)} />
|
||||
</Typography>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Container disableGutters maxWidth="lg" sx={{ mx: 0 }}>
|
||||
<Button onClick={props.routeToMainPage}>Back</Button>
|
||||
<Typography variant="h4">Faction Augmentations</Typography>
|
||||
<Paper sx={{ p: 1, mb: 1 }}>
|
||||
<Typography>
|
||||
These are all of the Augmentations that are available to purchase from <b>{props.faction.name}</b>.
|
||||
Augmentations are powerful upgrades that will enhance your abilities.
|
||||
<br />
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${props.faction.name === FactionNames.ShadowsOfAnarchy ? "2" : "3"}, 1fr)`,
|
||||
justifyItems: "center",
|
||||
my: 1,
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The price of every Augmentation increases for every queued Augmentation and it is reset when you
|
||||
install them.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
{multiplierComponent}
|
||||
</Tooltip>
|
||||
<Typography>
|
||||
The price of every Augmentation increases for every queued Augmentation and it is reset when you install
|
||||
them.
|
||||
<b>Reputation:</b> <Reputation reputation={props.faction.playerReputation} />
|
||||
</Typography>
|
||||
<Typography>
|
||||
<b>Favor:</b> <Favor favor={Math.floor(props.faction.favor)} />
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)" }}>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>
|
||||
Sort by Reputation
|
||||
</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>
|
||||
Sort by Default Order
|
||||
</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Purchasable)}>
|
||||
Sort by Purchasable
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
|
||||
<PurchasableAugmentations
|
||||
augNames={purchasable}
|
||||
ownedAugNames={owned}
|
||||
player={player}
|
||||
canPurchase={(player, aug) => {
|
||||
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}
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>Sort by Reputation</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>Sort by Default Order</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Purchasable)}>
|
||||
Sort by Purchasable
|
||||
</Button>
|
||||
<br />
|
||||
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>{augListElems}</TableBody>
|
||||
</Table>
|
||||
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>{ownedElem}</TableBody>
|
||||
</Table>
|
||||
}}
|
||||
rep={props.faction.playerReputation}
|
||||
faction={props.faction}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -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 (
|
||||
<TableCell key={1} colSpan={2}>
|
||||
<Typography color="error">
|
||||
Requires{" "}
|
||||
{aug.prereqs.map((aug, i) => (
|
||||
<AugFormat key={i} name={aug} />
|
||||
))}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment key="f">
|
||||
<TableCell key={1}>
|
||||
<Typography>
|
||||
<Money money={props.cost} player={props.p} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell key={2}>
|
||||
<Typography color={props.hasRep ? "primary" : "error"}>
|
||||
Requires <Reputation reputation={props.rep} /> faction reputation
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
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 = (
|
||||
<>
|
||||
<span>{aug.info}</span>
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
} else
|
||||
tooltip = (
|
||||
<>
|
||||
{aug.info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
|
||||
function handleClick(): void {
|
||||
if (color === "error") return;
|
||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||
setOpen(true);
|
||||
} else {
|
||||
purchaseAugmentation(aug, props.faction);
|
||||
props.rerender();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
{!props.owned && (
|
||||
<TableCell key={0}>
|
||||
<Button onClick={handleClick} color={color}>
|
||||
Buy
|
||||
</Button>
|
||||
<PurchaseAugmentationModal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aug={aug}
|
||||
faction={props.faction}
|
||||
rerender={props.rerender}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell key={1}>
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography>{tooltip}</Typography>} placement="top">
|
||||
<Typography>{btnTxt}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</TableCell>
|
||||
{!props.owned && (
|
||||
<Requirements
|
||||
key={2}
|
||||
augName={props.augName}
|
||||
p={props.p}
|
||||
cost={moneyCost}
|
||||
rep={repCost}
|
||||
hasReq={hasReq}
|
||||
hasRep={hasRep}
|
||||
hasCost={hasCost}
|
||||
/>
|
||||
)}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -139,34 +139,37 @@ export const GraftingRoot = (): React.ReactElement => {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Typography color={Settings.theme.info}>
|
||||
<b>Time to Graft:</b>{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
|
||||
<Box sx={{ maxHeight: 330, overflowY: "scroll" }}>
|
||||
<Typography color={Settings.theme.info}>
|
||||
<b>Time to Graft:</b>{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
calculateGraftingTimeWithBonus(player, GraftableAugmentations[selectedAug]),
|
||||
)}
|
||||
{/* Use formula so the displayed creation time is accurate to player bonus */}
|
||||
</Typography>
|
||||
|
||||
{Augmentations[selectedAug].prereqs.length > 0 && (
|
||||
<AugPreReqsChecklist player={player} aug={Augmentations[selectedAug]} />
|
||||
)}
|
||||
{/* Use formula so the displayed creation time is accurate to player bonus */}
|
||||
</Typography>
|
||||
{Augmentations[selectedAug].prereqs.length > 0 && (
|
||||
<AugPreReqsChecklist player={player} aug={Augmentations[selectedAug]} />
|
||||
)}
|
||||
<br />
|
||||
|
||||
<br />
|
||||
<Typography sx={{ maxHeight: 305, overflowY: "scroll" }}>
|
||||
{(() => {
|
||||
const aug = Augmentations[selectedAug];
|
||||
<Typography>
|
||||
{(() => {
|
||||
const aug = Augmentations[selectedAug];
|
||||
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const tooltip = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
return tooltip;
|
||||
})()}
|
||||
</Typography>
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const tooltip = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
return tooltip;
|
||||
})()}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
) : (
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<>
|
||||
<Box sx={{ mx: 1 }}>
|
||||
<Typography>
|
||||
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.
|
||||
<br />
|
||||
<br />
|
||||
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.
|
||||
</Typography>
|
||||
<Box component={Paper} sx={{ my: 1, p: 1 }}>
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>
|
||||
{availableAugs.map((aug) => {
|
||||
return (
|
||||
<TableRow key={aug.name}>
|
||||
<TableCell>
|
||||
<Button onClick={() => purchaseAugmentation(aug)} disabled={player.money < aug.startingCost}>
|
||||
Buy
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex">
|
||||
<Tooltip title={aug.stats || ""}>
|
||||
<Typography>{aug.name}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Money money={aug.startingCost} player={player} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{ownedAugNames.length > 0 && (
|
||||
<>
|
||||
<Typography sx={{ mx: 1 }}>Owned Augmentations:</Typography>
|
||||
<Box display="grid" sx={{ gridTemplateColumns: "repeat(5, 1fr)", m: 1 }}>
|
||||
{ownedAugNames.map((augName) => {
|
||||
const aug = Augmentations[augName];
|
||||
const info = typeof aug.info === "string" ? <span>{aug.info}</span> : aug.info;
|
||||
const tooltip = (
|
||||
<>
|
||||
{info}
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip key={augName} title={<Typography>{tooltip}</Typography>}>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Typography>{augName}</Typography>
|
||||
</Paper>
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
<Container component={Paper} disableGutters maxWidth="lg" sx={{ mx: 0, mb: 1, p: 1 }}>
|
||||
<Typography>
|
||||
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.
|
||||
<br />
|
||||
<br />
|
||||
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.
|
||||
<br />
|
||||
<br />
|
||||
Augmentations will appear below as they become available.
|
||||
</Typography>
|
||||
</Container>
|
||||
<PurchasableAugmentations
|
||||
augNames={availableAugs.map((aug) => 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
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 => {
|
||||
|
Loading…
Reference in New Issue
Block a user