NETSCRIPT: Add ns.formulas.work.companyGains function (#195)

* added ns.formulas.work.companyGains.
* Removed Work/Formulas folder, added Work/Formulas.ts
* CompanyPosition.calculateJobPerformance now takes in a Person instead of taking in a full set of stats, and it takes INT into account.
* formulas.crimeGains takes in a person object.
* Renamed ns Player type to Person.
* added multWorkStats, which multiplies a WorkStats object with a multipliers object.
* Remove unused types in NetscriptDefinitons.d.ts
* reuse formulas code in other parts of game
* getSleeveInformation also returns skills

Co-authored-by: Alexey <alexey.kozhemiakin@gmail.com>
This commit is contained in:
Snarling 2022-11-06 17:27:04 -05:00 committed by GitHub
parent 5205ff2837
commit 9e869bc876
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 241 additions and 350 deletions

@ -1,3 +1,4 @@
import { Person } from "../PersonObjects/Person";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import * as names from "./data/companypositionnames"; import * as names from "./data/companypositionnames";
@ -117,13 +118,13 @@ export class CompanyPosition {
this.charismaExpGain = p.charismaExpGain != null ? p.charismaExpGain : 0; this.charismaExpGain = p.charismaExpGain != null ? p.charismaExpGain : 0;
} }
calculateJobPerformance(hack: number, str: number, def: number, dex: number, agi: number, cha: number): number { calculateJobPerformance(worker: Person): number {
const hackRatio: number = (this.hackingEffectiveness * hack) / CONSTANTS.MaxSkillLevel; const hackRatio: number = (this.hackingEffectiveness * worker.skills.hacking) / CONSTANTS.MaxSkillLevel;
const strRatio: number = (this.strengthEffectiveness * str) / CONSTANTS.MaxSkillLevel; const strRatio: number = (this.strengthEffectiveness * worker.skills.strength) / CONSTANTS.MaxSkillLevel;
const defRatio: number = (this.defenseEffectiveness * def) / CONSTANTS.MaxSkillLevel; const defRatio: number = (this.defenseEffectiveness * worker.skills.defense) / CONSTANTS.MaxSkillLevel;
const dexRatio: number = (this.dexterityEffectiveness * dex) / CONSTANTS.MaxSkillLevel; const dexRatio: number = (this.dexterityEffectiveness * worker.skills.dexterity) / CONSTANTS.MaxSkillLevel;
const agiRatio: number = (this.agilityEffectiveness * agi) / CONSTANTS.MaxSkillLevel; const agiRatio: number = (this.agilityEffectiveness * worker.skills.agility) / CONSTANTS.MaxSkillLevel;
const chaRatio: number = (this.charismaEffectiveness * cha) / CONSTANTS.MaxSkillLevel; const chaRatio: number = (this.charismaEffectiveness * worker.skills.charisma) / CONSTANTS.MaxSkillLevel;
let reputationGain: number = let reputationGain: number =
(this.repMultiplier * (hackRatio + strRatio + defRatio + dexRatio + agiRatio + chaRatio)) / 100; (this.repMultiplier * (hackRatio + strRatio + defRatio + dexRatio + agiRatio + chaRatio)) / 100;
@ -131,7 +132,7 @@ export class CompanyPosition {
console.error("Company reputation gain calculated to be NaN"); console.error("Company reputation gain calculated to be NaN");
reputationGain = 0; reputationGain = 0;
} }
reputationGain += worker.skills.intelligence / CONSTANTS.MaxSkillLevel;
return reputationGain; return reputationGain;
} }

@ -14,7 +14,7 @@ import { Money } from "../../ui/React/Money";
import { Router } from "../../ui/GameRoot"; import { Router } from "../../ui/GameRoot";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { ClassWork, ClassType, Classes } from "../../Work/ClassWork"; import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/formulas/Class"; import { calculateCost } from "../../Work/Formulas";
type IProps = { type IProps = {
loc: Location; loc: Location;

@ -15,7 +15,7 @@ import { Player } from "@player";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { ClassWork, ClassType, Classes } from "../../Work/ClassWork"; import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/formulas/Class"; import { calculateCost } from "../../Work/Formulas";
type IProps = { type IProps = {
loc: Location; loc: Location;

@ -600,6 +600,7 @@ export const RamCosts: RamCostTree<Omit<NSFull, "args" | "enums">> = {
crimeGains: 0, crimeGains: 0,
classGains: 0, classGains: 0,
factionGains: 0, factionGains: 0,
companyGains: 0,
}, },
}, },
} as const; } as const;

@ -39,17 +39,20 @@ import { favorToRep as calculateFavorToRep, repToFavor as calculateRepToFavor }
import { repFromDonation } from "../Faction/formulas/donation"; import { repFromDonation } from "../Faction/formulas/donation";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { calculateCrimeWorkStats } from "../Work/formulas/Crime"; import { calculateCrimeWorkStats } from "../Work/Formulas";
import { Crimes } from "../Crime/Crimes"; import { Crimes } from "../Crime/Crimes";
import { calculateClassEarnings } from "../Work/formulas/Class"; import { calculateCompanyWorkStats } from "../Work/Formulas";
import { Companies } from "../Company/Companies";
import { calculateClassEarnings } from "../Work/Formulas";
import { ClassType } from "../Work/ClassWork"; import { ClassType } from "../Work/ClassWork";
import { LocationName } from "../Locations/data/LocationNames"; import { LocationName } from "../Locations/data/LocationNames";
import { calculateFactionExp, calculateFactionRep } from "../Work/formulas/Faction"; import { calculateFactionExp, calculateFactionRep } from "../Work/Formulas";
import { FactionWorkType } from "../Work/data/FactionWorkType"; import { FactionWorkType } from "../Work/data/FactionWorkType";
import { defaultMultipliers } from "../PersonObjects/Multipliers"; import { defaultMultipliers } from "../PersonObjects/Multipliers";
import { checkEnum } from "../utils/helpers/checkEnum"; import { checkEnum } from "../utils/helpers/checkEnum";
import { CrimeType } from "../utils/WorkType"; import { CrimeType } from "../utils/WorkType";
import { CompanyPositions } from "../Company/CompanyPositions";
export function NetscriptFormulas(): InternalAPI<IFormulas> { export function NetscriptFormulas(): InternalAPI<IFormulas> {
const checkFormulasAccess = function (ctx: NetscriptContext): void { const checkFormulasAccess = function (ctx: NetscriptContext): void {
@ -362,10 +365,11 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
}, },
}, },
work: { work: {
crimeGains: (ctx) => (_crimeType) => { crimeGains: (ctx) => (_person, _crimeType) => {
const person = helpers.player(ctx, _person);
const crimeType = helpers.string(ctx, "crimeType", _crimeType); const crimeType = helpers.string(ctx, "crimeType", _crimeType);
if (!checkEnum(CrimeType, crimeType)) throw new Error(`Invalid crime type: ${crimeType}`); if (!checkEnum(CrimeType, crimeType)) throw new Error(`Invalid crime type: ${crimeType}`);
return calculateCrimeWorkStats(Crimes[crimeType]); return calculateCrimeWorkStats(person, Crimes[crimeType]);
}, },
classGains: (ctx) => (_person, _classType, _locationName) => { classGains: (ctx) => (_person, _classType, _locationName) => {
const person = helpers.player(ctx, _person); const person = helpers.player(ctx, _person);
@ -382,10 +386,22 @@ export function NetscriptFormulas(): InternalAPI<IFormulas> {
exp.reputation = rep; exp.reputation = rep;
return exp; return exp;
}, },
// companyGains: (ctx) => (_player) {
// const player = helpers.player(ctx, _player);
// }, companyGains: (ctx) => (_player, _companyName, _positionName, _favor) => {
const player = helpers.player(ctx, _player);
CompanyPositions;
const positionName = helpers.string(ctx, "_positionName", _positionName);
const position = Object.values(CompanyPositions).find((c) => c.name === positionName);
if (!position) throw new Error(`Invalid position name: ${positionName}`);
const companyName = helpers.string(ctx, "_companyName", _companyName);
const company = Object.values(Companies).find((c) => c.name === companyName);
if (!company) throw new Error(`Invalid company name: ${companyName}`);
const favor = helpers.number(ctx, "favor", _favor);
return calculateCompanyWorkStats(player, company, position, favor);
},
}, },
}; };
} }

@ -49,7 +49,7 @@ import { FactionWorkType } from "../Work/data/FactionWorkType";
import { CompanyWork } from "../Work/CompanyWork"; import { CompanyWork } from "../Work/CompanyWork";
import { canGetBonus, onExport } from "../ExportBonus"; import { canGetBonus, onExport } from "../ExportBonus";
import { saveObject } from "../SaveObject"; import { saveObject } from "../SaveObject";
import { calculateCrimeWorkStats } from "../Work/formulas/Crime"; import { calculateCrimeWorkStats } from "../Work/Formulas";
import { checkEnum } from "../utils/helpers/checkEnum"; import { checkEnum } from "../utils/helpers/checkEnum";
import { Crimes } from "../Crime/Crimes"; import { Crimes } from "../Crime/Crimes";
import { CrimeType } from "../utils/WorkType"; import { CrimeType } from "../utils/WorkType";
@ -1160,7 +1160,7 @@ export function NetscriptSingularity(): InternalAPI<ISingularity> {
const crime = checkEnum(CrimeType, crimeType) ? Crimes[crimeType] : findCrime(crimeType); const crime = checkEnum(CrimeType, crimeType) ? Crimes[crimeType] : findCrime(crimeType);
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`); if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
const crimeStatsWithMultipliers = calculateCrimeWorkStats(crime); const crimeStatsWithMultipliers = calculateCrimeWorkStats(Player, crime);
return Object.assign({}, crime, { return Object.assign({}, crime, {
money: crimeStatsWithMultipliers.money, money: crimeStatsWithMultipliers.money,

@ -13,6 +13,7 @@ import { isSleeveCompanyWork } from "../PersonObjects/Sleeve/Work/SleeveCompanyW
import { helpers } from "../Netscript/NetscriptHelpers"; import { helpers } from "../Netscript/NetscriptHelpers";
import { Crimes } from "../Crime/Crimes"; import { Crimes } from "../Crime/Crimes";
import { CrimeType } from "../utils/WorkType"; import { CrimeType } from "../utils/WorkType";
import { cloneDeep } from "lodash";
export function NetscriptSleeve(): InternalAPI<ISleeve> { export function NetscriptSleeve(): InternalAPI<ISleeve> {
const checkSleeveAPIAccess = function (ctx: NetscriptContext) { const checkSleeveAPIAccess = function (ctx: NetscriptContext) {
@ -200,6 +201,7 @@ export function NetscriptSleeve(): InternalAPI<ISleeve> {
strengthExp: sl.mults.strength_exp, strengthExp: sl.mults.strength_exp,
workMoney: sl.mults.work_money, workMoney: sl.mults.work_money,
}, },
skills: cloneDeep(sl.skills),
}; };
}, },
getSleeveAugmentations: (ctx) => (_sleeveNumber) => { getSleeveAugmentations: (ctx) => (_sleeveNumber) => {

@ -2,7 +2,7 @@ import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../../
import { applySleeveGains, Work, WorkType } from "./Work"; import { applySleeveGains, Work, WorkType } from "./Work";
import { ClassType } from "../../../Work/ClassWork"; import { ClassType } from "../../../Work/ClassWork";
import { LocationName } from "../../../Locations/data/LocationNames"; import { LocationName } from "../../../Locations/data/LocationNames";
import { calculateClassEarnings } from "../../../Work/formulas/Class"; import { calculateClassEarnings } from "../../../Work/Formulas";
import { Sleeve } from "../Sleeve"; import { Sleeve } from "../Sleeve";
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats"; import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";

@ -4,9 +4,11 @@ import { applySleeveGains, Work, WorkType } from "./Work";
import { LocationName } from "../../../Locations/data/LocationNames"; import { LocationName } from "../../../Locations/data/LocationNames";
import { Companies } from "../../../Company/Companies"; import { Companies } from "../../../Company/Companies";
import { Company } from "../../../Company/Company"; import { Company } from "../../../Company/Company";
import { calculateCompanyWorkStats } from "../../../Work/formulas/Company"; import { calculateCompanyWorkStats } from "../../../Work/Formulas";
import { WorkStats } from "../../../Work/WorkStats"; import { WorkStats } from "../../../Work/WorkStats";
import { influenceStockThroughCompanyWork } from "../../../StockMarket/PlayerInfluencing"; import { influenceStockThroughCompanyWork } from "../../../StockMarket/PlayerInfluencing";
import { Player } from "@player";
import { CompanyPositions } from "../../../Company/CompanyPositions";
interface SleeveCompanyWorkParams { interface SleeveCompanyWorkParams {
companyName: string; companyName: string;
@ -30,7 +32,8 @@ export class SleeveCompanyWork extends Work {
} }
getGainRates(sleeve: Sleeve): WorkStats { getGainRates(sleeve: Sleeve): WorkStats {
return calculateCompanyWorkStats(sleeve, this.getCompany()); const company = this.getCompany();
return calculateCompanyWorkStats(sleeve, company, CompanyPositions[Player.jobs[company.name]], company.favor);
} }
process(sleeve: Sleeve, cycles: number): number { process(sleeve: Sleeve, cycles: number): number {

@ -5,10 +5,10 @@ import { applySleeveGains, Work, WorkType } from "./Work";
import { CrimeType } from "../../../utils/WorkType"; import { CrimeType } from "../../../utils/WorkType";
import { Crimes } from "../../../Crime/Crimes"; import { Crimes } from "../../../Crime/Crimes";
import { Crime } from "../../../Crime/Crime"; import { Crime } from "../../../Crime/Crime";
import { newWorkStats, scaleWorkStats, WorkStats } from "../../../Work/WorkStats"; import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { CONSTANTS } from "../../../Constants"; import { CONSTANTS } from "../../../Constants";
import { BitNodeMultipliers } from "../../../BitNode/BitNodeMultipliers";
import { checkEnum } from "../../../utils/helpers/checkEnum"; import { checkEnum } from "../../../utils/helpers/checkEnum";
import { calculateCrimeWorkStats } from "../../../Work/Formulas";
export const isSleeveCrimeWork = (w: Work | null): w is SleeveCrimeWork => w !== null && w.type === WorkType.CRIME; export const isSleeveCrimeWork = (w: Work | null): w is SleeveCrimeWork => w !== null && w.type === WorkType.CRIME;
@ -26,17 +26,7 @@ export class SleeveCrimeWork extends Work {
} }
getExp(sleeve: Sleeve): WorkStats { getExp(sleeve: Sleeve): WorkStats {
const crime = this.getCrime(); return calculateCrimeWorkStats(sleeve, this.getCrime());
return newWorkStats({
money: crime.money * BitNodeMultipliers.CrimeMoney * sleeve.mults.crime_money,
hackExp: crime.hacking_exp * BitNodeMultipliers.CrimeExpGain * sleeve.mults.hacking_exp,
strExp: crime.strength_exp * BitNodeMultipliers.CrimeExpGain * sleeve.mults.strength_exp,
defExp: crime.defense_exp * BitNodeMultipliers.CrimeExpGain * sleeve.mults.defense_exp,
dexExp: crime.dexterity_exp * BitNodeMultipliers.CrimeExpGain * sleeve.mults.dexterity_exp,
agiExp: crime.agility_exp * BitNodeMultipliers.CrimeExpGain * sleeve.mults.agility_exp,
chaExp: crime.charisma_exp * BitNodeMultipliers.CrimeExpGain * sleeve.mults.charisma_exp,
intExp: crime.intelligence_exp * BitNodeMultipliers.CrimeExpGain,
});
} }
cyclesNeeded(): number { cyclesNeeded(): number {

@ -5,15 +5,9 @@ import { applySleeveGains, Work, WorkType } from "./Work";
import { FactionWorkType } from "../../../Work/data/FactionWorkType"; import { FactionWorkType } from "../../../Work/data/FactionWorkType";
import { FactionNames } from "../../../Faction/data/FactionNames"; import { FactionNames } from "../../../Faction/data/FactionNames";
import { Factions } from "../../../Faction/Factions"; import { Factions } from "../../../Faction/Factions";
import { calculateFactionExp } from "../../../Work/formulas/Faction"; import { calculateFactionExp, calculateFactionRep } from "../../../Work/Formulas";
import { Faction } from "../../../Faction/Faction"; import { Faction } from "../../../Faction/Faction";
import {
getFactionFieldWorkRepGain,
getFactionSecurityWorkRepGain,
getHackingWorkRepGain,
} from "../../../PersonObjects/formulas/reputation";
import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats"; import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats";
import { BitNodeMultipliers } from "../../../BitNode/BitNodeMultipliers";
interface SleeveFactionWorkParams { interface SleeveFactionWorkParams {
factionWorkType: FactionWorkType; factionWorkType: FactionWorkType;
@ -38,17 +32,7 @@ export class SleeveFactionWork extends Work {
} }
getReputationRate(sleeve: Sleeve): number { getReputationRate(sleeve: Sleeve): number {
const faction = this.getFaction(); return calculateFactionRep(sleeve, this.factionWorkType, this.getFaction().favor) * sleeve.shockBonus();
const repFormulas = {
[FactionWorkType.HACKING]: getHackingWorkRepGain,
[FactionWorkType.FIELD]: getFactionFieldWorkRepGain,
[FactionWorkType.SECURITY]: getFactionSecurityWorkRepGain,
};
return (
repFormulas[this.factionWorkType](sleeve, faction.favor) *
sleeve.shockBonus() *
BitNodeMultipliers.FactionWorkRepGain
);
} }
getFaction(): Faction { getFaction(): Faction {

@ -37,7 +37,7 @@ type ScriptArg = string | number | boolean;
type FilenameOrPID = number | string; type FilenameOrPID = number | string;
/** @public */ /** @public */
interface Player { interface Person {
hp: HP; hp: HP;
skills: Skills; skills: Skills;
exp: Skills; exp: Skills;
@ -641,62 +641,6 @@ export interface NodeStats {
totalProduction: number; totalProduction: number;
} }
/** @public */
export interface CharacterMult {
/** Agility stat */
agility: number;
/** Agility exp */
agilityExp: number;
/** Charisma stat */
charisma: number;
/** Charisma exp */
charismaExp: number;
/** Company reputation */
companyRep: number;
/** Money earned from crimes */
crimeMoney: number;
/** Crime success chance */
crimeSuccess: number;
/** Defense stat */
defense: number;
/** Defense exp */
defenseExp: number;
/** Dexterity stat */
dexterity: number;
/** Dexterity exp */
dexterityExp: number;
/** Faction reputation */
factionRep: number;
/** Hacking stat */
hacking: number;
/** Hacking exp */
hackingExp: number;
/** Strength stat */
strength: number;
/** Strength exp */
strengthExp: number;
/** Money earned from jobs */
workMoney: number;
}
/** @public */
export interface SleeveWorkGains {
/** Hacking exp gained from work */
workHackExpGain: number;
/** Strength exp gained from work */
workStrExpGain: number;
/** Defense exp gained from work, */
workDefExpGain: number;
/** Dexterity exp gained from work */
workDexExpGain: number;
/** Agility exp gained from work */
workAgiExpGain: number;
/** Charisma exp gained from work */
workChaExpGain: number;
/** Money gained from work */
workMoneyGain: number;
}
/** @public */ /** @public */
export interface SourceFileLvl { export interface SourceFileLvl {
/** The number of the source file */ /** The number of the source file */
@ -966,7 +910,9 @@ export interface SleeveInformation {
/** Does this sleeve have access to the tor router */ /** Does this sleeve have access to the tor router */
tor: boolean; tor: boolean;
/** Sleeve multipliers */ /** Sleeve multipliers */
mult: CharacterMult; mult: Multipliers;
/** Sleeve skills */
skills: Skills;
} }
/** /**
@ -3907,9 +3853,10 @@ export interface WorkStats {
* @public * @public
*/ */
interface WorkFormulas { interface WorkFormulas {
crimeGains(crimeType: CrimeType | CrimeNames): WorkStats; crimeGains(person: Person, crimeType: CrimeType | CrimeNames): WorkStats;
classGains(player: Player, classType: string, locationName: string): WorkStats; classGains(person: Person, classType: string, locationName: string): WorkStats;
factionGains(player: Player, workType: string, favor: number): WorkStats; factionGains(person: Person, workType: string, favor: number): WorkStats;
companyGains(person: Person, companyName: string, workType: string, favor: number): WorkStats;
} }
/** /**
@ -3936,7 +3883,7 @@ interface ReputationFormulas {
* @param amount - Amount of money donated * @param amount - Amount of money donated
* @param player - Player info from {@link NS.getPlayer | getPlayer} * @param player - Player info from {@link NS.getPlayer | getPlayer}
*/ */
repFromDonation(amount: number, player: Player): number; repFromDonation(amount: number, player: Person): number;
} }
/** /**
@ -3951,7 +3898,7 @@ interface HackingFormulas {
* @param player - Player info from {@link NS.getPlayer | getPlayer} * @param player - Player info from {@link NS.getPlayer | getPlayer}
* @returns The calculated hack chance. * @returns The calculated hack chance.
*/ */
hackChance(server: Server, player: Player): number; hackChance(server: Server, player: Person): number;
/** /**
* Calculate hack exp for one thread. * Calculate hack exp for one thread.
* @remarks * @remarks
@ -3960,7 +3907,7 @@ interface HackingFormulas {
* @param player - Player info from {@link NS.getPlayer | getPlayer} * @param player - Player info from {@link NS.getPlayer | getPlayer}
* @returns The calculated hack exp. * @returns The calculated hack exp.
*/ */
hackExp(server: Server, player: Player): number; hackExp(server: Server, player: Person): number;
/** /**
* Calculate hack percent for one thread. * Calculate hack percent for one thread.
* (Ex: 0.25 would steal 25% of the server's current value.) * (Ex: 0.25 would steal 25% of the server's current value.)
@ -3970,7 +3917,7 @@ interface HackingFormulas {
* @param player - Player info from {@link NS.getPlayer | getPlayer} * @param player - Player info from {@link NS.getPlayer | getPlayer}
* @returns The calculated hack percent. * @returns The calculated hack percent.
*/ */
hackPercent(server: Server, player: Player): number; hackPercent(server: Server, player: Person): number;
/** /**
* Calculate the percent a server would grow to. * Calculate the percent a server would grow to.
* (Ex: 3.0 would would grow the server to 300% of its current value.) * (Ex: 3.0 would would grow the server to 300% of its current value.)
@ -3980,28 +3927,28 @@ interface HackingFormulas {
* @param cores - Number of cores on the computer that will execute grow. * @param cores - Number of cores on the computer that will execute grow.
* @returns The calculated grow percent. * @returns The calculated grow percent.
*/ */
growPercent(server: Server, threads: number, player: Player, cores?: number): number; growPercent(server: Server, threads: number, player: Person, cores?: number): number;
/** /**
* Calculate hack time. * Calculate hack time.
* @param server - Server info from {@link NS.getServer | getServer} * @param server - Server info from {@link NS.getServer | getServer}
* @param player - Player info from {@link NS.getPlayer | getPlayer} * @param player - Player info from {@link NS.getPlayer | getPlayer}
* @returns The calculated hack time. * @returns The calculated hack time.
*/ */
hackTime(server: Server, player: Player): number; hackTime(server: Server, player: Person): number;
/** /**
* Calculate grow time. * Calculate grow time.
* @param server - Server info from {@link NS.getServer | getServer} * @param server - Server info from {@link NS.getServer | getServer}
* @param player - Player info from {@link NS.getPlayer | getPlayer} * @param player - Player info from {@link NS.getPlayer | getPlayer}
* @returns The calculated grow time. * @returns The calculated grow time.
*/ */
growTime(server: Server, player: Player): number; growTime(server: Server, player: Person): number;
/** /**
* Calculate weaken time. * Calculate weaken time.
* @param server - Server info from {@link NS.getServer | getServer} * @param server - Server info from {@link NS.getServer | getServer}
* @param player - Player info from {@link NS.getPlayer | getPlayer} * @param player - Player info from {@link NS.getPlayer | getPlayer}
* @returns The calculated weaken time. * @returns The calculated weaken time.
*/ */
weakenTime(server: Server, player: Player): number; weakenTime(server: Server, player: Person): number;
} }
/** /**
@ -4182,7 +4129,7 @@ interface GangFormulas {
*/ */
export interface Formulas { export interface Formulas {
mockServer(): Server; mockServer(): Server;
mockPlayer(): Player; mockPlayer(): Person;
/** Reputation formulas */ /** Reputation formulas */
reputation: ReputationFormulas; reputation: ReputationFormulas;
/** Skills formulas */ /** Skills formulas */
@ -6758,7 +6705,7 @@ export interface NS {
* *
* @returns Player info * @returns Player info
*/ */
getPlayer(): Player; getPlayer(): Person;
/** /**
* Get information about the sources of income for this run. * Get information about the sources of income for this run.

@ -7,7 +7,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Money } from "../ui/React/Money"; import { Money } from "../ui/React/Money";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
import { Player } from "@player"; import { Player } from "@player";
import { calculateClassEarnings as calculateClassEarningsRate } from "./formulas/Class"; import { calculateClassEarnings as calculateClassEarningsRate } from "./Formulas";
import { Work, WorkType } from "./Work"; import { Work, WorkType } from "./Work";
import { applyWorkStats, newWorkStats, sumWorkStats, WorkStats } from "./WorkStats"; import { applyWorkStats, newWorkStats, sumWorkStats, WorkStats } from "./WorkStats";

@ -4,7 +4,7 @@ import { Player } from "@player";
import { Work, WorkType } from "./Work"; import { Work, WorkType } from "./Work";
import { influenceStockThroughCompanyWork } from "../StockMarket/PlayerInfluencing"; import { influenceStockThroughCompanyWork } from "../StockMarket/PlayerInfluencing";
import { LocationName } from "../Locations/data/LocationNames"; import { LocationName } from "../Locations/data/LocationNames";
import { calculateCompanyWorkStats } from "./formulas/Company"; import { calculateCompanyWorkStats } from "./Formulas";
import { Companies } from "../Company/Companies"; import { Companies } from "../Company/Companies";
import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats"; import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats";
import { Company } from "../Company/Company"; import { Company } from "../Company/Company";
@ -12,6 +12,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation"; import { Reputation } from "../ui/React/Reputation";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { CompanyPositions } from "../Company/CompanyPositions";
interface CompanyWorkParams { interface CompanyWorkParams {
companyName: string; companyName: string;
@ -38,7 +39,11 @@ export class CompanyWork extends Work {
if (!Player.hasAugmentation(AugmentationNames.NeuroreceptorManager, true)) { if (!Player.hasAugmentation(AugmentationNames.NeuroreceptorManager, true)) {
focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus; focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus;
} }
return scaleWorkStats(calculateCompanyWorkStats(Player, this.getCompany()), focusBonus); const company = this.getCompany();
return scaleWorkStats(
calculateCompanyWorkStats(Player, company, CompanyPositions[Player.jobs[company.name]], company.favor),
focusBonus,
);
} }
process(cycles: number): boolean { process(cycles: number): boolean {

@ -8,7 +8,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { CrimeType } from "../utils/WorkType"; import { CrimeType } from "../utils/WorkType";
import { Work, WorkType } from "./Work"; import { Work, WorkType } from "./Work";
import { scaleWorkStats, WorkStats } from "./WorkStats"; import { scaleWorkStats, WorkStats } from "./WorkStats";
import { calculateCrimeWorkStats } from "./formulas/Crime"; import { calculateCrimeWorkStats } from "./Formulas";
import { checkEnum } from "../utils/helpers/checkEnum"; import { checkEnum } from "../utils/helpers/checkEnum";
interface CrimeWorkParams { interface CrimeWorkParams {
@ -47,7 +47,7 @@ export class CrimeWork extends Work {
} }
earnings(): WorkStats { earnings(): WorkStats {
return calculateCrimeWorkStats(this.getCrime()); return calculateCrimeWorkStats(Player, this.getCrime());
} }
commit(): void { commit(): void {

@ -10,7 +10,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reputation } from "../ui/React/Reputation"; import { Reputation } from "../ui/React/Reputation";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames"; import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { calculateFactionExp, calculateFactionRep } from "./formulas/Faction"; import { calculateFactionExp, calculateFactionRep } from "./Formulas";
import { FactionWorkType } from "./data/FactionWorkType"; import { FactionWorkType } from "./data/FactionWorkType";
interface FactionWorkParams { interface FactionWorkParams {

142
src/Work/Formulas.ts Normal file

@ -0,0 +1,142 @@
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Crime } from "../Crime/Crime";
import { newWorkStats, scaleWorkStats, WorkStats, multWorkStats } from "./WorkStats";
import { Person } from "../PersonObjects/Person";
import { CONSTANTS } from "../Constants";
import { FactionWorkType } from "./data/FactionWorkType";
import {
getFactionFieldWorkRepGain,
getFactionSecurityWorkRepGain,
getHackingWorkRepGain,
} from "../PersonObjects/formulas/reputation";
import { Locations } from "../Locations/Locations";
import { Location } from "../Locations/Location";
import { Player } from "@player";
import { Class, Classes, ClassType } from "./ClassWork";
import { Server } from "../Server/Server";
import { GetServer } from "../Server/AllServers";
import { serverMetadata } from "../Server/data/servers";
import { LocationName } from "../Locations/data/LocationNames";
import { Company } from "../Company/Company";
import { CompanyPosition } from "../Company/CompanyPosition";
const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second
export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
[FactionWorkType.HACKING]: newWorkStats({ hackExp: 15 }),
[FactionWorkType.FIELD]: newWorkStats({
hackExp: 10,
strExp: 10,
defExp: 10,
dexExp: 10,
agiExp: 10,
chaExp: 10,
}),
[FactionWorkType.SECURITY]: newWorkStats({
hackExp: 5,
strExp: 15,
defExp: 15,
dexExp: 15,
agiExp: 15,
}),
};
export function calculateCrimeWorkStats(person: Person, crime: Crime): WorkStats {
const gains = scaleWorkStats(
multWorkStats(
//Todo: rework crime and workstats interfaces to use the same naming convention for exp values, then we can just make a workStats directly from a crime.
newWorkStats({
money: crime.money,
hackExp: crime.hacking_exp,
strExp: crime.strength_exp,
defExp: crime.defense_exp,
dexExp: crime.dexterity_exp,
agiExp: crime.agility_exp,
chaExp: crime.charisma_exp,
intExp: crime.intelligence_exp,
}),
person.mults,
person.mults.crime_money * BitNodeMultipliers.CrimeMoney,
),
BitNodeMultipliers.CrimeExpGain,
false,
);
return gains;
}
export const calculateFactionRep = (person: Person, type: FactionWorkType, favor: number): number => {
const repFormulas = {
[FactionWorkType.HACKING]: getHackingWorkRepGain,
[FactionWorkType.FIELD]: getFactionFieldWorkRepGain,
[FactionWorkType.SECURITY]: getFactionSecurityWorkRepGain,
};
return repFormulas[type](person, favor);
};
export function calculateFactionExp(person: Person, type: FactionWorkType): WorkStats {
return scaleWorkStats(
multWorkStats(FactionWorkStats[type], person.mults),
BitNodeMultipliers.FactionWorkExpGain / gameCPS,
);
}
/** Calculate cost for a class */
export function calculateCost(classs: Class, location: Location): number {
const serverMeta = serverMetadata.find((s) => s.specialName === location.name);
const server = GetServer(serverMeta ? serverMeta.hostname : "");
const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
return classs.earnings.money * location.costMult * discount;
}
export function calculateClassEarnings(person: Person, type: ClassType, locationName: LocationName): WorkStats {
const hashManager = Player.hashManager;
const classs = Classes[type];
const location = Locations[locationName];
const hashMult = [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymStrength, ClassType.GymDexterity].includes(
type,
)
? hashManager.getTrainingMult()
: hashManager.getStudyMult();
const earnings = multWorkStats(
scaleWorkStats(classs.earnings, (location.expMult / gameCPS) * hashMult, false),
person.mults,
);
earnings.money = calculateCost(classs, location) / gameCPS;
return earnings;
}
export const calculateCompanyWorkStats = (
worker: Person,
company: Company,
companyPosition: CompanyPosition,
favor: number,
): WorkStats => {
// If player has SF-11, calculate salary multiplier from favor
const favorMult = isNaN(favor) ? 1 : 1 + favor / 100;
const bn11Mult = Player.sourceFileLvl(11) > 0 ? favorMult : 1;
const gains = scaleWorkStats(
multWorkStats(
{
money: companyPosition.baseSalary * company.salaryMultiplier * bn11Mult * BitNodeMultipliers.CompanyWorkMoney,
hackExp: companyPosition.hackingExpGain,
strExp: companyPosition.strengthExpGain,
defExp: companyPosition.defenseExpGain,
dexExp: companyPosition.dexterityExpGain,
agiExp: companyPosition.agilityExpGain,
chaExp: companyPosition.charismaExpGain,
},
worker.mults,
worker.mults.work_money,
),
company.expMultiplier * BitNodeMultipliers.CompanyWorkExpGain,
false,
);
const jobPerformance = companyPosition.calculateJobPerformance(worker);
gains.reputation = jobPerformance * worker.mults.company_rep * favorMult;
return gains;
};

@ -1,5 +1,6 @@
import { Person } from "src/PersonObjects/Person"; import { Person } from "src/PersonObjects/Person";
import { Player } from "@player"; import { Player } from "@player";
import { Multipliers } from "../PersonObjects/Multipliers";
export interface WorkStats { export interface WorkStats {
money: number; money: number;
@ -13,19 +14,7 @@ export interface WorkStats {
intExp: number; intExp: number;
} }
interface newWorkStatsParams { export const newWorkStats = (params?: Partial<WorkStats>): WorkStats => {
money?: number;
reputation?: number;
hackExp?: number;
strExp?: number;
defExp?: number;
dexExp?: number;
agiExp?: number;
chaExp?: number;
intExp?: number;
}
export const newWorkStats = (params?: newWorkStatsParams): WorkStats => {
return { return {
money: params?.money ?? 0, money: params?.money ?? 0,
reputation: params?.reputation ?? 0, reputation: params?.reputation ?? 0,
@ -39,6 +28,7 @@ export const newWorkStats = (params?: newWorkStatsParams): WorkStats => {
}; };
}; };
/** Add two workStats objects */
export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => { export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => {
return { return {
money: w0.money + w1.money, money: w0.money + w1.money,
@ -53,6 +43,7 @@ export const sumWorkStats = (w0: WorkStats, w1: WorkStats): WorkStats => {
}; };
}; };
/** Scale all stats on a WorkStats object by a number. Money scaling optional but defaults to true. */
export const scaleWorkStats = (w: WorkStats, n: number, scaleMoney = true): WorkStats => { export const scaleWorkStats = (w: WorkStats, n: number, scaleMoney = true): WorkStats => {
const m = scaleMoney ? n : 1; const m = scaleMoney ? n : 1;
return { return {
@ -107,3 +98,18 @@ export const applyWorkStatsExp = (target: Person, workStats: WorkStats, cycles:
target.gainIntelligenceExp(gains.intExp); target.gainIntelligenceExp(gains.intExp);
return gains; return gains;
}; };
/** Calculate the application of a person's multipliers to a WorkStats object */
export function multWorkStats(workStats: Partial<WorkStats>, mults: Multipliers, moneyMult = 1, repMult = 1) {
return {
money: (workStats.money ?? 0) * moneyMult,
reputation: (workStats.reputation ?? 0) * repMult,
hackExp: (workStats.hackExp ?? 0) * mults.hacking_exp,
strExp: (workStats.strExp ?? 0) * mults.strength_exp,
defExp: (workStats.defExp ?? 0) * mults.defense_exp,
dexExp: (workStats.dexExp ?? 0) * mults.dexterity_exp,
agiExp: (workStats.agiExp ?? 0) * mults.agility_exp,
chaExp: (workStats.chaExp ?? 0) * mults.charisma_exp,
intExp: workStats.intExp ?? 0,
};
}

@ -1,53 +0,0 @@
import { Locations } from "../../Locations/Locations";
import { Location } from "../../Locations/Location";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../../Constants";
import { Player } from "@player";
import { Class, Classes, ClassType } from "../ClassWork";
import { WorkStats } from "../WorkStats";
import { Server } from "../../Server/Server";
import { GetServer } from "../../Server/AllServers";
import { serverMetadata } from "../../Server/data/servers";
import { Person } from "../../PersonObjects/Person";
import { LocationName } from "../../Locations/data/LocationNames";
const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second
export function calculateCost(classs: Class, location: Location): number {
const serverMeta = serverMetadata.find((s) => s.specialName === location.name);
const server = GetServer(serverMeta ? serverMeta.hostname : "");
const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
return classs.earnings.money * location.costMult * discount;
}
export function calculateClassEarnings(person: Person, type: ClassType, locationName: LocationName): WorkStats {
//Find cost and exp gain per game cycle
const hashManager = Player.hashManager;
const classs = Classes[type];
const location = Locations[locationName];
const hashMult = [ClassType.GymAgility, ClassType.GymDefense, ClassType.GymStrength, ClassType.GymDexterity].includes(
type,
)
? hashManager.getTrainingMult()
: hashManager.getStudyMult();
const cost = calculateCost(classs, location) / gameCPS;
const hackExp = ((classs.earnings.hackExp * location.expMult) / gameCPS) * hashMult;
const strExp = ((classs.earnings.strExp * location.expMult) / gameCPS) * hashMult;
const defExp = ((classs.earnings.defExp * location.expMult) / gameCPS) * hashMult;
const dexExp = ((classs.earnings.dexExp * location.expMult) / gameCPS) * hashMult;
const agiExp = ((classs.earnings.agiExp * location.expMult) / gameCPS) * hashMult;
const chaExp = ((classs.earnings.chaExp * location.expMult) / gameCPS) * hashMult;
return {
money: cost,
reputation: 0,
hackExp: hackExp * person.mults.hacking_exp * BitNodeMultipliers.ClassGymExpGain,
strExp: strExp * person.mults.strength_exp * BitNodeMultipliers.ClassGymExpGain,
defExp: defExp * person.mults.defense_exp * BitNodeMultipliers.ClassGymExpGain,
dexExp: dexExp * person.mults.dexterity_exp * BitNodeMultipliers.ClassGymExpGain,
agiExp: agiExp * person.mults.agility_exp * BitNodeMultipliers.ClassGymExpGain,
chaExp: chaExp * person.mults.charisma_exp * BitNodeMultipliers.ClassGymExpGain,
intExp: 0,
};
}

@ -1,75 +0,0 @@
import { CompanyPositions } from "../../Company/CompanyPositions";
import { Company } from "../../Company/Company";
import { Player } from "@player";
import { WorkStats } from "../WorkStats";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../../Constants";
import { Person } from "../../PersonObjects/Person";
export const calculateCompanyWorkStats = (worker: Person, company: Company): WorkStats => {
const companyPositionName = Player.jobs[company.name];
const companyPosition = CompanyPositions[companyPositionName];
// If player has SF-11, calculate salary multiplier from favor
let favorMult = 1 + company.favor / 100;
if (isNaN(favorMult)) {
favorMult = 1;
}
let bn11Mult = 1;
if (Player.sourceFileLvl(11) > 0) {
bn11Mult = favorMult;
}
let jobPerformance = companyPosition.calculateJobPerformance(
worker.skills.hacking,
worker.skills.strength,
worker.skills.defense,
worker.skills.dexterity,
worker.skills.agility,
worker.skills.charisma,
);
jobPerformance += worker.skills.intelligence / CONSTANTS.MaxSkillLevel;
return {
money:
companyPosition.baseSalary *
company.salaryMultiplier *
worker.mults.work_money *
BitNodeMultipliers.CompanyWorkMoney *
bn11Mult,
reputation: jobPerformance * worker.mults.company_rep * favorMult,
hackExp:
companyPosition.hackingExpGain *
company.expMultiplier *
worker.mults.hacking_exp *
BitNodeMultipliers.CompanyWorkExpGain,
strExp:
companyPosition.strengthExpGain *
company.expMultiplier *
worker.mults.strength_exp *
BitNodeMultipliers.CompanyWorkExpGain,
defExp:
companyPosition.defenseExpGain *
company.expMultiplier *
worker.mults.defense_exp *
BitNodeMultipliers.CompanyWorkExpGain,
dexExp:
companyPosition.dexterityExpGain *
company.expMultiplier *
worker.mults.dexterity_exp *
BitNodeMultipliers.CompanyWorkExpGain,
agiExp:
companyPosition.agilityExpGain *
company.expMultiplier *
worker.mults.agility_exp *
BitNodeMultipliers.CompanyWorkExpGain,
chaExp:
companyPosition.charismaExpGain *
company.expMultiplier *
worker.mults.charisma_exp *
BitNodeMultipliers.CompanyWorkExpGain,
intExp: 0,
};
};

@ -1,23 +0,0 @@
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Crime } from "src/Crime/Crime";
import { newWorkStats, scaleWorkStats, WorkStats } from "../WorkStats";
import { Player } from "@player";
export const calculateCrimeWorkStats = (crime: Crime): WorkStats => {
const gains = scaleWorkStats(
newWorkStats({
money: crime.money * Player.mults.crime_money,
hackExp: crime.hacking_exp * 2 * Player.mults.hacking_exp,
strExp: crime.strength_exp * 2 * Player.mults.strength_exp,
defExp: crime.defense_exp * 2 * Player.mults.defense_exp,
dexExp: crime.dexterity_exp * 2 * Player.mults.dexterity_exp,
agiExp: crime.agility_exp * 2 * Player.mults.agility_exp,
chaExp: crime.charisma_exp * 2 * Player.mults.charisma_exp,
intExp: crime.intelligence_exp * 2,
}),
BitNodeMultipliers.CrimeExpGain,
false,
);
gains.money *= BitNodeMultipliers.CrimeMoney;
return gains;
};

@ -1,55 +0,0 @@
import { Person } from "../../PersonObjects/Person";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { CONSTANTS } from "../../Constants";
import { FactionWorkType } from "../data/FactionWorkType";
import { newWorkStats, WorkStats } from "../WorkStats";
import {
getFactionFieldWorkRepGain,
getFactionSecurityWorkRepGain,
getHackingWorkRepGain,
} from "../../PersonObjects/formulas/reputation";
const gameCPS = 1000 / CONSTANTS._idleSpeed; // 5 cycles per second
export const FactionWorkStats: Record<FactionWorkType, WorkStats> = {
[FactionWorkType.HACKING]: newWorkStats({ hackExp: 15 }),
[FactionWorkType.FIELD]: newWorkStats({
hackExp: 10,
strExp: 10,
defExp: 10,
dexExp: 10,
agiExp: 10,
chaExp: 10,
}),
[FactionWorkType.SECURITY]: newWorkStats({
hackExp: 5,
strExp: 15,
defExp: 15,
dexExp: 15,
agiExp: 15,
}),
};
export const calculateFactionRep = (person: Person, tpe: FactionWorkType, favor: number): number => {
const repFormulas = {
[FactionWorkType.HACKING]: getHackingWorkRepGain,
[FactionWorkType.FIELD]: getFactionFieldWorkRepGain,
[FactionWorkType.SECURITY]: getFactionSecurityWorkRepGain,
};
return repFormulas[tpe](person, favor);
};
export function calculateFactionExp(person: Person, tpe: FactionWorkType): WorkStats {
const baseStats = FactionWorkStats[tpe];
return {
money: 0,
reputation: 0,
hackExp: (baseStats.hackExp * person.mults.hacking_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
strExp: (baseStats.strExp * person.mults.strength_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
defExp: (baseStats.defExp * person.mults.defense_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
dexExp: (baseStats.dexExp * person.mults.dexterity_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
agiExp: (baseStats.agiExp * person.mults.agility_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
chaExp: (baseStats.chaExp * person.mults.charisma_exp * BitNodeMultipliers.FactionWorkExpGain) / gameCPS,
intExp: 0,
};
}