diff --git a/src/Augmentation/AugmentationHelpers.tsx b/src/Augmentation/AugmentationHelpers.tsx index 23d67a389..a8ffa8e83 100644 --- a/src/Augmentation/AugmentationHelpers.tsx +++ b/src/Augmentation/AugmentationHelpers.tsx @@ -21,7 +21,7 @@ import { initUnstableCircadianModulator, } from "./data/AugmentationCreator"; import { Router } from "../ui/GameRoot"; -import { mergeMultipliers } from "../PersonObjects/Multipliers"; +import { mergeAugmentation } from "../PersonObjects/Multipliers"; export function AddToStaticAugmentations(aug: Augmentation): void { const name = aug.name; @@ -75,7 +75,7 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void const staticAugmentation = StaticAugmentations[aug.name]; // Apply multipliers - Player.mults = mergeMultipliers(Player.mults, staticAugmentation.mults); + Player.mults = mergeAugmentation(Player.mults, staticAugmentation.mults); // Special logic for Congruity Implant if (aug.name === AugmentationNames.CongruityImplant && !reapply) { diff --git a/src/Augmentation/data/AugmentationCreator.tsx b/src/Augmentation/data/AugmentationCreator.tsx index ef88705ee..e21aca702 100644 --- a/src/Augmentation/data/AugmentationCreator.tsx +++ b/src/Augmentation/data/AugmentationCreator.tsx @@ -6,6 +6,7 @@ import { WHRNG } from "../../Casino/RNG"; import React from "react"; import { FactionNames } from "../../Faction/data/FactionNames"; import { CONSTANTS } from "../../Constants"; +import { Faction } from "src/Faction/Faction"; interface CircadianBonus { bonuses: { @@ -1630,6 +1631,16 @@ export const initGeneralAugmentations = (): Augmentation[] => [ factions: [FactionNames.TianDiHui], }), + // new Augmentation({ + // name: AugmentationNames.UnnamedAug2, + // repCost: 500e3, + // moneyCost: 5e9, + // info: "Undecided description", + // startingMoney: 100e6, + // programs: [Programs.HTTPWormProgram.name, Programs.SQLInjectProgram.name], + // factions: [FactionNames.OmniTekIncorporated], + // }), + // Grafting-exclusive Augmentation new Augmentation({ name: AugmentationNames.CongruityImplant, @@ -1648,6 +1659,19 @@ export const initGeneralAugmentations = (): Augmentation[] => [ stats: <>This Augmentation removes the Entropy virus, and prevents it from affecting you again., factions: [], }), + + // Sleeve exclusive augmentations + new Augmentation({ + name: AugmentationNames.UnnamedAug1, + isSpecial: true, + repCost: Infinity, + moneyCost: 1e12, + info: "This augmentation is exclusive to sleeves.", + stats: <>Allows sleeves to benefit from Stanek's Gift but it is less powerful if several are installed., + factions: [ + /*Technically in FactionNames.ChurchOfTheMachineGod but not really for display reasons */ + ], + }), ]; export const initBladeburnerAugmentations = (): Augmentation[] => [ diff --git a/src/Augmentation/data/AugmentationNames.ts b/src/Augmentation/data/AugmentationNames.ts index 10c78c9c7..642a29128 100644 --- a/src/Augmentation/data/AugmentationNames.ts +++ b/src/Augmentation/data/AugmentationNames.ts @@ -93,6 +93,8 @@ export enum AugmentationNames { CongruityImplant = "nickofolas Congruity Implant", HydroflameLeftArm = "Hydroflame Left Arm", BigDsBigBrain = "BigD's Big ... Brain", + UnnamedAug1 = "UnnamedAug1", + UnnamedAug2 = "UnnamedAug2", // Bladeburner augs EsperEyewear = "EsperTech Bladeburner Eyewear", diff --git a/src/Augmentation/ui/PlayerMultipliers.tsx b/src/Augmentation/ui/PlayerMultipliers.tsx index ca7b4c58a..5ddb8b07f 100644 --- a/src/Augmentation/ui/PlayerMultipliers.tsx +++ b/src/Augmentation/ui/PlayerMultipliers.tsx @@ -4,7 +4,7 @@ import { DoubleArrow } from "@mui/icons-material"; import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material"; import * as React from "react"; -import { Multipliers, defaultMultipliers, mergeMultipliers } from "../../PersonObjects/Multipliers"; +import { Multipliers, defaultMultipliers, mergeAugmentation } from "../../PersonObjects/Multipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { Player } from "../../Player"; import { Settings } from "../../Settings/Settings"; @@ -15,7 +15,7 @@ function calculateAugmentedStats(): Multipliers { let augP: Multipliers = defaultMultipliers(); for (const aug of Player.queuedAugmentations) { const augObj = StaticAugmentations[aug.name]; - augP = mergeMultipliers(augP, augObj.mults); + augP = mergeAugmentation(augP, augObj.mults); } return augP; } diff --git a/src/CotMG/StaneksGift.ts b/src/CotMG/StaneksGift.ts index 4858bb64c..0d2917433 100644 --- a/src/CotMG/StaneksGift.ts +++ b/src/CotMG/StaneksGift.ts @@ -13,6 +13,7 @@ import { StanekConstants } from "./data/Constants"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { Player } from "../Player"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; +import { defaultMultipliers, mergeMultipliers, Multipliers, scaleMultipliers } from "../PersonObjects/Multipliers"; export class StaneksGift implements IStaneksGift { storedCycles = 0; @@ -135,81 +136,101 @@ export class StaneksGift implements IStaneksGift { }); } - updateMults(p: IPlayer): void { - // applyEntropy also reapplies all augmentations and source files - // This wraps up the reset nicely - p.applyEntropy(p.entropy); - + calculateMults(): Multipliers { + const mults = defaultMultipliers(); for (const aFrag of this.fragments) { const fragment = aFrag.fragment(); const power = this.effect(aFrag); switch (fragment.type) { case FragmentType.HackingChance: - p.mults.hacking_chance *= power; + mults.hacking_chance *= power; break; case FragmentType.HackingSpeed: - p.mults.hacking_speed *= power; + mults.hacking_speed *= power; break; case FragmentType.HackingMoney: - p.mults.hacking_money *= power; + mults.hacking_money *= power; break; case FragmentType.HackingGrow: - p.mults.hacking_grow *= power; + mults.hacking_grow *= power; break; case FragmentType.Hacking: - p.mults.hacking *= power; - p.mults.hacking_exp *= power; + mults.hacking *= power; + mults.hacking_exp *= power; break; case FragmentType.Strength: - p.mults.strength *= power; - p.mults.strength_exp *= power; + mults.strength *= power; + mults.strength_exp *= power; break; case FragmentType.Defense: - p.mults.defense *= power; - p.mults.defense_exp *= power; + mults.defense *= power; + mults.defense_exp *= power; break; case FragmentType.Dexterity: - p.mults.dexterity *= power; - p.mults.dexterity_exp *= power; + mults.dexterity *= power; + mults.dexterity_exp *= power; break; case FragmentType.Agility: - p.mults.agility *= power; - p.mults.agility_exp *= power; + mults.agility *= power; + mults.agility_exp *= power; break; case FragmentType.Charisma: - p.mults.charisma *= power; - p.mults.charisma_exp *= power; + mults.charisma *= power; + mults.charisma_exp *= power; break; case FragmentType.HacknetMoney: - p.mults.hacknet_node_money *= power; + mults.hacknet_node_money *= power; break; case FragmentType.HacknetCost: - p.mults.hacknet_node_purchase_cost /= power; - p.mults.hacknet_node_ram_cost /= power; - p.mults.hacknet_node_core_cost /= power; - p.mults.hacknet_node_level_cost /= power; + mults.hacknet_node_purchase_cost /= power; + mults.hacknet_node_ram_cost /= power; + mults.hacknet_node_core_cost /= power; + mults.hacknet_node_level_cost /= power; break; case FragmentType.Rep: - p.mults.company_rep *= power; - p.mults.faction_rep *= power; + mults.company_rep *= power; + mults.faction_rep *= power; break; case FragmentType.WorkMoney: - p.mults.work_money *= power; + mults.work_money *= power; break; case FragmentType.Crime: - p.mults.crime_success *= power; - p.mults.crime_money *= power; + mults.crime_success *= power; + mults.crime_money *= power; break; case FragmentType.Bladeburner: - p.mults.bladeburner_max_stamina *= power; - p.mults.bladeburner_stamina_gain *= power; - p.mults.bladeburner_analysis *= power; - p.mults.bladeburner_success_chance *= power; + mults.bladeburner_max_stamina *= power; + mults.bladeburner_stamina_gain *= power; + mults.bladeburner_analysis *= power; + mults.bladeburner_success_chance *= power; break; } } + return mults; + } + + updateMults(p: IPlayer): void { + // applyEntropy also reapplies all augmentations and source files + // This wraps up the reset nicely + p.applyEntropy(p.entropy); + const mults = this.calculateMults(); + p.mults = mergeMultipliers(p.mults, mults); p.updateSkillLevels(); + const unnamedAug1Amt = p.sleeves.reduce( + (n, sleeve) => n + (sleeve.hasAugmentation(AugmentationNames.UnnamedAug1) ? 1 : 0), + 0, + ); + if (unnamedAug1Amt === 0) return; + // Less powerful for each copy. + const scaling = 3 / (unnamedAug1Amt + 2); + const sleeveMults = scaleMultipliers(mults, scaling); + for (const sleeve of p.sleeves) { + if (!sleeve.hasAugmentation(AugmentationNames.UnnamedAug1)) continue; + sleeve.resetMultipliers(); + sleeve.mults = mergeMultipliers(sleeve.mults, sleeveMults); + sleeve.updateStatLevels(); + } } prestigeAugmentation(): void { diff --git a/src/PersonObjects/Multipliers.ts b/src/PersonObjects/Multipliers.ts index 9a941c03d..7e7bd44b1 100644 --- a/src/PersonObjects/Multipliers.ts +++ b/src/PersonObjects/Multipliers.ts @@ -68,7 +68,7 @@ export const defaultMultipliers = (): Multipliers => { }; }; -export const mergeMultipliers = (m0: Multipliers, m1: AugmentationStats): Multipliers => { +export const mergeAugmentation = (m0: Multipliers, m1: AugmentationStats): Multipliers => { return { hacking_chance: m0.hacking_chance * (m1.hacking_chance ?? 1), hacking_speed: m0.hacking_speed * (m1.hacking_speed ?? 1), @@ -102,3 +102,73 @@ export const mergeMultipliers = (m0: Multipliers, m1: AugmentationStats): Multip bladeburner_success_chance: m0.bladeburner_success_chance * (m1.bladeburner_success_chance ?? 1), }; }; + +export const mergeMultipliers = (m0: Multipliers, m1: Multipliers): Multipliers => { + return { + hacking_chance: m0.hacking_chance * m1.hacking_chance, + hacking_speed: m0.hacking_speed * m1.hacking_speed, + hacking_money: m0.hacking_money * m1.hacking_money, + hacking_grow: m0.hacking_grow * m1.hacking_grow, + hacking: m0.hacking * m1.hacking, + hacking_exp: m0.hacking_exp * m1.hacking_exp, + strength: m0.strength * m1.strength, + strength_exp: m0.strength_exp * m1.strength_exp, + defense: m0.defense * m1.defense, + defense_exp: m0.defense_exp * m1.defense_exp, + dexterity: m0.dexterity * m1.dexterity, + dexterity_exp: m0.dexterity_exp * m1.dexterity_exp, + agility: m0.agility * m1.agility, + agility_exp: m0.agility_exp * m1.agility_exp, + charisma: m0.charisma * m1.charisma, + charisma_exp: m0.charisma_exp * m1.charisma_exp, + hacknet_node_money: m0.hacknet_node_money * m1.hacknet_node_money, + hacknet_node_purchase_cost: m0.hacknet_node_purchase_cost * m1.hacknet_node_purchase_cost, + hacknet_node_ram_cost: m0.hacknet_node_ram_cost * m1.hacknet_node_ram_cost, + hacknet_node_core_cost: m0.hacknet_node_core_cost * m1.hacknet_node_core_cost, + hacknet_node_level_cost: m0.hacknet_node_level_cost * m1.hacknet_node_level_cost, + company_rep: m0.company_rep * m1.company_rep, + faction_rep: m0.faction_rep * m1.faction_rep, + work_money: m0.work_money * m1.work_money, + crime_success: m0.crime_success * m1.crime_success, + crime_money: m0.crime_money * m1.crime_money, + bladeburner_max_stamina: m0.bladeburner_max_stamina * m1.bladeburner_max_stamina, + bladeburner_stamina_gain: m0.bladeburner_stamina_gain * m1.bladeburner_stamina_gain, + bladeburner_analysis: m0.bladeburner_analysis * m1.bladeburner_analysis, + bladeburner_success_chance: m0.bladeburner_success_chance * m1.bladeburner_success_chance, + }; +}; + +export const scaleMultipliers = (m0: Multipliers, v: number): Multipliers => { + return { + hacking_chance: (m0.hacking_chance - 1) * v + 1, + hacking_speed: (m0.hacking_speed - 1) * v + 1, + hacking_money: (m0.hacking_money - 1) * v + 1, + hacking_grow: (m0.hacking_grow - 1) * v + 1, + hacking: (m0.hacking - 1) * v + 1, + hacking_exp: (m0.hacking_exp - 1) * v + 1, + strength: (m0.strength - 1) * v + 1, + strength_exp: (m0.strength_exp - 1) * v + 1, + defense: (m0.defense - 1) * v + 1, + defense_exp: (m0.defense_exp - 1) * v + 1, + dexterity: (m0.dexterity - 1) * v + 1, + dexterity_exp: (m0.dexterity_exp - 1) * v + 1, + agility: (m0.agility - 1) * v + 1, + agility_exp: (m0.agility_exp - 1) * v + 1, + charisma: (m0.charisma - 1) * v + 1, + charisma_exp: (m0.charisma_exp - 1) * v + 1, + hacknet_node_money: (m0.hacknet_node_money - 1) * v + 1, + hacknet_node_purchase_cost: (m0.hacknet_node_purchase_cost - 1) * v + 1, + hacknet_node_ram_cost: (m0.hacknet_node_ram_cost - 1) * v + 1, + hacknet_node_core_cost: (m0.hacknet_node_core_cost - 1) * v + 1, + hacknet_node_level_cost: (m0.hacknet_node_level_cost - 1) * v + 1, + company_rep: (m0.company_rep - 1) * v + 1, + faction_rep: (m0.faction_rep - 1) * v + 1, + work_money: (m0.work_money - 1) * v + 1, + crime_success: (m0.crime_success - 1) * v + 1, + crime_money: (m0.crime_money - 1) * v + 1, + bladeburner_max_stamina: (m0.bladeburner_max_stamina - 1) * v + 1, + bladeburner_stamina_gain: (m0.bladeburner_stamina_gain - 1) * v + 1, + bladeburner_analysis: (m0.bladeburner_analysis - 1) * v + 1, + bladeburner_success_chance: (m0.bladeburner_success_chance - 1) * v + 1, + }; +}; diff --git a/src/PersonObjects/Person.ts b/src/PersonObjects/Person.ts index f76fd1026..c08cc38dd 100644 --- a/src/PersonObjects/Person.ts +++ b/src/PersonObjects/Person.ts @@ -7,7 +7,7 @@ import { CONSTANTS } from "../Constants"; import { calculateSkill } from "./formulas/skill"; import { calculateIntelligenceBonus } from "./formulas/intelligence"; import { IPerson } from "./IPerson"; -import { defaultMultipliers, mergeMultipliers } from "./Multipliers"; +import { defaultMultipliers, mergeAugmentation } from "./Multipliers"; import { Skills } from "./Skills"; import { HP } from "./HP"; @@ -61,7 +61,7 @@ export abstract class Person implements IPerson { * Updates this object's multipliers for the given augmentation */ applyAugmentation(aug: Augmentation): void { - this.mults = mergeMultipliers(this.mults, aug.mults); + this.mults = mergeAugmentation(this.mults, aug.mults); } /** diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 2deb1edef..693aa0d20 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -280,15 +280,15 @@ export class Sleeve extends Person { return true; } + hasAugmentation(aug: string): boolean { + return this.augmentations.some((a) => a.name === aug); + } + tryBuyAugmentation(p: IPlayer, aug: Augmentation): boolean { - if (!p.canAfford(aug.baseCost)) { - return false; - } + if (!p.canAfford(aug.baseCost)) return false; // Verify that this sleeve does not already have that augmentation. - if (this.augmentations.some((a) => a.name === aug.name)) { - return false; - } + if (this.hasAugmentation(aug.name)) return false; p.loseMoney(aug.baseCost, "sleeves"); this.installAugmentation(aug); @@ -296,11 +296,6 @@ export class Sleeve extends Person { } upgradeMemory(n: number): void { - if (n < 0) { - console.warn(`Sleeve.upgradeMemory() called with negative value: ${n}`); - return; - } - this.memory = Math.min(100, Math.round(this.memory + n)); } diff --git a/src/PersonObjects/Sleeve/SleeveHelpers.ts b/src/PersonObjects/Sleeve/SleeveHelpers.ts index 2a612134d..279388968 100644 --- a/src/PersonObjects/Sleeve/SleeveHelpers.ts +++ b/src/PersonObjects/Sleeve/SleeveHelpers.ts @@ -5,32 +5,24 @@ import { IPlayer } from "../IPlayer"; import { Augmentation } from "../../Augmentation/Augmentation"; import { StaticAugmentations } from "../../Augmentation/StaticAugmentations"; -import { Faction } from "../../Faction/Faction"; import { Factions } from "../../Faction/Factions"; import { Multipliers } from "../Multipliers"; +import { AugmentationNames } from "../../Augmentation/data/AugmentationNames"; export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentation[] { // You can only purchase Augmentations that are actually available from // your factions. I.e. you must be in a faction that has the Augmentation // and you must also have enough rep in that faction in order to purchase it. - const ownedAugNames: string[] = sleeve.augmentations.map((e) => { - return e.name; - }); + const ownedAugNames = sleeve.augmentations.map((e) => e.name); const availableAugs: Augmentation[] = []; // 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 (ownedAugNames.includes(aug.name)) { - return false; - } - if (availableAugs.includes(aug)) { - return false; - } - if (aug.isSpecial) { - return false; - } + if (ownedAugNames.includes(aug.name)) return false; + if (availableAugs.includes(aug)) return false; + if (aug.isSpecial) return false; type MultKey = keyof Multipliers; const validMults: MultKey[] = [ @@ -53,9 +45,7 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat "work_money", ]; for (const mult of validMults) { - if (aug.mults[mult] !== 1) { - return true; - } + if (aug.mults[mult] !== 1) return true; } return false; @@ -68,9 +58,7 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat for (const augName of Object.keys(StaticAugmentations)) { const aug = StaticAugmentations[augName]; - if (!isAvailableForSleeve(aug)) { - continue; - } + if (!isAvailableForSleeve(aug)) continue; if (fac.playerReputation > aug.getCost(p).repCost) { availableAugs.push(aug); @@ -79,22 +67,14 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat } for (const facName of p.factions) { - if (facName === FactionNames.Bladeburners) { - continue; - } - if (facName === FactionNames.Netburners) { - continue; - } - const fac: Faction | null = Factions[facName]; - if (fac == null) { - continue; - } + if (facName === FactionNames.Bladeburners) continue; + if (facName === FactionNames.Netburners) continue; + const fac = Factions[facName]; + if (!fac) continue; for (const augName of fac.augmentations) { - const aug: Augmentation = StaticAugmentations[augName]; - if (!isAvailableForSleeve(aug)) { - continue; - } + const aug = StaticAugmentations[augName]; + if (!isAvailableForSleeve(aug)) continue; if (fac.playerReputation > aug.getCost(p).repCost) { availableAugs.push(aug); @@ -102,5 +82,14 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat } } + // Add the stanek sleeve aug + if ( + !ownedAugNames.includes(AugmentationNames.UnnamedAug1) && + p.factions.includes(FactionNames.ChurchOfTheMachineGod) + ) { + const aug = StaticAugmentations[AugmentationNames.UnnamedAug1]; + availableAugs.push(aug); + } + return availableAugs; } diff --git a/src/Server/ServerHelpers.ts b/src/Server/ServerHelpers.ts index b5bc46269..5196f7261 100644 --- a/src/Server/ServerHelpers.ts +++ b/src/Server/ServerHelpers.ts @@ -23,7 +23,9 @@ export function safetlyCreateUniqueServer(params: IConstructorParams): Server { } if (GetServer(hostname) != null) { - hostname = `${hostname}-0`; + if (hostname.slice(-2) != `-0`) { + hostname = `${hostname}-0`; + } // Use a for loop to ensure that we don't get suck in an infinite loop somehow for (let i = 0; i < 200; ++i) {