CORPORATION: Corp changes prior to 2.3 finalization (#503)

This commit is contained in:
Snarling 2023-05-15 18:06:57 -04:00 committed by GitHub
parent e2e9b084bc
commit 2ae3ac52f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
135 changed files with 3106 additions and 3582 deletions

@ -1,15 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Corporation](./bitburner.corporation.md) &gt; [getUnlockUpgradeCost](./bitburner.corporation.getunlockupgradecost.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Corporation](./bitburner.corporation.md) &gt; [getUnlockCost](./bitburner.corporation.getunlockcost.md)
## Corporation.getUnlockUpgradeCost() method ## Corporation.getUnlockCost() method
Gets the cost to unlock a one time unlockable upgrade Gets the cost to unlock a one time unlockable upgrade
**Signature:** **Signature:**
```typescript ```typescript
getUnlockUpgradeCost(upgradeName: string): number; getUnlockCost(upgradeName: string): number;
``` ```
## Parameters ## Parameters

@ -1,15 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Corporation](./bitburner.corporation.md) &gt; [hasUnlockUpgrade](./bitburner.corporation.hasunlockupgrade.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Corporation](./bitburner.corporation.md) &gt; [hasUnlock](./bitburner.corporation.hasunlock.md)
## Corporation.hasUnlockUpgrade() method ## Corporation.hasUnlock() method
Check if you have a one time unlockable upgrade Check if you have a one time unlockable upgrade
**Signature:** **Signature:**
```typescript ```typescript
hasUnlockUpgrade(upgradeName: string): boolean; hasUnlock(upgradeName: string): boolean;
``` ```
## Parameters ## Parameters

@ -30,15 +30,15 @@ export interface Corporation extends WarehouseAPI, OfficeAPI
| [getIndustryData(industryName)](./bitburner.corporation.getindustrydata.md) | Get constant industry definition data for a specific industry | | [getIndustryData(industryName)](./bitburner.corporation.getindustrydata.md) | Get constant industry definition data for a specific industry |
| [getInvestmentOffer()](./bitburner.corporation.getinvestmentoffer.md) | Get an offer for investment based on you companies current valuation | | [getInvestmentOffer()](./bitburner.corporation.getinvestmentoffer.md) | Get an offer for investment based on you companies current valuation |
| [getMaterialData(materialName)](./bitburner.corporation.getmaterialdata.md) | Get constant data for a specific material | | [getMaterialData(materialName)](./bitburner.corporation.getmaterialdata.md) | Get constant data for a specific material |
| [getUnlockUpgradeCost(upgradeName)](./bitburner.corporation.getunlockupgradecost.md) | Gets the cost to unlock a one time unlockable upgrade | | [getUnlockCost(upgradeName)](./bitburner.corporation.getunlockcost.md) | Gets the cost to unlock a one time unlockable upgrade |
| [getUpgradeLevel(upgradeName)](./bitburner.corporation.getupgradelevel.md) | Get the level of a levelable upgrade | | [getUpgradeLevel(upgradeName)](./bitburner.corporation.getupgradelevel.md) | Get the level of a levelable upgrade |
| [getUpgradeLevelCost(upgradeName)](./bitburner.corporation.getupgradelevelcost.md) | Gets the cost to unlock the next level of a levelable upgrade | | [getUpgradeLevelCost(upgradeName)](./bitburner.corporation.getupgradelevelcost.md) | Gets the cost to unlock the next level of a levelable upgrade |
| [goPublic(numShares)](./bitburner.corporation.gopublic.md) | Go public | | [goPublic(numShares)](./bitburner.corporation.gopublic.md) | Go public |
| [hasCorporation()](./bitburner.corporation.hascorporation.md) | Returns whether the player has a corporation. Does not require API access. | | [hasCorporation()](./bitburner.corporation.hascorporation.md) | Returns whether the player has a corporation. Does not require API access. |
| [hasUnlockUpgrade(upgradeName)](./bitburner.corporation.hasunlockupgrade.md) | Check if you have a one time unlockable upgrade | | [hasUnlock(upgradeName)](./bitburner.corporation.hasunlock.md) | Check if you have a one time unlockable upgrade |
| [issueDividends(rate)](./bitburner.corporation.issuedividends.md) | Issue dividends | | [issueDividends(rate)](./bitburner.corporation.issuedividends.md) | Issue dividends |
| [issueNewShares(amount)](./bitburner.corporation.issuenewshares.md) | Issue new shares | | [issueNewShares(amount)](./bitburner.corporation.issuenewshares.md) | Issue new shares |
| [levelUpgrade(upgradeName)](./bitburner.corporation.levelupgrade.md) | Level an upgrade. | | [levelUpgrade(upgradeName)](./bitburner.corporation.levelupgrade.md) | Level an upgrade. |
| [purchaseUnlock(upgradeName)](./bitburner.corporation.purchaseunlock.md) | Unlock an upgrade |
| [sellShares(amount)](./bitburner.corporation.sellshares.md) | Sell Shares | | [sellShares(amount)](./bitburner.corporation.sellshares.md) | Sell Shares |
| [unlockUpgrade(upgradeName)](./bitburner.corporation.unlockupgrade.md) | Unlock an upgrade |

@ -1,15 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Corporation](./bitburner.corporation.md) &gt; [unlockUpgrade](./bitburner.corporation.unlockupgrade.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Corporation](./bitburner.corporation.md) &gt; [purchaseUnlock](./bitburner.corporation.purchaseunlock.md)
## Corporation.unlockUpgrade() method ## Corporation.purchaseUnlock() method
Unlock an upgrade Unlock an upgrade
**Signature:** **Signature:**
```typescript ```typescript
unlockUpgrade(upgradeName: string): void; purchaseUnlock(upgradeName: string): void;
``` ```
## Parameters ## Parameters

@ -13,7 +13,6 @@ type CorpResearchName =
| "AutoBrew" | "AutoBrew"
| "AutoPartyManager" | "AutoPartyManager"
| "Automatic Drug Administration" | "Automatic Drug Administration"
| "Bulk Purchasing"
| "CPH4 Injections" | "CPH4 Injections"
| "Drones" | "Drones"
| "Drones - Assembly" | "Drones - Assembly"
@ -21,7 +20,6 @@ type CorpResearchName =
| "Go-Juice" | "Go-Juice"
| "HRBuddy-Recruitment" | "HRBuddy-Recruitment"
| "HRBuddy-Training" | "HRBuddy-Training"
| "JoyWire"
| "Market-TA.I" | "Market-TA.I"
| "Market-TA.II" | "Market-TA.II"
| "Overclock" | "Overclock"

@ -22,12 +22,12 @@ interface Division
| [lastCycleRevenue](./bitburner.division.lastcyclerevenue.md) | | number | Revenue last cycle | | [lastCycleRevenue](./bitburner.division.lastcyclerevenue.md) | | number | Revenue last cycle |
| [makesProducts](./bitburner.division.makesproducts.md) | | boolean | Whether the industry this division is in is capable of making products | | [makesProducts](./bitburner.division.makesproducts.md) | | boolean | Whether the industry this division is in is capable of making products |
| [name](./bitburner.division.name.md) | | string | Name of the division | | [name](./bitburner.division.name.md) | | string | Name of the division |
| [numAdVerts](./bitburner.division.numadverts.md) | | number | Number of times AdVert has been bought |
| [popularity](./bitburner.division.popularity.md) | | number | Popularity of the division | | [popularity](./bitburner.division.popularity.md) | | number | Popularity of the division |
| [prodMult](./bitburner.division.prodmult.md) | | number | Production multiplier | | [productionMult](./bitburner.division.productionmult.md) | | number | Production multiplier |
| [products](./bitburner.division.products.md) | | string\[\] | Products developed by this division | | [products](./bitburner.division.products.md) | | string\[\] | Names of Products developed by this division |
| [research](./bitburner.division.research.md) | | number | Amount of research in that division | | [researchPoints](./bitburner.division.researchpoints.md) | | number | Amount of research in that division |
| [thisCycleExpenses](./bitburner.division.thiscycleexpenses.md) | | number | Expenses this cycle | | [thisCycleExpenses](./bitburner.division.thiscycleexpenses.md) | | number | Expenses this cycle |
| [thisCycleRevenue](./bitburner.division.thiscyclerevenue.md) | | number | Revenue this cycle | | [thisCycleRevenue](./bitburner.division.thiscyclerevenue.md) | | number | Revenue this cycle |
| [type](./bitburner.division.type.md) | | [CorpIndustryName](./bitburner.corpindustryname.md) | Type of division, like Agriculture | | [type](./bitburner.division.type.md) | | [CorpIndustryName](./bitburner.corpindustryname.md) | Type of division, like Agriculture |
| [upgrades](./bitburner.division.upgrades.md) | | number\[\] | All research bought |

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Division](./bitburner.division.md) &gt; [upgrades](./bitburner.division.upgrades.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Division](./bitburner.division.md) &gt; [numAdVerts](./bitburner.division.numadverts.md)
## Division.upgrades property ## Division.numAdVerts property
All research bought Number of times AdVert has been bought
**Signature:** **Signature:**
```typescript ```typescript
upgrades: number[]; numAdVerts: number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Division](./bitburner.division.md) &gt; [prodMult](./bitburner.division.prodmult.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Division](./bitburner.division.md) &gt; [productionMult](./bitburner.division.productionmult.md)
## Division.prodMult property ## Division.productionMult property
Production multiplier Production multiplier
**Signature:** **Signature:**
```typescript ```typescript
prodMult: number; productionMult: number;
``` ```

@ -4,7 +4,7 @@
## Division.products property ## Division.products property
Products developed by this division Names of Products developed by this division
**Signature:** **Signature:**

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Division](./bitburner.division.md) &gt; [research](./bitburner.division.research.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Division](./bitburner.division.md) &gt; [researchPoints](./bitburner.division.researchpoints.md)
## Division.research property ## Division.researchPoints property
Amount of research in that division Amount of research in that division
**Signature:** **Signature:**
```typescript ```typescript
research: number; researchPoints: number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [amt](./bitburner.export.amt.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [amount](./bitburner.export.amount.md)
## Export.amt property ## Export.amount property
Amount of material exported Amount of material exported
**Signature:** **Signature:**
```typescript ```typescript
amt: string; amount: string;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [loc](./bitburner.export.loc.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [city](./bitburner.export.city.md)
## Export.loc property ## Export.city property
City the material is being exported to City the material is being exported to
**Signature:** **Signature:**
```typescript ```typescript
loc: CityName; city: CityName;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [div](./bitburner.export.div.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Export](./bitburner.export.md) &gt; [division](./bitburner.export.division.md)
## Export.div property ## Export.division property
Division the material is being exported to Division the material is being exported to
**Signature:** **Signature:**
```typescript ```typescript
div: string; division: string;
``` ```

@ -16,7 +16,7 @@ interface Export
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [amt](./bitburner.export.amt.md) | | string | Amount of material exported | | [amount](./bitburner.export.amount.md) | | string | Amount of material exported |
| [div](./bitburner.export.div.md) | | string | Division the material is being exported to | | [city](./bitburner.export.city.md) | | [CityName](./bitburner.cityname.md) | City the material is being exported to |
| [loc](./bitburner.export.loc.md) | | [CityName](./bitburner.cityname.md) | City the material is being exported to | | [division](./bitburner.export.division.md) | | string | Division the material is being exported to |

@ -4,7 +4,7 @@
## IndustryData.cost property ## IndustryData.cost property
Cost to expand to the division Cost to make a new division of this industry type
**Signature:** **Signature:**

@ -16,7 +16,7 @@ interface IndustryData
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [cost](./bitburner.industrydata.cost.md) | | number | Cost to expand to the division | | [cost](./bitburner.industrydata.cost.md) | | number | Cost to make a new division of this industry type |
| [makesMaterials](./bitburner.industrydata.makesmaterials.md) | | boolean | Whether the division makes materials | | [makesMaterials](./bitburner.industrydata.makesmaterials.md) | | boolean | Whether the division makes materials |
| [makesProducts](./bitburner.industrydata.makesproducts.md) | | boolean | Whether the division makes products | | [makesProducts](./bitburner.industrydata.makesproducts.md) | | boolean | Whether the division makes products |
| [producedMaterials?](./bitburner.industrydata.producedmaterials.md) | | string\[\] | _(Optional)_ Materials produced | | [producedMaterials?](./bitburner.industrydata.producedmaterials.md) | | string\[\] | _(Optional)_ Materials produced |

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [actualSellAmount](./bitburner.material.actualsellamount.md)
## Material.actualSellAmount property
Amount of material sold last cycle
**Signature:**
```typescript
actualSellAmount: number;
```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [cmp](./bitburner.material.cmp.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [competition](./bitburner.material.competition.md)
## Material.cmp property ## Material.competition property
Competition for the material, only present if "Market Research - Competition" unlocked Competition for the material, only present if "Market Research - Competition" unlocked
**Signature:** **Signature:**
```typescript ```typescript
cmp: number | undefined; competition: number | undefined;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [dmd](./bitburner.material.dmd.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [demand](./bitburner.material.demand.md)
## Material.dmd property ## Material.demand property
Demand for the material, only present if "Market Research - Demand" unlocked Demand for the material, only present if "Market Research - Demand" unlocked
**Signature:** **Signature:**
```typescript ```typescript
dmd: number | undefined; demand: number | undefined;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [sell](./bitburner.material.sell.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [desiredSellAmount](./bitburner.material.desiredsellamount.md)
## Material.sell property ## Material.desiredSellAmount property
Amount of material sold Sell amount, can be "PROD/2"
**Signature:** **Signature:**
```typescript ```typescript
sell: number; desiredSellAmount: string | number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [sCost](./bitburner.material.scost.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [desiredSellPrice](./bitburner.material.desiredsellprice.md)
## Material.sCost property ## Material.desiredSellPrice property
Sell cost, can be "MP+5" Sell cost, can be "MP+5"
**Signature:** **Signature:**
```typescript ```typescript
sCost: string | number; desiredSellPrice: string | number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [exp](./bitburner.material.exp.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [exports](./bitburner.material.exports.md)
## Material.exp property ## Material.exports property
Export orders Export orders
**Signature:** **Signature:**
```typescript ```typescript
exp: Export[]; exports: Export[];
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [cost](./bitburner.material.cost.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [marketPrice](./bitburner.material.marketprice.md)
## Material.cost property ## Material.marketPrice property
Cost to buy material Cost to buy material
**Signature:** **Signature:**
```typescript ```typescript
cost: number; marketPrice: number;
``` ```

@ -16,15 +16,15 @@ interface Material
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [cmp](./bitburner.material.cmp.md) | | number \| undefined | Competition for the material, only present if "Market Research - Competition" unlocked | | [actualSellAmount](./bitburner.material.actualsellamount.md) | | number | Amount of material sold last cycle |
| [cost](./bitburner.material.cost.md) | | number | Cost to buy material | | [competition](./bitburner.material.competition.md) | | number \| undefined | Competition for the material, only present if "Market Research - Competition" unlocked |
| [dmd](./bitburner.material.dmd.md) | | number \| undefined | Demand for the material, only present if "Market Research - Demand" unlocked | | [demand](./bitburner.material.demand.md) | | number \| undefined | Demand for the material, only present if "Market Research - Demand" unlocked |
| [exp](./bitburner.material.exp.md) | | [Export](./bitburner.export.md)<!-- -->\[\] | Export orders | | [desiredSellAmount](./bitburner.material.desiredsellamount.md) | | string \| number | Sell amount, can be "PROD/2" |
| [desiredSellPrice](./bitburner.material.desiredsellprice.md) | | string \| number | Sell cost, can be "MP+5" |
| [exports](./bitburner.material.exports.md) | | [Export](./bitburner.export.md)<!-- -->\[\] | Export orders |
| [marketPrice](./bitburner.material.marketprice.md) | | number | Cost to buy material |
| [name](./bitburner.material.name.md) | | [CorpMaterialName](./bitburner.corpmaterialname.md) | Name of the material | | [name](./bitburner.material.name.md) | | [CorpMaterialName](./bitburner.corpmaterialname.md) | Name of the material |
| [prod](./bitburner.material.prod.md) | | number | Amount of material produced | | [productionAmount](./bitburner.material.productionamount.md) | | number | Amount of material produced last cycle |
| [qlt](./bitburner.material.qlt.md) | | number | Quality of the material | | [quality](./bitburner.material.quality.md) | | number | Quality of the material |
| [qty](./bitburner.material.qty.md) | | number | Amount of material | | [stored](./bitburner.material.stored.md) | | number | Amount of material |
| [sAmt](./bitburner.material.samt.md) | | string \| number | Sell amount, can be "PROD/2" |
| [sCost](./bitburner.material.scost.md) | | string \| number | Sell cost, can be "MP+5" |
| [sell](./bitburner.material.sell.md) | | number | Amount of material sold |

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [prod](./bitburner.material.prod.md)
## Material.prod property
Amount of material produced
**Signature:**
```typescript
prod: number;
```

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [productionAmount](./bitburner.material.productionamount.md)
## Material.productionAmount property
Amount of material produced last cycle
**Signature:**
```typescript
productionAmount: number;
```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [qlt](./bitburner.material.qlt.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [quality](./bitburner.material.quality.md)
## Material.qlt property ## Material.quality property
Quality of the material Quality of the material
**Signature:** **Signature:**
```typescript ```typescript
qlt: number; quality: number;
``` ```

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [sAmt](./bitburner.material.samt.md)
## Material.sAmt property
Sell amount, can be "PROD/2"
**Signature:**
```typescript
sAmt: string | number;
```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [qty](./bitburner.material.qty.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Material](./bitburner.material.md) &gt; [stored](./bitburner.material.stored.md)
## Material.qty property ## Material.stored property
Amount of material Amount of material
**Signature:** **Signature:**
```typescript ```typescript
qty: number; stored: number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [avgEne](./bitburner.office.avgene.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [avgEnergy](./bitburner.office.avgenergy.md)
## Office.avgEne property ## Office.avgEnergy property
Average energy of the employees Average energy of the employees
**Signature:** **Signature:**
```typescript ```typescript
avgEne: number; avgEnergy: number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [avgMor](./bitburner.office.avgmor.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [avgMorale](./bitburner.office.avgmorale.md)
## Office.avgMor property ## Office.avgMorale property
Average morale of the employees Average morale of the employees
**Signature:** **Signature:**
```typescript ```typescript
avgMor: number; avgMorale: number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [loc](./bitburner.office.loc.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [city](./bitburner.office.city.md)
## Office.loc property ## Office.city property
City of the office City of the office
**Signature:** **Signature:**
```typescript ```typescript
loc: CityName; city: CityName;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [employeeProd](./bitburner.office.employeeprod.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [employeeProductionByJob](./bitburner.office.employeeproductionbyjob.md)
## Office.employeeProd property ## Office.employeeProductionByJob property
Production of the employees Production of the employees
**Signature:** **Signature:**
```typescript ```typescript
employeeProd: Record<CorpEmployeePosition, number>; employeeProductionByJob: Record<CorpEmployeePosition, number>;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [maxEne](./bitburner.office.maxene.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [maxEnergy](./bitburner.office.maxenergy.md)
## Office.maxEne property ## Office.maxEnergy property
Maximum amount of energy of the employees Maximum amount of energy of the employees
**Signature:** **Signature:**
```typescript ```typescript
maxEne: number; maxEnergy: number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [maxMor](./bitburner.office.maxmor.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [maxMorale](./bitburner.office.maxmorale.md)
## Office.maxMor property ## Office.maxMorale property
Maximum morale of the employees Maximum morale of the employees
**Signature:** **Signature:**
```typescript ```typescript
maxMor: number; maxMorale: number;
``` ```

@ -16,14 +16,14 @@ export interface Office
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [avgEne](./bitburner.office.avgene.md) | | number | Average energy of the employees | | [avgEnergy](./bitburner.office.avgenergy.md) | | number | Average energy of the employees |
| [avgMor](./bitburner.office.avgmor.md) | | number | Average morale of the employees | | [avgMorale](./bitburner.office.avgmorale.md) | | number | Average morale of the employees |
| [city](./bitburner.office.city.md) | | [CityName](./bitburner.cityname.md) | City of the office |
| [employeeJobs](./bitburner.office.employeejobs.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Positions of the employees | | [employeeJobs](./bitburner.office.employeejobs.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Positions of the employees |
| [employeeProd](./bitburner.office.employeeprod.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Production of the employees | | [employeeProductionByJob](./bitburner.office.employeeproductionbyjob.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Production of the employees |
| [employees](./bitburner.office.employees.md) | | number | Amount of employees | | [maxEnergy](./bitburner.office.maxenergy.md) | | number | Maximum amount of energy of the employees |
| [loc](./bitburner.office.loc.md) | | [CityName](./bitburner.cityname.md) | City of the office | | [maxMorale](./bitburner.office.maxmorale.md) | | number | Maximum morale of the employees |
| [maxEne](./bitburner.office.maxene.md) | | number | Maximum amount of energy of the employees | | [numEmployees](./bitburner.office.numemployees.md) | | number | Amount of employees |
| [maxMor](./bitburner.office.maxmor.md) | | number | Maximum morale of the employees |
| [size](./bitburner.office.size.md) | | number | Maximum number of employee | | [size](./bitburner.office.size.md) | | number | Maximum number of employee |
| [totalExperience](./bitburner.office.totalexperience.md) | | number | Total experience of all employees | | [totalExperience](./bitburner.office.totalexperience.md) | | number | Total experience of all employees |

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [employees](./bitburner.office.employees.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Office](./bitburner.office.md) &gt; [numEmployees](./bitburner.office.numemployees.md)
## Office.employees property ## Office.numEmployees property
Amount of employees Amount of employees
**Signature:** **Signature:**
```typescript ```typescript
employees: number; numEmployees: number;
``` ```

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [actualSellAmount](./bitburner.product.actualsellamount.md)
## Product.actualSellAmount property
Amount of product sold last cycle
**Signature:**
```typescript
actualSellAmount: number;
```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [cmp](./bitburner.product.cmp.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [competition](./bitburner.product.competition.md)
## Product.cmp property ## Product.competition property
Competition for the product, only present if "Market Research - Competition" unlocked Competition for the product, only present if "Market Research - Competition" unlocked
**Signature:** **Signature:**
```typescript ```typescript
cmp: number | undefined; competition: number | undefined;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [dmd](./bitburner.product.dmd.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [demand](./bitburner.product.demand.md)
## Product.dmd property ## Product.demand property
Demand for the product, only present if "Market Research - Demand" unlocked Demand for the product, only present if "Market Research - Demand" unlocked
**Signature:** **Signature:**
```typescript ```typescript
dmd: number | undefined; demand: number | undefined;
``` ```

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [desiredSellAmount](./bitburner.product.desiredsellamount.md)
## Product.desiredSellAmount property
Desired sell amount, e.g. "PROD/2"
**Signature:**
```typescript
desiredSellAmount: string | number;
```

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [desiredSellPrice](./bitburner.product.desiredsellprice.md)
## Product.desiredSellPrice property
Desired sell price, can be "MP+5"
**Signature:**
```typescript
desiredSellPrice: string | number;
```

@ -4,7 +4,7 @@
## Product.developmentProgress property ## Product.developmentProgress property
Creation progress - A number between 0-100 representing percentage A number between 0-100 representing percentage completion
**Signature:** **Signature:**

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [effectiveRating](./bitburner.product.effectiverating.md)
## Product.effectiveRating property
Effective rating in the specific city
**Signature:**
```typescript
effectiveRating: number;
```

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [effRat](./bitburner.product.effrat.md)
## Product.effRat property
Effective rating
**Signature:**
```typescript
effRat: number;
```

@ -16,17 +16,17 @@ interface Product
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [cmp](./bitburner.product.cmp.md) | | number \| undefined | Competition for the product, only present if "Market Research - Competition" unlocked | | [actualSellAmount](./bitburner.product.actualsellamount.md) | | number | Amount of product sold last cycle |
| [developmentProgress](./bitburner.product.developmentprogress.md) | | number | Creation progress - A number between 0-100 representing percentage | | [competition](./bitburner.product.competition.md) | | number \| undefined | Competition for the product, only present if "Market Research - Competition" unlocked |
| [dmd](./bitburner.product.dmd.md) | | number \| undefined | Demand for the product, only present if "Market Research - Demand" unlocked | | [demand](./bitburner.product.demand.md) | | number \| undefined | Demand for the product, only present if "Market Research - Demand" unlocked |
| [effRat](./bitburner.product.effrat.md) | | number | Effective rating | | [desiredSellAmount](./bitburner.product.desiredsellamount.md) | | string \| number | Desired sell amount, e.g. "PROD/2" |
| [desiredSellPrice](./bitburner.product.desiredsellprice.md) | | string \| number | Desired sell price, can be "MP+5" |
| [developmentProgress](./bitburner.product.developmentprogress.md) | | number | A number between 0-100 representing percentage completion |
| [effectiveRating](./bitburner.product.effectiverating.md) | | number | Effective rating in the specific city |
| [name](./bitburner.product.name.md) | | string | Name of the product | | [name](./bitburner.product.name.md) | | string | Name of the product |
| [pCost](./bitburner.product.pcost.md) | | number | Production cost | | [productionAmount](./bitburner.product.productionamount.md) | | number | Amount of product produced last cycle |
| [prod](./bitburner.product.prod.md) | | number | Amount of product produced | | [productionCost](./bitburner.product.productioncost.md) | | number | Production cost |
| [properties](./bitburner.product.properties.md) | | { \[key: string\]: number } | Product Properties. The data is {<!-- -->qlt, per, dur, rel, aes, fea<!-- -->} | | [rating](./bitburner.product.rating.md) | | number | Rating based on stats |
| [qty](./bitburner.product.qty.md) | | number | Amount of product | | [stats](./bitburner.product.stats.md) | | { quality: number; performance: number; durability: number; reliability: number; aesthetics: number; features: number; } | Product stats |
| [rat](./bitburner.product.rat.md) | | number | Product Rating | | [stored](./bitburner.product.stored.md) | | number | Amount of product stored in warehouse |
| [sAmt](./bitburner.product.samt.md) | | string | Sell amount, can be "PROD/2" |
| [sCost](./bitburner.product.scost.md) | | string | Sell cost, can be "MP+5" |
| [sell](./bitburner.product.sell.md) | | number | Amount of product sold |

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [productionAmount](./bitburner.product.productionamount.md)
## Product.productionAmount property
Amount of product produced last cycle
**Signature:**
```typescript
productionAmount: number;
```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [pCost](./bitburner.product.pcost.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [productionCost](./bitburner.product.productioncost.md)
## Product.pCost property ## Product.productionCost property
Production cost Production cost
**Signature:** **Signature:**
```typescript ```typescript
pCost: number; productionCost: number;
``` ```

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [properties](./bitburner.product.properties.md)
## Product.properties property
Product Properties. The data is {<!-- -->qlt, per, dur, rel, aes, fea<!-- -->}
**Signature:**
```typescript
properties: { [key: string]: number };
```

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [rat](./bitburner.product.rat.md)
## Product.rat property
Product Rating
**Signature:**
```typescript
rat: number;
```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [prod](./bitburner.product.prod.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [rating](./bitburner.product.rating.md)
## Product.prod property ## Product.rating property
Amount of product produced Rating based on stats
**Signature:** **Signature:**
```typescript ```typescript
prod: number; rating: number;
``` ```

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [sAmt](./bitburner.product.samt.md)
## Product.sAmt property
Sell amount, can be "PROD/2"
**Signature:**
```typescript
sAmt: string;
```

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [sCost](./bitburner.product.scost.md)
## Product.sCost property
Sell cost, can be "MP+5"
**Signature:**
```typescript
sCost: string;
```

@ -1,13 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [sell](./bitburner.product.sell.md)
## Product.sell property
Amount of product sold
**Signature:**
```typescript
sell: number;
```

@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [stats](./bitburner.product.stats.md)
## Product.stats property
Product stats
**Signature:**
```typescript
stats: {
quality: number;
performance: number;
durability: number;
reliability: number;
aesthetics: number;
features: number;
};
```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [qty](./bitburner.product.qty.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Product](./bitburner.product.md) &gt; [stored](./bitburner.product.stored.md)
## Product.qty property ## Product.stored property
Amount of product Amount of product stored in warehouse
**Signature:** **Signature:**
```typescript ```typescript
qty: number; stored: number;
``` ```

@ -1,13 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Warehouse](./bitburner.warehouse.md) &gt; [loc](./bitburner.warehouse.loc.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Warehouse](./bitburner.warehouse.md) &gt; [city](./bitburner.warehouse.city.md)
## Warehouse.loc property ## Warehouse.city property
City in which the warehouse is located City in which the warehouse is located
**Signature:** **Signature:**
```typescript ```typescript
loc: CityName; city: CityName;
``` ```

@ -16,8 +16,8 @@ interface Warehouse
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [city](./bitburner.warehouse.city.md) | | [CityName](./bitburner.cityname.md) | City in which the warehouse is located |
| [level](./bitburner.warehouse.level.md) | | number | Amount of size upgrade bought | | [level](./bitburner.warehouse.level.md) | | number | Amount of size upgrade bought |
| [loc](./bitburner.warehouse.loc.md) | | [CityName](./bitburner.cityname.md) | City in which the warehouse is located |
| [size](./bitburner.warehouse.size.md) | | number | Total space in the warehouse | | [size](./bitburner.warehouse.size.md) | | number | Total space in the warehouse |
| [sizeUsed](./bitburner.warehouse.sizeused.md) | | number | Used space in the warehouse | | [sizeUsed](./bitburner.warehouse.sizeused.md) | | number | Used space in the warehouse |
| [smartSupplyEnabled](./bitburner.warehouse.smartsupplyenabled.md) | | boolean | Smart Supply status in the warehouse | | [smartSupplyEnabled](./bitburner.warehouse.smartsupplyenabled.md) | | boolean | Smart Supply status in the warehouse |

@ -9,7 +9,7 @@ Get product data
**Signature:** **Signature:**
```typescript ```typescript
getProduct(divisionName: string, city: CityName | `${CityName}`, productName: string): Product; getProduct(divisionName: string, cityName: CityName | `${CityName}`, productName: string): Product;
``` ```
## Parameters ## Parameters
@ -17,7 +17,7 @@ getProduct(divisionName: string, city: CityName | `${CityName}`, productName: st
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| divisionName | string | Name of the division | | divisionName | string | Name of the division |
| city | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Name of the city | | cityName | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | |
| productName | string | Name of the product | | productName | string | Name of the product |
**Returns:** **Returns:**

@ -26,7 +26,7 @@ Requires the Warehouse API upgrade from your corporation.
| [discontinueProduct(divisionName, productName)](./bitburner.warehouseapi.discontinueproduct.md) | Discontinue a product. | | [discontinueProduct(divisionName, productName)](./bitburner.warehouseapi.discontinueproduct.md) | Discontinue a product. |
| [exportMaterial(sourceDivision, sourceCity, targetDivision, targetCity, materialName, amt)](./bitburner.warehouseapi.exportmaterial.md) | Set material export data | | [exportMaterial(sourceDivision, sourceCity, targetDivision, targetCity, materialName, amt)](./bitburner.warehouseapi.exportmaterial.md) | Set material export data |
| [getMaterial(divisionName, city, materialName)](./bitburner.warehouseapi.getmaterial.md) | Get material data | | [getMaterial(divisionName, city, materialName)](./bitburner.warehouseapi.getmaterial.md) | Get material data |
| [getProduct(divisionName, city, productName)](./bitburner.warehouseapi.getproduct.md) | Get product data | | [getProduct(divisionName, cityName, productName)](./bitburner.warehouseapi.getproduct.md) | Get product data |
| [getUpgradeWarehouseCost(divisionName, city, amt)](./bitburner.warehouseapi.getupgradewarehousecost.md) | Gets the cost to upgrade a warehouse to the next level | | [getUpgradeWarehouseCost(divisionName, city, amt)](./bitburner.warehouseapi.getupgradewarehousecost.md) | Gets the cost to upgrade a warehouse to the next level |
| [getWarehouse(divisionName, city)](./bitburner.warehouseapi.getwarehouse.md) | Get warehouse data | | [getWarehouse(divisionName, city)](./bitburner.warehouseapi.getwarehouse.md) | Get warehouse data |
| [hasWarehouse(divisionName, city)](./bitburner.warehouseapi.haswarehouse.md) | Check if you have a warehouse in city | | [hasWarehouse(divisionName, city)](./bitburner.warehouseapi.haswarehouse.md) | Check if you have a warehouse in city |
@ -38,8 +38,8 @@ Requires the Warehouse API upgrade from your corporation.
| [sellProduct(divisionName, city, productName, amt, price, all)](./bitburner.warehouseapi.sellproduct.md) | Set product sell data. | | [sellProduct(divisionName, city, productName, amt, price, all)](./bitburner.warehouseapi.sellproduct.md) | Set product sell data. |
| [setMaterialMarketTA1(divisionName, city, materialName, on)](./bitburner.warehouseapi.setmaterialmarketta1.md) | Set market TA 1 for a material. | | [setMaterialMarketTA1(divisionName, city, materialName, on)](./bitburner.warehouseapi.setmaterialmarketta1.md) | Set market TA 1 for a material. |
| [setMaterialMarketTA2(divisionName, city, materialName, on)](./bitburner.warehouseapi.setmaterialmarketta2.md) | Set market TA 2 for a material. | | [setMaterialMarketTA2(divisionName, city, materialName, on)](./bitburner.warehouseapi.setmaterialmarketta2.md) | Set market TA 2 for a material. |
| [setProductMarketTA1(divisionName, city, productName, on)](./bitburner.warehouseapi.setproductmarketta1.md) | Set market TA 1 for a product. | | [setProductMarketTA1(divisionName, productName, on)](./bitburner.warehouseapi.setproductmarketta1.md) | \* Set market TA 1 for a product. |
| [setProductMarketTA2(divisionName, city, productName, on)](./bitburner.warehouseapi.setproductmarketta2.md) | Set market TA 2 for a product. | | [setProductMarketTA2(divisionName, productName, on)](./bitburner.warehouseapi.setproductmarketta2.md) | Set market TA 2 for a product. |
| [setSmartSupply(divisionName, city, enabled)](./bitburner.warehouseapi.setsmartsupply.md) | Set smart supply | | [setSmartSupply(divisionName, city, enabled)](./bitburner.warehouseapi.setsmartsupply.md) | Set smart supply |
| [setSmartSupplyOption(divisionName, city, materialName, option)](./bitburner.warehouseapi.setsmartsupplyoption.md) | Set whether smart supply uses leftovers before buying | | [setSmartSupplyOption(divisionName, city, materialName, option)](./bitburner.warehouseapi.setsmartsupplyoption.md) | Set whether smart supply uses leftovers before buying |
| [upgradeWarehouse(divisionName, city, amt)](./bitburner.warehouseapi.upgradewarehouse.md) | Upgrade warehouse | | [upgradeWarehouse(divisionName, city, amt)](./bitburner.warehouseapi.upgradewarehouse.md) | Upgrade warehouse |

@ -4,12 +4,12 @@
## WarehouseAPI.setProductMarketTA1() method ## WarehouseAPI.setProductMarketTA1() method
Set market TA 1 for a product. \* Set market TA 1 for a product.
**Signature:** **Signature:**
```typescript ```typescript
setProductMarketTA1(divisionName: string, city: CityName | `${CityName}`, productName: string, on: boolean): void; setProductMarketTA1(divisionName: string, productName: string, on: boolean): void;
``` ```
## Parameters ## Parameters
@ -17,7 +17,6 @@ setProductMarketTA1(divisionName: string, city: CityName | `${CityName}`, produc
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| divisionName | string | Name of the division | | divisionName | string | Name of the division |
| city | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Name of the city |
| productName | string | Name of the product | | productName | string | Name of the product |
| on | boolean | market ta enabled | | on | boolean | market ta enabled |

@ -9,7 +9,7 @@ Set market TA 2 for a product.
**Signature:** **Signature:**
```typescript ```typescript
setProductMarketTA2(divisionName: string, city: CityName | `${CityName}`, productName: string, on: boolean): void; setProductMarketTA2(divisionName: string, productName: string, on: boolean): void;
``` ```
## Parameters ## Parameters
@ -17,7 +17,6 @@ setProductMarketTA2(divisionName: string, city: CityName | `${CityName}`, produc
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| divisionName | string | Name of the division | | divisionName | string | Name of the division |
| city | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Name of the city |
| productName | string | Name of the product | | productName | string | Name of the product |
| on | boolean | market ta enabled | | on | boolean | market ta enabled |

@ -2,7 +2,7 @@ import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { SkillNames } from "../Bladeburner/data/SkillNames"; import { SkillNames } from "../Bladeburner/data/SkillNames";
import { Skills } from "../Bladeburner/Skills"; import { Skills } from "../Bladeburner/Skills";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { IndustryType } from "../Corporation/data/Enums"; import { CorpUnlockName, IndustryType } from "../Corporation/data/Enums";
import { Exploit } from "../Exploits/Exploit"; import { Exploit } from "../Exploits/Exploit";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { AllGangs } from "../Gang/AllGangs"; import { AllGangs } from "../Gang/AllGangs";
@ -27,6 +27,7 @@ import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { workerScripts } from "../Netscript/WorkerScripts"; import { workerScripts } from "../Netscript/WorkerScripts";
import type { PlayerObject } from "../PersonObjects/Player/PlayerObject"; import type { PlayerObject } from "../PersonObjects/Player/PlayerObject";
import { getRecordValues } from "../Types/Record";
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise... // Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements; const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
@ -441,23 +442,28 @@ export const achievements: Record<string, Achievement> = {
...achievementData.CORPORATION_BRIBE, ...achievementData.CORPORATION_BRIBE,
Icon: "CORPLOBBY", Icon: "CORPLOBBY",
Visible: () => hasAccessToSF(Player, 3), Visible: () => hasAccessToSF(Player, 3),
Condition: () => Player.corporation !== null && Player.corporation.unlockUpgrades[6] === 1, Condition: () => !!Player.corporation && Player.corporation.unlocks.has(CorpUnlockName.GovernmentPartnership),
}, },
CORPORATION_PROD_1000: { CORPORATION_PROD_1000: {
...achievementData.CORPORATION_PROD_1000, ...achievementData.CORPORATION_PROD_1000,
Icon: "CORP1000", Icon: "CORP1000",
Visible: () => hasAccessToSF(Player, 3), Visible: () => hasAccessToSF(Player, 3),
Condition: () => Player.corporation !== null && Player.corporation.divisions.some((d) => d.prodMult >= 1000), Condition: () => {
if (!Player.corporation) return false;
for (const division of Player.corporation.divisions.values()) {
if (division.productionMult >= 1000) return true;
}
return false;
},
}, },
CORPORATION_EMPLOYEE_3000: { CORPORATION_EMPLOYEE_3000: {
...achievementData.CORPORATION_EMPLOYEE_3000, ...achievementData.CORPORATION_EMPLOYEE_3000,
Icon: "CORPCITY", Icon: "CORPCITY",
Visible: () => hasAccessToSF(Player, 3), Visible: () => hasAccessToSF(Player, 3),
Condition: (): boolean => { Condition: (): boolean => {
if (Player.corporation === null) return false; if (!Player.corporation) return false;
for (const d of Player.corporation.divisions) { for (const division of Player.corporation.divisions.values()) {
let totalEmployees = 0; const totalEmployees = getRecordValues(division.offices).reduce((a, b) => a + b.numEmployees, 0);
for (const o of Object.values(d.offices)) if (o && o.totalEmployees) totalEmployees += o.totalEmployees;
if (totalEmployees >= 3000) return true; if (totalEmployees >= 3000) return true;
} }
return false; return false;
@ -469,8 +475,13 @@ export const achievements: Record<string, Achievement> = {
Name: "Own the land", Name: "Own the land",
Description: "Expand to the Real Estate division.", Description: "Expand to the Real Estate division.",
Visible: () => hasAccessToSF(Player, 3), Visible: () => hasAccessToSF(Player, 3),
Condition: () => Condition: () => {
Player.corporation !== null && Player.corporation.divisions.some((d) => d.type === IndustryType.RealEstate), if (!Player.corporation) return false;
for (const division of Player.corporation.divisions.values()) {
if (division.type === IndustryType.RealEstate) return true;
}
return false;
},
}, },
INTELLIGENCE_255: { INTELLIGENCE_255: {
...achievementData.INTELLIGENCE_255, ...achievementData.INTELLIGENCE_255,

@ -1,7 +1,7 @@
// Class definition for a single Augmentation object // Class definition for a single Augmentation object
import * as React from "react"; import * as React from "react";
import type { CompletedProgramName } from "src/Programs/Programs"; import type { CompletedProgramName } from "../Programs/Programs";
import { Faction } from "../Faction/Faction"; import { Faction } from "../Faction/Faction";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { formatPercent } from "../ui/formatNumber"; import { formatPercent } from "../ui/formatNumber";

@ -37,6 +37,7 @@ import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportW
import { WorkStats, newWorkStats } from "../Work/WorkStats"; import { WorkStats, newWorkStats } from "../Work/WorkStats";
import { CityName } from "../Enums"; import { CityName } from "../Enums";
import { getRandomMember } from "../utils/helpers/enum"; import { getRandomMember } from "../utils/helpers/enum";
import { createEnumKeyedRecord } from "../Types/Record";
export interface BlackOpsAttempt { export interface BlackOpsAttempt {
error?: string; error?: string;
@ -70,7 +71,7 @@ export class Bladeburner {
type: ActionTypes.Idle, type: ActionTypes.Idle,
}); });
cities: Record<CityName, City>; cities = createEnumKeyedRecord(CityName, (name) => new City(name));
city = CityName.Sector12; city = CityName.Sector12;
// Todo: better types for all these Record<string, etc> types. Will need custom types or enums for the named string categories (e.g. skills). // Todo: better types for all these Record<string, etc> types. Will need custom types or enums for the named string categories (e.g. skills).
skills: Record<string, number> = {}; skills: Record<string, number> = {};
@ -101,10 +102,6 @@ export class Bladeburner {
consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"]; consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"];
constructor() { constructor() {
this.cities = {} as Record<CityName, City>;
// This for loop ensures the above type is met for this.cities.
for (const city of Object.values(CityName)) this.cities[city] = new City(city);
this.updateSkillMultipliers(); // Calls resetSkillMultipliers() this.updateSkillMultipliers(); // Calls resetSkillMultipliers()
// Max Stamina is based on stats and Bladeburner-specific bonuses // Max Stamina is based on stats and Bladeburner-specific bonuses

@ -2,33 +2,28 @@ import { Player } from "@player";
import { MaterialInfo } from "./MaterialInfo"; import { MaterialInfo } from "./MaterialInfo";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
import { IndustryResearchTrees, IndustriesData } from "./IndustryData"; import { IndustryResearchTrees, IndustriesData } from "./IndustryData";
import { Industry } from "./Industry"; import { Division } from "./Division";
import * as corpConstants from "./data/Constants"; import * as corpConstants from "./data/Constants";
import { OfficeSpace } from "./OfficeSpace"; import { OfficeSpace } from "./OfficeSpace";
import { Material } from "./Material"; import { Material } from "./Material";
import { Product } from "./Product"; import { Product } from "./Product";
import { Warehouse } from "./Warehouse"; import { Warehouse } from "./Warehouse";
import { CorporationUnlockUpgrade } from "./data/CorporationUnlockUpgrades";
import { CorporationUpgrade } from "./data/CorporationUpgrades";
import { Cities } from "../Locations/Cities";
import { IndustryType } from "./data/Enums"; import { IndustryType } from "./data/Enums";
import { ResearchMap } from "./ResearchMap"; import { ResearchMap } from "./ResearchMap";
import { isRelevantMaterial } from "./ui/Helpers"; import { isRelevantMaterial } from "./ui/Helpers";
import { CityName } from "../Enums"; import { CityName } from "../Enums";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CorpResearchName } from "@nsdefs"; import { CorpResearchName } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers";
import { isInteger } from "lodash"; import { isInteger } from "lodash";
import { getRecordValues } from "../Types/Record";
export function NewIndustry(corporation: Corporation, industry: IndustryType, name: string): void { export function NewIndustry(corporation: Corporation, industry: IndustryType, name: string): void {
if (corporation.divisions.length >= corporation.maxDivisions) if (corporation.divisions.size >= corporation.maxDivisions)
throw new Error(`Cannot expand into ${industry} industry, too many divisions!`); throw new Error(`Cannot expand into ${industry} industry, too many divisions!`);
for (let i = 0; i < corporation.divisions.length; ++i) { if (corporation.divisions.has(name)) throw new Error(`Division name ${name} is already in use!`);
if (corporation.divisions[i].name === name) { // "Overview" is forbidden as a division name, see CorporationRoot.tsx for why this would cause issues.
throw new Error("This division name is already in use!"); if (name === "Overview") throw new Error(`"Overview" is a forbidden division name.`);
}
}
const data = IndustriesData[industry]; const data = IndustriesData[industry];
if (!data) throw new Error(`Invalid industry: '${industry}'`); if (!data) throw new Error(`Invalid industry: '${industry}'`);
@ -39,8 +34,9 @@ export function NewIndustry(corporation: Corporation, industry: IndustryType, na
throw new Error("New division must have a name!"); throw new Error("New division must have a name!");
} else { } else {
corporation.funds = corporation.funds - cost; corporation.funds = corporation.funds - cost;
corporation.divisions.push( corporation.divisions.set(
new Industry({ name,
new Division({
corp: corporation, corp: corporation,
name: name, name: name,
type: industry, type: industry,
@ -50,13 +46,11 @@ export function NewIndustry(corporation: Corporation, industry: IndustryType, na
} }
export function removeIndustry(corporation: Corporation, name: string) { export function removeIndustry(corporation: Corporation, name: string) {
const divIndex = corporation.divisions.findIndex((div) => div.name === name); if (!corporation.divisions.has(name)) throw new Error("There is no division called " + name);
if (divIndex === -1) throw new Error("There is no division called " + name); corporation.divisions.delete(name);
corporation.divisions.splice(divIndex, 1);
} }
export function NewCity(corporation: Corporation, division: Industry, city: CityName): void { export function purchaseOffice(corporation: Corporation, division: Division, city: CityName): void {
if (corporation.funds < corpConstants.officeInitialCost) { if (corporation.funds < corpConstants.officeInitialCost) {
throw new Error("You don't have enough company funds to open a new office!"); throw new Error("You don't have enough company funds to open a new office!");
} }
@ -65,30 +59,11 @@ export function NewCity(corporation: Corporation, division: Industry, city: City
} }
corporation.funds = corporation.funds - corpConstants.officeInitialCost; corporation.funds = corporation.funds - corpConstants.officeInitialCost;
division.offices[city] = new OfficeSpace({ division.offices[city] = new OfficeSpace({
loc: city, city: city,
size: corpConstants.officeInitialSize, size: corpConstants.officeInitialSize,
}); });
} }
export function UnlockUpgrade(corporation: Corporation, upgrade: CorporationUnlockUpgrade): void {
if (corporation.funds < upgrade.price) {
throw new Error("Insufficient funds");
}
if (corporation.unlockUpgrades[upgrade.index] === 1) {
throw new Error(`You have already unlocked the ${upgrade.name} upgrade!`);
}
corporation.unlock(upgrade);
}
export function LevelUpgrade(corporation: Corporation, upgrade: CorporationUpgrade, amount: number): void {
const cost = calculateUpgradeCost(corporation, upgrade, amount);
if (corporation.funds < cost) {
throw new Error("Insufficient funds");
} else {
corporation.upgrade(upgrade, amount);
}
}
export function IssueDividends(corporation: Corporation, rate: number): void { export function IssueDividends(corporation: Corporation, rate: number): void {
if (isNaN(rate) || rate < 0 || rate > corpConstants.dividendMaxRate) { if (isNaN(rate) || rate < 0 || rate > corpConstants.dividendMaxRate) {
throw new Error(`Invalid value. Must be an number between 0 and ${corpConstants.dividendMaxRate}`); throw new Error(`Invalid value. Must be an number between 0 and ${corpConstants.dividendMaxRate}`);
@ -124,9 +99,9 @@ export function IssueNewShares(corporation: Corporation, amount: number): [numbe
return [profit, amount, privateShares]; return [profit, amount, privateShares];
} }
export function SellMaterial(mat: Material, amt: string, price: string): void { export function SellMaterial(material: Material, amount: string, price: string): void {
if (price === "") price = "0"; if (price === "") price = "0";
if (amt === "") amt = "0"; if (amount === "") amount = "0";
let cost = price.replace(/\s+/g, ""); let cost = price.replace(/\s+/g, "");
cost = cost.replace(/[^-()\d/*+.MPe]/g, ""); //Sanitize cost cost = cost.replace(/[^-()\d/*+.MPe]/g, ""); //Sanitize cost
let temp = cost.replace(/MP/, "1.234e5"); let temp = cost.replace(/MP/, "1.234e5");
@ -142,19 +117,19 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
} }
if (cost.includes("MP")) { if (cost.includes("MP")) {
mat.sCost = cost; //Dynamically evaluated material.desiredSellPrice = cost; //Dynamically evaluated
} else { } else {
mat.sCost = temp; material.desiredSellPrice = temp;
} }
//Parse quantity //Parse quantity
amt = amt.toUpperCase(); amount = amount.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) { if (amount.includes("MAX") || amount.includes("PROD") || amount.includes("INV")) {
let q = amt.replace(/\s+/g, ""); let q = amount.replace(/\s+/g, "");
q = q.replace(/[^-()\d/*+.MAXPRODINV]/g, ""); q = q.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let tempQty = q.replace(/MAX/g, mat.maxsll.toString()); let tempQty = q.replace(/MAX/g, material.maxSellPerCycle.toString());
tempQty = tempQty.replace(/PROD/g, mat.prd.toString()); tempQty = tempQty.replace(/PROD/g, material.productionAmount.toString());
tempQty = tempQty.replace(/INV/g, mat.prd.toString()); tempQty = tempQty.replace(/INV/g, material.productionAmount.toString());
try { try {
tempQty = eval(tempQty); tempQty = eval(tempQty);
} catch (e) { } catch (e) {
@ -164,26 +139,19 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
if (tempQty == null || isNaN(parseFloat(tempQty))) { if (tempQty == null || isNaN(parseFloat(tempQty))) {
throw new Error("Invalid value or expression for sell quantity field"); throw new Error("Invalid value or expression for sell quantity field");
} }
mat.sllman[0] = true; material.desiredSellAmount = q; //Use sanitized input
mat.sllman[1] = q; //Use sanitized input } else if (isNaN(parseFloat(amount)) || parseFloat(amount) < 0) {
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'"); throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'");
} else { } else {
let q = parseFloat(amt); let q = parseFloat(amount);
if (isNaN(q)) { if (isNaN(q)) {
q = 0; q = 0;
} }
if (q === 0) { material.desiredSellAmount = q;
mat.sllman[0] = false;
mat.sllman[1] = 0;
} else {
mat.sllman[0] = true;
mat.sllman[1] = q;
}
} }
} }
export function SellProduct(product: Product, city: string, amt: string, price: string, all: boolean): void { export function SellProduct(product: Product, city: CityName, amt: string, price: string, all: boolean): void {
//Parse price //Parse price
if (price.includes("MP")) { if (price.includes("MP")) {
//Dynamically evaluated quantity. First test to make sure its valid //Dynamically evaluated quantity. First test to make sure its valid
@ -200,27 +168,24 @@ export function SellProduct(product: Product, city: string, amt: string, price:
if (temp == null || isNaN(parseFloat(temp))) { if (temp == null || isNaN(parseFloat(temp))) {
throw new Error("Invalid value or expression for sell price field."); throw new Error("Invalid value or expression for sell price field.");
} }
product.sCost[city] = price; //Use sanitized price product.cityData[city].desiredSellPrice = price; //Use sanitized price
} else { } else {
const cost = parseFloat(price); const cost = parseFloat(price);
if (isNaN(cost)) { if (isNaN(cost)) {
throw new Error("Invalid value for sell price field"); throw new Error("Invalid value for sell price field");
} }
product.sCost[city] = cost; product.cityData[city].desiredSellPrice = cost;
} }
// Array of all cities. Used later
const cities = Object.keys(Cities);
// Parse quantity // Parse quantity
amt = amt.toUpperCase(); amt = amt.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) { if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) {
//Dynamically evaluated quantity. First test to make sure its valid //Dynamically evaluated quantity. First test to make sure its valid
let qty = amt.replace(/\s+/g, ""); let qty = amt.replace(/\s+/g, "");
qty = qty.replace(/[^-()\d/*+.MAXPRODINV]/g, ""); qty = qty.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let temp = qty.replace(/MAX/g, product.maxsll.toString()); let temp = qty.replace(/MAX/g, product.maxSellAmount.toString());
temp = temp.replace(/PROD/g, product.data[city][1].toString()); temp = temp.replace(/PROD/g, product.cityData[city].productionAmount.toString());
temp = temp.replace(/INV/g, product.data[city][0].toString()); temp = temp.replace(/INV/g, product.cityData[city].stored.toString());
try { try {
temp = eval(temp); temp = eval(temp);
} catch (e) { } catch (e) {
@ -231,14 +196,11 @@ export function SellProduct(product: Product, city: string, amt: string, price:
throw new Error("Invalid value or expression for sell quantity field"); throw new Error("Invalid value or expression for sell quantity field");
} }
if (all) { if (all) {
for (let i = 0; i < cities.length; ++i) { for (const cityName of Object.values(CityName)) {
const tempCity = cities[i]; product.cityData[cityName].desiredSellAmount = qty; //Use sanitized input
product.sllman[tempCity][0] = true;
product.sllman[tempCity][1] = qty; //Use sanitized input
} }
} else { } else {
product.sllman[city][0] = true; product.cityData[city].desiredSellAmount = qty; //Use sanitized input
product.sllman[city][1] = qty; //Use sanitized input
} }
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) { } else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'"); throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'");
@ -249,24 +211,18 @@ export function SellProduct(product: Product, city: string, amt: string, price:
} }
if (qty === 0) { if (qty === 0) {
if (all) { if (all) {
for (let i = 0; i < cities.length; ++i) { for (const cityName of Object.values(CityName)) {
const tempCity = cities[i]; product.cityData[cityName].desiredSellAmount = 0;
product.sllman[tempCity][0] = false;
product.sllman[tempCity][1] = "";
} }
} else { } else {
product.sllman[city][0] = false; product.cityData[city].desiredSellAmount = 0;
product.sllman[city][1] = "";
} }
} else if (all) { } else if (all) {
for (let i = 0; i < cities.length; ++i) { for (const cityName of Object.values(CityName)) {
const tempCity = cities[i]; product.cityData[cityName].desiredSellAmount = qty;
product.sllman[tempCity][0] = true;
product.sllman[tempCity][1] = qty;
} }
} else { } else {
product.sllman[city][0] = true; product.cityData[city].desiredSellAmount = qty;
product.sllman[city][1] = qty;
} }
} }
} }
@ -286,7 +242,7 @@ export function BuyMaterial(material: Material, amt: number): void {
if (isNaN(amt) || amt < 0) { if (isNaN(amt) || amt < 0) {
throw new Error(`Invalid amount '${amt}' to buy material '${material.name}'`); throw new Error(`Invalid amount '${amt}' to buy material '${material.name}'`);
} }
material.buy = amt; material.buyAmount = amt;
} }
export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material: Material, amt: number): void { export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material: Material, amt: number): void {
@ -298,10 +254,10 @@ export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material:
if (amt > maxAmount) { if (amt > maxAmount) {
throw new Error(`You do not have enough warehouse size to fit this purchase`); throw new Error(`You do not have enough warehouse size to fit this purchase`);
} }
const cost = amt * material.bCost; const cost = amt * material.marketPrice;
if (corp.funds >= cost) { if (corp.funds >= cost) {
corp.funds = corp.funds - cost; corp.funds = corp.funds - cost;
material.qty += amt; material.stored += amt;
} else { } else {
throw new Error(`You cannot afford this purchase.`); throw new Error(`You cannot afford this purchase.`);
} }
@ -365,7 +321,7 @@ export function BuyTea(corp: Corporation, office: OfficeSpace): boolean {
export function ThrowParty(corp: Corporation, office: OfficeSpace, costPerEmployee: number): number { export function ThrowParty(corp: Corporation, office: OfficeSpace, costPerEmployee: number): number {
const mult = 1 + costPerEmployee / 10e6; const mult = 1 + costPerEmployee / 10e6;
const cost = costPerEmployee * office.totalEmployees; const cost = costPerEmployee * office.numEmployees;
if (corp.funds < cost) { if (corp.funds < cost) {
return 0; return 0;
} }
@ -378,16 +334,15 @@ export function ThrowParty(corp: Corporation, office: OfficeSpace, costPerEmploy
return mult; return mult;
} }
export function PurchaseWarehouse(corp: Corporation, division: Industry, city: CityName): void { export function purchaseWarehouse(corp: Corporation, division: Division, city: CityName): void {
if (corp.funds < corpConstants.warehouseInitialCost) return; if (corp.funds < corpConstants.warehouseInitialCost) return;
if (division.warehouses[city]) return; if (division.warehouses[city]) return;
corp.funds = corp.funds - corpConstants.warehouseInitialCost;
division.warehouses[city] = new Warehouse({ division.warehouses[city] = new Warehouse({
corp: corp, division: division,
industry: division,
loc: city, loc: city,
size: corpConstants.warehouseInitialSize, size: corpConstants.warehouseInitialSize,
}); });
corp.funds = corp.funds - corpConstants.warehouseInitialCost;
} }
export function UpgradeWarehouseCost(warehouse: Warehouse, amt: number): number { export function UpgradeWarehouseCost(warehouse: Warehouse, amt: number): number {
@ -397,7 +352,7 @@ export function UpgradeWarehouseCost(warehouse: Warehouse, amt: number): number
); );
} }
export function UpgradeWarehouse(corp: Corporation, division: Industry, warehouse: Warehouse, amt = 1): void { export function UpgradeWarehouse(corp: Corporation, division: Division, warehouse: Warehouse, amt = 1): void {
const sizeUpgradeCost = UpgradeWarehouseCost(warehouse, amt); const sizeUpgradeCost = UpgradeWarehouseCost(warehouse, amt);
if (corp.funds < sizeUpgradeCost) return; if (corp.funds < sizeUpgradeCost) return;
warehouse.level += amt; warehouse.level += amt;
@ -405,7 +360,7 @@ export function UpgradeWarehouse(corp: Corporation, division: Industry, warehous
corp.funds = corp.funds - sizeUpgradeCost; corp.funds = corp.funds - sizeUpgradeCost;
} }
export function HireAdVert(corp: Corporation, division: Industry): void { export function HireAdVert(corp: Corporation, division: Division): void {
const cost = division.getAdVertCost(); const cost = division.getAdVertCost();
if (corp.funds < cost) return; if (corp.funds < cost) return;
corp.funds = corp.funds - cost; corp.funds = corp.funds - cost;
@ -414,7 +369,7 @@ export function HireAdVert(corp: Corporation, division: Industry): void {
export function MakeProduct( export function MakeProduct(
corp: Corporation, corp: Corporation,
division: Industry, division: Division,
city: CityName, city: CityName,
productName: string, productName: string,
designInvest: number, designInvest: number,
@ -447,53 +402,46 @@ export function MakeProduct(
} else if (division.hasResearch("uPgrade: Capacity.I")) { } else if (division.hasResearch("uPgrade: Capacity.I")) {
maxProducts = 4; maxProducts = 4;
} }
const products = division.products; if (division.products.size >= maxProducts) {
if (Object.keys(products).length >= maxProducts) {
throw new Error(`You are already at the max products (${maxProducts}) for division: ${division.name}!`); throw new Error(`You are already at the max products (${maxProducts}) for division: ${division.name}!`);
} }
const product = new Product({ const product = new Product({
name: productName.replace(/[<>]/g, "").trim(), //Sanitize for HTMl elements name: productName.replace(/[<>]/g, "").trim(), //Sanitize for HTMl elements
createCity: city, createCity: city,
designCost: designInvest, designInvestment: designInvest,
advCost: marketingInvest, advertisingInvestment: marketingInvest,
}); });
if (products[product.name]) { if (division.products.has(product.name)) {
throw new Error(`You already have a product with this name!`); throw new Error(`You already have a product with this name!`);
} }
corp.funds = corp.funds - (designInvest + marketingInvest); corp.funds = corp.funds - (designInvest + marketingInvest);
products[product.name] = product; division.products.set(product.name, product);
} }
export function Research(division: Industry, researchName: CorpResearchName): void { export function Research(division: Division, researchName: CorpResearchName): void {
const corp = Player.corporation;
if (!corp) return;
const researchTree = IndustryResearchTrees[division.type]; const researchTree = IndustryResearchTrees[division.type];
if (researchTree === undefined) throw new Error(`No research tree for industry '${division.type}'`); if (researchTree === undefined) throw new Error(`No research tree for industry '${division.type}'`);
const allResearch = researchTree.getAllNodes();
if (!allResearch.includes(researchName)) throw new Error(`No research named '${researchName}'`);
const research = ResearchMap[researchName]; const research = ResearchMap[researchName];
if (division.researched[researchName]) return; if (division.researched.has(researchName)) return;
if (division.sciResearch < research.cost) if (division.researchPoints < research.cost)
throw new Error(`You do not have enough Scientific Research for ${research.name}`); throw new Error(`You do not have enough Scientific Research for ${research.name}`);
division.sciResearch -= research.cost; division.researchPoints -= research.cost;
// Get the Node from the Research Tree and set its 'researched' property // Get the Node from the Research Tree and set its 'researched' property
researchTree.research(researchName); researchTree.research(researchName);
division.researched[researchName] = true; division.researched.add(researchName);
// I couldn't figure out where else to put this so that warehouse size would get updated instantly // I couldn't figure out where else to put this so that warehouse size would get updated instantly
// whether research is done by script or UI. All other stats gets calculated in every cycle // whether research is done by script or UI. All other stats gets calculated in every cycle
// Warehouse size gets updated only when something increases it. // Warehouse size gets updated only when something increases it.
if (researchName == "Drones - Transport") { if (researchName == "Drones - Transport") {
for (const city of Object.values(CityName)) { for (const warehouse of getRecordValues(division.warehouses)) {
const warehouse = division.warehouses[city]; warehouse.updateSize(corp, division);
if (!warehouse) continue;
if (Player.corporation) {
// Stores cycles in a "buffer". Processed separately using Engine Counters
warehouse.updateSize(Player.corporation, division);
}
} }
} }
} }
@ -503,7 +451,7 @@ export function ExportMaterial(
cityName: CityName, cityName: CityName,
material: Material, material: Material,
amt: string, amt: string,
division?: Industry, division?: Division,
): void { ): void {
// Sanitize amt // Sanitize amt
let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase(); let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase();
@ -529,36 +477,36 @@ export function ExportMaterial(
throw new Error(`You cannot export material: ${material.name} to division: ${divisionName}!`); throw new Error(`You cannot export material: ${material.name} to division: ${divisionName}!`);
} }
const exportObj = { ind: divisionName, city: cityName, amt: sanitizedAmt }; const exportObj = { division: divisionName, city: cityName, amount: sanitizedAmt };
material.exp.push(exportObj); material.exports.push(exportObj);
} }
export function CancelExportMaterial(divisionName: string, cityName: string, material: Material, amt: string): void { export function CancelExportMaterial(divisionName: string, cityName: string, material: Material, amt: string): void {
for (let i = 0; i < material.exp.length; ++i) { for (let i = 0; i < material.exports.length; ++i) {
if (material.exp[i].ind !== divisionName || material.exp[i].city !== cityName || material.exp[i].amt !== amt) if (
material.exports[i].division !== divisionName ||
material.exports[i].city !== cityName ||
material.exports[i].amount !== amt
)
continue; continue;
material.exp.splice(i, 1); material.exports.splice(i, 1);
break; break;
} }
} }
export function LimitProductProduction(product: Product, cityName: string, qty: number): void { export function LimitProductProduction(product: Product, cityName: CityName, quantity: number): void {
if (qty < 0 || isNaN(qty)) { if (quantity < 0 || isNaN(quantity)) {
product.prdman[cityName][0] = false; product.cityData[cityName].productionLimit = null;
product.prdman[cityName][1] = 0;
} else { } else {
product.prdman[cityName][0] = true; product.cityData[cityName].productionLimit = quantity;
product.prdman[cityName][1] = qty;
} }
} }
export function LimitMaterialProduction(material: Material, qty: number): void { export function LimitMaterialProduction(material: Material, quantity: number): void {
if (qty < 0 || isNaN(qty)) { if (quantity < 0 || isNaN(quantity)) {
material.prdman[0] = false; material.productionLimit = null;
material.prdman[1] = 0;
} else { } else {
material.prdman[0] = true; material.productionLimit = quantity;
material.prdman[1] = qty;
} }
} }

@ -1,8 +1,8 @@
import { CorporationState } from "./CorporationState"; import { CorporationState } from "./CorporationState";
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "./data/CorporationUnlockUpgrades"; import { CorpUnlocks } from "./data/CorporationUnlocks";
import { CorporationUpgrade, CorporationUpgrades } from "./data/CorporationUpgrades"; import { CorpUpgrades } from "./data/CorporationUpgrades";
import * as corpConstants from "./data/Constants"; import * as corpConstants from "./data/Constants";
import { Industry } from "./Industry"; import { Division } from "./Division";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { showLiterature } from "../Literature/LiteratureHelpers"; import { showLiterature } from "../Literature/LiteratureHelpers";
@ -11,9 +11,13 @@ import { Player } from "@player";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver"; import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { CityName } from "../Enums";
import { CorpStateName } from "@nsdefs"; import { CorpStateName } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers"; import { calculateUpgradeCost } from "./helpers";
import { JSONMap, JSONSet } from "../Types/Jsonable";
import { CorpUnlockName, CorpUpgradeName } from "./data/Enums";
import { formatMoney } from "../ui/formatNumber";
import { isPositiveInteger } from "../types";
import { createEnumKeyedRecord, getRecordValues } from "../Types/Record";
interface IParams { interface IParams {
name?: string; name?: string;
@ -23,8 +27,8 @@ interface IParams {
export class Corporation { export class Corporation {
name = "The Corporation"; name = "The Corporation";
//A division/business sector is represented by the object: /** Map keyed by division name */
divisions: Industry[] = []; divisions = new JSONMap<string, Division>();
maxDivisions = 20 * BitNodeMultipliers.CorporationDivisions; maxDivisions = 20 * BitNodeMultipliers.CorporationDivisions;
//Financial stats //Financial stats
@ -44,9 +48,12 @@ export class Corporation {
sharePrice = 0; sharePrice = 0;
storedCycles = 0; storedCycles = 0;
unlockUpgrades: number[]; unlocks = new JSONSet<CorpUnlockName>();
upgrades: number[]; upgrades = createEnumKeyedRecord(CorpUpgradeName, (name) => ({
upgradeMultipliers: number[]; level: 0,
// For dreamsense, value is not a multiplier so it starts at 0
value: name === CorpUpgradeName.DreamSense ? 0 : 1,
}));
cycleValuation = 0; cycleValuation = 0;
valuationsList = [0]; valuationsList = [0];
@ -58,11 +65,6 @@ export class Corporation {
constructor(params: IParams = {}) { constructor(params: IParams = {}) {
this.name = params.name ? params.name : "The Corporation"; this.name = params.name ? params.name : "The Corporation";
const numUnlockUpgrades = Object.keys(CorporationUnlockUpgrades).length;
const numUpgrades = Object.keys(CorporationUpgrades).length;
this.unlockUpgrades = Array(numUnlockUpgrades).fill(0);
this.upgrades = Array(numUpgrades).fill(0);
this.upgradeMultipliers = Array(numUpgrades).fill(1);
this.seedFunded = params.seedFunded ?? false; this.seedFunded = params.seedFunded ?? false;
} }
@ -93,9 +95,6 @@ export class Corporation {
this.divisions.forEach((ind) => { this.divisions.forEach((ind) => {
ind.resetImports(state); ind.resetImports(state);
});
this.divisions.forEach((ind) => {
ind.process(marketCycles, state, this); ind.process(marketCycles, state, this);
}); });
@ -134,8 +133,6 @@ export class Corporation {
this.funds = 150e9; this.funds = 150e9;
} }
// Process dividends
this.updateDividendTax();
if (this.dividendRate > 0 && cycleProfit > 0) { if (this.dividendRate > 0 && cycleProfit > 0) {
// Validate input again, just to be safe // Validate input again, just to be safe
if (isNaN(this.dividendRate) || this.dividendRate < 0 || this.dividendRate > corpConstants.dividendMaxRate) { if (isNaN(this.dividendRate) || this.dividendRate < 0 || this.dividendRate > corpConstants.dividendMaxRate) {
@ -157,16 +154,6 @@ export class Corporation {
} }
} }
updateDividendTax(): void {
this.dividendTax = 1 - BitNodeMultipliers.CorporationSoftcap + 0.15;
if (this.unlockUpgrades[5] === 1) {
this.dividendTax -= 0.05;
}
if (this.unlockUpgrades[6] === 1) {
this.dividendTax -= 0.1;
}
}
getCycleDividends(): number { getCycleDividends(): number {
const profit = this.revenue - this.expenses; const profit = this.revenue - this.expenses;
const cycleProfit = profit * corpConstants.secondsPerMarketCycle; const cycleProfit = profit * corpConstants.secondsPerMarketCycle;
@ -186,14 +173,14 @@ export class Corporation {
} }
val = this.funds + profit * 85e3; val = this.funds + profit * 85e3;
val *= Math.pow(1.1, this.divisions.length); val *= Math.pow(1.1, this.divisions.size);
val = Math.max(val, 0); val = Math.max(val, 0);
} else { } else {
val = 10e9 + Math.max(this.funds, 0) / 3; //Base valuation val = 10e9 + Math.max(this.funds, 0) / 3; //Base valuation
if (profit > 0) { if (profit > 0) {
val += profit * 315e3; val += profit * 315e3;
} }
val *= Math.pow(1.1, this.divisions.length); val *= Math.pow(1.1, this.divisions.size);
val -= val % 1e6; //Round down to nearest millionth val -= val % 1e6; //Round down to nearest millionth
} }
return val * BitNodeMultipliers.CorporationValuation; return val * BitNodeMultipliers.CorporationValuation;
@ -293,145 +280,81 @@ export class Corporation {
} }
} }
//One time upgrades that unlock new features /** Purchasing a one-time unlock
unlock(upgrade: CorporationUnlockUpgrade): void { * @returns A string on failure, indicating the reason for failure. */
const upgN = upgrade.index, purchaseUnlock(unlockName: CorpUnlockName): string | void {
price = upgrade.price; if (this.unlocks.has(unlockName)) return `The corporation has already unlocked ${unlockName}`;
while (this.unlockUpgrades.length <= upgN) { const price = CorpUnlocks[unlockName].price;
this.unlockUpgrades.push(0); if (this.funds < price) return `Insufficient funds to purchase ${unlockName}, requires ${formatMoney(price)}`;
} this.funds -= price;
if (this.funds < price) { this.unlocks.add(unlockName);
dialogBoxCreate("You don't have enough funds to unlock this!");
return;
}
this.unlockUpgrades[upgN] = 1;
this.funds = this.funds - price;
// Apply effects for one-time upgrades // Apply effects for one-time unlocks
this.updateDividendTax(); if (unlockName === CorpUnlockName.ShadyAccounting) this.dividendTax -= 0.05;
if (unlockName === CorpUnlockName.GovernmentPartnership) this.dividendTax -= 0.1;
} }
//Levelable upgrades /** Purchasing a levelable upgrade
upgrade(upgrade: CorporationUpgrade, amount: number): void { * @returns A string on failure, indicating the reason for failure. */
if (amount < 1) amount = 1; purchaseUpgrade(upgradeName: CorpUpgradeName, amount = 1): string | void {
const upgN = upgrade.index, if (!isPositiveInteger(amount)) {
upgradeAmt = upgrade.benefit; //Amount by which the upgrade multiplier gets increased (additive) return `Number of upgrade levels purchased must be a positive integer (attempted: ${amount}).`;
while (this.upgrades.length <= upgN) {
this.upgrades.push(0);
}
while (this.upgradeMultipliers.length <= upgN) {
this.upgradeMultipliers.push(1);
} }
const upgrade = CorpUpgrades[upgradeName];
const totalCost = calculateUpgradeCost(this, upgrade, amount); const totalCost = calculateUpgradeCost(this, upgrade, amount);
if (this.funds < totalCost) { if (this.funds < totalCost) return `Not enough funds to purchase ${amount} of upgrade ${upgradeName}.`;
dialogBoxCreate("You don't have enough funds to purchase this!"); this.funds -= totalCost;
return; this.upgrades[upgradeName].level += amount;
} this.upgrades[upgradeName].value += upgrade.benefit;
this.upgrades[upgN] += amount;
this.funds = this.funds - totalCost;
//Increase upgrade multiplier // Apply effects for upgrades
this.upgradeMultipliers[upgN] = 1 + this.upgrades[upgN] * upgradeAmt; if (upgradeName === CorpUpgradeName.SmartStorage) {
for (const division of this.divisions.values()) {
//If storage size is being updated, update values in Warehouse objects for (const warehouse of getRecordValues(division.warehouses)) {
if (upgN === 1) { warehouse.updateSize(this, division);
for (let i = 0; i < this.divisions.length; ++i) {
const industry = this.divisions[i];
for (const city of Object.keys(industry.warehouses) as CityName[]) {
const warehouse = industry.warehouses[city];
if (warehouse === 0) continue;
if (Object.hasOwn(industry.warehouses, city) && warehouse) {
warehouse.updateSize(this, industry);
}
} }
} }
} }
} }
getProductionMultiplier(): number { getProductionMultiplier(): number {
const mult = this.upgradeMultipliers[0]; return this.upgrades[CorpUpgradeName.SmartFactories].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getStorageMultiplier(): number { getStorageMultiplier(): number {
const mult = this.upgradeMultipliers[1]; return this.upgrades[CorpUpgradeName.SmartStorage].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getDreamSenseGain(): number { getDreamSenseGain(): number {
const gain = this.upgradeMultipliers[2] - 1; return this.upgrades[CorpUpgradeName.DreamSense].value;
return gain <= 0 ? 0 : gain;
} }
getAdvertisingMultiplier(): number { getAdvertisingMultiplier(): number {
const mult = this.upgradeMultipliers[3]; return this.upgrades[CorpUpgradeName.WilsonAnalytics].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getEmployeeCreMultiplier(): number { getEmployeeCreMultiplier(): number {
const mult = this.upgradeMultipliers[4]; return this.upgrades[CorpUpgradeName.NuoptimalNootropicInjectorImplants].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getEmployeeChaMultiplier(): number { getEmployeeChaMult(): number {
const mult = this.upgradeMultipliers[5]; return this.upgrades[CorpUpgradeName.SpeechProcessorImplants].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getEmployeeIntMultiplier(): number { getEmployeeIntMult(): number {
const mult = this.upgradeMultipliers[6]; return this.upgrades[CorpUpgradeName.NeuralAccelerators].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getEmployeeEffMultiplier(): number { getEmployeeEffMult(): number {
const mult = this.upgradeMultipliers[7]; return this.upgrades[CorpUpgradeName.FocusWires].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getSalesMultiplier(): number { getSalesMult(): number {
const mult = this.upgradeMultipliers[8]; return this.upgrades[CorpUpgradeName.ABCSalesBots].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
getScientificResearchMultiplier(): number { getScientificResearchMult(): number {
const mult = this.upgradeMultipliers[9]; return this.upgrades[CorpUpgradeName.ProjectInsight].value;
if (isNaN(mult) || mult < 1) {
return 1;
} else {
return mult;
}
} }
// Adds the Corporation Handbook (Starter Guide) to the player's home computer. // Adds the Corporation Handbook (Starter Guide) to the player's home computer.

@ -15,14 +15,7 @@ export class CorporationState {
// Transition to the next state // Transition to the next state
nextState(): void { nextState(): void {
if (this.state < 0 || this.state >= stateNames.length) { this.state = (this.state + 1) % stateNames.length;
this.state = 0;
}
++this.state;
if (this.state >= stateNames.length) {
this.state = 0;
}
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.

1111
src/Corporation/Division.ts Normal file

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
import { CityName } from "../Enums"; import { CityName } from "../Enums";
export interface Export { export interface Export {
ind: string; division: string;
city: CityName; city: CityName;
amt: string; amount: string;
} }

File diff suppressed because it is too large Load Diff

@ -1,12 +1,12 @@
import React from "react"; import React from "react";
import { ResearchTree } from "./ResearchTree";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
import { getBaseResearchTreeCopy, getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree"; import { getBaseResearchTreeCopy, getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree";
import { MoneyCost } from "./ui/MoneyCost"; import { MoneyCost } from "./ui/MoneyCost";
import { CorpIndustryData, CorpIndustryName } from "@nsdefs"; import { CorpIndustryData } from "@nsdefs";
import { IndustryType } from "./data/Enums"; import { IndustryType } from "./data/Enums";
import { createFullRecordFromEntries } from "../Types/Record";
export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = { export const IndustriesData: Record<IndustryType, CorpIndustryData> = {
[IndustryType.Agriculture]: { [IndustryType.Agriculture]: {
startingCost: 40e9, startingCost: 40e9,
description: "Cultivate crops and breed livestock to produce food.", description: "Cultivate crops and breed livestock to produce food.",
@ -315,9 +315,14 @@ export const IndustryDescriptions = (industry: IndustryType, corp: Corporation)
); );
}; };
export const IndustryResearchTrees = {} as Record<IndustryType, ResearchTree>; export const IndustryResearchTrees = createFullRecordFromEntries(
resetIndustryResearchTrees(); Object.values(IndustryType).map((industryType) => {
return [
industryType,
IndustriesData[industryType].product ? getProductIndustryResearchTreeCopy() : getBaseResearchTreeCopy(),
];
}),
);
export function resetIndustryResearchTrees() { export function resetIndustryResearchTrees() {
Object.values(IndustryType).forEach( Object.values(IndustryType).forEach(
(ind) => (ind) =>

@ -13,52 +13,51 @@ export class Material {
name: CorpMaterialName; name: CorpMaterialName;
// Amount of material owned // Amount of material owned
qty = 0; stored = 0;
// Material's "quality". Unbounded // Material's "quality". Unbounded
qlt = 1; quality = 1;
// How much demand the Material has in the market, and the range of possible // How much demand the Material has in the market, and the range of possible
// values for this "demand" // values for this "demand"
dmd = 0; demand = 0;
dmdR: number[] = [0, 0]; demandRange: number[] = [0, 0];
// How much competition there is for this Material in the market, and the range // How much competition there is for this Material in the market, and the range
// of possible values for this "competition" // of possible values for this "competition"
cmp = 0; competition = 0;
cmpR: number[] = [0, 0]; competitionRange: number[] = [0, 0];
// Maximum volatility of this Materials stats // Maximum volatility of this Materials stats
mv = 0; maxVolatility = 0;
// Markup. Determines how high of a price you can charge on the material // Markup. Determines how high of a price you can charge on the material
// compared to the market price without suffering loss in # of sales // compared to the market price without suffering loss in # of sales
// Quality is divided by this to determine markup limits // Quality is divided by this to determine markup limits
// e,g, If mku is 10 and quality is 100 then you can markup prices by 100/10 = 10 // e,g, If mku is 10 and quality is 100 then you can markup prices by 100/10 = 10
mku = 0; markup = 0;
// How much of this material is being bought, sold, imported and produced every second // How much of this material is being bought, sold, imported and produced every second
buy = 0; buyAmount = 0;
sll = 0; actualSellAmount = 0;
prd = 0; productionAmount = 0;
imp = 0; importAmount = 0;
// Exports of this material to another warehouse/industry // Exports of this material to another warehouse/industry
exp: Export[] = []; exports: Export[] = [];
// Total amount of this material exported in the last cycle // Total amount of this material exported in the last cycle
totalExp = 0; exportedLastCycle = 0;
// Cost / sec to buy this material. AKA Market Price // Cost / sec to buy this material. AKA Market Price
bCost = 0; marketPrice = 0;
// Cost / sec to sell this material /** null if there is no limit set on production. 0 actually limits production to 0. */
sCost: string | number = 0; productionLimit: number | null = null;
// Flags to keep track of whether production and/or sale of this material is limited // Player inputs for sell price and amount.
// [Whether production/sale is limited, limit amount] desiredSellAmount: string | number = 0;
prdman: [boolean, number] = [false, 0]; // Production desiredSellPrice: string | number = 0;
sllman: [boolean, string | number] = [false, 0]; // Sale
// Flags that signal whether automatic sale pricing through Market TA is enabled // Flags that signal whether automatic sale pricing through Market TA is enabled
marketTa1 = false; marketTa1 = false;
@ -66,62 +65,62 @@ export class Material {
marketTa2Price = 0; marketTa2Price = 0;
// Determines the maximum amount of this material that can be sold in one market cycle // Determines the maximum amount of this material that can be sold in one market cycle
maxsll = 0; maxSellPerCycle = 0;
constructor(params?: IConstructorParams) { constructor(params?: IConstructorParams) {
this.name = params?.name ?? materialNames[0]; this.name = params?.name ?? materialNames[0];
this.dmd = MaterialInfo[this.name].demandBase; this.demand = MaterialInfo[this.name].demandBase;
this.dmdR = MaterialInfo[this.name].demandRange; this.demandRange = MaterialInfo[this.name].demandRange;
this.cmp = MaterialInfo[this.name].competitionBase; this.competition = MaterialInfo[this.name].competitionBase;
this.cmpR = MaterialInfo[this.name].competitionRange; this.competitionRange = MaterialInfo[this.name].competitionRange;
this.bCost = MaterialInfo[this.name].baseCost; this.marketPrice = MaterialInfo[this.name].baseCost;
this.mv = MaterialInfo[this.name].maxVolatility; this.maxVolatility = MaterialInfo[this.name].maxVolatility;
this.mku = MaterialInfo[this.name].baseMarkup; this.markup = MaterialInfo[this.name].baseMarkup;
} }
getMarkupLimit(): number { getMarkupLimit(): number {
return this.qlt / this.mku; return this.quality / this.markup;
} }
// Process change in demand, competition, and buy cost of this material // Process change in demand, competition, and buy cost of this material
processMarket(): void { processMarket(): void {
// The price will change in accordance with demand and competition. // The price will change in accordance with demand and competition.
// e.g. If demand goes up, then so does price. If competition goes up, price goes down // e.g. If demand goes up, then so does price. If competition goes up, price goes down
const priceVolatility: number = (Math.random() * this.mv) / 300; const priceVolatility: number = (Math.random() * this.maxVolatility) / 300;
const priceChange: number = 1 + priceVolatility; const priceChange: number = 1 + priceVolatility;
//This 1st random check determines whether competition increases or decreases //This 1st random check determines whether competition increases or decreases
const compVolatility: number = (Math.random() * this.mv) / 100; const compVolatility: number = (Math.random() * this.maxVolatility) / 100;
const compChange: number = 1 + compVolatility; const compChange: number = 1 + compVolatility;
if (Math.random() < 0.5) { if (Math.random() < 0.5) {
this.cmp *= compChange; this.competition *= compChange;
if (this.cmp > this.cmpR[1]) { if (this.competition > this.competitionRange[1]) {
this.cmp = this.cmpR[1]; this.competition = this.competitionRange[1];
} }
this.bCost *= 1 / priceChange; // Competition increases, so price goes down this.marketPrice *= 1 / priceChange; // Competition increases, so price goes down
} else { } else {
this.cmp *= 1 / compChange; this.competition *= 1 / compChange;
if (this.cmp < this.cmpR[0]) { if (this.competition < this.competitionRange[0]) {
this.cmp = this.cmpR[0]; this.competition = this.competitionRange[0];
} }
this.bCost *= priceChange; // Competition decreases, so price goes up this.marketPrice *= priceChange; // Competition decreases, so price goes up
} }
// This 2nd random check determines whether demand increases or decreases // This 2nd random check determines whether demand increases or decreases
const dmdVolatility: number = (Math.random() * this.mv) / 100; const dmdVolatility: number = (Math.random() * this.maxVolatility) / 100;
const dmdChange: number = 1 + dmdVolatility; const dmdChange: number = 1 + dmdVolatility;
if (Math.random() < 0.5) { if (Math.random() < 0.5) {
this.dmd *= dmdChange; this.demand *= dmdChange;
if (this.dmd > this.dmdR[1]) { if (this.demand > this.demandRange[1]) {
this.dmd = this.dmdR[1]; this.demand = this.demandRange[1];
} }
this.bCost *= priceChange; // Demand increases, so price goes up this.marketPrice *= priceChange; // Demand increases, so price goes up
} else { } else {
this.dmd *= 1 / dmdChange; this.demand *= 1 / dmdChange;
if (this.dmd < this.dmdR[0]) { if (this.demand < this.demandRange[0]) {
this.dmd = this.dmdR[0]; this.demand = this.demandRange[0];
} }
this.bCost *= 1 / priceChange; this.marketPrice *= 1 / priceChange;
} }
} }

@ -1,33 +1,34 @@
import { EmployeePositions } from "./data/Enums"; import { CorpEmployeeJob } from "./data/Enums";
import * as corpConstants from "./data/Constants"; import * as corpConstants from "./data/Constants";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { Industry } from "./Industry"; import { Division } from "./Division";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CityName } from "../Enums"; import { CityName } from "../Enums";
import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record";
interface IParams { interface IParams {
loc?: CityName; city: CityName;
size?: number; size: number;
} }
export class OfficeSpace { export class OfficeSpace {
loc: CityName; city = CityName.Sector12;
size: number; size = 1;
maxEne = 100; maxEnergy = 100;
maxMor = 100; maxMorale = 100;
avgEne = 75; avgEnergy = 75;
avgMor = 75; avgMorale = 75;
avgInt = 75; avgIntelligence = 75;
avgCha = 75; avgCharisma = 75;
totalExp = 0; avgCreativity = 75;
avgCre = 75; avgEfficiency = 75;
avgEff = 75;
totalEmployees = 0; totalExperience = 0;
numEmployees = 0;
totalSalary = 0; totalSalary = 0;
autoTea = false; autoTea = false;
@ -35,73 +36,49 @@ export class OfficeSpace {
teaPending = false; teaPending = false;
partyMult = 1; partyMult = 1;
employeeProd: Record<EmployeePositions | "total", number> = { employeeProductionByJob = { total: 0, ...createEnumKeyedRecord(CorpEmployeeJob, () => 0) };
[EmployeePositions.Operations]: 0, employeeJobs = createEnumKeyedRecord(CorpEmployeeJob, () => 0);
[EmployeePositions.Engineer]: 0, employeeNextJobs = createEnumKeyedRecord(CorpEmployeeJob, () => 0);
[EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0,
[EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0,
total: 0,
};
employeeJobs: Record<EmployeePositions, number> = {
[EmployeePositions.Operations]: 0,
[EmployeePositions.Engineer]: 0,
[EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0,
[EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0,
};
employeeNextJobs: Record<EmployeePositions, number> = {
[EmployeePositions.Operations]: 0,
[EmployeePositions.Engineer]: 0,
[EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0,
[EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0,
};
constructor(params: IParams = {}) { constructor(params: IParams | null = null) {
this.loc = params.loc ? params.loc : CityName.Sector12; if (!params) return;
this.size = params.size ? params.size : 1; this.city = params.city;
this.size = params.size;
} }
atCapacity(): boolean { atCapacity(): boolean {
return this.totalEmployees >= this.size; return this.numEmployees >= this.size;
} }
process(marketCycles = 1, corporation: Corporation, industry: Industry): number { process(marketCycles = 1, corporation: Corporation, industry: Division): number {
// HRBuddy AutoRecruitment and Interning // HRBuddy AutoRecruitment and Interning
if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) { if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) {
this.hireRandomEmployee( this.hireRandomEmployee(
industry.hasResearch("HRBuddy-Training") ? EmployeePositions.Intern : EmployeePositions.Unassigned, industry.hasResearch("HRBuddy-Training") ? CorpEmployeeJob.Intern : CorpEmployeeJob.Unassigned,
); );
} }
// Update employee jobs and job counts // Update employee jobs and job counts
for (const [pos, jobCount] of Object.entries(this.employeeNextJobs) as [EmployeePositions, number][]) { for (const [pos, jobCount] of Object.entries(this.employeeNextJobs) as [CorpEmployeeJob, number][]) {
this.employeeJobs[pos] = jobCount; this.employeeJobs[pos] = jobCount;
} }
// Process Office properties // Process Office properties
this.maxEne = 100; this.maxEnergy = 100;
this.maxMor = 100; this.maxMorale = 100;
if (industry.hasResearch("Go-Juice")) this.maxEne += 10; if (industry.hasResearch("Go-Juice")) this.maxEnergy += 10;
if (industry.hasResearch("Sti.mu")) this.maxMor += 10; if (industry.hasResearch("Sti.mu")) this.maxMorale += 10;
if (industry.hasResearch("AutoBrew")) this.autoTea = true; if (industry.hasResearch("AutoBrew")) this.autoTea = true;
if (industry.hasResearch("AutoPartyManager")) this.autoParty = true; if (industry.hasResearch("AutoPartyManager")) this.autoParty = true;
if (this.totalEmployees > 0) { if (this.numEmployees > 0) {
/** Multiplier for employee morale/energy based on company performance */ /** Multiplier for employee morale/energy based on company performance */
let perfMult = 1.002; let perfMult = 1.002;
if (this.totalEmployees >= 9) { if (this.numEmployees >= 9) {
perfMult = Math.pow( perfMult = Math.pow(
1 + 1 +
0.002 * Math.min(1 / 9, this.employeeJobs.Intern / this.totalEmployees - 1 / 9) * 9 - 0.002 * Math.min(1 / 9, this.employeeJobs.Intern / this.numEmployees - 1 / 9) * 9 -
(corporation.funds < 0 && industry.lastCycleRevenue < industry.lastCycleExpenses ? 0.001 : 0), (corporation.funds < 0 && industry.lastCycleRevenue < industry.lastCycleExpenses ? 0.001 : 0),
marketCycles, marketCycles,
); );
@ -111,121 +88,125 @@ export class OfficeSpace {
const reduction = 0.002 * marketCycles; const reduction = 0.002 * marketCycles;
if (this.autoTea) { if (this.autoTea) {
this.avgEne = this.maxEne; this.avgEnergy = this.maxEnergy;
} else { } else {
// Tea gives a flat +2 to energy // Tea gives a flat +2 to energy
this.avgEne = (this.avgEne - reduction * Math.random()) * perfMult + (this.teaPending ? 2 : 0); this.avgEnergy = (this.avgEnergy - reduction * Math.random()) * perfMult + (this.teaPending ? 2 : 0);
} }
if (this.autoParty) { if (this.autoParty) {
this.avgMor = this.maxMor; this.avgMorale = this.maxMorale;
} else { } else {
// Each 5% multiplier gives an extra flat +1 to morale to make recovering from low morale easier. // Each 5% multiplier gives an extra flat +1 to morale to make recovering from low morale easier.
const increase = this.partyMult > 1 ? (this.partyMult - 1) * 10 : 0; const increase = this.partyMult > 1 ? (this.partyMult - 1) * 10 : 0;
this.avgMor = ((this.avgMor - reduction * Math.random()) * perfMult + increase) * this.partyMult; this.avgMorale = ((this.avgMorale - reduction * Math.random()) * perfMult + increase) * this.partyMult;
} }
this.avgEne = Math.max(Math.min(this.avgEne, this.maxEne), corpConstants.minEmployeeDecay); this.avgEnergy = Math.max(Math.min(this.avgEnergy, this.maxEnergy), corpConstants.minEmployeeDecay);
this.avgMor = Math.max(Math.min(this.avgMor, this.maxMor), corpConstants.minEmployeeDecay); this.avgMorale = Math.max(Math.min(this.avgMorale, this.maxMorale), corpConstants.minEmployeeDecay);
this.teaPending = false; this.teaPending = false;
this.partyMult = 1; this.partyMult = 1;
} }
// Get experience increase; unassigned employees do not contribute, interning employees contribute 10x // Get experience increase; unassigned employees do not contribute, interning employees contribute 10x
this.totalExp += this.totalExperience +=
0.0015 * 0.0015 *
marketCycles * marketCycles *
(this.totalEmployees - (this.numEmployees -
this.employeeJobs[EmployeePositions.Unassigned] + this.employeeJobs[CorpEmployeeJob.Unassigned] +
this.employeeJobs[EmployeePositions.Intern] * 9); this.employeeJobs[CorpEmployeeJob.Intern] * 9);
this.calculateEmployeeProductivity(corporation, industry); this.calculateEmployeeProductivity(corporation, industry);
if (this.totalEmployees === 0) { if (this.numEmployees === 0) {
this.totalSalary = 0; this.totalSalary = 0;
} else { } else {
this.totalSalary = this.totalSalary =
corpConstants.employeeSalaryMultiplier * corpConstants.employeeSalaryMultiplier *
marketCycles * marketCycles *
this.totalEmployees * this.numEmployees *
(this.avgInt + this.avgCha + this.totalExp / this.totalEmployees + this.avgCre + this.avgEff); (this.avgIntelligence +
this.avgCharisma +
this.totalExperience / this.numEmployees +
this.avgCreativity +
this.avgEfficiency);
} }
return this.totalSalary; return this.totalSalary;
} }
calculateEmployeeProductivity(corporation: Corporation, industry: Industry): void { calculateEmployeeProductivity(corporation: Corporation, industry: Division): void {
const effCre = this.avgCre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(), const effCre = this.avgCreativity * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(),
effCha = this.avgCha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(), effCha = this.avgCharisma * corporation.getEmployeeChaMult() * industry.getEmployeeChaMultiplier(),
effInt = this.avgInt * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(), effInt = this.avgIntelligence * corporation.getEmployeeIntMult() * industry.getEmployeeIntMultiplier(),
effEff = this.avgEff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier(); effEff = this.avgEfficiency * corporation.getEmployeeEffMult() * industry.getEmployeeEffMultiplier();
const prodBase = this.avgMor * this.avgEne * 1e-4; const prodBase = this.avgMorale * this.avgEnergy * 1e-4;
let total = 0; let total = 0;
const exp = this.totalExp / this.totalEmployees || 0; const exp = this.totalExperience / this.numEmployees || 0;
for (const name of Object.keys(this.employeeProd) as (EmployeePositions | "total")[]) { for (const name of getRecordKeys(this.employeeProductionByJob)) {
let prodMult = 0; let prodMult = 0;
switch (name) { switch (name) {
case EmployeePositions.Operations: case CorpEmployeeJob.Operations:
prodMult = 0.6 * effInt + 0.1 * effCha + exp + 0.5 * effCre + effEff; prodMult = 0.6 * effInt + 0.1 * effCha + exp + 0.5 * effCre + effEff;
break; break;
case EmployeePositions.Engineer: case CorpEmployeeJob.Engineer:
prodMult = effInt + 0.1 * effCha + 1.5 * exp + effEff; prodMult = effInt + 0.1 * effCha + 1.5 * exp + effEff;
break; break;
case EmployeePositions.Business: case CorpEmployeeJob.Business:
prodMult = 0.4 * effInt + effCha + 0.5 * exp; prodMult = 0.4 * effInt + effCha + 0.5 * exp;
break; break;
case EmployeePositions.Management: case CorpEmployeeJob.Management:
prodMult = 2 * effCha + exp + 0.2 * effCre + 0.7 * effEff; prodMult = 2 * effCha + exp + 0.2 * effCre + 0.7 * effEff;
break; break;
case EmployeePositions.RandD: case CorpEmployeeJob.RandD:
prodMult = 1.5 * effInt + 0.8 * exp + effCre + 0.5 * effEff; prodMult = 1.5 * effInt + 0.8 * exp + effCre + 0.5 * effEff;
break; break;
case EmployeePositions.Unassigned: case CorpEmployeeJob.Unassigned:
case EmployeePositions.Intern: case CorpEmployeeJob.Intern:
case "total": case "total":
continue; continue;
default: default:
console.error(`Invalid employee position: ${name}`); console.error(`Invalid employee position: ${name}`);
break; break;
} }
this.employeeProd[name] = this.employeeJobs[name] * prodMult * prodBase; this.employeeProductionByJob[name] = this.employeeJobs[name] * prodMult * prodBase;
total += this.employeeProd[name]; total += this.employeeProductionByJob[name];
} }
this.employeeProd.total = total; this.employeeProductionByJob.total = total;
} }
hireRandomEmployee(position: EmployeePositions): boolean { hireRandomEmployee(position: CorpEmployeeJob): boolean {
if (this.atCapacity()) return false; if (this.atCapacity()) return false;
if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) return false; if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) return false;
++this.totalEmployees; ++this.numEmployees;
++this.employeeJobs[position]; ++this.employeeJobs[position];
++this.employeeNextJobs[position]; ++this.employeeNextJobs[position];
this.totalExp += getRandomInt(50, 100); this.totalExperience += getRandomInt(50, 100);
this.avgMor = (this.avgMor * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgMorale = (this.avgMorale * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgEne = (this.avgEne * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgEnergy = (this.avgEnergy * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgInt = (this.avgInt * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgIntelligence = (this.avgIntelligence * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgCha = (this.avgCha * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgCharisma = (this.avgCharisma * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgCre = (this.avgCre * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgCreativity = (this.avgCreativity * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgEff = (this.avgEff * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgEfficiency = (this.avgEfficiency * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
return true; return true;
} }
autoAssignJob(job: EmployeePositions, target: number): boolean { autoAssignJob(job: CorpEmployeeJob, target: number): boolean {
if (job === EmployeePositions.Unassigned) { if (job === CorpEmployeeJob.Unassigned) {
throw new Error("internal autoAssignJob function called with EmployeePositions.Unassigned"); throw new Error("internal autoAssignJob function called with EmployeePositions.Unassigned");
} }
const diff = target - this.employeeNextJobs[job]; const diff = target - this.employeeNextJobs[job];
if (diff === 0) return true; if (diff === 0) return true;
// We are already at the desired number // We are already at the desired number
else if (diff <= this.employeeNextJobs[EmployeePositions.Unassigned]) { else if (diff <= this.employeeNextJobs[CorpEmployeeJob.Unassigned]) {
// This covers both a negative diff (reducing the amount of employees in position) and a positive (increasing and using up unassigned employees) // This covers both a negative diff (reducing the amount of employees in position) and a positive (increasing and using up unassigned employees)
this.employeeNextJobs[EmployeePositions.Unassigned] -= diff; this.employeeNextJobs[CorpEmployeeJob.Unassigned] -= diff;
this.employeeNextJobs[job] = target; this.employeeNextJobs[job] = target;
return true; return true;
} }
@ -233,11 +214,11 @@ export class OfficeSpace {
} }
getTeaCost(): number { getTeaCost(): number {
return corpConstants.teaCostPerEmployee * this.totalEmployees; return corpConstants.teaCostPerEmployee * this.numEmployees;
} }
setTea(): boolean { setTea(): boolean {
if (!this.teaPending && !this.autoTea && this.totalEmployees > 0) { if (!this.teaPending && !this.autoTea && this.numEmployees > 0) {
this.teaPending = true; this.teaPending = true;
return true; return true;
} }
@ -245,7 +226,7 @@ export class OfficeSpace {
} }
setParty(mult: number): boolean { setParty(mult: number): boolean {
if (mult > 1 && this.partyMult === 1 && !this.autoParty && this.totalEmployees > 0) { if (mult > 1 && this.partyMult === 1 && !this.autoParty && this.numEmployees > 0) {
this.partyMult = mult; this.partyMult = mult;
return true; return true;
} }
@ -262,10 +243,10 @@ export class OfficeSpace {
const empCopy: [{ data: { mor: number; ene: number; exp: number } }] = value.data.employees; const empCopy: [{ data: { mor: number; ene: number; exp: number } }] = value.data.employees;
delete value.data.employees; delete value.data.employees;
const ret = Generic_fromJSON(OfficeSpace, value.data); const ret = Generic_fromJSON(OfficeSpace, value.data);
ret.totalEmployees = empCopy.length; ret.numEmployees = empCopy.length;
ret.avgMor = empCopy.reduce((a, b) => a + b.data.mor, 0) / ret.totalEmployees || 75; ret.avgMorale = empCopy.reduce((a, b) => a + b.data.mor, 0) / ret.numEmployees || 75;
ret.avgEne = empCopy.reduce((a, b) => a + b.data.ene, 0) / ret.totalEmployees || 75; ret.avgEnergy = empCopy.reduce((a, b) => a + b.data.ene, 0) / ret.numEmployees || 75;
ret.totalExp = empCopy.reduce((a, b) => a + b.data.exp, 0); ret.totalExperience = empCopy.reduce((a, b) => a + b.data.exp, 0);
return ret; return ret;
} }
return Generic_fromJSON(OfficeSpace, value.data); return Generic_fromJSON(OfficeSpace, value.data);

@ -1,144 +1,119 @@
import { EmployeePositions } from "./data/Enums"; import { CorpEmployeeJob } from "./data/Enums";
import { MaterialInfo } from "./MaterialInfo"; import { MaterialInfo } from "./MaterialInfo";
import { Industry } from "./Industry"; import { Division } from "./Division";
import { IndustriesData } from "./IndustryData"; import { IndustriesData } from "./IndustryData";
import { createCityMap } from "../Locations/createCityMap";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CityName } from "../Enums"; import { CityName } from "../Enums";
import { materialNames } from "./data/Constants";
import { CorpMaterialName } from "@nsdefs"; import { CorpMaterialName } from "@nsdefs";
import { PartialRecord, createEnumKeyedRecord, getRecordEntries, getRecordKeys } from "../Types/Record";
interface IConstructorParams { interface IConstructorParams {
name?: string; name: string;
demand?: number; createCity: CityName;
competition?: number; designInvestment: number;
markup?: number; advertisingInvestment: number;
createCity?: CityName;
designCost?: number;
advCost?: number;
quality?: number;
performance?: number;
durability?: number;
reliability?: number;
aesthetics?: number;
features?: number;
loc?: string;
size?: number;
req?: Partial<Record<CorpMaterialName, number>>;
} }
/** A corporation product. Products are shared across the entire division, unlike materials which are per-warehouse */
export class Product { export class Product {
// Product name /** Name of the product */
name = ""; name = "DefaultProductName";
// The demand for this Product in the market. Gradually decreases /** Demand for this product, which goes down over time. */
dmd = 0; demand = 0;
// How much competition there is in the market for this Product /** Competition for this product */
cmp = 0; competition = 0;
// Markup. Affects how high of a price you can charge for this Product /** Markup. Affects how high of a price you can charge for this Product
// without suffering a loss in the # of sales without suffering a loss in the # of sales */
mku = 0; markup = 0;
// Production cost - estimation of how much money it costs to make this Product /** Cost of producing this product if buying its component materials at market price */
pCost = 0; productionCost = 0;
// Sell costs /** Whether the development for this product is finished yet */
sCost: Record<string, any> = createCityMap<any>(0); finished = false;
developmentProgress = 0; // Creation progress - A number between 0-100 representing percentage
// Variables for handling the creation process of this Product creationCity = CityName.Sector12; // City in which the product is/was being created
fin = false; // Whether this Product has finished being created designInvestment = 0; // How much money was invested into designing this Product
prog = 0; // Creation progress - A number between 0-100 representing percentage advertisingInvestment = 0; // How much money was invested into advertising this Product
createCity = CityName.Sector12; // City in which the product is/was being created
designCost = 0; // How much money was invested into designing this Product
advCost = 0; // How much money was invested into advertising this Product
// The average employee productivity and scientific research across the creation of the Product // The average employee productivity and scientific research across the creation of the Product
creationProd: Record<string, number> = { creationJobFactors = {
[EmployeePositions.Operations]: 0, [CorpEmployeeJob.Operations]: 0,
[EmployeePositions.Engineer]: 0, [CorpEmployeeJob.Engineer]: 0,
[EmployeePositions.Business]: 0, [CorpEmployeeJob.Business]: 0,
[EmployeePositions.Management]: 0, [CorpEmployeeJob.Management]: 0,
[EmployeePositions.RandD]: 0, [CorpEmployeeJob.RandD]: 0,
total: 0, total: 0,
}; };
// Aggregate score for this Product's 'rating' // Aggregate score for this Product's 'rating'
// This is based on the stats/properties below. The weighting of the // This is based on the stats/properties below. The weighting of the
// stats/properties below differs between different industries // stats/properties below differs between different industries
rat = 0; rating = 0;
// Stats/properties of this Product /** Stats of the product */
qlt = 0; stats = {
per = 0; quality: 0,
dur = 0; performance: 0,
rel = 0; durability: 0,
aes = 0; reliability: 0,
fea = 0; aesthetics: 0,
features: 0,
};
// Data refers to the production, sale, and quantity of the products // data that is stored per city
// These values are specific to a city cityData = createEnumKeyedRecord(CityName, () => ({
// For each city, the data is [qty, prod, sell, effRat] /** Amount of product stored in warehouse */
data: Record<string, number[]> = createCityMap<number[]>([0, 0, 0, 0]); stored: 0,
/** Amount of this product produced per cycle in this city */
productionAmount: 0,
/** Amount of this product that was sold last cycle in this city */
actualSellAmount: 0,
/** Total effective rating of the product in this city */
effectiveRating: 0,
/** Manual limit on production amount for the product in this city*/
productionLimit: null as number | null,
/** Player input sell amount e.g. "MAX" */
desiredSellAmount: 0 as number | string,
/** Player input sell price e.g. "MP * 5" */
desiredSellPrice: 0 as number | string,
}));
// Location of this Product /** How much warehouse space is occupied per unit of this product */
// Only applies for location-based products like restaurants/hospitals size = 0;
loc = "";
// How much space 1 unit of the Product takes (in the warehouse) /** Required materials per unit of this product */
// Not applicable for all Products requiredMaterials: PartialRecord<CorpMaterialName, number> = {};
siz = 0;
// Material requirements. An object that maps the name of a material to how much it requires
// to make 1 unit of the product.
reqMats: Partial<Record<CorpMaterialName, number>> = {};
// Data to keep track of whether production/sale of this Product is
// manually limited. These values are specific to a city
// [Whether production/sale is limited, limit amount]
prdman: Record<string, any[]> = createCityMap<any[]>([false, 0]);
sllman: Record<string, any[]> = createCityMap<any[]>([false, 0]);
// Flags that signal whether automatic sale pricing through Market TA is enabled // Flags that signal whether automatic sale pricing through Market TA is enabled
marketTa1 = false; marketTa1 = false;
marketTa2 = false; marketTa2 = false;
marketTa2Price: Record<string, number> = createCityMap<number>(0); marketTa2Price = createEnumKeyedRecord(CityName, () => 0);
// Determines the maximum amount of this product that can be sold in one market cycle /** Effective number that "MAX" represents in a sell amount */
maxsll = 0; maxSellAmount = 0;
constructor(params: IConstructorParams = {}) {
this.name = params.name ? params.name : ""; constructor(params: IConstructorParams | null = null) {
this.dmd = params.demand ? params.demand : 0; if (!params) return;
this.cmp = params.competition ? params.competition : 0; this.name = params.name;
this.mku = params.markup ? params.markup : 0; this.creationCity = params.createCity;
this.createCity = params.createCity ? params.createCity : CityName.Sector12; this.designInvestment = params.designInvestment;
this.designCost = params.designCost ? params.designCost : 0; this.advertisingInvestment = params.advertisingInvestment;
this.advCost = params.advCost ? params.advCost : 0;
this.qlt = params.quality ? params.quality : 0;
this.per = params.performance ? params.performance : 0;
this.dur = params.durability ? params.durability : 0;
this.rel = params.reliability ? params.reliability : 0;
this.aes = params.aesthetics ? params.aesthetics : 0;
this.fea = params.features ? params.features : 0;
this.loc = params.loc ? params.loc : "";
this.siz = params.size ? params.size : 0;
this.reqMats = params.req ? params.req : {};
} }
// Make progress on this product based on current employee productivity // Make progress on this product based on current employee productivity
createProduct(marketCycles: number, employeeProd: typeof this.creationProd): void { createProduct(marketCycles: number, employeeProd: typeof Product.prototype.creationJobFactors): void {
if (this.fin) { if (this.finished) return;
return;
}
// Designing/Creating a Product is based mostly off Engineers // Designing/Creating a Product is based mostly off Engineers
const opProd = employeeProd[EmployeePositions.Operations]; const opProd = employeeProd[CorpEmployeeJob.Operations];
const engrProd = employeeProd[EmployeePositions.Engineer]; const engrProd = employeeProd[CorpEmployeeJob.Engineer];
const mgmtProd = employeeProd[EmployeePositions.Management]; const mgmtProd = employeeProd[CorpEmployeeJob.Management];
const total = opProd + engrProd + mgmtProd; const total = opProd + engrProd + mgmtProd;
if (total <= 0) { if (total <= 0) {
return; return;
@ -147,119 +122,104 @@ export class Product {
// Management is a multiplier for the production from Engineers // Management is a multiplier for the production from Engineers
const mgmtFactor = 1 + mgmtProd / (1.2 * total); const mgmtFactor = 1 + mgmtProd / (1.2 * total);
const prodMult = (Math.pow(engrProd, 0.34) + Math.pow(opProd, 0.2)) * mgmtFactor; const prodMult = (Math.pow(engrProd, 0.34) + Math.pow(opProd, 0.2)) * mgmtFactor;
const progress = Math.min(marketCycles * 0.01 * prodMult, 100 - this.prog); const progress = Math.min(marketCycles * 0.01 * prodMult, 100 - this.developmentProgress);
if (progress <= 0) { if (progress <= 0) {
return; return;
} }
this.prog += progress; this.developmentProgress += progress;
for (const pos of Object.keys(employeeProd)) { for (const pos of getRecordKeys(employeeProd)) {
this.creationProd[pos] += (employeeProd[pos] * progress) / 100; this.creationJobFactors[pos] += (employeeProd[pos] * progress) / 100;
} }
} }
// @param industry - Industry object. Reference to industry that makes this Product // @param industry - Industry object. Reference to industry that makes this Product
finishProduct(industry: Industry): void { finishProduct(industry: Division): void {
this.fin = true; this.finished = true;
// Calculate properties // Calculate properties
const totalProd = this.creationProd.total; const totalProd = this.creationJobFactors.total;
const engrRatio = this.creationProd[EmployeePositions.Engineer] / totalProd; const engrRatio = this.creationJobFactors[CorpEmployeeJob.Engineer] / totalProd;
const mgmtRatio = this.creationProd[EmployeePositions.Management] / totalProd; const mgmtRatio = this.creationJobFactors[CorpEmployeeJob.Management] / totalProd;
const rndRatio = this.creationProd[EmployeePositions.RandD] / totalProd; const rndRatio = this.creationJobFactors[CorpEmployeeJob.RandD] / totalProd;
const opsRatio = this.creationProd[EmployeePositions.Operations] / totalProd; const opsRatio = this.creationJobFactors[CorpEmployeeJob.Operations] / totalProd;
const busRatio = this.creationProd[EmployeePositions.Business] / totalProd; const busRatio = this.creationJobFactors[CorpEmployeeJob.Business] / totalProd;
const designMult = 1 + Math.pow(this.designCost, 0.1) / 100; const designMult = 1 + Math.pow(this.designInvestment, 0.1) / 100;
const balanceMult = 1.2 * engrRatio + 0.9 * mgmtRatio + 1.3 * rndRatio + 1.5 * opsRatio + busRatio; const balanceMult = 1.2 * engrRatio + 0.9 * mgmtRatio + 1.3 * rndRatio + 1.5 * opsRatio + busRatio;
const sciMult = 1 + Math.pow(industry.sciResearch, industry.sciFac) / 800; const sciMult = 1 + Math.pow(industry.researchPoints, industry.researchFactor) / 800;
const totalMult = balanceMult * designMult * sciMult; const totalMult = balanceMult * designMult * sciMult;
this.qlt = this.stats.quality =
totalMult * totalMult *
(0.1 * this.creationProd[EmployeePositions.Engineer] + (0.1 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.05 * this.creationProd[EmployeePositions.Management] + 0.05 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.05 * this.creationProd[EmployeePositions.RandD] + 0.05 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.02 * this.creationProd[EmployeePositions.Operations] + 0.02 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.02 * this.creationProd[EmployeePositions.Business]); 0.02 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.per = this.stats.performance =
totalMult * totalMult *
(0.15 * this.creationProd[EmployeePositions.Engineer] + (0.15 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.02 * this.creationProd[EmployeePositions.Management] + 0.02 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.02 * this.creationProd[EmployeePositions.RandD] + 0.02 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.02 * this.creationProd[EmployeePositions.Operations] + 0.02 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.02 * this.creationProd[EmployeePositions.Business]); 0.02 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.dur = this.stats.durability =
totalMult * totalMult *
(0.05 * this.creationProd[EmployeePositions.Engineer] + (0.05 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.02 * this.creationProd[EmployeePositions.Management] + 0.02 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.08 * this.creationProd[EmployeePositions.RandD] + 0.08 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.05 * this.creationProd[EmployeePositions.Operations] + 0.05 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.05 * this.creationProd[EmployeePositions.Business]); 0.05 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.rel = this.stats.reliability =
totalMult * totalMult *
(0.02 * this.creationProd[EmployeePositions.Engineer] + (0.02 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.08 * this.creationProd[EmployeePositions.Management] + 0.08 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.02 * this.creationProd[EmployeePositions.RandD] + 0.02 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.05 * this.creationProd[EmployeePositions.Operations] + 0.05 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.08 * this.creationProd[EmployeePositions.Business]); 0.08 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.aes = this.stats.aesthetics =
totalMult * totalMult *
(0.0 * this.creationProd[EmployeePositions.Engineer] + (0.0 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.08 * this.creationProd[EmployeePositions.Management] + 0.08 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.05 * this.creationProd[EmployeePositions.RandD] + 0.05 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.02 * this.creationProd[EmployeePositions.Operations] + 0.02 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.1 * this.creationProd[EmployeePositions.Business]); 0.1 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.fea = this.stats.features =
totalMult * totalMult *
(0.08 * this.creationProd[EmployeePositions.Engineer] + (0.08 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.05 * this.creationProd[EmployeePositions.Management] + 0.05 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.02 * this.creationProd[EmployeePositions.RandD] + 0.02 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.05 * this.creationProd[EmployeePositions.Operations] + 0.05 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.05 * this.creationProd[EmployeePositions.Business]); 0.05 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.calculateRating(industry); this.calculateRating(industry);
const advMult = 1 + Math.pow(this.advCost, 0.1) / 100; const advMult = 1 + Math.pow(this.advertisingInvestment, 0.1) / 100;
const busmgtgRatio = Math.max(busRatio + mgmtRatio, 1 / totalProd); const busmgtgRatio = Math.max(busRatio + mgmtRatio, 1 / totalProd);
this.mku = 100 / (advMult * Math.pow(this.qlt + 0.001, 0.65) * busmgtgRatio); this.markup = 100 / (advMult * Math.pow(this.stats.quality + 0.001, 0.65) * busmgtgRatio);
// I actually don't understand well enough to know if this is right. // I actually don't understand well enough to know if this is right.
// I'm adding this to prevent a crash. // I'm adding this to prevent a crash.
if (this.mku === 0 || !isFinite(this.mku)) this.mku = 1; if (this.markup === 0 || !isFinite(this.markup)) this.markup = 1;
this.dmd = this.demand =
industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness))); industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));
this.cmp = getRandomInt(0, 70); this.competition = getRandomInt(0, 70);
//Calculate the product's required materials //Calculate the product's required materials and size
//For now, just set it to be the same as the requirements to make materials this.size = 0;
for (const matName of Object.keys(industry.reqMats) as CorpMaterialName[]) { for (const [matName, reqQty] of getRecordEntries(industry.requiredMaterials)) {
if (Object.hasOwn(industry.reqMats, matName)) { this.requiredMaterials[matName] = reqQty;
const reqMat = industry.reqMats[matName]; this.size += MaterialInfo[matName].size * reqQty;
if (reqMat === undefined) continue;
this.reqMats[matName] = reqMat;
} }
} }
//Calculate the product's size calculateRating(industry: Division): void {
//For now, just set it to be the same size as the requirements to make materials
this.siz = 0;
for (const matName of Object.values(materialNames)) {
const reqMat = industry.reqMats[matName];
if (reqMat === undefined) continue;
this.siz += MaterialInfo[matName].size * reqMat;
}
}
calculateRating(industry: Industry): void {
const weights = IndustriesData[industry.type].product?.ratingWeights; const weights = IndustriesData[industry.type].product?.ratingWeights;
if (!weights) return console.error(`Could not find product rating weights for: ${industry}`); if (!weights) return console.error(`Could not find product rating weights for: ${industry}`);
this.rat = 0; this.rating = getRecordEntries(weights).reduce(
this.rat += weights.quality ? this.qlt * weights.quality : 0; (total, [statName, weight]) => total + this.stats[statName] * weight,
this.rat += weights.performance ? this.per * weights.performance : 0; 0,
this.rat += weights.durability ? this.dur * weights.durability : 0; );
this.rat += weights.reliability ? this.rel * weights.reliability : 0;
this.rat += weights.aesthetics ? this.aes * weights.aesthetics : 0;
this.rat += weights.features ? this.fea * weights.features : 0;
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.
@ -269,18 +229,6 @@ export class Product {
// Initializes a Product object from a JSON save state. // Initializes a Product object from a JSON save state.
static fromJSON(value: IReviverValue): Product { static fromJSON(value: IReviverValue): Product {
// TODO: Remove all corp graceful loading measures during major corp rebalance / rework.
// For that version, Player.corporation will just get reset to null when loading from older version.
// Gracefully load saves from when RealEstate and AICores didn't have spaces
if (value.data.reqMats?.RealEstate) {
value.data.reqMats["Real Estate"] = value.data.reqMats.RealEstate;
delete value.data.reqMats.RealEstate;
}
if (value.data.reqMats?.AICores) {
value.data.reqMats["AI Cores"] = value.data.reqMats.AICores;
delete value.data.reqMats.AICores;
}
return Generic_fromJSON(Product, value.data); return Generic_fromJSON(Product, value.data);
} }
} }

@ -1,7 +1,6 @@
import { CorpResearchName } from "@nsdefs"; import { CorpResearchName } from "@nsdefs";
import { researchNames } from "./data/Constants";
export interface IConstructorParams { export interface ResearchParams {
name: CorpResearchName; name: CorpResearchName;
cost: number; cost: number;
desc: string; desc: string;
@ -19,13 +18,13 @@ export interface IConstructorParams {
export class Research { export class Research {
// Name of research. This will be used to identify researches in the Research Tree // Name of research. This will be used to identify researches in the Research Tree
name: CorpResearchName; name: CorpResearchName = "AutoBrew";
// How much scientific research it costs to unlock this // How much scientific research it costs to unlock this
cost = 0; cost = 0;
// Description of what the Research does // Description of what the Research does
desc = ""; description = "";
// All possible generic upgrades for the company, in the form of multipliers // All possible generic upgrades for the company, in the form of multipliers
advertisingMult = 1; advertisingMult = 1;
@ -39,39 +38,20 @@ export class Research {
sciResearchMult = 1; sciResearchMult = 1;
storageMult = 1; storageMult = 1;
constructor(p: IConstructorParams = { name: researchNames[0], cost: 0, desc: "" }) { constructor(p: ResearchParams | null = null) {
if (!p) return;
this.name = p.name; this.name = p.name;
this.cost = p.cost; this.cost = p.cost;
this.desc = p.desc; this.description = p.desc;
if (p.advertisingMult) { this.advertisingMult = p.advertisingMult ?? 1;
this.advertisingMult = p.advertisingMult; this.employeeChaMult = p.employeeChaMult ?? 1;
} this.employeeCreMult = p.employeeCreMult ?? 1;
if (p.employeeChaMult) { this.employeeEffMult = p.employeeEffMult ?? 1;
this.employeeChaMult = p.employeeChaMult; this.employeeIntMult = p.employeeIntMult ?? 1;
} this.productionMult = p.productionMult ?? 1;
if (p.employeeCreMult) { this.productProductionMult = p.productProductionMult ?? 1;
this.employeeCreMult = p.employeeCreMult; this.salesMult = p.salesMult ?? 1;
} this.sciResearchMult = p.sciResearchMult ?? 1;
if (p.employeeEffMult) { this.storageMult = p.storageMult ?? 1;
this.employeeEffMult = p.employeeEffMult;
}
if (p.employeeIntMult) {
this.employeeIntMult = p.employeeIntMult;
}
if (p.productionMult) {
this.productionMult = p.productionMult;
}
if (p.productProductionMult) {
this.productProductionMult = p.productProductionMult;
}
if (p.salesMult) {
this.salesMult = p.salesMult;
}
if (p.sciResearchMult) {
this.sciResearchMult = p.sciResearchMult;
}
if (p.storageMult) {
this.storageMult = p.storageMult;
}
} }
} }

@ -1,17 +1,189 @@
// The Research Map is an object that holds all Corporation Research objects import { Research } from "./Research";
// as values. They are identified by their names import { CorpResearchName } from "@nsdefs";
import { Research, IConstructorParams } from "./Research";
import { researchMetadata } from "./data/ResearchMetadata";
export const ResearchMap: Record<string, Research> = {}; // A full record ensures that every research name is present
/** A record for looking up a research object from the name */
export const ResearchMap: Record<CorpResearchName, Research> = {
AutoBrew: new Research({
name: "AutoBrew",
cost: 12e3,
desc:
"Automatically keep your employees fully caffeinated with " +
"tea injections. This research will keep the energy of all " +
"employees at its maximum possible value, for no cost. " +
"This will also disable the Tea upgrade.",
}),
AutoPartyManager: new Research({
name: "AutoPartyManager",
cost: 15e3,
desc:
"Automatically analyzes your employees' morale " +
"and boosts them whenever it detects a decrease. This research will " +
"keep the morale of all employees at their maximum possible " +
"values, for no cost. " +
"This will also disable the 'Throw Party' feature.",
}),
"Automatic Drug Administration": new Research({
name: "Automatic Drug Administration",
cost: 10e3,
desc:
"Research how to automatically administer performance-enhancing drugs to all of " +
"your employees. This unlocks Drug-related Research.",
}),
"CPH4 Injections": new Research({
name: "CPH4 Injections",
cost: 25e3,
desc:
"Develop an advanced and harmless synthetic drug that is administered to " +
"employees to increase all of their stats, except experience, by 10%.",
employeeCreMult: 1.1,
employeeChaMult: 1.1,
employeeEffMult: 1.1,
employeeIntMult: 1.1,
}),
Drones: new Research({
name: "Drones",
cost: 5e3,
desc:
"Acquire the knowledge needed to create advanced drones. This research does nothing " +
"by itself, but unlocks other Drone-related research.",
}),
"Drones - Assembly": new Research({
name: "Drones - Assembly",
cost: 25e3,
desc:
"Manufacture and use Assembly Drones to improve the efficiency of " +
"your production lines. This increases all production by 20%.",
productionMult: 1.2,
}),
"Drones - Transport": new Research({
name: "Drones - Transport",
cost: 30e3,
desc:
"Manufacture and use intelligent Transport Drones to optimize " +
"your warehouses. This increases the storage space of all warehouses " +
"by 50%.",
storageMult: 1.5,
}),
"Go-Juice": new Research({
name: "Go-Juice",
cost: 25e3,
desc:
"Provide employees with Go-Juice, a tea-derivative that further enhances " +
"the brain's dopamine production. This increases the maximum energy of all " +
"employees by 10.",
}),
"HRBuddy-Recruitment": new Research({
name: "HRBuddy-Recruitment",
cost: 15e3,
desc:
"Use automated software to handle the hiring of employees. With this " +
"research, each office will automatically hire one employee per " +
"market cycle if there is available space.",
}),
"HRBuddy-Training": new Research({
name: "HRBuddy-Training",
cost: 20e3,
desc:
"Use automated software to handle the training of employees. With this " +
"research, each employee hired with HRBuddy-Recruitment will automatically " +
"be assigned to 'Intern', rather than being unassigned.",
}),
"Hi-Tech R&D Laboratory": new Research({
name: "Hi-Tech R&D Laboratory",
cost: 5e3,
desc:
"Construct a cutting-edge facility dedicated to advanced research and " +
"development. This allows you to spend Scientific Research " +
"on powerful upgrades. It also globally increases Scientific Research " +
"production by 10%.",
sciResearchMult: 1.1,
}),
function addResearch(p: IConstructorParams): void { "Market-TA.I": new Research({
if (ResearchMap[p.name] != null) { name: "Market-TA.I",
console.warn(`Duplicate Research being defined: ${p.name}`); cost: 20e3,
} desc:
ResearchMap[p.name] = new Research(p); "Develop advanced AI software that uses technical analysis to " +
} "help you understand and exploit the market. This research " +
"allows you to know what price to sell your Materials/Products " +
for (const metadata of researchMetadata) { "at in order to avoid losing sales due to having too high of a mark-up. " +
addResearch(metadata); "It also lets you automatically use that sale price.",
} }),
"Market-TA.II": new Research({
name: "Market-TA.II",
cost: 50e3,
desc:
"Develop double-advanced AI software that uses technical analysis to " +
"help you understand and exploit the market. This research " +
"allows you to know how many sales of a Material/Product you lose or gain " +
"from having too high or too low of a sale price. It also lets you automatically " +
"set the sale price of your Materials/Products at the optimal price such that " +
"the amount sold matches the amount produced.",
}),
Overclock: new Research({
name: "Overclock",
cost: 15e3,
desc:
"Equip employees with a headset that uses transcranial direct current " +
"stimulation (tDCS) to increase the speed of their neurotransmitters. " +
"This research increases the intelligence and efficiency of all " +
"employees by 25%.",
employeeEffMult: 1.25,
employeeIntMult: 1.25,
}),
"Self-Correcting Assemblers": new Research({
name: "Self-Correcting Assemblers",
cost: 25e3,
desc:
"Create assemblers that can be used for universal production. " +
"These assemblers use deep learning to improve their efficiency " +
"at their tasks. This research increases all production by 10%.",
productionMult: 1.1,
}),
"Sti.mu": new Research({
name: "Sti.mu",
cost: 30e3,
desc:
"Upgrade the tDCS headset to stimulate regions of the brain that " +
"control confidence and enthusiasm. This research increases the maximum " +
"morale of all employees by 10.",
}),
"sudo.Assist": new Research({
name: "sudo.Assist",
cost: 15e3,
desc: "Develop a virtual assistant AI to handle and manage administrative issues for your corporation.",
}),
"uPgrade: Capacity.I": new Research({
name: "uPgrade: Capacity.I",
cost: 20e3,
desc:
"Expand the industry's capacity for designing and manufacturing its " +
"various products. This increases the industry's maximum number of products " +
"by 1 (from 3 to 4).",
}),
"uPgrade: Capacity.II": new Research({
name: "uPgrade: Capacity.II",
cost: 30e3,
desc:
"Expand the industry's capacity for designing and manufacturing its " +
"various products. This increases the industry's maximum number of products " +
"by 1 (from 4 to 5).",
}),
"uPgrade: Dashboard": new Research({
name: "uPgrade: Dashboard",
cost: 5e3,
desc:
"Improve the software used to manage the industry's production line " +
"for its various products. This allows you to manage the production and " +
"sale of a product before it's finished being designed.",
}),
"uPgrade: Fulcrum": new Research({
name: "uPgrade: Fulcrum",
cost: 10e3,
desc:
"Streamline the manufacturing of this industry's various products. " +
"This research increases the production of your products by 5%.",
productProductionMult: 1.05,
}),
};

@ -4,14 +4,13 @@
// not an actual Research object. The name can be used to obtain a reference // not an actual Research object. The name can be used to obtain a reference
// to the corresponding Research object using the ResearchMap // to the corresponding Research object using the ResearchMap
import { CorpResearchName } from "@nsdefs"; import { CorpResearchName } from "@nsdefs";
import { researchNames } from "./data/Constants";
import { Research } from "./Research"; import { Research } from "./Research";
import { ResearchMap } from "./ResearchMap"; import { ResearchMap } from "./ResearchMap";
interface IConstructorParams { interface IConstructorParams {
children?: Node[]; children?: Node[];
cost: number; cost: number;
text: CorpResearchName; researchName: CorpResearchName;
parent?: Node | null; parent?: Node | null;
} }
@ -34,14 +33,10 @@ export class Node {
parent: Node | null = null; parent: Node | null = null;
// Name of the Research held in this Node // Name of the Research held in this Node
text: CorpResearchName; researchName: CorpResearchName;
constructor(p: IConstructorParams = { cost: 0, text: researchNames[0] }) { constructor(p: IConstructorParams) {
if (ResearchMap[p.text] == null) { this.researchName = p.researchName;
throw new Error(`Invalid Research name used when constructing ResearchTree Node: ${p.text}`);
}
this.text = p.text;
this.cost = p.cost; this.cost = p.cost;
if (p.children && p.children.length > 0) { if (p.children && p.children.length > 0) {
@ -59,16 +54,16 @@ export class Node {
} }
// Recursive function for finding a Node with the specified text // Recursive function for finding a Node with the specified text
findNode(text: string): Node | null { findNode(name: CorpResearchName): Node | null {
// Is this the Node? // Is this the Node?
if (this.text === text) { if (this.researchName === name) {
return this; return this;
} }
// Recursively search children // Recursively search children
let res = null; let res = null;
for (let i = 0; i < this.children.length; ++i) { for (let i = 0; i < this.children.length; ++i) {
res = this.children[i].findNode(text); res = this.children[i].findNode(name);
if (res != null) { if (res != null) {
return res; return res;
} }
@ -86,7 +81,7 @@ export class Node {
// The root node in a Research Tree must always be the "Hi-Tech R&D Laboratory" // The root node in a Research Tree must always be the "Hi-Tech R&D Laboratory"
export class ResearchTree { export class ResearchTree {
// Object containing names of all acquired Research by name // Object containing names of all acquired Research by name
researched: Record<string, boolean> = {}; researched = new Set<CorpResearchName>();
// Root Node // Root Node
root: Node | null = null; root: Node | null = null;
@ -107,7 +102,7 @@ export class ResearchTree {
continue; continue;
} }
res.push(node.text); res.push(node.researchName);
for (let i = 0; i < node.children.length; ++i) { for (let i = 0; i < node.children.length; ++i) {
queue.push(node.children[i]); queue.push(node.children[i]);
} }
@ -175,11 +170,11 @@ export class ResearchTree {
continue; continue;
} }
const research: Research | null = ResearchMap[node.text]; const research: Research | null = ResearchMap[node.researchName];
// Safety checks // Safety checks
if (research == null) { if (research == null) {
console.warn(`Invalid Research name in node: ${node.text}`); console.warn(`Invalid Research name in node: ${node.researchName}`);
continue; continue;
} }
@ -197,7 +192,7 @@ export class ResearchTree {
storageMult: research.storageMult, storageMult: research.storageMult,
}[propName] ?? null; }[propName] ?? null;
if (mult == null) { if (mult === null) {
console.warn(`Invalid propName specified in ResearchTree.getMultiplierHelper: ${propName}`); console.warn(`Invalid propName specified in ResearchTree.getMultiplierHelper: ${propName}`);
continue; continue;
} }
@ -230,13 +225,11 @@ export class ResearchTree {
queue.push(this.root); queue.push(this.root);
while (queue.length !== 0) { while (queue.length !== 0) {
const node: Node | undefined = queue.shift(); const node: Node | undefined = queue.shift();
if (node == null) { if (!node) continue;
continue;
}
if (node.text === name) { if (node.researchName === name) {
node.researched = true; node.researched = true;
this.researched[name] = true; this.researched.add(name);
return; return;
} }

@ -1,18 +1,18 @@
import { Material } from "./Material"; import { Material } from "./Material";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
import { Industry } from "./Industry"; import { Division } from "./Division";
import { MaterialInfo } from "./MaterialInfo"; import { MaterialInfo } from "./MaterialInfo";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { CityName } from "../Enums"; import { CityName } from "../Enums";
import { CorpMaterialName } from "@nsdefs";
import { materialNames } from "./data/Constants"; import { materialNames } from "./data/Constants";
import { createFullRecordFromEntries, getRecordEntries } from "../Types/Record";
import { CorpUnlockName } from "./data/Enums";
import { Player } from "@player";
interface IConstructorParams { interface IConstructorParams {
corp?: Corporation; division: Division;
industry?: Industry; loc: CityName;
loc?: CityName; size: number;
size?: number;
} }
export class Warehouse { export class Warehouse {
@ -20,13 +20,13 @@ export class Warehouse {
level = 1; level = 1;
// City that this Warehouse is in // City that this Warehouse is in
loc: CityName; city = CityName.Sector12;
// Map of Materials held by this Warehouse // Map of Materials held by this Warehouse
materials: Record<CorpMaterialName, Material>; materials = createFullRecordFromEntries(materialNames.map((matName) => [matName, new Material({ name: matName })]));
// Maximum amount warehouse can hold // Maximum amount warehouse can hold
size: number; size = 0;
// Amount of space currently used by warehouse // Amount of space currently used by warehouse
sizeUsed = 0; sizeUsed = 0;
@ -35,30 +35,22 @@ export class Warehouse {
smartSupplyEnabled = false; smartSupplyEnabled = false;
// Decide if smart supply should use the amount of materials imported into account when deciding on the amount to buy. // Decide if smart supply should use the amount of materials imported into account when deciding on the amount to buy.
smartSupplyOptions: Record<CorpMaterialName, string>; smartSupplyOptions = createFullRecordFromEntries(materialNames.map((matName) => [matName, "leftovers"]));
// Stores the amount of product to be produced. Used for Smart Supply unlock. // Stores the amount of product to be produced. Used for Smart Supply unlock.
// The production tracked by smart supply is always based on the previous cycle, // The production tracked by smart supply is always based on the previous cycle,
// so it will always trail the "true" production by 1 cycle // so it will always trail the "true" production by 1 cycle
smartSupplyStore = 0; smartSupplyStore = 0;
constructor(params: IConstructorParams = {}) { constructor(params: IConstructorParams | null = null) {
this.loc = params.loc ? params.loc : CityName.Sector12; const corp = Player.corporation;
this.size = params.size ? params.size : 0; if (!corp || params === null) return;
this.city = params.loc;
this.materials = {} as Record<CorpMaterialName, Material>; this.size = params.size;
this.smartSupplyOptions = {} as Record<CorpMaterialName, string>; this.updateSize(corp, params.division);
for (const matName of materialNames) {
this.materials[matName] = new Material({ name: matName });
this.smartSupplyOptions[matName] = "leftovers";
}
if (params.corp && params.industry) {
this.updateSize(params.corp, params.industry);
}
// Default smart supply to being enabled if the upgrade is unlocked // Default smart supply to being enabled if the upgrade is unlocked
if (params.corp?.unlockUpgrades[1]) { if (corp.unlocks.has(CorpUnlockName.SmartSupply)) {
this.smartSupplyEnabled = true; this.smartSupplyEnabled = true;
} }
} }
@ -66,21 +58,16 @@ export class Warehouse {
// Re-calculate how much space is being used by this Warehouse // Re-calculate how much space is being used by this Warehouse
updateMaterialSizeUsed(): void { updateMaterialSizeUsed(): void {
this.sizeUsed = 0; this.sizeUsed = 0;
for (const matName of Object.values(materialNames)) { for (const [matName, mat] of getRecordEntries(this.materials)) {
const mat = this.materials[matName]; this.sizeUsed += mat.stored * MaterialInfo[matName].size;
this.sizeUsed += mat.qty * MaterialInfo[matName].size;
} }
if (this.sizeUsed > this.size) { if (this.sizeUsed > this.size) {
console.warn("Warehouse size used greater than capacity, something went wrong"); console.warn("Warehouse size used greater than capacity, something went wrong");
} }
} }
updateSize(corporation: Corporation, industry: Industry): void { updateSize(corporation: Corporation, division: Division): void {
try { this.size = this.level * 100 * corporation.getStorageMultiplier() * division.getStorageMultiplier();
this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier();
} catch (e: unknown) {
exceptionAlert(e);
}
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.
@ -90,19 +77,6 @@ export class Warehouse {
// Initializes a Warehouse object from a JSON save state. // Initializes a Warehouse object from a JSON save state.
static fromJSON(value: IReviverValue): Warehouse { static fromJSON(value: IReviverValue): Warehouse {
//Gracefully load saves where AICores and RealEstate material names sometimes did not use spaces
if (value.data?.materials?.AICores) {
value.data.materials["AI Cores"] = value.data.materials.AICores;
value.data.smartSupplyUseLeftovers["AI Cores"] = value.data.smartSupplyUseLeftovers.AICores;
delete value.data.materials.AICores;
delete value.data.smartSupplyUseLeftovers.AICores;
}
if (value.data?.materials?.RealEstate) {
value.data.materials["Real Estate"] = value.data.materials.RealEstate;
value.data.smartSupplyUseLeftovers["Real Estate"] = value.data.smartSupplyUseLeftovers.RealEstate;
delete value.data.materials.RealEstate;
delete value.data.smartSupplyUseLeftovers.RealEstate;
}
return Generic_fromJSON(Warehouse, value.data); return Generic_fromJSON(Warehouse, value.data);
} }
} }

@ -1,16 +1,13 @@
// Defines the ResearchTree that is common to all Corporation Industries // Defines the ResearchTree that is common to all Corporation Industries
// i.e. all Industries have these types of Research available to unlock // i.e. all Industries have these types of Research available to unlock
import { CorpResearchName } from "@nsdefs";
import { Research } from "../Research"; import { Research } from "../Research";
import { ResearchMap } from "../ResearchMap"; import { ResearchMap } from "../ResearchMap";
import { ResearchTree, Node } from "../ResearchTree"; import { ResearchTree, Node } from "../ResearchTree";
function makeNode(name: string): Node { function makeNode(name: CorpResearchName): Node {
const research: Research | null = ResearchMap[name]; const research: Research = ResearchMap[name];
if (research == null) { return new Node({ researchName: research.name, cost: research.cost });
throw new Error(`Invalid research name: ${name}`);
}
return new Node({ text: research.name, cost: research.cost });
} }
// Creates the Nodes for the BaseResearchTree. // Creates the Nodes for the BaseResearchTree.

@ -8,7 +8,8 @@ import {
CorpUpgradeName, CorpUpgradeName,
} from "@nsdefs"; } from "@nsdefs";
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IndustryType, EmployeePositions } from "./Enums"; import { IndustryType, CorpEmployeeJob } from "./Enums";
import { PositiveInteger } from "../../types";
// For typed strings, we need runtime objects to do API typechecking against. // For typed strings, we need runtime objects to do API typechecking against.
@ -18,7 +19,7 @@ import { IndustryType, EmployeePositions } from "./Enums";
export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "EXPORT", "SALE"], export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "EXPORT", "SALE"],
// TODO: remove IndustryType and EmployeePositions enums and just use the typed strings. // TODO: remove IndustryType and EmployeePositions enums and just use the typed strings.
/** Names of all corporation employee positions */ /** Names of all corporation employee positions */
employeePositions: CorpEmployeePosition[] = Object.values(EmployeePositions), employeePositions: CorpEmployeePosition[] = Object.values(CorpEmployeeJob),
/** Names of all industries. */ /** Names of all industries. */
industryNames: CorpIndustryName[] = Object.values(IndustryType), industryNames: CorpIndustryName[] = Object.values(IndustryType),
/** Names of all materials */ /** Names of all materials */
@ -66,7 +67,6 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
"AutoBrew", "AutoBrew",
"AutoPartyManager", "AutoPartyManager",
"Automatic Drug Administration", "Automatic Drug Administration",
"Bulk Purchasing",
"CPH4 Injections", "CPH4 Injections",
"Drones", "Drones",
"Drones - Assembly", "Drones - Assembly",
@ -123,19 +123,11 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
minEmployeeDecay = 10, minEmployeeDecay = 10,
/**smart supply ot */ /**smart supply ot */
smartSupplyUseOptions = ["leftovers", "imports", "none"], smartSupplyUseOptions = ["leftovers", "imports", "none"],
PurchaseMultipliers: { PurchaseMultipliers = {
[key: string]: number | "MAX" | undefined; x1: 1 as PositiveInteger,
x1: number; x5: 5 as PositiveInteger,
x5: number; x10: 10 as PositiveInteger,
x10: number; x50: 50 as PositiveInteger,
x50: number; x100: 100 as PositiveInteger,
x100: number; MAX: "MAX" as const,
MAX: "MAX";
} = {
x1: 1,
x5: 5,
x10: 10,
x50: 50,
x100: 100,
MAX: "MAX",
}; };

@ -1,100 +1,85 @@
export interface CorporationUnlockUpgrade { import { CorpUnlockName } from "./Enums";
index: number;
price: number;
name: string;
desc: string;
}
export enum CorporationUnlockUpgradeIndex { export interface CorpUnlock {
Export = 0, name: CorpUnlockName;
SmartSupply = 1, price: number;
MarketResearchDemand = 2, desc: string;
MarketDataCompetition = 3,
VeChain = 4,
ShadyAccounting = 5,
GovernmentPartnership = 6,
WarehouseAPI = 7,
OfficeAPI = 8,
} }
// Corporation Unlock Upgrades // Corporation Unlock Upgrades
// Upgrades for entire corporation, unlocks features, either you have it or you don't. // Upgrades for entire corporation, unlocks features, either you have it or you don't.
export const CorporationUnlockUpgrades: Record<CorporationUnlockUpgradeIndex, CorporationUnlockUpgrade> = { export const CorpUnlocks: Record<CorpUnlockName, CorpUnlock> = {
//Lets you export goods //Lets you export goods
[CorporationUnlockUpgradeIndex.Export]: { [CorpUnlockName.Export]: {
index: 0, name: CorpUnlockName.Export,
price: 20e9, price: 20e9,
name: "Export",
desc: desc:
"Develop infrastructure to export your materials to your other facilities. " + "Develop infrastructure to export your materials to your other facilities. " +
"This allows you to move materials around between different divisions and cities.", "This allows you to move materials around between different divisions and cities.",
}, },
//Lets you buy exactly however many required materials you need for production //Lets you buy exactly however many required materials you need for production
[CorporationUnlockUpgradeIndex.SmartSupply]: { [CorpUnlockName.SmartSupply]: {
index: 1, name: CorpUnlockName.SmartSupply,
price: 25e9, price: 25e9,
name: "Smart Supply",
desc: desc:
"Use advanced AI to anticipate your supply needs. " + "Use advanced AI to anticipate your supply needs. " +
"This allows you to purchase exactly however many materials you need for production.", "This allows you to purchase exactly however many materials you need for production.",
}, },
//Displays each material/product's demand //Displays each material/product's demand
[CorporationUnlockUpgradeIndex.MarketResearchDemand]: { [CorpUnlockName.MarketResearchDemand]: {
index: 2, name: CorpUnlockName.MarketResearchDemand,
price: 5e9, price: 5e9,
name: "Market Research - Demand",
desc: desc:
"Mine and analyze market data to determine the demand of all resources. " + "Mine and analyze market data to determine the demand of all resources. " +
"The demand attribute, which affects sales, will be displayed for every material and product.", "The demand attribute, which affects sales, will be displayed for every material and product.",
}, },
//Display's each material/product's competition //Display's each material/product's competition
[CorporationUnlockUpgradeIndex.MarketDataCompetition]: { [CorpUnlockName.MarketDataCompetition]: {
index: 3, name: CorpUnlockName.MarketDataCompetition,
price: 5e9, price: 5e9,
name: "Market Data - Competition",
desc: desc:
"Mine and analyze market data to determine how much competition there is on the market " + "Mine and analyze market data to determine how much competition there is on the market " +
"for all resources. The competition attribute, which affects sales, will be displayed for " + "for all resources. The competition attribute, which affects sales, will be displayed for " +
"every material and product.", "every material and product.",
}, },
[CorporationUnlockUpgradeIndex.VeChain]: {
index: 4, [CorpUnlockName.VeChain]: {
name: CorpUnlockName.VeChain,
price: 10e9, price: 10e9,
name: "VeChain",
desc: desc:
"Use AI and blockchain technology to identify where you can improve your supply chain systems. " + "Use AI and blockchain technology to identify where you can improve your supply chain systems. " +
"This upgrade will allow you to view a wide array of useful statistics about your " + "This upgrade will allow you to view a wide array of useful statistics about your " +
"Corporation.", "Corporation.",
}, },
[CorporationUnlockUpgradeIndex.ShadyAccounting]: {
index: 5, [CorpUnlockName.ShadyAccounting]: {
name: CorpUnlockName.ShadyAccounting,
price: 500e12, price: 500e12,
name: "Shady Accounting",
desc: desc:
"Utilize unscrupulous accounting practices and pay off government officials to save money " + "Utilize unscrupulous accounting practices and pay off government officials to save money " +
"on taxes. This reduces the dividend tax rate by 5%.", "on taxes. This reduces the dividend tax rate by 5%.",
}, },
[CorporationUnlockUpgradeIndex.GovernmentPartnership]: {
index: 6, [CorpUnlockName.GovernmentPartnership]: {
name: CorpUnlockName.GovernmentPartnership,
price: 2e15, price: 2e15,
name: "Government Partnership",
desc: desc:
"Help national governments further their agendas in exchange for lowered taxes. " + "Help national governments further their agendas in exchange for lowered taxes. " +
"This reduces the dividend tax rate by 10%", "This reduces the dividend tax rate by 10%",
}, },
[CorporationUnlockUpgradeIndex.WarehouseAPI]: {
index: 7, [CorpUnlockName.WarehouseAPI]: {
name: CorpUnlockName.WarehouseAPI,
price: 50e9, price: 50e9,
name: "Warehouse API",
desc: "Enables the warehouse API.", desc: "Enables the warehouse API.",
}, },
[CorporationUnlockUpgradeIndex.OfficeAPI]: {
index: 8, [CorpUnlockName.OfficeAPI]: {
name: CorpUnlockName.OfficeAPI,
price: 50e9, price: 50e9,
name: "Office API",
desc: "Enables the office API.", desc: "Enables the office API.",
}, },
}; };

@ -1,59 +1,43 @@
export interface CorporationUpgrade { import { CorpUpgradeName } from "./Enums";
index: number;
export interface CorpUpgrade {
name: CorpUpgradeName;
basePrice: number; basePrice: number;
priceMult: number; priceMult: number;
benefit: number; benefit: number;
name: string;
desc: string; desc: string;
} }
export enum CorporationUpgradeIndex { /** Levelable upgrades that affect the entire corporation */
SmartFactories = 0, export const CorpUpgrades: Record<CorpUpgradeName, CorpUpgrade> = {
SmartStorage = 1,
DreamSense = 2,
WilsonAnalytics = 3,
NuoptimalNootropicInjectorImplants = 4,
SpeechProcessorImplants = 5,
NeuralAccelerators = 6,
FocusWires = 7,
ABCSalesBots = 8,
ProjectInsight = 9,
}
// Corporation Upgrades
// Upgrades for entire corporation, levelable upgrades
export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpgrade> = {
//Smart factories, increases production //Smart factories, increases production
[CorporationUpgradeIndex.SmartFactories]: { [CorpUpgradeName.SmartFactories]: {
index: CorporationUpgradeIndex.SmartFactories, name: CorpUpgradeName.SmartFactories,
basePrice: 2e9, basePrice: 2e9,
priceMult: 1.06, priceMult: 1.06,
benefit: 0.03, benefit: 0.03,
name: "Smart Factories",
desc: desc:
"Advanced AI automatically optimizes the operation and productivity " + "Advanced AI automatically optimizes the operation and productivity " +
"of factories. Each level of this upgrade increases your global production by 3% (additive).", "of factories. Each level of this upgrade increases your global production by 3% (additive).",
}, },
//Smart warehouses, increases storage size //Smart warehouses, increases storage size
[CorporationUpgradeIndex.SmartStorage]: { [CorpUpgradeName.SmartStorage]: {
index: CorporationUpgradeIndex.SmartStorage, name: CorpUpgradeName.SmartStorage,
basePrice: 2e9, basePrice: 2e9,
priceMult: 1.06, priceMult: 1.06,
benefit: 0.1, benefit: 0.1,
name: "Smart Storage",
desc: desc:
"Advanced AI automatically optimizes your warehouse storage methods. " + "Advanced AI automatically optimizes your warehouse storage methods. " +
"Each level of this upgrade increases your global warehouse storage size by 10% (additive).", "Each level of this upgrade increases your global warehouse storage size by 10% (additive).",
}, },
//Advertise through dreams, passive popularity/ awareness gain //Advertise through dreams, passive popularity/ awareness gain
[CorporationUpgradeIndex.DreamSense]: { [CorpUpgradeName.DreamSense]: {
index: CorporationUpgradeIndex.DreamSense, name: CorpUpgradeName.DreamSense,
basePrice: 4e9, basePrice: 4e9,
priceMult: 1.1, priceMult: 1.1,
benefit: 0.001, benefit: 0.001,
name: "DreamSense",
desc: desc:
"Use DreamSense LCC Technologies to advertise your corporation " + "Use DreamSense LCC Technologies to advertise your corporation " +
"to consumers through their dreams. Each level of this upgrade provides a passive " + "to consumers through their dreams. Each level of this upgrade provides a passive " +
@ -63,12 +47,11 @@ export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpg
}, },
//Makes advertising more effective //Makes advertising more effective
[CorporationUpgradeIndex.WilsonAnalytics]: { [CorpUpgradeName.WilsonAnalytics]: {
index: CorporationUpgradeIndex.WilsonAnalytics, name: CorpUpgradeName.WilsonAnalytics,
basePrice: 4e9, basePrice: 4e9,
priceMult: 2, priceMult: 2,
benefit: 0.005, benefit: 0.005,
name: "Wilson Analytics",
desc: desc:
"Purchase data and analysis from Wilson, a marketing research " + "Purchase data and analysis from Wilson, a marketing research " +
"firm. Each level of this upgrade increases the effectiveness of your " + "firm. Each level of this upgrade increases the effectiveness of your " +
@ -76,12 +59,11 @@ export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpg
}, },
//Augmentation for employees, increases cre //Augmentation for employees, increases cre
[CorporationUpgradeIndex.NuoptimalNootropicInjectorImplants]: { [CorpUpgradeName.NuoptimalNootropicInjectorImplants]: {
index: CorporationUpgradeIndex.NuoptimalNootropicInjectorImplants, name: CorpUpgradeName.NuoptimalNootropicInjectorImplants,
basePrice: 1e9, basePrice: 1e9,
priceMult: 1.06, priceMult: 1.06,
benefit: 0.1, benefit: 0.1,
name: "Nuoptimal Nootropic Injector Implants",
desc: desc:
"Purchase the Nuoptimal Nootropic " + "Purchase the Nuoptimal Nootropic " +
"Injector augmentation for your employees. Each level of this upgrade " + "Injector augmentation for your employees. Each level of this upgrade " +
@ -89,24 +71,22 @@ export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpg
}, },
//Augmentation for employees, increases cha //Augmentation for employees, increases cha
[CorporationUpgradeIndex.SpeechProcessorImplants]: { [CorpUpgradeName.SpeechProcessorImplants]: {
index: CorporationUpgradeIndex.SpeechProcessorImplants, name: CorpUpgradeName.SpeechProcessorImplants,
basePrice: 1e9, basePrice: 1e9,
priceMult: 1.06, priceMult: 1.06,
benefit: 0.1, benefit: 0.1,
name: "Speech Processor Implants",
desc: desc:
"Purchase the Speech Processor augmentation for your employees. " + "Purchase the Speech Processor augmentation for your employees. " +
"Each level of this upgrade globally increases the charisma of your employees by 10% (additive).", "Each level of this upgrade globally increases the charisma of your employees by 10% (additive).",
}, },
//Augmentation for employees, increases int //Augmentation for employees, increases int
[CorporationUpgradeIndex.NeuralAccelerators]: { [CorpUpgradeName.NeuralAccelerators]: {
index: CorporationUpgradeIndex.NeuralAccelerators, name: CorpUpgradeName.NeuralAccelerators,
basePrice: 1e9, basePrice: 1e9,
priceMult: 1.06, priceMult: 1.06,
benefit: 0.1, benefit: 0.1,
name: "Neural Accelerators",
desc: desc:
"Purchase the Neural Accelerator augmentation for your employees. " + "Purchase the Neural Accelerator augmentation for your employees. " +
"Each level of this upgrade globally increases the intelligence of your employees " + "Each level of this upgrade globally increases the intelligence of your employees " +
@ -114,24 +94,22 @@ export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpg
}, },
//Augmentation for employees, increases eff //Augmentation for employees, increases eff
[CorporationUpgradeIndex.FocusWires]: { [CorpUpgradeName.FocusWires]: {
index: CorporationUpgradeIndex.FocusWires, name: CorpUpgradeName.FocusWires,
basePrice: 1e9, basePrice: 1e9,
priceMult: 1.06, priceMult: 1.06,
benefit: 0.1, benefit: 0.1,
name: "FocusWires",
desc: desc:
"Purchase the FocusWire augmentation for your employees. Each level " + "Purchase the FocusWire augmentation for your employees. Each level " +
"of this upgrade globally increases the efficiency of your employees by 10% (additive).", "of this upgrade globally increases the efficiency of your employees by 10% (additive).",
}, },
//Improves sales of materials/products //Improves sales of materials/products
[CorporationUpgradeIndex.ABCSalesBots]: { [CorpUpgradeName.ABCSalesBots]: {
index: CorporationUpgradeIndex.ABCSalesBots, name: CorpUpgradeName.ABCSalesBots,
basePrice: 1e9, basePrice: 1e9,
priceMult: 1.07, priceMult: 1.07,
benefit: 0.01, benefit: 0.01,
name: "ABC SalesBots",
desc: desc:
"Always Be Closing. Purchase these robotic salesmen to increase the amount of " + "Always Be Closing. Purchase these robotic salesmen to increase the amount of " +
"materials and products you sell. Each level of this upgrade globally increases your sales " + "materials and products you sell. Each level of this upgrade globally increases your sales " +
@ -139,12 +117,11 @@ export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpg
}, },
//Improves scientific research rate //Improves scientific research rate
[CorporationUpgradeIndex.ProjectInsight]: { [CorpUpgradeName.ProjectInsight]: {
index: CorporationUpgradeIndex.ProjectInsight, name: CorpUpgradeName.ProjectInsight,
basePrice: 5e9, basePrice: 5e9,
priceMult: 1.07, priceMult: 1.07,
benefit: 0.05, benefit: 0.05,
name: "Project Insight",
desc: desc:
"Purchase 'Project Insight', a R&D service provided by the secretive " + "Purchase 'Project Insight', a R&D service provided by the secretive " +
"Fulcrum Technologies. Each level of this upgrade globally increases the amount of " + "Fulcrum Technologies. Each level of this upgrade globally increases the amount of " +

@ -16,7 +16,7 @@ export enum IndustryType {
RealEstate = "Real Estate", RealEstate = "Real Estate",
} }
export enum EmployeePositions { export enum CorpEmployeeJob {
Operations = "Operations", Operations = "Operations",
Engineer = "Engineer", Engineer = "Engineer",
Business = "Business", Business = "Business",
@ -25,3 +25,28 @@ export enum EmployeePositions {
Intern = "Intern", Intern = "Intern",
Unassigned = "Unassigned", Unassigned = "Unassigned",
} }
export enum CorpUnlockName {
Export = "Export",
SmartSupply = "Smart Supply",
MarketResearchDemand = "Market Research - Demand",
MarketDataCompetition = "Market Data - Competition",
VeChain = "VeChain",
ShadyAccounting = "Shady Accounting",
GovernmentPartnership = "Government Partnership",
WarehouseAPI = "Warehouse API",
OfficeAPI = "Office API",
}
export enum CorpUpgradeName {
SmartFactories = "Smart Factories",
SmartStorage = "Smart Storage",
DreamSense = "DreamSense",
WilsonAnalytics = "Wilson Analytics",
NuoptimalNootropicInjectorImplants = "Nuoptimal Nootropic Injector Implants",
SpeechProcessorImplants = "Speech Processor Implants",
NeuralAccelerators = "Neural Accelerators",
FocusWires = "FocusWires",
ABCSalesBots = "ABC SalesBots",
ProjectInsight = "Project Insight",
}

@ -1,185 +0,0 @@
import { IConstructorParams } from "../Research";
export const researchMetadata: IConstructorParams[] = [
{
name: "AutoBrew",
cost: 12e3,
desc:
"Automatically keep your employees fully caffeinated with " +
"tea injections. This research will keep the energy of all " +
"employees at its maximum possible value, for no cost. " +
"This will also disable the Tea upgrade.",
},
{
name: "AutoPartyManager",
cost: 15e3,
desc:
"Automatically analyzes your employees' morale " +
"and boosts them whenever it detects a decrease. This research will " +
"keep the morale of all employees at their maximum possible " +
"values, for no cost. " +
"This will also disable the 'Throw Party' feature.",
},
{
name: "Automatic Drug Administration",
cost: 10e3,
desc:
"Research how to automatically administer performance-enhancing drugs to all of " +
"your employees. This unlocks Drug-related Research.",
},
{
name: "CPH4 Injections",
cost: 25e3,
desc:
"Develop an advanced and harmless synthetic drug that is administered to " +
"employees to increase all of their stats, except experience, by 10%.",
employeeCreMult: 1.1,
employeeChaMult: 1.1,
employeeEffMult: 1.1,
employeeIntMult: 1.1,
},
{
name: "Drones",
cost: 5e3,
desc:
"Acquire the knowledge needed to create advanced drones. This research does nothing " +
"by itself, but unlocks other Drone-related research.",
},
{
name: "Drones - Assembly",
cost: 25e3,
desc:
"Manufacture and use Assembly Drones to improve the efficiency of " +
"your production lines. This increases all production by 20%.",
productionMult: 1.2,
},
{
name: "Drones - Transport",
cost: 30e3,
desc:
"Manufacture and use intelligent Transport Drones to optimize " +
"your warehouses. This increases the storage space of all warehouses " +
"by 50%.",
storageMult: 1.5,
},
{
name: "Go-Juice",
cost: 25e3,
desc:
"Provide employees with Go-Juice, a tea-derivative that further enhances " +
"the brain's dopamine production. This increases the maximum energy of all " +
"employees by 10.",
},
{
name: "Hi-Tech R&D Laboratory",
cost: 5e3,
desc:
"Construct a cutting-edge facility dedicated to advanced research and " +
"development. This allows you to spend Scientific Research " +
"on powerful upgrades. It also globally increases Scientific Research " +
"production by 10%.",
sciResearchMult: 1.1,
},
{
name: "HRBuddy-Recruitment",
cost: 15e3,
desc:
"Use automated software to handle the hiring of employees. With this " +
"research, each office will automatically hire one employee per " +
"market cycle if there is available space.",
},
{
name: "HRBuddy-Training",
cost: 20e3,
desc:
"Use automated software to handle the training of employees. With this " +
"research, each employee hired with HRBuddy-Recruitment will automatically " +
"be assigned to 'Intern', rather than being unassigned.",
},
{
name: "Market-TA.I",
cost: 20e3,
desc:
"Develop advanced AI software that uses technical analysis to " +
"help you understand and exploit the market. This research " +
"allows you to know what price to sell your Materials/Products " +
"at in order to avoid losing sales due to having too high of a mark-up. " +
"It also lets you automatically use that sale price.",
},
{
name: "Market-TA.II",
cost: 50e3,
desc:
"Develop double-advanced AI software that uses technical analysis to " +
"help you understand and exploit the market. This research " +
"allows you to know how many sales of a Material/Product you lose or gain " +
"from having too high or too low of a sale price. It also lets you automatically " +
"set the sale price of your Materials/Products at the optimal price such that " +
"the amount sold matches the amount produced.",
},
{
name: "Overclock",
cost: 15e3,
desc:
"Equip employees with a headset that uses transcranial direct current " +
"stimulation (tDCS) to increase the speed of their neurotransmitters. " +
"This research increases the intelligence and efficiency of all " +
"employees by 25%.",
employeeEffMult: 1.25,
employeeIntMult: 1.25,
},
{
name: "Self-Correcting Assemblers",
cost: 25e3,
desc:
"Create assemblers that can be used for universal production. " +
"These assemblers use deep learning to improve their efficiency " +
"at their tasks. This research increases all production by 10%.",
productionMult: 1.1,
},
{
name: "Sti.mu",
cost: 30e3,
desc:
"Upgrade the tDCS headset to stimulate regions of the brain that " +
"control confidence and enthusiasm. This research increases the maximum " +
"morale of all employees by 10.",
},
{
name: "sudo.Assist",
cost: 15e3,
desc: "Develop a virtual assistant AI to handle and manage administrative issues for your corporation.",
},
{
name: "uPgrade: Capacity.I",
cost: 20e3,
desc:
"Expand the industry's capacity for designing and manufacturing its " +
"various products. This increases the industry's maximum number of products " +
"by 1 (from 3 to 4).",
},
{
name: "uPgrade: Capacity.II",
cost: 30e3,
desc:
"Expand the industry's capacity for designing and manufacturing its " +
"various products. This increases the industry's maximum number of products " +
"by 1 (from 4 to 5).",
},
{
name: "uPgrade: Dashboard",
cost: 5e3,
desc:
"Improve the software used to manage the industry's production line " +
"for its various products. This allows you to manage the production and " +
"sale of a product before it's finished being designed.",
},
{
name: "uPgrade: Fulcrum",
cost: 10e3,
desc:
"Streamline the manufacturing of this industry's various products. " +
"This research increases the production of your products by 5%.",
productProductionMult: 1.05,
},
];

@ -1,35 +1,32 @@
import { PositiveInteger } from "../types";
import { Corporation } from "./Corporation"; import { Corporation } from "./Corporation";
import { CorporationUpgrade } from "./data/CorporationUpgrades"; import { CorpUpgrade } from "./data/CorporationUpgrades";
export function calculateUpgradeCost(corporation: Corporation, upgrade: CorporationUpgrade, amount: number): number { export function calculateUpgradeCost(corporation: Corporation, upgrade: CorpUpgrade, amount: PositiveInteger): number {
if (amount < 1) return 0;
const priceMult = upgrade.priceMult; const priceMult = upgrade.priceMult;
const level = corporation.upgrades[upgrade.index]; const level = corporation.upgrades[upgrade.name].level;
const baseCost = upgrade.basePrice * Math.pow(priceMult, level); const baseCost = upgrade.basePrice * Math.pow(priceMult, level);
const cost = (baseCost * (1 - Math.pow(priceMult, amount))) / (1 - priceMult); const cost = (baseCost * (1 - Math.pow(priceMult, amount))) / (1 - priceMult);
return cost; return cost;
} }
// There ought to be a more clever mathematical solution for this
export function calculateMaxAffordableUpgrade( export function calculateMaxAffordableUpgrade(
corporation: Corporation, corp: Corporation,
upgrade: CorporationUpgrade, upgrade: CorpUpgrade,
amount: number | "MAX", amount: PositiveInteger | "MAX",
): number { ): 0 | PositiveInteger {
if (amount != "MAX") { if (amount !== "MAX" && calculateUpgradeCost(corp, upgrade, amount) < corp.funds) return amount;
if (amount === 0) return 0; if (calculateUpgradeCost(corp, upgrade, 1 as PositiveInteger) > corp.funds) return 0;
if (calculateUpgradeCost(corporation, upgrade, amount) < corporation.funds) return amount; // We can definitely afford 1 of the upgrade
}
let n = 1; let n = 1;
while ( // Multiply by 2 until we can't afford it anymore
calculateUpgradeCost(corporation, upgrade, n * 2) < corporation.funds && while (calculateUpgradeCost(corp, upgrade, (n * 2) as PositiveInteger) <= corp.funds) n *= 2;
(amount != "MAX" ? n < amount : true) let tooHigh = n * 2;
) { while (tooHigh - n > 1) {
n *= 2; const nextCheck = (Math.ceil((tooHigh - n) / 2) + n) as PositiveInteger;
if (calculateUpgradeCost(corp, upgrade, nextCheck) <= corp.funds) n = nextCheck;
else tooHigh = nextCheck;
} }
for (let i = n / 2; i >= 1; i /= 2) { return n as PositiveInteger;
if (calculateUpgradeCost(corporation, upgrade, n + i) < corporation.funds) n += i;
}
return amount === "MAX" ? n : Math.min(n, amount);
} }

@ -8,6 +8,7 @@ import { useDivision } from "./Context";
import Tabs from "@mui/material/Tabs"; import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab"; import Tab from "@mui/material/Tab";
import { CityName } from "../../Enums"; import { CityName } from "../../Enums";
import { getRecordKeys } from "../../Types/Record";
interface IProps { interface IProps {
city: CityName | "Expand"; city: CityName | "Expand";
@ -23,7 +24,7 @@ export function CityTabs(props: IProps): React.ReactElement {
mainContent = <ExpandNewCity cityStateSetter={setCity} />; mainContent = <ExpandNewCity cityStateSetter={setCity} />;
} else { } else {
const office = division.offices[city]; const office = division.offices[city];
if (office === 0) { if (!office) {
setCity(CityName.Sector12); setCity(CityName.Sector12);
return <></>; return <></>;
} }
@ -31,7 +32,7 @@ export function CityTabs(props: IProps): React.ReactElement {
<Industry rerender={props.rerender} city={city} warehouse={division.warehouses[city]} office={office} /> <Industry rerender={props.rerender} city={city} warehouse={division.warehouses[city]} office={office} />
); );
} }
const canExpand = Object.values(CityName).filter((cityName) => division.offices[cityName] === 0).length > 0; const canExpand = Object.values(CityName).length > getRecordKeys(division.offices).length;
function handleChange(event: React.SyntheticEvent, tab: CityName | "Expand"): void { function handleChange(event: React.SyntheticEvent, tab: CityName | "Expand"): void {
setCity(tab); setCity(tab);
} }
@ -40,7 +41,8 @@ export function CityTabs(props: IProps): React.ReactElement {
<> <>
<Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: "65vw" }}> <Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: "65vw" }}>
{Object.values(division.offices).map( {Object.values(division.offices).map(
(office: OfficeSpace | 0) => office !== 0 && <Tab key={office.loc} label={office.loc} value={office.loc} />, (office: OfficeSpace | 0) =>
office !== 0 && <Tab key={office.city} label={office.city} value={office.city} />,
)} )}
{canExpand && <Tab label={"Expand"} value={"Expand"} />} {canExpand && <Tab label={"Expand"} value={"Expand"} />}
</Tabs> </Tabs>

@ -1,11 +1,11 @@
import React, { useContext } from "react"; import React, { useContext } from "react";
import { Corporation } from "../Corporation"; import { Corporation } from "../Corporation";
import { Industry } from "../Industry"; import { Division } from "../Division";
export const Context = { export const Context = {
Corporation: React.createContext<Corporation>({} as Corporation), Corporation: React.createContext<Corporation>({} as Corporation),
Division: React.createContext<Industry>({} as Industry), Division: React.createContext<Division>({} as Division),
}; };
export const useCorporation = (): Corporation => useContext(Context.Corporation); export const useCorporation = (): Corporation => useContext(Context.Corporation);
export const useDivision = (): Industry => useContext(Context.Division); export const useDivision = (): Division => useContext(Context.Division);

@ -3,7 +3,6 @@
// divisions, see an overview of your corporation, or create a new industry // divisions, see an overview of your corporation, or create a new industry
import React, { useState } from "react"; import React, { useState } from "react";
import { MainPanel } from "./MainPanel"; import { MainPanel } from "./MainPanel";
import { IndustryType } from "../data/Enums";
import { ExpandIndustryTab } from "./ExpandIndustryTab"; import { ExpandIndustryTab } from "./ExpandIndustryTab";
import { Player } from "@player"; import { Player } from "@player";
import { Context } from "./Context"; import { Context } from "./Context";
@ -22,16 +21,13 @@ export function CorporationRoot(): React.ReactElement {
setDivisionName(tab); setDivisionName(tab);
} }
const canExpand = const canExpand = corporation.divisions.size < corporation.maxDivisions;
Object.values(IndustryType).filter(
(industryType) => corporation.divisions.find((division) => division.type === industryType) === undefined,
).length > 0;
return ( return (
<Context.Corporation.Provider value={corporation}> <Context.Corporation.Provider value={corporation}>
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65vw" }} scrollButtons> <Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65vw" }} scrollButtons>
<Tab label={corporation.name} value={"Overview"} /> <Tab label={corporation.name} value={"Overview"} />
{corporation.divisions.map((div) => ( {[...corporation.divisions.values()].map((div) => (
<Tab key={div.name} label={div.name} value={div.name} /> <Tab key={div.name} label={div.name} value={div.name} />
))} ))}
{canExpand && <Tab label={"Expand"} value={-1} />} {canExpand && <Tab label={"Expand"} value={-1} />}

@ -6,13 +6,12 @@ import { useCorporation } from "./Context";
import { NewIndustry } from "../Actions"; import { NewIndustry } from "../Actions";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import { KEY } from "../../utils/helpers/keyCodes"; import { KEY } from "../../utils/helpers/keyCodes";
interface IProps { interface IProps {
setDivisionName: (name: string) => void; setDivisionName: (name: string) => void;
} }
@ -26,10 +25,15 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
const data = IndustriesData[industry]; const data = IndustriesData[industry];
if (!data) return <></>; if (!data) return <></>;
const disabled = corp.funds < data.startingCost && corp.divisions.length < corp.maxDivisions; const disabledText =
corp.divisions.size >= corp.maxDivisions
? "Corporation already has the maximum number of divisions"
: corp.funds < data.startingCost
? "Insufficient corporation funds"
: "";
function newIndustry(): void { function newIndustry(): void {
if (disabled) return; if (disabledText) return;
try { try {
NewIndustry(corp, industry, name); NewIndustry(corp, industry, name);
} catch (err) { } catch (err) {
@ -60,7 +64,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
return ( return (
<> <>
<Typography> <Typography>
{corp.name} has {corp.divisions.length}/{corp.maxDivisions} divisions. {corp.name} has {corp.divisions.size} of {corp.maxDivisions} divisions.
</Typography> </Typography>
<Typography>Create a new division to expand into a new industry:</Typography> <Typography>Create a new division to expand into a new industry:</Typography>
<Select value={industry} onChange={onIndustryChange}> <Select value={industry} onChange={onIndustryChange}>
@ -77,20 +81,10 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
<Typography>Division name:</Typography> <Typography>Division name:</Typography>
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<TextField <TextField autoFocus={true} value={name} onChange={onNameChange} onKeyDown={onKeyDown} type="text"></TextField>{" "}
autoFocus={true} <ButtonWithTooltip disabledTooltip={disabledText} onClick={newIndustry}>
value={name}
onChange={onNameChange}
onKeyDown={onKeyDown}
type="text"
InputProps={{
endAdornment: (
<Button disabled={disabled} sx={{ mx: 1 }} onClick={newIndustry}>
Expand Expand
</Button> </ButtonWithTooltip>
),
}}
/>
</Box> </Box>
</> </>
); );

@ -1,13 +1,13 @@
import React, { useState } from "react"; import React, { useState } from "react";
import * as corpConstants from "../data/Constants"; import * as corpConstants from "../data/Constants";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { NewCity } from "../Actions"; import { purchaseOffice } from "../Actions";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import Button from "@mui/material/Button"; import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import { CityName } from "../../Enums"; import { CityName } from "../../Enums";
interface IProps { interface IProps {
@ -17,10 +17,10 @@ interface IProps {
export function ExpandNewCity(props: IProps): React.ReactElement { export function ExpandNewCity(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = useDivision(); const division = useDivision();
const possibleCities = Object.values(CityName).filter((cityName) => division.offices[cityName] === 0); const possibleCities = Object.values(CityName).filter((cityName) => !(cityName in division.offices));
const [city, setCity] = useState(possibleCities[0]); const [city, setCity] = useState(possibleCities[0]);
const disabled = corp.funds < corpConstants.officeInitialCost; const disabledText = corp.funds < corpConstants.officeInitialCost ? "Insufficient corporation funds" : "";
function onCityChange(event: SelectChangeEvent): void { function onCityChange(event: SelectChangeEvent): void {
setCity(event.target.value as CityName); setCity(event.target.value as CityName);
@ -28,7 +28,7 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
function expand(): void { function expand(): void {
try { try {
NewCity(corp, division, city); purchaseOffice(corp, division, city);
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
return; return;
@ -44,21 +44,16 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
Would you like to expand into a new city by opening an office? This would cost{" "} Would you like to expand into a new city by opening an office? This would cost{" "}
<MoneyCost money={corpConstants.officeInitialCost} corp={corp} /> <MoneyCost money={corpConstants.officeInitialCost} corp={corp} />
</Typography> </Typography>
<Select <Select value={city} onChange={onCityChange}>
endAdornment={
<Button onClick={expand} disabled={disabled}>
Confirm
</Button>
}
value={city}
onChange={onCityChange}
>
{possibleCities.map((cityName: string) => ( {possibleCities.map((cityName: string) => (
<MenuItem key={cityName} value={cityName}> <MenuItem key={cityName} value={cityName}>
{cityName} {cityName}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
<ButtonWithTooltip onClick={expand} disabledTooltip={disabledText}>
Confirm
</ButtonWithTooltip>
</> </>
); );
} }

@ -1,14 +1,14 @@
import { CorpMaterialName } from "@nsdefs"; import { CorpMaterialName } from "@nsdefs";
import { Industry } from "../Industry"; import { Division } from "../Division";
// Returns a boolean indicating whether the given material is relevant for the // Returns a boolean indicating whether the given material is relevant for the
// current industry. // current industry.
export function isRelevantMaterial(matName: CorpMaterialName, division: Industry): boolean { export function isRelevantMaterial(matName: CorpMaterialName, division: Division): boolean {
// Materials that affect Production multiplier // Materials that affect Production multiplier
const prodMultiplierMats: CorpMaterialName[] = ["Hardware", "Robots", "AI Cores", "Real Estate"]; const prodMultiplierMats: CorpMaterialName[] = ["Hardware", "Robots", "AI Cores", "Real Estate"];
if (Object.keys(division.reqMats).includes(matName)) return true; if (Object.keys(division.requiredMaterials).includes(matName)) return true;
if (division.prodMats.includes(matName)) return true; if (division.producedMaterials.includes(matName)) return true;
if (prodMultiplierMats.includes(matName)) return true; if (prodMultiplierMats.includes(matName)) return true;
return false; return false;

@ -13,7 +13,7 @@ import { CityName } from "../../Enums";
interface IProps { interface IProps {
city: CityName; city: CityName;
warehouse: Warehouse | 0; warehouse?: Warehouse;
office: OfficeSpace; office: OfficeSpace;
rerender: () => void; rerender: () => void;
} }

@ -3,7 +3,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { EmployeePositions } from "../data/Enums"; import { CorpUnlockName, CorpEmployeeJob } from "../data/Enums";
import { BuyTea } from "../Actions"; import { BuyTea } from "../Actions";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
@ -15,7 +15,7 @@ import { Money } from "../../ui/React/Money";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
@ -35,7 +35,7 @@ interface IProps {
interface IAutoAssignProps { interface IAutoAssignProps {
office: OfficeSpace; office: OfficeSpace;
job: EmployeePositions; job: CorpEmployeeJob;
desc: string; desc: string;
rerender: () => void; rerender: () => void;
} }
@ -53,7 +53,7 @@ function EmployeeCount(props: { num: number; next: number }): React.ReactElement
function AutoAssignJob(props: IAutoAssignProps): React.ReactElement { function AutoAssignJob(props: IAutoAssignProps): React.ReactElement {
const currJob = props.office.employeeJobs[props.job]; const currJob = props.office.employeeJobs[props.job];
const nextJob = props.office.employeeNextJobs[props.job]; const nextJob = props.office.employeeNextJobs[props.job];
const nextUna = props.office.employeeNextJobs[EmployeePositions.Unassigned]; const nextUna = props.office.employeeNextJobs[CorpEmployeeJob.Unassigned];
function assignEmployee(): void { function assignEmployee(): void {
if (nextUna <= 0) return console.warn("Cannot assign employee. No unassigned employees available"); if (nextUna <= 0) return console.warn("Cannot assign employee. No unassigned employees available");
@ -94,10 +94,9 @@ function AutoAssignJob(props: IAutoAssignProps): React.ReactElement {
function AutoManagement(props: IProps): React.ReactElement { function AutoManagement(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = useDivision(); const division = useDivision();
const vechain = corp.unlockUpgrades[4] === 1; // Has Vechain upgrade
const currUna = props.office.employeeJobs[EmployeePositions.Unassigned]; const currUna = props.office.employeeJobs[CorpEmployeeJob.Unassigned];
const nextUna = props.office.employeeNextJobs[EmployeePositions.Unassigned]; const nextUna = props.office.employeeNextJobs[CorpEmployeeJob.Unassigned];
return ( return (
<Table padding="none"> <Table padding="none">
@ -115,7 +114,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<Typography>Avg Employee Morale:</Typography> <Typography>Avg Employee Morale:</Typography>
</TableCell> </TableCell>
<TableCell align="right"> <TableCell align="right">
<Typography>{formatCorpStat(props.office.avgMor)}</Typography> <Typography>{formatCorpStat(props.office.avgMorale)}</Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow> <TableRow>
@ -123,7 +122,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<Typography>Avg Employee Energy:</Typography> <Typography>Avg Employee Energy:</Typography>
</TableCell> </TableCell>
<TableCell align="right"> <TableCell align="right">
<Typography>{formatCorpStat(props.office.avgEne)}</Typography> <Typography>{formatCorpStat(props.office.avgEnergy)}</Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow> <TableRow>
@ -131,7 +130,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<Typography>Avg Employee Experience:</Typography> <Typography>Avg Employee Experience:</Typography>
</TableCell> </TableCell>
<TableCell align="right"> <TableCell align="right">
<Typography>{formatCorpStat(props.office.totalExp / props.office.totalEmployees || 0)}</Typography> <Typography>{formatCorpStat(props.office.totalExperience / props.office.numEmployees || 0)}</Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow> <TableRow>
@ -144,7 +143,7 @@ function AutoManagement(props: IProps): React.ReactElement {
</Typography> </Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
{vechain && ( {corp.unlocks.has(CorpUnlockName.VeChain) && (
<> <>
<TableRow> <TableRow>
<TableCell> <TableCell>
@ -201,14 +200,14 @@ function AutoManagement(props: IProps): React.ReactElement {
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.Operations} job={CorpEmployeeJob.Operations}
desc={"Manages supply chain operations. Improves the amount of Materials and Products you produce."} desc={"Manages supply chain operations. Improves the amount of Materials and Products you produce."}
/> />
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.Engineer} job={CorpEmployeeJob.Engineer}
desc={ desc={
"Develops and maintains products and production systems. Increases the quality of everything you produce. Also increases the amount you produce (not as much as Operations, however)." "Develops and maintains products and production systems. Increases the quality of everything you produce. Also increases the amount you produce (not as much as Operations, however)."
} }
@ -217,14 +216,14 @@ function AutoManagement(props: IProps): React.ReactElement {
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.Business} job={CorpEmployeeJob.Business}
desc={"Handles sales and finances. Improves the amount of Materials and Products you can sell."} desc={"Handles sales and finances. Improves the amount of Materials and Products you can sell."}
/> />
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.Management} job={CorpEmployeeJob.Management}
desc={ desc={
"Leads and oversees employees and office operations. Improves the effectiveness of Engineer and Operations employees." "Leads and oversees employees and office operations. Improves the effectiveness of Engineer and Operations employees."
} }
@ -233,7 +232,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.RandD} job={CorpEmployeeJob.RandD}
desc={ desc={
"Research new innovative ways to improve the company. Generates Scientific Research. Also increases the quality of everything you produce (not as much as Engineer, however)." "Research new innovative ways to improve the company. Generates Scientific Research. Also increases the quality of everything you produce (not as much as Engineer, however)."
} }
@ -242,7 +241,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.Intern} job={CorpEmployeeJob.Intern}
desc={ desc={
"Set employee to intern, which will increase some of their stats. Employees in intern do not affect any company operations, but gain increased exp and improve morale and energy." "Set employee to intern, which will increase some of their stats. Employees in intern do not affect any company operations, but gain increased exp and improve morale and energy."
} }
@ -260,32 +259,38 @@ export function IndustryOffice(props: IProps): React.ReactElement {
function autohireEmployeeButtonOnClick(): void { function autohireEmployeeButtonOnClick(): void {
if (props.office.atCapacity()) return; if (props.office.atCapacity()) return;
props.office.hireRandomEmployee(EmployeePositions.Unassigned); props.office.hireRandomEmployee(CorpEmployeeJob.Unassigned);
props.rerender(); props.rerender();
} }
const hireEmployeeDisabledText = props.office.atCapacity() ? "Insufficient office space" : "";
const teaDisabledText =
corp.funds < props.office.getTeaCost()
? "Insufficient corporation funds"
: props.office.teaPending
? "Tea is already pending for this cycle"
: "";
const partyPending = props.office.partyMult > 1;
const partyDisabledText =
corp.funds < 0 ? "Insufficient corporation funds" : partyPending ? "A party is already pending for this cycle" : "";
return ( return (
<Paper> <Paper>
<Typography>Office Space</Typography> <Typography>Office Space</Typography>
<Typography> <Typography>
Size: {props.office.totalEmployees} / {props.office.size} employees Size: {props.office.numEmployees} / {props.office.size} employees
</Typography> </Typography>
<Box sx={{ display: "grid", gridTemplateColumns: "1fr", width: "fit-content" }}> <Box sx={{ display: "grid", gridTemplateColumns: "1fr", width: "fit-content" }}>
<Box sx={{ gridTemplateColumns: "repeat(3, 1fr)" }}> <Box sx={{ gridTemplateColumns: "repeat(3, 1fr)" }}>
<Tooltip title={<Typography>Hires an employee</Typography>}> <ButtonWithTooltip disabledTooltip={hireEmployeeDisabledText} onClick={autohireEmployeeButtonOnClick}>
<span>
<Button disabled={props.office.atCapacity()} onClick={autohireEmployeeButtonOnClick}>
Hire Employee Hire Employee
</Button> </ButtonWithTooltip>
</span> <ButtonWithTooltip
</Tooltip> normalTooltip={"Upgrade the office's size so that it can hold more employees!"}
<Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}> onClick={() => setUpgradeOfficeSizeOpen(true)}
<span> >
<Button disabled={corp.funds < 0} onClick={() => setUpgradeOfficeSizeOpen(true)}>
Upgrade size Upgrade size
</Button> </ButtonWithTooltip>
</span>
</Tooltip>
<UpgradeOfficeSizeModal <UpgradeOfficeSizeModal
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
@ -294,44 +299,32 @@ export function IndustryOffice(props: IProps): React.ReactElement {
/> />
{!division.hasResearch("AutoBrew") && ( {!division.hasResearch("AutoBrew") && (
<> <ButtonWithTooltip
<Tooltip normalTooltip={
title={ "Provide your employees with tea, increasing their energy by half the difference to 100%, plus 1.5%"
<Typography>
Provide your employees with tea, increasing their energy by half the difference to 100%, plus 1.5%
</Typography>
} }
> disabledTooltip={teaDisabledText}
<span>
<Button
disabled={corp.funds < props.office.getTeaCost() || props.office.teaPending}
onClick={() => BuyTea(corp, props.office)} onClick={() => BuyTea(corp, props.office)}
> >
{props.office.teaPending ? ( {props.office.teaPending ? (
"Buying tea..." "Buying Tea"
) : ( ) : (
<span> <>
Buy Tea - <MoneyCost money={props.office.getTeaCost()} corp={corp} /> Buy Tea - <MoneyCost money={props.office.getTeaCost()} corp={corp} />
</span>
)}
</Button>
</span>
</Tooltip>
</> </>
)} )}
</ButtonWithTooltip>
)}
{!division.hasResearch("AutoPartyManager") && ( {!division.hasResearch("AutoPartyManager") && (
<> <>
<Tooltip title={<Typography>Throw an office party to increase your employee's morale</Typography>}> <ButtonWithTooltip
<span> normalTooltip={"Throw an office party to increase your employees' morale"}
<Button disabledTooltip={partyDisabledText}
disabled={corp.funds < 0 || props.office.partyMult > 1}
onClick={() => setThrowPartyOpen(true)} onClick={() => setThrowPartyOpen(true)}
> >
{props.office.partyMult > 1 ? "Throwing Party..." : "Throw Party"} {props.office.partyMult > 1 ? "Throwing Party..." : "Throw Party"}
</Button> </ButtonWithTooltip>
</span>
</Tooltip>
<ThrowPartyModal <ThrowPartyModal
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}

@ -2,7 +2,7 @@
// (top-left panel in the Industry UI) // (top-left panel in the Industry UI)
import React, { useState } from "react"; import React, { useState } from "react";
import { IndustryType } from "../data/Enums"; import { CorpUnlockName, IndustryType } from "../data/Enums";
import { HireAdVert } from "../Actions"; import { HireAdVert } from "../Actions";
import { formatBigNumber } from "../../ui/formatNumber"; import { formatBigNumber } from "../../ui/formatNumber";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText"; import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
@ -16,6 +16,7 @@ import { MoneyCost } from "./MoneyCost";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
@ -30,7 +31,7 @@ function MakeProductButton(): React.ReactElement {
const hasMaxProducts = division.hasMaximumNumberProducts(); const hasMaxProducts = division.hasMaximumNumberProducts();
function shouldFlash(): boolean { function shouldFlash(): boolean {
return Object.keys(division.products).length === 0; return division.products.size === 0;
} }
function onButtonClick() { function onButtonClick() {
@ -69,38 +70,35 @@ function MakeProductButton(): React.ReactElement {
return <></>; return <></>;
} }
const disabledText = hasMaxProducts
? `${division.name} already has the maximum number of products (${division.getMaximumNumberProducts()})`
: corp.funds < 0
? "Insufficient corporation funds"
: "";
return ( return (
<> <>
<Tooltip <ButtonWithTooltip
title={ disabledTooltip={disabledText}
hasMaxProducts ? ( onClick={onButtonClick}
<Typography> buttonProps={{ color: shouldFlash() ? "error" : "primary" }}
You have reached the maximum number of products: {division.getMaximumNumberProducts()}
</Typography>
) : (
""
)
}
> >
<Button color={shouldFlash() ? "error" : "primary"} onClick={onButtonClick} disabled={corp.funds < 0}>
{createProductButtonText} {createProductButtonText}
</Button> </ButtonWithTooltip>
</Tooltip>
<MakeProductModal open={makeOpen} onClose={() => setMakeOpen(false)} /> <MakeProductModal open={makeOpen} onClose={() => setMakeOpen(false)} />
</> </>
); );
} }
interface IProps { interface IndustryOverviewProps {
rerender: () => void; rerender: () => void;
} }
export function IndustryOverview(props: IProps): React.ReactElement { export function IndustryOverview(props: IndustryOverviewProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = useDivision(); const division = useDivision();
const [helpOpen, setHelpOpen] = useState(false); const [helpOpen, setHelpOpen] = useState(false);
const [researchOpen, setResearchOpen] = useState(false); const [researchOpen, setResearchOpen] = useState(false);
const vechain = corp.unlockUpgrades[4] === 1;
const profit = division.lastCycleRevenue - division.lastCycleExpenses; const profit = division.lastCycleRevenue - division.lastCycleExpenses;
let advertisingInfo = false; let advertisingInfo = false;
@ -109,7 +107,7 @@ export function IndustryOverview(props: IProps): React.ReactElement {
const popularityFac = advertisingFactors[2]; const popularityFac = advertisingFactors[2];
const ratioFac = advertisingFactors[3]; const ratioFac = advertisingFactors[3];
const totalAdvertisingFac = advertisingFactors[0]; const totalAdvertisingFac = advertisingFactors[0];
if (vechain) { if (corp.unlocks.has(CorpUnlockName.VeChain)) {
advertisingInfo = true; advertisingInfo = true;
} }
@ -162,13 +160,13 @@ export function IndustryOverview(props: IProps): React.ReactElement {
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Tooltip <Tooltip
title={ title={
<Typography> <>
Production gain from owning production-boosting materials such as hardware, Robots, AI Cores, and Real Production gain from owning production-boosting materials such as hardware, Robots, AI Cores, and Real
Estate. Estate.
</Typography> </>
} }
> >
<Typography>Production Multiplier: {formatBigNumber(division.prodMult)}</Typography> <Typography>Production Multiplier: {formatBigNumber(division.productionMult)}</Typography>
</Tooltip> </Tooltip>
<IconButton onClick={() => setHelpOpen(true)}> <IconButton onClick={() => setHelpOpen(true)}>
<HelpIcon /> <HelpIcon />
@ -189,25 +187,19 @@ export function IndustryOverview(props: IProps): React.ReactElement {
multiplier (Bigger bars = more effective): multiplier (Bigger bars = more effective):
<br /> <br />
<br /> <br />
Hardware:&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.hwFac)} Hardware:&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.hardwareFactor)}
<br /> <br />
Robots:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.robFac)} Robots:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.robotFactor)}
<br /> <br />
AI Cores:&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.aiFac)} AI Cores:&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.aiCoreFactor)}
<br /> <br />
Real Estate: {convertEffectFacToGraphic(division.reFac)} Real Estate: {convertEffectFacToGraphic(division.realEstateFactor)}
</Typography> </Typography>
</StaticModal> </StaticModal>
</Box> </Box>
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Tooltip <Tooltip title={"Scientific Research increases the quality of the materials and products that you produce."}>
title={ <Typography>Scientific Research: {formatBigNumber(division.researchPoints)}</Typography>
<Typography>
Scientific Research increases the quality of the materials and products that you produce.
</Typography>
}
>
<Typography>Scientific Research: {formatBigNumber(division.sciResearch)}</Typography>
</Tooltip> </Tooltip>
<Button sx={{ mx: 1 }} onClick={() => setResearchOpen(true)}> <Button sx={{ mx: 1 }} onClick={() => setResearchOpen(true)}>
Research Research
@ -216,26 +208,23 @@ export function IndustryOverview(props: IProps): React.ReactElement {
</Box> </Box>
<br /> <br />
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Tooltip <ButtonWithTooltip
title={ normalTooltip={
<Typography> <>
Hire AdVert.Inc to advertise your company. Each level of this upgrade grants your company a static Hire AdVert.Inc to advertise your company. Each level of this upgrade grants your company a static
increase of 3 and 1 to its awareness and popularity, respectively. It will then increase your company's increase of 3 and 1 to its awareness and popularity, respectively. It will then increase your company's" +
awareness by 1%, and its popularity by a random percentage between 1% and 3%. These effects are increased awareness by 1%, and its popularity by a random percentage between 1% and 3%. These effects are increased
by other upgrades that increase the power of your advertising. by other upgrades that increase the power of your advertising.
</Typography> </>
} }
> disabledTooltip={division.getAdVertCost() > corp.funds ? "Insufficient corporation funds" : ""}
<Button onClick={() => {
disabled={division.getAdVertCost() > corp.funds}
onClick={function () {
HireAdVert(corp, division); HireAdVert(corp, division);
props.rerender(); props.rerender();
}} }}
> >
Hire AdVert -&nbsp; <MoneyCost money={division.getAdVertCost()} corp={corp} /> Hire AdVert -&nbsp; <MoneyCost money={division.getAdVertCost()} corp={corp} />
</Button> </ButtonWithTooltip>
</Tooltip>
{division.makesProducts && <MakeProductButton />} {division.makesProducts && <MakeProductButton />}
</Box> </Box>
</Paper> </Paper>

@ -1,20 +1,20 @@
import React from "react"; import React from "react";
import { Industry } from "../Industry"; import { Division } from "../Division";
import { MathJax } from "better-react-mathjax"; import { MathJax } from "better-react-mathjax";
import { CorpMaterialName } from "@nsdefs"; import { CorpMaterialName } from "@nsdefs";
interface IProps { interface IProps {
division: Industry; division: Division;
} }
export function IndustryProductEquation(props: IProps): React.ReactElement { export function IndustryProductEquation(props: IProps): React.ReactElement {
const reqs = []; const reqs = [];
for (const reqMat of Object.keys(props.division.reqMats) as CorpMaterialName[]) { for (const reqMat of Object.keys(props.division.requiredMaterials) as CorpMaterialName[]) {
const reqAmt = props.division.reqMats[reqMat]; const reqAmt = props.division.requiredMaterials[reqMat];
if (reqAmt === undefined) continue; if (reqAmt === undefined) continue;
reqs.push(String.raw`${reqAmt}\text{ }${reqMat}`); reqs.push(String.raw`${reqAmt}\text{ }${reqMat}`);
} }
const prod = props.division.prodMats.map((p) => `1\\text{ }${p}`); const prod = props.division.producedMaterials.map((p) => `1\\text{ }${p}`);
if (props.division.makesProducts) { if (props.division.makesProducts) {
prod.push("Products"); prod.push("Products");
} }

@ -12,26 +12,28 @@ import { MaterialInfo } from "../MaterialInfo";
import { formatBigNumber, formatMaterialSize } from "../../ui/formatNumber"; import { formatBigNumber, formatMaterialSize } from "../../ui/formatNumber";
import { Corporation } from "../Corporation"; import { Corporation } from "../Corporation";
import { Industry } from "../Industry"; import { Division } from "../Division";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { isRelevantMaterial } from "./Helpers"; import { isRelevantMaterial } from "./Helpers";
import { IndustryProductEquation } from "./IndustryProductEquation"; import { IndustryProductEquation } from "./IndustryProductEquation";
import { PurchaseWarehouse } from "../Actions"; import { purchaseWarehouse } from "../Actions";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import { CityName } from "../../Enums"; import { CityName } from "../../Enums";
import { CorpUnlockName } from "../data/Enums";
interface IProps { interface IProps {
corp: Corporation; corp: Corporation;
division: Industry; division: Division;
warehouse: Warehouse | 0; warehouse?: Warehouse;
currentCity: CityName; currentCity: CityName;
rerender: () => void; rerender: () => void;
} }
@ -48,14 +50,13 @@ function WarehouseRoot(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = useDivision(); const division = useDivision();
const [smartSupplyOpen, setSmartSupplyOpen] = useState(false); const [smartSupplyOpen, setSmartSupplyOpen] = useState(false);
if (props.warehouse === 0) return <></>; if (!props.warehouse) return <></>;
// Upgrade Warehouse size button // Upgrade Warehouse size button
const sizeUpgradeCost = corpConstants.warehouseSizeUpgradeCostBase * Math.pow(1.07, props.warehouse.level + 1); const sizeUpgradeCost = corpConstants.warehouseSizeUpgradeCostBase * Math.pow(1.07, props.warehouse.level + 1);
const canAffordUpgrade = corp.funds > sizeUpgradeCost; const canAffordUpgrade = corp.funds > sizeUpgradeCost;
function upgradeWarehouseOnClick(): void { function upgradeWarehouseOnClick(): void {
if (division === null) return; if (!props.warehouse) return;
if (props.warehouse === 0) return;
if (!canAffordUpgrade) return; if (!canAffordUpgrade) return;
++props.warehouse.level; ++props.warehouse.level;
props.warehouse.updateSize(corp, division); props.warehouse.updateSize(corp, division);
@ -93,7 +94,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
for (const matName of Object.values(corpConstants.materialNames)) { for (const matName of Object.values(corpConstants.materialNames)) {
if (!props.warehouse.materials[matName]) continue; if (!props.warehouse.materials[matName]) continue;
// Only create UI for materials that are relevant for the industry or in stock // Only create UI for materials that are relevant for the industry or in stock
const isInStock = props.warehouse.materials[matName].qty > 0; const isInStock = props.warehouse.materials[matName].stored > 0;
const isRelevant = isRelevantMaterial(matName, division); const isRelevant = isRelevantMaterial(matName, division);
if (!isInStock && !isRelevant) continue; if (!isInStock && !isRelevant) continue;
mats.push( mats.push(
@ -108,51 +109,31 @@ function WarehouseRoot(props: IProps): React.ReactElement {
} }
// Create React components for products // Create React components for products
const products = []; const productElements = [];
if (division.makesProducts && Object.keys(division.products).length > 0) { if (division.makesProducts && division.products.size > 0) {
for (const productName of Object.keys(division.products)) { for (const [productName, product] of division.products) {
const product = division.products[productName]; productElements.push(
if (!product) continue;
products.push(
<ProductElem rerender={props.rerender} city={props.currentCity} key={productName} product={product} />, <ProductElem rerender={props.rerender} city={props.currentCity} key={productName} product={product} />,
); );
} }
} }
const breakdownItems: JSX.Element[] = []; const breakdownItems: string[] = [];
for (const matName of Object.values(corpConstants.materialNames)) { for (const matName of corpConstants.materialNames) {
const mat = props.warehouse.materials[matName]; const mat = props.warehouse.materials[matName];
if (!Object.hasOwn(MaterialInfo, matName)) continue; if (mat.stored === 0) continue;
if (mat.qty === 0) continue; breakdownItems.push(`${matName}: ${formatMaterialSize(mat.stored * MaterialInfo[matName].size)}`);
breakdownItems.push(
<>
{matName}: {formatMaterialSize(mat.qty * MaterialInfo[matName].size)}
</>,
);
} }
for (const prodName of Object.keys(division.products)) { for (const [prodName, product] of division.products) {
const prod = division.products[prodName];
if (prod === undefined) continue;
breakdownItems.push( breakdownItems.push(
<> `${prodName}: ${formatMaterialSize(product.cityData[props.currentCity].stored * product.size)}`,
{prodName}: {formatMaterialSize(prod.data[props.warehouse.loc][0] * prod.siz)}
</>,
); );
} }
let breakdown; let breakdown;
if (breakdownItems && breakdownItems.length > 0) { if (breakdownItems.length > 0) {
breakdown = breakdownItems.reduce( breakdown = breakdownItems.map((item, i) => <p key={i}>{item}</p>);
(previous: JSX.Element, current: JSX.Element): JSX.Element =>
(previous && (
<>
{previous}
<br />
{current}
</>
)) || <>{current}</>,
);
} else { } else {
breakdown = <>No items in storage.</>; breakdown = <>No items in storage.</>;
} }
@ -160,27 +141,20 @@ function WarehouseRoot(props: IProps): React.ReactElement {
return ( return (
<Paper> <Paper>
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Tooltip <Tooltip title={breakdown}>
title={
props.warehouse.sizeUsed !== 0 ? (
<Typography>
<>{breakdown}</>
</Typography>
) : (
""
)
}
>
<Typography color={props.warehouse.sizeUsed >= props.warehouse.size ? "error" : "primary"}> <Typography color={props.warehouse.sizeUsed >= props.warehouse.size ? "error" : "primary"}>
Storage: {formatBigNumber(props.warehouse.sizeUsed)} / {formatBigNumber(props.warehouse.size)} Storage: {formatBigNumber(props.warehouse.sizeUsed)} / {formatBigNumber(props.warehouse.size)}
</Typography> </Typography>
</Tooltip> </Tooltip>
</Box> </Box>
<Button disabled={!canAffordUpgrade} onClick={upgradeWarehouseOnClick}> <ButtonWithTooltip
disabledTooltip={canAffordUpgrade ? "" : "Insufficient corporation funds"}
onClick={upgradeWarehouseOnClick}
>
Upgrade Warehouse Size -&nbsp; Upgrade Warehouse Size -&nbsp;
<MoneyCost money={sizeUpgradeCost} corp={corp} /> <MoneyCost money={sizeUpgradeCost} corp={corp} />
</Button> </ButtonWithTooltip>
<Typography>This industry uses the following equation for its production: </Typography> <Typography>This industry uses the following equation for its production: </Typography>
<br /> <br />
@ -196,7 +170,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
<Typography className={classes.retainHeight}>{stateText}</Typography> <Typography className={classes.retainHeight}>{stateText}</Typography>
{corp.unlockUpgrades[1] && ( {corp.unlocks.has(CorpUnlockName.SmartSupply) && (
<> <>
<Button onClick={() => setSmartSupplyOpen(true)}>Configure Smart Supply</Button> <Button onClick={() => setSmartSupplyOpen(true)}>Configure Smart Supply</Button>
<SmartSupplyModal <SmartSupplyModal
@ -209,7 +183,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
{mats} {mats}
{products} {productElements}
</Paper> </Paper>
); );
} }
@ -230,18 +204,18 @@ interface IEmptyProps {
function EmptyWarehouse(props: IEmptyProps): React.ReactElement { function EmptyWarehouse(props: IEmptyProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = useDivision(); const division = useDivision();
const disabled = corp.funds < corpConstants.warehouseInitialCost; const disabledText = corp.funds < corpConstants.warehouseInitialCost ? "Insufficient corporation funds" : "";
function purchaseWarehouse(): void { function newWarehouse(): void {
if (disabled) return; if (disabledText) return;
PurchaseWarehouse(corp, division, props.city); purchaseWarehouse(corp, division, props.city);
props.rerender(); props.rerender();
} }
return ( return (
<Paper> <Paper>
<Button onClick={purchaseWarehouse} disabled={disabled}> <ButtonWithTooltip onClick={newWarehouse} disabledTooltip={disabledText}>
Purchase Warehouse ( Purchase Warehouse (
<MoneyCost money={corpConstants.warehouseInitialCost} corp={corp} />) <MoneyCost money={corpConstants.warehouseInitialCost} corp={corp} />)
</Button> </ButtonWithTooltip>
</Paper> </Paper>
); );
} }

@ -2,49 +2,48 @@
import React from "react"; import React from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { CorporationUpgrade } from "../data/CorporationUpgrades"; import { CorpUpgrades } from "../data/CorporationUpgrades";
import { LevelUpgrade } from "../Actions";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button"; import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
import { calculateMaxAffordableUpgrade, calculateUpgradeCost } from "../helpers"; import { calculateMaxAffordableUpgrade, calculateUpgradeCost } from "../helpers";
import { CorpUpgradeName } from "../data/Enums";
import { PositiveInteger } from "../../types";
interface IProps { interface IProps {
upgrade: CorporationUpgrade; upgradeName: CorpUpgradeName;
amount: number | "MAX"; mult: PositiveInteger | "MAX";
rerender: () => void; rerender: () => void;
} }
export function LevelableUpgrade(props: IProps): React.ReactElement { export function LevelableUpgrade({ upgradeName, mult, rerender }: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const data = props.upgrade; const data = CorpUpgrades[upgradeName];
const level = corp.upgrades[data.index]; const level = corp.upgrades[upgradeName].level;
const amount = props.amount;
const maxUpgrades = amount === "MAX" ? calculateMaxAffordableUpgrade(corp, data, amount) : amount; const amount = mult === "MAX" ? calculateMaxAffordableUpgrade(corp, data, mult) : mult;
const cost = calculateUpgradeCost(corp, data, maxUpgrades); const cost = amount === 0 ? 0 : calculateUpgradeCost(corp, data, amount);
const tooltip = data.desc; const tooltip = data.desc;
function onClick(): void { function onClick(): void {
if (corp.funds < cost) return; if (corp.funds < cost) return;
try { const message = corp.purchaseUpgrade(upgradeName, amount);
LevelUpgrade(corp, props.upgrade, maxUpgrades); if (message) dialogBoxCreate(`Could not upgrade ${upgradeName} ${amount} times:\n${message}`);
} catch (err) { rerender();
dialogBoxCreate(err + "");
}
props.rerender();
} }
return ( return (
<Grid item xs={4}> <Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse"> <Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds < cost} sx={{ mx: 1 }} onClick={onClick}> <ButtonWithTooltip
+{maxUpgrades} -&nbsp; disabledTooltip={corp.funds < cost || amount === 0 ? "Insufficient corporation funds" : ""}
<MoneyCost money={cost} corp={corp} /> onClick={onClick}
</Button> >
+{amount} -&nbsp; <MoneyCost money={cost} corp={corp} />
</ButtonWithTooltip>
<Tooltip title={tooltip}> <Tooltip title={tooltip}>
<Typography> <Typography>
{data.name} - lvl {level} {data.name} - lvl {level}

@ -4,7 +4,6 @@
import React from "react"; import React from "react";
import { CityTabs } from "./CityTabs"; import { CityTabs } from "./CityTabs";
import { Industry } from "../Industry";
import { Context, useCorporation } from "./Context"; import { Context, useCorporation } from "./Context";
import { CityName } from "../../Enums"; import { CityName } from "../../Enums";
@ -16,12 +15,8 @@ interface IProps {
export function MainPanel(props: IProps): React.ReactElement { export function MainPanel(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = const division = corp.divisions.get(props.divisionName);
props.divisionName !== "Overview" if (!division) throw new Error("Cannot find division");
? corp.divisions.find((division: Industry) => division.name === props.divisionName)
: undefined; // use undefined because find returns undefined
if (division === undefined) throw new Error("Cannot find division");
return ( return (
<Context.Division.Provider value={division}> <Context.Division.Provider value={division}>
<CityTabs rerender={props.rerender} city={CityName.Sector12} /> <CityTabs rerender={props.rerender} city={CityName.Sector12} />

@ -22,6 +22,7 @@ import Button from "@mui/material/Button";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { LimitMaterialProductionModal } from "./modals/LimitMaterialProductionModal"; import { LimitMaterialProductionModal } from "./modals/LimitMaterialProductionModal";
import { CityName } from "../../Enums"; import { CityName } from "../../Enums";
import { CorpUnlockName } from "../data/Enums";
interface IMaterialProps { interface IMaterialProps {
warehouse: Warehouse; warehouse: Warehouse;
@ -50,29 +51,33 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
} }
// Total gain or loss of this material (per second) // Total gain or loss of this material (per second)
const totalGain = mat.buy + mat.prd + mat.imp - mat.sll - mat.totalExp; const totalGain =
mat.buyAmount + mat.productionAmount + mat.importAmount - mat.actualSellAmount - mat.exportedLastCycle;
// Flag that determines whether this industry is "new" and the current material should be // Flag that determines whether this industry is "new" and the current material should be
// marked with flashing-red lights // marked with flashing-red lights
const tutorial = const tutorial =
division.newInd && Object.keys(division.reqMats).includes(mat.name) && mat.buy === 0 && mat.imp === 0; division.newInd &&
Object.keys(division.requiredMaterials).includes(mat.name) &&
mat.buyAmount === 0 &&
mat.importAmount === 0;
// Purchase material button // Purchase material button
const purchaseButtonText = `Buy (${formatBigNumber(mat.buy)})`; const purchaseButtonText = `Buy (${formatBigNumber(mat.buyAmount)})`;
// Sell material button // Sell material button
let sellButtonText: JSX.Element; let sellButtonText: JSX.Element;
if (mat.sllman[0]) { if (mat.desiredSellAmount) {
if (isString(mat.sllman[1])) { if (isString(mat.desiredSellAmount)) {
sellButtonText = ( sellButtonText = (
<> <>
Sell ({formatBigNumber(mat.sll)}/{mat.sllman[1]}) Sell ({formatBigNumber(mat.actualSellAmount)}/{mat.desiredSellAmount[1]})
</> </>
); );
} else { } else {
sellButtonText = ( sellButtonText = (
<> <>
Sell ({formatBigNumber(mat.sll)}/{formatBigNumber(mat.sllman[1])}) Sell ({formatBigNumber(mat.actualSellAmount)}/{formatBigNumber(mat.desiredSellAmount)})
</> </>
); );
} }
@ -86,12 +91,12 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
} else if (mat.marketTa1) { } else if (mat.marketTa1) {
sellButtonText = ( sellButtonText = (
<> <>
{sellButtonText} @ <Money money={mat.bCost + markupLimit} /> {sellButtonText} @ <Money money={mat.marketPrice + markupLimit} />
</> </>
); );
} else if (mat.sCost) { } else if (mat.desiredSellPrice) {
if (isString(mat.sCost)) { if (isString(mat.desiredSellPrice)) {
const sCost = mat.sCost.replace(/MP/g, mat.bCost + ""); const sCost = mat.desiredSellPrice.replace(/MP/g, mat.marketPrice + "");
sellButtonText = ( sellButtonText = (
<> <>
{sellButtonText} @ <Money money={eval(sCost)} /> {sellButtonText} @ <Money money={eval(sCost)} />
@ -100,7 +105,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
} else { } else {
sellButtonText = ( sellButtonText = (
<> <>
{sellButtonText} @ <Money money={mat.sCost} /> {sellButtonText} @ <Money money={mat.desiredSellPrice} />
</> </>
); );
} }
@ -111,8 +116,8 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
// Limit Production button // Limit Production button
let limitMaterialButtonText = "Limit Material"; let limitMaterialButtonText = "Limit Material";
if (mat.prdman[0]) { if (mat.productionLimit !== null) {
limitMaterialButtonText += " (" + formatCorpStat(mat.prdman[1]) + ")"; limitMaterialButtonText += " (" + formatCorpStat(mat.productionLimit) + ")";
} }
return ( return (
@ -122,20 +127,28 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
Buy: {mat.buy >= 1e33 ? mat.buy.toExponential(3) : formatBigNumber(mat.buy)} <br /> Buy: {mat.buyAmount >= 1e33 ? mat.buyAmount.toExponential(3) : formatBigNumber(mat.buyAmount)} <br />
Prod: {formatBigNumber(mat.prd)} <br /> Prod: {formatBigNumber(mat.productionAmount)} <br />
Sell: {formatBigNumber(mat.sll)} <br /> Sell: {formatBigNumber(mat.actualSellAmount)} <br />
Export: {formatBigNumber(mat.totalExp)} <br /> Export: {formatBigNumber(mat.exportedLastCycle)} <br />
Import: {formatBigNumber(mat.imp)} Import: {formatBigNumber(mat.importAmount)}
{corp.unlockUpgrades[2] === 1 && <br />} {corp.unlocks.has(CorpUnlockName.MarketResearchDemand) && (
{corp.unlockUpgrades[2] === 1 && "Demand: " + formatCorpStat(mat.dmd)} <>
{corp.unlockUpgrades[3] === 1 && <br />} <br />
{corp.unlockUpgrades[3] === 1 && "Competition: " + formatCorpStat(mat.cmp)} Demand: {formatCorpStat(mat.demand)}
</>
)}
{corp.unlocks.has(CorpUnlockName.MarketDataCompetition) && (
<>
<br />
Competition: {formatCorpStat(mat.competition)}
</>
)}
</Typography> </Typography>
} }
> >
<Typography> <Typography>
{mat.name}: {formatBigNumber(mat.qty)} ( {mat.name}: {formatBigNumber(mat.stored)} (
{totalGain >= 1e33 ? totalGain.toExponential(3) : formatBigNumber(totalGain)}/s) {totalGain >= 1e33 ? totalGain.toExponential(3) : formatBigNumber(totalGain)}/s)
</Typography> </Typography>
</Tooltip> </Tooltip>
@ -146,12 +159,12 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
</Typography> </Typography>
} }
> >
<Typography>MP: {formatMoney(mat.bCost)}</Typography> <Typography>MP: {formatMoney(mat.marketPrice)}</Typography>
</Tooltip> </Tooltip>
<Tooltip <Tooltip
title={<Typography>The quality of your material. Higher quality will lead to more sales</Typography>} title={<Typography>The quality of your material. Higher quality will lead to more sales</Typography>}
> >
<Typography>Quality: {formatQuality(mat.qlt)}</Typography> <Typography>Quality: {formatQuality(mat.quality)}</Typography>
</Tooltip> </Tooltip>
</Box> </Box>
@ -167,13 +180,11 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
mat={mat} mat={mat}
warehouse={warehouse} warehouse={warehouse}
open={purchaseMaterialOpen} open={purchaseMaterialOpen}
disablePurchaseLimit={ disablePurchaseLimit={props.warehouse.smartSupplyEnabled && props.mat.name in division.requiredMaterials}
props.warehouse.smartSupplyEnabled && Object.keys(division.reqMats).includes(props.mat.name)
}
onClose={() => setPurchaseMaterialOpen(false)} onClose={() => setPurchaseMaterialOpen(false)}
/> />
{corp.unlockUpgrades[0] === 1 && ( {corp.unlocks.has(CorpUnlockName.Export) && (
<> <>
<Button onClick={() => setExportOpen(true)}>Export</Button> <Button onClick={() => setExportOpen(true)}>Export</Button>
@ -182,7 +193,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
)} )}
<Button <Button
color={division.prodMats.includes(props.mat.name) && !mat.sllman[0] ? "error" : "primary"} color={division.producedMaterials.includes(props.mat.name) && !mat.desiredSellAmount ? "error" : "primary"}
onClick={() => setSellMaterialOpen(true)} onClick={() => setSellMaterialOpen(true)}
> >
{sellButtonText} {sellButtonText}

Some files were not shown because too many files have changed in this diff Show More