CORPORATION: Reorganize Industry data (#154)

This commit is contained in:
Snarling 2022-10-24 21:54:54 -04:00 committed by GitHub
parent d7193ca8ff
commit 5fe89d5599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 446 additions and 739 deletions

@ -3,7 +3,7 @@ import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { SkillNames } from "../Bladeburner/data/SkillNames";
import { Skills } from "../Bladeburner/Skills";
import { CONSTANTS } from "../Constants";
import { Industries } from "../Corporation/IndustryData";
import { IndustryType } from "../Corporation/IndustryData";
import { Exploit } from "../Exploits/Exploit";
import { Factions } from "../Faction/Factions";
import { AllGangs } from "../Gang/AllGangs";
@ -477,7 +477,7 @@ export const achievements: Record<string, Achievement> = {
Description: "Expand to the Real Estate division.",
Visible: () => hasAccessToSF(Player, 3),
Condition: () =>
Player.corporation !== null && Player.corporation.divisions.some((d) => d.type === Industries.RealEstate),
Player.corporation !== null && Player.corporation.divisions.some((d) => d.type === IndustryType.RealEstate),
},
INTELLIGENCE_255: {
...achievementData["INTELLIGENCE_255"],

@ -1,7 +1,7 @@
import { Player } from "@player";
import { MaterialSizes } from "./MaterialSizes";
import { Corporation } from "./Corporation";
import { IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { IndustryResearchTrees, IndustryType, IndustriesData } from "./IndustryData";
import { Industry } from "./Industry";
import { CorporationConstants } from "./data/Constants";
import { OfficeSpace } from "./OfficeSpace";
@ -15,8 +15,9 @@ import { EmployeePositions } from "./EmployeePositions";
import { ResearchMap } from "./ResearchMap";
import { isRelevantMaterial } from "./ui/Helpers";
import { checkEnum } from "../utils/helpers/checkEnum";
import { CityName } from "src/Locations/data/CityNames";
export function NewIndustry(corporation: Corporation, industry: string, name: string): void {
export function NewIndustry(corporation: Corporation, industry: IndustryType, name: string): void {
if (corporation.divisions.find(({ type }) => industry == type))
throw new Error(`You have already expanded into the ${industry} industry!`);
@ -26,10 +27,9 @@ export function NewIndustry(corporation: Corporation, industry: string, name: st
}
}
const cost = IndustryStartingCosts[industry];
if (cost === undefined) {
throw new Error(`Invalid industry: '${industry}'`);
}
const data = IndustriesData[industry];
if (!data) throw new Error(`Invalid industry: '${industry}'`);
const cost = data.startingCost;
if (corporation.funds < cost) {
throw new Error("Not enough money to create a new division in this industry");
} else if (name === "") {
@ -350,7 +350,7 @@ export function ThrowParty(corp: Corporation, office: OfficeSpace, costPerEmploy
return mult;
}
export function PurchaseWarehouse(corp: Corporation, division: Industry, city: string): void {
export function PurchaseWarehouse(corp: Corporation, division: Industry, city: CityName): void {
if (corp.funds < CorporationConstants.WarehouseInitialCost) return;
if (division.warehouses[city]) return;
division.warehouses[city] = new Warehouse({
@ -474,7 +474,7 @@ export function Research(division: Industry, researchName: string): void {
export function ExportMaterial(
divisionName: string,
cityName: string,
cityName: CityName,
material: Material,
amt: string,
division?: Industry,

@ -12,6 +12,7 @@ import { Player } from "@player";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { isString } from "../utils/helpers/isString";
import { CityName } from "../Locations/data/CityNames";
interface IParams {
name?: string;
@ -327,7 +328,7 @@ export class Corporation {
if (upgN === 1) {
for (let i = 0; i < this.divisions.length; ++i) {
const industry = this.divisions[i];
for (const city of Object.keys(industry.warehouses)) {
for (const city of Object.keys(industry.warehouses) as CityName[]) {
const warehouse = industry.warehouses[city];
if (warehouse === 0) continue;
if (industry.warehouses.hasOwnProperty(city) && warehouse) {

@ -1,5 +1,7 @@
import { CityName } from "../Locations/data/CityNames";
export interface Export {
ind: string;
city: string;
city: CityName;
amt: string;
}

@ -1,6 +1,6 @@
import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { CityName } from "../Locations/data/CityNames";
import { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { IndustryType, IndustryResearchTrees, IndustriesData } from "./IndustryData";
import { CorporationConstants } from "./data/Constants";
import { EmployeePositions } from "./EmployeePositions";
import { Material } from "./Material";
@ -17,24 +17,24 @@ import { Corporation } from "./Corporation";
interface IParams {
name?: string;
corp?: Corporation;
type?: string;
type?: IndustryType | "Computer" | "Utilities";
}
export class Industry {
name = "";
type = Industries.Agriculture;
name: string;
type: IndustryType;
sciResearch = new Material({ name: "Scientific Research" });
researched: { [key: string]: boolean | undefined } = {};
reqMats: { [key: string]: number | undefined } = {};
//An array of the name of materials being produced
prodMats: string[] = [];
prodMats: string[];
products: { [key: string]: Product | undefined } = {};
makesProducts = false;
makesProducts: boolean;
awareness = 0;
popularity = 0; //Should always be less than awareness
popularity = 0;
startingCost = 0;
/* The following are factors for how much production/other things are increased by
@ -42,12 +42,12 @@ export class Industry {
and they are all represented by exponentials of < 1 (e.g x ^ 0.5, x ^ 0.8)
The number for these represent the exponential. A lower number means more
diminishing returns */
reFac = 0; //Real estate Factor
sciFac = 0; //Scientific Research Factor, affects quality
hwFac = 0; //Hardware factor
robFac = 0; //Robotics Factor
aiFac = 0; //AI Cores factor;
advFac = 0; //Advertising factor, affects sales
reFac: number; //Real estate Factor
sciFac: number; //Scientific Research Factor, affects quality
hwFac: number; //Hardware factor
robFac: number; //Robotics Factor
aiFac: number; //AI Cores factor;
advFac: number; //Advertising factor, affects sales
prodMult = 0; //Production multiplier
@ -61,7 +61,7 @@ export class Industry {
newInd = true;
//Maps locations to warehouses. 0 if no warehouse at that location
warehouses: { [key: string]: Warehouse | 0 };
warehouses: Record<CityName, Warehouse | 0>;
//Maps locations to offices. 0 if no office at that location
offices: { [key: string]: OfficeSpace | 0 } = {
@ -79,8 +79,10 @@ export class Industry {
numAdVerts = 0;
constructor(params: IParams = {}) {
if (params.type === "Utilities") params.type = IndustryType.Utilities;
if (params.type === "Computer") params.type = IndustryType.Computers;
this.type = params.type || IndustryType.Agriculture;
this.name = params.name ? params.name : "";
this.type = params.type ? params.type : Industries.Agriculture;
//Financials
this.lastCycleRevenue = 0;
@ -102,235 +104,18 @@ export class Industry {
[CityName.Volhaven]: 0,
};
this.init();
}
init(): void {
//Set the unique properties of an industry (how much its affected by real estate/scientific research, etc.)
const startingCost = IndustryStartingCosts[this.type];
if (startingCost === undefined) throw new Error(`Invalid industry: "${this.type}"`);
this.startingCost = startingCost;
switch (this.type) {
case Industries.Energy:
this.reFac = 0.65;
this.sciFac = 0.7;
this.robFac = 0.05;
this.aiFac = 0.3;
this.advFac = 0.08;
this.reqMats = {
Hardware: 0.1,
Metal: 0.2,
};
this.prodMats = ["Energy"];
break;
case Industries.Utilities:
case "Utilities":
this.reFac = 0.5;
this.sciFac = 0.6;
this.robFac = 0.4;
this.aiFac = 0.4;
this.advFac = 0.08;
this.reqMats = {
Hardware: 0.1,
Metal: 0.1,
};
this.prodMats = ["Water"];
break;
case Industries.Agriculture:
this.reFac = 0.72;
this.sciFac = 0.5;
this.hwFac = 0.2;
this.robFac = 0.3;
this.aiFac = 0.3;
this.advFac = 0.04;
this.reqMats = {
Water: 0.5,
Energy: 0.5,
};
this.prodMats = ["Plants", "Food"];
break;
case Industries.Fishing:
this.reFac = 0.15;
this.sciFac = 0.35;
this.hwFac = 0.35;
this.robFac = 0.5;
this.aiFac = 0.2;
this.advFac = 0.08;
this.reqMats = {
Energy: 0.5,
};
this.prodMats = ["Food"];
break;
case Industries.Mining:
this.reFac = 0.3;
this.sciFac = 0.26;
this.hwFac = 0.4;
this.robFac = 0.45;
this.aiFac = 0.45;
this.advFac = 0.06;
this.reqMats = {
Energy: 0.8,
};
this.prodMats = ["Metal"];
break;
case Industries.Food:
//reFac is unique for this bc it diminishes greatly per city. Handle this separately in code?
this.sciFac = 0.12;
this.hwFac = 0.15;
this.robFac = 0.3;
this.aiFac = 0.25;
this.advFac = 0.25;
this.reFac = 0.05;
this.reqMats = {
Food: 0.5,
Water: 0.5,
Energy: 0.2,
};
this.makesProducts = true;
break;
case Industries.Tobacco:
this.reFac = 0.15;
this.sciFac = 0.75;
this.hwFac = 0.15;
this.robFac = 0.2;
this.aiFac = 0.15;
this.advFac = 0.2;
this.reqMats = {
Plants: 1,
Water: 0.2,
};
this.makesProducts = true;
break;
case Industries.Chemical:
this.reFac = 0.25;
this.sciFac = 0.75;
this.hwFac = 0.2;
this.robFac = 0.25;
this.aiFac = 0.2;
this.advFac = 0.07;
this.reqMats = {
Plants: 1,
Energy: 0.5,
Water: 0.5,
};
this.prodMats = ["Chemicals"];
break;
case Industries.Pharmaceutical:
this.reFac = 0.05;
this.sciFac = 0.8;
this.hwFac = 0.15;
this.robFac = 0.25;
this.aiFac = 0.2;
this.advFac = 0.16;
this.reqMats = {
Chemicals: 2,
Energy: 1,
Water: 0.5,
};
this.prodMats = ["Drugs"];
this.makesProducts = true;
break;
case Industries.Computer:
case "Computer":
this.reFac = 0.2;
this.sciFac = 0.62;
this.robFac = 0.36;
this.aiFac = 0.19;
this.advFac = 0.17;
this.reqMats = {
Metal: 2,
Energy: 1,
};
this.prodMats = ["Hardware"];
this.makesProducts = true;
break;
case Industries.Robotics:
this.reFac = 0.32;
this.sciFac = 0.65;
this.aiFac = 0.36;
this.advFac = 0.18;
this.hwFac = 0.19;
this.reqMats = {
Hardware: 5,
Energy: 3,
};
this.prodMats = ["Robots"];
this.makesProducts = true;
break;
case Industries.Software:
this.sciFac = 0.62;
this.advFac = 0.16;
this.hwFac = 0.25;
this.reFac = 0.15;
this.aiFac = 0.18;
this.robFac = 0.05;
this.reqMats = {
Hardware: 0.5,
Energy: 0.5,
};
this.prodMats = ["AICores"];
this.makesProducts = true;
break;
case Industries.Healthcare:
this.reFac = 0.1;
this.sciFac = 0.75;
this.advFac = 0.11;
this.hwFac = 0.1;
this.robFac = 0.1;
this.aiFac = 0.1;
this.reqMats = {
Robots: 10,
AICores: 5,
Energy: 5,
Water: 5,
};
this.makesProducts = true;
break;
case Industries.RealEstate:
this.robFac = 0.6;
this.aiFac = 0.6;
this.advFac = 0.25;
this.sciFac = 0.05;
this.hwFac = 0.05;
this.reqMats = {
Metal: 5,
Energy: 5,
Water: 2,
Hardware: 4,
};
this.prodMats = ["RealEstate"];
this.makesProducts = true;
break;
default:
console.error(`Invalid Industry Type passed into Industry.init(): ${this.type}`);
return;
}
}
getProductDescriptionText(): string {
if (!this.makesProducts) return "";
switch (this.type) {
case Industries.Food:
return "create and manage restaurants";
case Industries.Tobacco:
return "create tobacco and tobacco-related products";
case Industries.Pharmaceutical:
return "develop new pharmaceutical drugs";
case Industries.Computer:
case "Computer":
return "create new computer hardware and networking infrastructures";
case Industries.Robotics:
return "build specialized robots and robot-related products";
case Industries.Software:
return "develop computer software";
case Industries.Healthcare:
return "build and manage hospitals";
case Industries.RealEstate:
return "develop and manage real estate properties";
default:
console.error("Invalid industry type in Industry.getProductDescriptionText");
return "";
}
const data = IndustriesData[this.type];
if (!data) throw new Error(`Invalid industry: "${this.type}"`);
this.startingCost = data.startingCost;
this.makesProducts = data.product ? true : false;
this.reFac = data.reFac ?? 0;
this.sciFac = data.sciFac ?? 0;
this.hwFac = data.hwFac ?? 0;
this.robFac = data.robFac ?? 0;
this.aiFac = data.aiFac ?? 0;
this.advFac = data.advFac ?? 0;
this.reqMats = data.reqMats;
this.prodMats = data.prodMats ?? [];
}
getMaximumNumberProducts(): number {
@ -496,9 +281,9 @@ export class Industry {
if (change === 0) continue;
if (
this.type === Industries.Pharmaceutical ||
this.type === Industries.Software ||
this.type === Industries.Robotics
this.type === IndustryType.Pharmaceutical ||
this.type === IndustryType.Software ||
this.type === IndustryType.Robotics
) {
change *= 3;
}

@ -4,236 +4,255 @@ import { Corporation } from "./Corporation";
import { getBaseResearchTreeCopy, getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree";
import { MoneyCost } from "./ui/MoneyCost";
interface IIndustryMap<T> {
[key: string]: T | undefined;
Energy: T;
Utilities: T;
Agriculture: T;
Fishing: T;
Mining: T;
Food: T;
Tobacco: T;
Chemical: T;
Pharmaceutical: T;
Computer: T;
Robotics: T;
Software: T;
Healthcare: T;
RealEstate: T;
export enum IndustryType {
Energy = "Energy",
Utilities = "Water Utilities",
Agriculture = "Agriculture",
Fishing = "Fishing",
Mining = "Mining",
Food = "Food",
Tobacco = "Tobacco",
Chemical = "Chemical",
Pharmaceutical = "Pharmaceutical",
Computers = "Computer Hardware",
Robotics = "Robotics",
Software = "Software",
Healthcare = "Healthcare",
RealEstate = "RealEstate",
}
// Map of official names for each Industry
export const Industries: IIndustryMap<string> = {
Energy: "Energy",
Utilities: "Water Utilities",
Agriculture: "Agriculture",
Fishing: "Fishing",
Mining: "Mining",
Food: "Food",
Tobacco: "Tobacco",
Chemical: "Chemical",
Pharmaceutical: "Pharmaceutical",
Computer: "Computer Hardware",
Robotics: "Robotics",
Software: "Software",
Healthcare: "Healthcare",
RealEstate: "RealEstate",
type IndustryData = {
startingCost: number;
description: string;
/** Product name for industry. Empty string for industries with no products. */
product?: { name: string; verb: string; desc: string };
recommendStarting: boolean;
reqMats: Record<string, number>;
/** Real estate factor */
reFac?: number;
/** Scientific research factor (affects quality) */
sciFac?: number;
/** Hardware factor */
hwFac?: number;
/** Robots factor */
robFac?: number;
/** AI Cores factor */
aiFac?: number;
/** Advertising factor (affects sales) */
advFac?: number;
prodMats?: string[];
};
// Map of how much money it takes to start each industry
export const IndustryStartingCosts: IIndustryMap<number> = {
Energy: 225e9,
Utilities: 150e9,
Agriculture: 40e9,
Fishing: 80e9,
Mining: 300e9,
Food: 10e9,
Tobacco: 20e9,
Chemical: 70e9,
Pharmaceutical: 200e9,
Computer: 500e9,
Robotics: 1e12,
Software: 25e9,
Healthcare: 750e9,
RealEstate: 600e9,
export const IndustriesData: Record<IndustryType, IndustryData> = {
[IndustryType.Agriculture]: {
startingCost: 40e9,
description: "Cultivate crops and breed livestock to produce food.",
recommendStarting: true,
reFac: 0.72,
sciFac: 0.5,
hwFac: 0.2,
robFac: 0.3,
aiFac: 0.3,
advFac: 0.04,
reqMats: { Water: 0.5, Energy: 0.5 },
prodMats: ["Plants", "Food"],
},
[IndustryType.Chemical]: {
startingCost: 70e9,
description: "Produce industrial chemicals.",
recommendStarting: false,
reFac: 0.25,
sciFac: 0.75,
hwFac: 0.2,
robFac: 0.25,
aiFac: 0.2,
advFac: 0.07,
reqMats: { Plants: 1, Energy: 0.5, Water: 0.5 },
prodMats: ["Chemicals"],
},
[IndustryType.Computers]: {
startingCost: 500e9,
description: "Develop and manufacture new computer hardware and networking infrastructures.",
product: { name: "Product", verb: "Create", desc: "Design and manufacture a new computer hardware product!" },
recommendStarting: false,
reFac: 0.2,
sciFac: 0.62,
robFac: 0.36,
aiFac: 0.19,
advFac: 0.17,
reqMats: { Metal: 2, Energy: 1 },
prodMats: ["Hardware"],
},
[IndustryType.Energy]: {
startingCost: 225e9,
description: "Engage in the production and distribution of energy.",
recommendStarting: false,
reFac: 0.65,
sciFac: 0.7,
robFac: 0.05,
aiFac: 0.3,
advFac: 0.08,
reqMats: { Hardware: 0.1, Metal: 0.2 },
prodMats: ["Energy"],
},
[IndustryType.Fishing]: {
startingCost: 80e9,
description: "Produce food through the breeding and processing of fish and fish products.",
recommendStarting: false,
reFac: 0.15,
sciFac: 0.35,
hwFac: 0.35,
robFac: 0.5,
aiFac: 0.2,
advFac: 0.08,
reqMats: { Energy: 0.5 },
prodMats: ["Food"],
},
[IndustryType.Food]: {
startingCost: 10e9,
description: "Create your own restaurants all around the world.",
product: { name: "Restaurant", verb: "Build", desc: "Build and manage a new restaurant!" },
recommendStarting: true,
sciFac: 0.12,
hwFac: 0.15,
robFac: 0.3,
aiFac: 0.25,
advFac: 0.25,
reFac: 0.05,
reqMats: { Food: 0.5, Water: 0.5, Energy: 0.2 },
},
[IndustryType.Healthcare]: {
startingCost: 750e9,
description: "Create and manage hospitals.",
product: { name: "Hospital", verb: "Build", desc: "Build and manage a new hospital!" },
recommendStarting: false,
reFac: 0.1,
sciFac: 0.75,
advFac: 0.11,
hwFac: 0.1,
robFac: 0.1,
aiFac: 0.1,
reqMats: { Robots: 10, AICores: 5, Energy: 5, Water: 5 },
},
[IndustryType.Mining]: {
startingCost: 300e9,
description: "Extract and process metals from the earth.",
recommendStarting: false,
reFac: 0.3,
sciFac: 0.26,
hwFac: 0.4,
robFac: 0.45,
aiFac: 0.45,
advFac: 0.06,
reqMats: { Energy: 0.8 },
prodMats: ["Metal"],
},
[IndustryType.Pharmaceutical]: {
startingCost: 200e9,
description: "Discover, develop, and create new pharmaceutical drugs.",
product: { name: "Drug", verb: "Develop", desc: "Design and develop a new pharmaceutical drug!" },
recommendStarting: false,
reFac: 0.05,
sciFac: 0.8,
hwFac: 0.15,
robFac: 0.25,
aiFac: 0.2,
advFac: 0.16,
reqMats: { Chemicals: 2, Energy: 1, Water: 0.5 },
prodMats: ["Drugs"],
},
[IndustryType.RealEstate]: {
startingCost: 600e9,
description: "Develop and manage real estate properties.",
product: { name: "Property", verb: "Develop", desc: "Develop a new piece of real estate property!" },
recommendStarting: false,
robFac: 0.6,
aiFac: 0.6,
advFac: 0.25,
sciFac: 0.05,
hwFac: 0.05,
reqMats: { Metal: 5, Energy: 5, Water: 2, Hardware: 4 },
prodMats: ["RealEstate"],
},
[IndustryType.Robotics]: {
startingCost: 1e12,
description: "Develop and create robots.",
product: { name: "Robot", verb: "Design", desc: "Design and create a new robot or robotic system!" },
recommendStarting: false,
reFac: 0.32,
sciFac: 0.65,
aiFac: 0.36,
advFac: 0.18,
hwFac: 0.19,
reqMats: { Hardware: 5, Energy: 3 },
prodMats: ["Robots"],
},
[IndustryType.Software]: {
startingCost: 25e9,
description: "Develop computer software and create AI Cores.",
product: { name: "Software", verb: "Develop", desc: "Develop a new piece of software!" },
recommendStarting: true,
sciFac: 0.62,
advFac: 0.16,
hwFac: 0.25,
reFac: 0.15,
aiFac: 0.18,
robFac: 0.05,
reqMats: { Hardware: 0.5, Energy: 0.5 },
prodMats: ["AICores"],
},
[IndustryType.Tobacco]: {
startingCost: 20e9,
description: "Create and distribute tobacco and tobacco-related products.",
product: { name: "Product", verb: "Create", desc: "Create a new tobacco product!" },
recommendStarting: true,
reFac: 0.15,
sciFac: 0.75,
hwFac: 0.15,
robFac: 0.2,
aiFac: 0.15,
advFac: 0.2,
reqMats: { Plants: 1, Water: 0.2 },
},
[IndustryType.Utilities]: {
startingCost: 150e9,
description: "Distribute water and provide wastewater services.",
recommendStarting: false,
reFac: 0.5,
sciFac: 0.6,
robFac: 0.4,
aiFac: 0.4,
advFac: 0.08,
reqMats: { Hardware: 0.1, Metal: 0.1 },
prodMats: ["Water"],
},
};
export const IndustryStartingCosts = {};
// Map of description for each industry
export const IndustryDescriptions: IIndustryMap<(corp: Corporation) => React.ReactElement> = {
Energy: (corp: Corporation) => (
export const IndustryDescriptions = (industry: IndustryType, corp: Corporation) => {
const data = IndustriesData[industry];
return (
<>
Engage in the production and distribution of energy.
${data.description}
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Energy} corp={corp} />
Starting cost: <MoneyCost money={data.startingCost} corp={corp} />
<br />
Recommended starting Industry: NO
Recommended starting Industry: {data.recommendStarting ? "YES" : "NO"}
</>
),
Utilities: (corp: Corporation) => (
<>
Distribute water and provide wastewater services.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Utilities} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
Agriculture: (corp: Corporation) => (
<>
Cultivate crops and breed livestock to produce food.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Agriculture} corp={corp} />
<br />
Recommended starting Industry: YES
</>
),
Fishing: (corp: Corporation) => (
<>
Produce food through the breeding and processing of fish and fish products.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Fishing} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
Mining: (corp: Corporation) => (
<>
Extract and process metals from the earth.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Mining} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
Food: (corp: Corporation) => (
<>
Create your own restaurants all around the world.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Food} corp={corp} />
<br />
Recommended starting Industry: YES
</>
),
Tobacco: (corp: Corporation) => (
<>
Create and distribute tobacco and tobacco-related products.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Tobacco} corp={corp} />
<br />
Recommended starting Industry: YES
</>
),
Chemical: (corp: Corporation) => (
<>
Produce industrial chemicals.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Chemical} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
Pharmaceutical: (corp: Corporation) => (
<>
Discover, develop, and create new pharmaceutical drugs.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Pharmaceutical} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
Computer: (corp: Corporation) => (
<>
Develop and manufacture new computer hardware and networking infrastructures.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Computer} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
Robotics: (corp: Corporation) => (
<>
Develop and create robots.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Robotics} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
Software: (corp: Corporation) => (
<>
Develop computer software and create AI Cores.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Software} corp={corp} />
<br />
Recommended starting Industry: YES
</>
),
Healthcare: (corp: Corporation) => (
<>
Create and manage hospitals.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.Healthcare} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
RealEstate: (corp: Corporation) => (
<>
Develop and manage real estate properties.
<br />
<br />
Starting cost: <MoneyCost money={IndustryStartingCosts.RealEstate} corp={corp} />
<br />
Recommended starting Industry: NO
</>
),
);
};
// Map of available Research for each Industry. This data is held in a
// ResearchTree object
export const IndustryResearchTrees: IIndustryMap<ResearchTree> = {
Energy: getBaseResearchTreeCopy(),
Utilities: getBaseResearchTreeCopy(),
Agriculture: getBaseResearchTreeCopy(),
Fishing: getBaseResearchTreeCopy(),
Mining: getBaseResearchTreeCopy(),
Food: getProductIndustryResearchTreeCopy(),
Tobacco: getProductIndustryResearchTreeCopy(),
Chemical: getBaseResearchTreeCopy(),
Pharmaceutical: getProductIndustryResearchTreeCopy(),
Computer: getProductIndustryResearchTreeCopy(),
Robotics: getProductIndustryResearchTreeCopy(),
Software: getProductIndustryResearchTreeCopy(),
Healthcare: getProductIndustryResearchTreeCopy(),
RealEstate: getProductIndustryResearchTreeCopy(),
};
export const IndustryResearchTrees = {} as Record<IndustryType, ResearchTree>;
resetIndustryResearchTrees();
export function resetIndustryResearchTrees(): void {
IndustryResearchTrees.Energy = getBaseResearchTreeCopy();
IndustryResearchTrees.Utilities = getBaseResearchTreeCopy();
IndustryResearchTrees.Agriculture = getBaseResearchTreeCopy();
IndustryResearchTrees.Fishing = getBaseResearchTreeCopy();
IndustryResearchTrees.Mining = getBaseResearchTreeCopy();
IndustryResearchTrees.Food = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Tobacco = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Chemical = getBaseResearchTreeCopy();
IndustryResearchTrees.Pharmaceutical = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Computer = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Robotics = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Software = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Healthcare = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.RealEstate = getProductIndustryResearchTreeCopy();
export function resetIndustryResearchTrees() {
Object.values(IndustryType).forEach(
(ind) =>
(IndustryResearchTrees[ind] = IndustriesData[ind].product
? getProductIndustryResearchTreeCopy()
: getBaseResearchTreeCopy()),
);
}

@ -1,7 +1,7 @@
import { EmployeePositions } from "./EmployeePositions";
import { MaterialSizes } from "./MaterialSizes";
import { Industry } from "./Industry";
import { ProductRatingWeights, IProductRatingWeight } from "./ProductRatingWeights";
import { ProductRatingWeights } from "./ProductRatingWeights";
import { createCityMap } from "../Locations/createCityMap";
@ -248,11 +248,8 @@ export class Product {
}
calculateRating(industry: Industry): void {
const weights: IProductRatingWeight = ProductRatingWeights[industry.type];
if (weights == null) {
console.error(`Could not find product rating weights for: ${industry}`);
return;
}
const weights = ProductRatingWeights[industry.type];
if (!weights) return console.error(`Could not find product rating weights for: ${industry}`);
this.rat = 0;
this.rat += weights.Quality ? this.qlt * weights.Quality : 0;
this.rat += weights.Performance ? this.per * weights.Performance : 0;

@ -1,4 +1,4 @@
import { Industries } from "./IndustryData";
import { IndustryType } from "./IndustryData";
export interface IProductRatingWeight {
Aesthetics?: number;
@ -8,27 +8,27 @@ export interface IProductRatingWeight {
Performance?: number;
Reliability?: number;
}
export const ProductRatingWeights: Record<string, IProductRatingWeight> = {
[Industries.Food]: {
//TODO: Move this to IndustryData
export const ProductRatingWeights: Partial<Record<IndustryType, IProductRatingWeight>> = {
[IndustryType.Food]: {
Quality: 0.7,
Durability: 0.1,
Aesthetics: 0.2,
},
[Industries.Tobacco]: {
[IndustryType.Tobacco]: {
Quality: 0.4,
Durability: 0.2,
Reliability: 0.2,
Aesthetics: 0.2,
},
[Industries.Pharmaceutical]: {
[IndustryType.Pharmaceutical]: {
Quality: 0.2,
Performance: 0.2,
Durability: 0.1,
Reliability: 0.3,
Features: 0.2,
},
[Industries.Computer]: {
[IndustryType.Computers]: {
Quality: 0.15,
Performance: 0.25,
Durability: 0.25,
@ -36,16 +36,7 @@ export const ProductRatingWeights: Record<string, IProductRatingWeight> = {
Aesthetics: 0.05,
Features: 0.1,
},
Computer: {
//Repeat
Quality: 0.15,
Performance: 0.25,
Durability: 0.25,
Reliability: 0.2,
Aesthetics: 0.05,
Features: 0.1,
},
[Industries.Robotics]: {
[IndustryType.Robotics]: {
Quality: 0.1,
Performance: 0.2,
Durability: 0.2,
@ -53,21 +44,21 @@ export const ProductRatingWeights: Record<string, IProductRatingWeight> = {
Aesthetics: 0.1,
Features: 0.2,
},
[Industries.Software]: {
[IndustryType.Software]: {
Quality: 0.2,
Performance: 0.2,
Reliability: 0.2,
Durability: 0.2,
Features: 0.2,
},
[Industries.Healthcare]: {
[IndustryType.Healthcare]: {
Quality: 0.4,
Performance: 0.1,
Durability: 0.1,
Reliability: 0.3,
Features: 0.1,
},
[Industries.RealEstate]: {
[IndustryType.RealEstate]: {
Quality: 0.2,
Durability: 0.25,
Reliability: 0.1,

@ -54,22 +54,6 @@ export const CorporationConstants = {
"AI Cores",
"Real Estate",
],
AllIndustryTypes: [
"Energy",
"Utilities",
"Agriculture",
"Fishing",
"Mining",
"Food",
"Tobacco",
"Chemical",
"Pharmaceutical",
"Computers",
"Robotics",
"Software",
"Healthcare",
"RealEstate",
],
AllUnlocks: [
"Export",
"Smart Supply",

@ -7,9 +7,10 @@ import { ExpandNewCity } from "./ExpandNewCity";
import { useDivision } from "./Context";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { CityName } from "../../Locations/data/CityNames";
interface IProps {
city: string;
city: CityName | "Expand";
rerender: () => void;
}
@ -19,13 +20,13 @@ export function CityTabs(props: IProps): React.ReactElement {
const office = division.offices[city];
if (office === 0) {
setCity("Sector-12");
setCity(CityName.Sector12);
return <></>;
}
const canExpand =
Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] === 0).length > 0;
function handleChange(event: React.SyntheticEvent, tab: string): void {
function handleChange(event: React.SyntheticEvent, tab: CityName | "Expand"): void {
setCity(tab);
}
return (

@ -2,9 +2,8 @@
// These are the tabs at the top of the UI that let you switch to different
// divisions, see an overview of your corporation, or create a new industry
import React, { useState, useEffect } from "react";
import { Industry } from "../Industry";
import { MainPanel } from "./MainPanel";
import { Industries } from "../IndustryData";
import { IndustryType } from "../IndustryData";
import { ExpandIndustryTab } from "./ExpandIndustryTab";
import { Player } from "@player";
import { Context } from "./Context";
@ -30,9 +29,8 @@ export function CorporationRoot(): React.ReactElement {
}, []);
const canExpand =
Object.keys(Industries).filter(
(industryType: string) =>
corporation.divisions.find((division: Industry) => division.type === industryType) === undefined,
Object.values(IndustryType).filter(
(industryType) => corporation.divisions.find((division) => division.type === industryType) === undefined,
).length > 0;
return (

@ -1,6 +1,6 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { IndustryStartingCosts, Industries, IndustryDescriptions } from "../IndustryData";
import { IndustryType, IndustryDescriptions, IndustriesData } from "../IndustryData";
import { useCorporation } from "./Context";
import { Industry } from "../Industry";
import { NewIndustry } from "../Actions";
@ -19,21 +19,21 @@ interface IProps {
export function ExpandIndustryTab(props: IProps): React.ReactElement {
const corp = useCorporation();
const allIndustries = Object.keys(Industries).sort();
const possibleIndustries = allIndustries
.filter(
(industryType: string) =>
corp.divisions.find((division: Industry) => division.type === industryType) === undefined,
)
.sort();
const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : "");
const allIndustries = Object.values(IndustryType).sort();
const possibleIndustries = allIndustries.filter(
(industryType: IndustryType) =>
corp.divisions.find((division: Industry) => division.type === industryType) === undefined,
);
const [industry, setIndustry] = useState(possibleIndustries[0]);
const [name, setName] = useState("");
const cost = IndustryStartingCosts[industry];
if (cost === undefined) {
throw new Error(`Invalid industry: '${industry}'`);
}
const disabled = corp.funds < cost || name === "";
//If there are no possible industries to expand into, nothing to render in this tab.
if (possibleIndustries.length === 0) return <></>;
const data = IndustriesData[industry];
if (!data) return <></>;
const disabled = corp.funds < data.startingCost;
function newIndustry(): void {
if (disabled) return;
@ -58,23 +58,23 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
}
function onIndustryChange(event: SelectChangeEvent<string>): void {
setIndustry(event.target.value);
setIndustry(event.target.value as IndustryType);
}
const desc = IndustryDescriptions[industry];
const desc = IndustryDescriptions(industry, corp);
if (desc === undefined) throw new Error(`Trying to create an industry that doesn't exists: '${industry}'`);
return (
<>
<Typography>Create a new division to expand into a new industry:</Typography>
<Select value={industry} onChange={onIndustryChange}>
{possibleIndustries.map((industry: string) => (
{possibleIndustries.map((industry) => (
<MenuItem key={industry} value={industry}>
{industry}
</MenuItem>
))}
</Select>
<Typography>{desc(corp)}</Typography>
<Typography>{desc}</Typography>
<br />
<br />

@ -8,21 +8,22 @@ import Typography from "@mui/material/Typography";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import Button from "@mui/material/Button";
import { CityName } from "../../Locations/data/CityNames";
interface IProps {
cityStateSetter: (city: string) => void;
cityStateSetter: (city: CityName | "Expand") => void;
}
export function ExpandNewCity(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const possibleCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] === 0);
const possibleCities = Object.values(CityName).filter((cityName) => division.offices[cityName] === 0);
const [city, setCity] = useState(possibleCities[0]);
const disabled = corp.funds < CorporationConstants.OfficeInitialCost;
function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value);
setCity(event.target.value as CityName);
}
function expand(): void {

@ -9,9 +9,10 @@ import { Warehouse } from "../Warehouse";
import { OfficeSpace } from "../OfficeSpace";
import { useCorporation, useDivision } from "./Context";
import Box from "@mui/material/Box";
import { CityName } from "../../Locations/data/CityNames";
interface IProps {
city: string;
city: CityName;
warehouse: Warehouse | 0;
office: OfficeSpace;
rerender: () => void;

@ -2,7 +2,7 @@
// (top-left panel in the Industry UI)
import React, { useState } from "react";
import { Industries } from "../IndustryData";
import { IndustryType } from "../IndustryData";
import { HireAdVert } from "../Actions";
import { numeralWrapper } from "../../ui/numeralFormat";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
@ -40,29 +40,28 @@ function MakeProductButton(): React.ReactElement {
let createProductButtonText = "";
switch (division.type) {
case Industries.Food:
case IndustryType.Food:
createProductButtonText = "Build Restaurant";
break;
case Industries.Tobacco:
case IndustryType.Tobacco:
createProductButtonText = "Create Product";
break;
case Industries.Pharmaceutical:
case IndustryType.Pharmaceutical:
createProductButtonText = "Create Drug";
break;
case Industries.Computer:
case "Computer":
case IndustryType.Computers:
createProductButtonText = "Create Product";
break;
case Industries.Robotics:
case IndustryType.Robotics:
createProductButtonText = "Design Robot";
break;
case Industries.Software:
case IndustryType.Software:
createProductButtonText = "Develop Software";
break;
case Industries.Healthcare:
case IndustryType.Healthcare:
createProductButtonText = "Build Hospital";
break;
case Industries.RealEstate:
case IndustryType.RealEstate:
createProductButtonText = "Develop Property";
break;
default:

@ -26,12 +26,13 @@ import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import { CityName } from "../../Locations/data/CityNames";
interface IProps {
corp: Corporation;
division: Industry;
warehouse: Warehouse | 0;
currentCity: string;
currentCity: CityName;
rerender: () => void;
}
@ -223,7 +224,7 @@ export function IndustryWarehouse(props: IProps): React.ReactElement {
}
interface IEmptyProps {
city: string;
city: CityName;
rerender: () => void;
}

@ -13,6 +13,7 @@ import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { CityName } from "../../../Locations/data/CityNames";
interface IProps {
open: boolean;
@ -29,7 +30,7 @@ export function ExportModal(props: IProps): React.ReactElement {
if (Object.keys(defaultDivision.warehouses).length === 0)
throw new Error("Export popup created in a division with no warehouses.");
const [industry, setIndustry] = useState<string>(defaultDivision.name);
const [city, setCity] = useState<string>(Object.keys(defaultDivision.warehouses)[0]);
const [city, setCity] = useState(Object.keys(defaultDivision.warehouses)[0] as CityName);
const [amt, setAmt] = useState("");
const setRerender = useState(false)[1];
@ -38,13 +39,13 @@ export function ExportModal(props: IProps): React.ReactElement {
}
function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value);
setCity(event.target.value as CityName);
}
function onIndustryChange(event: SelectChangeEvent<string>): void {
const div = event.target.value;
setIndustry(div);
setCity(Object.keys(corp.divisions[0].warehouses)[0]);
setCity(Object.keys(corp.divisions[0].warehouses)[0] as CityName);
}
function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {
@ -73,7 +74,7 @@ export function ExportModal(props: IProps): React.ReactElement {
const currentDivision = corp.divisions.find((division: Industry) => division.name === industry);
if (currentDivision === undefined)
throw new Error(`Export popup somehow ended up with undefined division '${currentDivision}'`);
const possibleCities = Object.keys(currentDivision.warehouses).filter(
const possibleCities = (Object.keys(currentDivision.warehouses) as CityName[]).filter(
(city) => currentDivision.warehouses[city] !== 0,
);
if (possibleCities.length > 0 && !possibleCities.includes(city)) {
@ -96,7 +97,7 @@ export function ExportModal(props: IProps): React.ReactElement {
))}
</Select>
<Select onChange={onCityChange} value={city}>
{possibleCities.map((cityName: string) => {
{possibleCities.map((cityName) => {
if (currentDivision.warehouses[cityName] === 0) return;
return (
<MenuItem key={cityName} value={cityName}>

@ -1,7 +1,7 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal";
import { Industries } from "../../IndustryData";
import { IndustriesData, IndustryType } from "../../IndustryData";
import { MakeProduct } from "../../Actions";
import { useCorporation, useDivision } from "../Context";
import Typography from "@mui/material/Typography";
@ -17,12 +17,12 @@ interface IProps {
onClose: () => void;
}
function productPlaceholder(tpe: string): string {
if (tpe === Industries.Food) {
function productPlaceholder(type: string): string {
if (type === IndustryType.Food) {
return "Restaurant Name";
} else if (tpe === Industries.Healthcare) {
} else if (type === IndustryType.Healthcare) {
return "Hospital Name";
} else if (tpe === Industries.RealEstate) {
} else if (type === IndustryType.RealEstate) {
return "Property Name";
}
return "Product Name";
@ -32,111 +32,13 @@ function productPlaceholder(tpe: string): string {
export function MakeProductModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const allCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] !== 0);
const allCities = Object.keys(division.offices).filter((cityName) => division.offices[cityName] !== 0);
const [city, setCity] = useState(allCities.length > 0 ? allCities[0] : "");
const [name, setName] = useState("");
const [design, setDesign] = useState<number>(NaN);
const [marketing, setMarketing] = useState<number>(NaN);
if (division.hasMaximumNumberProducts()) return <></>;
let createProductPopupText = <></>;
switch (division.type) {
case Industries.Food:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Build and manage a new restaurant!
</>
);
break;
case Industries.Tobacco:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Create a new tobacco product!
</>
);
break;
case Industries.Pharmaceutical:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Design and develop a new pharmaceutical drug!
</>
);
break;
case Industries.Computer:
case "Computer":
createProductPopupText = (
<>
{createProductPopupText}
<br />
Design and manufacture a new computer hardware product!
</>
);
break;
case Industries.Robotics:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Design and create a new robot or robotic system!
</>
);
break;
case Industries.Software:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Develop a new piece of software!
</>
);
break;
case Industries.Healthcare:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Build and manage a new hospital!
</>
);
break;
case Industries.RealEstate:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Develop a new piece of real estate property!
</>
);
break;
default:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Create a new product!
</>
);
return <></>;
}
createProductPopupText = (
<>
{createProductPopupText}
<br />
<br />
To begin developing a product, first choose the city in which it will be designed. The stats of your employees in
the selected city affect the properties of the finished product, such as its quality, performance, and durability.
<br />
<br />
You can also choose to invest money in the design and marketing of the product. Investing money in its design will
result in a superior product. Investing money in marketing the product will help the product's sales.
</>
);
const data = IndustriesData[division.type];
if (division.hasMaximumNumberProducts() || !data.product) return <></>;
function makeProduct(): void {
if (isNaN(design) || isNaN(marketing)) return;
@ -162,7 +64,19 @@ export function MakeProductModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>{createProductPopupText}</Typography>
<Typography>
<br />
{data.product.desc}
<br />
<br />
To begin developing a product, first choose the city in which it will be designed. The stats of your employees
in the selected city affect the properties of the finished product, such as its quality, performance, and
durability.
<br />
<br />
You can also choose to invest money in the design and marketing of the product. Investing money in its design
will result in a superior product. Investing money in marketing the product will help the product's sales.
</Typography>
<Select style={{ margin: "5px" }} onChange={onCityChange} defaultValue={city}>
{allCities.map((cityName: string) => (
<MenuItem key={cityName} value={cityName}>

@ -34,6 +34,7 @@ import { arrayToString } from "../utils/helpers/arrayToString";
import { HacknetServer } from "../Hacknet/HacknetServer";
import { BaseServer } from "../Server/BaseServer";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { checkEnum } from "../utils/helpers/checkEnum";
export const helpers = {
string,
@ -65,6 +66,22 @@ export const helpers = {
failOnHacknetServer,
};
export function assertEnumMember<T extends string>(
ctx: NetscriptContext,
obj: Record<string, T>,
enumName: string,
argName: string,
v: unknown,
): asserts v is T {
assertString(ctx, argName, v);
if (!checkEnum(obj, v)) throw makeRuntimeErrorMsg(ctx, `${argName}: ${v} is not a valid ${enumName}.`, "TYPE");
}
export function assertString(ctx: NetscriptContext, argName: string, v: unknown): asserts v is string {
if (typeof v !== "string")
throw makeRuntimeErrorMsg(ctx, `${argName} expected to be a string. ${debugType(v)}`, "TYPE");
}
/** Will probably remove the below function in favor of a different approach to object type assertion.
* This method cannot be used to handle optional properties. */
export function assertObjectType<T extends object>(
@ -124,9 +141,9 @@ const debugType = (v: unknown): string => {
/** Convert a provided value v for argument argName to string. If it wasn't originally a string or number, throw. */
function string(ctx: NetscriptContext, argName: string, v: unknown): string {
if (typeof v === "string") return v;
if (typeof v === "number") return v + ""; // cast to string;
throw makeRuntimeErrorMsg(ctx, `'${argName}' should be a string. ${debugType(v)}`, "TYPE");
if (typeof v === "number") v = v + ""; // cast to string;
assertString(ctx, argName, v);
return v;
}
/** Convert provided value v for argument argName to number. Throw if could not convert to a non-NaN number. */
@ -357,9 +374,8 @@ function updateDynamicRam(ctx: NetscriptContext, ramCost: number): void {
/** Validates the input v as being a CityName. Throws an error if it is not. */
function city(ctx: NetscriptContext, argName: string, v: unknown): CityName {
if (typeof v !== "string") throw makeRuntimeErrorMsg(ctx, `${argName} should be a city name.`);
const s = v as CityName;
if (!Object.values(CityName).includes(s)) throw makeRuntimeErrorMsg(ctx, `${argName} should be a city name.`);
return s;
if (!checkEnum(CityName, v)) throw makeRuntimeErrorMsg(ctx, `${argName} should be a city name.`);
return v;
}
function scriptIdentifier(

@ -345,6 +345,7 @@ const grafting = {
const corporation = {
getMaterialNames: 0,
getIndustryTypes: 0,
getEmployeePositions: 0,
getUnlockables: 0,
getUpgradeNames: 0,
getResearchNames: 0,

@ -78,7 +78,6 @@ import { ScriptDeath } from "./Netscript/ScriptDeath";
import { getBitNodeMultipliers } from "./BitNode/BitNode";
import { assert, arrayAssert, stringAssert, objectAssert } from "./utils/helpers/typeAssertion";
import { CrimeType } from "./utils/WorkType";
import { EmployeePositions } from "./Corporation/EmployeePositions";
export const enums = {
toast: ToastVariant,
@ -88,12 +87,7 @@ export const enums = {
export type NSFull = Readonly<NS & INetscriptExtra>;
export function NetscriptFunctions(workerScript: WorkerScript): NSFull {
const api = wrapAPI(workerScript, ns, workerScript.args.slice());
// Example of conditionally adding optional spoilered content to enums
if (Player.sourceFileLvl(3) > 0 || Player.bitNodeN === 3) {
api.enums.corp = Object.assign({}, { EmployeePositions });
}
return api;
return wrapAPI(workerScript, ns, workerScript.args.slice());
}
const base: InternalAPI<NS> = {

@ -51,14 +51,15 @@ import {
import { CorporationUnlockUpgrades } from "../Corporation/data/CorporationUnlockUpgrades";
import { CorporationUpgrades } from "../Corporation/data/CorporationUpgrades";
import { EmployeePositions } from "../Corporation/EmployeePositions";
import { IndustryResearchTrees, IndustryStartingCosts } from "../Corporation/IndustryData";
import { IndustriesData, IndustryResearchTrees, IndustryType } from "../Corporation/IndustryData";
import { CorporationConstants } from "../Corporation/data/Constants";
import { ResearchMap } from "../Corporation/ResearchMap";
import { Factions } from "../Faction/Factions";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers";
import { assertEnumMember, helpers } from "../Netscript/NetscriptHelpers";
import { checkEnum } from "../utils/helpers/checkEnum";
import { CityName } from "../Locations/data/CityNames";
export function NetscriptCorporation(): InternalAPI<NSCorporation> {
function createCorporation(corporationName: string, selfFund = true): boolean {
@ -114,14 +115,6 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
return baseCost * Math.pow(priceMult, level);
}
function getExpandIndustryCost(industryName: string): number {
const cost = IndustryStartingCosts[industryName];
if (cost === undefined) {
throw new Error(`Invalid industry: '${industryName}'`);
}
return cost;
}
function getExpandCityCost(): number {
return CorporationConstants.OfficeInitialCost;
}
@ -238,7 +231,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
function getWarehouse(divisionName: string, cityName: string): Warehouse {
const division = getDivision(divisionName);
if (!(cityName in division.warehouses)) throw new Error(`Invalid city name '${cityName}'`);
if (!checkEnum(CityName, cityName)) throw new Error(`Invalid city name '${cityName}'`);
const warehouse = division.warehouses[cityName];
if (warehouse === 0) throw new Error(`${division.name} has not expanded to '${cityName}'`);
return warehouse;
@ -497,12 +490,12 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
},
exportMaterial:
(ctx) =>
(_sourceDivision, _sourceCity, _targetDivision, _targetCity, _materialName, _amt): void => {
(_sourceDivision, sourceCity, _targetDivision, targetCity, _materialName, _amt): void => {
checkAccess(ctx, 7);
const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision);
const sourceCity = helpers.string(ctx, "sourceCity", _sourceCity);
assertEnumMember(ctx, CityName, "City", "sourceCity", sourceCity);
const targetDivision = helpers.string(ctx, "targetDivision", _targetDivision);
const targetCity = helpers.string(ctx, "targetCity", _targetCity);
assertEnumMember(ctx, CityName, "City", "targetCity", targetCity);
const materialName = helpers.string(ctx, "materialName", _materialName);
const amt = helpers.string(ctx, "amt", _amt);
ExportMaterial(
@ -515,12 +508,12 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
},
cancelExportMaterial:
(ctx) =>
(_sourceDivision, _sourceCity, _targetDivision, _targetCity, _materialName, _amt): void => {
(_sourceDivision, sourceCity, _targetDivision, targetCity, _materialName, _amt): void => {
checkAccess(ctx, 7);
const sourceDivision = helpers.string(ctx, "sourceDivision", _sourceDivision);
const sourceCity = helpers.string(ctx, "sourceCity", _sourceCity);
assertEnumMember(ctx, CityName, "City", "sourceCity", sourceCity);
const targetDivision = helpers.string(ctx, "targetDivision", _targetDivision);
const targetCity = helpers.string(ctx, "targetCity", _targetCity);
assertEnumMember(ctx, CityName, "City", "targetCity", targetCity);
const materialName = helpers.string(ctx, "materialName", _materialName);
const amt = helpers.string(ctx, "amt", _amt);
CancelExportMaterial(
@ -712,13 +705,18 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
return {
...warehouseAPI,
...officeAPI,
// Todo: Just remove these functions and provide enums?
getMaterialNames: (ctx) => () => {
checkAccess(ctx);
return [...CorporationConstants.AllMaterials];
},
getIndustryTypes: (ctx) => () => {
checkAccess(ctx);
return [...CorporationConstants.AllIndustryTypes];
return Object.values(IndustryType);
},
getEmployeePositions: (ctx) => () => {
checkAccess(ctx);
return Object.values(EmployeePositions);
},
getUnlockables: (ctx) => () => {
checkAccess(ctx);
@ -735,6 +733,9 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
expandIndustry: (ctx) => (_industryName, _divisionName) => {
checkAccess(ctx);
const industryName = helpers.string(ctx, "industryName", _industryName);
if (!checkEnum(IndustryType, industryName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid industry: ${industryName}`);
}
const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const corporation = getCorporation();
NewIndustry(corporation, industryName, divisionName);
@ -834,7 +835,10 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
getExpandIndustryCost: (ctx) => (_industryName) => {
checkAccess(ctx);
const industryName = helpers.string(ctx, "industryName", _industryName);
return getExpandIndustryCost(industryName);
if (!checkEnum(IndustryType, industryName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid industry: '${industryName}'`);
}
return IndustriesData[industryName].startingCost;
},
getExpandCityCost: (ctx) => () => {
checkAccess(ctx);

@ -6872,19 +6872,30 @@ export interface NS {
enums: NSEnums;
}
/** @public */
declare enum EmployeePositions {
Operations = "Operations",
Engineer = "Engineer",
Business = "Business",
Management = "Management",
RandD = "Research & Development",
Training = "Training",
Unassigned = "Unassigned",
}
type EmployeePosName =
| "Operations"
| "Engineer"
| "Business"
| "Management"
| "Research & Development"
| "Training"
| "Unassigned";
/** @public */
export type EmployeePosNames = `${EmployeePositions}`;
type IndustryTypeName =
| "Energy"
| "Water Utilities"
| "Agriculture"
| "Fishing"
| "Mining"
| "Food"
| "Tobacco"
| "Chemical"
| "Pharmaceutical"
| "Computer Hardware"
| "Robotics"
| "Software"
| "Healthcare"
| "RealEstate";
/** @public */
declare enum ToastVariant {
@ -6918,7 +6929,6 @@ type CrimeNames = `${CrimeType}`;
export type NSEnums = {
toast: typeof ToastVariant;
CrimeType: typeof CrimeType;
corp?: { EmployeePositions: typeof EmployeePositions };
};
/**
@ -6936,11 +6946,7 @@ export interface OfficeAPI {
* @param employeePosition - Position to place into. Defaults to "Unassigned".
* @returns True if an employee was hired, false otherwise
*/
hireEmployee(
divisionName: string,
cityName: string,
employeePosition?: EmployeePositions | EmployeePosNames,
): boolean;
hireEmployee(divisionName: string, cityName: string, employeePosition?: EmployeePosName): boolean;
/**
* Upgrade office size.
* @param divisionName - Name of the division
@ -7308,7 +7314,12 @@ export interface Corporation extends WarehouseAPI, OfficeAPI {
* Get list of industry types
* @returns industry names
*/
getIndustryTypes(): string[];
getIndustryTypes(): IndustryTypeName[];
/**
* Get list of industry types
* @returns industry names
*/
getEmployeePositions(): EmployeePosName[];
/**
* Get list of one-time unlockable upgrades
* @returns unlockable upgrades names
@ -7557,23 +7568,9 @@ export interface Office {
/** Average morale of the employees */
avgMor: number;
/** Production of the employees */
employeeProd: EmployeeJobs;
employeeProd: Record<EmployeePosName, number>;
/** Positions of the employees */
employeeJobs: EmployeeJobs;
}
/**
* Object representing the number of employee in each job.
* @public
*/
interface EmployeeJobs {
Operations: number;
Engineer: number;
Business: number;
Management: number;
"Research & Development": number;
Training: number;
Unassigned: number;
employeeJobs: Record<EmployeePosName, number>;
}
/**