diff --git a/src/Achievements/Achievements.ts b/src/Achievements/Achievements.ts index 2920125db..c8b56065b 100644 --- a/src/Achievements/Achievements.ts +++ b/src/Achievements/Achievements.ts @@ -776,6 +776,7 @@ export const achievements: IMap = { // { ID: FactionNames.Bladeburners.toUpperCase(), Condition: () => Player.factions.includes(FactionNames.Bladeburners) }, // { ID: "DEEPSCANV1.EXE", Condition: () => Player.getHomeComputer().programs.includes(Programs.DeepscanV1.name) }, // { ID: "DEEPSCANV2.EXE", Condition: () => Player.getHomeComputer().programs.includes(Programs.DeepscanV2.name) }, +// { ID: "INFILTRATORS", Condition: () => Player.factions.includes(FactionNames.Infiltrators) }, // { // ID: "SERVERPROFILER.EXE", // Condition: () => Player.getHomeComputer().programs.includes(Programs.ServerProfiler.name), diff --git a/src/Augmentation/Augmentation.tsx b/src/Augmentation/Augmentation.tsx index 88cbdc6f3..a7174c8ca 100644 --- a/src/Augmentation/Augmentation.tsx +++ b/src/Augmentation/Augmentation.tsx @@ -8,6 +8,7 @@ import { numeralWrapper } from "../ui/numeralFormat"; import { Money } from "../ui/React/Money"; import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; +import { FactionNames } from "../Faction/data/FactionNames"; export interface IConstructorParams { info: string | JSX.Element; @@ -49,6 +50,12 @@ export interface IConstructorParams { bladeburner_stamina_gain_mult?: number; bladeburner_analysis_mult?: number; bladeburner_success_chance_mult?: number; + infiltration_base_rep_increase?: number; + infiltration_rep_mult?: number; + infiltration_trade_mult?: number; + infiltration_sell_mult?: number; + infiltration_timer_mult?: number; + infiltration_damage_reduction_mult?: number; startingMoney?: number; programs?: string[]; @@ -337,6 +344,50 @@ function generateStatsDescription(mults: IMap, programs?: string[], star
+{f(mults.bladeburner_success_chance_mult - 1)} Bladeburner Contracts and Operations success chance ); + if (mults.infiltration_base_rep_increase) + desc = ( + <> + {desc} +
+{f(mults.infiltration_base_rep_increase - 1)} Infiltration {FactionNames.ShadowsOfAnarchy} Reputation + base reward + + ); + if (mults.infiltration_rep_mult) + desc = ( + <> + {desc} +
+{f(mults.infiltration_rep_mult - 1)} Infiltration {FactionNames.ShadowsOfAnarchy} Reputation reward + + ); + if (mults.infiltration_trade_mult) + desc = ( + <> + {desc} +
+{f(mults.infiltration_trade_mult - 1)} Infiltration Reputation for trading information + + ); + if (mults.infiltration_sell_mult) + desc = ( + <> + {desc} +
+{f(mults.infiltration_sell_mult - 1)} Infiltration cash reward for selling information + + ); + if (mults.infiltration_timer_mult) + desc = ( + <> + {desc} +
+{f(mults.infiltration_timer_mult - 1)} Infiltration time per minigame + + ); + if (mults.infiltration_damage_reduction_mult) + desc = ( + <> + {desc} +
+ {f(mults.infiltration_damage_reduction_mult - 1)} Infiltration health lost per failed minigame + + ); if (startingMoney) desc = ( @@ -390,6 +441,9 @@ export class Augmentation { // Initial cost. Doesn't change when you purchase multiple Augmentation startingCost = 0; + // Initial rep requirement. Doesn't change when you purchase multiple Augmentation + startingRepRequirement = 0; + // Factions that offer this aug. factions: string[] = []; @@ -409,6 +463,7 @@ export class Augmentation { this.baseRepRequirement = params.repCost; this.baseCost = params.moneyCost; this.startingCost = this.baseCost; + this.startingRepRequirement = this.baseRepRequirement; this.factions = params.factions; if (params.isSpecial) { @@ -509,6 +564,25 @@ export class Augmentation { this.mults.bladeburner_success_chance_mult = params.bladeburner_success_chance_mult; } + if (params.infiltration_base_rep_increase) { + this.mults.infiltration_base_rep_increase = params.infiltration_base_rep_increase; + } + if (params.infiltration_rep_mult) { + this.mults.infiltration_rep_mult = params.infiltration_rep_mult; + } + if (params.infiltration_trade_mult) { + this.mults.infiltration_trade_mult = params.infiltration_trade_mult; + } + if (params.infiltration_sell_mult) { + this.mults.infiltration_sell_mult = params.infiltration_sell_mult; + } + if (params.infiltration_timer_mult) { + this.mults.infiltration_timer_mult = params.infiltration_timer_mult; + } + if (params.infiltration_damage_reduction_mult) { + this.mults.infiltration_damage_reduction_mult = params.infiltration_damage_reduction_mult; + } + if (params.stats === undefined) this.stats = generateStatsDescription(this.mults, params.programs, params.startingMoney); else this.stats = params.stats; diff --git a/src/Augmentation/AugmentationHelpers.tsx b/src/Augmentation/AugmentationHelpers.tsx index 44c0f64ba..9bd25a6e0 100644 --- a/src/Augmentation/AugmentationHelpers.tsx +++ b/src/Augmentation/AugmentationHelpers.tsx @@ -3,7 +3,6 @@ import { Augmentations } from "./Augmentations"; import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation"; import { AugmentationNames } from "./data/AugmentationNames"; -import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { CONSTANTS } from "../Constants"; import { Factions, factionExists } from "../Faction/Factions"; import { Player } from "../Player"; @@ -17,9 +16,11 @@ import { initBladeburnerAugmentations, initChurchOfTheMachineGodAugmentations, initGeneralAugmentations, + initSoAAugmentations, initNeuroFluxGovernor, initUnstableCircadianModulator, -} from "./AugmentationCreator"; +} from "./data/AugmentationCreator"; +import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { Router } from "../ui/GameRoot"; export function AddToAugmentations(aug: Augmentation): void { @@ -50,6 +51,7 @@ function createAugmentations(): void { initNeuroFluxGovernor(), initUnstableCircadianModulator(), ...initGeneralAugmentations(), + ...initSoAAugmentations(), ...(factionExists(FactionNames.Bladeburners) ? initBladeburnerAugmentations() : []), ...(factionExists(FactionNames.ChurchOfTheMachineGod) ? initChurchOfTheMachineGodAugmentations() : []), ].map(resetAugmentation); @@ -82,20 +84,36 @@ function updateNeuroFluxGovernorCosts(neuroFluxGovernorAugmentation: Augmentatio let nextLevel = getNextNeuroFluxLevel(); --nextLevel; const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel); - neuroFluxGovernorAugmentation.baseRepRequirement *= multiplier * BitNodeMultipliers.AugmentationRepCost; - neuroFluxGovernorAugmentation.baseCost *= multiplier * BitNodeMultipliers.AugmentationMoneyCost; + neuroFluxGovernorAugmentation.baseRepRequirement = + neuroFluxGovernorAugmentation.startingRepRequirement * multiplier * BitNodeMultipliers.AugmentationRepCost; + neuroFluxGovernorAugmentation.baseCost = + neuroFluxGovernorAugmentation.startingCost * multiplier * BitNodeMultipliers.AugmentationMoneyCost; - for (let i = 0; i < Player.queuedAugmentations.length - 1; ++i) { + for (let i = 0; i < Player.queuedAugmentations.length; ++i) { neuroFluxGovernorAugmentation.baseCost *= getBaseAugmentationPriceMultiplier(); } } +function updateSoACosts(soaAugmentation: Augmentation): void { + const soaAugmentationNames = initSoAAugmentations().map((augmentation) => augmentation.name); + const soaAugCount = soaAugmentationNames.filter((augmentationName) => + Player.hasAugmentation(augmentationName), + ).length; + soaAugmentation.baseCost = soaAugmentation.startingCost * Math.pow(CONSTANTS.SoACostMult, soaAugCount); + if (soaAugmentationNames.find((augmentationName) => augmentationName === soaAugmentation.name)) { + soaAugmentation.baseRepRequirement = + soaAugmentation.startingRepRequirement * Math.pow(CONSTANTS.SoARepMult, soaAugCount); + } +} + export function updateAugmentationCosts(): void { for (const name of Object.keys(Augmentations)) { if (Augmentations.hasOwnProperty(name)) { const augmentationToUpdate = Augmentations[name]; if (augmentationToUpdate.name === AugmentationNames.NeuroFluxGovernor) { updateNeuroFluxGovernorCosts(augmentationToUpdate); + } else if (augmentationToUpdate.factions.includes(FactionNames.ShadowsOfAnarchy)) { + updateSoACosts(augmentationToUpdate); } else { augmentationToUpdate.baseCost = augmentationToUpdate.startingCost * diff --git a/src/Augmentation/AugmentationCreator.tsx b/src/Augmentation/data/AugmentationCreator.tsx similarity index 94% rename from src/Augmentation/AugmentationCreator.tsx rename to src/Augmentation/data/AugmentationCreator.tsx index dd4bd5d70..02ce0bdd2 100644 --- a/src/Augmentation/AugmentationCreator.tsx +++ b/src/Augmentation/data/AugmentationCreator.tsx @@ -1,11 +1,11 @@ -import { Augmentation, IConstructorParams } from "./Augmentation"; -import { AugmentationNames } from "./data/AugmentationNames"; -import { Player } from "../Player"; -import { Programs } from "../Programs/Programs"; -import { WHRNG } from "../Casino/RNG"; +import { Augmentation, IConstructorParams } from "../Augmentation"; +import { AugmentationNames } from "./AugmentationNames"; +import { Player } from "../../Player"; +import { Programs } from "../../Programs/Programs"; +import { WHRNG } from "../../Casino/RNG"; import React from "react"; -import { FactionNames } from "../Faction/data/FactionNames"; -import { CONSTANTS } from "../Constants"; +import { FactionNames } from "../../Faction/data/FactionNames"; +import { CONSTANTS } from "../../Constants"; function getRandomBonus(): any { const bonuses = [ @@ -95,6 +95,99 @@ function getRandomBonus(): any { return bonuses[Math.floor(bonuses.length * randomNumber.random())]; } +export const initSoAAugmentations = (): Augmentation[] => [ + new Augmentation({ + name: AugmentationNames.WKSharmonizer, + repCost: 1e4, + moneyCost: 1e6, + info: + `A copy of the WKS harmonizer from the MIA leader of the ${FactionNames.ShadowsOfAnarchy} ` + + "injects *Γ-based cells that provides general enhancement to the body.", + stats: ( + <> + This augmentation makes many aspect of infiltration easier and more productive. Such as increased timer, + rewards, reduced damage taken, etc. + + ), + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.MightOfAres, + repCost: 1e4, + moneyCost: 1e6, + info: + "Extra-occular neurons taken from old martial art master. Injecting the user the ability to " + + "predict enemy attack before they even know it themself.", + stats: ( + <>This augmentation makes the Slash minigame easier by showing you via an indictor when the slash in coming. + ), + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.WisdomOfAthena, + repCost: 1e4, + 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 '[' ']'., + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.ChaosOfDionysus, + repCost: 1e4, + 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., + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.BeautyOfAphrodite, + repCost: 1e4, + moneyCost: 1e6, + info: + "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., + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.TrickeryOfHermes, + repCost: 1e4, + 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., + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.FloodOfPoseidon, + repCost: 1e4, + 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., + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.HuntOfArtemis, + repCost: 1e4, + moneyCost: 1e6, + info: "magneto-turboencabulator based on technology by Micha Eike Siemon, increases the users electro-magnetic sensitivity.", + stats: ( + <> + This augmentation makes the Minesweeper minigame easier by showing the location of all mines and keeping their + position. + + ), + factions: [FactionNames.ShadowsOfAnarchy], + }), + new Augmentation({ + name: AugmentationNames.KnowledgeOfApollo, + repCost: 1e4, + 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., + factions: [FactionNames.ShadowsOfAnarchy], + }), +]; + export const initGeneralAugmentations = (): Augmentation[] => [ new Augmentation({ name: AugmentationNames.HemoRecirculator, @@ -1936,7 +2029,12 @@ export function initNeuroFluxGovernor(): Augmentation { hacknet_node_core_cost_mult: 1 / (1.01 + donationBonus), hacknet_node_level_cost_mult: 1 / (1.01 + donationBonus), work_money_mult: 1.01 + donationBonus, - factions: Object.values(FactionNames), + factions: Object.values(FactionNames).filter( + (factionName) => + ![FactionNames.ShadowsOfAnarchy, FactionNames.Bladeburners, FactionNames.ChurchOfTheMachineGod].includes( + factionName, + ), + ), }); } diff --git a/src/Augmentation/data/AugmentationNames.ts b/src/Augmentation/data/AugmentationNames.ts index fc77c6d78..574f232f4 100644 --- a/src/Augmentation/data/AugmentationNames.ts +++ b/src/Augmentation/data/AugmentationNames.ts @@ -114,6 +114,28 @@ export enum AugmentationNames { StaneksGift2 = "Stanek's Gift - Awakening", StaneksGift3 = "Stanek's Gift - Serenity", + /* + MightOfAres = "Might of Ares", // slash + WisdomOfAthena = "Wisdom of Athena", // bracket + TrickeryOfHermes = "Trickery of Hermes", // cheatcode + BeautyOfAphrodite = "Beauty of Aphrodite", // bribe + ChaosOfDionysus = "Chaos of Dionysus", // reverse + FloodOfPoseidon = "Flood of Poseidon", // hex + HuntOfArtemis = "Hunt of Artemis", // mine + KnowledgeOfApollo = "Knowledge of Apollo", // wire + */ + + // Infiltrators MiniGames + MightOfAres = "SoA - Might of Ares", // slash + WisdomOfAthena = "SoA - Wisdom of Athena", // bracket + TrickeryOfHermes = "SoA - Trickery of Hermes", // cheatcode + BeautyOfAphrodite = "SoA - Beauty of Aphrodite", // bribe + ChaosOfDionysus = "SoA - Chaos of Dionysus", // reverse + FloodOfPoseidon = "SoA - Flood of Poseidon", // hex + HuntOfArtemis = "SoA - Hunt of Artemis", // mine + KnowledgeOfApollo = "SoA - Knowledge of Apollo", // wire + WKSharmonizer = "SoA - phyzical WKS harmonizer", + //Wasteland Augs //PepBoy: "P.E.P-Boy", Plasma Energy Projection System //PepBoyForceField Generates plasma force fields diff --git a/src/BitNode/ui/BitverseRoot.tsx b/src/BitNode/ui/BitverseRoot.tsx index 1b4c2558a..d75b97652 100644 --- a/src/BitNode/ui/BitverseRoot.tsx +++ b/src/BitNode/ui/BitverseRoot.tsx @@ -171,7 +171,6 @@ export function BitverseRoot(props: IProps): React.ReactElement { <> {Object.values(BitNodes) .filter((node) => { - console.log(node.desc); return node.desc !== "COMING SOON"; }) .map((node) => { diff --git a/src/Constants.ts b/src/Constants.ts index 74668dd4c..6ea3af2d5 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -112,6 +112,8 @@ export const CONSTANTS: { CodingContractBaseMoneyGain: number; AugmentationGraftingCostMult: number; AugmentationGraftingTimeBase: number; + SoACostMult: number; + SoARepMult: number; EntropyEffect: number; TotalNumBitNodes: number; Donations: number; // number of blood/plasma/palette donation the dev have verified., boosts NFG @@ -281,6 +283,10 @@ export const CONSTANTS: { AugmentationGraftingCostMult: 3, AugmentationGraftingTimeBase: 3600000, + // SoA mults + SoACostMult: 7, + SoARepMult: 1.3, + // Value raised to the number of entropy stacks, then multiplied to player multipliers EntropyEffect: 0.98, diff --git a/src/Faction/FactionHelpers.tsx b/src/Faction/FactionHelpers.tsx index 27e5f2816..84519d5af 100644 --- a/src/Faction/FactionHelpers.tsx +++ b/src/Faction/FactionHelpers.tsx @@ -3,7 +3,6 @@ import { Augmentation } from "../Augmentation/Augmentation"; import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; -import { CONSTANTS } from "../Constants"; import { Faction } from "./Faction"; import { Factions } from "./Factions"; @@ -19,6 +18,7 @@ import { import { dialogBoxCreate } from "../ui/React/DialogBox"; import { InvitationEvent } from "./ui/InvitationModal"; import { FactionNames } from "./data/FactionNames"; +import { updateAugmentationCosts, getNextNeuroFluxLevel } from "../Augmentation/AugmentationHelpers"; import { SFC32RNG } from "../Casino/RNG"; export function inviteToFaction(faction: Faction): void { @@ -104,31 +104,13 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal } else if (aug.baseCost === 0 || Player.money >= aug.baseCost) { const queuedAugmentation = new PlayerOwnedAugmentation(aug.name); if (aug.name == AugmentationNames.NeuroFluxGovernor) { - queuedAugmentation.level = getNextNeurofluxLevel(); + queuedAugmentation.level = getNextNeuroFluxLevel(); } Player.queuedAugmentations.push(queuedAugmentation); Player.loseMoney(aug.baseCost, "augmentations"); - // If you just purchased Neuroflux Governor, recalculate the cost - if (aug.name == AugmentationNames.NeuroFluxGovernor) { - let nextLevel = getNextNeurofluxLevel(); - --nextLevel; - const mult = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, nextLevel); - aug.baseRepRequirement = 500 * mult * BitNodeMultipliers.AugmentationRepCost; - aug.baseCost = 750e3 * mult * BitNodeMultipliers.AugmentationMoneyCost; - - for (let i = 0; i < Player.queuedAugmentations.length - 1; ++i) { - aug.baseCost *= CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)]; - } - } - - for (const name of Object.keys(Augmentations)) { - if (Augmentations.hasOwnProperty(name)) { - Augmentations[name].baseCost *= - CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)]; - } - } + updateAugmentationCosts(); if (sing) { return "You purchased " + aug.name; @@ -152,24 +134,6 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal return ""; } -export function getNextNeurofluxLevel(): number { - // Get current Neuroflux level based on Player's augmentations - let currLevel = 0; - for (let i = 0; i < Player.augmentations.length; ++i) { - if (Player.augmentations[i].name === AugmentationNames.NeuroFluxGovernor) { - currLevel = Player.augmentations[i].level; - } - } - - // Account for purchased but uninstalled Augmentations - for (let i = 0; i < Player.queuedAugmentations.length; ++i) { - if (Player.queuedAugmentations[i].name == AugmentationNames.NeuroFluxGovernor) { - ++currLevel; - } - } - return currLevel + 1; -} - export function processPassiveFactionRepGain(numCycles: number): void { for (const name of Object.keys(Factions)) { if (name === Player.currentWorkFactionName) continue; diff --git a/src/Faction/FactionInfo.tsx b/src/Faction/FactionInfo.tsx index 42387fc90..48441a93f 100644 --- a/src/Faction/FactionInfo.tsx +++ b/src/Faction/FactionInfo.tsx @@ -3,6 +3,7 @@ import { IMap } from "../types"; import { FactionNames } from "./data/FactionNames"; import { use } from "../ui/Context"; import { Option } from "./ui/Option"; +import { Typography } from "@mui/material"; interface FactionInfoParams { infoText?: JSX.Element; @@ -511,4 +512,17 @@ export const FactionInfos: IMap = { ); }, }), + [FactionNames.ShadowsOfAnarchy]: new FactionInfo({ + infoText: ( + <> + The government is ruled by the corporations that we have allowed to consume it. To release the world from its + shackles, the gods grant us their strength. + + ), + special: true, + keepOnInstall: true, + assignment: (): React.ReactElement => { + return {FactionNames.ShadowsOfAnarchy} can only gain reputation by infiltrating.; + }, + }), }; diff --git a/src/Faction/data/FactionNames.ts b/src/Faction/data/FactionNames.ts index a343b9509..ebee021a6 100644 --- a/src/Faction/data/FactionNames.ts +++ b/src/Faction/data/FactionNames.ts @@ -32,4 +32,5 @@ export enum FactionNames { CyberSec = "CyberSec", Bladeburners = "Bladeburners", ChurchOfTheMachineGod = "Church of the Machine God", + ShadowsOfAnarchy = "Shadows of Anarchy", } diff --git a/src/Faction/ui/AugmentationsPage.tsx b/src/Faction/ui/AugmentationsPage.tsx index b8ad88522..35718b458 100644 --- a/src/Faction/ui/AugmentationsPage.tsx +++ b/src/Faction/ui/AugmentationsPage.tsx @@ -1,14 +1,7 @@ /** * Root React Component for displaying a faction's "Purchase Augmentations" page */ -import Box from "@mui/material/Box"; -import Button from "@mui/material/Button"; -import Table from "@mui/material/Table"; -import TableBody from "@mui/material/TableBody"; -import Tooltip from "@mui/material/Tooltip"; -import Typography from "@mui/material/Typography"; import React, { useState } from "react"; -import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers"; import { Augmentations } from "../../Augmentation/Augmentations"; import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { PurchaseableAugmentation, PurchaseableAugmentations } from "../../Augmentation/ui/PurchaseableAugmentations"; @@ -21,6 +14,11 @@ import { Reputation } from "../../ui/React/Reputation"; import { Faction } from "../Faction"; import { getFactionAugmentationsFiltered, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers"; +import {Box, Button, Typography, Tooltip, TableBody, Table} from "@mui/material"; + +import { getGenericAugmentationPriceMultiplier } from "../../Augmentation/AugmentationHelpers"; +import { FactionNames } from "../data/FactionNames"; + type IProps = { faction: Faction; routeToMainPage: () => void; @@ -160,6 +158,14 @@ export function AugmentationsPage(props: IProps): React.ReactElement { ); } + const multiplierComponent = + props.faction.name !== FactionNames.ShadowsOfAnarchy ? ( + + Price multiplier: x {numeralWrapper.formatMultiplier(getGenericAugmentationPriceMultiplier())} + + ) : ( + <> + ); return ( <> @@ -181,9 +187,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement { } > - - Price multiplier: x {numeralWrapper.formatMultiplier(getGenericAugmentationPriceMultiplier())} - + {multiplierComponent} diff --git a/src/Faction/ui/Info.tsx b/src/Faction/ui/Info.tsx index f882003bf..c2f0ae5de 100644 --- a/src/Faction/ui/Info.tsx +++ b/src/Faction/ui/Info.tsx @@ -58,6 +58,7 @@ export function Info(props: IProps): React.ReactElement { const Assignment = props.factionInfo.assignment ?? DefaultAssignment; const favorGain = props.faction.getFavorGain(); + return ( <> {props.factionInfo.infoText} diff --git a/src/Faction/ui/PurchaseableAugmentation.tsx b/src/Faction/ui/PurchaseableAugmentation.tsx new file mode 100644 index 000000000..feebb29e0 --- /dev/null +++ b/src/Faction/ui/PurchaseableAugmentation.tsx @@ -0,0 +1,170 @@ +/** + * 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/game.ts b/src/Infiltration/formulas/game.ts new file mode 100644 index 000000000..2b98bc239 --- /dev/null +++ b/src/Infiltration/formulas/game.ts @@ -0,0 +1,25 @@ +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { calculateSkill } from "../../PersonObjects/formulas/skill"; + +function calculateRawDiff(player: IPlayer, stats: number, startingDifficulty: number): number { + const difficulty = startingDifficulty - Math.pow(stats, 0.9) / 250 - player.intelligence / 1600; + if (difficulty < 0) return 0; + if (difficulty > 3) return 3; + return difficulty; +} + +export function calculateDifficulty(player: IPlayer, startingSecurityLevel: number): number { + const totalStats = player.strength + player.defense + player.dexterity + player.agility + player.charisma; + return calculateRawDiff(player, totalStats, startingSecurityLevel); +} + +export function calculateReward(player: IPlayer, startingSecurityLevel: number): number { + const xpMult = 10 * 60 * 15; + const total = + calculateSkill(player.strength_exp_mult * xpMult, player.strength_mult) + + calculateSkill(player.defense_exp_mult * xpMult, player.defense_mult) + + calculateSkill(player.agility_exp_mult * xpMult, player.agility_mult) + + calculateSkill(player.dexterity_exp_mult * xpMult, player.dexterity_mult) + + calculateSkill(player.charisma_exp_mult * xpMult, player.charisma_mult); + return calculateRawDiff(player, total, startingSecurityLevel); +} diff --git a/src/Infiltration/formulas/victory.ts b/src/Infiltration/formulas/victory.ts new file mode 100644 index 000000000..2fca9e3a4 --- /dev/null +++ b/src/Infiltration/formulas/victory.ts @@ -0,0 +1,51 @@ +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; +import { LocationsMetadata } from "../../Locations/data/LocationsMetadata"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; +import { Faction } from "../../Faction/Faction"; + +export function calculateSellInformationCashReward( + player: IPlayer, + reward: number, + maxLevel: number, + difficulty: number, +): number { + const levelBonus = maxLevel * Math.pow(1.01, maxLevel); + + return ( + Math.pow(reward + 1, 2) * + Math.pow(difficulty, 3) * + 3e3 * + levelBonus * + (player.hasAugmentation(AugmentationNames.WKSharmonizer) ? 1.5 : 1) * + BitNodeMultipliers.InfiltrationMoney + ); +} + +export function calculateTradeInformationRepReward( + player: IPlayer, + reward: number, + maxLevel: number, + difficulty: number, +): number { + const levelBonus = maxLevel * Math.pow(1.01, maxLevel); + + return ( + Math.pow(reward + 1, 2) * + Math.pow(difficulty, 3) * + 3e3 * + levelBonus * + (player.hasAugmentation(AugmentationNames.WKSharmonizer) ? 1.5 : 1) * + BitNodeMultipliers.InfiltrationMoney + ); +} + +export function calculateInfiltratorsRepReward(player: IPlayer, faction: Faction, difficulty: number): number { + const maxStartingSecurityLevel = LocationsMetadata.reduce((acc, data): number => { + const startingSecurityLevel = data.infiltrationData?.startingSecurityLevel || 0; + return acc > startingSecurityLevel ? acc : startingSecurityLevel; + }, 0); + const baseRepGain = (difficulty / maxStartingSecurityLevel) * 5000; + + return baseRepGain * (player.hasAugmentation(AugmentationNames.WKSharmonizer) ? 2 : 1) * (1 + faction.favor / 100); +} diff --git a/src/Infiltration/ui/BackwardGame.tsx b/src/Infiltration/ui/BackwardGame.tsx index 7cd7ed724..1c5ef1e6c 100644 --- a/src/Infiltration/ui/BackwardGame.tsx +++ b/src/Infiltration/ui/BackwardGame.tsx @@ -8,6 +8,8 @@ import { interpolate } from "./Difficulty"; import { BlinkingCursor } from "./BlinkingCursor"; import Typography from "@mui/material/Typography"; import { KEY } from "../../utils/helpers/keyCodes"; +import { Player } from "../../Player"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; interface Difficulty { [key: string]: number; @@ -34,6 +36,7 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement { const timer = difficulty.timer; const [answer] = useState(makeAnswer(difficulty)); const [guess, setGuess] = useState(""); + const hasAugment = Player.hasAugmentation(AugmentationNames.ChaosOfDionysus, true); function press(this: Document, event: KeyboardEvent): void { event.preventDefault(); @@ -48,11 +51,13 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement { - Type it backward + Type it{!hasAugment ? " backward" : ""} - {answer} + + {answer} + diff --git a/src/Infiltration/ui/BracketGame.tsx b/src/Infiltration/ui/BracketGame.tsx index 6c1aee432..41015e8e6 100644 --- a/src/Infiltration/ui/BracketGame.tsx +++ b/src/Infiltration/ui/BracketGame.tsx @@ -7,6 +7,8 @@ import { random } from "../utils"; import { interpolate } from "./Difficulty"; import { BlinkingCursor } from "./BlinkingCursor"; import Typography from "@mui/material/Typography"; +import { Player } from "../../Player"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; import { KEY } from "../../utils/helpers/keyCodes"; interface Difficulty { @@ -31,9 +33,12 @@ const difficulties: { function generateLeftSide(difficulty: Difficulty): string { let str = ""; const options = [KEY.OPEN_BRACKET, KEY.LESS_THAN, KEY.OPEN_PARENTHESIS, KEY.OPEN_BRACE]; + if (Player.hasAugmentation(AugmentationNames.WisdomOfAthena, true)) { + options.splice(0, 1); + } const length = random(difficulty.min, difficulty.max); for (let i = 0; i < length; i++) { - str += options[Math.floor(Math.random() * 4)]; + str += options[Math.floor(Math.random() * options.length)]; } return str; diff --git a/src/Infiltration/ui/BribeGame.tsx b/src/Infiltration/ui/BribeGame.tsx index f7ff526a8..6f6811e14 100644 --- a/src/Infiltration/ui/BribeGame.tsx +++ b/src/Infiltration/ui/BribeGame.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import Grid from "@mui/material/Grid"; import { IMinigameProps } from "./IMinigameProps"; import { KeyHandler } from "./KeyHandler"; @@ -6,6 +6,9 @@ import { GameTimer } from "./GameTimer"; import { interpolate } from "./Difficulty"; import Typography from "@mui/material/Typography"; import { KEY } from "../../utils/helpers/keyCodes"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; +import { Player } from "../../Player"; +import { Settings } from "../../Settings/Settings"; import { downArrowSymbol, upArrowSymbol } from "../utils"; interface Difficulty { @@ -31,20 +34,54 @@ export function BribeGame(props: IMinigameProps): React.ReactElement { interpolate(difficulties, props.difficulty, difficulty); const timer = difficulty.timer; const [choices] = useState(makeChoices(difficulty)); + const [correctIndex, setCorrectIndex] = useState(0); const [index, setIndex] = useState(0); + const currentChoice = choices[index]; + + useEffect(() => { + setCorrectIndex(choices.findIndex((choice) => positive.includes(choice))); + }, [choices]); + + const defaultColor = Settings.theme.primary; + const disabledColor = Settings.theme.disabled; + let upColor = defaultColor; + let downColor = defaultColor; + let choiceColor = defaultColor; + const hasAugment = Player.hasAugmentation(AugmentationNames.BeautyOfAphrodite, true); + + if (hasAugment) { + const upIndex = index + 1 >= choices.length ? 0 : index + 1; + let upDistance = correctIndex - upIndex; + if (upIndex > correctIndex) { + upDistance = choices.length - 1 - upIndex + correctIndex; + } + + const downIndex = index - 1 < 0 ? choices.length - 1 : index - 1; + let downDistance = downIndex - correctIndex; + if (downIndex < correctIndex) { + downDistance = downIndex + choices.length - 1 - correctIndex; + } + + const onCorrectIndex = correctIndex == index; + + upColor = upDistance <= downDistance && !onCorrectIndex ? upColor : disabledColor; + downColor = upDistance >= downDistance && !onCorrectIndex ? downColor : disabledColor; + choiceColor = onCorrectIndex ? defaultColor : disabledColor; + } function press(this: Document, event: KeyboardEvent): void { event.preventDefault(); + const k = event.key; if (k === KEY.SPACE) { - if (positive.includes(choices[index])) props.onSuccess(); + if (positive.includes(currentChoice)) props.onSuccess(); else props.onFailure(); return; } let newIndex = index; - if ([KEY.UP_ARROW, KEY.W, KEY.RIGHT_ARROW, KEY.D].map((key) => key as string).includes(k)) newIndex++; - if ([KEY.DOWN_ARROW, KEY.S, KEY.LEFT_ARROW, KEY.A].map((key) => key as string).includes(k)) newIndex--; + if ([KEY.UP_ARROW, KEY.W, KEY.RIGHT_ARROW, KEY.D].map((k) => k as string).includes(k)) newIndex++; + if ([KEY.DOWN_ARROW, KEY.S, KEY.LEFT_ARROW, KEY.A].map((k) => k as string).includes(k)) newIndex--; while (newIndex < 0) newIndex += choices.length; while (newIndex > choices.length - 1) newIndex -= choices.length; setIndex(newIndex); @@ -58,13 +95,13 @@ export function BribeGame(props: IMinigameProps): React.ReactElement { - + {upArrowSymbol} - - {choices[index]} + + {currentChoice} - + {downArrowSymbol} diff --git a/src/Infiltration/ui/CheatCodeGame.tsx b/src/Infiltration/ui/CheatCodeGame.tsx index 38cb80e6a..f0974a3bc 100644 --- a/src/Infiltration/ui/CheatCodeGame.tsx +++ b/src/Infiltration/ui/CheatCodeGame.tsx @@ -3,9 +3,19 @@ import Grid from "@mui/material/Grid"; import { IMinigameProps } from "./IMinigameProps"; import { KeyHandler } from "./KeyHandler"; import { GameTimer } from "./GameTimer"; -import { random, getArrow, rightArrowSymbol, leftArrowSymbol, upArrowSymbol, downArrowSymbol } from "../utils"; +import { + random, + getArrow, + getInverseArrow, + leftArrowSymbol, + rightArrowSymbol, + upArrowSymbol, + downArrowSymbol, +} from "../utils"; import { interpolate } from "./Difficulty"; import Typography from "@mui/material/Typography"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; +import { Player } from "../../Player"; interface Difficulty { [key: string]: number; @@ -32,10 +42,11 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement { const timer = difficulty.timer; const [code] = useState(generateCode(difficulty)); const [index, setIndex] = useState(0); + const hasAugment = Player.hasAugmentation(AugmentationNames.TrickeryOfHermes, true); function press(this: Document, event: KeyboardEvent): void { event.preventDefault(); - if (code[index] !== getArrow(event)) { + if (code[index] !== getArrow(event) && (!hasAugment || code[index] !== getInverseArrow(event))) { props.onFailure(); return; } diff --git a/src/Infiltration/ui/Cyberpunk2077Game.tsx b/src/Infiltration/ui/Cyberpunk2077Game.tsx index ab63eccac..6c61f6494 100644 --- a/src/Infiltration/ui/Cyberpunk2077Game.tsx +++ b/src/Infiltration/ui/Cyberpunk2077Game.tsx @@ -7,6 +7,9 @@ import { interpolate } from "./Difficulty"; import { downArrowSymbol, getArrow, leftArrowSymbol, rightArrowSymbol, upArrowSymbol } from "../utils"; import Typography from "@mui/material/Typography"; import { KEY } from "../../utils/helpers/keyCodes"; +import { Settings } from "../../Settings/Settings"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; +import { Player } from "../../Player"; interface Difficulty { [key: string]: number; @@ -33,10 +36,11 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement { interpolate(difficulties, props.difficulty, difficulty); const timer = difficulty.timer; const [grid] = useState(generatePuzzle(difficulty)); - const [answer] = useState(generateAnswer(grid, difficulty)); - const [index, setIndex] = useState(0); + const [answers] = useState(generateAnswers(grid, difficulty)); + const [currentAnswerIndex, setCurrentAnswerIndex] = useState(0); const [pos, setPos] = useState([0, 0]); + const hasAugment = Player.hasAugmentation(AugmentationNames.FloodOfPoseidon, true); function press(this: Document, event: KeyboardEvent): void { event.preventDefault(); const move = [0, 0]; @@ -62,13 +66,13 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement { if (event.key === KEY.SPACE) { const selected = grid[pos[1]][pos[0]]; - const expected = answer[index]; + const expected = answers[currentAnswerIndex]; if (selected !== expected) { props.onFailure(); return; } - setIndex(index + 1); - if (answer.length === index + 1) props.onSuccess(); + setCurrentAnswerIndex(currentAnswerIndex + 1); + if (answers.length === currentAnswerIndex + 1) props.onSuccess(); } } @@ -78,17 +82,17 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement { Match the symbols! - + Targets:{" "} - {answer.map((a, i) => { - if (i == index) + {answers.map((a, i) => { + if (i == currentAnswerIndex) return ( {a}  ); return ( - + {a}  ); @@ -99,14 +103,19 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
{line.map((cell, x) => { - if (x == pos[0] && y == pos[1]) + const isCorrectAnswer = cell === answers[currentAnswerIndex]; + + if (x == pos[0] && y == pos[1]) { return ( {cell}  ); + } + + const optionColor = hasAugment && !isCorrectAnswer ? Settings.theme.disabled : Settings.theme.primary; return ( - + {cell}  ); @@ -121,12 +130,12 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement { ); } -function generateAnswer(grid: string[][], difficulty: Difficulty): string[] { - const answer = []; +function generateAnswers(grid: string[][], difficulty: Difficulty): string[] { + const answers = []; for (let i = 0; i < Math.round(difficulty.symbols); i++) { - answer.push(grid[Math.floor(Math.random() * grid.length)][Math.floor(Math.random() * grid[0].length)]); + answers.push(grid[Math.floor(Math.random() * grid.length)][Math.floor(Math.random() * grid[0].length)]); } - return answer; + return answers; } function randChar(): string { diff --git a/src/Infiltration/ui/Game.tsx b/src/Infiltration/ui/Game.tsx index b0daa37d4..34732a8d6 100644 --- a/src/Infiltration/ui/Game.tsx +++ b/src/Infiltration/ui/Game.tsx @@ -13,6 +13,7 @@ import { MinesweeperGame } from "./MinesweeperGame"; import { WireCuttingGame } from "./WireCuttingGame"; import { Victory } from "./Victory"; import Typography from "@mui/material/Typography"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; interface IProps { StartingDifficulty: number; @@ -91,7 +92,9 @@ export function Game(props: IProps): React.ReactElement { pushResult(false); // Kill the player immediately if they use automation, so // it's clear they're not meant to - const damage = options?.automated ? player.hp : props.StartingDifficulty * 3; + const damage = options?.automated + ? player.hp + : props.StartingDifficulty * 3 * (player.hasAugmentation(AugmentationNames.WKSharmonizer) ? 0.5 : 1); if (player.takeDamage(damage)) { router.toCity(); return; @@ -110,7 +113,16 @@ export function Game(props: IProps): React.ReactElement { stageComponent = setStage(Stage.Minigame)} />; break; case Stage.Minigame: { - const MiniGame = minigames[gameIds.id]; + /** + * + BackwardGame, + BribeGame, + CheatCodeGame, + Cyberpunk2077Game, + MinesweeperGame, + WireCuttingGame, + */ + const MiniGame = WireCuttingGame; // minigames[gameIds.id]; stageComponent = ; break; } diff --git a/src/Infiltration/ui/GameTimer.tsx b/src/Infiltration/ui/GameTimer.tsx index b7ee53c30..a55ab0587 100644 --- a/src/Infiltration/ui/GameTimer.tsx +++ b/src/Infiltration/ui/GameTimer.tsx @@ -3,6 +3,8 @@ import React, { useState, useEffect } from "react"; import withStyles from "@mui/styles/withStyles"; import { Theme } from "@mui/material/styles"; import Grid from "@mui/material/Grid"; +import { use } from "../../ui/Context"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; const TimerProgress = withStyles((theme: Theme) => ({ root: { @@ -20,14 +22,16 @@ interface IProps { } export function GameTimer(props: IProps): React.ReactElement { + const player = use.Player(); const [v, setV] = useState(100); + const totalMillis = (player.hasAugmentation(AugmentationNames.WKSharmonizer) ? 1.3 : 1) * props.millis; const tick = 200; useEffect(() => { const intervalId = setInterval(() => { setV((old) => { if (old <= 0) props.onExpire(); - return old - (tick / props.millis) * 100; + return old - (tick / totalMillis) * 100; }); }, tick); return () => { diff --git a/src/Infiltration/ui/InfiltrationRoot.tsx b/src/Infiltration/ui/InfiltrationRoot.tsx index 54c78c019..1c78b5b19 100644 --- a/src/Infiltration/ui/InfiltrationRoot.tsx +++ b/src/Infiltration/ui/InfiltrationRoot.tsx @@ -1,48 +1,22 @@ -import { IPlayer } from "../../PersonObjects/IPlayer"; import React, { useState } from "react"; import { Intro } from "./Intro"; import { Game } from "./Game"; import { Location } from "../../Locations/Location"; import { use } from "../../ui/Context"; -import { calculateSkill } from "../../PersonObjects/formulas/skill"; - +import { calculateDifficulty, calculateReward } from "../formulas/game"; interface IProps { location: Location; } -function calcRawDiff(player: IPlayer, stats: number, startingDifficulty: number): number { - const difficulty = startingDifficulty - Math.pow(stats, 0.9) / 250 - player.intelligence / 1600; - if (difficulty < 0) return 0; - if (difficulty > 3) return 3; - return difficulty; -} - -function calcDifficulty(player: IPlayer, startingDifficulty: number): number { - const totalStats = player.strength + player.defense + player.dexterity + player.agility + player.charisma; - return calcRawDiff(player, totalStats, startingDifficulty); -} - -function calcReward(player: IPlayer, startingDifficulty: number): number { - const xpMult = 10 * 60 * 15; - const total = - calculateSkill(player.strength_exp_mult * xpMult, player.strength_mult) + - calculateSkill(player.defense_exp_mult * xpMult, player.defense_mult) + - calculateSkill(player.agility_exp_mult * xpMult, player.agility_mult) + - calculateSkill(player.dexterity_exp_mult * xpMult, player.dexterity_mult) + - calculateSkill(player.charisma_exp_mult * xpMult, player.charisma_mult); - return calcRawDiff(player, total, startingDifficulty); -} - export function InfiltrationRoot(props: IProps): React.ReactElement { const player = use.Player(); const router = use.Router(); const [start, setStart] = useState(false); if (props.location.infiltrationData === undefined) throw new Error("Trying to do infiltration on invalid location."); - const startingDifficulty = props.location.infiltrationData.startingSecurityLevel; - const difficulty = calcDifficulty(player, startingDifficulty); - const reward = calcReward(player, startingDifficulty); - console.log(`${difficulty} ${reward}`); + const startingSecurityLevel = props.location.infiltrationData.startingSecurityLevel; + const difficulty = calculateDifficulty(player, startingSecurityLevel); + const reward = calculateReward(player, startingSecurityLevel); function cancel(): void { router.toCity(); @@ -62,7 +36,7 @@ export function InfiltrationRoot(props: IProps): React.ReactElement { return ( [X] ; if (answer[y][x]) return [.] ; + if (hasAugment && minefield[y][x]) return [?] ; return [ ] ; } })} diff --git a/src/Infiltration/ui/SlashGame.tsx b/src/Infiltration/ui/SlashGame.tsx index 2926052ef..b9eb523a9 100644 --- a/src/Infiltration/ui/SlashGame.tsx +++ b/src/Infiltration/ui/SlashGame.tsx @@ -6,6 +6,8 @@ import { GameTimer } from "./GameTimer"; import { interpolate } from "./Difficulty"; import Typography from "@mui/material/Typography"; import { KEY } from "../../utils/helpers/keyCodes"; +import { Player } from "../../Player"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; interface Difficulty { [key: string]: number; @@ -38,6 +40,10 @@ export function SlashGame(props: IMinigameProps): React.ReactElement { props.onSuccess(); } } + const hasAugment = Player.hasAugmentation(AugmentationNames.MightOfAres, true); + const phaseZeroTime = Math.random() * 3250 + 1500 - (250 + difficulty.window); + const phaseOneTime = 250; + const timeUntilAttacking = phaseZeroTime + phaseOneTime; useEffect(() => { let id = window.setTimeout(() => { @@ -45,8 +51,8 @@ export function SlashGame(props: IMinigameProps): React.ReactElement { id = window.setTimeout(() => { setPhase(2); id = window.setTimeout(() => setPhase(0), difficulty.window); - }, 250); - }, Math.random() * 3250 + 1500 - (250 + difficulty.window)); + }, phaseOneTime); + }, phaseZeroTime); return () => { clearInterval(id); }; @@ -57,6 +63,14 @@ export function SlashGame(props: IMinigameProps): React.ReactElement { Slash when his guard is down! + {hasAugment ? ( + <> + Guard will drop in... + + + ) : ( + <> + )} {phase === 0 && Guarding ...} {phase === 1 && Preparing?} {phase === 2 && ATTACKING!} diff --git a/src/Infiltration/ui/Victory.tsx b/src/Infiltration/ui/Victory.tsx index de3e181f0..2a90351a0 100644 --- a/src/Infiltration/ui/Victory.tsx +++ b/src/Infiltration/ui/Victory.tsx @@ -3,12 +3,19 @@ import React, { useState } from "react"; import Grid from "@mui/material/Grid"; import { Money } from "../../ui/React/Money"; import { Reputation } from "../../ui/React/Reputation"; -import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { use } from "../../ui/Context"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; import MenuItem from "@mui/material/MenuItem"; import Select, { SelectChangeEvent } from "@mui/material/Select"; +import { FactionNames } from "../../Faction/data/FactionNames"; +import { formatNumber } from "../../utils/StringHelperFunctions"; +import { + calculateInfiltratorsRepReward, + calculateSellInformationCashReward, + calculateTradeInformationRepReward, +} from "../formulas/victory"; +import { inviteToFaction } from "../../Faction/FactionHelpers"; interface IProps { StartingDifficulty: number; @@ -23,31 +30,25 @@ export function Victory(props: IProps): React.ReactElement { const [faction, setFaction] = useState("none"); function quitInfiltration(): void { + handleInfiltrators(); router.toCity(); } - const levelBonus = props.MaxLevel * Math.pow(1.01, props.MaxLevel); + const soa = Factions[FactionNames.ShadowsOfAnarchy]; + const repGain = calculateTradeInformationRepReward(player, props.Reward, props.MaxLevel, props.StartingDifficulty); + const moneyGain = calculateSellInformationCashReward(player, props.Reward, props.MaxLevel, props.StartingDifficulty); + const infiltrationRepGain = calculateInfiltratorsRepReward(player, soa, props.StartingDifficulty); - const repGain = - Math.pow(props.Reward + 1, 1.1) * - Math.pow(props.StartingDifficulty, 1.2) * - 30 * - levelBonus * - BitNodeMultipliers.InfiltrationRep; - - const moneyGain = - Math.pow(props.Reward + 1, 2) * - Math.pow(props.StartingDifficulty, 3) * - 3e3 * - levelBonus * - BitNodeMultipliers.InfiltrationMoney; + const isMemberOfInfiltrators = player.factions.includes(FactionNames.ShadowsOfAnarchy); function sell(): void { + handleInfiltrators(); player.gainMoney(moneyGain, "infiltration"); quitInfiltration(); } function trade(): void { + handleInfiltrators(); if (faction === "none") return; Factions[faction].playerReputation += repGain; quitInfiltration(); @@ -57,6 +58,13 @@ export function Victory(props: IProps): React.ReactElement { setFaction(event.target.value); } + function handleInfiltrators(): void { + inviteToFaction(Factions[FactionNames.ShadowsOfAnarchy]); + if (isMemberOfInfiltrators) { + soa.playerReputation += infiltrationRepGain; + } + } + return ( <> @@ -65,7 +73,15 @@ export function Victory(props: IProps): React.ReactElement { - You can trade the confidential information you found for money or reputation. + You{" "} + {isMemberOfInfiltrators ? ( + <> + have gained {formatNumber(infiltrationRepGain, 2)} rep for {FactionNames.ShadowsOfAnarchy} and{" "} + + ) : ( + <> + )} + can trade the confidential information you found for money or reputation.