diff --git a/src/Company/Companies.ts b/src/Company/Companies.ts index 37721fc81..1c1716ff2 100644 --- a/src/Company/Companies.ts +++ b/src/Company/Companies.ts @@ -1,46 +1,30 @@ // Constructs all CompanyPosition objects using the metadata in data/companypositions.ts -import { companiesMetadata } from "./data/CompaniesMetadata"; -import { Company, IConstructorParams } from "./Company"; -import { Reviver } from "../utils/JSONReviver"; +import { getCompaniesMetadata } from "./data/CompaniesMetadata"; +import { Company } from "./Company"; +import { Reviver, assertLoadingType } from "../utils/JSONReviver"; +import { CompanyName } from "./Enums"; +import { createEnumKeyedRecord } from "../Types/Record"; +import { getEnumHelper } from "../utils/EnumHelper"; -export let Companies: Record = {}; - -function addCompany(params: IConstructorParams): void { - if (Companies[params.name] != null) { - console.warn(`Duplicate Company Position being defined: ${params.name}`); - } - Companies[params.name] = new Company(params); -} - -// Used to initialize new Company objects for the Companies map -// Called when creating new game or after a prestige/reset -export function initCompanies(): void { - // Save Old Company data for 'favor' - const oldCompanies = Companies; - - // Re-construct all Companies - Companies = {}; - companiesMetadata.forEach((e) => { - addCompany(e); - }); - - // Reset data - for (const companyName of Object.keys(Companies)) { - const company = Companies[companyName]; - const oldCompany = oldCompanies[companyName]; - if (!oldCompany) { - // New game, so no OldCompanies data - company.favor = 0; - } else { - company.favor = oldCompanies[companyName].favor; - if (isNaN(company.favor)) { - company.favor = 0; - } - } - } -} +export const Companies: Record = (() => { + const metadata = getCompaniesMetadata(); + return createEnumKeyedRecord(CompanyName, (name) => new Company(metadata[name])); +})(); // Used to load Companies map from a save export function loadCompanies(saveString: string): void { - Companies = JSON.parse(saveString, Reviver); + const loadedCompanies = JSON.parse(saveString, Reviver) as unknown; + // This loading method allows invalid data in player save, but just ignores anything invalid + if (!loadedCompanies) return; + if (typeof loadedCompanies !== "object") return; + for (const [loadedCompanyName, loadedCompany] of Object.entries(loadedCompanies) as [string, unknown][]) { + if (!getEnumHelper("CompanyName").isMember(loadedCompanyName)) continue; + if (!loadedCompany) continue; + if (typeof loadedCompany !== "object") continue; + const company = Companies[loadedCompanyName]; + assertLoadingType(loadedCompany); + const { playerReputation: loadedRep, favor: loadedFavor } = loadedCompany; + if (typeof loadedRep === "number" && loadedRep > 0) company.playerReputation = loadedRep; + if (typeof loadedFavor === "number" && loadedFavor > 0) company.favor = loadedFavor; + } } diff --git a/src/Company/Company.ts b/src/Company/Company.ts index 956ce9ff5..acebb1c0f 100644 --- a/src/Company/Company.ts +++ b/src/Company/Company.ts @@ -1,50 +1,32 @@ -import { CompanyPosition } from "./CompanyPosition"; -import * as posNames from "./data/JobTracks"; +import type { CompanyPosition } from "./CompanyPosition"; + +import { CompanyName, JobName } from "@enums"; import { favorToRep, repToFavor } from "../Faction/formulas/favor"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; -export interface IConstructorParams { - name: string; - info: string; - companyPositions: Record; +export interface CompanyCtorParams { + name: CompanyName; + info?: string; + companyPositions: JobName[]; expMultiplier: number; salaryMultiplier: number; jobStatReqOffset: number; - isMegacorp?: boolean; + hasFaction?: boolean; } -const DefaultConstructorParams: IConstructorParams = { - name: "", - info: "", - companyPositions: {}, - expMultiplier: 1, - salaryMultiplier: 1, - jobStatReqOffset: 0, -}; - export class Company { - /** Company name */ - name: string; + // Static info, initialized once at game load. - /** Description and general information about company */ - info: string; + name = CompanyName.NoodleBar; + info = ""; + hasFaction = false; - /** Has faction associated. */ - isMegacorp: boolean; - - /** - * Object that holds all available positions in this Company. - * Position names are held in keys. - * The values for the keys don't matter, but we'll make them booleans - * - * Must match names of Company Positions, defined in data/companypositionnames.ts - */ - companyPositions: Record; + companyPositions = new Set(); /** Company-specific multiplier for earnings */ - expMultiplier: number; - salaryMultiplier: number; + expMultiplier = 1; + salaryMultiplier = 1; /** * The additional levels of stats you need to quality for a job @@ -53,79 +35,76 @@ export class Company { * For example, the base stat requirement for an intern position is 1. * But if a company has a offset of 200, then you would need stat(s) of 201 */ - jobStatReqOffset: number; + jobStatReqOffset = 0; - /** Properties to track the player's progress in this company */ - isPlayerEmployed: boolean; - playerReputation: number; - favor: number; + // Dynamic info, loaded from save and updated during game. + playerReputation = 0; + favor = 0; - constructor(p: IConstructorParams = DefaultConstructorParams) { + constructor(p?: CompanyCtorParams) { + if (!p) return; this.name = p.name; - this.info = p.info; - this.companyPositions = p.companyPositions; + if (p.info) this.info = p.info; + p.companyPositions.forEach((jobName) => this.companyPositions.add(jobName)); this.expMultiplier = p.expMultiplier; this.salaryMultiplier = p.salaryMultiplier; this.jobStatReqOffset = p.jobStatReqOffset; - - this.isPlayerEmployed = false; - this.playerReputation = 1; - this.favor = 0; - this.isMegacorp = false; - if (p.isMegacorp) this.isMegacorp = true; + if (p.hasFaction) this.hasFaction = true; } - hasPosition(pos: CompanyPosition | string): boolean { - return this.companyPositions[typeof pos === "string" ? pos : pos.name] != null; + hasPosition(pos: CompanyPosition | JobName): boolean { + return this.companyPositions.has(typeof pos === "string" ? pos : pos.name); } hasAgentPositions(): boolean { - return this.companyPositions[posNames.AgentCompanyPositions[0]] != null; + return this.companyPositions.has(JobName.agent0); } hasBusinessConsultantPositions(): boolean { - return this.companyPositions[posNames.BusinessConsultantCompanyPositions[0]] != null; + return this.companyPositions.has(JobName.businessConsult0); } hasBusinessPositions(): boolean { - return this.companyPositions[posNames.BusinessCompanyPositions[0]] != null; + return this.companyPositions.has(JobName.business0); } hasEmployeePositions(): boolean { - return this.companyPositions[posNames.MiscCompanyPositions[1]] != null; + return this.companyPositions.has(JobName.employee); } hasITPositions(): boolean { - return this.companyPositions[posNames.ITCompanyPositions[0]] != null; + return this.companyPositions.has(JobName.IT0); } hasSecurityPositions(): boolean { - return this.companyPositions[posNames.SecurityCompanyPositions[2]] != null; + return this.companyPositions.has(JobName.security0); } hasSoftwareConsultantPositions(): boolean { - return this.companyPositions[posNames.SoftwareConsultantCompanyPositions[0]] != null; + return this.companyPositions.has(JobName.softwareConsult0); } hasSoftwarePositions(): boolean { - return this.companyPositions[posNames.SoftwareCompanyPositions[0]] != null; + return this.companyPositions.has(JobName.software0); } hasWaiterPositions(): boolean { - return this.companyPositions[posNames.MiscCompanyPositions[0]] != null; + return this.companyPositions.has(JobName.waiter); } - gainFavor(): void { - if (this.favor == null) { - this.favor = 0; - } + prestigeAugmentation(): void { + if (this.favor == null) this.favor = 0; this.favor += this.getFavorGain(); + this.playerReputation = 0; + } + + prestigeSourceFile() { + this.favor = 0; + this.playerReputation = 0; } getFavorGain(): number { - if (this.favor == null) { - this.favor = 0; - } + if (this.favor == null) this.favor = 0; const storedRep = Math.max(0, favorToRep(this.favor)); const totalRep = storedRep + this.playerReputation; const newFavor = repToFavor(totalRep); @@ -134,13 +113,16 @@ export class Company { /** Serialize the current object to a JSON save state. */ toJSON(): IReviverValue { - return Generic_toJSON("Company", this); + return Generic_toJSON("Company", this, Company.includedKeys); } /** Initializes a Company from a JSON save state. */ static fromJSON(value: IReviverValue): Company { - return Generic_fromJSON(Company, value.data); + return Generic_fromJSON(Company, value.data, Company.includedKeys); } + + // Only these 3 keys are relevant to the save file + static includedKeys = ["favor", "playerReputation"] as const; } constructorsForReviver.Company = Company; diff --git a/src/Company/CompanyPosition.ts b/src/Company/CompanyPosition.ts index 6c51270b2..a752512ec 100644 --- a/src/Company/CompanyPosition.ts +++ b/src/Company/CompanyPosition.ts @@ -1,10 +1,18 @@ import { Person as IPerson } from "@nsdefs"; import { CONSTANTS } from "../Constants"; -import * as names from "./data/JobTracks"; import { JobName } from "@enums"; +import { + agentJobs, + businessConsultJobs, + businessJobs, + itJobs, + netEngJobs, + securityJobs, + softwareConsultJobs, + softwareJobs, +} from "./data/JobTracks"; -export interface IConstructorParams { - name: JobName; +export interface CompanyPositionCtorParams { nextPosition: JobName | null; baseSalary: number; repMultiplier: number; @@ -75,8 +83,8 @@ export class CompanyPosition { agilityExpGain: number; charismaExpGain: number; - constructor(p: IConstructorParams) { - this.name = p.name; + constructor(name: JobName, p: CompanyPositionCtorParams) { + this.name = name; this.nextPosition = p.nextPosition; this.baseSalary = p.baseSalary; this.repMultiplier = p.repMultiplier; @@ -136,42 +144,42 @@ export class CompanyPosition { } isSoftwareJob(): boolean { - return names.SoftwareCompanyPositions.includes(this.name); + return softwareJobs.includes(this.name); } isITJob(): boolean { - return names.ITCompanyPositions.includes(this.name); + return itJobs.includes(this.name); } isSecurityEngineerJob(): boolean { - return names.SecurityEngineerCompanyPositions.includes(this.name); + return this.name === JobName.securityEng; } isNetworkEngineerJob(): boolean { - return names.NetworkEngineerCompanyPositions.includes(this.name); + return netEngJobs.includes(this.name); } isBusinessJob(): boolean { - return names.BusinessCompanyPositions.includes(this.name); + return businessJobs.includes(this.name); } isSecurityJob(): boolean { - return names.SecurityCompanyPositions.includes(this.name); + return securityJobs.includes(this.name); } isAgentJob(): boolean { - return names.AgentCompanyPositions.includes(this.name); + return agentJobs.includes(this.name); } isSoftwareConsultantJob(): boolean { - return names.SoftwareConsultantCompanyPositions.includes(this.name); + return softwareConsultJobs.includes(this.name); } isBusinessConsultantJob(): boolean { - return names.BusinessConsultantCompanyPositions.includes(this.name); + return businessConsultJobs.includes(this.name); } isPartTimeJob(): boolean { - return names.PartTimeCompanyPositions.includes(this.name); + return [JobName.employeePT, JobName.waiterPT].includes(this.name); } } diff --git a/src/Company/CompanyPositions.ts b/src/Company/CompanyPositions.ts index b4147e116..7653f33a2 100644 --- a/src/Company/CompanyPositions.ts +++ b/src/Company/CompanyPositions.ts @@ -1,16 +1,10 @@ -// Constructs all CompanyPosition objects using the metadata in data/companypositions.ts -import { companyPositionMetadata } from "./data/CompanyPositionsMetadata"; -import { CompanyPosition, IConstructorParams } from "./CompanyPosition"; +import { JobName } from "@enums"; -export const CompanyPositions: Record = {}; +import { getCompanyPositionMetadata } from "./data/CompanyPositionsMetadata"; +import { CompanyPosition } from "./CompanyPosition"; +import { createEnumKeyedRecord } from "../Types/Record"; -function addCompanyPosition(params: IConstructorParams): void { - if (CompanyPositions[params.name] != null) { - console.warn(`Duplicate Company Position being defined: ${params.name}`); - } - CompanyPositions[params.name] = new CompanyPosition(params); -} - -companyPositionMetadata.forEach((e) => { - addCompanyPosition(e); -}); +export const CompanyPositions: Record = (() => { + const metadata = getCompanyPositionMetadata(); + return createEnumKeyedRecord(JobName, (name) => new CompanyPosition(name, metadata[name])); +})(); diff --git a/src/Company/Enums.ts b/src/Company/Enums.ts new file mode 100644 index 000000000..5e6873acc --- /dev/null +++ b/src/Company/Enums.ts @@ -0,0 +1,40 @@ +export enum CompanyName { + ECorp = "ECorp", + MegaCorp = "MegaCorp", + BachmanAndAssociates = "Bachman & Associates", + BladeIndustries = "Blade Industries", + NWO = "NWO", + ClarkeIncorporated = "Clarke Incorporated", + OmniTekIncorporated = "OmniTek Incorporated", + FourSigma = "Four Sigma", + KuaiGongInternational = "KuaiGong International", + FulcrumTechnologies = "Fulcrum Technologies", + StormTechnologies = "Storm Technologies", + DefComm = "DefComm", + HeliosLabs = "Helios Labs", + VitaLife = "VitaLife", + IcarusMicrosystems = "Icarus Microsystems", + UniversalEnergy = "Universal Energy", + GalacticCybersystems = "Galactic Cybersystems", + AeroCorp = "AeroCorp", + OmniaCybersystems = "Omnia Cybersystems", + SolarisSpaceSystems = "Solaris Space Systems", + DeltaOne = "DeltaOne", + GlobalPharmaceuticals = "Global Pharmaceuticals", + NovaMedical = "Nova Medical", + CIA = "Central Intelligence Agency", + NSA = "National Security Agency", + WatchdogSecurity = "Watchdog Security", + LexoCorp = "LexoCorp", + RhoConstruction = "Rho Construction", + AlphaEnterprises = "Alpha Enterprises", + Police = "Aevum Police Headquarters", + SysCoreSecurities = "SysCore Securities", + CompuTek = "CompuTek", + NetLinkTechnologies = "NetLink Technologies", + CarmichaelSecurity = "Carmichael Security", + FoodNStuff = "FoodNStuff", + JoesGuns = "Joe's Guns", + OmegaSoftware = "Omega Software", + NoodleBar = "Noodle Bar", +} diff --git a/src/Company/GetNextCompanyPosition.ts b/src/Company/GetNextCompanyPosition.ts index 9313a3432..5365dd7aa 100644 --- a/src/Company/GetNextCompanyPosition.ts +++ b/src/Company/GetNextCompanyPosition.ts @@ -1,17 +1,13 @@ // Function that returns the next Company Position in the "ladder" // i.e. the next position to get promoted to -import { CompanyPosition } from "./CompanyPosition"; +import type { CompanyPosition } from "./CompanyPosition"; import { CompanyPositions } from "./CompanyPositions"; export function getNextCompanyPositionHelper(currPos: CompanyPosition | null): CompanyPosition | null { - if (currPos == null) { - return null; - } + if (!currPos) return null; - const nextPosName: string | null = currPos.nextPosition; - if (nextPosName == null) { - return null; - } + const nextPosName = currPos.nextPosition; + if (!nextPosName) return null; return CompanyPositions[nextPosName]; } diff --git a/src/Company/data/CompaniesMetadata.ts b/src/Company/data/CompaniesMetadata.ts index 5fa8cfdd2..e8647a585 100644 --- a/src/Company/data/CompaniesMetadata.ts +++ b/src/Company/data/CompaniesMetadata.ts @@ -1,449 +1,309 @@ -import * as posNames from "./JobTracks"; -import { IConstructorParams } from "../Company"; +import { CompanyCtorParams } from "../Company"; -import { LocationName } from "@enums"; +import { CompanyName, JobName } from "@enums"; +import { + agentJobs, + businessJobs, + itJobs, + netEngJobs, + securityJobs, + softwareConsultJobs, + softwareJobs, +} from "./JobTracks"; -// These are grossly typed, need to address -// Create Objects containing Company Positions by category -// Will help in metadata construction later -const AllSoftwarePositions: Record = {}; -const AllITPositions: Record = {}; -const AllNetworkEngineerPositions: Record = {}; -const SecurityEngineerPositions: Record = {}; -const AllTechnologyPositions: Record = {}; -const AllBusinessPositions: Record = {}; -const AllAgentPositions: Record = {}; -const AllSecurityPositions: Record = {}; -const AllSoftwareConsultantPositions: Record = {}; -const AllBusinessConsultantPositions: Record = {}; -const SoftwarePositionsUpToHeadOfEngineering: Record = {}; -const SoftwarePositionsUpToLeadDeveloper: Record = {}; -const BusinessPositionsUpToOperationsManager: Record = {}; -const WaiterOnly: Record = {}; -const EmployeeOnly: Record = {}; -const PartTimeWaiterOnly: Record = {}; -const PartTimeEmployeeOnly: Record = {}; -const OperationsManagerOnly: Record = {}; -const CEOOnly: Record = {}; +export function getCompaniesMetadata(): Record { + const allTechJobs: JobName[] = [...softwareJobs, ...itJobs, ...netEngJobs, JobName.securityEng]; + const softwareJobsToHeadOfEng: JobName[] = softwareJobs.slice(0, 6); + const softwareJobsToLeadDev: JobName[] = softwareJobs.slice(0, 4); + const businessJobToOpsManager: JobName[] = businessJobs.slice(0, 4); -posNames.SoftwareCompanyPositions.forEach((e) => { - AllSoftwarePositions[e] = true; - AllTechnologyPositions[e] = true; -}); - -posNames.ITCompanyPositions.forEach((e) => { - AllITPositions[e] = true; - AllTechnologyPositions[e] = true; -}); - -posNames.NetworkEngineerCompanyPositions.forEach((e) => { - AllNetworkEngineerPositions[e] = true; - AllTechnologyPositions[e] = true; -}); - -AllTechnologyPositions[posNames.SecurityEngineerCompanyPositions[0]] = true; -SecurityEngineerPositions[posNames.SecurityEngineerCompanyPositions[0]] = true; - -posNames.BusinessCompanyPositions.forEach((e) => { - AllBusinessPositions[e] = true; -}); - -posNames.SecurityCompanyPositions.forEach((e) => { - AllSecurityPositions[e] = true; -}); - -posNames.AgentCompanyPositions.forEach((e) => { - AllAgentPositions[e] = true; -}); - -posNames.SoftwareConsultantCompanyPositions.forEach((e) => { - AllSoftwareConsultantPositions[e] = true; -}); - -posNames.BusinessConsultantCompanyPositions.forEach((e) => { - AllBusinessConsultantPositions[e] = true; -}); - -for (let i = 0; i < posNames.SoftwareCompanyPositions.length; ++i) { - const e = posNames.SoftwareCompanyPositions[i]; - if (i <= 5) { - SoftwarePositionsUpToHeadOfEngineering[e] = true; - } - if (i <= 3) { - SoftwarePositionsUpToLeadDeveloper[e] = true; - } + return { + [CompanyName.ECorp]: { + name: CompanyName.ECorp, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 3, + salaryMultiplier: 3, + jobStatReqOffset: 249, + }, + [CompanyName.MegaCorp]: { + name: CompanyName.MegaCorp, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 3, + salaryMultiplier: 3, + jobStatReqOffset: 249, + }, + [CompanyName.BachmanAndAssociates]: { + name: CompanyName.BachmanAndAssociates, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 2.6, + salaryMultiplier: 2.6, + jobStatReqOffset: 224, + }, + [CompanyName.BladeIndustries]: { + name: CompanyName.BladeIndustries, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 2.75, + salaryMultiplier: 2.75, + jobStatReqOffset: 224, + }, + [CompanyName.NWO]: { + name: CompanyName.NWO, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 2.75, + salaryMultiplier: 2.75, + jobStatReqOffset: 249, + }, + [CompanyName.ClarkeIncorporated]: { + name: CompanyName.ClarkeIncorporated, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 2.25, + salaryMultiplier: 2.25, + jobStatReqOffset: 224, + }, + [CompanyName.OmniTekIncorporated]: { + name: CompanyName.OmniTekIncorporated, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 2.25, + salaryMultiplier: 2.25, + jobStatReqOffset: 224, + }, + [CompanyName.FourSigma]: { + name: CompanyName.FourSigma, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 2.5, + salaryMultiplier: 2.5, + jobStatReqOffset: 224, + }, + [CompanyName.KuaiGongInternational]: { + name: CompanyName.KuaiGongInternational, + companyPositions: [...allTechJobs, ...businessJobs, ...securityJobs], + expMultiplier: 2.2, + salaryMultiplier: 2.2, + jobStatReqOffset: 224, + }, + [CompanyName.FulcrumTechnologies]: { + name: CompanyName.FulcrumTechnologies, + companyPositions: [...allTechJobs, ...businessJobs], + expMultiplier: 2, + salaryMultiplier: 2, + jobStatReqOffset: 224, + }, + [CompanyName.StormTechnologies]: { + name: CompanyName.StormTechnologies, + companyPositions: [...allTechJobs, ...softwareConsultJobs, ...businessJobs], + expMultiplier: 1.8, + salaryMultiplier: 1.8, + jobStatReqOffset: 199, + }, + [CompanyName.DefComm]: { + name: CompanyName.DefComm, + companyPositions: [JobName.business5, ...allTechJobs, ...softwareConsultJobs], + expMultiplier: 1.75, + salaryMultiplier: 1.75, + jobStatReqOffset: 199, + }, + [CompanyName.HeliosLabs]: { + name: CompanyName.HeliosLabs, + companyPositions: [JobName.business5, ...allTechJobs, ...softwareConsultJobs], + expMultiplier: 1.8, + salaryMultiplier: 1.8, + jobStatReqOffset: 199, + }, + [CompanyName.VitaLife]: { + name: CompanyName.VitaLife, + companyPositions: [...allTechJobs, ...businessJobs, ...softwareConsultJobs], + expMultiplier: 1.8, + salaryMultiplier: 1.8, + jobStatReqOffset: 199, + }, + [CompanyName.IcarusMicrosystems]: { + name: CompanyName.IcarusMicrosystems, + companyPositions: [...allTechJobs, ...businessJobs, ...softwareConsultJobs], + expMultiplier: 1.9, + salaryMultiplier: 1.9, + jobStatReqOffset: 199, + }, + [CompanyName.UniversalEnergy]: { + name: CompanyName.UniversalEnergy, + companyPositions: [...allTechJobs, ...businessJobs, ...softwareConsultJobs], + expMultiplier: 2, + salaryMultiplier: 2, + jobStatReqOffset: 199, + }, + [CompanyName.GalacticCybersystems]: { + name: CompanyName.GalacticCybersystems, + companyPositions: [...allTechJobs, ...businessJobs, ...softwareConsultJobs], + expMultiplier: 1.9, + salaryMultiplier: 1.9, + jobStatReqOffset: 199, + }, + [CompanyName.AeroCorp]: { + name: CompanyName.AeroCorp, + companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs], + expMultiplier: 1.7, + salaryMultiplier: 1.7, + jobStatReqOffset: 199, + }, + [CompanyName.OmniaCybersystems]: { + name: CompanyName.OmniaCybersystems, + companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs], + expMultiplier: 1.7, + salaryMultiplier: 1.7, + jobStatReqOffset: 199, + }, + [CompanyName.SolarisSpaceSystems]: { + name: CompanyName.SolarisSpaceSystems, + companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs], + expMultiplier: 1.7, + salaryMultiplier: 1.7, + jobStatReqOffset: 199, + }, + [CompanyName.DeltaOne]: { + name: CompanyName.DeltaOne, + companyPositions: [JobName.business3, JobName.business5, ...allTechJobs, ...securityJobs], + expMultiplier: 1.6, + salaryMultiplier: 1.6, + jobStatReqOffset: 199, + }, + [CompanyName.GlobalPharmaceuticals]: { + name: CompanyName.GlobalPharmaceuticals, + companyPositions: [...allTechJobs, ...businessJobs, ...softwareConsultJobs, ...securityJobs], + expMultiplier: 1.8, + salaryMultiplier: 1.8, + jobStatReqOffset: 224, + }, + [CompanyName.NovaMedical]: { + name: CompanyName.NovaMedical, + companyPositions: [...allTechJobs, ...businessJobs, ...softwareConsultJobs, ...securityJobs], + expMultiplier: 1.75, + salaryMultiplier: 1.75, + jobStatReqOffset: 199, + }, + [CompanyName.CIA]: { + name: CompanyName.CIA, + companyPositions: [ + ...softwareJobsToHeadOfEng, + ...netEngJobs, + JobName.securityEng, + ...itJobs, + ...securityJobs, + ...agentJobs, + ], + expMultiplier: 2, + salaryMultiplier: 2, + jobStatReqOffset: 149, + }, + [CompanyName.NSA]: { + name: CompanyName.NSA, + companyPositions: [ + ...softwareJobsToHeadOfEng, + ...netEngJobs, + JobName.securityEng, + ...itJobs, + ...securityJobs, + ...agentJobs, + ], + expMultiplier: 2, + salaryMultiplier: 2, + jobStatReqOffset: 149, + }, + [CompanyName.WatchdogSecurity]: { + name: CompanyName.WatchdogSecurity, + companyPositions: [ + ...softwareJobsToHeadOfEng, + ...netEngJobs, + ...itJobs, + ...securityJobs, + ...agentJobs, + ...softwareConsultJobs, + ], + expMultiplier: 1.5, + salaryMultiplier: 1.5, + jobStatReqOffset: 124, + }, + [CompanyName.LexoCorp]: { + name: CompanyName.LexoCorp, + companyPositions: [...allTechJobs, ...softwareConsultJobs, ...businessJobs, ...securityJobs], + expMultiplier: 1.4, + salaryMultiplier: 1.4, + jobStatReqOffset: 99, + }, + [CompanyName.RhoConstruction]: { + name: CompanyName.RhoConstruction, + companyPositions: [...softwareJobsToLeadDev, ...businessJobToOpsManager], + expMultiplier: 1.3, + salaryMultiplier: 1.3, + jobStatReqOffset: 49, + }, + [CompanyName.AlphaEnterprises]: { + name: CompanyName.AlphaEnterprises, + companyPositions: [...softwareJobsToLeadDev, ...businessJobToOpsManager, ...softwareConsultJobs], + expMultiplier: 1.5, + salaryMultiplier: 1.5, + jobStatReqOffset: 99, + }, + [CompanyName.Police]: { + name: CompanyName.Police, + companyPositions: [...securityJobs, ...softwareJobsToLeadDev], + expMultiplier: 1.3, + salaryMultiplier: 1.3, + jobStatReqOffset: 99, + }, + [CompanyName.SysCoreSecurities]: { + name: CompanyName.SysCoreSecurities, + companyPositions: [...allTechJobs], + expMultiplier: 1.3, + salaryMultiplier: 1.3, + jobStatReqOffset: 124, + }, + [CompanyName.CompuTek]: { + name: CompanyName.CompuTek, + companyPositions: [...allTechJobs], + expMultiplier: 1.2, + salaryMultiplier: 1.2, + jobStatReqOffset: 74, + }, + [CompanyName.NetLinkTechnologies]: { + name: CompanyName.NetLinkTechnologies, + companyPositions: [...allTechJobs], + expMultiplier: 1.2, + salaryMultiplier: 1.2, + jobStatReqOffset: 99, + }, + [CompanyName.CarmichaelSecurity]: { + name: CompanyName.CarmichaelSecurity, + companyPositions: [...allTechJobs, ...softwareConsultJobs, ...agentJobs, ...securityJobs], + expMultiplier: 1.2, + salaryMultiplier: 1.2, + jobStatReqOffset: 74, + }, + [CompanyName.FoodNStuff]: { + name: CompanyName.FoodNStuff, + companyPositions: [JobName.employee, JobName.employeePT], + expMultiplier: 1, + salaryMultiplier: 1, + jobStatReqOffset: 0, + }, + [CompanyName.JoesGuns]: { + name: CompanyName.JoesGuns, + companyPositions: [JobName.employee, JobName.employeePT], + expMultiplier: 1, + salaryMultiplier: 1, + jobStatReqOffset: 0, + }, + [CompanyName.OmegaSoftware]: { + name: CompanyName.OmegaSoftware, + companyPositions: [...softwareJobs, ...softwareConsultJobs, ...itJobs], + expMultiplier: 1.1, + salaryMultiplier: 1.1, + jobStatReqOffset: 49, + }, + [CompanyName.NoodleBar]: { + name: CompanyName.NoodleBar, + companyPositions: [JobName.waiter, JobName.waiterPT], + expMultiplier: 1, + salaryMultiplier: 1, + jobStatReqOffset: 0, + }, + }; } -for (let i = 0; i < posNames.BusinessCompanyPositions.length; ++i) { - const e = posNames.BusinessCompanyPositions[i]; - if (i <= 3) { - BusinessPositionsUpToOperationsManager[e] = true; - } -} - -WaiterOnly[posNames.MiscCompanyPositions[0]] = true; -EmployeeOnly[posNames.MiscCompanyPositions[1]] = true; -PartTimeWaiterOnly[posNames.PartTimeCompanyPositions[0]] = true; -PartTimeEmployeeOnly[posNames.PartTimeCompanyPositions[1]] = true; -OperationsManagerOnly[posNames.BusinessCompanyPositions[3]] = true; -CEOOnly[posNames.BusinessCompanyPositions[5]] = true; - -// Metadata -export const companiesMetadata: IConstructorParams[] = [ - { - name: LocationName.AevumECorp, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 3, - salaryMultiplier: 3, - jobStatReqOffset: 249, - }, - { - name: LocationName.Sector12MegaCorp, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 3, - salaryMultiplier: 3, - jobStatReqOffset: 249, - }, - { - name: LocationName.AevumBachmanAndAssociates, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 2.6, - salaryMultiplier: 2.6, - jobStatReqOffset: 224, - }, - { - name: LocationName.Sector12BladeIndustries, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 2.75, - salaryMultiplier: 2.75, - jobStatReqOffset: 224, - }, - { - name: LocationName.VolhavenNWO, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 2.75, - salaryMultiplier: 2.75, - jobStatReqOffset: 249, - }, - { - name: LocationName.AevumClarkeIncorporated, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 2.25, - salaryMultiplier: 2.25, - jobStatReqOffset: 224, - }, - { - name: LocationName.VolhavenOmniTekIncorporated, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 2.25, - salaryMultiplier: 2.25, - jobStatReqOffset: 224, - }, - { - name: LocationName.Sector12FourSigma, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 2.5, - salaryMultiplier: 2.5, - jobStatReqOffset: 224, - }, - { - name: LocationName.ChongqingKuaiGongInternational, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSecurityPositions), - expMultiplier: 2.2, - salaryMultiplier: 2.2, - jobStatReqOffset: 224, - }, - { - name: LocationName.AevumFulcrumTechnologies, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions), - expMultiplier: 2, - salaryMultiplier: 2, - jobStatReqOffset: 224, - }, - { - name: LocationName.IshimaStormTechnologies, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllSoftwareConsultantPositions, AllBusinessPositions), - expMultiplier: 1.8, - salaryMultiplier: 1.8, - jobStatReqOffset: 199, - }, - { - name: LocationName.NewTokyoDefComm, - info: "", - companyPositions: Object.assign({}, CEOOnly, AllTechnologyPositions, AllSoftwareConsultantPositions), - expMultiplier: 1.75, - salaryMultiplier: 1.75, - jobStatReqOffset: 199, - }, - { - name: LocationName.VolhavenHeliosLabs, - info: "", - companyPositions: Object.assign({}, CEOOnly, AllTechnologyPositions, AllSoftwareConsultantPositions), - expMultiplier: 1.8, - salaryMultiplier: 1.8, - jobStatReqOffset: 199, - }, - { - name: LocationName.NewTokyoVitaLife, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSoftwareConsultantPositions), - expMultiplier: 1.8, - salaryMultiplier: 1.8, - jobStatReqOffset: 199, - }, - { - name: LocationName.Sector12IcarusMicrosystems, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSoftwareConsultantPositions), - expMultiplier: 1.9, - salaryMultiplier: 1.9, - jobStatReqOffset: 199, - }, - { - name: LocationName.Sector12UniversalEnergy, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSoftwareConsultantPositions), - expMultiplier: 2, - salaryMultiplier: 2, - jobStatReqOffset: 199, - }, - { - name: LocationName.AevumGalacticCybersystems, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions, AllBusinessPositions, AllSoftwareConsultantPositions), - expMultiplier: 1.9, - salaryMultiplier: 1.9, - jobStatReqOffset: 199, - }, - { - name: LocationName.AevumAeroCorp, - info: "", - companyPositions: Object.assign({}, CEOOnly, OperationsManagerOnly, AllTechnologyPositions, AllSecurityPositions), - expMultiplier: 1.7, - salaryMultiplier: 1.7, - jobStatReqOffset: 199, - }, - { - name: LocationName.VolhavenOmniaCybersystems, - info: "", - companyPositions: Object.assign({}, CEOOnly, OperationsManagerOnly, AllTechnologyPositions, AllSecurityPositions), - expMultiplier: 1.7, - salaryMultiplier: 1.7, - jobStatReqOffset: 199, - }, - { - name: LocationName.ChongqingSolarisSpaceSystems, - info: "", - companyPositions: Object.assign({}, CEOOnly, OperationsManagerOnly, AllTechnologyPositions, AllSecurityPositions), - expMultiplier: 1.7, - salaryMultiplier: 1.7, - jobStatReqOffset: 199, - }, - { - name: LocationName.Sector12DeltaOne, - info: "", - companyPositions: Object.assign({}, CEOOnly, OperationsManagerOnly, AllTechnologyPositions, AllSecurityPositions), - expMultiplier: 1.6, - salaryMultiplier: 1.6, - jobStatReqOffset: 199, - }, - { - name: LocationName.NewTokyoGlobalPharmaceuticals, - info: "", - companyPositions: Object.assign( - {}, - AllTechnologyPositions, - AllBusinessPositions, - AllSoftwareConsultantPositions, - AllSecurityPositions, - ), - expMultiplier: 1.8, - salaryMultiplier: 1.8, - jobStatReqOffset: 224, - }, - { - name: LocationName.IshimaNovaMedical, - info: "", - companyPositions: Object.assign( - {}, - AllTechnologyPositions, - AllBusinessPositions, - AllSoftwareConsultantPositions, - AllSecurityPositions, - ), - expMultiplier: 1.75, - salaryMultiplier: 1.75, - jobStatReqOffset: 199, - }, - { - name: LocationName.Sector12CIA, - info: "", - companyPositions: Object.assign( - {}, - SoftwarePositionsUpToHeadOfEngineering, - AllNetworkEngineerPositions, - SecurityEngineerPositions, - AllITPositions, - AllSecurityPositions, - AllAgentPositions, - ), - expMultiplier: 2, - salaryMultiplier: 2, - jobStatReqOffset: 149, - }, - { - name: LocationName.Sector12NSA, - info: "", - companyPositions: Object.assign( - {}, - SoftwarePositionsUpToHeadOfEngineering, - AllNetworkEngineerPositions, - SecurityEngineerPositions, - AllITPositions, - AllSecurityPositions, - AllAgentPositions, - ), - expMultiplier: 2, - salaryMultiplier: 2, - jobStatReqOffset: 149, - }, - { - name: LocationName.AevumWatchdogSecurity, - info: "", - companyPositions: Object.assign( - {}, - SoftwarePositionsUpToHeadOfEngineering, - AllNetworkEngineerPositions, - AllITPositions, - AllSecurityPositions, - AllAgentPositions, - AllSoftwareConsultantPositions, - ), - expMultiplier: 1.5, - salaryMultiplier: 1.5, - jobStatReqOffset: 124, - }, - { - name: LocationName.VolhavenLexoCorp, - info: "", - companyPositions: Object.assign( - {}, - AllTechnologyPositions, - AllSoftwareConsultantPositions, - AllBusinessPositions, - AllSecurityPositions, - ), - expMultiplier: 1.4, - salaryMultiplier: 1.4, - jobStatReqOffset: 99, - }, - { - name: LocationName.AevumRhoConstruction, - info: "", - companyPositions: Object.assign({}, SoftwarePositionsUpToLeadDeveloper, BusinessPositionsUpToOperationsManager), - expMultiplier: 1.3, - salaryMultiplier: 1.3, - jobStatReqOffset: 49, - }, - { - name: LocationName.Sector12AlphaEnterprises, - info: "", - companyPositions: Object.assign( - {}, - SoftwarePositionsUpToLeadDeveloper, - BusinessPositionsUpToOperationsManager, - AllSoftwareConsultantPositions, - ), - expMultiplier: 1.5, - salaryMultiplier: 1.5, - jobStatReqOffset: 99, - }, - { - name: LocationName.AevumPolice, - info: "", - companyPositions: Object.assign({}, AllSecurityPositions, SoftwarePositionsUpToLeadDeveloper), - expMultiplier: 1.3, - salaryMultiplier: 1.3, - jobStatReqOffset: 99, - }, - { - name: LocationName.VolhavenSysCoreSecurities, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions), - expMultiplier: 1.3, - salaryMultiplier: 1.3, - jobStatReqOffset: 124, - }, - { - name: LocationName.VolhavenCompuTek, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions), - expMultiplier: 1.2, - salaryMultiplier: 1.2, - jobStatReqOffset: 74, - }, - { - name: LocationName.AevumNetLinkTechnologies, - info: "", - companyPositions: Object.assign({}, AllTechnologyPositions), - expMultiplier: 1.2, - salaryMultiplier: 1.2, - jobStatReqOffset: 99, - }, - { - name: LocationName.Sector12CarmichaelSecurity, - info: "", - companyPositions: Object.assign( - {}, - AllTechnologyPositions, - AllSoftwareConsultantPositions, - AllAgentPositions, - AllSecurityPositions, - ), - expMultiplier: 1.2, - salaryMultiplier: 1.2, - jobStatReqOffset: 74, - }, - { - name: LocationName.Sector12FoodNStuff, - info: "", - companyPositions: Object.assign({}, EmployeeOnly, PartTimeEmployeeOnly), - expMultiplier: 1, - salaryMultiplier: 1, - jobStatReqOffset: 0, - }, - { - name: LocationName.Sector12JoesGuns, - info: "", - companyPositions: Object.assign({}, EmployeeOnly, PartTimeEmployeeOnly), - expMultiplier: 1, - salaryMultiplier: 1, - jobStatReqOffset: 0, - }, - { - name: LocationName.IshimaOmegaSoftware, - info: "", - companyPositions: Object.assign({}, AllSoftwarePositions, AllSoftwareConsultantPositions, AllITPositions), - expMultiplier: 1.1, - salaryMultiplier: 1.1, - jobStatReqOffset: 49, - }, - { - name: LocationName.NewTokyoNoodleBar, - info: "", - companyPositions: Object.assign({}, WaiterOnly, PartTimeWaiterOnly), - expMultiplier: 1, - salaryMultiplier: 1, - jobStatReqOffset: 0, - }, -]; diff --git a/src/Company/data/CompanyPositionsMetadata.ts b/src/Company/data/CompanyPositionsMetadata.ts index dbd9e3c7f..629ac9a9a 100644 --- a/src/Company/data/CompanyPositionsMetadata.ts +++ b/src/Company/data/CompanyPositionsMetadata.ts @@ -1,602 +1,518 @@ // Metadata used for constructing Company Positions -import { IConstructorParams } from "../CompanyPosition"; -import * as posNames from "./JobTracks"; +import { JobName } from "@enums"; +import { CompanyPositionCtorParams } from "../CompanyPosition"; -export const companyPositionMetadata: IConstructorParams[] = [ - { - name: posNames.SoftwareCompanyPositions[0], // Software Engineering Intern - nextPosition: posNames.SoftwareCompanyPositions[1], // Junior Software Engineer - baseSalary: 33, - charismaEffectiveness: 15, - charismaExpGain: 0.02, - hackingEffectiveness: 85, - hackingExpGain: 0.05, - reqdHacking: 1, - repMultiplier: 0.9, - }, - { - name: posNames.SoftwareCompanyPositions[1], // Junior Software Engineer - nextPosition: posNames.SoftwareCompanyPositions[2], // Senior Software Engineer - baseSalary: 80, - charismaEffectiveness: 15, - charismaExpGain: 0.05, - hackingEffectiveness: 85, - hackingExpGain: 0.1, - reqdHacking: 51, - reqdReputation: 8e3, - repMultiplier: 1.1, - }, - { - name: posNames.SoftwareCompanyPositions[2], // Senior Software Engineer - nextPosition: posNames.SoftwareCompanyPositions[3], // Lead Software Developer - baseSalary: 165, - charismaEffectiveness: 20, - charismaExpGain: 0.08, - hackingEffectiveness: 80, - hackingExpGain: 0.4, - reqdCharisma: 51, - reqdHacking: 251, - reqdReputation: 40e3, - repMultiplier: 1.3, - }, - { - name: posNames.SoftwareCompanyPositions[3], // Lead Software Developer - nextPosition: posNames.SoftwareCompanyPositions[4], // Head of Software - baseSalary: 500, - charismaEffectiveness: 25, - charismaExpGain: 0.1, - hackingEffectiveness: 75, - hackingExpGain: 0.8, - reqdCharisma: 151, - reqdHacking: 401, - reqdReputation: 200e3, - repMultiplier: 1.5, - }, - { - name: posNames.SoftwareCompanyPositions[4], // Head of Software - nextPosition: posNames.SoftwareCompanyPositions[5], // Head of Engineering - baseSalary: 800, - charismaEffectiveness: 25, - charismaExpGain: 0.5, - hackingEffectiveness: 75, - hackingExpGain: 1, - reqdCharisma: 251, - reqdHacking: 501, - reqdReputation: 400e3, - repMultiplier: 1.6, - }, - { - name: posNames.SoftwareCompanyPositions[5], // Head of Engineering - nextPosition: posNames.SoftwareCompanyPositions[6], // Vice President of Technology - baseSalary: 1650, - charismaEffectiveness: 25, - charismaExpGain: 0.5, - hackingEffectiveness: 75, - hackingExpGain: 1.1, - reqdCharisma: 251, - reqdHacking: 501, - reqdReputation: 800e3, - repMultiplier: 1.6, - }, - { - name: posNames.SoftwareCompanyPositions[6], // Vice President of Technology - nextPosition: posNames.SoftwareCompanyPositions[7], // Chief Technology Officer - baseSalary: 2310, - charismaEffectiveness: 30, - charismaExpGain: 0.6, - hackingEffectiveness: 70, - hackingExpGain: 1.2, - reqdCharisma: 401, - reqdHacking: 601, - reqdReputation: 1.6e6, - repMultiplier: 1.75, - }, - { - name: posNames.SoftwareCompanyPositions[7], // Chief Technology Officer - nextPosition: null, - baseSalary: 2640, - charismaEffectiveness: 35, - charismaExpGain: 1, - hackingEffectiveness: 65, - hackingExpGain: 1.5, - reqdCharisma: 501, - reqdHacking: 751, - reqdReputation: 3.2e6, - repMultiplier: 2, - }, - { - name: posNames.ITCompanyPositions[0], // IT Intern - nextPosition: posNames.ITCompanyPositions[1], // IT Analyst - baseSalary: 26, - charismaEffectiveness: 10, - charismaExpGain: 0.01, - hackingEffectiveness: 90, - hackingExpGain: 0.04, - reqdHacking: 1, - repMultiplier: 0.9, - }, - { - name: posNames.ITCompanyPositions[1], // IT Analyst - nextPosition: posNames.ITCompanyPositions[2], // IT Manager - baseSalary: 66, - charismaEffectiveness: 15, - charismaExpGain: 0.02, - hackingEffectiveness: 85, - hackingExpGain: 0.08, - reqdHacking: 26, - reqdReputation: 7e3, - repMultiplier: 1.1, - }, - { - name: posNames.ITCompanyPositions[2], // IT Manager - nextPosition: posNames.ITCompanyPositions[3], // Systems Administrator - baseSalary: 132, - charismaEffectiveness: 20, - charismaExpGain: 0.1, - hackingEffectiveness: 80, - hackingExpGain: 0.3, - reqdCharisma: 51, - reqdHacking: 151, - reqdReputation: 35e3, - repMultiplier: 1.3, - }, - { - name: posNames.ITCompanyPositions[3], // Systems Administrator - nextPosition: posNames.SoftwareCompanyPositions[5], // Head of Engineering - baseSalary: 410, - charismaEffectiveness: 20, - charismaExpGain: 0.2, - hackingEffectiveness: 80, - hackingExpGain: 0.5, - reqdCharisma: 76, - reqdHacking: 251, - reqdReputation: 175e3, - repMultiplier: 1.4, - }, - { - name: posNames.SecurityEngineerCompanyPositions[0], // Security Engineer - nextPosition: posNames.SoftwareCompanyPositions[5], // Head of Engineering - baseSalary: 121, - charismaEffectiveness: 15, - charismaExpGain: 0.05, - hackingEffectiveness: 85, - hackingExpGain: 0.4, - reqdCharisma: 26, - reqdHacking: 151, - reqdReputation: 35e3, - repMultiplier: 1.2, - }, - { - name: posNames.NetworkEngineerCompanyPositions[0], // Network Engineer - nextPosition: posNames.NetworkEngineerCompanyPositions[1], // Network Administrator - baseSalary: 121, - charismaEffectiveness: 15, - charismaExpGain: 0.05, - hackingEffectiveness: 85, - hackingExpGain: 0.4, - reqdCharisma: 26, - reqdHacking: 151, - reqdReputation: 35e3, - repMultiplier: 1.2, - }, - { - name: posNames.NetworkEngineerCompanyPositions[1], // Network Administrator - nextPosition: posNames.SoftwareCompanyPositions[5], // Head of Engineering - baseSalary: 410, - charismaEffectiveness: 20, - charismaExpGain: 0.1, - hackingEffectiveness: 80, - hackingExpGain: 0.5, - reqdCharisma: 76, - reqdHacking: 251, - reqdReputation: 175e3, - repMultiplier: 1.3, - }, - { - name: posNames.BusinessCompanyPositions[0], // Business Intern - nextPosition: posNames.BusinessCompanyPositions[1], // Business Analyst - baseSalary: 46, - charismaEffectiveness: 90, - charismaExpGain: 0.08, - hackingEffectiveness: 10, - hackingExpGain: 0.01, - reqdCharisma: 1, - reqdHacking: 1, - repMultiplier: 0.9, - }, - { - name: posNames.BusinessCompanyPositions[1], // Business Analyst - nextPosition: posNames.BusinessCompanyPositions[2], // Business Manager - baseSalary: 100, - charismaEffectiveness: 85, - charismaExpGain: 0.15, - hackingEffectiveness: 15, - hackingExpGain: 0.02, - reqdCharisma: 51, - reqdHacking: 6, - reqdReputation: 8e3, - repMultiplier: 1.1, - }, - { - name: posNames.BusinessCompanyPositions[2], // Business Manager - nextPosition: posNames.BusinessCompanyPositions[3], // Operations Manager - baseSalary: 200, - charismaEffectiveness: 85, - charismaExpGain: 0.3, - hackingEffectiveness: 15, - hackingExpGain: 0.02, - reqdCharisma: 101, - reqdHacking: 51, - reqdReputation: 40e3, - repMultiplier: 1.3, - }, - { - name: posNames.BusinessCompanyPositions[3], // Operations Manager - nextPosition: posNames.BusinessCompanyPositions[4], // Chief Financial Officer - baseSalary: 660, - charismaEffectiveness: 85, - charismaExpGain: 0.4, - hackingEffectiveness: 15, - hackingExpGain: 0.02, - reqdCharisma: 226, - reqdHacking: 51, - reqdReputation: 200e3, - repMultiplier: 1.5, - }, - { - name: posNames.BusinessCompanyPositions[4], // Chief Financial Officer - nextPosition: posNames.BusinessCompanyPositions[5], // Chief Executive Officer - baseSalary: 1950, - charismaEffectiveness: 90, - charismaExpGain: 1, - hackingEffectiveness: 10, - hackingExpGain: 0.05, - reqdCharisma: 501, - reqdHacking: 76, - reqdReputation: 800e3, - repMultiplier: 1.6, - }, - { - name: posNames.BusinessCompanyPositions[5], // Chief Executive Officer - nextPosition: null, - baseSalary: 3900, - charismaEffectiveness: 90, - charismaExpGain: 1.5, - hackingEffectiveness: 10, - hackingExpGain: 0.05, - reqdCharisma: 751, - reqdHacking: 101, - reqdReputation: 3.2e6, - repMultiplier: 1.75, - }, - { - name: posNames.SecurityCompanyPositions[0], // Police Officer - nextPosition: posNames.SecurityCompanyPositions[1], // Police Chief - baseSalary: 82, - hackingEffectiveness: 5, - strengthEffectiveness: 20, - defenseEffectiveness: 20, - dexterityEffectiveness: 20, - agilityEffectiveness: 20, - charismaEffectiveness: 15, - hackingExpGain: 0.02, - strengthExpGain: 0.08, - defenseExpGain: 0.08, - dexterityExpGain: 0.08, - agilityExpGain: 0.08, - charismaExpGain: 0.04, - reqdHacking: 11, - reqdStrength: 101, - reqdDefense: 101, - reqdDexterity: 101, - reqdAgility: 101, - reqdCharisma: 51, - reqdReputation: 8e3, - repMultiplier: 1, - }, - { - name: posNames.SecurityCompanyPositions[1], // Police Chief - nextPosition: null, - baseSalary: 460, - hackingEffectiveness: 5, - strengthEffectiveness: 20, - defenseEffectiveness: 20, - dexterityEffectiveness: 20, - agilityEffectiveness: 20, - charismaEffectiveness: 15, - hackingExpGain: 0.02, - strengthExpGain: 0.1, - defenseExpGain: 0.1, - dexterityExpGain: 0.1, - agilityExpGain: 0.1, - charismaExpGain: 0.1, - reqdHacking: 101, - reqdStrength: 301, - reqdDefense: 301, - reqdDexterity: 301, - reqdAgility: 301, - reqdCharisma: 151, - reqdReputation: 36e3, - repMultiplier: 1.25, - }, - { - name: posNames.SecurityCompanyPositions[2], // Security Guard - nextPosition: posNames.SecurityCompanyPositions[3], // Security Officer - baseSalary: 50, - hackingEffectiveness: 5, - strengthEffectiveness: 20, - defenseEffectiveness: 20, - dexterityEffectiveness: 20, - agilityEffectiveness: 20, - charismaEffectiveness: 15, - hackingExpGain: 0.01, - strengthExpGain: 0.04, - defenseExpGain: 0.04, - dexterityExpGain: 0.04, - agilityExpGain: 0.04, - charismaExpGain: 0.02, - reqdStrength: 51, - reqdDefense: 51, - reqdDexterity: 51, - reqdAgility: 51, - reqdCharisma: 1, - repMultiplier: 1, - }, - { - name: posNames.SecurityCompanyPositions[3], // Security Officer - nextPosition: posNames.SecurityCompanyPositions[4], // Security Supervisor - baseSalary: 195, - hackingEffectiveness: 10, - strengthEffectiveness: 20, - defenseEffectiveness: 20, - dexterityEffectiveness: 20, - agilityEffectiveness: 20, - charismaEffectiveness: 10, - hackingExpGain: 0.02, - strengthExpGain: 0.1, - defenseExpGain: 0.1, - dexterityExpGain: 0.1, - agilityExpGain: 0.1, - charismaExpGain: 0.05, - reqdHacking: 26, - reqdStrength: 151, - reqdDefense: 151, - reqdDexterity: 151, - reqdAgility: 151, - reqdCharisma: 51, - reqdReputation: 8e3, - repMultiplier: 1.1, - }, - { - name: posNames.SecurityCompanyPositions[4], // Security Supervisor - nextPosition: posNames.SecurityCompanyPositions[5], // Head of Security - baseSalary: 660, - hackingEffectiveness: 10, - strengthEffectiveness: 15, - defenseEffectiveness: 15, - dexterityEffectiveness: 15, - agilityEffectiveness: 15, - charismaEffectiveness: 30, - hackingExpGain: 0.02, - strengthExpGain: 0.12, - defenseExpGain: 0.12, - dexterityExpGain: 0.12, - agilityExpGain: 0.12, - charismaExpGain: 0.1, - reqdHacking: 26, - reqdStrength: 251, - reqdDefense: 251, - reqdDexterity: 251, - reqdAgility: 251, - reqdCharisma: 101, - reqdReputation: 36e3, - repMultiplier: 1.25, - }, - { - name: posNames.SecurityCompanyPositions[5], // Head of Security - nextPosition: null, - baseSalary: 1320, - hackingEffectiveness: 10, - strengthEffectiveness: 15, - defenseEffectiveness: 15, - dexterityEffectiveness: 15, - agilityEffectiveness: 15, - charismaEffectiveness: 30, - hackingExpGain: 0.05, - strengthExpGain: 0.15, - defenseExpGain: 0.15, - dexterityExpGain: 0.15, - agilityExpGain: 0.15, - charismaExpGain: 0.15, - reqdHacking: 51, - reqdStrength: 501, - reqdDefense: 501, - reqdDexterity: 501, - reqdAgility: 501, - reqdCharisma: 151, - reqdReputation: 144e3, - repMultiplier: 1.4, - }, - { - name: posNames.AgentCompanyPositions[0], // Field Agent - nextPosition: posNames.AgentCompanyPositions[1], // Secret Agent - baseSalary: 330, - hackingEffectiveness: 10, - strengthEffectiveness: 15, - defenseEffectiveness: 15, - dexterityEffectiveness: 20, - agilityEffectiveness: 20, - charismaEffectiveness: 20, - hackingExpGain: 0.04, - strengthExpGain: 0.08, - defenseExpGain: 0.08, - dexterityExpGain: 0.08, - agilityExpGain: 0.08, - charismaExpGain: 0.05, - reqdHacking: 101, - reqdStrength: 101, - reqdDefense: 101, - reqdDexterity: 101, - reqdAgility: 101, - reqdCharisma: 101, - reqdReputation: 8e3, - repMultiplier: 1, - }, - { - name: posNames.AgentCompanyPositions[1], // Secret Agent - nextPosition: posNames.AgentCompanyPositions[2], // Special Operative - baseSalary: 990, - hackingEffectiveness: 15, - strengthEffectiveness: 15, - defenseEffectiveness: 15, - dexterityEffectiveness: 20, - agilityEffectiveness: 20, - charismaEffectiveness: 15, - hackingExpGain: 0.1, - strengthExpGain: 0.15, - defenseExpGain: 0.15, - dexterityExpGain: 0.15, - agilityExpGain: 0.15, - charismaExpGain: 0.1, - reqdHacking: 201, - reqdStrength: 251, - reqdDefense: 251, - reqdDexterity: 251, - reqdAgility: 251, - reqdCharisma: 201, - reqdReputation: 32e3, - repMultiplier: 1.25, - }, - { - name: posNames.AgentCompanyPositions[2], // Special Operative - nextPosition: null, - baseSalary: 2000, - hackingEffectiveness: 15, - strengthEffectiveness: 15, - defenseEffectiveness: 15, - dexterityEffectiveness: 20, - agilityEffectiveness: 20, - charismaEffectiveness: 15, - hackingExpGain: 0.15, - strengthExpGain: 0.2, - defenseExpGain: 0.2, - dexterityExpGain: 0.2, - agilityExpGain: 0.2, - charismaExpGain: 0.15, - reqdHacking: 251, - reqdStrength: 501, - reqdDefense: 501, - reqdDexterity: 501, - reqdAgility: 501, - reqdCharisma: 251, - reqdReputation: 162e3, - repMultiplier: 1.5, - }, - { - name: posNames.MiscCompanyPositions[0], // Waiter - nextPosition: null, - baseSalary: 22, - strengthEffectiveness: 10, - dexterityEffectiveness: 10, - agilityEffectiveness: 10, - charismaEffectiveness: 70, - strengthExpGain: 0.02, - defenseExpGain: 0.02, - dexterityExpGain: 0.02, - agilityExpGain: 0.02, - charismaExpGain: 0.05, - repMultiplier: 1, - }, - { - name: posNames.MiscCompanyPositions[1], // Employee - nextPosition: null, - baseSalary: 22, - strengthEffectiveness: 10, - dexterityEffectiveness: 10, - agilityEffectiveness: 10, - charismaEffectiveness: 70, - strengthExpGain: 0.02, - defenseExpGain: 0.02, - dexterityExpGain: 0.02, - agilityExpGain: 0.02, - charismaExpGain: 0.04, - repMultiplier: 1, - }, - { - name: posNames.SoftwareConsultantCompanyPositions[0], // Software Consultant - nextPosition: posNames.SoftwareConsultantCompanyPositions[1], // Senior Software Consultant - baseSalary: 66, - hackingEffectiveness: 80, - charismaEffectiveness: 20, - hackingExpGain: 0.08, - charismaExpGain: 0.03, - reqdHacking: 51, - repMultiplier: 1, - }, - { - name: posNames.SoftwareConsultantCompanyPositions[1], // Senior Software Consultant - nextPosition: null, - baseSalary: 132, - hackingEffectiveness: 75, - charismaEffectiveness: 25, - hackingExpGain: 0.25, - charismaExpGain: 0.06, - reqdHacking: 251, - reqdCharisma: 51, - repMultiplier: 1.2, - }, - { - name: posNames.BusinessConsultantCompanyPositions[0], // Business Consultant - nextPosition: posNames.BusinessConsultantCompanyPositions[1], // Senior Business Consultant - baseSalary: 66, - hackingEffectiveness: 20, - charismaEffectiveness: 80, - hackingExpGain: 0.015, - charismaExpGain: 0.15, - reqdHacking: 6, - reqdCharisma: 51, - repMultiplier: 1, - }, - { - name: posNames.BusinessConsultantCompanyPositions[1], // Senior Business Consultant - nextPosition: null, - baseSalary: 525, - hackingEffectiveness: 15, - charismaEffectiveness: 85, - hackingExpGain: 0.015, - charismaExpGain: 0.3, - reqdHacking: 51, - reqdCharisma: 226, - repMultiplier: 1.2, - }, - { - name: posNames.PartTimeCompanyPositions[0], // Part-time waiter - nextPosition: null, - baseSalary: 20, - strengthEffectiveness: 10, - dexterityEffectiveness: 10, - agilityEffectiveness: 10, - charismaEffectiveness: 70, - strengthExpGain: 0.0075, - defenseExpGain: 0.0075, - dexterityExpGain: 0.0075, - agilityExpGain: 0.0075, - charismaExpGain: 0.04, - repMultiplier: 1, - }, - { - name: posNames.PartTimeCompanyPositions[1], // Part-time employee - nextPosition: null, - baseSalary: 20, - strengthEffectiveness: 10, - dexterityEffectiveness: 10, - agilityEffectiveness: 10, - charismaEffectiveness: 70, - strengthExpGain: 0.0075, - defenseExpGain: 0.0075, - dexterityExpGain: 0.0075, - agilityExpGain: 0.0075, - charismaExpGain: 0.03, - repMultiplier: 1, - }, -]; +export function getCompanyPositionMetadata(): Record { + return { + [JobName.software0]: { + nextPosition: JobName.software1, // Junior Software Engineer + baseSalary: 33, + charismaEffectiveness: 15, + charismaExpGain: 0.02, + hackingEffectiveness: 85, + hackingExpGain: 0.05, + reqdHacking: 1, + repMultiplier: 0.9, + }, + [JobName.software1]: { + nextPosition: JobName.software2, // Senior Software Engineer + baseSalary: 80, + charismaEffectiveness: 15, + charismaExpGain: 0.05, + hackingEffectiveness: 85, + hackingExpGain: 0.1, + reqdHacking: 51, + reqdReputation: 8e3, + repMultiplier: 1.1, + }, + [JobName.software2]: { + nextPosition: JobName.software3, // Lead Software Developer + baseSalary: 165, + charismaEffectiveness: 20, + charismaExpGain: 0.08, + hackingEffectiveness: 80, + hackingExpGain: 0.4, + reqdCharisma: 51, + reqdHacking: 251, + reqdReputation: 40e3, + repMultiplier: 1.3, + }, + [JobName.software3]: { + nextPosition: JobName.software4, // Head of Software + baseSalary: 500, + charismaEffectiveness: 25, + charismaExpGain: 0.1, + hackingEffectiveness: 75, + hackingExpGain: 0.8, + reqdCharisma: 151, + reqdHacking: 401, + reqdReputation: 200e3, + repMultiplier: 1.5, + }, + [JobName.software4]: { + nextPosition: JobName.software5, // Head of Engineering + baseSalary: 800, + charismaEffectiveness: 25, + charismaExpGain: 0.5, + hackingEffectiveness: 75, + hackingExpGain: 1, + reqdCharisma: 251, + reqdHacking: 501, + reqdReputation: 400e3, + repMultiplier: 1.6, + }, + [JobName.software5]: { + nextPosition: JobName.software6, // Vice President of Technology + baseSalary: 1650, + charismaEffectiveness: 25, + charismaExpGain: 0.5, + hackingEffectiveness: 75, + hackingExpGain: 1.1, + reqdCharisma: 251, + reqdHacking: 501, + reqdReputation: 800e3, + repMultiplier: 1.6, + }, + [JobName.software6]: { + nextPosition: JobName.software7, // Chief Technology Officer + baseSalary: 2310, + charismaEffectiveness: 30, + charismaExpGain: 0.6, + hackingEffectiveness: 70, + hackingExpGain: 1.2, + reqdCharisma: 401, + reqdHacking: 601, + reqdReputation: 1.6e6, + repMultiplier: 1.75, + }, + [JobName.software7]: { + nextPosition: null, + baseSalary: 2640, + charismaEffectiveness: 35, + charismaExpGain: 1, + hackingEffectiveness: 65, + hackingExpGain: 1.5, + reqdCharisma: 501, + reqdHacking: 751, + reqdReputation: 3.2e6, + repMultiplier: 2, + }, + [JobName.IT0]: { + nextPosition: JobName.IT1, // IT Analyst + baseSalary: 26, + charismaEffectiveness: 10, + charismaExpGain: 0.01, + hackingEffectiveness: 90, + hackingExpGain: 0.04, + reqdHacking: 1, + repMultiplier: 0.9, + }, + [JobName.IT1]: { + nextPosition: JobName.IT2, // IT Manager + baseSalary: 66, + charismaEffectiveness: 15, + charismaExpGain: 0.02, + hackingEffectiveness: 85, + hackingExpGain: 0.08, + reqdHacking: 26, + reqdReputation: 7e3, + repMultiplier: 1.1, + }, + [JobName.IT2]: { + nextPosition: JobName.IT3, // Systems Administrator + baseSalary: 132, + charismaEffectiveness: 20, + charismaExpGain: 0.1, + hackingEffectiveness: 80, + hackingExpGain: 0.3, + reqdCharisma: 51, + reqdHacking: 151, + reqdReputation: 35e3, + repMultiplier: 1.3, + }, + [JobName.IT3]: { + nextPosition: JobName.software5, // Head of Engineering + baseSalary: 410, + charismaEffectiveness: 20, + charismaExpGain: 0.2, + hackingEffectiveness: 80, + hackingExpGain: 0.5, + reqdCharisma: 76, + reqdHacking: 251, + reqdReputation: 175e3, + repMultiplier: 1.4, + }, + [JobName.securityEng]: { + nextPosition: JobName.software5, // Head of Engineering + baseSalary: 121, + charismaEffectiveness: 15, + charismaExpGain: 0.05, + hackingEffectiveness: 85, + hackingExpGain: 0.4, + reqdCharisma: 26, + reqdHacking: 151, + reqdReputation: 35e3, + repMultiplier: 1.2, + }, + [JobName.networkEng0]: { + nextPosition: JobName.networkEng1, // Network Administrator + baseSalary: 121, + charismaEffectiveness: 15, + charismaExpGain: 0.05, + hackingEffectiveness: 85, + hackingExpGain: 0.4, + reqdCharisma: 26, + reqdHacking: 151, + reqdReputation: 35e3, + repMultiplier: 1.2, + }, + [JobName.networkEng1]: { + nextPosition: JobName.software5, // Head of Engineering + baseSalary: 410, + charismaEffectiveness: 20, + charismaExpGain: 0.1, + hackingEffectiveness: 80, + hackingExpGain: 0.5, + reqdCharisma: 76, + reqdHacking: 251, + reqdReputation: 175e3, + repMultiplier: 1.3, + }, + [JobName.business0]: { + nextPosition: JobName.business1, // Business Analyst + baseSalary: 46, + charismaEffectiveness: 90, + charismaExpGain: 0.08, + hackingEffectiveness: 10, + hackingExpGain: 0.01, + reqdCharisma: 1, + reqdHacking: 1, + repMultiplier: 0.9, + }, + [JobName.business1]: { + nextPosition: JobName.business2, // Business Manager + baseSalary: 100, + charismaEffectiveness: 85, + charismaExpGain: 0.15, + hackingEffectiveness: 15, + hackingExpGain: 0.02, + reqdCharisma: 51, + reqdHacking: 6, + reqdReputation: 8e3, + repMultiplier: 1.1, + }, + [JobName.business2]: { + nextPosition: JobName.business3, // Operations Manager + baseSalary: 200, + charismaEffectiveness: 85, + charismaExpGain: 0.3, + hackingEffectiveness: 15, + hackingExpGain: 0.02, + reqdCharisma: 101, + reqdHacking: 51, + reqdReputation: 40e3, + repMultiplier: 1.3, + }, + [JobName.business3]: { + nextPosition: JobName.business4, // Chief Financial Officer + baseSalary: 660, + charismaEffectiveness: 85, + charismaExpGain: 0.4, + hackingEffectiveness: 15, + hackingExpGain: 0.02, + reqdCharisma: 226, + reqdHacking: 51, + reqdReputation: 200e3, + repMultiplier: 1.5, + }, + [JobName.business4]: { + nextPosition: JobName.business5, // Chief Executive Officer + baseSalary: 1950, + charismaEffectiveness: 90, + charismaExpGain: 1, + hackingEffectiveness: 10, + hackingExpGain: 0.05, + reqdCharisma: 501, + reqdHacking: 76, + reqdReputation: 800e3, + repMultiplier: 1.6, + }, + [JobName.business5]: { + nextPosition: null, + baseSalary: 3900, + charismaEffectiveness: 90, + charismaExpGain: 1.5, + hackingEffectiveness: 10, + hackingExpGain: 0.05, + reqdCharisma: 751, + reqdHacking: 101, + reqdReputation: 3.2e6, + repMultiplier: 1.75, + }, + [JobName.security0]: { + nextPosition: JobName.security1, // Security Officer + baseSalary: 50, + hackingEffectiveness: 5, + strengthEffectiveness: 20, + defenseEffectiveness: 20, + dexterityEffectiveness: 20, + agilityEffectiveness: 20, + charismaEffectiveness: 15, + hackingExpGain: 0.01, + strengthExpGain: 0.04, + defenseExpGain: 0.04, + dexterityExpGain: 0.04, + agilityExpGain: 0.04, + charismaExpGain: 0.02, + reqdStrength: 51, + reqdDefense: 51, + reqdDexterity: 51, + reqdAgility: 51, + reqdCharisma: 1, + repMultiplier: 1, + }, + [JobName.security1]: { + nextPosition: JobName.security2, // Security Supervisor + baseSalary: 195, + hackingEffectiveness: 10, + strengthEffectiveness: 20, + defenseEffectiveness: 20, + dexterityEffectiveness: 20, + agilityEffectiveness: 20, + charismaEffectiveness: 10, + hackingExpGain: 0.02, + strengthExpGain: 0.1, + defenseExpGain: 0.1, + dexterityExpGain: 0.1, + agilityExpGain: 0.1, + charismaExpGain: 0.05, + reqdHacking: 26, + reqdStrength: 151, + reqdDefense: 151, + reqdDexterity: 151, + reqdAgility: 151, + reqdCharisma: 51, + reqdReputation: 8e3, + repMultiplier: 1.1, + }, + [JobName.security2]: { + nextPosition: JobName.security3, // Head of Security + baseSalary: 660, + hackingEffectiveness: 10, + strengthEffectiveness: 15, + defenseEffectiveness: 15, + dexterityEffectiveness: 15, + agilityEffectiveness: 15, + charismaEffectiveness: 30, + hackingExpGain: 0.02, + strengthExpGain: 0.12, + defenseExpGain: 0.12, + dexterityExpGain: 0.12, + agilityExpGain: 0.12, + charismaExpGain: 0.1, + reqdHacking: 26, + reqdStrength: 251, + reqdDefense: 251, + reqdDexterity: 251, + reqdAgility: 251, + reqdCharisma: 101, + reqdReputation: 36e3, + repMultiplier: 1.25, + }, + [JobName.security3]: { + nextPosition: null, + baseSalary: 1320, + hackingEffectiveness: 10, + strengthEffectiveness: 15, + defenseEffectiveness: 15, + dexterityEffectiveness: 15, + agilityEffectiveness: 15, + charismaEffectiveness: 30, + hackingExpGain: 0.05, + strengthExpGain: 0.15, + defenseExpGain: 0.15, + dexterityExpGain: 0.15, + agilityExpGain: 0.15, + charismaExpGain: 0.15, + reqdHacking: 51, + reqdStrength: 501, + reqdDefense: 501, + reqdDexterity: 501, + reqdAgility: 501, + reqdCharisma: 151, + reqdReputation: 144e3, + repMultiplier: 1.4, + }, + [JobName.agent0]: { + nextPosition: JobName.agent1, // Secret Agent + baseSalary: 330, + hackingEffectiveness: 10, + strengthEffectiveness: 15, + defenseEffectiveness: 15, + dexterityEffectiveness: 20, + agilityEffectiveness: 20, + charismaEffectiveness: 20, + hackingExpGain: 0.04, + strengthExpGain: 0.08, + defenseExpGain: 0.08, + dexterityExpGain: 0.08, + agilityExpGain: 0.08, + charismaExpGain: 0.05, + reqdHacking: 101, + reqdStrength: 101, + reqdDefense: 101, + reqdDexterity: 101, + reqdAgility: 101, + reqdCharisma: 101, + reqdReputation: 8e3, + repMultiplier: 1, + }, + [JobName.agent1]: { + nextPosition: JobName.agent2, // Special Operative + baseSalary: 990, + hackingEffectiveness: 15, + strengthEffectiveness: 15, + defenseEffectiveness: 15, + dexterityEffectiveness: 20, + agilityEffectiveness: 20, + charismaEffectiveness: 15, + hackingExpGain: 0.1, + strengthExpGain: 0.15, + defenseExpGain: 0.15, + dexterityExpGain: 0.15, + agilityExpGain: 0.15, + charismaExpGain: 0.1, + reqdHacking: 201, + reqdStrength: 251, + reqdDefense: 251, + reqdDexterity: 251, + reqdAgility: 251, + reqdCharisma: 201, + reqdReputation: 32e3, + repMultiplier: 1.25, + }, + [JobName.agent2]: { + nextPosition: null, + baseSalary: 2000, + hackingEffectiveness: 15, + strengthEffectiveness: 15, + defenseEffectiveness: 15, + dexterityEffectiveness: 20, + agilityEffectiveness: 20, + charismaEffectiveness: 15, + hackingExpGain: 0.15, + strengthExpGain: 0.2, + defenseExpGain: 0.2, + dexterityExpGain: 0.2, + agilityExpGain: 0.2, + charismaExpGain: 0.15, + reqdHacking: 251, + reqdStrength: 501, + reqdDefense: 501, + reqdDexterity: 501, + reqdAgility: 501, + reqdCharisma: 251, + reqdReputation: 162e3, + repMultiplier: 1.5, + }, + [JobName.waiter]: { + nextPosition: null, + baseSalary: 22, + strengthEffectiveness: 10, + dexterityEffectiveness: 10, + agilityEffectiveness: 10, + charismaEffectiveness: 70, + strengthExpGain: 0.02, + defenseExpGain: 0.02, + dexterityExpGain: 0.02, + agilityExpGain: 0.02, + charismaExpGain: 0.05, + repMultiplier: 1, + }, + [JobName.employee]: { + nextPosition: null, + baseSalary: 22, + strengthEffectiveness: 10, + dexterityEffectiveness: 10, + agilityEffectiveness: 10, + charismaEffectiveness: 70, + strengthExpGain: 0.02, + defenseExpGain: 0.02, + dexterityExpGain: 0.02, + agilityExpGain: 0.02, + charismaExpGain: 0.04, + repMultiplier: 1, + }, + [JobName.softwareConsult0]: { + nextPosition: JobName.softwareConsult1, // Senior Software Consultant + baseSalary: 66, + hackingEffectiveness: 80, + charismaEffectiveness: 20, + hackingExpGain: 0.08, + charismaExpGain: 0.03, + reqdHacking: 51, + repMultiplier: 1, + }, + [JobName.softwareConsult1]: { + nextPosition: null, + baseSalary: 132, + hackingEffectiveness: 75, + charismaEffectiveness: 25, + hackingExpGain: 0.25, + charismaExpGain: 0.06, + reqdHacking: 251, + reqdCharisma: 51, + repMultiplier: 1.2, + }, + [JobName.businessConsult0]: { + nextPosition: JobName.businessConsult1, // Senior Business Consultant + baseSalary: 66, + hackingEffectiveness: 20, + charismaEffectiveness: 80, + hackingExpGain: 0.015, + charismaExpGain: 0.15, + reqdHacking: 6, + reqdCharisma: 51, + repMultiplier: 1, + }, + [JobName.businessConsult1]: { + nextPosition: null, + baseSalary: 525, + hackingEffectiveness: 15, + charismaEffectiveness: 85, + hackingExpGain: 0.015, + charismaExpGain: 0.3, + reqdHacking: 51, + reqdCharisma: 226, + repMultiplier: 1.2, + }, + [JobName.waiterPT]: { + nextPosition: null, + baseSalary: 20, + strengthEffectiveness: 10, + dexterityEffectiveness: 10, + agilityEffectiveness: 10, + charismaEffectiveness: 70, + strengthExpGain: 0.0075, + defenseExpGain: 0.0075, + dexterityExpGain: 0.0075, + agilityExpGain: 0.0075, + charismaExpGain: 0.04, + repMultiplier: 1, + }, + [JobName.employeePT]: { + nextPosition: null, + baseSalary: 20, + strengthEffectiveness: 10, + dexterityEffectiveness: 10, + agilityEffectiveness: 10, + charismaEffectiveness: 70, + strengthExpGain: 0.0075, + defenseExpGain: 0.0075, + dexterityExpGain: 0.0075, + agilityExpGain: 0.0075, + charismaExpGain: 0.03, + repMultiplier: 1, + }, + }; +} diff --git a/src/Company/data/JobTracks.ts b/src/Company/data/JobTracks.ts index b4d06aea2..5eedaf032 100644 --- a/src/Company/data/JobTracks.ts +++ b/src/Company/data/JobTracks.ts @@ -1,7 +1,5 @@ import { JobName } from "@enums"; - -// This entire file can be reworked to -export const SoftwareCompanyPositions: JobName[] = [ +export const softwareJobs = [ JobName.software0, JobName.software1, JobName.software2, @@ -11,14 +9,9 @@ export const SoftwareCompanyPositions: JobName[] = [ JobName.software6, JobName.software7, ]; - -export const ITCompanyPositions: JobName[] = [JobName.IT0, JobName.IT1, JobName.IT2, JobName.IT3]; - -export const SecurityEngineerCompanyPositions: JobName[] = [JobName.securityEng]; - -export const NetworkEngineerCompanyPositions: JobName[] = [JobName.networkEng0, JobName.networkEng1]; - -export const BusinessCompanyPositions: JobName[] = [ +export const itJobs = [JobName.IT0, JobName.IT1, JobName.IT2, JobName.IT3]; +export const netEngJobs = [JobName.networkEng0, JobName.networkEng1]; +export const businessJobs = [ JobName.business0, JobName.business1, JobName.business2, @@ -26,22 +19,7 @@ export const BusinessCompanyPositions: JobName[] = [ JobName.business4, JobName.business5, ]; - -export const SecurityCompanyPositions: JobName[] = [ - JobName.security0, - JobName.security1, - JobName.security2, - JobName.security3, - JobName.security4, - JobName.security5, -]; - -export const AgentCompanyPositions: JobName[] = [JobName.agent0, JobName.agent1, JobName.agent2]; - -export const MiscCompanyPositions: JobName[] = [JobName.waiter, JobName.employee]; - -export const SoftwareConsultantCompanyPositions: JobName[] = [JobName.softwareConsult0, JobName.softwareConsult1]; - -export const BusinessConsultantCompanyPositions: JobName[] = [JobName.businessConsult0, JobName.businessConsult1]; - -export const PartTimeCompanyPositions: JobName[] = [JobName.waiterPT, JobName.employeePT]; +export const securityJobs = [JobName.security0, JobName.security1, JobName.security2, JobName.security3]; +export const agentJobs = [JobName.agent0, JobName.agent1, JobName.agent2]; +export const softwareConsultJobs = [JobName.softwareConsult0, JobName.softwareConsult1]; +export const businessConsultJobs = [JobName.businessConsult0, JobName.businessConsult1]; diff --git a/src/Company/ui/QuitJobModal.tsx b/src/Company/ui/QuitJobModal.tsx index 2e7f8a48c..0653a8770 100644 --- a/src/Company/ui/QuitJobModal.tsx +++ b/src/Company/ui/QuitJobModal.tsx @@ -4,18 +4,19 @@ import { Player } from "@player"; import { Modal } from "../../ui/React/Modal"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; +import { CompanyName } from "../Enums"; interface IProps { open: boolean; onClose: () => void; - locName: string; + companyName: CompanyName; company: Company; onQuit: () => void; } export function QuitJobModal(props: IProps): React.ReactElement { function quit(): void { - Player.quitJob(props.locName); + Player.quitJob(props.companyName); props.onQuit(); props.onClose(); } diff --git a/src/Company/utils.ts b/src/Company/utils.ts new file mode 100644 index 000000000..e936ccad0 --- /dev/null +++ b/src/Company/utils.ts @@ -0,0 +1,11 @@ +import type { CompanyName, LocationName } from "@enums"; + +type LocationNameString = `${LocationName}`; +type CompanyNameString = `${CompanyName}`; +type CompanyNamesAreAllLocationNames = CompanyNameString extends LocationNameString ? true : false; +const __companyNameCheck: CompanyNamesAreAllLocationNames = true; + +export function companyNameAsLocationName(companyName: CompanyName): LocationName { + // Due to the check above, we know that all company names are valid location names. + return companyName as unknown as LocationName; +} diff --git a/src/DevMenu/ui/CompaniesDev.tsx b/src/DevMenu/ui/CompaniesDev.tsx index 26e4263a7..b233bed33 100644 --- a/src/DevMenu/ui/CompaniesDev.tsx +++ b/src/DevMenu/ui/CompaniesDev.tsx @@ -9,64 +9,67 @@ import Button from "@mui/material/Button"; import Select, { SelectChangeEvent } from "@mui/material/Select"; import MenuItem from "@mui/material/MenuItem"; -import { FactionName } from "@enums"; -import { Companies as AllCompanies } from "../../Company/Companies"; +import { CompanyName } from "@enums"; +import { Companies } from "../../Company/Companies"; import { Adjuster } from "./Adjuster"; +import { isMember } from "../../utils/EnumHelper"; +import { getRecordValues } from "../../Types/Record"; const bigNumber = 1e12; export function CompaniesDev(): React.ReactElement { - const [company, setCompany] = useState(FactionName.ECorp as string); + const [companyName, setCompanyName] = useState(CompanyName.ECorp); function setCompanyDropdown(event: SelectChangeEvent): void { - setCompany(event.target.value); + if (!isMember("CompanyName", event.target.value)) return; + setCompanyName(event.target.value); } function resetCompanyRep(): void { - AllCompanies[company].playerReputation = 0; + Companies[companyName].playerReputation = 0; } function modifyCompanyRep(modifier: number): (x: number) => void { return function (reputation: number): void { - const c = AllCompanies[company]; - if (c != null && !isNaN(reputation)) { - c.playerReputation += reputation * modifier; + const company = Companies[companyName]; + if (!isNaN(reputation)) { + company.playerReputation += reputation * modifier; } }; } function modifyCompanyFavor(modifier: number): (x: number) => void { return function (favor: number): void { - const c = AllCompanies[company]; - if (c != null && !isNaN(favor)) { - c.favor += favor * modifier; + const company = Companies[companyName]; + if (!isNaN(favor)) { + company.favor += favor * modifier; } }; } function resetCompanyFavor(): void { - AllCompanies[company].favor = 0; + Companies[companyName].favor = 0; } function tonsOfRepCompanies(): void { - for (const c of Object.keys(AllCompanies)) { - AllCompanies[c].playerReputation = bigNumber; + for (const company of getRecordValues(Companies)) { + company.playerReputation = bigNumber; } } function resetAllRepCompanies(): void { - for (const c of Object.keys(AllCompanies)) { - AllCompanies[c].playerReputation = 0; + for (const company of getRecordValues(Companies)) { + company.playerReputation = 0; } } function tonsOfFavorCompanies(): void { - for (const c of Object.keys(AllCompanies)) { - AllCompanies[c].favor = bigNumber; + for (const company of getRecordValues(Companies)) { + company.favor = bigNumber; } } function resetAllFavorCompanies(): void { - for (const c of Object.keys(AllCompanies)) { - AllCompanies[c].favor = 0; + for (const company of getRecordValues(Companies)) { + company.favor = 0; } } @@ -83,8 +86,8 @@ export function CompaniesDev(): React.ReactElement { Company: - + {Object.values(Companies).map((company) => ( {company.name} diff --git a/src/Enums.ts b/src/Enums.ts index 6d3b3190f..d38f0727c 100644 --- a/src/Enums.ts +++ b/src/Enums.ts @@ -2,6 +2,7 @@ export * from "./Augmentation/Enums"; export * from "./Bladeburner/Enums"; +export * from "./Company/Enums"; export * from "./Corporation/Enums"; export * from "./Crime/Enums"; export * from "./Faction/Enums"; diff --git a/src/Faction/Factions.ts b/src/Faction/Factions.ts index 94b970e81..a72967f85 100644 --- a/src/Faction/Factions.ts +++ b/src/Faction/Factions.ts @@ -21,7 +21,6 @@ for (const aug of getRecordValues(Augmentations)) { } export function loadFactions(saveString: string): void { - // The only information that should be loaded from player save is const loadedFactions = JSON.parse(saveString, Reviver) as unknown; // This loading method allows invalid data in player save, but just ignores anything invalid if (!loadedFactions) return; diff --git a/src/Hacknet/HacknetHelpers.tsx b/src/Hacknet/HacknetHelpers.tsx index 888bfeed0..913960936 100644 --- a/src/Hacknet/HacknetHelpers.tsx +++ b/src/Hacknet/HacknetHelpers.tsx @@ -22,6 +22,7 @@ import { Player } from "@player"; import { GetServer } from "../Server/AllServers"; import { Server } from "../Server/Server"; import { Companies } from "../Company/Companies"; +import { isMember } from "../utils/EnumHelper"; // Returns a boolean indicating whether the player has Hacknet Servers // (the upgraded form of Hacknet Nodes) @@ -564,7 +565,7 @@ export function purchaseHashUpgrade(upgName: string, upgTarget: string, count = break; } case "Company Favor": { - if (!(upgTarget in Companies)) { + if (!isMember("CompanyName", upgTarget)) { console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); throw new Error(`'${upgTarget}' is not a company.`); } diff --git a/src/Hacknet/ui/HacknetUpgradeElem.tsx b/src/Hacknet/ui/HacknetUpgradeElem.tsx index 38113fb70..8ab2d1c60 100644 --- a/src/Hacknet/ui/HacknetUpgradeElem.tsx +++ b/src/Hacknet/ui/HacknetUpgradeElem.tsx @@ -15,8 +15,9 @@ import Typography from "@mui/material/Typography"; import Paper from "@mui/material/Paper"; import Button from "@mui/material/Button"; import { SelectChangeEvent } from "@mui/material/Select"; -import { FactionName } from "@enums"; -import { companiesMetadata } from "../../Company/data/CompaniesMetadata"; +import { CompanyName, FactionName } from "@enums"; +import { PartialRecord } from "../../Types/Record"; +import { isMember } from "../../utils/EnumHelper"; interface IProps { hashManager: HashManager; @@ -24,8 +25,9 @@ interface IProps { rerender: () => void; } +// Key is the hash upgrade name const serversMap: Record = {}; -const companiesMap: Record = {}; +const companiesMap: PartialRecord = {}; export function HacknetUpgradeElem(props: IProps): React.ReactElement { const [selectedServer, setSelectedServer] = useState( @@ -35,10 +37,9 @@ export function HacknetUpgradeElem(props: IProps): React.ReactElement { setSelectedServer(event.target.value); serversMap[props.upg.name] = event.target.value; } - const [selectedCompany, setSelectedCompany] = useState( - companiesMap[props.upg.name] ? companiesMap[props.upg.name] : companiesMetadata[0].name, - ); - function changeTargetCompany(event: SelectChangeEvent): void { + const [selectedCompany, setSelectedCompany] = useState(companiesMap[props.upg.name] ?? CompanyName.NoodleBar); + function changeTargetCompany(event: SelectChangeEvent): void { + if (!isMember("CompanyName", event.target.value)) return; setSelectedCompany(event.target.value); companiesMap[props.upg.name] = event.target.value; } diff --git a/src/Locations/ui/CompanyLocation.tsx b/src/Locations/ui/CompanyLocation.tsx index 2a8f33027..456356086 100644 --- a/src/Locations/ui/CompanyLocation.tsx +++ b/src/Locations/ui/CompanyLocation.tsx @@ -12,11 +12,10 @@ import Box from "@mui/material/Box"; import { ApplyToJobButton } from "./ApplyToJobButton"; import { Locations } from "../Locations"; -import { LocationName } from "@enums"; +import { CompanyName, JobName } from "@enums"; import { Companies } from "../../Company/Companies"; import { CompanyPositions } from "../../Company/CompanyPositions"; -import * as posNames from "../../Company/data/JobTracks"; import { Reputation } from "../../ui/React/Reputation"; import { Favor } from "../../ui/React/Favor"; @@ -26,9 +25,10 @@ import { Player } from "@player"; import { QuitJobModal } from "../../Company/ui/QuitJobModal"; import { CompanyWork } from "../../Work/CompanyWork"; import { useRerender } from "../../ui/React/hooks"; +import { companyNameAsLocationName } from "../../Company/utils"; interface IProps { - locName: LocationName; + companyName: CompanyName; } export function CompanyLocation(props: IProps): React.ReactElement { @@ -39,17 +39,18 @@ export function CompanyLocation(props: IProps): React.ReactElement { * We'll keep a reference to the Company that this component is being rendered for, * so we don't have to look it up every time */ - const company = Companies[props.locName]; - if (company == null) throw new Error(`CompanyLocation component constructed with invalid company: ${props.locName}`); + const company = Companies[props.companyName]; + if (company == null) + throw new Error(`CompanyLocation component constructed with invalid company: ${props.companyName}`); /** Reference to the Location that this component is being rendered for */ - const location = Locations[props.locName]; + const location = Locations[props.companyName]; if (location == null) { - throw new Error(`CompanyLocation component constructed with invalid location: ${props.locName}`); + throw new Error(`CompanyLocation component constructed with invalid location: ${props.companyName}`); } /** Name of company position that player holds, if applicable */ - const jobTitle = Player.jobs[props.locName] ? Player.jobs[props.locName] : null; + const jobTitle = Player.jobs[props.companyName] ? Player.jobs[props.companyName] : null; /** * CompanyPosition object for the job that the player holds at this company @@ -57,7 +58,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { */ const companyPosition = jobTitle ? CompanyPositions[jobTitle] : null; - Player.location = props.locName; + Player.location = companyNameAsLocationName(props.companyName); function applyForAgentJob(e: React.MouseEvent): void { if (!e.isTrusted) { @@ -152,7 +153,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { return; } if (!location.infiltrationData) - throw new Error(`trying to start infiltration at ${props.locName} but the infiltrationData is null`); + throw new Error(`trying to start infiltration at ${props.companyName} but the infiltrationData is null`); Router.toPage(Page.Infiltration, { location }); } @@ -167,7 +168,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { Player.startWork( new CompanyWork({ singularity: false, - companyName: props.locName, + companyName: props.companyName, }), ); Player.startFocusing(); @@ -224,7 +225,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { @@ -243,7 +244,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasBusinessConsultantPositions() && ( @@ -251,7 +252,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasBusinessPositions() && ( @@ -259,7 +260,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasEmployeePositions() && ( @@ -267,7 +268,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasEmployeePositions() && ( @@ -275,7 +276,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasITPositions() && ( @@ -283,7 +284,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasSecurityPositions() && ( @@ -291,7 +292,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasSoftwareConsultantPositions() && ( @@ -299,7 +300,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasSoftwarePositions() && ( @@ -307,7 +308,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasWaiterPositions() && ( @@ -315,7 +316,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { {company.hasWaiterPositions() && ( diff --git a/src/Locations/ui/GenericLocation.tsx b/src/Locations/ui/GenericLocation.tsx index bd2abbbe4..e6f589f26 100644 --- a/src/Locations/ui/GenericLocation.tsx +++ b/src/Locations/ui/GenericLocation.tsx @@ -31,6 +31,7 @@ import { Router } from "../../ui/GameRoot"; import { Page } from "../../ui/Router"; import { serverMetadata } from "../../Server/data/servers"; import { Tooltip } from "@mui/material"; +import { getEnumHelper } from "../../utils/EnumHelper"; interface IProps { loc: Location; @@ -45,7 +46,10 @@ export function GenericLocation({ loc }: IProps): React.ReactElement { const content: React.ReactNode[] = []; if (loc.types.includes(LocationType.Company)) { - content.push(); + if (!getEnumHelper("CompanyName").isMember(loc.name)) { + throw new Error(`Location name ${loc.name} is for a company but is not a company name.`); + } + content.push(); } if (loc.types.includes(LocationType.Gym)) { diff --git a/src/NetscriptFunctions/Singularity.ts b/src/NetscriptFunctions/Singularity.ts index f15480888..fc2145026 100644 --- a/src/NetscriptFunctions/Singularity.ts +++ b/src/NetscriptFunctions/Singularity.ts @@ -1,5 +1,4 @@ import type { Singularity as ISingularity } from "@nsdefs"; -import type { Company } from "../Company/Company"; import { Player } from "@player"; import { @@ -31,7 +30,6 @@ import { Programs } from "../Programs/Programs"; import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber"; import { currentNodeMults } from "../BitNode/BitNodeMultipliers"; import { Companies } from "../Company/Companies"; -import { companiesMetadata } from "../Company/data/CompaniesMetadata"; import { Factions } from "../Faction/Factions"; import { helpers } from "../Netscript/NetscriptHelpers"; import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions"; @@ -42,7 +40,7 @@ import { Server } from "../Server/Server"; import { netscriptCanHack } from "../Hacking/netscriptCanHack"; import { FactionInfos } from "../Faction/FactionInfo"; import { donate, repNeededToDonate } from "../Faction/formulas/donation"; -import { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper"; +import { InternalAPI, removedFunction } from "../Netscript/APIWrapper"; import { enterBitNode } from "../RedPill"; import { ClassWork } from "../Work/ClassWork"; import { CreateProgramWork, isCreateProgramWork } from "../Work/CreateProgramWork"; @@ -56,14 +54,10 @@ import { Engine } from "../engine"; import { getEnumHelper } from "../utils/EnumHelper"; import { ScriptFilePath, resolveScriptFilePath } from "../Paths/ScriptFilePath"; import { root } from "../Paths/Directory"; +import { companyNameAsLocationName } from "../Company/utils"; +import { getRecordEntries } from "../Types/Record"; export function NetscriptSingularity(): InternalAPI { - const getCompany = function (ctx: NetscriptContext, name: string): Company { - const company = Companies[name]; - if (!company) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company name: '${name}'`); - return company; - }; - const runAfterReset = function (cbScript: ScriptFilePath) { //Run a script after reset if (!cbScript) return; @@ -670,45 +664,35 @@ export function NetscriptSingularity(): InternalAPI { }, getCompanyPositions: (ctx) => (_companyName) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); - // Make sure its a valid company - if (companyName == null || companyName === "" || !Companies[companyName]) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company: '${companyName}'`); - } - - return Object.entries(CompanyPositions) + return getRecordEntries(CompanyPositions) .filter((_position) => Companies[companyName].hasPosition(_position[0])) .map((_position) => _position[1].name); }, getCompanyPositionInfo: (ctx) => (_companyName, _positionName) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); const positionName = getEnumHelper("JobName").nsGetMember(ctx, _positionName, "positionName"); + const company = Companies[companyName]; - // Make sure its a valid company - if (!(companyName in Companies)) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company: '${companyName}'`); - } - - if (!Companies[companyName].hasPosition(positionName)) { + if (!company.hasPosition(positionName)) { throw helpers.makeRuntimeErrorMsg(ctx, `Company '${companyName}' does not have position '${positionName}'`); } - const c = CompanyPositions[positionName]; - const n = companiesMetadata.filter((company) => company.name === companyName)[0]; + const job = CompanyPositions[positionName]; const res = { name: CompanyPositions[positionName].name, nextPosition: CompanyPositions[positionName].nextPosition, - salary: CompanyPositions[positionName].baseSalary * n.salaryMultiplier, + salary: CompanyPositions[positionName].baseSalary * company.salaryMultiplier, requiredReputation: CompanyPositions[positionName].requiredReputation, requiredSkills: { - hacking: c.requiredHacking > 0 ? c.requiredHacking + n.jobStatReqOffset : 0, - strength: c.requiredStrength > 0 ? c.requiredStrength + n.jobStatReqOffset : 0, - defense: c.requiredDefense > 0 ? c.requiredDefense + n.jobStatReqOffset : 0, - dexterity: c.requiredDexterity > 0 ? c.requiredDexterity + n.jobStatReqOffset : 0, - agility: c.requiredAgility > 0 ? c.requiredAgility + n.jobStatReqOffset : 0, - charisma: c.requiredCharisma > 0 ? c.requiredCharisma + n.jobStatReqOffset : 0, + hacking: job.requiredHacking > 0 ? job.requiredHacking + company.jobStatReqOffset : 0, + strength: job.requiredStrength > 0 ? job.requiredStrength + company.jobStatReqOffset : 0, + defense: job.requiredDefense > 0 ? job.requiredDefense + company.jobStatReqOffset : 0, + dexterity: job.requiredDexterity > 0 ? job.requiredDexterity + company.jobStatReqOffset : 0, + agility: job.requiredAgility > 0 ? job.requiredAgility + company.jobStatReqOffset : 0, + charisma: job.requiredCharisma > 0 ? job.requiredCharisma + company.jobStatReqOffset : 0, intelligence: 0, }, }; @@ -718,26 +702,15 @@ export function NetscriptSingularity(): InternalAPI { (ctx) => (_companyName, _focus = true) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); const focus = !!_focus; - // Make sure its a valid company - if (companyName == null || companyName === "" || !Companies[companyName]) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company: '${companyName}'`); - } - + const jobName = Player.jobs[companyName]; // Make sure player is actually employed at the company - if (!Object.keys(Player.jobs).includes(companyName)) { + if (!jobName) { throw helpers.makeRuntimeErrorMsg(ctx, `You do not have a job at: '${companyName}'`); } - // Check to make sure company position data is valid - const companyPositionName = Player.jobs[companyName]; - const companyPosition = CompanyPositions[companyPositionName]; - if (companyPositionName === "" || !companyPosition) { - throw helpers.makeRuntimeErrorMsg(ctx, `You do not have a job`); - } - const wasFocused = Player.focus; Player.startWork( @@ -753,16 +726,15 @@ export function NetscriptSingularity(): InternalAPI { Player.stopFocusing(); Router.toPage(Page.Terminal); } - helpers.log(ctx, () => `Began working at '${companyName}' with position '${companyPositionName}'`); + helpers.log(ctx, () => `Began working at '${companyName}' with position '${jobName}'`); return true; }, applyToCompany: (ctx) => (_companyName, _field) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); const field = helpers.string(ctx, "field", _field); - getCompany(ctx, companyName); - Player.location = companyName as LocationName; + Player.location = companyNameAsLocationName(companyName); let res; switch (field.toLowerCase()) { case "software": @@ -826,26 +798,23 @@ export function NetscriptSingularity(): InternalAPI { }, quitJob: (ctx) => (_companyName) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); Player.quitJob(companyName); }, getCompanyRep: (ctx) => (_companyName) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); - const company = getCompany(ctx, companyName); - return company.playerReputation; + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); + return Companies[companyName].playerReputation; }, getCompanyFavor: (ctx) => (_companyName) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); - const company = getCompany(ctx, companyName); - return company.favor; + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); + return Companies[companyName].favor; }, getCompanyFavorGain: (ctx) => (_companyName) => { helpers.checkSingularityAccess(ctx); - const companyName = helpers.string(ctx, "companyName", _companyName); - const company = getCompany(ctx, companyName); - return company.getFavorGain(); + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); + return Companies[companyName].getFavorGain(); }, checkFactionInvitations: (ctx) => () => { helpers.checkSingularityAccess(ctx); diff --git a/src/NetscriptFunctions/Sleeve.ts b/src/NetscriptFunctions/Sleeve.ts index 9505a1fdc..1f7ec0f02 100644 --- a/src/NetscriptFunctions/Sleeve.ts +++ b/src/NetscriptFunctions/Sleeve.ts @@ -78,9 +78,9 @@ export function NetscriptSleeve(): InternalAPI { checkSleeveNumber(ctx, sleeveNumber); return Player.sleeves[sleeveNumber].travel(cityName); }, - setToCompanyWork: (ctx) => (_sleeveNumber, acompanyName) => { + setToCompanyWork: (ctx) => (_sleeveNumber, _companyName) => { const sleeveNumber = helpers.number(ctx, "sleeveNumber", _sleeveNumber); - const companyName = helpers.string(ctx, "companyName", acompanyName); + const companyName = getEnumHelper("CompanyName").nsGetMember(ctx, _companyName); checkSleeveAPIAccess(ctx); checkSleeveNumber(ctx, sleeveNumber); diff --git a/src/PersonObjects/Player/PlayerObject.ts b/src/PersonObjects/Player/PlayerObject.ts index b7bb59e29..c662e1348 100644 --- a/src/PersonObjects/Player/PlayerObject.ts +++ b/src/PersonObjects/Player/PlayerObject.ts @@ -17,7 +17,7 @@ import * as serverMethods from "./PlayerObjectServerMethods"; import * as workMethods from "./PlayerObjectWorkMethods"; import { setPlayer } from "../../Player"; -import { FactionName, LocationName } from "@enums"; +import { CompanyName, FactionName, JobName, LocationName } from "@enums"; import { HashManager } from "../../Hacknet/HashManager"; import { MoneySourceTracker } from "../../utils/MoneySourceTracker"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../../utils/JSONReviver"; @@ -26,7 +26,8 @@ import { cyrb53 } from "../../utils/StringHelperFunctions"; import { getRandomInt } from "../../utils/helpers/getRandomInt"; import { CONSTANTS } from "../../Constants"; import { Person } from "../Person"; -import { getEnumHelper } from "../../utils/EnumHelper"; +import { isMember } from "../../utils/EnumHelper"; +import { PartialRecord } from "../../Types/Record"; export class PlayerObject extends Person implements IPlayer { // Player-specific properties @@ -43,7 +44,7 @@ export class PlayerObject extends Person implements IPlayer { hashManager = new HashManager(); hasTixApiAccess = false; hasWseAccount = false; - jobs: Record = {}; + jobs: PartialRecord = {}; karma = 0; numPeopleKilled = 0; location = LocationName.TravelAgency; @@ -172,11 +173,9 @@ export class PlayerObject extends Person implements IPlayer { player.hp = { current: player.hp?.current ?? 10, max: player.hp?.max ?? 10 }; player.money ??= 0; // Just remove from the save file any augs that have invalid name - player.augmentations = player.augmentations.filter((ownedAug) => - getEnumHelper("AugmentationName").isMember(ownedAug.name), - ); + player.augmentations = player.augmentations.filter((ownedAug) => isMember("AugmentationName", ownedAug.name)); player.queuedAugmentations = player.queuedAugmentations.filter((ownedAug) => - getEnumHelper("AugmentationName").isMember(ownedAug.name), + isMember("AugmentationName", ownedAug.name), ); player.updateSkillLevels(); // Converstion code for Player.sourceFiles is here instead of normal save conversion area because it needs @@ -186,6 +185,12 @@ export class PlayerObject extends Person implements IPlayer { type OldSourceFiles = { n: number; lvl: number }[]; player.sourceFiles = new JSONMap((player.sourceFiles as OldSourceFiles).map(({ n, lvl }) => [n, lvl])); } + // Remove any invalid jobs + for (const [loadedCompanyName, loadedJobName] of Object.entries(player.jobs)) { + if (!isMember("CompanyName", loadedCompanyName) || !isMember("JobName", loadedJobName)) { + delete player.jobs[loadedCompanyName as CompanyName]; + } + } return player; } } diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts index de4cd10e1..be3becf89 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.ts @@ -1,4 +1,13 @@ -import { AugmentationName, CityName, CompletedProgramName, FactionName, LocationName, ToastVariant } from "@enums"; +import { + AugmentationName, + CityName, + CompanyName, + CompletedProgramName, + FactionName, + JobName, + LocationName, + ToastVariant, +} from "@enums"; import type { PlayerObject } from "./PlayerObject"; import type { ProgramFilePath } from "../../Paths/ProgramFilePath"; @@ -13,7 +22,6 @@ import { getNextCompanyPositionHelper } from "../../Company/GetNextCompanyPositi import { getJobRequirementText } from "../../Company/GetJobRequirementText"; import { CompanyPositions } from "../../Company/CompanyPositions"; import { CompanyPosition } from "../../Company/CompanyPosition"; -import * as posNames from "../../Company/data/JobTracks"; import { CONSTANTS } from "../../Constants"; import { Exploit } from "../../Exploits/Exploit"; import { Faction } from "../../Faction/Faction"; @@ -44,6 +52,7 @@ import { achievements } from "../../Achievements/Achievements"; import { isCompanyWork } from "../../Work/CompanyWork"; import { serverMetadata } from "../../Server/data/servers"; +import { getEnumHelper, isMember } from "../../utils/EnumHelper"; export function init(this: PlayerObject): void { /* Initialize Player's home computer */ @@ -262,12 +271,9 @@ export function hospitalize(this: PlayerObject): number { //The 'sing' argument designates whether or not this is being called from //the applyToCompany() Netscript Singularity function export function applyForJob(this: PlayerObject, entryPosType: CompanyPosition, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - if (!company) { - console.error(`Could not find company that matches the location: ${this.location}. Player.applyToCompany() failed`); - return false; - } - + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; //Company being applied to let pos = entryPosType; if (!this.isQualified(company, pos)) { @@ -347,7 +353,7 @@ export function getNextCompanyPosition( return entryPosType; } -export function quitJob(this: PlayerObject, company: string): void { +export function quitJob(this: PlayerObject, company: CompanyName): void { if (isCompanyWork(this.currentWork) && this.currentWork.companyName === company) { this.finishWork(true); } @@ -370,21 +376,23 @@ export function hasJob(this: PlayerObject): boolean { } export function applyForSoftwareJob(this: PlayerObject, sing = false): boolean { - return this.applyForJob(CompanyPositions[posNames.SoftwareCompanyPositions[0]], sing); + return this.applyForJob(CompanyPositions[JobName.software0], sing); } export function applyForSoftwareConsultantJob(this: PlayerObject, sing = false): boolean { - return this.applyForJob(CompanyPositions[posNames.SoftwareConsultantCompanyPositions[0]], sing); + return this.applyForJob(CompanyPositions[JobName.softwareConsult0], sing); } export function applyForItJob(this: PlayerObject, sing = false): boolean { - return this.applyForJob(CompanyPositions[posNames.ITCompanyPositions[0]], sing); + return this.applyForJob(CompanyPositions[JobName.IT0], sing); } export function applyForSecurityEngineerJob(this: PlayerObject, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - if (this.isQualified(company, CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]])) { - return this.applyForJob(CompanyPositions[posNames.SecurityEngineerCompanyPositions[0]], sing); + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; + if (this.isQualified(company, CompanyPositions[JobName.securityEng])) { + return this.applyForJob(CompanyPositions[JobName.securityEng], sing); } else { if (!sing) { dialogBoxCreate("Unfortunately, you do not qualify for this position"); @@ -394,9 +402,11 @@ export function applyForSecurityEngineerJob(this: PlayerObject, sing = false): b } export function applyForNetworkEngineerJob(this: PlayerObject, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - if (this.isQualified(company, CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]])) { - const pos = CompanyPositions[posNames.NetworkEngineerCompanyPositions[0]]; + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; + if (this.isQualified(company, CompanyPositions[JobName.networkEng0])) { + const pos = CompanyPositions[JobName.networkEng0]; return this.applyForJob(pos, sing); } else { if (!sing) { @@ -407,23 +417,25 @@ export function applyForNetworkEngineerJob(this: PlayerObject, sing = false): bo } export function applyForBusinessJob(this: PlayerObject, sing = false): boolean { - return this.applyForJob(CompanyPositions[posNames.BusinessCompanyPositions[0]], sing); + return this.applyForJob(CompanyPositions[JobName.business0], sing); } export function applyForBusinessConsultantJob(this: PlayerObject, sing = false): boolean { - return this.applyForJob(CompanyPositions[posNames.BusinessConsultantCompanyPositions[0]], sing); + return this.applyForJob(CompanyPositions[JobName.businessConsult0], sing); } export function applyForSecurityJob(this: PlayerObject, sing = false): boolean { // TODO Police Jobs // Indexing starts at 2 because 0 is for police officer - return this.applyForJob(CompanyPositions[posNames.SecurityCompanyPositions[2]], sing); + return this.applyForJob(CompanyPositions[JobName.security0], sing); } export function applyForAgentJob(this: PlayerObject, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - if (this.isQualified(company, CompanyPositions[posNames.AgentCompanyPositions[0]])) { - const pos = CompanyPositions[posNames.AgentCompanyPositions[0]]; + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; + if (this.isQualified(company, CompanyPositions[JobName.agent0])) { + const pos = CompanyPositions[JobName.agent0]; return this.applyForJob(pos, sing); } else { if (!sing) { @@ -434,8 +446,10 @@ export function applyForAgentJob(this: PlayerObject, sing = false): boolean { } export function applyForEmployeeJob(this: PlayerObject, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - const position = posNames.MiscCompanyPositions[1]; + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; + const position = JobName.employee; // Check if this company has the position if (!company.hasPosition(position)) { return false; @@ -458,8 +472,10 @@ export function applyForEmployeeJob(this: PlayerObject, sing = false): boolean { } export function applyForPartTimeEmployeeJob(this: PlayerObject, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - const position = posNames.PartTimeCompanyPositions[1]; + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; + const position = JobName.employeePT; // Check if this company has the position if (!company.hasPosition(position)) { return false; @@ -481,8 +497,10 @@ export function applyForPartTimeEmployeeJob(this: PlayerObject, sing = false): b } export function applyForWaiterJob(this: PlayerObject, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - const position = posNames.MiscCompanyPositions[0]; + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; + const position = JobName.waiter; // Check if this company has the position if (!company.hasPosition(position)) { return false; @@ -502,8 +520,10 @@ export function applyForWaiterJob(this: PlayerObject, sing = false): boolean { } export function applyForPartTimeWaiterJob(this: PlayerObject, sing = false): boolean { - const company = Companies[this.location]; //Company being applied to - const position = posNames.PartTimeCompanyPositions[0]; + const companyName = getEnumHelper("CompanyName").getMember(this.location); + if (!companyName) return false; + const company = Companies[companyName]; + const position = JobName.waiterPT; // Check if this company has the position if (!company.hasPosition(position)) { return false; @@ -594,20 +614,16 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { const allPositions = Object.values(this.jobs); // Given a company name, safely returns the reputation (returns 0 if invalid company is specified) - function getCompanyRep(companyName: string): number { + function getCompanyRep(companyName: CompanyName): number { const company = Companies[companyName]; - if (company == null) { - return 0; - } else { - return company.playerReputation; - } + return company.playerReputation; } // Helper function that returns a boolean indicating whether the Player meets // the requirements for the specified company. There are two requirements: // 1. High enough reputation // 2. Player is employed at the company - function checkMegacorpRequirements(companyName: string): boolean { + function checkMegacorpRequirements(companyName: CompanyName): boolean { const serverMeta = serverMetadata.find((s) => s.specialName === companyName); const server = GetServer(serverMeta ? serverMeta.hostname : ""); const bonus = (server as Server).backdoorInstalled ? -100e3 : 0; @@ -673,7 +689,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !ecorpFac.isBanned && !ecorpFac.isMember && !ecorpFac.alreadyInvited && - checkMegacorpRequirements(LocationName.AevumECorp) + checkMegacorpRequirements(CompanyName.ECorp) ) { invitedFactions.push(ecorpFac); } @@ -684,7 +700,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !megacorpFac.isBanned && !megacorpFac.isMember && !megacorpFac.alreadyInvited && - checkMegacorpRequirements(LocationName.Sector12MegaCorp) + checkMegacorpRequirements(CompanyName.MegaCorp) ) { invitedFactions.push(megacorpFac); } @@ -695,7 +711,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !bachmanandassociatesFac.isBanned && !bachmanandassociatesFac.isMember && !bachmanandassociatesFac.alreadyInvited && - checkMegacorpRequirements(LocationName.AevumBachmanAndAssociates) + checkMegacorpRequirements(CompanyName.BachmanAndAssociates) ) { invitedFactions.push(bachmanandassociatesFac); } @@ -706,19 +722,14 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !bladeindustriesFac.isBanned && !bladeindustriesFac.isMember && !bladeindustriesFac.alreadyInvited && - checkMegacorpRequirements(LocationName.Sector12BladeIndustries) + checkMegacorpRequirements(CompanyName.BladeIndustries) ) { invitedFactions.push(bladeindustriesFac); } //NWO const nwoFac = Factions[FactionName.NWO]; - if ( - !nwoFac.isBanned && - !nwoFac.isMember && - !nwoFac.alreadyInvited && - checkMegacorpRequirements(LocationName.VolhavenNWO) - ) { + if (!nwoFac.isBanned && !nwoFac.isMember && !nwoFac.alreadyInvited && checkMegacorpRequirements(CompanyName.NWO)) { invitedFactions.push(nwoFac); } @@ -728,7 +739,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !clarkeincorporatedFac.isBanned && !clarkeincorporatedFac.isMember && !clarkeincorporatedFac.alreadyInvited && - checkMegacorpRequirements(LocationName.AevumClarkeIncorporated) + checkMegacorpRequirements(CompanyName.ClarkeIncorporated) ) { invitedFactions.push(clarkeincorporatedFac); } @@ -739,7 +750,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !omnitekincorporatedFac.isBanned && !omnitekincorporatedFac.isMember && !omnitekincorporatedFac.alreadyInvited && - checkMegacorpRequirements(LocationName.VolhavenOmniTekIncorporated) + checkMegacorpRequirements(CompanyName.OmniTekIncorporated) ) { invitedFactions.push(omnitekincorporatedFac); } @@ -750,7 +761,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !foursigmaFac.isBanned && !foursigmaFac.isMember && !foursigmaFac.alreadyInvited && - checkMegacorpRequirements(LocationName.Sector12FourSigma) + checkMegacorpRequirements(CompanyName.FourSigma) ) { invitedFactions.push(foursigmaFac); } @@ -761,7 +772,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !kuaigonginternationalFac.isBanned && !kuaigonginternationalFac.isMember && !kuaigonginternationalFac.alreadyInvited && - checkMegacorpRequirements(LocationName.ChongqingKuaiGongInternational) + checkMegacorpRequirements(CompanyName.KuaiGongInternational) ) { invitedFactions.push(kuaigonginternationalFac); } @@ -778,7 +789,7 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !fulcrumsecrettechonologiesFac.isMember && !fulcrumsecrettechonologiesFac.alreadyInvited && fulcrumSecretServer.backdoorInstalled && - checkMegacorpRequirements(LocationName.AevumFulcrumTechnologies) + checkMegacorpRequirements(CompanyName.FulcrumTechnologies) ) { invitedFactions.push(fulcrumsecrettechonologiesFac); } @@ -966,9 +977,9 @@ export function checkForFactionInvitations(this: PlayerObject): Faction[] { !silhouetteFac.isBanned && !silhouetteFac.isMember && !silhouetteFac.alreadyInvited && - (allPositions.includes("Chief Technology Officer") || - allPositions.includes("Chief Financial Officer") || - allPositions.includes("Chief Executive Officer")) && + (allPositions.includes(JobName.software7) || // CTO + allPositions.includes(JobName.business4) || // CFO + allPositions.includes(JobName.business5)) && // CEO this.money >= 15000000 && this.karma <= -22 ) { @@ -1136,8 +1147,7 @@ export function gainCodingContractReward( return `Gained ${gainPerFaction} reputation for each of the following factions: ${factions.join(", ")}`; } case CodingContractRewardType.CompanyReputation: { - if (!Companies[reward.name]) { - //If no/invalid company was designated, just give rewards to all factions + if (!isMember("CompanyName", reward.name)) { return this.gainCodingContractReward({ type: CodingContractRewardType.FactionReputationAll }); } const repGain = CONSTANTS.CodingContractBaseCompanyRepGain * difficulty; diff --git a/src/PersonObjects/Sleeve/Sleeve.ts b/src/PersonObjects/Sleeve/Sleeve.ts index 18edf11fa..ba04bb4a9 100644 --- a/src/PersonObjects/Sleeve/Sleeve.ts +++ b/src/PersonObjects/Sleeve/Sleeve.ts @@ -9,18 +9,23 @@ import type { SleevePerson } from "@nsdefs"; import type { Augmentation } from "../../Augmentation/Augmentation"; -import type { Company } from "../../Company/Company"; -import type { CompanyPosition } from "../../Company/CompanyPosition"; import type { SleeveWork } from "./Work/Work"; import { Player } from "@player"; import { Person } from "../Person"; -import { Companies } from "../../Company/Companies"; -import { CompanyPositions } from "../../Company/CompanyPositions"; import { Contracts } from "../../Bladeburner/data/Contracts"; import { CONSTANTS } from "../../Constants"; -import { ClassType, CityName, CrimeType, FactionWorkType, GymType, LocationName, UniversityClassType } from "@enums"; +import { + ClassType, + CityName, + CrimeType, + FactionWorkType, + GymType, + LocationName, + UniversityClassType, + CompanyName, +} from "@enums"; import { Factions } from "../../Faction/Factions"; @@ -277,18 +282,11 @@ export class Sleeve extends Person implements SleevePerson { * Start work for one of the player's companies * Returns boolean indicating success */ - workForCompany(companyName: string): boolean { - if (!Companies[companyName] || Player.jobs[companyName] == null) { - return false; - } - - const company: Company | null = Companies[companyName]; - const companyPosition: CompanyPosition | null = CompanyPositions[Player.jobs[companyName]]; - if (company == null) return false; - if (companyPosition == null) return false; + workForCompany(companyName: CompanyName): boolean { + const companyPositionName = Player.jobs[companyName]; + if (!companyPositionName) return false; this.startWork(new SleeveCompanyWork(companyName)); - return true; } diff --git a/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts b/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts index a568391e9..5bfae23c8 100644 --- a/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts +++ b/src/PersonObjects/Sleeve/Work/SleeveCompanyWork.ts @@ -1,5 +1,5 @@ import { Player } from "@player"; -import { LocationName } from "@enums"; +import { CompanyName, JobName } from "@enums"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../../../utils/JSONReviver"; import { Sleeve } from "../Sleeve"; import { applySleeveGains, SleeveWorkClass, SleeveWorkType } from "./Work"; @@ -9,29 +9,29 @@ import { calculateCompanyWorkStats } from "../../../Work/Formulas"; import { scaleWorkStats, WorkStats } from "../../../Work/WorkStats"; import { influenceStockThroughCompanyWork } from "../../../StockMarket/PlayerInfluencing"; import { CompanyPositions } from "../../../Company/CompanyPositions"; +import { isMember } from "../../../utils/EnumHelper"; +import { invalidWork } from "../../../Work/InvalidWork"; export const isSleeveCompanyWork = (w: SleeveWorkClass | null): w is SleeveCompanyWork => w !== null && w.type === SleeveWorkType.COMPANY; export class SleeveCompanyWork extends SleeveWorkClass { type: SleeveWorkType.COMPANY = SleeveWorkType.COMPANY; - companyName: string; + companyName: CompanyName; - constructor(companyName?: string) { + constructor(companyName = CompanyName.NoodleBar) { super(); - this.companyName = companyName ?? LocationName.NewTokyoNoodleBar; + this.companyName = companyName; } getCompany(): Company { - const c = Companies[this.companyName]; - if (!c) throw new Error(`Company not found: '${this.companyName}'`); - return c; + return Companies[this.companyName]; } - getGainRates(sleeve: Sleeve): WorkStats { + getGainRates(sleeve: Sleeve, job: JobName): WorkStats { const company = this.getCompany(); return scaleWorkStats( - calculateCompanyWorkStats(sleeve, company, CompanyPositions[Player.jobs[company.name]], company.favor), + calculateCompanyWorkStats(sleeve, company, CompanyPositions[job], company.favor), sleeve.shockBonus(), false, ); @@ -39,7 +39,9 @@ export class SleeveCompanyWork extends SleeveWorkClass { process(sleeve: Sleeve, cycles: number) { const company = this.getCompany(); - const gains = this.getGainRates(sleeve); + const job = Player.jobs[this.companyName]; + if (!job) return sleeve.stopWork(); + const gains = this.getGainRates(sleeve, job); applySleeveGains(sleeve, gains, cycles); company.playerReputation += gains.reputation * cycles; influenceStockThroughCompanyWork(company, gains.reputation, cycles); @@ -59,7 +61,9 @@ export class SleeveCompanyWork extends SleeveWorkClass { /** Initializes a CompanyWork object from a JSON save state. */ static fromJSON(value: IReviverValue): SleeveCompanyWork { - return Generic_fromJSON(SleeveCompanyWork, value.data); + const work = Generic_fromJSON(SleeveCompanyWork, value.data); + if (!isMember("CompanyName", work.companyName)) return invalidWork(); + return work; } } diff --git a/src/PersonObjects/Sleeve/ui/SleeveElem.tsx b/src/PersonObjects/Sleeve/ui/SleeveElem.tsx index 228e13101..b12d7e985 100644 --- a/src/PersonObjects/Sleeve/ui/SleeveElem.tsx +++ b/src/PersonObjects/Sleeve/ui/SleeveElem.tsx @@ -13,6 +13,7 @@ import { TaskSelector } from "./TaskSelector"; import { TravelModal } from "./TravelModal"; import { findCrime } from "../../../Crime/CrimeHelpers"; import { SleeveWorkType } from "../Work/Work"; +import { getEnumHelper } from "../../../utils/EnumHelper"; function getWorkDescription(sleeve: Sleeve, progress: number): string { const work = sleeve.currentWork; @@ -75,7 +76,8 @@ export function SleeveElem(props: SleeveElemProps): React.ReactElement { case "------": break; case "Work for Company": - props.sleeve.workForCompany(abc[1]); + if (getEnumHelper("CompanyName").isMember(abc[1])) props.sleeve.workForCompany(abc[1]); + else console.error(`Invalid company name in setSleeveTask: ${abc[1]}`); break; case "Work for Faction": props.sleeve.workForFaction(abc[1], abc[2]); diff --git a/src/PersonObjects/Sleeve/ui/StatsElement.tsx b/src/PersonObjects/Sleeve/ui/StatsElement.tsx index 222d527cd..5a0f9cd15 100644 --- a/src/PersonObjects/Sleeve/ui/StatsElement.tsx +++ b/src/PersonObjects/Sleeve/ui/StatsElement.tsx @@ -2,6 +2,8 @@ import React from "react"; import { Typography, Table, TableBody, TableCell, TableRow } from "@mui/material"; +import { Player } from "@player"; + import { CONSTANTS } from "../../../Constants"; import { @@ -141,8 +143,10 @@ export function EarningsElement(props: IProps): React.ReactElement { ]; } - if (isSleeveCompanyWork(props.sleeve.currentWork)) { - const rates = props.sleeve.currentWork.getGainRates(props.sleeve); + companyWork: if (isSleeveCompanyWork(props.sleeve.currentWork)) { + const job = Player.jobs[props.sleeve.currentWork.companyName]; + if (!job) break companyWork; + const rates = props.sleeve.currentWork.getGainRates(props.sleeve, job); data = [ [`Money:`, ], [`Hacking Exp:`, `${formatExp(CYCLES_PER_SEC * rates.hackExp)} / sec`], diff --git a/src/Prestige.ts b/src/Prestige.ts index ded96b5d8..d8e60cfce 100755 --- a/src/Prestige.ts +++ b/src/Prestige.ts @@ -1,6 +1,6 @@ import { AugmentationName, CityName, CompletedProgramName, FactionName, LiteratureName } from "@enums"; import { initBitNodeMultipliers } from "./BitNode/BitNode"; -import { Companies, initCompanies } from "./Company/Companies"; +import { Companies } from "./Company/Companies"; import { resetIndustryResearchTrees } from "./Corporation/data/IndustryData"; import { Factions } from "./Faction/Factions"; import { joinFaction } from "./Faction/FactionHelpers"; @@ -71,7 +71,7 @@ export function prestigeAugmentation(): void { initForeignServers(Player.getHomeComputer()); // Gain favor for Companies and Factions - for (const company of Object.values(Companies)) company.gainFavor(); + for (const company of Object.values(Companies)) company.prestigeAugmentation(); for (const faction of Object.values(Factions)) faction.prestigeAugmentation(); // Stop a Terminal action if there is one. @@ -89,7 +89,6 @@ export function prestigeAugmentation(): void { Player.reapplyAllAugmentations(); Player.reapplyAllSourceFiles(); Player.hp.current = Player.hp.max; - initCompanies(); // Apply entropy from grafting Player.applyEntropy(Player.entropy); @@ -194,7 +193,7 @@ export function prestigeSourceFile(isFlume: boolean): void { homeComp.cpuCores = 1; // Reset favor for Companies and Factions - for (const company of Object.values(Companies)) company.favor = 0; + for (const company of Object.values(Companies)) company.prestigeSourceFile(); for (const faction of Object.values(Factions)) faction.prestigeSourceFile(); // Stop a Terminal action if there is one @@ -214,7 +213,6 @@ export function prestigeSourceFile(isFlume: boolean): void { Player.reapplyAllAugmentations(); Player.reapplyAllSourceFiles(); - initCompanies(); if (Player.sourceFileLvl(5) > 0 || Player.bitNodeN === 5) { homeComp.programs.push(CompletedProgramName.formulas); diff --git a/src/SaveObject.ts b/src/SaveObject.ts index 88d9a1bcb..2ba63f345 100644 --- a/src/SaveObject.ts +++ b/src/SaveObject.ts @@ -270,28 +270,6 @@ function evaluateVersionCompatibility(ver: string | number): void { anyPlayer.companyPosition = ""; } } - - // The "companyName" property of all Companies is renamed to "name" - interface Company0_41_2 { - name: string | number; - companyName: string; - companyPositions: Record; - } - for (const companyName of Object.keys(Companies)) { - const company = Companies[companyName] as unknown as Company0_41_2; - if (company.name == 0 && company.companyName != null) { - company.name = company.companyName; - } - - if (company.companyPositions instanceof Array) { - const pos: Record = {}; - - for (let i = 0; i < company.companyPositions.length; ++i) { - pos[company.companyPositions[i]] = true; - } - company.companyPositions = pos; - } - } } // This version allowed players to hold multiple jobs diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index bbdb93c76..27afac6c1 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -6728,12 +6728,10 @@ declare enum JobName { business3 = "Operations Manager", business4 = "Chief Financial Officer", business5 = "Chief Executive Officer", - security0 = "Police Officer", - security1 = "Police Chief", - security2 = "Security Guard", - security3 = "Security Officer", - security4 = "Security Supervisor", - security5 = "Head of Security", + security0 = "Security Guard", + security1 = "Security Officer", + security2 = "Security Supervisor", + security3 = "Head of Security", agent0 = "Field Agent", agent1 = "Secret Agent", agent2 = "Special Operative", diff --git a/src/StockMarket/PlayerInfluencing.ts b/src/StockMarket/PlayerInfluencing.ts index 3b18430a0..08eb5b805 100644 --- a/src/StockMarket/PlayerInfluencing.ts +++ b/src/StockMarket/PlayerInfluencing.ts @@ -74,9 +74,7 @@ export function influenceStockThroughCompanyWork( ): void { const compName = company.name; let stock: Stock | null = null; - if (typeof compName === "string" && compName !== "") { - stock = StockMarket[compName]; - } + stock = StockMarket[compName]; if (!(stock instanceof Stock)) { return; } diff --git a/src/Work/CompanyWork.tsx b/src/Work/CompanyWork.tsx index caee7dc2e..78ddd1d43 100644 --- a/src/Work/CompanyWork.tsx +++ b/src/Work/CompanyWork.tsx @@ -3,7 +3,7 @@ import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue import { Player } from "@player"; import { Work, WorkType } from "./Work"; import { influenceStockThroughCompanyWork } from "../StockMarket/PlayerInfluencing"; -import { AugmentationName, LocationName } from "@enums"; +import { AugmentationName, CompanyName, JobName } from "@enums"; import { calculateCompanyWorkStats } from "./Formulas"; import { Companies } from "../Company/Companies"; import { applyWorkStats, scaleWorkStats, WorkStats } from "./WorkStats"; @@ -12,43 +12,42 @@ import { dialogBoxCreate } from "../ui/React/DialogBox"; import { Reputation } from "../ui/React/Reputation"; import { CONSTANTS } from "../Constants"; import { CompanyPositions } from "../Company/CompanyPositions"; +import { isMember } from "../utils/EnumHelper"; +import { invalidWork } from "./InvalidWork"; interface CompanyWorkParams { - companyName: string; + companyName: CompanyName; singularity: boolean; } export const isCompanyWork = (w: Work | null): w is CompanyWork => w !== null && w.type === WorkType.COMPANY; export class CompanyWork extends Work { - companyName: string; + companyName: CompanyName; constructor(params?: CompanyWorkParams) { super(WorkType.COMPANY, params?.singularity ?? false); - this.companyName = params?.companyName ?? LocationName.NewTokyoNoodleBar; + this.companyName = params?.companyName ?? CompanyName.NoodleBar; } getCompany(): Company { - const c = Companies[this.companyName]; - if (!c) throw new Error(`Company not found: '${this.companyName}'`); - return c; + return Companies[this.companyName]; } - getGainRates(): WorkStats { + getGainRates(job: JobName): WorkStats { let focusBonus = 1; if (!Player.hasAugmentation(AugmentationName.NeuroreceptorManager, true)) { focusBonus = Player.focus ? 1 : CONSTANTS.BaseFocusBonus; } const company = this.getCompany(); - return scaleWorkStats( - calculateCompanyWorkStats(Player, company, CompanyPositions[Player.jobs[company.name]], company.favor), - focusBonus, - ); + return scaleWorkStats(calculateCompanyWorkStats(Player, company, CompanyPositions[job], company.favor), focusBonus); } process(cycles: number): boolean { this.cyclesWorked += cycles; const company = this.getCompany(); - const gains = this.getGainRates(); + const job = Player.jobs[this.companyName]; + if (!job) return true; + const gains = this.getGainRates(job); applyWorkStats(Player, gains, cycles, "work"); company.playerReputation += gains.reputation * cycles; influenceStockThroughCompanyWork(company, gains.reputation, cycles); @@ -81,7 +80,9 @@ export class CompanyWork extends Work { /** Initializes a CompanyWork object from a JSON save state. */ static fromJSON(value: IReviverValue): CompanyWork { - return Generic_fromJSON(CompanyWork, value.data); + const work = Generic_fromJSON(CompanyWork, value.data); + if (!isMember("CompanyName", work.companyName)) return invalidWork(); + return work; } } diff --git a/src/Work/Enums.ts b/src/Work/Enums.ts index fad37419e..1c31d3f6b 100644 --- a/src/Work/Enums.ts +++ b/src/Work/Enums.ts @@ -47,12 +47,10 @@ export enum JobName { business3 = "Operations Manager", business4 = "Chief Financial Officer", business5 = "Chief Executive Officer", - security0 = "Police Officer", - security1 = "Police Chief", - security2 = "Security Guard", - security3 = "Security Officer", - security4 = "Security Supervisor", - security5 = "Head of Security", + security0 = "Security Guard", + security1 = "Security Officer", + security2 = "Security Supervisor", + security3 = "Head of Security", agent0 = "Field Agent", agent1 = "Secret Agent", agent2 = "Special Operative", diff --git a/src/Work/InvalidWork.ts b/src/Work/InvalidWork.ts new file mode 100644 index 000000000..92f7726be --- /dev/null +++ b/src/Work/InvalidWork.ts @@ -0,0 +1,13 @@ +// This file is just for providing the ability to not load an invalid work. +import type { PlayerObject } from "../PersonObjects/Player/PlayerObject"; +import type { Sleeve } from "../PersonObjects/Sleeve/Sleeve"; +import type { SleeveWork } from "../PersonObjects/Sleeve/Work/Work"; +import type { Work } from "./Work"; + +// Type verifications to validate that Player.currentWork and sleeve.currentWork are allowed to be null. +const __canPlayerWorkBeNull: null extends PlayerObject["currentWork"] ? true : false = true; +const __canSleeveWorkBeNull: null extends Sleeve["currentWork"] ? true : false = true; + +export function invalidWork(): W { + return null as unknown as W; +} diff --git a/src/engine.tsx b/src/engine.tsx index 7d00b2ecc..9be60d447 100644 --- a/src/engine.tsx +++ b/src/engine.tsx @@ -3,7 +3,6 @@ import { AugmentationName, ToastVariant } from "@enums"; import { initBitNodeMultipliers } from "./BitNode/BitNode"; import { initSourceFiles } from "./SourceFile/SourceFiles"; import { generateRandomContract } from "./CodingContractGenerator"; -import { initCompanies } from "./Company/Companies"; import { CONSTANTS } from "./Constants"; import { Factions } from "./Faction/Factions"; import { staneksGift } from "./CotMG/Helper"; @@ -374,7 +373,6 @@ const Engine: { Engine.start(); // Run main game loop and Scripts loop Player.init(); initForeignServers(Player.getHomeComputer()); - initCompanies(); Player.reapplyAllAugmentations(); // Start interactive tutorial diff --git a/src/ui/React/CharacterOverview.tsx b/src/ui/React/CharacterOverview.tsx index 4fc17306b..abe57b70f 100644 --- a/src/ui/React/CharacterOverview.tsx +++ b/src/ui/React/CharacterOverview.tsx @@ -1,5 +1,5 @@ // Root React Component for the Corporation UI -import React, { useMemo, useState, useEffect } from "react"; +import React, { useMemo, useState, useEffect, ReactNode } from "react"; import { Theme, useTheme } from "@mui/material/styles"; import makeStyles from "@mui/styles/makeStyles"; @@ -337,9 +337,9 @@ function Work(): React.ReactElement { if (Player.currentWork === null || Player.focus) return <>; - let details = <>; - let header = <>; - let innerText = <>; + let details: ReactNode = ""; + let header: ReactNode = ""; + let innerText: ReactNode = ""; if (isCrimeWork(Player.currentWork)) { const crime = Player.currentWork.getCrime(); const perc = (Player.currentWork.unitCompleted / crime.time) * 100; @@ -391,11 +391,14 @@ function Work(): React.ReactElement { } if (isCompanyWork(Player.currentWork)) { const companyWork = Player.currentWork; + const job = Player.jobs[companyWork.companyName]; + if (!job) return <>; details = ( <> - {Player.jobs[companyWork.companyName]} at {companyWork.companyName} + {job} at {companyWork.companyName} ); + header = ( <> Working at {companyWork.companyName} @@ -405,7 +408,7 @@ function Work(): React.ReactElement { <> rep
( - ) + ) ); } diff --git a/src/ui/React/CompanyDropdown.tsx b/src/ui/React/CompanyDropdown.tsx index 0d043f0b4..4cfad8b9e 100644 --- a/src/ui/React/CompanyDropdown.tsx +++ b/src/ui/React/CompanyDropdown.tsx @@ -1,28 +1,28 @@ -/** - * Creates a dropdown (select HTML element) with company names as options - */ +import type { CompanyName } from "../../Enums"; + import React from "react"; -import { companiesMetadata } from "../../Company/data/CompaniesMetadata"; import Select, { SelectChangeEvent } from "@mui/material/Select"; import MenuItem from "@mui/material/MenuItem"; import Button from "@mui/material/Button"; +import { Companies } from "../../Company/Companies"; +import { getRecordKeys } from "../../Types/Record"; interface IProps { purchase: () => void; canPurchase: boolean; - onChange: (event: SelectChangeEvent) => void; - value: string; + onChange: (event: SelectChangeEvent) => void; + value: CompanyName; } -const sortedCompanies = companiesMetadata.sort((a, b) => a.name.localeCompare(b.name)); +const sortedCompanies = getRecordKeys(Companies).sort((a, b) => a.localeCompare(b)); export function CompanyDropdown(props: IProps): React.ReactElement { const companies = []; for (const company of sortedCompanies) { companies.push( - - {company.name} + + {company} , ); } diff --git a/src/ui/WorkInProgressRoot.tsx b/src/ui/WorkInProgressRoot.tsx index 9e3f0c09f..25b7fb222 100644 --- a/src/ui/WorkInProgressRoot.tsx +++ b/src/ui/WorkInProgressRoot.tsx @@ -411,7 +411,7 @@ export function WorkInProgressRoot(): React.ReactElement { cancel: () => Router.toPage(Page.Terminal), }, title: - `You cannot work for ${Player.currentWork.companyName || "(Company not found)"} at this time,` + + `You cannot work for ${Player.currentWork.companyName} at this time,` + " please try again if you think this should have worked", stopText: "Back to Terminal", @@ -421,7 +421,8 @@ export function WorkInProgressRoot(): React.ReactElement { const companyRep = comp.playerReputation; const position = Player.jobs[Player.currentWork.companyName]; - const gains = Player.currentWork.getGainRates(); + if (!position) return <>; + const gains = Player.currentWork.getGainRates(position); workInfo = { buttons: { cancel: () => { diff --git a/test/jest/FullSave.test.ts b/test/jest/FullSave.test.ts index 1122d1729..3ca7630f4 100644 --- a/test/jest/FullSave.test.ts +++ b/test/jest/FullSave.test.ts @@ -3,16 +3,17 @@ import { Factions } from "../../src/Faction/Factions"; import { Player, setPlayer } from "../../src/Player"; import { PlayerObject } from "../../src/PersonObjects/Player/PlayerObject"; import { joinFaction } from "../../src/Faction/FactionHelpers"; -import { AugmentationName, CrimeType, FactionName } from "../../src/Enums"; +import { AugmentationName, CompanyName, CrimeType, FactionName } from "../../src/Enums"; import { Augmentations } from "../../src/Augmentation/Augmentations"; import { SleeveCrimeWork } from "../../src/PersonObjects/Sleeve/Work/SleeveCrimeWork"; +import { Companies } from "../../src/Company/Companies"; describe("Check Save File Continuity", () => { establishInitialConditions(); // Calling getSaveString forces save info to update saveObject.getSaveString(); - const savesToTest = ["FactionsSave", "PlayerSave"] as const; + const savesToTest = ["FactionsSave", "PlayerSave", "CompaniesSave"] as const; for (const saveToTest of savesToTest) { test(`${saveToTest} continuity`, () => { const parsed = JSON.parse(saveObject[saveToTest]); @@ -44,9 +45,6 @@ function establishInitialConditions() { initForeignServers(Player.getHomeComputer()); */ - // not comparing companies yet - // initCompanies() - // Sleeves (already added in game initializers section) Player.sleeves[0].installAugmentation(Augmentations[AugmentationName.BionicArms]); Player.sleeves[0].startWork(new SleeveCrimeWork(CrimeType.homicide)); @@ -61,6 +59,11 @@ function establishInitialConditions() { csec.playerReputation = 1e6; csec.favor = 20; + // Companies + const noodleBar = Companies[CompanyName.NoodleBar]; + noodleBar.favor = 100; + noodleBar.playerReputation = 100000; + // Bladeburner. Adding rank will also add bladeburner faction rep. Player.startBladeburner(); Player.bladeburner?.changeRank(Player, 2000); diff --git a/test/jest/StockMarket.test.ts b/test/jest/StockMarket.test.ts index 208eba395..25d26fa7a 100644 --- a/test/jest/StockMarket.test.ts +++ b/test/jest/StockMarket.test.ts @@ -31,7 +31,7 @@ import { getSellTransactionGain, processTransactionForecastMovement, } from "../../src/StockMarket/StockMarketHelpers"; -import { OrderType, PositionType } from "../../src/Enums"; +import { CompanyName, OrderType, PositionType } from "../../src/Enums"; jest.mock(`!!raw-loader!../NetscriptDefinitions.d.ts`, () => "", { virtual: true, @@ -1255,9 +1255,9 @@ describe("Stock Market Tests", function () { }); const company = new Company({ - name: "MockStock", + name: "MockStock" as CompanyName, info: "", - companyPositions: {}, + companyPositions: [], expMultiplier: 1, salaryMultiplier: 1, jobStatReqOffset: 1, diff --git a/test/jest/__snapshots__/FullSave.test.ts.snap b/test/jest/__snapshots__/FullSave.test.ts.snap index 3d60e8585..00df9f154 100644 --- a/test/jest/__snapshots__/FullSave.test.ts.snap +++ b/test/jest/__snapshots__/FullSave.test.ts.snap @@ -1,5 +1,276 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Check Save File Continuity CompaniesSave continuity 1`] = ` +{ + "AeroCorp": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Aevum Police Headquarters": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Alpha Enterprises": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Bachman & Associates": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Blade Industries": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Carmichael Security": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Central Intelligence Agency": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Clarke Incorporated": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "CompuTek": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "DefComm": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "DeltaOne": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "ECorp": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "FoodNStuff": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Four Sigma": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Fulcrum Technologies": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Galactic Cybersystems": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Global Pharmaceuticals": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Helios Labs": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Icarus Microsystems": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Joe's Guns": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "KuaiGong International": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "LexoCorp": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "MegaCorp": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "NWO": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "National Security Agency": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "NetLink Technologies": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Noodle Bar": { + "ctor": "Company", + "data": { + "favor": 100, + "playerReputation": 100000, + }, + }, + "Nova Medical": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Omega Software": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "OmniTek Incorporated": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Omnia Cybersystems": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Rho Construction": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Solaris Space Systems": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Storm Technologies": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "SysCore Securities": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Universal Energy": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "VitaLife": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, + "Watchdog Security": { + "ctor": "Company", + "data": { + "favor": 0, + "playerReputation": 0, + }, + }, +} +`; + exports[`Check Save File Continuity FactionsSave continuity 1`] = ` { "Aevum": {