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. -->
[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
**Signature:**
```typescript
getUnlockUpgradeCost(upgradeName: string): number;
getUnlockCost(upgradeName: string): number;
```
## Parameters

@ -1,15 +1,15 @@
<!-- 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
**Signature:**
```typescript
hasUnlockUpgrade(upgradeName: string): boolean;
hasUnlock(upgradeName: string): boolean;
```
## 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 |
| [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 |
| [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 |
| [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 |
| [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 |
| [issueNewShares(amount)](./bitburner.corporation.issuenewshares.md) | Issue new shares |
| [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 |
| [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. -->
[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
**Signature:**
```typescript
unlockUpgrade(upgradeName: string): void;
purchaseUnlock(upgradeName: string): void;
```
## Parameters

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

@ -22,12 +22,12 @@ interface Division
| [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 |
| [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 |
| [prodMult](./bitburner.division.prodmult.md) | | number | Production multiplier |
| [products](./bitburner.division.products.md) | | string\[\] | Products developed by this division |
| [research](./bitburner.division.research.md) | | number | Amount of research in that division |
| [productionMult](./bitburner.division.productionmult.md) | | number | Production multiplier |
| [products](./bitburner.division.products.md) | | string\[\] | Names of Products developed by this division |
| [researchPoints](./bitburner.division.researchpoints.md) | | number | Amount of research in that division |
| [thisCycleExpenses](./bitburner.division.thiscycleexpenses.md) | | number | Expenses 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 |
| [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. -->
[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:**
```typescript
upgrades: number[];
numAdVerts: number;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
prodMult: number;
productionMult: number;
```

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

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
research: number;
researchPoints: number;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
amt: string;
amount: string;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
loc: CityName;
city: CityName;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
div: string;
division: string;
```

@ -16,7 +16,7 @@ interface Export
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [amt](./bitburner.export.amt.md) | | string | Amount of material exported |
| [div](./bitburner.export.div.md) | | string | Division the material is being exported to |
| [loc](./bitburner.export.loc.md) | | [CityName](./bitburner.cityname.md) | City the material is being exported to |
| [amount](./bitburner.export.amount.md) | | string | Amount of material exported |
| [city](./bitburner.export.city.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
Cost to expand to the division
Cost to make a new division of this industry type
**Signature:**

@ -16,7 +16,7 @@ interface IndustryData
| 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 |
| [makesProducts](./bitburner.industrydata.makesproducts.md) | | boolean | Whether the division makes products |
| [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. -->
[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
**Signature:**
```typescript
cmp: number | undefined;
competition: number | undefined;
```

@ -1,13 +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; [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
**Signature:**
```typescript
dmd: number | undefined;
demand: number | undefined;
```

@ -1,13 +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; [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:**
```typescript
sell: number;
desiredSellAmount: string | number;
```

@ -1,13 +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; [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"
**Signature:**
```typescript
sCost: string | number;
desiredSellPrice: string | number;
```

@ -1,13 +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; [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
**Signature:**
```typescript
exp: Export[];
exports: Export[];
```

@ -1,13 +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; [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
**Signature:**
```typescript
cost: number;
marketPrice: number;
```

@ -16,15 +16,15 @@ interface Material
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [cmp](./bitburner.material.cmp.md) | | number \| undefined | Competition for the material, only present if "Market Research - Competition" unlocked |
| [cost](./bitburner.material.cost.md) | | number | Cost to buy material |
| [dmd](./bitburner.material.dmd.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 |
| [actualSellAmount](./bitburner.material.actualsellamount.md) | | number | Amount of material sold last cycle |
| [competition](./bitburner.material.competition.md) | | number \| undefined | Competition for the material, only present if "Market Research - Competition" unlocked |
| [demand](./bitburner.material.demand.md) | | number \| undefined | Demand for the material, only present if "Market Research - Demand" unlocked |
| [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 |
| [prod](./bitburner.material.prod.md) | | number | Amount of material produced |
| [qlt](./bitburner.material.qlt.md) | | number | Quality of the material |
| [qty](./bitburner.material.qty.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 |
| [productionAmount](./bitburner.material.productionamount.md) | | number | Amount of material produced last cycle |
| [quality](./bitburner.material.quality.md) | | number | Quality of the material |
| [stored](./bitburner.material.stored.md) | | number | Amount of material |

@ -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. -->
[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
**Signature:**
```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. -->
[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
**Signature:**
```typescript
qty: number;
stored: number;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
avgEne: number;
avgEnergy: number;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
avgMor: number;
avgMorale: number;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
loc: CityName;
city: CityName;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```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. -->
[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
**Signature:**
```typescript
maxEne: number;
maxEnergy: number;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
maxMor: number;
maxMorale: number;
```

@ -16,14 +16,14 @@ export interface Office
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [avgEne](./bitburner.office.avgene.md) | | number | Average energy of the employees |
| [avgMor](./bitburner.office.avgmor.md) | | number | Average morale of the employees |
| [avgEnergy](./bitburner.office.avgenergy.md) | | number | Average energy 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 |
| [employeeProd](./bitburner.office.employeeprod.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Production of the employees |
| [employees](./bitburner.office.employees.md) | | number | Amount of employees |
| [loc](./bitburner.office.loc.md) | | [CityName](./bitburner.cityname.md) | City of the office |
| [maxEne](./bitburner.office.maxene.md) | | number | Maximum amount of energy of the employees |
| [maxMor](./bitburner.office.maxmor.md) | | number | Maximum morale of the employees |
| [employeeProductionByJob](./bitburner.office.employeeproductionbyjob.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Production of the employees |
| [maxEnergy](./bitburner.office.maxenergy.md) | | number | Maximum amount of energy of the employees |
| [maxMorale](./bitburner.office.maxmorale.md) | | number | Maximum morale of the employees |
| [numEmployees](./bitburner.office.numemployees.md) | | number | Amount of employees |
| [size](./bitburner.office.size.md) | | number | Maximum number of employee |
| [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. -->
[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
**Signature:**
```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. -->
[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
**Signature:**
```typescript
cmp: number | undefined;
competition: number | undefined;
```

@ -1,13 +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; [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
**Signature:**
```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
Creation progress - A number between 0-100 representing percentage
A number between 0-100 representing percentage completion
**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 |
| --- | --- | --- | --- |
| [cmp](./bitburner.product.cmp.md) | | number \| undefined | Competition for the product, only present if "Market Research - Competition" unlocked |
| [developmentProgress](./bitburner.product.developmentprogress.md) | | number | Creation progress - A number between 0-100 representing percentage |
| [dmd](./bitburner.product.dmd.md) | | number \| undefined | Demand for the product, only present if "Market Research - Demand" unlocked |
| [effRat](./bitburner.product.effrat.md) | | number | Effective rating |
| [actualSellAmount](./bitburner.product.actualsellamount.md) | | number | Amount of product sold last cycle |
| [competition](./bitburner.product.competition.md) | | number \| undefined | Competition for the product, only present if "Market Research - Competition" unlocked |
| [demand](./bitburner.product.demand.md) | | number \| undefined | Demand for the product, only present if "Market Research - Demand" unlocked |
| [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 |
| [pCost](./bitburner.product.pcost.md) | | number | Production cost |
| [prod](./bitburner.product.prod.md) | | number | Amount of product produced |
| [properties](./bitburner.product.properties.md) | | { \[key: string\]: number } | Product Properties. The data is {<!-- -->qlt, per, dur, rel, aes, fea<!-- -->} |
| [qty](./bitburner.product.qty.md) | | number | Amount of product |
| [rat](./bitburner.product.rat.md) | | number | Product Rating |
| [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 |
| [productionAmount](./bitburner.product.productionamount.md) | | number | Amount of product produced last cycle |
| [productionCost](./bitburner.product.productioncost.md) | | number | Production cost |
| [rating](./bitburner.product.rating.md) | | number | Rating based on stats |
| [stats](./bitburner.product.stats.md) | | { quality: number; performance: number; durability: number; reliability: number; aesthetics: number; features: number; } | Product stats |
| [stored](./bitburner.product.stored.md) | | number | Amount of product stored in warehouse |

@ -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. -->
[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
**Signature:**
```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. -->
[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:**
```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. -->
[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:**
```typescript
qty: number;
stored: number;
```

@ -1,13 +1,13 @@
<!-- 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
**Signature:**
```typescript
loc: CityName;
city: CityName;
```

@ -16,8 +16,8 @@ interface Warehouse
| 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 |
| [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 |
| [sizeUsed](./bitburner.warehouse.sizeused.md) | | number | Used space in the warehouse |
| [smartSupplyEnabled](./bitburner.warehouse.smartsupplyenabled.md) | | boolean | Smart Supply status in the warehouse |

@ -9,7 +9,7 @@ Get product data
**Signature:**
```typescript
getProduct(divisionName: string, city: CityName | `${CityName}`, productName: string): Product;
getProduct(divisionName: string, cityName: CityName | `${CityName}`, productName: string): Product;
```
## Parameters
@ -17,7 +17,7 @@ getProduct(divisionName: string, city: CityName | `${CityName}`, productName: st
| Parameter | Type | Description |
| --- | --- | --- |
| 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 |
**Returns:**

@ -26,7 +26,7 @@ Requires the Warehouse API upgrade from your corporation.
| [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 |
| [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 |
| [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 |
@ -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. |
| [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. |
| [setProductMarketTA1(divisionName, city, 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. |
| [setProductMarketTA1(divisionName, productName, on)](./bitburner.warehouseapi.setproductmarketta1.md) | \* Set market TA 1 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 |
| [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 |

@ -4,12 +4,12 @@
## WarehouseAPI.setProductMarketTA1() method
Set market TA 1 for a product.
\* Set market TA 1 for a product.
**Signature:**
```typescript
setProductMarketTA1(divisionName: string, city: CityName | `${CityName}`, productName: string, on: boolean): void;
setProductMarketTA1(divisionName: string, productName: string, on: boolean): void;
```
## Parameters
@ -17,7 +17,6 @@ setProductMarketTA1(divisionName: string, city: CityName | `${CityName}`, produc
| Parameter | Type | Description |
| --- | --- | --- |
| 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 |
| on | boolean | market ta enabled |

@ -9,7 +9,7 @@ Set market TA 2 for a product.
**Signature:**
```typescript
setProductMarketTA2(divisionName: string, city: CityName | `${CityName}`, productName: string, on: boolean): void;
setProductMarketTA2(divisionName: string, productName: string, on: boolean): void;
```
## Parameters
@ -17,7 +17,6 @@ setProductMarketTA2(divisionName: string, city: CityName | `${CityName}`, produc
| Parameter | Type | Description |
| --- | --- | --- |
| 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 |
| on | boolean | market ta enabled |

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

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

@ -37,6 +37,7 @@ import { isSleeveSupportWork } from "../PersonObjects/Sleeve/Work/SleeveSupportW
import { WorkStats, newWorkStats } from "../Work/WorkStats";
import { CityName } from "../Enums";
import { getRandomMember } from "../utils/helpers/enum";
import { createEnumKeyedRecord } from "../Types/Record";
export interface BlackOpsAttempt {
error?: string;
@ -70,7 +71,7 @@ export class Bladeburner {
type: ActionTypes.Idle,
});
cities: Record<CityName, City>;
cities = createEnumKeyedRecord(CityName, (name) => new City(name));
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).
skills: Record<string, number> = {};
@ -101,10 +102,6 @@ export class Bladeburner {
consoleLogs: string[] = ["Bladeburner Console", "Type 'help' to see console commands"];
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()
// Max Stamina is based on stats and Bladeburner-specific bonuses

@ -2,33 +2,28 @@ import { Player } from "@player";
import { MaterialInfo } from "./MaterialInfo";
import { Corporation } from "./Corporation";
import { IndustryResearchTrees, IndustriesData } from "./IndustryData";
import { Industry } from "./Industry";
import { Division } from "./Division";
import * as corpConstants from "./data/Constants";
import { OfficeSpace } from "./OfficeSpace";
import { Material } from "./Material";
import { Product } from "./Product";
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 { ResearchMap } from "./ResearchMap";
import { isRelevantMaterial } from "./ui/Helpers";
import { CityName } from "../Enums";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CorpResearchName } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers";
import { isInteger } from "lodash";
import { getRecordValues } from "../Types/Record";
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!`);
for (let i = 0; i < corporation.divisions.length; ++i) {
if (corporation.divisions[i].name === name) {
throw new Error("This division name is already in use!");
}
}
if (corporation.divisions.has(name)) throw new Error(`Division name ${name} is already in use!`);
// "Overview" is forbidden as a division name, see CorporationRoot.tsx for why this would cause issues.
if (name === "Overview") throw new Error(`"Overview" is a forbidden division name.`);
const data = IndustriesData[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!");
} else {
corporation.funds = corporation.funds - cost;
corporation.divisions.push(
new Industry({
corporation.divisions.set(
name,
new Division({
corp: corporation,
name: name,
type: industry,
@ -50,13 +46,11 @@ export function NewIndustry(corporation: Corporation, industry: IndustryType, na
}
export function removeIndustry(corporation: Corporation, name: string) {
const divIndex = corporation.divisions.findIndex((div) => div.name === name);
if (divIndex === -1) throw new Error("There is no division called " + name);
corporation.divisions.splice(divIndex, 1);
if (!corporation.divisions.has(name)) throw new Error("There is no division called " + name);
corporation.divisions.delete(name);
}
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) {
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;
division.offices[city] = new OfficeSpace({
loc: city,
city: city,
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 {
if (isNaN(rate) || rate < 0 || rate > 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];
}
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 (amt === "") amt = "0";
if (amount === "") amount = "0";
let cost = price.replace(/\s+/g, "");
cost = cost.replace(/[^-()\d/*+.MPe]/g, ""); //Sanitize cost
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")) {
mat.sCost = cost; //Dynamically evaluated
material.desiredSellPrice = cost; //Dynamically evaluated
} else {
mat.sCost = temp;
material.desiredSellPrice = temp;
}
//Parse quantity
amt = amt.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) {
let q = amt.replace(/\s+/g, "");
amount = amount.toUpperCase();
if (amount.includes("MAX") || amount.includes("PROD") || amount.includes("INV")) {
let q = amount.replace(/\s+/g, "");
q = q.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let tempQty = q.replace(/MAX/g, mat.maxsll.toString());
tempQty = tempQty.replace(/PROD/g, mat.prd.toString());
tempQty = tempQty.replace(/INV/g, mat.prd.toString());
let tempQty = q.replace(/MAX/g, material.maxSellPerCycle.toString());
tempQty = tempQty.replace(/PROD/g, material.productionAmount.toString());
tempQty = tempQty.replace(/INV/g, material.productionAmount.toString());
try {
tempQty = eval(tempQty);
} catch (e) {
@ -164,26 +139,19 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
if (tempQty == null || isNaN(parseFloat(tempQty))) {
throw new Error("Invalid value or expression for sell quantity field");
}
mat.sllman[0] = true;
mat.sllman[1] = q; //Use sanitized input
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
material.desiredSellAmount = q; //Use sanitized input
} else if (isNaN(parseFloat(amount)) || parseFloat(amount) < 0) {
throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'");
} else {
let q = parseFloat(amt);
let q = parseFloat(amount);
if (isNaN(q)) {
q = 0;
}
if (q === 0) {
mat.sllman[0] = false;
mat.sllman[1] = 0;
} else {
mat.sllman[0] = true;
mat.sllman[1] = q;
}
material.desiredSellAmount = 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
if (price.includes("MP")) {
//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))) {
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 {
const cost = parseFloat(price);
if (isNaN(cost)) {
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
amt = amt.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) {
//Dynamically evaluated quantity. First test to make sure its valid
let qty = amt.replace(/\s+/g, "");
qty = qty.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let temp = qty.replace(/MAX/g, product.maxsll.toString());
temp = temp.replace(/PROD/g, product.data[city][1].toString());
temp = temp.replace(/INV/g, product.data[city][0].toString());
let temp = qty.replace(/MAX/g, product.maxSellAmount.toString());
temp = temp.replace(/PROD/g, product.cityData[city].productionAmount.toString());
temp = temp.replace(/INV/g, product.cityData[city].stored.toString());
try {
temp = eval(temp);
} 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");
}
if (all) {
for (let i = 0; i < cities.length; ++i) {
const tempCity = cities[i];
product.sllman[tempCity][0] = true;
product.sllman[tempCity][1] = qty; //Use sanitized input
for (const cityName of Object.values(CityName)) {
product.cityData[cityName].desiredSellAmount = qty; //Use sanitized input
}
} else {
product.sllman[city][0] = true;
product.sllman[city][1] = qty; //Use sanitized input
product.cityData[city].desiredSellAmount = qty; //Use sanitized input
}
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
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 (all) {
for (let i = 0; i < cities.length; ++i) {
const tempCity = cities[i];
product.sllman[tempCity][0] = false;
product.sllman[tempCity][1] = "";
for (const cityName of Object.values(CityName)) {
product.cityData[cityName].desiredSellAmount = 0;
}
} else {
product.sllman[city][0] = false;
product.sllman[city][1] = "";
product.cityData[city].desiredSellAmount = 0;
}
} else if (all) {
for (let i = 0; i < cities.length; ++i) {
const tempCity = cities[i];
product.sllman[tempCity][0] = true;
product.sllman[tempCity][1] = qty;
for (const cityName of Object.values(CityName)) {
product.cityData[cityName].desiredSellAmount = qty;
}
} else {
product.sllman[city][0] = true;
product.sllman[city][1] = qty;
product.cityData[city].desiredSellAmount = qty;
}
}
}
@ -286,7 +242,7 @@ export function BuyMaterial(material: Material, amt: number): void {
if (isNaN(amt) || amt < 0) {
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 {
@ -298,10 +254,10 @@ export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material:
if (amt > maxAmount) {
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) {
corp.funds = corp.funds - cost;
material.qty += amt;
material.stored += amt;
} else {
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 {
const mult = 1 + costPerEmployee / 10e6;
const cost = costPerEmployee * office.totalEmployees;
const cost = costPerEmployee * office.numEmployees;
if (corp.funds < cost) {
return 0;
}
@ -378,16 +334,15 @@ export function ThrowParty(corp: Corporation, office: OfficeSpace, costPerEmploy
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 (division.warehouses[city]) return;
corp.funds = corp.funds - corpConstants.warehouseInitialCost;
division.warehouses[city] = new Warehouse({
corp: corp,
industry: division,
division: division,
loc: city,
size: corpConstants.warehouseInitialSize,
});
corp.funds = corp.funds - corpConstants.warehouseInitialCost;
}
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);
if (corp.funds < sizeUpgradeCost) return;
warehouse.level += amt;
@ -405,7 +360,7 @@ export function UpgradeWarehouse(corp: Corporation, division: Industry, warehous
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();
if (corp.funds < cost) return;
corp.funds = corp.funds - cost;
@ -414,7 +369,7 @@ export function HireAdVert(corp: Corporation, division: Industry): void {
export function MakeProduct(
corp: Corporation,
division: Industry,
division: Division,
city: CityName,
productName: string,
designInvest: number,
@ -447,53 +402,46 @@ export function MakeProduct(
} else if (division.hasResearch("uPgrade: Capacity.I")) {
maxProducts = 4;
}
const products = division.products;
if (Object.keys(products).length >= maxProducts) {
if (division.products.size >= maxProducts) {
throw new Error(`You are already at the max products (${maxProducts}) for division: ${division.name}!`);
}
const product = new Product({
name: productName.replace(/[<>]/g, "").trim(), //Sanitize for HTMl elements
createCity: city,
designCost: designInvest,
advCost: marketingInvest,
designInvestment: designInvest,
advertisingInvestment: marketingInvest,
});
if (products[product.name]) {
if (division.products.has(product.name)) {
throw new Error(`You already have a product with this name!`);
}
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];
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];
if (division.researched[researchName]) return;
if (division.sciResearch < research.cost)
if (division.researched.has(researchName)) return;
if (division.researchPoints < research.cost)
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
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
// 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.
if (researchName == "Drones - Transport") {
for (const city of Object.values(CityName)) {
const warehouse = division.warehouses[city];
if (!warehouse) continue;
if (Player.corporation) {
// Stores cycles in a "buffer". Processed separately using Engine Counters
warehouse.updateSize(Player.corporation, division);
}
for (const warehouse of getRecordValues(division.warehouses)) {
warehouse.updateSize(corp, division);
}
}
}
@ -503,7 +451,7 @@ export function ExportMaterial(
cityName: CityName,
material: Material,
amt: string,
division?: Industry,
division?: Division,
): void {
// Sanitize amt
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}!`);
}
const exportObj = { ind: divisionName, city: cityName, amt: sanitizedAmt };
material.exp.push(exportObj);
const exportObj = { division: divisionName, city: cityName, amount: sanitizedAmt };
material.exports.push(exportObj);
}
export function CancelExportMaterial(divisionName: string, cityName: string, material: Material, amt: string): void {
for (let i = 0; i < material.exp.length; ++i) {
if (material.exp[i].ind !== divisionName || material.exp[i].city !== cityName || material.exp[i].amt !== amt)
for (let i = 0; i < material.exports.length; ++i) {
if (
material.exports[i].division !== divisionName ||
material.exports[i].city !== cityName ||
material.exports[i].amount !== amt
)
continue;
material.exp.splice(i, 1);
material.exports.splice(i, 1);
break;
}
}
export function LimitProductProduction(product: Product, cityName: string, qty: number): void {
if (qty < 0 || isNaN(qty)) {
product.prdman[cityName][0] = false;
product.prdman[cityName][1] = 0;
export function LimitProductProduction(product: Product, cityName: CityName, quantity: number): void {
if (quantity < 0 || isNaN(quantity)) {
product.cityData[cityName].productionLimit = null;
} else {
product.prdman[cityName][0] = true;
product.prdman[cityName][1] = qty;
product.cityData[cityName].productionLimit = quantity;
}
}
export function LimitMaterialProduction(material: Material, qty: number): void {
if (qty < 0 || isNaN(qty)) {
material.prdman[0] = false;
material.prdman[1] = 0;
export function LimitMaterialProduction(material: Material, quantity: number): void {
if (quantity < 0 || isNaN(quantity)) {
material.productionLimit = null;
} else {
material.prdman[0] = true;
material.prdman[1] = qty;
material.productionLimit = quantity;
}
}

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

@ -15,14 +15,7 @@ export class CorporationState {
// Transition to the next state
nextState(): void {
if (this.state < 0 || this.state >= stateNames.length) {
this.state = 0;
}
++this.state;
if (this.state >= stateNames.length) {
this.state = 0;
}
this.state = (this.state + 1) % stateNames.length;
}
// 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";
export interface Export {
ind: string;
division: string;
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 { ResearchTree } from "./ResearchTree";
import { Corporation } from "./Corporation";
import { getBaseResearchTreeCopy, getProductIndustryResearchTreeCopy } from "./data/BaseResearchTree";
import { MoneyCost } from "./ui/MoneyCost";
import { CorpIndustryData, CorpIndustryName } from "@nsdefs";
import { CorpIndustryData } from "@nsdefs";
import { IndustryType } from "./data/Enums";
import { createFullRecordFromEntries } from "../Types/Record";
export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
export const IndustriesData: Record<IndustryType, CorpIndustryData> = {
[IndustryType.Agriculture]: {
startingCost: 40e9,
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>;
resetIndustryResearchTrees();
export const IndustryResearchTrees = createFullRecordFromEntries(
Object.values(IndustryType).map((industryType) => {
return [
industryType,
IndustriesData[industryType].product ? getProductIndustryResearchTreeCopy() : getBaseResearchTreeCopy(),
];
}),
);
export function resetIndustryResearchTrees() {
Object.values(IndustryType).forEach(
(ind) =>

@ -13,52 +13,51 @@ export class Material {
name: CorpMaterialName;
// Amount of material owned
qty = 0;
stored = 0;
// Material's "quality". Unbounded
qlt = 1;
quality = 1;
// How much demand the Material has in the market, and the range of possible
// values for this "demand"
dmd = 0;
dmdR: number[] = [0, 0];
demand = 0;
demandRange: number[] = [0, 0];
// How much competition there is for this Material in the market, and the range
// of possible values for this "competition"
cmp = 0;
cmpR: number[] = [0, 0];
competition = 0;
competitionRange: number[] = [0, 0];
// Maximum volatility of this Materials stats
mv = 0;
maxVolatility = 0;
// Markup. Determines how high of a price you can charge on the material
// compared to the market price without suffering loss in # of sales
// 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
mku = 0;
markup = 0;
// How much of this material is being bought, sold, imported and produced every second
buy = 0;
sll = 0;
prd = 0;
imp = 0;
buyAmount = 0;
actualSellAmount = 0;
productionAmount = 0;
importAmount = 0;
// Exports of this material to another warehouse/industry
exp: Export[] = [];
exports: Export[] = [];
// Total amount of this material exported in the last cycle
totalExp = 0;
exportedLastCycle = 0;
// Cost / sec to buy this material. AKA Market Price
bCost = 0;
marketPrice = 0;
// Cost / sec to sell this material
sCost: string | number = 0;
/** null if there is no limit set on production. 0 actually limits production to 0. */
productionLimit: number | null = null;
// Flags to keep track of whether production and/or sale of this material is limited
// [Whether production/sale is limited, limit amount]
prdman: [boolean, number] = [false, 0]; // Production
sllman: [boolean, string | number] = [false, 0]; // Sale
// Player inputs for sell price and amount.
desiredSellAmount: string | number = 0;
desiredSellPrice: string | number = 0;
// Flags that signal whether automatic sale pricing through Market TA is enabled
marketTa1 = false;
@ -66,62 +65,62 @@ export class Material {
marketTa2Price = 0;
// Determines the maximum amount of this material that can be sold in one market cycle
maxsll = 0;
maxSellPerCycle = 0;
constructor(params?: IConstructorParams) {
this.name = params?.name ?? materialNames[0];
this.dmd = MaterialInfo[this.name].demandBase;
this.dmdR = MaterialInfo[this.name].demandRange;
this.cmp = MaterialInfo[this.name].competitionBase;
this.cmpR = MaterialInfo[this.name].competitionRange;
this.bCost = MaterialInfo[this.name].baseCost;
this.mv = MaterialInfo[this.name].maxVolatility;
this.mku = MaterialInfo[this.name].baseMarkup;
this.demand = MaterialInfo[this.name].demandBase;
this.demandRange = MaterialInfo[this.name].demandRange;
this.competition = MaterialInfo[this.name].competitionBase;
this.competitionRange = MaterialInfo[this.name].competitionRange;
this.marketPrice = MaterialInfo[this.name].baseCost;
this.maxVolatility = MaterialInfo[this.name].maxVolatility;
this.markup = MaterialInfo[this.name].baseMarkup;
}
getMarkupLimit(): number {
return this.qlt / this.mku;
return this.quality / this.markup;
}
// Process change in demand, competition, and buy cost of this material
processMarket(): void {
// 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
const priceVolatility: number = (Math.random() * this.mv) / 300;
const priceVolatility: number = (Math.random() * this.maxVolatility) / 300;
const priceChange: number = 1 + priceVolatility;
//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;
if (Math.random() < 0.5) {
this.cmp *= compChange;
if (this.cmp > this.cmpR[1]) {
this.cmp = this.cmpR[1];
this.competition *= compChange;
if (this.competition > this.competitionRange[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 {
this.cmp *= 1 / compChange;
if (this.cmp < this.cmpR[0]) {
this.cmp = this.cmpR[0];
this.competition *= 1 / compChange;
if (this.competition < this.competitionRange[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
const dmdVolatility: number = (Math.random() * this.mv) / 100;
const dmdVolatility: number = (Math.random() * this.maxVolatility) / 100;
const dmdChange: number = 1 + dmdVolatility;
if (Math.random() < 0.5) {
this.dmd *= dmdChange;
if (this.dmd > this.dmdR[1]) {
this.dmd = this.dmdR[1];
this.demand *= dmdChange;
if (this.demand > this.demandRange[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 {
this.dmd *= 1 / dmdChange;
if (this.dmd < this.dmdR[0]) {
this.dmd = this.dmdR[0];
this.demand *= 1 / dmdChange;
if (this.demand < this.demandRange[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 { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { Industry } from "./Industry";
import { Division } from "./Division";
import { Corporation } from "./Corporation";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CityName } from "../Enums";
import { createEnumKeyedRecord, getRecordKeys } from "../Types/Record";
interface IParams {
loc?: CityName;
size?: number;
city: CityName;
size: number;
}
export class OfficeSpace {
loc: CityName;
size: number;
city = CityName.Sector12;
size = 1;
maxEne = 100;
maxMor = 100;
maxEnergy = 100;
maxMorale = 100;
avgEne = 75;
avgMor = 75;
avgEnergy = 75;
avgMorale = 75;
avgInt = 75;
avgCha = 75;
totalExp = 0;
avgCre = 75;
avgEff = 75;
avgIntelligence = 75;
avgCharisma = 75;
avgCreativity = 75;
avgEfficiency = 75;
totalEmployees = 0;
totalExperience = 0;
numEmployees = 0;
totalSalary = 0;
autoTea = false;
@ -35,73 +36,49 @@ export class OfficeSpace {
teaPending = false;
partyMult = 1;
employeeProd: Record<EmployeePositions | "total", number> = {
[EmployeePositions.Operations]: 0,
[EmployeePositions.Engineer]: 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,
};
employeeProductionByJob = { total: 0, ...createEnumKeyedRecord(CorpEmployeeJob, () => 0) };
employeeJobs = createEnumKeyedRecord(CorpEmployeeJob, () => 0);
employeeNextJobs = createEnumKeyedRecord(CorpEmployeeJob, () => 0);
constructor(params: IParams = {}) {
this.loc = params.loc ? params.loc : CityName.Sector12;
this.size = params.size ? params.size : 1;
constructor(params: IParams | null = null) {
if (!params) return;
this.city = params.city;
this.size = params.size;
}
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
if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) {
this.hireRandomEmployee(
industry.hasResearch("HRBuddy-Training") ? EmployeePositions.Intern : EmployeePositions.Unassigned,
industry.hasResearch("HRBuddy-Training") ? CorpEmployeeJob.Intern : CorpEmployeeJob.Unassigned,
);
}
// 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;
}
// Process Office properties
this.maxEne = 100;
this.maxMor = 100;
this.maxEnergy = 100;
this.maxMorale = 100;
if (industry.hasResearch("Go-Juice")) this.maxEne += 10;
if (industry.hasResearch("Sti.mu")) this.maxMor += 10;
if (industry.hasResearch("Go-Juice")) this.maxEnergy += 10;
if (industry.hasResearch("Sti.mu")) this.maxMorale += 10;
if (industry.hasResearch("AutoBrew")) this.autoTea = 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 */
let perfMult = 1.002;
if (this.totalEmployees >= 9) {
if (this.numEmployees >= 9) {
perfMult = Math.pow(
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),
marketCycles,
);
@ -111,121 +88,125 @@ export class OfficeSpace {
const reduction = 0.002 * marketCycles;
if (this.autoTea) {
this.avgEne = this.maxEne;
this.avgEnergy = this.maxEnergy;
} else {
// 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) {
this.avgMor = this.maxMor;
this.avgMorale = this.maxMorale;
} else {
// 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;
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.avgMor = Math.max(Math.min(this.avgMor, this.maxMor), corpConstants.minEmployeeDecay);
this.avgEnergy = Math.max(Math.min(this.avgEnergy, this.maxEnergy), corpConstants.minEmployeeDecay);
this.avgMorale = Math.max(Math.min(this.avgMorale, this.maxMorale), corpConstants.minEmployeeDecay);
this.teaPending = false;
this.partyMult = 1;
}
// Get experience increase; unassigned employees do not contribute, interning employees contribute 10x
this.totalExp +=
this.totalExperience +=
0.0015 *
marketCycles *
(this.totalEmployees -
this.employeeJobs[EmployeePositions.Unassigned] +
this.employeeJobs[EmployeePositions.Intern] * 9);
(this.numEmployees -
this.employeeJobs[CorpEmployeeJob.Unassigned] +
this.employeeJobs[CorpEmployeeJob.Intern] * 9);
this.calculateEmployeeProductivity(corporation, industry);
if (this.totalEmployees === 0) {
if (this.numEmployees === 0) {
this.totalSalary = 0;
} else {
this.totalSalary =
corpConstants.employeeSalaryMultiplier *
marketCycles *
this.totalEmployees *
(this.avgInt + this.avgCha + this.totalExp / this.totalEmployees + this.avgCre + this.avgEff);
this.numEmployees *
(this.avgIntelligence +
this.avgCharisma +
this.totalExperience / this.numEmployees +
this.avgCreativity +
this.avgEfficiency);
}
return this.totalSalary;
}
calculateEmployeeProductivity(corporation: Corporation, industry: Industry): void {
const effCre = this.avgCre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(),
effCha = this.avgCha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(),
effInt = this.avgInt * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(),
effEff = this.avgEff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier();
const prodBase = this.avgMor * this.avgEne * 1e-4;
calculateEmployeeProductivity(corporation: Corporation, industry: Division): void {
const effCre = this.avgCreativity * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(),
effCha = this.avgCharisma * corporation.getEmployeeChaMult() * industry.getEmployeeChaMultiplier(),
effInt = this.avgIntelligence * corporation.getEmployeeIntMult() * industry.getEmployeeIntMultiplier(),
effEff = this.avgEfficiency * corporation.getEmployeeEffMult() * industry.getEmployeeEffMultiplier();
const prodBase = this.avgMorale * this.avgEnergy * 1e-4;
let total = 0;
const exp = this.totalExp / this.totalEmployees || 0;
for (const name of Object.keys(this.employeeProd) as (EmployeePositions | "total")[]) {
const exp = this.totalExperience / this.numEmployees || 0;
for (const name of getRecordKeys(this.employeeProductionByJob)) {
let prodMult = 0;
switch (name) {
case EmployeePositions.Operations:
case CorpEmployeeJob.Operations:
prodMult = 0.6 * effInt + 0.1 * effCha + exp + 0.5 * effCre + effEff;
break;
case EmployeePositions.Engineer:
case CorpEmployeeJob.Engineer:
prodMult = effInt + 0.1 * effCha + 1.5 * exp + effEff;
break;
case EmployeePositions.Business:
case CorpEmployeeJob.Business:
prodMult = 0.4 * effInt + effCha + 0.5 * exp;
break;
case EmployeePositions.Management:
case CorpEmployeeJob.Management:
prodMult = 2 * effCha + exp + 0.2 * effCre + 0.7 * effEff;
break;
case EmployeePositions.RandD:
case CorpEmployeeJob.RandD:
prodMult = 1.5 * effInt + 0.8 * exp + effCre + 0.5 * effEff;
break;
case EmployeePositions.Unassigned:
case EmployeePositions.Intern:
case CorpEmployeeJob.Unassigned:
case CorpEmployeeJob.Intern:
case "total":
continue;
default:
console.error(`Invalid employee position: ${name}`);
break;
}
this.employeeProd[name] = this.employeeJobs[name] * prodMult * prodBase;
total += this.employeeProd[name];
this.employeeProductionByJob[name] = this.employeeJobs[name] * prodMult * prodBase;
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 (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) return false;
++this.totalEmployees;
++this.numEmployees;
++this.employeeJobs[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.avgEne = (this.avgEne * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
this.avgMorale = (this.avgMorale * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 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.avgCha = (this.avgCha * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
this.avgCre = (this.avgCre * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
this.avgEff = (this.avgEff * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
this.avgIntelligence = (this.avgIntelligence * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgCharisma = (this.avgCharisma * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgCreativity = (this.avgCreativity * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
this.avgEfficiency = (this.avgEfficiency * this.numEmployees + getRandomInt(50, 100)) / (this.numEmployees + 1);
return true;
}
autoAssignJob(job: EmployeePositions, target: number): boolean {
if (job === EmployeePositions.Unassigned) {
autoAssignJob(job: CorpEmployeeJob, target: number): boolean {
if (job === CorpEmployeeJob.Unassigned) {
throw new Error("internal autoAssignJob function called with EmployeePositions.Unassigned");
}
const diff = target - this.employeeNextJobs[job];
if (diff === 0) return true;
// 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.employeeNextJobs[EmployeePositions.Unassigned] -= diff;
this.employeeNextJobs[CorpEmployeeJob.Unassigned] -= diff;
this.employeeNextJobs[job] = target;
return true;
}
@ -233,11 +214,11 @@ export class OfficeSpace {
}
getTeaCost(): number {
return corpConstants.teaCostPerEmployee * this.totalEmployees;
return corpConstants.teaCostPerEmployee * this.numEmployees;
}
setTea(): boolean {
if (!this.teaPending && !this.autoTea && this.totalEmployees > 0) {
if (!this.teaPending && !this.autoTea && this.numEmployees > 0) {
this.teaPending = true;
return true;
}
@ -245,7 +226,7 @@ export class OfficeSpace {
}
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;
return true;
}
@ -262,10 +243,10 @@ export class OfficeSpace {
const empCopy: [{ data: { mor: number; ene: number; exp: number } }] = value.data.employees;
delete value.data.employees;
const ret = Generic_fromJSON(OfficeSpace, value.data);
ret.totalEmployees = empCopy.length;
ret.avgMor = empCopy.reduce((a, b) => a + b.data.mor, 0) / ret.totalEmployees || 75;
ret.avgEne = empCopy.reduce((a, b) => a + b.data.ene, 0) / ret.totalEmployees || 75;
ret.totalExp = empCopy.reduce((a, b) => a + b.data.exp, 0);
ret.numEmployees = empCopy.length;
ret.avgMorale = empCopy.reduce((a, b) => a + b.data.mor, 0) / ret.numEmployees || 75;
ret.avgEnergy = empCopy.reduce((a, b) => a + b.data.ene, 0) / ret.numEmployees || 75;
ret.totalExperience = empCopy.reduce((a, b) => a + b.data.exp, 0);
return ret;
}
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 { Industry } from "./Industry";
import { Division } from "./Division";
import { IndustriesData } from "./IndustryData";
import { createCityMap } from "../Locations/createCityMap";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CityName } from "../Enums";
import { materialNames } from "./data/Constants";
import { CorpMaterialName } from "@nsdefs";
import { PartialRecord, createEnumKeyedRecord, getRecordEntries, getRecordKeys } from "../Types/Record";
interface IConstructorParams {
name?: string;
demand?: number;
competition?: number;
markup?: 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>>;
name: string;
createCity: CityName;
designInvestment: number;
advertisingInvestment: number;
}
/** A corporation product. Products are shared across the entire division, unlike materials which are per-warehouse */
export class Product {
// Product name
name = "";
/** Name of the product */
name = "DefaultProductName";
// The demand for this Product in the market. Gradually decreases
dmd = 0;
/** Demand for this product, which goes down over time. */
demand = 0;
// How much competition there is in the market for this Product
cmp = 0;
/** Competition for this product */
competition = 0;
// Markup. Affects how high of a price you can charge for this Product
// without suffering a loss in the # of sales
mku = 0;
/** Markup. Affects how high of a price you can charge for this Product
without suffering a loss in the # of sales */
markup = 0;
// Production cost - estimation of how much money it costs to make this Product
pCost = 0;
/** Cost of producing this product if buying its component materials at market price */
productionCost = 0;
// Sell costs
sCost: Record<string, any> = createCityMap<any>(0);
// Variables for handling the creation process of this Product
fin = false; // Whether this Product has finished being created
prog = 0; // Creation progress - A number between 0-100 representing percentage
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
/** Whether the development for this product is finished yet */
finished = false;
developmentProgress = 0; // Creation progress - A number between 0-100 representing percentage
creationCity = CityName.Sector12; // City in which the product is/was being created
designInvestment = 0; // How much money was invested into designing this Product
advertisingInvestment = 0; // How much money was invested into advertising this Product
// The average employee productivity and scientific research across the creation of the Product
creationProd: Record<string, number> = {
[EmployeePositions.Operations]: 0,
[EmployeePositions.Engineer]: 0,
[EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0,
creationJobFactors = {
[CorpEmployeeJob.Operations]: 0,
[CorpEmployeeJob.Engineer]: 0,
[CorpEmployeeJob.Business]: 0,
[CorpEmployeeJob.Management]: 0,
[CorpEmployeeJob.RandD]: 0,
total: 0,
};
// Aggregate score for this Product's 'rating'
// This is based on the stats/properties below. The weighting of the
// stats/properties below differs between different industries
rat = 0;
rating = 0;
// Stats/properties of this Product
qlt = 0;
per = 0;
dur = 0;
rel = 0;
aes = 0;
fea = 0;
/** Stats of the product */
stats = {
quality: 0,
performance: 0,
durability: 0,
reliability: 0,
aesthetics: 0,
features: 0,
};
// Data refers to the production, sale, and quantity of the products
// These values are specific to a city
// For each city, the data is [qty, prod, sell, effRat]
data: Record<string, number[]> = createCityMap<number[]>([0, 0, 0, 0]);
// data that is stored per city
cityData = createEnumKeyedRecord(CityName, () => ({
/** Amount of product stored in warehouse */
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
// Only applies for location-based products like restaurants/hospitals
loc = "";
/** How much warehouse space is occupied per unit of this product */
size = 0;
// How much space 1 unit of the Product takes (in the warehouse)
// Not applicable for all Products
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]);
/** Required materials per unit of this product */
requiredMaterials: PartialRecord<CorpMaterialName, number> = {};
// Flags that signal whether automatic sale pricing through Market TA is enabled
marketTa1 = 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
maxsll = 0;
constructor(params: IConstructorParams = {}) {
this.name = params.name ? params.name : "";
this.dmd = params.demand ? params.demand : 0;
this.cmp = params.competition ? params.competition : 0;
this.mku = params.markup ? params.markup : 0;
this.createCity = params.createCity ? params.createCity : CityName.Sector12;
this.designCost = params.designCost ? params.designCost : 0;
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 : {};
/** Effective number that "MAX" represents in a sell amount */
maxSellAmount = 0;
constructor(params: IConstructorParams | null = null) {
if (!params) return;
this.name = params.name;
this.creationCity = params.createCity;
this.designInvestment = params.designInvestment;
this.advertisingInvestment = params.advertisingInvestment;
}
// Make progress on this product based on current employee productivity
createProduct(marketCycles: number, employeeProd: typeof this.creationProd): void {
if (this.fin) {
return;
}
createProduct(marketCycles: number, employeeProd: typeof Product.prototype.creationJobFactors): void {
if (this.finished) return;
// Designing/Creating a Product is based mostly off Engineers
const opProd = employeeProd[EmployeePositions.Operations];
const engrProd = employeeProd[EmployeePositions.Engineer];
const mgmtProd = employeeProd[EmployeePositions.Management];
const opProd = employeeProd[CorpEmployeeJob.Operations];
const engrProd = employeeProd[CorpEmployeeJob.Engineer];
const mgmtProd = employeeProd[CorpEmployeeJob.Management];
const total = opProd + engrProd + mgmtProd;
if (total <= 0) {
return;
@ -147,119 +122,104 @@ export class Product {
// Management is a multiplier for the production from Engineers
const mgmtFactor = 1 + mgmtProd / (1.2 * total);
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) {
return;
}
this.prog += progress;
for (const pos of Object.keys(employeeProd)) {
this.creationProd[pos] += (employeeProd[pos] * progress) / 100;
this.developmentProgress += progress;
for (const pos of getRecordKeys(employeeProd)) {
this.creationJobFactors[pos] += (employeeProd[pos] * progress) / 100;
}
}
// @param industry - Industry object. Reference to industry that makes this Product
finishProduct(industry: Industry): void {
this.fin = true;
finishProduct(industry: Division): void {
this.finished = true;
// Calculate properties
const totalProd = this.creationProd.total;
const engrRatio = this.creationProd[EmployeePositions.Engineer] / totalProd;
const mgmtRatio = this.creationProd[EmployeePositions.Management] / totalProd;
const rndRatio = this.creationProd[EmployeePositions.RandD] / totalProd;
const opsRatio = this.creationProd[EmployeePositions.Operations] / totalProd;
const busRatio = this.creationProd[EmployeePositions.Business] / totalProd;
const totalProd = this.creationJobFactors.total;
const engrRatio = this.creationJobFactors[CorpEmployeeJob.Engineer] / totalProd;
const mgmtRatio = this.creationJobFactors[CorpEmployeeJob.Management] / totalProd;
const rndRatio = this.creationJobFactors[CorpEmployeeJob.RandD] / totalProd;
const opsRatio = this.creationJobFactors[CorpEmployeeJob.Operations] / 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 sciMult = 1 + Math.pow(industry.sciResearch, industry.sciFac) / 800;
const sciMult = 1 + Math.pow(industry.researchPoints, industry.researchFactor) / 800;
const totalMult = balanceMult * designMult * sciMult;
this.qlt =
this.stats.quality =
totalMult *
(0.1 * this.creationProd[EmployeePositions.Engineer] +
0.05 * this.creationProd[EmployeePositions.Management] +
0.05 * this.creationProd[EmployeePositions.RandD] +
0.02 * this.creationProd[EmployeePositions.Operations] +
0.02 * this.creationProd[EmployeePositions.Business]);
this.per =
(0.1 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.05 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.05 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.02 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.02 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.stats.performance =
totalMult *
(0.15 * this.creationProd[EmployeePositions.Engineer] +
0.02 * this.creationProd[EmployeePositions.Management] +
0.02 * this.creationProd[EmployeePositions.RandD] +
0.02 * this.creationProd[EmployeePositions.Operations] +
0.02 * this.creationProd[EmployeePositions.Business]);
this.dur =
(0.15 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.02 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.02 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.02 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.02 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.stats.durability =
totalMult *
(0.05 * this.creationProd[EmployeePositions.Engineer] +
0.02 * this.creationProd[EmployeePositions.Management] +
0.08 * this.creationProd[EmployeePositions.RandD] +
0.05 * this.creationProd[EmployeePositions.Operations] +
0.05 * this.creationProd[EmployeePositions.Business]);
this.rel =
(0.05 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.02 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.08 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.05 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.05 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.stats.reliability =
totalMult *
(0.02 * this.creationProd[EmployeePositions.Engineer] +
0.08 * this.creationProd[EmployeePositions.Management] +
0.02 * this.creationProd[EmployeePositions.RandD] +
0.05 * this.creationProd[EmployeePositions.Operations] +
0.08 * this.creationProd[EmployeePositions.Business]);
this.aes =
(0.02 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.08 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.02 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.05 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.08 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.stats.aesthetics =
totalMult *
(0.0 * this.creationProd[EmployeePositions.Engineer] +
0.08 * this.creationProd[EmployeePositions.Management] +
0.05 * this.creationProd[EmployeePositions.RandD] +
0.02 * this.creationProd[EmployeePositions.Operations] +
0.1 * this.creationProd[EmployeePositions.Business]);
this.fea =
(0.0 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.08 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.05 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.02 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.1 * this.creationJobFactors[CorpEmployeeJob.Business]);
this.stats.features =
totalMult *
(0.08 * this.creationProd[EmployeePositions.Engineer] +
0.05 * this.creationProd[EmployeePositions.Management] +
0.02 * this.creationProd[EmployeePositions.RandD] +
0.05 * this.creationProd[EmployeePositions.Operations] +
0.05 * this.creationProd[EmployeePositions.Business]);
(0.08 * this.creationJobFactors[CorpEmployeeJob.Engineer] +
0.05 * this.creationJobFactors[CorpEmployeeJob.Management] +
0.02 * this.creationJobFactors[CorpEmployeeJob.RandD] +
0.05 * this.creationJobFactors[CorpEmployeeJob.Operations] +
0.05 * this.creationJobFactors[CorpEmployeeJob.Business]);
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);
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'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)));
this.cmp = getRandomInt(0, 70);
this.competition = getRandomInt(0, 70);
//Calculate the product's required materials
//For now, just set it to be the same as the requirements to make materials
for (const matName of Object.keys(industry.reqMats) as CorpMaterialName[]) {
if (Object.hasOwn(industry.reqMats, matName)) {
const reqMat = industry.reqMats[matName];
if (reqMat === undefined) continue;
this.reqMats[matName] = reqMat;
//Calculate the product's required materials and size
this.size = 0;
for (const [matName, reqQty] of getRecordEntries(industry.requiredMaterials)) {
this.requiredMaterials[matName] = reqQty;
this.size += MaterialInfo[matName].size * reqQty;
}
}
//Calculate the product's size
//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 {
calculateRating(industry: Division): void {
const weights = IndustriesData[industry.type].product?.ratingWeights;
if (!weights) return console.error(`Could not find product rating weights for: ${industry}`);
this.rat = 0;
this.rat += weights.quality ? this.qlt * weights.quality : 0;
this.rat += weights.performance ? this.per * weights.performance : 0;
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;
this.rating = getRecordEntries(weights).reduce(
(total, [statName, weight]) => total + this.stats[statName] * weight,
0,
);
}
// 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.
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);
}
}

@ -1,7 +1,6 @@
import { CorpResearchName } from "@nsdefs";
import { researchNames } from "./data/Constants";
export interface IConstructorParams {
export interface ResearchParams {
name: CorpResearchName;
cost: number;
desc: string;
@ -19,13 +18,13 @@ export interface IConstructorParams {
export class Research {
// 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
cost = 0;
// Description of what the Research does
desc = "";
description = "";
// All possible generic upgrades for the company, in the form of multipliers
advertisingMult = 1;
@ -39,39 +38,20 @@ export class Research {
sciResearchMult = 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.cost = p.cost;
this.desc = p.desc;
if (p.advertisingMult) {
this.advertisingMult = p.advertisingMult;
}
if (p.employeeChaMult) {
this.employeeChaMult = p.employeeChaMult;
}
if (p.employeeCreMult) {
this.employeeCreMult = p.employeeCreMult;
}
if (p.employeeEffMult) {
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;
}
this.description = p.desc;
this.advertisingMult = p.advertisingMult ?? 1;
this.employeeChaMult = p.employeeChaMult ?? 1;
this.employeeCreMult = p.employeeCreMult ?? 1;
this.employeeEffMult = p.employeeEffMult ?? 1;
this.employeeIntMult = p.employeeIntMult ?? 1;
this.productionMult = p.productionMult ?? 1;
this.productProductionMult = p.productProductionMult ?? 1;
this.salesMult = p.salesMult ?? 1;
this.sciResearchMult = p.sciResearchMult ?? 1;
this.storageMult = p.storageMult ?? 1;
}
}

@ -1,17 +1,189 @@
// The Research Map is an object that holds all Corporation Research objects
// as values. They are identified by their names
import { Research, IConstructorParams } from "./Research";
import { researchMetadata } from "./data/ResearchMetadata";
import { Research } from "./Research";
import { CorpResearchName } from "@nsdefs";
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 {
if (ResearchMap[p.name] != null) {
console.warn(`Duplicate Research being defined: ${p.name}`);
}
ResearchMap[p.name] = new Research(p);
}
for (const metadata of researchMetadata) {
addResearch(metadata);
}
"Market-TA.I": new Research({
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.",
}),
"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
// to the corresponding Research object using the ResearchMap
import { CorpResearchName } from "@nsdefs";
import { researchNames } from "./data/Constants";
import { Research } from "./Research";
import { ResearchMap } from "./ResearchMap";
interface IConstructorParams {
children?: Node[];
cost: number;
text: CorpResearchName;
researchName: CorpResearchName;
parent?: Node | null;
}
@ -34,14 +33,10 @@ export class Node {
parent: Node | null = null;
// Name of the Research held in this Node
text: CorpResearchName;
researchName: CorpResearchName;
constructor(p: IConstructorParams = { cost: 0, text: researchNames[0] }) {
if (ResearchMap[p.text] == null) {
throw new Error(`Invalid Research name used when constructing ResearchTree Node: ${p.text}`);
}
this.text = p.text;
constructor(p: IConstructorParams) {
this.researchName = p.researchName;
this.cost = p.cost;
if (p.children && p.children.length > 0) {
@ -59,16 +54,16 @@ export class Node {
}
// Recursive function for finding a Node with the specified text
findNode(text: string): Node | null {
findNode(name: CorpResearchName): Node | null {
// Is this the Node?
if (this.text === text) {
if (this.researchName === name) {
return this;
}
// Recursively search children
let res = null;
for (let i = 0; i < this.children.length; ++i) {
res = this.children[i].findNode(text);
res = this.children[i].findNode(name);
if (res != null) {
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"
export class ResearchTree {
// Object containing names of all acquired Research by name
researched: Record<string, boolean> = {};
researched = new Set<CorpResearchName>();
// Root Node
root: Node | null = null;
@ -107,7 +102,7 @@ export class ResearchTree {
continue;
}
res.push(node.text);
res.push(node.researchName);
for (let i = 0; i < node.children.length; ++i) {
queue.push(node.children[i]);
}
@ -175,11 +170,11 @@ export class ResearchTree {
continue;
}
const research: Research | null = ResearchMap[node.text];
const research: Research | null = ResearchMap[node.researchName];
// Safety checks
if (research == null) {
console.warn(`Invalid Research name in node: ${node.text}`);
console.warn(`Invalid Research name in node: ${node.researchName}`);
continue;
}
@ -197,7 +192,7 @@ export class ResearchTree {
storageMult: research.storageMult,
}[propName] ?? null;
if (mult == null) {
if (mult === null) {
console.warn(`Invalid propName specified in ResearchTree.getMultiplierHelper: ${propName}`);
continue;
}
@ -230,13 +225,11 @@ export class ResearchTree {
queue.push(this.root);
while (queue.length !== 0) {
const node: Node | undefined = queue.shift();
if (node == null) {
continue;
}
if (!node) continue;
if (node.text === name) {
if (node.researchName === name) {
node.researched = true;
this.researched[name] = true;
this.researched.add(name);
return;
}

@ -1,18 +1,18 @@
import { Material } from "./Material";
import { Corporation } from "./Corporation";
import { Industry } from "./Industry";
import { Division } from "./Division";
import { MaterialInfo } from "./MaterialInfo";
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { CityName } from "../Enums";
import { CorpMaterialName } from "@nsdefs";
import { materialNames } from "./data/Constants";
import { createFullRecordFromEntries, getRecordEntries } from "../Types/Record";
import { CorpUnlockName } from "./data/Enums";
import { Player } from "@player";
interface IConstructorParams {
corp?: Corporation;
industry?: Industry;
loc?: CityName;
size?: number;
division: Division;
loc: CityName;
size: number;
}
export class Warehouse {
@ -20,13 +20,13 @@ export class Warehouse {
level = 1;
// City that this Warehouse is in
loc: CityName;
city = CityName.Sector12;
// 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
size: number;
size = 0;
// Amount of space currently used by warehouse
sizeUsed = 0;
@ -35,30 +35,22 @@ export class Warehouse {
smartSupplyEnabled = false;
// 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.
// The production tracked by smart supply is always based on the previous cycle,
// so it will always trail the "true" production by 1 cycle
smartSupplyStore = 0;
constructor(params: IConstructorParams = {}) {
this.loc = params.loc ? params.loc : CityName.Sector12;
this.size = params.size ? params.size : 0;
this.materials = {} as Record<CorpMaterialName, Material>;
this.smartSupplyOptions = {} as Record<CorpMaterialName, string>;
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);
}
constructor(params: IConstructorParams | null = null) {
const corp = Player.corporation;
if (!corp || params === null) return;
this.city = params.loc;
this.size = params.size;
this.updateSize(corp, params.division);
// 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;
}
}
@ -66,21 +58,16 @@ export class Warehouse {
// Re-calculate how much space is being used by this Warehouse
updateMaterialSizeUsed(): void {
this.sizeUsed = 0;
for (const matName of Object.values(materialNames)) {
const mat = this.materials[matName];
this.sizeUsed += mat.qty * MaterialInfo[matName].size;
for (const [matName, mat] of getRecordEntries(this.materials)) {
this.sizeUsed += mat.stored * MaterialInfo[matName].size;
}
if (this.sizeUsed > this.size) {
console.warn("Warehouse size used greater than capacity, something went wrong");
}
}
updateSize(corporation: Corporation, industry: Industry): void {
try {
this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier();
} catch (e: unknown) {
exceptionAlert(e);
}
updateSize(corporation: Corporation, division: Division): void {
this.size = this.level * 100 * corporation.getStorageMultiplier() * division.getStorageMultiplier();
}
// 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.
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);
}
}

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

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

@ -1,100 +1,85 @@
export interface CorporationUnlockUpgrade {
index: number;
price: number;
name: string;
desc: string;
}
import { CorpUnlockName } from "./Enums";
export enum CorporationUnlockUpgradeIndex {
Export = 0,
SmartSupply = 1,
MarketResearchDemand = 2,
MarketDataCompetition = 3,
VeChain = 4,
ShadyAccounting = 5,
GovernmentPartnership = 6,
WarehouseAPI = 7,
OfficeAPI = 8,
export interface CorpUnlock {
name: CorpUnlockName;
price: number;
desc: string;
}
// Corporation Unlock Upgrades
// 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
[CorporationUnlockUpgradeIndex.Export]: {
index: 0,
[CorpUnlockName.Export]: {
name: CorpUnlockName.Export,
price: 20e9,
name: "Export",
desc:
"Develop infrastructure to export your materials to your other facilities. " +
"This allows you to move materials around between different divisions and cities.",
},
//Lets you buy exactly however many required materials you need for production
[CorporationUnlockUpgradeIndex.SmartSupply]: {
index: 1,
[CorpUnlockName.SmartSupply]: {
name: CorpUnlockName.SmartSupply,
price: 25e9,
name: "Smart Supply",
desc:
"Use advanced AI to anticipate your supply needs. " +
"This allows you to purchase exactly however many materials you need for production.",
},
//Displays each material/product's demand
[CorporationUnlockUpgradeIndex.MarketResearchDemand]: {
index: 2,
[CorpUnlockName.MarketResearchDemand]: {
name: CorpUnlockName.MarketResearchDemand,
price: 5e9,
name: "Market Research - Demand",
desc:
"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.",
},
//Display's each material/product's competition
[CorporationUnlockUpgradeIndex.MarketDataCompetition]: {
index: 3,
[CorpUnlockName.MarketDataCompetition]: {
name: CorpUnlockName.MarketDataCompetition,
price: 5e9,
name: "Market Data - Competition",
desc:
"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 " +
"every material and product.",
},
[CorporationUnlockUpgradeIndex.VeChain]: {
index: 4,
[CorpUnlockName.VeChain]: {
name: CorpUnlockName.VeChain,
price: 10e9,
name: "VeChain",
desc:
"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 " +
"Corporation.",
},
[CorporationUnlockUpgradeIndex.ShadyAccounting]: {
index: 5,
[CorpUnlockName.ShadyAccounting]: {
name: CorpUnlockName.ShadyAccounting,
price: 500e12,
name: "Shady Accounting",
desc:
"Utilize unscrupulous accounting practices and pay off government officials to save money " +
"on taxes. This reduces the dividend tax rate by 5%.",
},
[CorporationUnlockUpgradeIndex.GovernmentPartnership]: {
index: 6,
[CorpUnlockName.GovernmentPartnership]: {
name: CorpUnlockName.GovernmentPartnership,
price: 2e15,
name: "Government Partnership",
desc:
"Help national governments further their agendas in exchange for lowered taxes. " +
"This reduces the dividend tax rate by 10%",
},
[CorporationUnlockUpgradeIndex.WarehouseAPI]: {
index: 7,
[CorpUnlockName.WarehouseAPI]: {
name: CorpUnlockName.WarehouseAPI,
price: 50e9,
name: "Warehouse API",
desc: "Enables the warehouse API.",
},
[CorporationUnlockUpgradeIndex.OfficeAPI]: {
index: 8,
[CorpUnlockName.OfficeAPI]: {
name: CorpUnlockName.OfficeAPI,
price: 50e9,
name: "Office API",
desc: "Enables the office API.",
},
};

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

@ -16,7 +16,7 @@ export enum IndustryType {
RealEstate = "Real Estate",
}
export enum EmployeePositions {
export enum CorpEmployeeJob {
Operations = "Operations",
Engineer = "Engineer",
Business = "Business",
@ -25,3 +25,28 @@ export enum EmployeePositions {
Intern = "Intern",
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 { CorporationUpgrade } from "./data/CorporationUpgrades";
import { CorpUpgrade } from "./data/CorporationUpgrades";
export function calculateUpgradeCost(corporation: Corporation, upgrade: CorporationUpgrade, amount: number): number {
if (amount < 1) return 0;
export function calculateUpgradeCost(corporation: Corporation, upgrade: CorpUpgrade, amount: PositiveInteger): number {
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 cost = (baseCost * (1 - Math.pow(priceMult, amount))) / (1 - priceMult);
return cost;
}
// There ought to be a more clever mathematical solution for this
export function calculateMaxAffordableUpgrade(
corporation: Corporation,
upgrade: CorporationUpgrade,
amount: number | "MAX",
): number {
if (amount != "MAX") {
if (amount === 0) return 0;
if (calculateUpgradeCost(corporation, upgrade, amount) < corporation.funds) return amount;
}
corp: Corporation,
upgrade: CorpUpgrade,
amount: PositiveInteger | "MAX",
): 0 | PositiveInteger {
if (amount !== "MAX" && calculateUpgradeCost(corp, upgrade, amount) < corp.funds) return amount;
if (calculateUpgradeCost(corp, upgrade, 1 as PositiveInteger) > corp.funds) return 0;
// We can definitely afford 1 of the upgrade
let n = 1;
while (
calculateUpgradeCost(corporation, upgrade, n * 2) < corporation.funds &&
(amount != "MAX" ? n < amount : true)
) {
n *= 2;
// Multiply by 2 until we can't afford it anymore
while (calculateUpgradeCost(corp, upgrade, (n * 2) as PositiveInteger) <= corp.funds) n *= 2;
let tooHigh = n * 2;
while (tooHigh - n > 1) {
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) {
if (calculateUpgradeCost(corporation, upgrade, n + i) < corporation.funds) n += i;
}
return amount === "MAX" ? n : Math.min(n, amount);
return n as PositiveInteger;
}

@ -8,6 +8,7 @@ import { useDivision } from "./Context";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { CityName } from "../../Enums";
import { getRecordKeys } from "../../Types/Record";
interface IProps {
city: CityName | "Expand";
@ -23,7 +24,7 @@ export function CityTabs(props: IProps): React.ReactElement {
mainContent = <ExpandNewCity cityStateSetter={setCity} />;
} else {
const office = division.offices[city];
if (office === 0) {
if (!office) {
setCity(CityName.Sector12);
return <></>;
}
@ -31,7 +32,7 @@ export function CityTabs(props: IProps): React.ReactElement {
<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 {
setCity(tab);
}
@ -40,7 +41,8 @@ export function CityTabs(props: IProps): React.ReactElement {
<>
<Tabs variant="fullWidth" value={city} onChange={handleChange} sx={{ maxWidth: "65vw" }}>
{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"} />}
</Tabs>

@ -1,11 +1,11 @@
import React, { useContext } from "react";
import { Corporation } from "../Corporation";
import { Industry } from "../Industry";
import { Division } from "../Division";
export const Context = {
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 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
import React, { useState } from "react";
import { MainPanel } from "./MainPanel";
import { IndustryType } from "../data/Enums";
import { ExpandIndustryTab } from "./ExpandIndustryTab";
import { Player } from "@player";
import { Context } from "./Context";
@ -22,16 +21,13 @@ export function CorporationRoot(): React.ReactElement {
setDivisionName(tab);
}
const canExpand =
Object.values(IndustryType).filter(
(industryType) => corporation.divisions.find((division) => division.type === industryType) === undefined,
).length > 0;
const canExpand = corporation.divisions.size < corporation.maxDivisions;
return (
<Context.Corporation.Provider value={corporation}>
<Tabs variant="scrollable" value={divisionName} onChange={handleChange} sx={{ maxWidth: "65vw" }} scrollButtons>
<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} />
))}
{canExpand && <Tab label={"Expand"} value={-1} />}

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

@ -1,13 +1,13 @@
import React, { useState } from "react";
import * as corpConstants from "../data/Constants";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { NewCity } from "../Actions";
import { purchaseOffice } from "../Actions";
import { MoneyCost } from "./MoneyCost";
import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import Button from "@mui/material/Button";
import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
import { CityName } from "../../Enums";
interface IProps {
@ -17,10 +17,10 @@ interface IProps {
export function ExpandNewCity(props: IProps): React.ReactElement {
const corp = useCorporation();
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 disabled = corp.funds < corpConstants.officeInitialCost;
const disabledText = corp.funds < corpConstants.officeInitialCost ? "Insufficient corporation funds" : "";
function onCityChange(event: SelectChangeEvent): void {
setCity(event.target.value as CityName);
@ -28,7 +28,7 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
function expand(): void {
try {
NewCity(corp, division, city);
purchaseOffice(corp, division, city);
} catch (err) {
dialogBoxCreate(err + "");
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{" "}
<MoneyCost money={corpConstants.officeInitialCost} corp={corp} />
</Typography>
<Select
endAdornment={
<Button onClick={expand} disabled={disabled}>
Confirm
</Button>
}
value={city}
onChange={onCityChange}
>
<Select value={city} onChange={onCityChange}>
{possibleCities.map((cityName: string) => (
<MenuItem key={cityName} value={cityName}>
{cityName}
</MenuItem>
))}
</Select>
<ButtonWithTooltip onClick={expand} disabledTooltip={disabledText}>
Confirm
</ButtonWithTooltip>
</>
);
}

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

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

@ -3,7 +3,7 @@
import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace";
import { EmployeePositions } from "../data/Enums";
import { CorpUnlockName, CorpEmployeeJob } from "../data/Enums";
import { BuyTea } from "../Actions";
import { MoneyCost } from "./MoneyCost";
@ -15,7 +15,7 @@ import { Money } from "../../ui/React/Money";
import { useCorporation, useDivision } from "./Context";
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 Paper from "@mui/material/Paper";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
@ -35,7 +35,7 @@ interface IProps {
interface IAutoAssignProps {
office: OfficeSpace;
job: EmployeePositions;
job: CorpEmployeeJob;
desc: string;
rerender: () => void;
}
@ -53,7 +53,7 @@ function EmployeeCount(props: { num: number; next: number }): React.ReactElement
function AutoAssignJob(props: IAutoAssignProps): React.ReactElement {
const currJob = props.office.employeeJobs[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 {
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 {
const corp = useCorporation();
const division = useDivision();
const vechain = corp.unlockUpgrades[4] === 1; // Has Vechain upgrade
const currUna = props.office.employeeJobs[EmployeePositions.Unassigned];
const nextUna = props.office.employeeNextJobs[EmployeePositions.Unassigned];
const currUna = props.office.employeeJobs[CorpEmployeeJob.Unassigned];
const nextUna = props.office.employeeNextJobs[CorpEmployeeJob.Unassigned];
return (
<Table padding="none">
@ -115,7 +114,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<Typography>Avg Employee Morale:</Typography>
</TableCell>
<TableCell align="right">
<Typography>{formatCorpStat(props.office.avgMor)}</Typography>
<Typography>{formatCorpStat(props.office.avgMorale)}</Typography>
</TableCell>
</TableRow>
<TableRow>
@ -123,7 +122,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<Typography>Avg Employee Energy:</Typography>
</TableCell>
<TableCell align="right">
<Typography>{formatCorpStat(props.office.avgEne)}</Typography>
<Typography>{formatCorpStat(props.office.avgEnergy)}</Typography>
</TableCell>
</TableRow>
<TableRow>
@ -131,7 +130,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<Typography>Avg Employee Experience:</Typography>
</TableCell>
<TableCell align="right">
<Typography>{formatCorpStat(props.office.totalExp / props.office.totalEmployees || 0)}</Typography>
<Typography>{formatCorpStat(props.office.totalExperience / props.office.numEmployees || 0)}</Typography>
</TableCell>
</TableRow>
<TableRow>
@ -144,7 +143,7 @@ function AutoManagement(props: IProps): React.ReactElement {
</Typography>
</TableCell>
</TableRow>
{vechain && (
{corp.unlocks.has(CorpUnlockName.VeChain) && (
<>
<TableRow>
<TableCell>
@ -201,14 +200,14 @@ function AutoManagement(props: IProps): React.ReactElement {
<AutoAssignJob
rerender={props.rerender}
office={props.office}
job={EmployeePositions.Operations}
job={CorpEmployeeJob.Operations}
desc={"Manages supply chain operations. Improves the amount of Materials and Products you produce."}
/>
<AutoAssignJob
rerender={props.rerender}
office={props.office}
job={EmployeePositions.Engineer}
job={CorpEmployeeJob.Engineer}
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)."
}
@ -217,14 +216,14 @@ function AutoManagement(props: IProps): React.ReactElement {
<AutoAssignJob
rerender={props.rerender}
office={props.office}
job={EmployeePositions.Business}
job={CorpEmployeeJob.Business}
desc={"Handles sales and finances. Improves the amount of Materials and Products you can sell."}
/>
<AutoAssignJob
rerender={props.rerender}
office={props.office}
job={EmployeePositions.Management}
job={CorpEmployeeJob.Management}
desc={
"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
rerender={props.rerender}
office={props.office}
job={EmployeePositions.RandD}
job={CorpEmployeeJob.RandD}
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)."
}
@ -242,7 +241,7 @@ function AutoManagement(props: IProps): React.ReactElement {
<AutoAssignJob
rerender={props.rerender}
office={props.office}
job={EmployeePositions.Intern}
job={CorpEmployeeJob.Intern}
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."
}
@ -260,32 +259,38 @@ export function IndustryOffice(props: IProps): React.ReactElement {
function autohireEmployeeButtonOnClick(): void {
if (props.office.atCapacity()) return;
props.office.hireRandomEmployee(EmployeePositions.Unassigned);
props.office.hireRandomEmployee(CorpEmployeeJob.Unassigned);
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 (
<Paper>
<Typography>Office Space</Typography>
<Typography>
Size: {props.office.totalEmployees} / {props.office.size} employees
Size: {props.office.numEmployees} / {props.office.size} employees
</Typography>
<Box sx={{ display: "grid", gridTemplateColumns: "1fr", width: "fit-content" }}>
<Box sx={{ gridTemplateColumns: "repeat(3, 1fr)" }}>
<Tooltip title={<Typography>Hires an employee</Typography>}>
<span>
<Button disabled={props.office.atCapacity()} onClick={autohireEmployeeButtonOnClick}>
<ButtonWithTooltip disabledTooltip={hireEmployeeDisabledText} onClick={autohireEmployeeButtonOnClick}>
Hire Employee
</Button>
</span>
</Tooltip>
<Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}>
<span>
<Button disabled={corp.funds < 0} onClick={() => setUpgradeOfficeSizeOpen(true)}>
</ButtonWithTooltip>
<ButtonWithTooltip
normalTooltip={"Upgrade the office's size so that it can hold more employees!"}
onClick={() => setUpgradeOfficeSizeOpen(true)}
>
Upgrade size
</Button>
</span>
</Tooltip>
</ButtonWithTooltip>
<UpgradeOfficeSizeModal
rerender={props.rerender}
office={props.office}
@ -294,44 +299,32 @@ export function IndustryOffice(props: IProps): React.ReactElement {
/>
{!division.hasResearch("AutoBrew") && (
<>
<Tooltip
title={
<Typography>
Provide your employees with tea, increasing their energy by half the difference to 100%, plus 1.5%
</Typography>
<ButtonWithTooltip
normalTooltip={
"Provide your employees with tea, increasing their energy by half the difference to 100%, plus 1.5%"
}
>
<span>
<Button
disabled={corp.funds < props.office.getTeaCost() || props.office.teaPending}
disabledTooltip={teaDisabledText}
onClick={() => BuyTea(corp, props.office)}
>
{props.office.teaPending ? (
"Buying tea..."
"Buying Tea"
) : (
<span>
<>
Buy Tea - <MoneyCost money={props.office.getTeaCost()} corp={corp} />
</span>
)}
</Button>
</span>
</Tooltip>
</>
)}
</ButtonWithTooltip>
)}
{!division.hasResearch("AutoPartyManager") && (
<>
<Tooltip title={<Typography>Throw an office party to increase your employee's morale</Typography>}>
<span>
<Button
disabled={corp.funds < 0 || props.office.partyMult > 1}
<ButtonWithTooltip
normalTooltip={"Throw an office party to increase your employees' morale"}
disabledTooltip={partyDisabledText}
onClick={() => setThrowPartyOpen(true)}
>
{props.office.partyMult > 1 ? "Throwing Party..." : "Throw Party"}
</Button>
</span>
</Tooltip>
</ButtonWithTooltip>
<ThrowPartyModal
rerender={props.rerender}
office={props.office}

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

@ -1,20 +1,20 @@
import React from "react";
import { Industry } from "../Industry";
import { Division } from "../Division";
import { MathJax } from "better-react-mathjax";
import { CorpMaterialName } from "@nsdefs";
interface IProps {
division: Industry;
division: Division;
}
export function IndustryProductEquation(props: IProps): React.ReactElement {
const reqs = [];
for (const reqMat of Object.keys(props.division.reqMats) as CorpMaterialName[]) {
const reqAmt = props.division.reqMats[reqMat];
for (const reqMat of Object.keys(props.division.requiredMaterials) as CorpMaterialName[]) {
const reqAmt = props.division.requiredMaterials[reqMat];
if (reqAmt === undefined) continue;
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) {
prod.push("Products");
}

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

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

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

@ -22,6 +22,7 @@ import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import { LimitMaterialProductionModal } from "./modals/LimitMaterialProductionModal";
import { CityName } from "../../Enums";
import { CorpUnlockName } from "../data/Enums";
interface IMaterialProps {
warehouse: Warehouse;
@ -50,29 +51,33 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
}
// 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
// marked with flashing-red lights
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
const purchaseButtonText = `Buy (${formatBigNumber(mat.buy)})`;
const purchaseButtonText = `Buy (${formatBigNumber(mat.buyAmount)})`;
// Sell material button
let sellButtonText: JSX.Element;
if (mat.sllman[0]) {
if (isString(mat.sllman[1])) {
if (mat.desiredSellAmount) {
if (isString(mat.desiredSellAmount)) {
sellButtonText = (
<>
Sell ({formatBigNumber(mat.sll)}/{mat.sllman[1]})
Sell ({formatBigNumber(mat.actualSellAmount)}/{mat.desiredSellAmount[1]})
</>
);
} else {
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) {
sellButtonText = (
<>
{sellButtonText} @ <Money money={mat.bCost + markupLimit} />
{sellButtonText} @ <Money money={mat.marketPrice + markupLimit} />
</>
);
} else if (mat.sCost) {
if (isString(mat.sCost)) {
const sCost = mat.sCost.replace(/MP/g, mat.bCost + "");
} else if (mat.desiredSellPrice) {
if (isString(mat.desiredSellPrice)) {
const sCost = mat.desiredSellPrice.replace(/MP/g, mat.marketPrice + "");
sellButtonText = (
<>
{sellButtonText} @ <Money money={eval(sCost)} />
@ -100,7 +105,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
} else {
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
let limitMaterialButtonText = "Limit Material";
if (mat.prdman[0]) {
limitMaterialButtonText += " (" + formatCorpStat(mat.prdman[1]) + ")";
if (mat.productionLimit !== null) {
limitMaterialButtonText += " (" + formatCorpStat(mat.productionLimit) + ")";
}
return (
@ -122,20 +127,28 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
<Tooltip
title={
<Typography>
Buy: {mat.buy >= 1e33 ? mat.buy.toExponential(3) : formatBigNumber(mat.buy)} <br />
Prod: {formatBigNumber(mat.prd)} <br />
Sell: {formatBigNumber(mat.sll)} <br />
Export: {formatBigNumber(mat.totalExp)} <br />
Import: {formatBigNumber(mat.imp)}
{corp.unlockUpgrades[2] === 1 && <br />}
{corp.unlockUpgrades[2] === 1 && "Demand: " + formatCorpStat(mat.dmd)}
{corp.unlockUpgrades[3] === 1 && <br />}
{corp.unlockUpgrades[3] === 1 && "Competition: " + formatCorpStat(mat.cmp)}
Buy: {mat.buyAmount >= 1e33 ? mat.buyAmount.toExponential(3) : formatBigNumber(mat.buyAmount)} <br />
Prod: {formatBigNumber(mat.productionAmount)} <br />
Sell: {formatBigNumber(mat.actualSellAmount)} <br />
Export: {formatBigNumber(mat.exportedLastCycle)} <br />
Import: {formatBigNumber(mat.importAmount)}
{corp.unlocks.has(CorpUnlockName.MarketResearchDemand) && (
<>
<br />
Demand: {formatCorpStat(mat.demand)}
</>
)}
{corp.unlocks.has(CorpUnlockName.MarketDataCompetition) && (
<>
<br />
Competition: {formatCorpStat(mat.competition)}
</>
)}
</Typography>
}
>
<Typography>
{mat.name}: {formatBigNumber(mat.qty)} (
{mat.name}: {formatBigNumber(mat.stored)} (
{totalGain >= 1e33 ? totalGain.toExponential(3) : formatBigNumber(totalGain)}/s)
</Typography>
</Tooltip>
@ -146,12 +159,12 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
</Typography>
}
>
<Typography>MP: {formatMoney(mat.bCost)}</Typography>
<Typography>MP: {formatMoney(mat.marketPrice)}</Typography>
</Tooltip>
<Tooltip
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>
</Box>
@ -167,13 +180,11 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
mat={mat}
warehouse={warehouse}
open={purchaseMaterialOpen}
disablePurchaseLimit={
props.warehouse.smartSupplyEnabled && Object.keys(division.reqMats).includes(props.mat.name)
}
disablePurchaseLimit={props.warehouse.smartSupplyEnabled && props.mat.name in division.requiredMaterials}
onClose={() => setPurchaseMaterialOpen(false)}
/>
{corp.unlockUpgrades[0] === 1 && (
{corp.unlocks.has(CorpUnlockName.Export) && (
<>
<Button onClick={() => setExportOpen(true)}>Export</Button>
@ -182,7 +193,7 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
)}
<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)}
>
{sellButtonText}

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