CORP: rework (#428)

* corp overhaul: Corp production quality now depends on materials

* corp overhaul: Product price can be set separately for each city

* corp overhaul: export uses relatives

* corp overhaul: ignore energy in quality

* corp overhaul: getProduct() is city dependant

* corp overhaul: bulkbuy available from start

* corp overhaul: add multibuy for leveled upgrads

* corp overhaul: changes to UI

* corp overhaul: base quality 1, reqmat changes

* corp overhaul: puchased material quality is 1

* corp overhaul: get rid of the text box from ta2

* corp overhaul: sold shares limitations

* corp overhaul: coffee -> tea, training -> intern

* corp overhaul: smartsupply has multiple options

* corp overhaul: restart, literature, investore, ui

* corp overhaul: nerf advertising

* corp overhaul: bunch of stuff
This commit is contained in:
Mughur 2023-03-18 03:12:43 +02:00 committed by GitHub
parent 5ffefcca80
commit 1f98eecb57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 1045 additions and 570 deletions

6
.gitignore vendored

@ -20,9 +20,9 @@ dist/assets
dist/*.worker.* dist/*.worker.*
# tmp folder for build and electron # tmp folder for build and electron
.app .app
.package .package
.build .build
# editor files # editor files
.vscode .vscode

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

@ -19,7 +19,6 @@ interface CorpConstants
| [baseProductProfitMult](./bitburner.corpconstants.baseproductprofitmult.md) | | number | | | [baseProductProfitMult](./bitburner.corpconstants.baseproductprofitmult.md) | | number | |
| [bribeAmountPerReputation](./bitburner.corpconstants.bribeamountperreputation.md) | | number | | | [bribeAmountPerReputation](./bitburner.corpconstants.bribeamountperreputation.md) | | number | |
| [bribeThreshold](./bitburner.corpconstants.bribethreshold.md) | | number | | | [bribeThreshold](./bitburner.corpconstants.bribethreshold.md) | | number | |
| [coffeeCostPerEmployee](./bitburner.corpconstants.coffeecostperemployee.md) | | number | |
| [dividendMaxRate](./bitburner.corpconstants.dividendmaxrate.md) | | number | | | [dividendMaxRate](./bitburner.corpconstants.dividendmaxrate.md) | | number | |
| [employeeRaiseAmount](./bitburner.corpconstants.employeeraiseamount.md) | | number | | | [employeeRaiseAmount](./bitburner.corpconstants.employeeraiseamount.md) | | number | |
| [employeeSalaryMultiplier](./bitburner.corpconstants.employeesalarymultiplier.md) | | number | Conversion factor for employee stats to initial salary | | [employeeSalaryMultiplier](./bitburner.corpconstants.employeesalarymultiplier.md) | | number | Conversion factor for employee stats to initial salary |
@ -31,7 +30,7 @@ interface CorpConstants
| [marketCyclesPerEmployeeRaise](./bitburner.corpconstants.marketcyclesperemployeeraise.md) | | number | | | [marketCyclesPerEmployeeRaise](./bitburner.corpconstants.marketcyclesperemployeeraise.md) | | number | |
| [materialNames](./bitburner.corpconstants.materialnames.md) | | [CorpMaterialName](./bitburner.corpmaterialname.md)<!-- -->\[\] | Names of all materials | | [materialNames](./bitburner.corpconstants.materialnames.md) | | [CorpMaterialName](./bitburner.corpmaterialname.md)<!-- -->\[\] | Names of all materials |
| [maxProductsBase](./bitburner.corpconstants.maxproductsbase.md) | | number | Max products for a division without upgrades | | [maxProductsBase](./bitburner.corpconstants.maxproductsbase.md) | | number | Max products for a division without upgrades |
| [minEmployeeDecay](./bitburner.corpconstants.minemployeedecay.md) | | number | The minimum decay value for happiness/morale/energy | | [minEmployeeDecay](./bitburner.corpconstants.minemployeedecay.md) | | number | The minimum decay value for morale/energy |
| [officeInitialCost](./bitburner.corpconstants.officeinitialcost.md) | | number | | | [officeInitialCost](./bitburner.corpconstants.officeinitialcost.md) | | number | |
| [officeInitialSize](./bitburner.corpconstants.officeinitialsize.md) | | number | | | [officeInitialSize](./bitburner.corpconstants.officeinitialsize.md) | | number | |
| [officeSizeUpgradeCostBase](./bitburner.corpconstants.officesizeupgradecostbase.md) | | number | | | [officeSizeUpgradeCostBase](./bitburner.corpconstants.officesizeupgradecostbase.md) | | number | |
@ -42,6 +41,7 @@ interface CorpConstants
| [sellSharesCooldown](./bitburner.corpconstants.sellsharescooldown.md) | | number | Cooldown for selling shares in game cycles (1 game cycle = 200ms) | | [sellSharesCooldown](./bitburner.corpconstants.sellsharescooldown.md) | | number | Cooldown for selling shares in game cycles (1 game cycle = 200ms) |
| [sharesPerPriceUpdate](./bitburner.corpconstants.sharesperpriceupdate.md) | | number | When selling large number of shares, price is dynamically updated for every batch of this amount | | [sharesPerPriceUpdate](./bitburner.corpconstants.sharesperpriceupdate.md) | | number | When selling large number of shares, price is dynamically updated for every batch of this amount |
| [stateNames](./bitburner.corpconstants.statenames.md) | | [CorpStateName](./bitburner.corpstatename.md)<!-- -->\[\] | Names of all corporation game states | | [stateNames](./bitburner.corpconstants.statenames.md) | | [CorpStateName](./bitburner.corpstatename.md)<!-- -->\[\] | Names of all corporation game states |
| [teaCostPerEmployee](./bitburner.corpconstants.teacostperemployee.md) | | number | |
| [unlockNames](./bitburner.corpconstants.unlocknames.md) | | [CorpUnlockName](./bitburner.corpunlockname.md)<!-- -->\[\] | Names of all one-time corporation-wide unlocks | | [unlockNames](./bitburner.corpconstants.unlocknames.md) | | [CorpUnlockName](./bitburner.corpunlockname.md)<!-- -->\[\] | Names of all one-time corporation-wide unlocks |
| [upgradeNames](./bitburner.corpconstants.upgradenames.md) | | [CorpUpgradeName](./bitburner.corpupgradename.md)<!-- -->\[\] | Names of all corporation-wide upgrades | | [upgradeNames](./bitburner.corpconstants.upgradenames.md) | | [CorpUpgradeName](./bitburner.corpupgradename.md)<!-- -->\[\] | Names of all corporation-wide upgrades |
| [warehouseInitialCost](./bitburner.corpconstants.warehouseinitialcost.md) | | number | | | [warehouseInitialCost](./bitburner.corpconstants.warehouseinitialcost.md) | | number | |

@ -4,7 +4,7 @@
## CorpConstants.minEmployeeDecay property ## CorpConstants.minEmployeeDecay property
The minimum decay value for happiness/morale/energy The minimum decay value for morale/energy
**Signature:** **Signature:**

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

@ -14,6 +14,6 @@ type CorpEmployeePosition =
| "Business" | "Business"
| "Management" | "Management"
| "Research & Development" | "Research & Development"
| "Training" | "Intern"
| "Unassigned"; | "Unassigned";
``` ```

@ -9,12 +9,13 @@
```typescript ```typescript
type CorpIndustryName = type CorpIndustryName =
| "Energy" | "Spring Water"
| "Water Utilities" | "Water Utilities"
| "Agriculture" | "Agriculture"
| "Fishing" | "Fishing"
| "Mining" | "Mining"
| "Food" | "Refinery"
| "Restaurant"
| "Tobacco" | "Tobacco"
| "Chemical" | "Chemical"
| "Pharmaceutical" | "Pharmaceutical"

@ -9,8 +9,9 @@
```typescript ```typescript
type CorpMaterialName = type CorpMaterialName =
| "Minerals"
| "Ore"
| "Water" | "Water"
| "Energy"
| "Food" | "Food"
| "Plants" | "Plants"
| "Metal" | "Metal"

@ -16,7 +16,7 @@ buyBackShares(amount: number): void;
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| amount | number | Amount of shares to buy back. | | amount | number | Amount of shares to buy back, must be integer and larger than 0 |
**Returns:** **Returns:**

@ -16,7 +16,7 @@ sellShares(amount: number): void;
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| amount | number | Amount of shares to sell. | | amount | number | Amount of shares to sell, must be integer between 1 and 100t |
**Returns:** **Returns:**

@ -24,6 +24,7 @@ interface Material
| [prod](./bitburner.material.prod.md) | | number | Amount of material produced | | [prod](./bitburner.material.prod.md) | | number | Amount of material produced |
| [qlt](./bitburner.material.qlt.md) | | number | Quality of the material | | [qlt](./bitburner.material.qlt.md) | | number | Quality of the material |
| [qty](./bitburner.material.qty.md) | | number | Amount of 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" | | [sCost](./bitburner.material.scost.md) | | string \| number | Sell cost, can be "MP+5" |
| [sell](./bitburner.material.sell.md) | | number | Amount of material sold | | [sell](./bitburner.material.sell.md) | | number | Amount of material sold |

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [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 +0,0 @@
<!-- 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; [avgHap](./bitburner.office.avghap.md)
## Office.avgHap property
Average happiness of the employees
**Signature:**
```typescript
avgHap: 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; [Office](./bitburner.office.md) &gt; [maxHap](./bitburner.office.maxhap.md)
## Office.maxHap property
Maximum happiness of the employees
**Signature:**
```typescript
maxHap: number;
```

@ -17,14 +17,12 @@ export interface Office
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [avgEne](./bitburner.office.avgene.md) | | number | Average energy of the employees | | [avgEne](./bitburner.office.avgene.md) | | number | Average energy of the employees |
| [avgHap](./bitburner.office.avghap.md) | | number | Average happiness of the employees |
| [avgMor](./bitburner.office.avgmor.md) | | number | Average morale of the employees | | [avgMor](./bitburner.office.avgmor.md) | | number | Average morale of the employees |
| [employeeJobs](./bitburner.office.employeejobs.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Positions of the employees | | [employeeJobs](./bitburner.office.employeejobs.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Positions of the employees |
| [employeeProd](./bitburner.office.employeeprod.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Production of the employees | | [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 | | [employees](./bitburner.office.employees.md) | | number | Amount of employees |
| [loc](./bitburner.office.loc.md) | | [CityName](./bitburner.cityname.md) | City of the office | | [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 | | [maxEne](./bitburner.office.maxene.md) | | number | Maximum amount of energy of the employees |
| [maxHap](./bitburner.office.maxhap.md) | | number | Maximum happiness of the employees |
| [maxMor](./bitburner.office.maxmor.md) | | number | Maximum morale of the employees | | [maxMor](./bitburner.office.maxmor.md) | | number | Maximum morale of the employees |
| [size](./bitburner.office.size.md) | | number | Maximum number of employee | | [size](./bitburner.office.size.md) | | number | Maximum number of employee |
| [totalExperience](./bitburner.office.totalexperience.md) | | number | Total experience of all employees | | [totalExperience](./bitburner.office.totalexperience.md) | | number | Total experience of all employees |

@ -1,15 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [OfficeAPI](./bitburner.officeapi.md) &gt; [buyCoffee](./bitburner.officeapi.buycoffee.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [OfficeAPI](./bitburner.officeapi.md) &gt; [buyTea](./bitburner.officeapi.buytea.md)
## OfficeAPI.buyCoffee() method ## OfficeAPI.buyTea() method
Buy coffee for your employees Buy tea for your employees
**Signature:** **Signature:**
```typescript ```typescript
buyCoffee(divisionName: string, city: CityName | `${CityName}`): boolean; buyTea(divisionName: string, city: CityName | `${CityName}`): boolean;
``` ```
## Parameters ## Parameters
@ -23,5 +23,5 @@ buyCoffee(divisionName: string, city: CityName | `${CityName}`): boolean;
boolean boolean
true if buying coffee was successful, false otherwise true if buying tea was successful, false otherwise

@ -20,7 +20,7 @@ requires the Office API upgrade from your corporation.
| Method | Description | | Method | Description |
| --- | --- | | --- | --- |
| [buyCoffee(divisionName, city)](./bitburner.officeapi.buycoffee.md) | Buy coffee for your employees | | [buyTea(divisionName, city)](./bitburner.officeapi.buytea.md) | Buy tea for your employees |
| [getHireAdVertCost(divisionName)](./bitburner.officeapi.gethireadvertcost.md) | Get the cost to hire AdVert. | | [getHireAdVertCost(divisionName)](./bitburner.officeapi.gethireadvertcost.md) | Get the cost to hire AdVert. |
| [getHireAdVertCount(divisionName)](./bitburner.officeapi.gethireadvertcount.md) | Get the number of times you have hired AdVert. | | [getHireAdVertCount(divisionName)](./bitburner.officeapi.gethireadvertcount.md) | Get the number of times you have hired AdVert. |
| [getOffice(divisionName, city)](./bitburner.officeapi.getoffice.md) | Get data about an office | | [getOffice(divisionName, city)](./bitburner.officeapi.getoffice.md) | Get data about an office |

@ -24,5 +24,5 @@ throwParty(divisionName: string, city: CityName | `${CityName}`, costPerEmployee
number number
Multiplier for happiness and morale, or zero on failure Multiplier for morale, or zero on failure

@ -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; [cityData](./bitburner.product.citydata.md)
## Product.cityData property
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\]
**Signature:**
```typescript
cityData: Record<CityName | `${CityName}`, 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; [effRat](./bitburner.product.effrat.md)
## Product.effRat property
Effective rating
**Signature:**
```typescript
effRat: number;
```

@ -16,13 +16,17 @@ interface Product
| Property | Modifiers | Type | Description | | Property | Modifiers | Type | Description |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [cityData](./bitburner.product.citydata.md) | | Record&lt;[CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\`, number\[\]&gt; | 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\] |
| [cmp](./bitburner.product.cmp.md) | | number \| undefined | Competition for the product, only present if "Market Research - Competition" unlocked | | [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 | | [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 | | [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 |
| [name](./bitburner.product.name.md) | | string | Name of the product | | [name](./bitburner.product.name.md) | | string | Name of the product |
| [pCost](./bitburner.product.pcost.md) | | number | Production cost | | [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<!-- -->} | | [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 | | [rat](./bitburner.product.rat.md) | | number | Product Rating |
| [sCost](./bitburner.product.scost.md) | | string \| number | Sell cost, can be "MP+5" | | [sAmt](./bitburner.product.samt.md) | | string | Sell amount, can be "PROD/2" |
| [sCost](./bitburner.product.scost.md) | | string | Sell cost, can be "MP+5" |
| [sell](./bitburner.product.sell.md) | | number | Amount of product sold |

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

@ -9,5 +9,5 @@ Sell cost, can be "MP+5"
**Signature:** **Signature:**
```typescript ```typescript
sCost: string | number; sCost: string;
``` ```

@ -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; [sell](./bitburner.product.sell.md)
## Product.sell property
Amount of product sold
**Signature:**
```typescript
sell: number;
```

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

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

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

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

@ -1,19 +1,19 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. --> <!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [WarehouseAPI](./bitburner.warehouseapi.md) &gt; [setSmartSupplyUseLeftovers](./bitburner.warehouseapi.setsmartsupplyuseleftovers.md) [Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [WarehouseAPI](./bitburner.warehouseapi.md) &gt; [setSmartSupplyOption](./bitburner.warehouseapi.setsmartsupplyoption.md)
## WarehouseAPI.setSmartSupplyUseLeftovers() method ## WarehouseAPI.setSmartSupplyOption() method
Set whether smart supply uses leftovers before buying Set whether smart supply uses leftovers before buying
**Signature:** **Signature:**
```typescript ```typescript
setSmartSupplyUseLeftovers( setSmartSupplyOption(
divisionName: string, divisionName: string,
city: CityName | `${CityName}`, city: CityName | `${CityName}`,
materialName: string, materialName: string,
enabled: boolean, option: string,
): void; ): void;
``` ```
@ -24,7 +24,7 @@ setSmartSupplyUseLeftovers(
| divisionName | string | Name of the division | | divisionName | string | Name of the division |
| city | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Name of the city | | city | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Name of the city |
| materialName | string | Name of the material | | materialName | string | Name of the material |
| enabled | boolean | smart supply use leftovers enabled | | option | string | smart supply option, "leftovers" to use leftovers, "imports" to use only imported materials, "none" to not use materials from store |
**Returns:** **Returns:**

@ -499,6 +499,7 @@ export const defaultMultipliers: IBitNodeMultipliers = {
CorporationValuation: 1, CorporationValuation: 1,
CorporationSoftcap: 1, CorporationSoftcap: 1,
CorporationDivisions: 1,
BladeburnerRank: 1, BladeburnerRank: 1,
BladeburnerSkillCost: 1, BladeburnerSkillCost: 1,
@ -538,6 +539,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
FactionWorkRepGain: 0.5, FactionWorkRepGain: 0.5,
CorporationSoftcap: 0.9, CorporationSoftcap: 0.9,
CorporationDivisions: 0.9,
InfiltrationMoney: 3, InfiltrationMoney: 3,
StaneksGiftPowerMultiplier: 2, StaneksGiftPowerMultiplier: 2,
@ -623,7 +625,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
InfiltrationMoney: 1.5, InfiltrationMoney: 1.5,
InfiltrationRep: 1.5, InfiltrationRep: 1.5,
CorporationValuation: 0.5, CorporationValuation: 0.75,
CorporationDivisions: 0.75,
GangUniqueAugs: 0.5, GangUniqueAugs: 0.5,
@ -654,6 +657,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
CorporationValuation: 0.2, CorporationValuation: 0.2,
CorporationSoftcap: 0.9, CorporationSoftcap: 0.9,
CorporationDivisions: 0.8,
GangSoftcap: 0.7, GangSoftcap: 0.7,
GangUniqueAugs: 0.2, GangUniqueAugs: 0.2,
@ -692,6 +696,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
CorporationValuation: 0.2, CorporationValuation: 0.2,
CorporationSoftcap: 0.9, CorporationSoftcap: 0.9,
CorporationDivisions: 0.8,
BladeburnerRank: 0.6, BladeburnerRank: 0.6,
BladeburnerSkillCost: 2, BladeburnerSkillCost: 2,
@ -725,6 +730,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
CorporationValuation: 0, CorporationValuation: 0,
CorporationSoftcap: 0, CorporationSoftcap: 0,
CorporationDivisions: 0,
BladeburnerRank: 0, BladeburnerRank: 0,
@ -760,7 +766,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
FourSigmaMarketDataApiCost: 4, FourSigmaMarketDataApiCost: 4,
CorporationValuation: 0.5, CorporationValuation: 0.5,
CorporationSoftcap: 0.7, CorporationSoftcap: 0.75,
CorporationDivisions: 0.8,
BladeburnerRank: 0.9, BladeburnerRank: 0.9,
BladeburnerSkillCost: 1.2, BladeburnerSkillCost: 1.2,
@ -804,6 +811,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
CorporationValuation: 0.5, CorporationValuation: 0.5,
CorporationSoftcap: 0.9, CorporationSoftcap: 0.9,
CorporationDivisions: 0.9,
BladeburnerRank: 0.8, BladeburnerRank: 0.8,
@ -844,6 +852,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
CorporationValuation: 0.1, CorporationValuation: 0.1,
CorporationSoftcap: 0.9, CorporationSoftcap: 0.9,
CorporationDivisions: 0.9,
GangUniqueAugs: 0.75, GangUniqueAugs: 0.75,
@ -907,6 +916,7 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
CorporationValuation: dec, CorporationValuation: dec,
CorporationSoftcap: 0.8, CorporationSoftcap: 0.8,
CorporationDivisions: 0.5,
BladeburnerRank: dec, BladeburnerRank: dec,
BladeburnerSkillCost: inc, BladeburnerSkillCost: inc,
@ -952,7 +962,8 @@ export function getBitNodeMultipliers(n: number, lvl: number): IBitNodeMultiplie
FourSigmaMarketDataApiCost: 10, FourSigmaMarketDataApiCost: 10,
CorporationValuation: 0.001, CorporationValuation: 0.001,
CorporationSoftcap: 0.3, CorporationSoftcap: 0.4,
CorporationDivisions: 0.4,
BladeburnerRank: 0.45, BladeburnerRank: 0.45,
BladeburnerSkillCost: 2, BladeburnerSkillCost: 2,

@ -154,9 +154,12 @@ export interface IBitNodeMultipliers {
/** Influences the hacking skill required to backdoor the world daemon. */ /** Influences the hacking skill required to backdoor the world daemon. */
WorldDaemonDifficulty: number; WorldDaemonDifficulty: number;
/** Influences corporation dividends. */ /** Influences profits from corporation dividends and selling shares. */
CorporationSoftcap: number; CorporationSoftcap: number;
/** Influences number of divisions a corporation can have. */
CorporationDivisions: number;
// Index signature // Index signature
[key: string]: number; [key: string]: number;
} }

@ -343,6 +343,7 @@ function CorporationMults({ mults }: IMultsProps): React.ReactElement {
content: mults.CorporationSoftcap.toFixed(3), content: mults.CorporationSoftcap.toFixed(3),
}, },
CorporationValuation: { name: "Valuation" }, CorporationValuation: { name: "Valuation" },
CorporationDivisions: { name: "Division limit" },
}; };
return <BNMultTable sectionName="Corporation" rowData={rows} mults={mults} />; return <BNMultTable sectionName="Corporation" rowData={rows} mults={mults} />;

@ -17,10 +17,12 @@ import { isRelevantMaterial } from "./ui/Helpers";
import { CityName } from "../Enums"; import { CityName } from "../Enums";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CorpResearchName } from "@nsdefs"; import { CorpResearchName } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers";
import { isInteger } from "lodash";
export function NewIndustry(corporation: Corporation, industry: IndustryType, name: string): void { export function NewIndustry(corporation: Corporation, industry: IndustryType, name: string): void {
if (corporation.divisions.find(({ type }) => industry == type)) if (corporation.divisions.length >= corporation.maxDivisions)
throw new Error(`You have already expanded into the ${industry} industry!`); throw new Error(`Cannot expand into ${industry} industry, too many divisions!`);
for (let i = 0; i < corporation.divisions.length; ++i) { for (let i = 0; i < corporation.divisions.length; ++i) {
if (corporation.divisions[i].name === name) { if (corporation.divisions[i].name === name) {
@ -47,6 +49,13 @@ 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);
}
export function NewCity(corporation: Corporation, division: Industry, city: CityName): void { export function NewCity(corporation: Corporation, division: Industry, city: CityName): void {
if (corporation.funds < corpConstants.officeInitialCost) { if (corporation.funds < corpConstants.officeInitialCost) {
throw new Error("You don't have enough company funds to open a new office!"); throw new Error("You don't have enough company funds to open a new office!");
@ -71,15 +80,12 @@ export function UnlockUpgrade(corporation: Corporation, upgrade: CorporationUnlo
corporation.unlock(upgrade); corporation.unlock(upgrade);
} }
export function LevelUpgrade(corporation: Corporation, upgrade: CorporationUpgrade): void { export function LevelUpgrade(corporation: Corporation, upgrade: CorporationUpgrade, amount: number): void {
const baseCost = upgrade.basePrice; const cost = calculateUpgradeCost(corporation, upgrade, amount);
const priceMult = upgrade.priceMult;
const level = corporation.upgrades[upgrade.index];
const cost = baseCost * Math.pow(priceMult, level);
if (corporation.funds < cost) { if (corporation.funds < cost) {
throw new Error("Insufficient funds"); throw new Error("Insufficient funds");
} else { } else {
corporation.upgrade(upgrade); corporation.upgrade(upgrade, amount);
} }
} }
@ -101,7 +107,7 @@ export function IssueNewShares(corporation: Corporation, amount: number): [numbe
throw new Error(`Invalid value. Must be an number between 10m and ${max} (20% of total shares)`); throw new Error(`Invalid value. Must be an number between 10m and ${max} (20% of total shares)`);
} }
const newSharePrice = Math.round(corporation.sharePrice * 0.9); const newSharePrice = Math.round(corporation.sharePrice * 0.8);
const profit = amount * newSharePrice; const profit = amount * newSharePrice;
corporation.issueNewSharesCooldown = corpConstants.issueNewSharesCooldown; corporation.issueNewSharesCooldown = corpConstants.issueNewSharesCooldown;
@ -143,21 +149,21 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
//Parse quantity //Parse quantity
amt = amt.toUpperCase(); amt = amt.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD")) { if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) {
let q = amt.replace(/\s+/g, ""); let q = amt.replace(/\s+/g, "");
q = q.replace(/[^-()\d/*+.MAXPROD]/g, ""); q = q.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let tempQty = q.replace(/MAX/g, mat.maxsll.toString()); let tempQty = q.replace(/MAX/g, mat.maxsll.toString());
tempQty = tempQty.replace(/PROD/g, mat.prd.toString()); tempQty = tempQty.replace(/PROD/g, mat.prd.toString());
tempQty = tempQty.replace(/INV/g, mat.prd.toString());
try { try {
tempQty = eval(tempQty); tempQty = eval(tempQty);
} catch (e) { } catch (e) {
throw new Error("Invalid value or expression for sell quantity field: " + e); throw new Error("Invalid value or expression for sell quantity field: " + e);
} }
if (tempQty == null || isNaN(parseFloat(tempQty)) || parseFloat(tempQty) < 0) { if (tempQty == null || isNaN(parseFloat(tempQty))) {
throw new Error("Invalid value or expression for sell quantity field"); throw new Error("Invalid value or expression for sell quantity field");
} }
mat.sllman[0] = true; mat.sllman[0] = true;
mat.sllman[1] = q; //Use sanitized input mat.sllman[1] = q; //Use sanitized input
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) { } else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
@ -194,13 +200,13 @@ export function SellProduct(product: Product, city: string, amt: string, price:
if (temp == null || isNaN(parseFloat(temp))) { if (temp == null || isNaN(parseFloat(temp))) {
throw new Error("Invalid value or expression for sell price field."); throw new Error("Invalid value or expression for sell price field.");
} }
product.sCost = price; //Use sanitized price product.sCost[city] = price; //Use sanitized price
} else { } else {
const cost = parseFloat(price); const cost = parseFloat(price);
if (isNaN(cost)) { if (isNaN(cost)) {
throw new Error("Invalid value for sell price field"); throw new Error("Invalid value for sell price field");
} }
product.sCost = cost; product.sCost[city] = cost;
} }
// Array of all cities. Used later // Array of all cities. Used later
@ -208,19 +214,20 @@ export function SellProduct(product: Product, city: string, amt: string, price:
// Parse quantity // Parse quantity
amt = amt.toUpperCase(); amt = amt.toUpperCase();
if (amt.includes("MAX") || amt.includes("PROD")) { if (amt.includes("MAX") || amt.includes("PROD") || amt.includes("INV")) {
//Dynamically evaluated quantity. First test to make sure its valid //Dynamically evaluated quantity. First test to make sure its valid
let qty = amt.replace(/\s+/g, ""); let qty = amt.replace(/\s+/g, "");
qty = qty.replace(/[^-()\d/*+.MAXPROD]/g, ""); qty = qty.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let temp = qty.replace(/MAX/g, product.maxsll.toString()); let temp = qty.replace(/MAX/g, product.maxsll.toString());
temp = temp.replace(/PROD/g, product.data[city][1].toString()); temp = temp.replace(/PROD/g, product.data[city][1].toString());
temp = temp.replace(/INV/g, product.data[city][0].toString());
try { try {
temp = eval(temp); temp = eval(temp);
} catch (e) { } catch (e) {
throw new Error("Invalid value or expression for sell quantity field: " + e); throw new Error("Invalid value or expression for sell quantity field: " + e);
} }
if (temp == null || isNaN(parseFloat(temp)) || parseFloat(temp) < 0) { if (temp == null || isNaN(parseFloat(temp))) {
throw new Error("Invalid value or expression for sell quantity field"); throw new Error("Invalid value or expression for sell quantity field");
} }
if (all) { if (all) {
@ -268,8 +275,11 @@ export function SetSmartSupply(warehouse: Warehouse, smartSupply: boolean): void
warehouse.smartSupplyEnabled = smartSupply; warehouse.smartSupplyEnabled = smartSupply;
} }
export function SetSmartSupplyUseLeftovers(warehouse: Warehouse, material: Material, useLeftover: boolean): void { export function SetSmartSupplyOption(warehouse: Warehouse, material: Material, useOption: string): void {
warehouse.smartSupplyUseLeftovers[material.name] = useLeftover; if (!corpConstants.smartSupplyUseOptions.includes(useOption)) {
throw new Error(`Invalid Smart Supply option '${useOption}'`);
}
warehouse.smartSupplyOptions[material.name] = useOption;
} }
export function BuyMaterial(material: Material, amt: number): void { export function BuyMaterial(material: Material, amt: number): void {
@ -298,9 +308,11 @@ export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material:
} }
export function SellShares(corporation: Corporation, numShares: number): number { export function SellShares(corporation: Corporation, numShares: number): number {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); if (isNaN(numShares) || !isInteger(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) throw new Error("Invalid value for number of shares"); if (numShares <= 0) throw new Error("Invalid value for number of shares");
if (numShares > corporation.numShares) throw new Error("You don't have that many shares to sell!"); if (numShares > corporation.numShares) throw new Error("You don't have that many shares to sell!");
if (numShares === corporation.numShares) throw new Error("You cant't sell all your shares!");
if (numShares > 1e14) throw new Error("Invalid value for number of shares");
if (!corporation.public) throw new Error("You haven't gone public!"); if (!corporation.public) throw new Error("You haven't gone public!");
if (corporation.shareSaleCooldown) throw new Error("Share sale on cooldown!"); if (corporation.shareSaleCooldown) throw new Error("Share sale on cooldown!");
const stockSaleResults = corporation.calculateShareSale(numShares); const stockSaleResults = corporation.calculateShareSale(numShares);
@ -318,8 +330,8 @@ export function SellShares(corporation: Corporation, numShares: number): number
} }
export function BuyBackShares(corporation: Corporation, numShares: number): boolean { export function BuyBackShares(corporation: Corporation, numShares: number): boolean {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares"); if (isNaN(numShares) || !isInteger(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) throw new Error("Invalid value for number of shares"); if (numShares <= 0) throw new Error("Invalid value for number of shares");
if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!"); if (numShares > corporation.issuedShares) throw new Error("You don't have that many shares to buy!");
if (!corporation.public) throw new Error("You haven't gone public!"); if (!corporation.public) throw new Error("You haven't gone public!");
const buybackPrice = corporation.sharePrice * 1.1; const buybackPrice = corporation.sharePrice * 1.1;
@ -344,9 +356,9 @@ export function UpgradeOfficeSize(corp: Corporation, office: OfficeSpace, size:
corp.funds = corp.funds - cost; corp.funds = corp.funds - cost;
} }
export function BuyCoffee(corp: Corporation, office: OfficeSpace): boolean { export function BuyTea(corp: Corporation, office: OfficeSpace): boolean {
const cost = office.getCoffeeCost(); const cost = office.getTeaCost();
if (corp.funds < cost || !office.setCoffee()) return false; if (corp.funds < cost || !office.setTea()) return false;
corp.funds -= cost; corp.funds -= cost;
return true; return true;
} }
@ -495,8 +507,12 @@ export function ExportMaterial(
): void { ): void {
// Sanitize amt // Sanitize amt
let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase(); let sanitizedAmt = amt.replace(/\s+/g, "").toUpperCase();
sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAX]/g, ""); sanitizedAmt = sanitizedAmt.replace(/[^-()\d/*+.MAXEPRODINV]/g, "");
let temp = sanitizedAmt.replace(/MAX/g, "1"); let temp = sanitizedAmt.replace(/MAX/g, "1");
temp = temp.replace(/IPROD/g, "1");
temp = temp.replace(/EPROD/g, "1");
temp = temp.replace(/IINV/g, "1");
temp = temp.replace(/EINV/g, "1");
try { try {
temp = eval(temp); temp = eval(temp);
} catch (e) { } catch (e) {
@ -505,7 +521,7 @@ export function ExportMaterial(
const n = parseFloat(temp); const n = parseFloat(temp);
if (n == null || isNaN(n) || n < 0) { if (n == null || isNaN(n)) {
throw new Error("Invalid amount entered for export"); throw new Error("Invalid amount entered for export");
} }

@ -14,9 +14,11 @@ import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../uti
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
import { CityName } from "../Enums"; import { CityName } from "../Enums";
import { CorpStateName } from "@nsdefs"; import { CorpStateName } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers";
interface IParams { interface IParams {
name?: string; name?: string;
seedFunded?: boolean;
} }
export class Corporation { export class Corporation {
@ -24,6 +26,7 @@ export class Corporation {
//A division/business sector is represented by the object: //A division/business sector is represented by the object:
divisions: Industry[] = []; divisions: Industry[] = [];
maxDivisions = 20 * BitNodeMultipliers.CorporationDivisions;
//Financial stats //Financial stats
funds = 150e9; funds = 150e9;
@ -50,6 +53,8 @@ export class Corporation {
valuationsList = [0]; valuationsList = [0];
valuation = 0; valuation = 0;
seedFunded: boolean;
state = new CorporationState(); state = new CorporationState();
constructor(params: IParams = {}) { constructor(params: IParams = {}) {
@ -59,6 +64,7 @@ export class Corporation {
this.unlockUpgrades = Array(numUnlockUpgrades).fill(0); this.unlockUpgrades = Array(numUnlockUpgrades).fill(0);
this.upgrades = Array(numUpgrades).fill(0); this.upgrades = Array(numUpgrades).fill(0);
this.upgradeMultipliers = Array(numUpgrades).fill(1); this.upgradeMultipliers = Array(numUpgrades).fill(1);
this.seedFunded = params.seedFunded ?? false;
} }
addFunds(amt: number): void { addFunds(amt: number): void {
@ -78,6 +84,8 @@ export class Corporation {
} }
process(): void { process(): void {
if (this.storedCycles < 0) this.storedCycles = 0;
if (this.storedCycles >= corpConstants.gameCyclesPerCorpStateCycle) { if (this.storedCycles >= corpConstants.gameCyclesPerCorpStateCycle) {
const state = this.getState(); const state = this.getState();
const marketCycles = 1; const marketCycles = 1;
@ -305,10 +313,9 @@ export class Corporation {
} }
//Levelable upgrades //Levelable upgrades
upgrade(upgrade: CorporationUpgrade): void { upgrade(upgrade: CorporationUpgrade, amount: number): void {
if (amount < 1) amount = 1;
const upgN = upgrade.index, const upgN = upgrade.index,
basePrice = upgrade.basePrice,
priceMult = upgrade.priceMult,
upgradeAmt = upgrade.benefit; //Amount by which the upgrade multiplier gets increased (additive) upgradeAmt = upgrade.benefit; //Amount by which the upgrade multiplier gets increased (additive)
while (this.upgrades.length <= upgN) { while (this.upgrades.length <= upgN) {
this.upgrades.push(0); this.upgrades.push(0);
@ -316,12 +323,12 @@ export class Corporation {
while (this.upgradeMultipliers.length <= upgN) { while (this.upgradeMultipliers.length <= upgN) {
this.upgradeMultipliers.push(1); this.upgradeMultipliers.push(1);
} }
const totalCost = basePrice * Math.pow(priceMult, this.upgrades[upgN]); const totalCost = calculateUpgradeCost(this, upgrade, amount);
if (this.funds < totalCost) { if (this.funds < totalCost) {
dialogBoxCreate("You don't have enough funds to purchase this!"); dialogBoxCreate("You don't have enough funds to purchase this!");
return; return;
} }
++this.upgrades[upgN]; this.upgrades[upgN] += amount;
this.funds = this.funds - totalCost; this.funds = this.funds - totalCost;
//Increase upgrade multiplier //Increase upgrade multiplier

@ -321,6 +321,7 @@ export class Industry {
buyAmt = Math.min(buyAmt, maxAmt); buyAmt = Math.min(buyAmt, maxAmt);
if (buyAmt > 0) { if (buyAmt > 0) {
mat.qlt = Math.max(0.1, (mat.qlt * mat.qty + 1 * buyAmt) / (mat.qty + buyAmt));
mat.qty += buyAmt; mat.qty += buyAmt;
expenses += buyAmt * mat.bCost; expenses += buyAmt * mat.bCost;
} }
@ -382,18 +383,25 @@ export class Industry {
// Use the materials already in the warehouse if the option is on. // Use the materials already in the warehouse if the option is on.
for (const matName of Object.keys(smartBuy) as CorpMaterialName[]) { for (const matName of Object.keys(smartBuy) as CorpMaterialName[]) {
if (!warehouse.smartSupplyUseLeftovers[matName]) continue; if (warehouse.smartSupplyOptions[matName] === "none") continue;
const mat = warehouse.materials[matName]; const mat = warehouse.materials[matName];
const buyAmt = smartBuy[matName]; const buyAmt = smartBuy[matName];
if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`); if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`);
smartBuy[matName] = Math.max(0, buyAmt - mat.qty); if (warehouse.smartSupplyOptions[matName] === "leftovers") {
smartBuy[matName] = Math.max(0, buyAmt - mat.qty);
} else {
smartBuy[matName] = Math.max(0, buyAmt - mat.imp);
}
} }
// buy them // buy them
for (const [matName, buyAmt] of Object.entries(smartBuy) as [CorpMaterialName, number][]) { for (const [matName, buyAmt] of Object.entries(smartBuy) as [CorpMaterialName, number][]) {
const mat = warehouse.materials[matName]; const mat = warehouse.materials[matName];
if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`); if (buyAmt === undefined) throw new Error(`Somehow smartbuy matname is undefined`);
if (mat.qty + buyAmt != 0) mat.qlt = (mat.qlt * mat.qty + 1 * buyAmt) / (mat.qty + buyAmt);
else mat.qlt = 1;
mat.qty += buyAmt; mat.qty += buyAmt;
mat.buy = buyAmt / 10;
expenses += buyAmt * mat.bCost; expenses += buyAmt * mat.bCost;
} }
break; break;
@ -463,6 +471,8 @@ export class Industry {
// Make our materials if they are producable // Make our materials if they are producable
if (producableFrac > 0 && prod > 0) { if (producableFrac > 0 && prod > 0) {
let avgQlt = 0;
let divider = 0;
for (const reqMatName of Object.keys(this.reqMats) as CorpMaterialName[]) { for (const reqMatName of Object.keys(this.reqMats) as CorpMaterialName[]) {
const reqMat = this.reqMats[reqMatName]; const reqMat = this.reqMats[reqMatName];
if (reqMat === undefined) continue; if (reqMat === undefined) continue;
@ -471,13 +481,26 @@ export class Industry {
warehouse.materials[reqMatName].prd = 0; warehouse.materials[reqMatName].prd = 0;
warehouse.materials[reqMatName].prd -= warehouse.materials[reqMatName].prd -=
reqMatQtyNeeded / (corpConstants.secondsPerMarketCycle * marketCycles); reqMatQtyNeeded / (corpConstants.secondsPerMarketCycle * marketCycles);
avgQlt += warehouse.materials[reqMatName].qlt;
divider++;
} }
avgQlt /= divider;
avgQlt = Math.max(avgQlt, 1);
for (let j = 0; j < this.prodMats.length; ++j) { for (let j = 0; j < this.prodMats.length; ++j) {
warehouse.materials[this.prodMats[j]].qty += prod * producableFrac; let tempQlt =
warehouse.materials[this.prodMats[j]].qlt =
office.employeeProd[EmployeePositions.Engineer] / 90 + office.employeeProd[EmployeePositions.Engineer] / 90 +
Math.pow(this.sciResearch, this.sciFac) + Math.pow(this.sciResearch, this.sciFac) +
Math.pow(warehouse.materials["AI Cores"].qty, this.aiFac) / 10e3; Math.pow(warehouse.materials["AI Cores"].qty, this.aiFac) / 10e3;
const logQlt = Math.max(Math.pow(tempQlt, 0.5), 1);
tempQlt = Math.min(tempQlt, avgQlt * logQlt);
warehouse.materials[this.prodMats[j]].qlt = Math.max(
1,
(warehouse.materials[this.prodMats[j]].qlt * warehouse.materials[this.prodMats[j]].qty +
tempQlt * prod * producableFrac) /
(warehouse.materials[this.prodMats[j]].qty + prod * producableFrac),
);
warehouse.materials[this.prodMats[j]].qty += prod * producableFrac;
} }
} else { } else {
for (const reqMatName of Object.keys(this.reqMats) as CorpMaterialName[]) { for (const reqMatName of Object.keys(this.reqMats) as CorpMaterialName[]) {
@ -610,6 +633,28 @@ export class Industry {
corporation.getSalesMultiplier() * corporation.getSalesMultiplier() *
advertisingFactor * advertisingFactor *
this.getSalesMultiplier(); this.getSalesMultiplier();
if (isString(mat.sllman[1])) {
//Dynamically evaluated
let tmp = (mat.sllman[1] as string).replace(/MAX/g, (mat.maxsll + "").toUpperCase());
tmp = tmp.replace(/PROD/g, mat.prd + "");
try {
sellAmt = eval(tmp);
} catch (e) {
dialogBoxCreate(
`Error evaluating your sell amount for material ${mat.name} in ${this.name}'s ${city} office. The sell amount is being set to zero, sellAmt is set to ${sellAmt}`,
);
sellAmt = 0;
}
sellAmt = Math.min(mat.maxsll, sellAmt);
sellAmt = Math.max(sellAmt, 0);
} else if (mat.sllman[1] === -1) {
//Backwards compatibility, -1 = MAX
sellAmt = mat.maxsll;
} else {
//Player's input value is just a number
sellAmt = Math.min(mat.maxsll, mat.sllman[1] as number);
}
sellAmt = Math.min(mat.maxsll, sellAmt); sellAmt = Math.min(mat.maxsll, sellAmt);
sellAmt = sellAmt * corpConstants.secondsPerMarketCycle * marketCycles; sellAmt = sellAmt * corpConstants.secondsPerMarketCycle * marketCycles;
sellAmt = Math.min(mat.qty, sellAmt); sellAmt = Math.min(mat.qty, sellAmt);
@ -636,10 +681,27 @@ export class Industry {
mat.totalExp = 0; //Reset export mat.totalExp = 0; //Reset export
for (let expI = 0; expI < mat.exp.length; ++expI) { for (let expI = 0; expI < mat.exp.length; ++expI) {
const exp = mat.exp[expI]; const exp = mat.exp[expI];
const amtStr = exp.amt.replace(
const expIndustry = corporation.divisions.find((div) => div.name === exp.ind);
if (!expIndustry) {
console.error(`Invalid export! ${exp.ind}`);
continue;
}
const expWarehouse = expIndustry.warehouses[exp.city];
if (!expWarehouse) {
console.error(`Invalid export! ${expIndustry.name} ${exp.city}`);
continue;
}
const tempMaterial = expWarehouse.materials[matName];
let amtStr = exp.amt.replace(
/MAX/g, /MAX/g,
(mat.qty / (corpConstants.secondsPerMarketCycle * marketCycles) + "").toUpperCase(), (mat.qty / (corpConstants.secondsPerMarketCycle * marketCycles) + "").toUpperCase(),
); );
amtStr = amtStr.replace(/EPROD/g, mat.prd.toString());
amtStr = amtStr.replace(/IPROD/g, tempMaterial.prd.toString());
amtStr = amtStr.replace(/EINV/g, mat.qty.toString());
amtStr = amtStr.replace(/IINV/g, tempMaterial.qty.toString());
let amt = 0; let amt = 0;
try { try {
amt = eval(amtStr); amt = eval(amtStr);
@ -660,38 +722,33 @@ export class Industry {
if (mat.qty < amt) { if (mat.qty < amt) {
amt = mat.qty; amt = mat.qty;
} }
if (amt === 0) {
break; //None left
}
for (let foo = 0; foo < corporation.divisions.length; ++foo) {
if (corporation.divisions[foo].name === exp.ind) {
const expIndustry = corporation.divisions[foo];
const expWarehouse = expIndustry.warehouses[exp.city];
if (!expWarehouse) {
console.error(`Invalid export! ${expIndustry.name} ${exp.city}`);
break;
}
// Make sure theres enough space in warehouse // Make sure theres enough space in warehouse
if (expWarehouse.sizeUsed >= expWarehouse.size) { if (expWarehouse.sizeUsed >= expWarehouse.size) {
// Warehouse at capacity. Exporting doesn't // Warehouse at capacity. Exporting doesn't
// affect revenue so just return 0's // affect revenue so just return 0's
return [0, 0]; continue;
} else { } else {
const maxAmt = Math.floor( const maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialInfo[matName].size);
(expWarehouse.size - expWarehouse.sizeUsed) / MaterialInfo[matName].size, amt = Math.min(maxAmt, amt);
);
amt = Math.min(maxAmt, amt);
}
expWarehouse.materials[matName].imp += amt / (corpConstants.secondsPerMarketCycle * marketCycles);
expWarehouse.materials[matName].qty += amt;
expWarehouse.materials[matName].qlt = mat.qlt;
mat.qty -= amt;
mat.totalExp += amt;
expIndustry.updateWarehouseSizeUsed(expWarehouse);
break;
}
} }
if (amt <= 0) {
continue;
}
expWarehouse.materials[matName].imp += amt / (corpConstants.secondsPerMarketCycle * marketCycles);
//Pretty sure this can cause some issues if there are multiple sources importing same material to same warehouse
//but this will do for now
expWarehouse.materials[matName].qlt = Math.max(
0.1,
(expWarehouse.materials[matName].qlt * expWarehouse.materials[matName].qty + amt * mat.qlt) /
(expWarehouse.materials[matName].qty + amt),
);
expWarehouse.materials[matName].qty += amt;
mat.qty -= amt;
mat.totalExp += amt;
expIndustry.updateWarehouseSizeUsed(expWarehouse);
} }
//totalExp should be per second //totalExp should be per second
mat.totalExp /= corpConstants.secondsPerMarketCycle * marketCycles; mat.totalExp /= corpConstants.secondsPerMarketCycle * marketCycles;
@ -814,12 +871,20 @@ export class Industry {
//Make our Products if they are producable //Make our Products if they are producable
if (producableFrac > 0 && prod > 0) { if (producableFrac > 0 && prod > 0) {
let avgQlt = 1;
for (const [reqMatName, reqQty] of Object.entries(product.reqMats) as [CorpMaterialName, number][]) { for (const [reqMatName, reqQty] of Object.entries(product.reqMats) as [CorpMaterialName, number][]) {
const reqMatQtyNeeded = reqQty * prod * producableFrac; const reqMatQtyNeeded = reqQty * prod * producableFrac;
warehouse.materials[reqMatName].qty -= reqMatQtyNeeded; warehouse.materials[reqMatName].qty -= reqMatQtyNeeded;
warehouse.materials[reqMatName].prd -= warehouse.materials[reqMatName].prd -=
reqMatQtyNeeded / (corpConstants.secondsPerMarketCycle * marketCycles); reqMatQtyNeeded / (corpConstants.secondsPerMarketCycle * marketCycles);
avgQlt += warehouse.materials[reqMatName].qlt;
} }
avgQlt /= Object.keys(product.reqMats).length;
const tempEffRat = Math.min(product.rat, avgQlt * Math.pow(product.rat, 0.5));
//Effective Rating
product.data[city][3] =
(product.data[city][3] * product.data[city][0] + tempEffRat * prod * producableFrac) /
(product.data[city][0] + prod * producableFrac);
//Quantity //Quantity
product.data[city][0] += prod * producableFrac; product.data[city][0] += prod * producableFrac;
} }
@ -874,7 +939,7 @@ export class Industry {
} }
// Calculate Sale Cost (sCost), which could be dynamically evaluated // Calculate Sale Cost (sCost), which could be dynamically evaluated
const markupLimit = product.rat / product.mku; const markupLimit = Math.max(product.data[city][3], 0.001) / product.mku;
let sCost; let sCost;
if (product.marketTa2) { if (product.marketTa2) {
// Reverse engineer the 'maxSell' formula // Reverse engineer the 'maxSell' formula
@ -885,7 +950,7 @@ export class Industry {
const sqrtNumerator = sellAmt; const sqrtNumerator = sellAmt;
const sqrtDenominator = const sqrtDenominator =
0.5 * 0.5 *
Math.pow(product.rat, 0.65) * Math.pow(product.data[city][3], 0.65) *
marketFactor * marketFactor *
corporation.getSalesMultiplier() * corporation.getSalesMultiplier() *
businessFactor * businessFactor *
@ -909,16 +974,16 @@ export class Industry {
sCost = optimalPrice; sCost = optimalPrice;
} else if (product.marketTa1) { } else if (product.marketTa1) {
sCost = product.pCost + markupLimit; sCost = product.pCost + markupLimit;
} else if (isString(product.sCost)) { } else if (isString(product.sCost[city])) {
const sCostString = product.sCost as string; const sCostString = product.sCost[city] as string;
if (product.mku === 0) { if (product.mku === 0) {
console.error(`mku is zero, reverting to 1 to avoid Infinity`); console.error(`mku is zero, reverting to 1 to avoid Infinity`);
product.mku = 1; product.mku = 1;
} }
sCost = sCostString.replace(/MP/g, product.pCost + product.rat / product.mku + ""); sCost = sCostString.replace(/MP/g, product.pCost + "");
sCost = Math.max(product.pCost, eval(sCost)); sCost = Math.max(product.pCost, eval(sCost));
} else { } else {
sCost = product.sCost; sCost = product.sCost[city];
} }
let markup = 1; let markup = 1;
@ -930,7 +995,7 @@ export class Industry {
product.maxsll = product.maxsll =
0.5 * 0.5 *
Math.pow(product.rat, 0.65) * Math.pow(product.data[city][3], 0.65) *
marketFactor * marketFactor *
corporation.getSalesMultiplier() * corporation.getSalesMultiplier() *
Math.pow(markup, 2) * Math.pow(markup, 2) *
@ -996,10 +1061,10 @@ export class Industry {
applyAdVert(corporation: Corporation): void { applyAdVert(corporation: Corporation): void {
const advMult = corporation.getAdvertisingMultiplier() * this.getAdvertisingMultiplier(); const advMult = corporation.getAdvertisingMultiplier() * this.getAdvertisingMultiplier();
const awareness = (this.awareness + 3 * advMult) * (1.01 * advMult); const awareness = (this.awareness + 3 * advMult) * (1.005 * advMult);
this.awareness = Math.min(awareness, Number.MAX_VALUE); this.awareness = Math.min(awareness, Number.MAX_VALUE);
const popularity = (this.popularity + 1 * advMult) * ((1 + getRandomInt(1, 3) / 100) * advMult); const popularity = (this.popularity + 1 * advMult) * ((1 + getRandomInt(1, 3) / 200) * advMult);
this.popularity = Math.min(popularity, Number.MAX_VALUE); this.popularity = Math.min(popularity, Number.MAX_VALUE);
++this.numAdVerts; ++this.numAdVerts;
@ -1154,7 +1219,7 @@ export class Industry {
const matNameMap = { AICores: "AI Cores", RealEstate: "Real Estate" }; const matNameMap = { AICores: "AI Cores", RealEstate: "Real Estate" };
const indNameMap = { const indNameMap = {
RealEstate: IndustryType.RealEstate, RealEstate: IndustryType.RealEstate,
Utilities: IndustryType.Utilities, Water: IndustryType.Water,
Computers: IndustryType.Computers, Computers: IndustryType.Computers,
Computer: IndustryType.Computers, Computer: IndustryType.Computers,
}; };

@ -17,9 +17,35 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.3, robotFactor: 0.3,
aiCoreFactor: 0.3, aiCoreFactor: 0.3,
advertisingFactor: 0.04, advertisingFactor: 0.04,
requiredMaterials: { Water: 0.5, Energy: 0.5 }, requiredMaterials: { Water: 0.5, Chemicals: 0.2 },
producedMaterials: ["Plants", "Food"], producedMaterials: ["Plants", "Food"],
}, },
[IndustryType.Spring]: {
startingCost: 10e9,
description: "Gather water through passive means.",
recommendStarting: false,
realEstateFactor: 0.2,
scienceFactor: 0.1,
hardwareFactor: 0.0,
robotFactor: 0.0,
aiCoreFactor: 0.1,
advertisingFactor: 0.03,
requiredMaterials: {},
producedMaterials: ["Plants", "Food"],
},
[IndustryType.Refinery]: {
startingCost: 50e9,
description: "Refine ore into usable metal.",
recommendStarting: true,
realEstateFactor: 0.3,
scienceFactor: 0.5,
hardwareFactor: 0.5,
robotFactor: 0.4,
aiCoreFactor: 0.3,
advertisingFactor: 0.04,
requiredMaterials: { Ore: 1 },
producedMaterials: ["Metal"],
},
[IndustryType.Chemical]: { [IndustryType.Chemical]: {
startingCost: 70e9, startingCost: 70e9,
description: "Produce industrial chemicals.", description: "Produce industrial chemicals.",
@ -30,7 +56,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.25, robotFactor: 0.25,
aiCoreFactor: 0.2, aiCoreFactor: 0.2,
advertisingFactor: 0.07, advertisingFactor: 0.07,
requiredMaterials: { Plants: 1, Energy: 0.5, Water: 0.5 }, requiredMaterials: { Plants: 1, Water: 0.5 },
producedMaterials: ["Chemicals"], producedMaterials: ["Chemicals"],
}, },
[IndustryType.Computers]: { [IndustryType.Computers]: {
@ -55,21 +81,9 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.36, robotFactor: 0.36,
aiCoreFactor: 0.19, aiCoreFactor: 0.19,
advertisingFactor: 0.17, advertisingFactor: 0.17,
requiredMaterials: { Metal: 2, Energy: 1 }, requiredMaterials: { Metal: 2 },
producedMaterials: ["Hardware"], producedMaterials: ["Hardware"],
}, },
[IndustryType.Energy]: {
startingCost: 225e9,
description: "Engage in the production and distribution of energy.",
recommendStarting: false,
realEstateFactor: 0.65,
scienceFactor: 0.7,
robotFactor: 0.05,
aiCoreFactor: 0.3,
advertisingFactor: 0.08,
requiredMaterials: { Hardware: 0.1, Metal: 0.2 },
producedMaterials: ["Energy"],
},
[IndustryType.Fishing]: { [IndustryType.Fishing]: {
startingCost: 80e9, startingCost: 80e9,
description: "Produce food through the breeding and processing of fish and fish products.", description: "Produce food through the breeding and processing of fish and fish products.",
@ -80,10 +94,10 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.5, robotFactor: 0.5,
aiCoreFactor: 0.2, aiCoreFactor: 0.2,
advertisingFactor: 0.08, advertisingFactor: 0.08,
requiredMaterials: { Energy: 0.5 }, requiredMaterials: { Plants: 0.5 },
producedMaterials: ["Food"], producedMaterials: ["Food"],
}, },
[IndustryType.Food]: { [IndustryType.Restaurant]: {
startingCost: 10e9, startingCost: 10e9,
description: "Create your own restaurants all around the world.", description: "Create your own restaurants all around the world.",
product: { product: {
@ -103,7 +117,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
aiCoreFactor: 0.25, aiCoreFactor: 0.25,
advertisingFactor: 0.25, advertisingFactor: 0.25,
realEstateFactor: 0.05, realEstateFactor: 0.05,
requiredMaterials: { Food: 0.5, Water: 0.5, Energy: 0.2 }, requiredMaterials: { Food: 0.5, Water: 0.5 },
}, },
[IndustryType.Healthcare]: { [IndustryType.Healthcare]: {
startingCost: 750e9, startingCost: 750e9,
@ -127,7 +141,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
hardwareFactor: 0.1, hardwareFactor: 0.1,
robotFactor: 0.1, robotFactor: 0.1,
aiCoreFactor: 0.1, aiCoreFactor: 0.1,
requiredMaterials: { Robots: 10, "AI Cores": 5, Energy: 5, Water: 5 }, requiredMaterials: { Robots: 10, "AI Cores": 5, Drugs: 5, Food: 5 },
}, },
[IndustryType.Mining]: { [IndustryType.Mining]: {
startingCost: 300e9, startingCost: 300e9,
@ -139,8 +153,8 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.45, robotFactor: 0.45,
aiCoreFactor: 0.45, aiCoreFactor: 0.45,
advertisingFactor: 0.06, advertisingFactor: 0.06,
requiredMaterials: { Energy: 0.8 }, requiredMaterials: { Hardware: 0.1 },
producedMaterials: ["Metal"], producedMaterials: ["Ore", "Minerals"],
}, },
[IndustryType.Pharmaceutical]: { [IndustryType.Pharmaceutical]: {
startingCost: 200e9, startingCost: 200e9,
@ -164,7 +178,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.25, robotFactor: 0.25,
aiCoreFactor: 0.2, aiCoreFactor: 0.2,
advertisingFactor: 0.16, advertisingFactor: 0.16,
requiredMaterials: { Chemicals: 2, Energy: 1, Water: 0.5 }, requiredMaterials: { Chemicals: 2, Water: 0.5 },
producedMaterials: ["Drugs"], producedMaterials: ["Drugs"],
}, },
[IndustryType.RealEstate]: { [IndustryType.RealEstate]: {
@ -188,7 +202,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
advertisingFactor: 0.25, advertisingFactor: 0.25,
scienceFactor: 0.05, scienceFactor: 0.05,
hardwareFactor: 0.05, hardwareFactor: 0.05,
requiredMaterials: { Metal: 5, Energy: 5, Water: 2, Hardware: 4 }, requiredMaterials: { Metal: 5, Plants: 1, Water: 2, Hardware: 4 },
producedMaterials: ["Real Estate"], producedMaterials: ["Real Estate"],
}, },
[IndustryType.Robotics]: { [IndustryType.Robotics]: {
@ -213,7 +227,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
aiCoreFactor: 0.36, aiCoreFactor: 0.36,
advertisingFactor: 0.18, advertisingFactor: 0.18,
hardwareFactor: 0.19, hardwareFactor: 0.19,
requiredMaterials: { Hardware: 5, Energy: 3 }, requiredMaterials: { Hardware: 5, "AI Cores": 3 },
producedMaterials: ["Robots"], producedMaterials: ["Robots"],
}, },
[IndustryType.Software]: { [IndustryType.Software]: {
@ -238,7 +252,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
realEstateFactor: 0.15, realEstateFactor: 0.15,
aiCoreFactor: 0.18, aiCoreFactor: 0.18,
robotFactor: 0.05, robotFactor: 0.05,
requiredMaterials: { Hardware: 0.5, Energy: 0.5 }, requiredMaterials: { Hardware: 0.5 },
producedMaterials: ["AI Cores"], producedMaterials: ["AI Cores"],
}, },
[IndustryType.Tobacco]: { [IndustryType.Tobacco]: {
@ -261,9 +275,9 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.2, robotFactor: 0.2,
aiCoreFactor: 0.15, aiCoreFactor: 0.15,
advertisingFactor: 0.2, advertisingFactor: 0.2,
requiredMaterials: { Plants: 1, Water: 0.2 }, requiredMaterials: { Plants: 1 },
}, },
[IndustryType.Utilities]: { [IndustryType.Water]: {
startingCost: 150e9, startingCost: 150e9,
description: "Distribute water and provide wastewater services.", description: "Distribute water and provide wastewater services.",
recommendStarting: false, recommendStarting: false,
@ -272,7 +286,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.4, robotFactor: 0.4,
aiCoreFactor: 0.4, aiCoreFactor: 0.4,
advertisingFactor: 0.08, advertisingFactor: 0.08,
requiredMaterials: { Hardware: 0.1, Metal: 0.1 }, requiredMaterials: { Hardware: 0.1 },
producedMaterials: ["Water"], producedMaterials: ["Water"],
}, },
}; };

@ -16,7 +16,7 @@ export class Material {
qty = 0; qty = 0;
// Material's "quality". Unbounded // Material's "quality". Unbounded
qlt = 0; qlt = 1;
// How much demand the Material has in the market, and the range of possible // How much demand the Material has in the market, and the range of possible
// values for this "demand" // values for this "demand"

@ -13,14 +13,25 @@ export const MaterialInfo: Record<CorpMaterialName, CorpMaterialConstantData> =
maxVolatility: 0.2, maxVolatility: 0.2,
baseMarkup: 6, baseMarkup: 6,
}, },
Energy: { Ore: {
name: "Energy", name: "Ore",
size: 0.01, size: 0.01,
demandBase: 90, demandBase: 50,
demandRange: [80, 99], demandRange: [40, 60],
competitionBase: 80, competitionBase: 80,
competitionRange: [65, 95], competitionRange: [65, 95],
baseCost: 2000, baseCost: 500,
maxVolatility: 0.2,
baseMarkup: 6,
},
Minerals: {
name: "Minerals",
size: 0.04,
demandBase: 75,
demandRange: [90, 60],
competitionBase: 80,
competitionRange: [65, 95],
baseCost: 500,
maxVolatility: 0.2, maxVolatility: 0.2,
baseMarkup: 6, baseMarkup: 6,
}, },

@ -16,11 +16,9 @@ export class OfficeSpace {
size: number; size: number;
maxEne = 100; maxEne = 100;
maxHap = 100;
maxMor = 100; maxMor = 100;
avgEne = 75; avgEne = 75;
avgHap = 75;
avgMor = 75; avgMor = 75;
avgInt = 75; avgInt = 75;
@ -32,9 +30,9 @@ export class OfficeSpace {
totalEmployees = 0; totalEmployees = 0;
totalSalary = 0; totalSalary = 0;
autoCoffee = false; autoTea = false;
autoParty = false; autoParty = false;
coffeePending = false; teaPending = false;
partyMult = 1; partyMult = 1;
employeeProd: Record<EmployeePositions | "total", number> = { employeeProd: Record<EmployeePositions | "total", number> = {
@ -43,7 +41,7 @@ export class OfficeSpace {
[EmployeePositions.Business]: 0, [EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0, [EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0, [EmployeePositions.RandD]: 0,
[EmployeePositions.Training]: 0, [EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0, [EmployeePositions.Unassigned]: 0,
total: 0, total: 0,
}; };
@ -53,7 +51,7 @@ export class OfficeSpace {
[EmployeePositions.Business]: 0, [EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0, [EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0, [EmployeePositions.RandD]: 0,
[EmployeePositions.Training]: 0, [EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0, [EmployeePositions.Unassigned]: 0,
}; };
employeeNextJobs: Record<EmployeePositions, number> = { employeeNextJobs: Record<EmployeePositions, number> = {
@ -62,7 +60,7 @@ export class OfficeSpace {
[EmployeePositions.Business]: 0, [EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0, [EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0, [EmployeePositions.RandD]: 0,
[EmployeePositions.Training]: 0, [EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0, [EmployeePositions.Unassigned]: 0,
}; };
@ -76,10 +74,10 @@ export class OfficeSpace {
} }
process(marketCycles = 1, corporation: Corporation, industry: Industry): number { process(marketCycles = 1, corporation: Corporation, industry: Industry): number {
// HRBuddy AutoRecruitment and training // HRBuddy AutoRecruitment and Interning
if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) { if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) {
this.hireRandomEmployee( this.hireRandomEmployee(
industry.hasResearch("HRBuddy-Training") ? EmployeePositions.Training : EmployeePositions.Unassigned, industry.hasResearch("HRBuddy-Training") ? EmployeePositions.Intern : EmployeePositions.Unassigned,
); );
} }
@ -90,62 +88,57 @@ export class OfficeSpace {
// Process Office properties // Process Office properties
this.maxEne = 100; this.maxEne = 100;
this.maxHap = 100;
this.maxMor = 100; this.maxMor = 100;
if (industry.hasResearch("Go-Juice")) this.maxEne += 10; if (industry.hasResearch("Go-Juice")) this.maxEne += 10;
if (industry.hasResearch("JoyWire")) this.maxHap += 10;
if (industry.hasResearch("Sti.mu")) this.maxMor += 10; if (industry.hasResearch("Sti.mu")) this.maxMor += 10;
if (industry.hasResearch("AutoBrew")) this.autoCoffee = true; if (industry.hasResearch("AutoBrew")) this.autoTea = true;
if (industry.hasResearch("AutoPartyManager")) this.autoParty = true; if (industry.hasResearch("AutoPartyManager")) this.autoParty = true;
if (this.totalEmployees > 0) { if (this.totalEmployees > 0) {
/** Multiplier for employee morale/happiness/energy based on company performance */ /** Multiplier for employee morale/energy based on company performance */
const perfMult = Math.pow( let perfMult = 1.002;
1.002 - if (this.totalEmployees >= 9) {
(corporation.funds < 0 ? 0.002 : 0) - perfMult = Math.pow(
(industry.lastCycleRevenue < industry.lastCycleExpenses ? 0.002 : 0), 1 +
marketCycles, 0.002 * Math.min(1 / 9, this.employeeJobs.Intern / this.totalEmployees - 1 / 9) * 9 -
); (corporation.funds < 0 && industry.lastCycleRevenue < industry.lastCycleExpenses ? 0.001 : 0),
marketCycles,
);
}
// Flat reduction per cycle. // Flat reduction per cycle.
// This does not cause a noticable decrease (it's only -.001% per cycle), it only serves // This does not cause a noticable decrease (it's only -.001% per cycle).
// to make the numbers slightly different between Happiness and Morale. const reduction = 0.002 * marketCycles;
const reduction = 0.001 * marketCycles;
if (this.autoCoffee) { if (this.autoTea) {
this.avgEne = this.maxEne; this.avgEne = this.maxEne;
} else { } else {
// Coffee gives a flat +3 to energy // Tea gives a flat +2 to energy
this.avgEne = (this.avgEne - reduction) * perfMult + (this.coffeePending ? 3 : 0); this.avgEne = (this.avgEne - reduction * Math.random()) * perfMult + (this.teaPending ? 2 : 0);
// Coffee also halves the difference between current and max energy
if (this.coffeePending) this.avgEne = this.maxEne - (this.maxEne - this.avgEne) / 2;
} }
if (this.autoParty) { if (this.autoParty) {
this.avgMor = this.maxMor; this.avgMor = this.maxMor;
this.avgHap = this.maxHap;
} else { } else {
// Each 5% multiplier gives an extra flat +1 to morale and happiness to make recovering from low morale easier. // Each 5% multiplier gives an extra flat +1 to morale to make recovering from low morale easier.
const increase = this.partyMult > 1 ? (this.partyMult - 1) * 20 : 0; const increase = this.partyMult > 1 ? (this.partyMult - 1) * 10 : 0;
this.avgHap = ((this.avgHap - reduction) * perfMult + increase) * this.partyMult; this.avgMor = ((this.avgMor - reduction * Math.random()) * perfMult + increase) * this.partyMult;
this.avgMor = (this.avgMor * perfMult + increase) * this.partyMult;
} }
this.avgEne = Math.max(Math.min(this.avgEne, this.maxEne), corpConstants.minEmployeeDecay); 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.avgMor = Math.max(Math.min(this.avgMor, this.maxMor), corpConstants.minEmployeeDecay);
this.avgHap = Math.max(Math.min(this.avgHap, this.maxHap), corpConstants.minEmployeeDecay);
this.coffeePending = false; this.teaPending = false;
this.partyMult = 1; this.partyMult = 1;
} }
// Get experience increase; unassigned employees do not contribute, employees in training contribute 5x // Get experience increase; unassigned employees do not contribute, interning employees contribute 10x
this.totalExp += this.totalExp +=
0.0015 * 0.0015 *
marketCycles * marketCycles *
(this.totalEmployees - (this.totalEmployees -
this.employeeJobs[EmployeePositions.Unassigned] + this.employeeJobs[EmployeePositions.Unassigned] +
this.employeeJobs[EmployeePositions.Training] * 4); this.employeeJobs[EmployeePositions.Intern] * 9);
this.calculateEmployeeProductivity(corporation, industry); this.calculateEmployeeProductivity(corporation, industry);
if (this.totalEmployees === 0) { if (this.totalEmployees === 0) {
@ -165,7 +158,7 @@ export class OfficeSpace {
effCha = this.avgCha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(), effCha = this.avgCha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(),
effInt = this.avgInt * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(), effInt = this.avgInt * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(),
effEff = this.avgEff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier(); effEff = this.avgEff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier();
const prodBase = this.avgMor * this.avgHap * this.avgEne * 1e-6; const prodBase = this.avgMor * this.avgEne * 1e-4;
let total = 0; let total = 0;
const exp = this.totalExp / this.totalEmployees || 0; const exp = this.totalExp / this.totalEmployees || 0;
@ -188,7 +181,7 @@ export class OfficeSpace {
prodMult = 1.5 * effInt + 0.8 * exp + effCre + 0.5 * effEff; prodMult = 1.5 * effInt + 0.8 * exp + effCre + 0.5 * effEff;
break; break;
case EmployeePositions.Unassigned: case EmployeePositions.Unassigned:
case EmployeePositions.Training: case EmployeePositions.Intern:
case "total": case "total":
continue; continue;
default: default:
@ -213,7 +206,6 @@ export class OfficeSpace {
this.totalExp += getRandomInt(50, 100); this.totalExp += getRandomInt(50, 100);
this.avgMor = (this.avgMor * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgMor = (this.avgMor * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
this.avgHap = (this.avgHap * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
this.avgEne = (this.avgEne * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgEne = (this.avgEne * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
this.avgInt = (this.avgInt * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1); this.avgInt = (this.avgInt * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
@ -240,13 +232,13 @@ export class OfficeSpace {
return false; return false;
} }
getCoffeeCost(): number { getTeaCost(): number {
return corpConstants.coffeeCostPerEmployee * this.totalEmployees; return corpConstants.teaCostPerEmployee * this.totalEmployees;
} }
setCoffee(): boolean { setTea(): boolean {
if (!this.coffeePending && !this.autoCoffee && this.totalEmployees > 0) { if (!this.teaPending && !this.autoTea && this.totalEmployees > 0) {
this.coffeePending = true; this.teaPending = true;
return true; return true;
} }
return false; return false;
@ -267,11 +259,10 @@ export class OfficeSpace {
static fromJSON(value: IReviverValue): OfficeSpace { static fromJSON(value: IReviverValue): OfficeSpace {
// Convert employees from the old version // Convert employees from the old version
if (value.data.hasOwnProperty("employees")) { if (value.data.hasOwnProperty("employees")) {
const empCopy: [{ data: { hap: number; mor: number; ene: number; exp: number } }] = value.data.employees; const empCopy: [{ data: { mor: number; ene: number; exp: number } }] = value.data.employees;
delete value.data.employees; delete value.data.employees;
const ret = Generic_fromJSON(OfficeSpace, value.data); const ret = Generic_fromJSON(OfficeSpace, value.data);
ret.totalEmployees = empCopy.length; ret.totalEmployees = empCopy.length;
ret.avgHap = empCopy.reduce((a, b) => a + b.data.hap, 0) / ret.totalEmployees || 75;
ret.avgMor = empCopy.reduce((a, b) => a + b.data.mor, 0) / ret.totalEmployees || 75; 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.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.totalExp = empCopy.reduce((a, b) => a + b.data.exp, 0);

@ -47,8 +47,8 @@ export class Product {
// Production cost - estimation of how much money it costs to make this Product // Production cost - estimation of how much money it costs to make this Product
pCost = 0; pCost = 0;
// Sell cost // Sell costs
sCost: string | number = 0; sCost: Record<string, any> = createCityMap<any>(0);
// Variables for handling the creation process of this Product // Variables for handling the creation process of this Product
fin = false; // Whether this Product has finished being created fin = false; // Whether this Product has finished being created
@ -82,8 +82,8 @@ export class Product {
// Data refers to the production, sale, and quantity of the products // Data refers to the production, sale, and quantity of the products
// These values are specific to a city // These values are specific to a city
// For each city, the data is [qty, prod, sell] // For each city, the data is [qty, prod, sell, effRat]
data: Record<string, number[]> = createCityMap<number[]>([0, 0, 0]); data: Record<string, number[]> = createCityMap<number[]>([0, 0, 0, 0]);
// Location of this Product // Location of this Product
// Only applies for location-based products like restaurants/hospitals // Only applies for location-based products like restaurants/hospitals
@ -130,7 +130,7 @@ export class Product {
} }
// Make progress on this product based on current employee productivity // Make progress on this product based on current employee productivity
createProduct(marketCycles: number, employeeProd: typeof this["creationProd"]): void { createProduct(marketCycles: number, employeeProd: typeof this.creationProd): void {
if (this.fin) { if (this.fin) {
return; return;
} }

@ -34,8 +34,8 @@ export class Warehouse {
// Whether Smart Supply is enabled for this Industry (the Industry that this Warehouse is for) // Whether Smart Supply is enabled for this Industry (the Industry that this Warehouse is for)
smartSupplyEnabled = false; smartSupplyEnabled = false;
// Decide if smart supply should use the materials already in the warehouse when deciding on the amount to buy. // Decide if smart supply should use the amount of materials imported into account when deciding on the amount to buy.
smartSupplyUseLeftovers: Record<CorpMaterialName, boolean>; smartSupplyOptions: Record<CorpMaterialName, string>;
// Stores the amount of product to be produced. Used for Smart Supply unlock. // Stores the amount of product to be produced. Used for Smart Supply unlock.
// The production tracked by smart supply is always based on the previous cycle, // The production tracked by smart supply is always based on the previous cycle,
@ -47,10 +47,10 @@ export class Warehouse {
this.size = params.size ? params.size : 0; this.size = params.size ? params.size : 0;
this.materials = {} as Record<CorpMaterialName, Material>; this.materials = {} as Record<CorpMaterialName, Material>;
this.smartSupplyUseLeftovers = {} as Record<CorpMaterialName, boolean>; this.smartSupplyOptions = {} as Record<CorpMaterialName, string>;
for (const matName of materialNames) { for (const matName of materialNames) {
this.materials[matName] = new Material({ name: matName }); this.materials[matName] = new Material({ name: matName });
this.smartSupplyUseLeftovers[matName] = true; this.smartSupplyOptions[matName] = "leftovers";
} }
if (params.corp && params.industry) { if (params.corp && params.industry) {

@ -20,7 +20,6 @@ function createBaseResearchTreeNodes(): Node {
const autoBrew: Node = makeNode("AutoBrew"); const autoBrew: Node = makeNode("AutoBrew");
const autoParty: Node = makeNode("AutoPartyManager"); const autoParty: Node = makeNode("AutoPartyManager");
const autoDrugs: Node = makeNode("Automatic Drug Administration"); const autoDrugs: Node = makeNode("Automatic Drug Administration");
const bulkPurchasing: Node = makeNode("Bulk Purchasing");
const cph4: Node = makeNode("CPH4 Injections"); const cph4: Node = makeNode("CPH4 Injections");
const drones: Node = makeNode("Drones"); const drones: Node = makeNode("Drones");
const dronesAssembly: Node = makeNode("Drones - Assembly"); const dronesAssembly: Node = makeNode("Drones - Assembly");
@ -28,7 +27,6 @@ function createBaseResearchTreeNodes(): Node {
const goJuice: Node = makeNode("Go-Juice"); const goJuice: Node = makeNode("Go-Juice");
const hrRecruitment: Node = makeNode("HRBuddy-Recruitment"); const hrRecruitment: Node = makeNode("HRBuddy-Recruitment");
const hrTraining: Node = makeNode("HRBuddy-Training"); const hrTraining: Node = makeNode("HRBuddy-Training");
const joywire: Node = makeNode("JoyWire");
const marketta1: Node = makeNode("Market-TA.I"); const marketta1: Node = makeNode("Market-TA.I");
const marketta2: Node = makeNode("Market-TA.II"); const marketta2: Node = makeNode("Market-TA.II");
const overclock: Node = makeNode("Overclock"); const overclock: Node = makeNode("Overclock");
@ -50,10 +48,8 @@ function createBaseResearchTreeNodes(): Node {
rootNode.addChild(autoBrew); rootNode.addChild(autoBrew);
rootNode.addChild(autoParty); rootNode.addChild(autoParty);
rootNode.addChild(autoDrugs); rootNode.addChild(autoDrugs);
rootNode.addChild(bulkPurchasing);
rootNode.addChild(drones); rootNode.addChild(drones);
rootNode.addChild(hrRecruitment); rootNode.addChild(hrRecruitment);
rootNode.addChild(joywire);
rootNode.addChild(marketta1); rootNode.addChild(marketta1);
rootNode.addChild(overclock); rootNode.addChild(overclock);
rootNode.addChild(scAssemblers); rootNode.addChild(scAssemblers);

@ -24,7 +24,8 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
/** Names of all materials */ /** Names of all materials */
materialNames: CorpMaterialName[] = [ materialNames: CorpMaterialName[] = [
"Water", "Water",
"Energy", "Ore",
"Minerals",
"Food", "Food",
"Plants", "Plants",
"Metal", "Metal",
@ -73,7 +74,6 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
"Go-Juice", "Go-Juice",
"HRBuddy-Recruitment", "HRBuddy-Recruitment",
"HRBuddy-Training", "HRBuddy-Training",
"JoyWire",
"Market-TA.I", "Market-TA.I",
"Market-TA.II", "Market-TA.II",
"Overclock", "Overclock",
@ -96,7 +96,7 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
issueNewSharesCooldown = 216e3, issueNewSharesCooldown = 216e3,
/** Cooldown for selling shares in game cycles. 1 hour. */ /** Cooldown for selling shares in game cycles. 1 hour. */
sellSharesCooldown = 18e3, sellSharesCooldown = 18e3,
coffeeCostPerEmployee = 500e3, teaCostPerEmployee = 500e3,
gameCyclesPerMarketCycle = 50, gameCyclesPerMarketCycle = 50,
gameCyclesPerCorpStateCycle = gameCyclesPerMarketCycle / stateNames.length, gameCyclesPerCorpStateCycle = gameCyclesPerMarketCycle / stateNames.length,
secondsPerMarketCycle = (gameCyclesPerMarketCycle * CONSTANTS.MilliPerCycle) / 1000, secondsPerMarketCycle = (gameCyclesPerMarketCycle * CONSTANTS.MilliPerCycle) / 1000,
@ -117,7 +117,25 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
/** Max products for a division without upgrades */ /** Max products for a division without upgrades */
maxProductsBase = 3, maxProductsBase = 3,
fundingRoundShares = [0.1, 0.35, 0.25, 0.2], fundingRoundShares = [0.1, 0.35, 0.25, 0.2],
fundingRoundMultiplier = [4, 3, 3, 2.5], fundingRoundMultiplier = [3, 2, 2, 1.5],
valuationLength = 5, valuationLength = 10,
/** Minimum decay value for employee morale/happiness/energy */ /** Minimum decay value for employee morale/energy */
minEmployeeDecay = 10; 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",
};

@ -66,7 +66,7 @@ export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpg
[CorporationUpgradeIndex.WilsonAnalytics]: { [CorporationUpgradeIndex.WilsonAnalytics]: {
index: CorporationUpgradeIndex.WilsonAnalytics, index: CorporationUpgradeIndex.WilsonAnalytics,
basePrice: 4e9, basePrice: 4e9,
priceMult: 1.5, priceMult: 2,
benefit: 0.005, benefit: 0.005,
name: "Wilson Analytics", name: "Wilson Analytics",
desc: desc:

@ -1,10 +1,11 @@
export enum IndustryType { export enum IndustryType {
Energy = "Energy", Water = "Water Utilities",
Utilities = "Water Utilities", Spring = "Spring Water",
Agriculture = "Agriculture", Agriculture = "Agriculture",
Fishing = "Fishing", Fishing = "Fishing",
Mining = "Mining", Mining = "Mining",
Food = "Food", Refinery = "Refinery",
Restaurant = "Restaurant",
Tobacco = "Tobacco", Tobacco = "Tobacco",
Chemical = "Chemical", Chemical = "Chemical",
Pharmaceutical = "Pharmaceutical", Pharmaceutical = "Pharmaceutical",
@ -21,6 +22,6 @@ export enum EmployeePositions {
Business = "Business", Business = "Business",
Management = "Management", Management = "Management",
RandD = "Research & Development", RandD = "Research & Development",
Training = "Training", Intern = "Intern",
Unassigned = "Unassigned", Unassigned = "Unassigned",
} }

@ -6,17 +6,17 @@ export const researchMetadata: IConstructorParams[] = [
cost: 12e3, cost: 12e3,
desc: desc:
"Automatically keep your employees fully caffeinated with " + "Automatically keep your employees fully caffeinated with " +
"coffee injections. This research will keep the energy of all " + "tea injections. This research will keep the energy of all " +
"employees at its maximum possible value, for no cost. " + "employees at its maximum possible value, for no cost. " +
"This will also disable the Coffee upgrade.", "This will also disable the Tea upgrade.",
}, },
{ {
name: "AutoPartyManager", name: "AutoPartyManager",
cost: 15e3, cost: 15e3,
desc: desc:
"Automatically analyzes your employees' happiness and morale " + "Automatically analyzes your employees' morale " +
"and boosts them whenever it detects a decrease. This research will " + "and boosts them whenever it detects a decrease. This research will " +
"keep the morale and happiness of all employees at their maximum possible " + "keep the morale of all employees at their maximum possible " +
"values, for no cost. " + "values, for no cost. " +
"This will also disable the 'Throw Party' feature.", "This will also disable the 'Throw Party' feature.",
}, },
@ -27,13 +27,6 @@ export const researchMetadata: IConstructorParams[] = [
"Research how to automatically administer performance-enhancing drugs to all of " + "Research how to automatically administer performance-enhancing drugs to all of " +
"your employees. This unlocks Drug-related Research.", "your employees. This unlocks Drug-related Research.",
}, },
{
name: "Bulk Purchasing",
cost: 5e3,
desc:
"Research the art of buying materials in bulk. This allows you to purchase " +
"any amount of a material instantly.",
},
{ {
name: "CPH4 Injections", name: "CPH4 Injections",
cost: 25e3, cost: 25e3,
@ -73,7 +66,7 @@ export const researchMetadata: IConstructorParams[] = [
name: "Go-Juice", name: "Go-Juice",
cost: 25e3, cost: 25e3,
desc: desc:
"Provide employees with Go-Juice, a coffee-derivative that further enhances " + "Provide employees with Go-Juice, a tea-derivative that further enhances " +
"the brain's dopamine production. This increases the maximum energy of all " + "the brain's dopamine production. This increases the maximum energy of all " +
"employees by 10.", "employees by 10.",
}, },
@ -101,12 +94,7 @@ export const researchMetadata: IConstructorParams[] = [
desc: desc:
"Use automated software to handle the training of employees. With this " + "Use automated software to handle the training of employees. With this " +
"research, each employee hired with HRBuddy-Recruitment will automatically " + "research, each employee hired with HRBuddy-Recruitment will automatically " +
"be assigned to 'Training', rather than being unassigned.", "be assigned to 'Intern', rather than being unassigned.",
},
{
name: "JoyWire",
cost: 20e3,
desc: "A brain implant which is installed in employees, increasing their maximum happiness by 10.",
}, },
{ {
name: "Market-TA.I", name: "Market-TA.I",

@ -0,0 +1,35 @@
import { Corporation } from "./Corporation";
import { CorporationUpgrade } from "./data/CorporationUpgrades";
export function calculateUpgradeCost(corporation: Corporation, upgrade: CorporationUpgrade, amount: number): number {
if (amount < 1) return 0;
const priceMult = upgrade.priceMult;
const level = corporation.upgrades[upgrade.index];
const baseCost = upgrade.basePrice * Math.pow(priceMult, level);
const cost = (baseCost * (1 - Math.pow(priceMult, amount))) / (1 - priceMult);
return cost;
}
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;
}
let n = 1;
while (
calculateUpgradeCost(corporation, upgrade, n * 2) < corporation.funds &&
(amount != "MAX" ? n < amount : true)
) {
n *= 2;
}
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);
}

@ -3,7 +3,6 @@ import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { IndustryDescriptions, IndustriesData } from "../IndustryData"; import { IndustryDescriptions, IndustriesData } from "../IndustryData";
import { IndustryType } from "../data/Enums"; import { IndustryType } from "../data/Enums";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import { Industry } from "../Industry";
import { NewIndustry } from "../Actions"; import { NewIndustry } from "../Actions";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -21,20 +20,13 @@ interface IProps {
export function ExpandIndustryTab(props: IProps): React.ReactElement { export function ExpandIndustryTab(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const allIndustries = Object.values(IndustryType).sort(); const allIndustries = Object.values(IndustryType).sort();
const possibleIndustries = allIndustries.filter( const [industry, setIndustry] = useState(allIndustries[0]);
(industryType: IndustryType) =>
corp.divisions.find((division: Industry) => division.type === industryType) === undefined,
);
const [industry, setIndustry] = useState(possibleIndustries[0]);
const [name, setName] = useState(""); const [name, setName] = useState("");
//If there are no possible industries to expand into, nothing to render in this tab.
if (possibleIndustries.length === 0) return <></>;
const data = IndustriesData[industry]; const data = IndustriesData[industry];
if (!data) return <></>; if (!data) return <></>;
const disabled = corp.funds < data.startingCost; const disabled = corp.funds < data.startingCost && corp.divisions.length < corp.maxDivisions;
function newIndustry(): void { function newIndustry(): void {
if (disabled) return; if (disabled) return;
@ -67,9 +59,12 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
return ( return (
<> <>
<Typography>
{corp.name} has {corp.divisions.length}/{corp.maxDivisions} divisions.
</Typography>
<Typography>Create a new division to expand into a new industry:</Typography> <Typography>Create a new division to expand into a new industry:</Typography>
<Select value={industry} onChange={onIndustryChange}> <Select value={industry} onChange={onIndustryChange}>
{possibleIndustries.map((industry) => ( {allIndustries.map((industry) => (
<MenuItem key={industry} value={industry}> <MenuItem key={industry} value={industry}>
{industry} {industry}
</MenuItem> </MenuItem>

@ -4,7 +4,7 @@ import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { EmployeePositions } from "../data/Enums"; import { EmployeePositions } from "../data/Enums";
import { BuyCoffee } from "../Actions"; import { BuyTea } from "../Actions";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { formatCorpStat } from "../../ui/formatNumber"; import { formatCorpStat } from "../../ui/formatNumber";
@ -118,14 +118,6 @@ function AutoManagement(props: IProps): React.ReactElement {
<Typography>{formatCorpStat(props.office.avgMor)}</Typography> <Typography>{formatCorpStat(props.office.avgMor)}</Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow>
<TableCell>
<Typography>Avg Employee Happiness:</Typography>
</TableCell>
<TableCell align="right">
<Typography>{formatCorpStat(props.office.avgHap)}</Typography>
</TableCell>
</TableRow>
<TableRow> <TableRow>
<TableCell> <TableCell>
<Typography>Avg Employee Energy:</Typography> <Typography>Avg Employee Energy:</Typography>
@ -218,7 +210,7 @@ function AutoManagement(props: IProps): React.ReactElement {
office={props.office} office={props.office}
job={EmployeePositions.Engineer} job={EmployeePositions.Engineer}
desc={ desc={
"Develops and maintains products and production systems. Increases the quality of everything you produce. Also increases the amount you produce (not as much as Operations, however)" "Develops and maintains products and production systems. Increases the quality of everything you produce. Also increases the amount you produce (not as much as Operations, however)."
} }
/> />
@ -242,15 +234,17 @@ function AutoManagement(props: IProps): React.ReactElement {
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.RandD} job={EmployeePositions.RandD}
desc={"Research new innovative ways to improve the company. Generates Scientific Research."} 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)."
}
/> />
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
job={EmployeePositions.Training} job={EmployeePositions.Intern}
desc={ desc={
"Set employee to training, which will increase some of their stats. Employees in training do not affect any company operations." "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."
} }
/> />
</TableBody> </TableBody>
@ -304,21 +298,20 @@ export function IndustryOffice(props: IProps): React.ReactElement {
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
Provide your employees with coffee, increasing their energy by half the difference to 100%, plus Provide your employees with tea, increasing their energy by half the difference to 100%, plus 1.5%
1.5%
</Typography> </Typography>
} }
> >
<span> <span>
<Button <Button
disabled={corp.funds < props.office.getCoffeeCost() || props.office.coffeePending} disabled={corp.funds < props.office.getTeaCost() || props.office.teaPending}
onClick={() => BuyCoffee(corp, props.office)} onClick={() => BuyTea(corp, props.office)}
> >
{props.office.coffeePending ? ( {props.office.teaPending ? (
"Buying coffee..." "Buying tea..."
) : ( ) : (
<span> <span>
Buy Coffee - <MoneyCost money={props.office.getCoffeeCost()} corp={corp} /> Buy Tea - <MoneyCost money={props.office.getTeaCost()} corp={corp} />
</span> </span>
)} )}
</Button> </Button>
@ -329,9 +322,7 @@ export function IndustryOffice(props: IProps): React.ReactElement {
{!division.hasResearch("AutoPartyManager") && ( {!division.hasResearch("AutoPartyManager") && (
<> <>
<Tooltip <Tooltip title={<Typography>Throw an office party to increase your employee's morale</Typography>}>
title={<Typography>Throw an office party to increase your employee's morale and happiness</Typography>}
>
<span> <span>
<Button <Button
disabled={corp.funds < 0 || props.office.partyMult > 1} disabled={corp.funds < 0 || props.office.partyMult > 1}

@ -40,7 +40,7 @@ function MakeProductButton(): React.ReactElement {
let createProductButtonText = ""; let createProductButtonText = "";
switch (division.type) { switch (division.type) {
case IndustryType.Food: case IndustryType.Restaurant:
createProductButtonText = "Build Restaurant"; createProductButtonText = "Build Restaurant";
break; break;
case IndustryType.Tobacco: case IndustryType.Tobacco:

@ -11,9 +11,11 @@ import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
import { calculateMaxAffordableUpgrade, calculateUpgradeCost } from "../helpers";
interface IProps { interface IProps {
upgrade: CorporationUpgrade; upgrade: CorporationUpgrade;
amount: number | "MAX";
rerender: () => void; rerender: () => void;
} }
@ -21,16 +23,15 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const data = props.upgrade; const data = props.upgrade;
const level = corp.upgrades[data.index]; const level = corp.upgrades[data.index];
const amount = props.amount;
const baseCost = data.basePrice; const maxUpgrades = amount === "MAX" ? calculateMaxAffordableUpgrade(corp, data, amount) : amount;
const priceMult = data.priceMult; const cost = calculateUpgradeCost(corp, data, maxUpgrades);
const cost = baseCost * Math.pow(priceMult, level);
const tooltip = data.desc; const tooltip = data.desc;
function onClick(): void { function onClick(): void {
if (corp.funds < cost) return; if (corp.funds < cost) return;
try { try {
LevelUpgrade(corp, props.upgrade); LevelUpgrade(corp, props.upgrade, maxUpgrades);
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }
@ -41,6 +42,7 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
<Grid item xs={4}> <Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse"> <Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds < cost} sx={{ mx: 1 }} onClick={onClick}> <Button disabled={corp.funds < cost} sx={{ mx: 1 }} onClick={onClick}>
+{maxUpgrades} -&nbsp;
<MoneyCost money={cost} corp={corp} /> <MoneyCost money={cost} corp={corp} />
</Button> </Button>
<Tooltip title={tooltip}> <Tooltip title={tooltip}>

@ -0,0 +1,50 @@
/**
* React Component for the Multiplier buttons on the Hacknet page.
* These buttons let the player control how many Nodes/Upgrades they're
* purchasing when using the UI (x1, x5, x10, MAX)
*/
import React from "react";
import { PurchaseMultipliers } from "../data/Constants";
import Button from "@mui/material/Button";
interface IMultiplierProps {
disabled: boolean;
onClick: () => void;
text: string;
}
function MultiplierButton(props: IMultiplierProps): React.ReactElement {
return (
<Button disabled={props.disabled} onClick={props.onClick}>
{props.text}
</Button>
);
}
interface IProps {
purchaseMultiplier: number | string;
onClicks: (() => void)[];
}
export function MultiplierButtons(props: IProps): React.ReactElement {
if (props.purchaseMultiplier == null) {
throw new Error(`MultiplierButtons constructed without required props`);
}
const mults = ["x1", "x5", "x10", "x50", "x100", "MAX"];
const onClicks = props.onClicks;
const buttons = [];
for (let i = 0; i < mults.length; ++i) {
const mult = mults[i];
const btnProps = {
disabled: props.purchaseMultiplier === PurchaseMultipliers[mult],
onClick: onClicks[i],
text: mult,
};
buttons.push(<MultiplierButton key={mult} {...btnProps} />);
}
return <>{buttons}</>;
}

@ -29,10 +29,14 @@ import Button from "@mui/material/Button";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
import { MultiplierButtons } from "./MultiplierButtons";
import { SellCorporationModal } from "./modals/SellCorporationModal";
import { SellDivisionModal } from "./modals/SellDivisionModal";
interface IProps { interface IProps {
rerender: () => void; rerender: () => void;
} }
export function Overview({ rerender }: IProps): React.ReactElement { export function Overview({ rerender }: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const profit: number = corp.revenue - corp.expenses; const profit: number = corp.revenue - corp.expenses;
@ -100,6 +104,8 @@ export function Overview({ rerender }: IProps): React.ReactElement {
</Tooltip> </Tooltip>
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />} {corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
<BribeButton /> <BribeButton />
{corp.divisions.length != 0 ? <SellDivisionButton /> : <></>}
<RestartButton />
</Box> </Box>
<br /> <br />
<Upgrades rerender={rerender} /> <Upgrades rerender={rerender} />
@ -155,25 +161,40 @@ function Upgrades({ rerender }: IUpgradeProps): React.ReactElement {
return <Typography variant="h4">Upgrades are unlocked once you create an industry.</Typography>; return <Typography variant="h4">Upgrades are unlocked once you create an industry.</Typography>;
} }
const [purchaseMultiplier, setPurchaseMultiplier] = useState<number | "MAX">(corpConstants.PurchaseMultipliers.x1);
// onClick event handlers for purchase multiplier buttons
const purchaseMultiplierOnClicks = [
() => setPurchaseMultiplier(corpConstants.PurchaseMultipliers.x1),
() => setPurchaseMultiplier(corpConstants.PurchaseMultipliers.x5),
() => setPurchaseMultiplier(corpConstants.PurchaseMultipliers.x10),
() => setPurchaseMultiplier(corpConstants.PurchaseMultipliers.x50),
() => setPurchaseMultiplier(corpConstants.PurchaseMultipliers.x100),
() => setPurchaseMultiplier(corpConstants.PurchaseMultipliers.MAX),
];
return ( return (
<> <>
<Paper sx={{ p: 1, my: 1 }}> <Paper sx={{ p: 1, my: 1 }}>
<Typography variant="h4">Unlocks</Typography> <Typography variant="h4">Unlocks</Typography>
<Grid container> <Grid container>
{Object.values(CorporationUnlockUpgrades) {Object.values(CorporationUnlockUpgrades).map((upgrade: CorporationUnlockUpgrade) => (
.filter((upgrade: CorporationUnlockUpgrade) => !corp.unlockUpgrades[upgrade.index]) <UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade.index} />
.map((upgrade: CorporationUnlockUpgrade) => ( ))}
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade.index} />
))}
</Grid> </Grid>
</Paper> </Paper>
<Paper sx={{ p: 1, my: 1 }}> <Paper sx={{ p: 1, my: 1 }}>
<Typography variant="h4">Upgrades</Typography> <Typography variant="h4">Upgrades</Typography>
<Grid container spacing={2}>
<Grid item xs={6}>
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={purchaseMultiplier} />
</Grid>
</Grid>
<Grid container> <Grid container>
{corp.upgrades {corp.upgrades
.map((level: number, i: number) => CorporationUpgrades[i as CorporationUpgradeIndex]) .map((level: number, i: number) => CorporationUpgrades[i as CorporationUpgradeIndex])
.map((upgrade: CorporationUpgrade) => ( .map((upgrade: CorporationUpgrade) => (
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade.index} /> <LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade.index} amount={purchaseMultiplier} />
))} ))}
</Grid> </Grid>
</Paper> </Paper>
@ -265,6 +286,39 @@ function BribeButton(): React.ReactElement {
); );
} }
function SellDivisionButton(): React.ReactElement {
const [open, setOpen] = useState(false);
function sellDiv(): void {
setOpen(true);
}
return (
<>
<Tooltip title={"Sell a division to make room for other divisions"}>
<Button onClick={sellDiv}>Sell division</Button>
</Tooltip>
<SellDivisionModal open={open} onClose={() => setOpen(false)} />
</>
);
}
function RestartButton(): React.ReactElement {
const [open, setOpen] = useState(false);
function restart(): void {
setOpen(true);
}
return (
<>
<Tooltip title={"Sell corporation and start over"}>
<Button onClick={restart}>Sell CEO position</Button>
</Tooltip>
<SellCorporationModal open={open} onClose={() => setOpen(false)} />
</>
);
}
interface IDividendsStatsProps { interface IDividendsStatsProps {
profit: number; profit: number;
} }

@ -76,9 +76,9 @@ export function ProductElem(props: IProductProps): React.ReactElement {
{sellButtonText} @ <Money money={product.pCost + markupLimit} /> {sellButtonText} @ <Money money={product.pCost + markupLimit} />
</> </>
); );
} else if (product.sCost) { } else if (product.sCost[city]) {
if (isString(product.sCost)) { if (isString(product.sCost[city])) {
const sCost = (product.sCost as string).replace(/MP/g, product.pCost + product.rat / product.mku + ""); const sCost = (product.sCost[city] as string).replace(/MP/g, product.pCost + product.rat / product.mku + "");
sellButtonText = ( sellButtonText = (
<> <>
{sellButtonText} @ <Money money={eval(sCost)} /> {sellButtonText} @ <Money money={eval(sCost)} />
@ -87,7 +87,7 @@ export function ProductElem(props: IProductProps): React.ReactElement {
} else { } else {
sellButtonText = ( sellButtonText = (
<> <>
{sellButtonText} @ <Money money={product.sCost} /> {sellButtonText} @ <Money money={product.sCost[city]} />
</> </>
); );
} }
@ -138,6 +138,8 @@ export function ProductElem(props: IProductProps): React.ReactElement {
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
Effective rating is calculated from product rating and the quality of materials used <br />
Rating: {formatCorpStat(product.rat)} <br /> <br />
Quality: {formatCorpStat(product.qlt)} <br /> Quality: {formatCorpStat(product.qlt)} <br />
Performance: {formatCorpStat(product.per)} <br /> Performance: {formatCorpStat(product.per)} <br />
Durability: {formatCorpStat(product.dur)} <br /> Durability: {formatCorpStat(product.dur)} <br />
@ -151,7 +153,7 @@ export function ProductElem(props: IProductProps): React.ReactElement {
</Typography> </Typography>
} }
> >
<Typography>Rating: {formatCorpStat(product.rat)}</Typography> <Typography>Effective rating: {formatCorpStat(product.data[city][3])}</Typography>
</Tooltip> </Tooltip>
</Box> </Box>
<Box display="flex"> <Box display="flex">

@ -21,7 +21,10 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const data = props.upgradeData; const data = props.upgradeData;
const tooltip = data.desc; const tooltip = data.desc;
const price = corp.unlockUpgrades[data.index] === 0 ? data.price : 0;
function onClick(): void { function onClick(): void {
if (corp.unlockUpgrades[data.index] === 1) return;
if (corp.funds < data.price) return; if (corp.funds < data.price) return;
try { try {
UU(corp, props.upgradeData); UU(corp, props.upgradeData);
@ -34,8 +37,12 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
return ( return (
<Grid item xs={4}> <Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse"> <Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds < data.price} sx={{ mx: 1 }} onClick={onClick}> <Button
<MoneyCost money={data.price} corp={corp} /> disabled={corp.funds < data.price || corp.unlockUpgrades[data.index] === 1}
sx={{ mx: 1 }}
onClick={onClick}
>
<MoneyCost money={price} corp={corp} />
</Button> </Button>
<Tooltip title={tooltip}> <Tooltip title={tooltip}>
<Typography>{data.name}</Typography> <Typography>{data.name}</Typography>

@ -30,7 +30,7 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
if (!canSelfFund) return; if (!canSelfFund) return;
if (name == "") return; if (name == "") return;
Player.startCorporation(name); Player.startCorporation(name, false);
Player.loseMoney(150e9, "corporation"); Player.loseMoney(150e9, "corporation");
props.onClose(); props.onClose();
@ -42,7 +42,7 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
return; return;
} }
Player.startCorporation(name, 500e6); Player.startCorporation(name, true);
props.onClose(); props.onClose();
Router.toPage(Page.Corporation); Router.toPage(Page.Corporation);

@ -82,7 +82,25 @@ export function ExportModal(props: IProps): React.ReactElement {
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography> <Typography>
Select the industry and city to export this material to, as well as how much of this material to export per Select the industry and city to export this material to, as well as how much of this material to export per
second. You can set the export amount to 'MAX' to export all of the materials in this warehouse. second.
<br />
<br />
You can use 'MAX', 'EINV', 'IINV', 'EPROD' or 'IPROD' in the amount for:
<br />
- 'MAX' to export maximum amount possible.
<br />
- 'EINV' export city's inventory of the material.
<br />
- 'IINV' import city's inventory of the material.
<br />
- 'EPROD' export city's per second production of the material
<br />
- 'IPROD' import city's per second production of the material
<br />
Note: Consumption is negative production.
<br />
<br />
For example: setting the amount "(EINV-20)/10" would try to export all except 20 of the material.
</Typography> </Typography>
<Select onChange={onIndustryChange} value={industry}> <Select onChange={onIndustryChange} value={industry}>
{corp.divisions {corp.divisions

@ -55,11 +55,11 @@ export function GoPublicModal(props: IProps): React.ReactElement {
be deposited directly into your Corporation's funds). be deposited directly into your Corporation's funds).
<br /> <br />
<br /> <br />
You have a total of {formatShares(corp.numShares)} shares that you can issue. You have a total of {formatShares(corp.numShares)}-1 shares that you can issue. You cannot sell all your shares.
</Typography> </Typography>
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<NumberInput onChange={setShares} autoFocus placeholder="Shares to issue" onKeyDown={onKeyDown} /> <NumberInput onChange={setShares} autoFocus placeholder="Shares to issue" onKeyDown={onKeyDown} />
<Button disabled={shares < 0 || shares > corp.numShares} sx={{ mx: 1 }} onClick={goPublic}> <Button disabled={shares < 0 || shares >= corp.numShares} sx={{ mx: 1 }} onClick={goPublic}>
Go Public Go Public
</Button> </Button>
</Box> </Box>

@ -20,7 +20,7 @@ interface IProps {
} }
function productPlaceholder(type: string): string { function productPlaceholder(type: string): string {
if (type === IndustryType.Food) { if (type === IndustryType.Restaurant) {
return "Restaurant Name"; return "Restaurant Name";
} else if (type === IndustryType.Healthcare) { } else if (type === IndustryType.Healthcare) {
return "Hospital Name"; return "Hospital Name";

@ -1,13 +1,11 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { formatMoney, formatPreciseMultiplier } from "../../../ui/formatNumber"; import { formatMoney } from "../../../ui/formatNumber";
import { Material } from "../../Material"; import { Material } from "../../Material";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context"; import { useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import Tooltip from "@mui/material/Tooltip";
import { useRerender } from "../../../ui/React/hooks"; import { useRerender } from "../../../ui/React/hooks";
interface IMarketTA2Props { interface IMarketTA2Props {
@ -17,30 +15,7 @@ interface IMarketTA2Props {
function MarketTA2(props: IMarketTA2Props): React.ReactElement { function MarketTA2(props: IMarketTA2Props): React.ReactElement {
const division = useDivision(); const division = useDivision();
if (!division.hasResearch("Market-TA.II")) return <></>; if (!division.hasResearch("Market-TA.II")) return <></>;
const [newCost, setNewCost] = useState<number>(props.mat.bCost);
const rerender = useRerender(); const rerender = useRerender();
const markupLimit = props.mat.getMarkupLimit();
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setNewCost(0);
else setNewCost(parseFloat(event.target.value));
}
const sCost = newCost;
let markup = 1;
if (sCost > props.mat.bCost) {
//Penalty if difference between sCost and bCost is greater than markup limit
if (sCost - props.mat.bCost > markupLimit) {
markup = Math.pow(markupLimit / (sCost - props.mat.bCost), 2);
}
} else if (sCost < props.mat.bCost) {
if (sCost <= 0) {
markup = 1e12; //Sell everything, essentially discard
} else {
//Lower prices than market increases sales
markup = props.mat.bCost / sCost;
}
}
function onMarketTA2(event: React.ChangeEvent<HTMLInputElement>): void { function onMarketTA2(event: React.ChangeEvent<HTMLInputElement>): void {
props.mat.marketTa2 = event.target.checked; props.mat.marketTa2 = event.target.checked;
@ -50,28 +25,15 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
return ( return (
<> <>
<Typography variant="h4">Market-TA.II</Typography> <Typography variant="h4">Market-TA.II</Typography>
<br />
<Typography> <Typography>
If you sell at {formatMoney(sCost)}, then you will sell x{formatPreciseMultiplier(markup)} as much compared to If this is enabled, then this Material will automatically be sold at the optimal price such that the amount sold
if you sold at market price. matches the amount produced. (i.e. the highest possible price, while still ensuring that all produced materials
will be sold)
</Typography> </Typography>
<TextField type="number" onChange={onChange} value={newCost} />
<br /> <br />
<FormControlLabel <FormControlLabel
control={<Switch checked={props.mat.marketTa2} onChange={onMarketTA2} />} control={<Switch checked={props.mat.marketTa2} onChange={onMarketTA2} />}
label={ label={<Typography>Use Market-TA.II for Auto-Sale Price</Typography>}
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the optimal price such that the
amount sold matches the amount produced. (i.e. the highest possible price, while still ensuring that all
produced materials will be sold)
</Typography>
}
>
<Typography>Use Market-TA.II for Auto-Sale Price</Typography>
</Tooltip>
}
/> />
</> </>
); );
@ -103,22 +65,15 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
<Typography> <Typography>
The maximum sale price you can mark this up to is {formatMoney(props.mat.bCost + markupLimit)}. This means The maximum sale price you can mark this up to is {formatMoney(props.mat.bCost + markupLimit)}. This means
that if you set the sale price higher than this, you will begin to experience a loss in number of sales that if you set the sale price higher than this, you will begin to experience a loss in number of sales
<br></br>
<br></br>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I (i.e.
the price shown above)
</Typography> </Typography>
<FormControlLabel <FormControlLabel
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />} control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
label={ label={<Typography>Use Market-TA.I for Auto-Sale Price</Typography>}
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the price identified by
Market-TA.I (i.e. the price shown above)
</Typography>
}
>
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
</Tooltip>
}
/> />
</> </>

@ -1,13 +1,11 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { formatMoney, formatPreciseMultiplier } from "../../../ui/formatNumber"; import { formatMoney } from "../../../ui/formatNumber";
import { Product } from "../../Product"; import { Product } from "../../Product";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context"; import { useDivision } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel"; import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import Tooltip from "@mui/material/Tooltip";
import { useRerender } from "../../../ui/React/hooks"; import { useRerender } from "../../../ui/React/hooks";
interface ITa2Props { interface ITa2Props {
@ -17,52 +15,26 @@ interface ITa2Props {
function MarketTA2(props: ITa2Props): React.ReactElement { function MarketTA2(props: ITa2Props): React.ReactElement {
const division = useDivision(); const division = useDivision();
if (!division.hasResearch("Market-TA.II")) return <></>; if (!division.hasResearch("Market-TA.II")) return <></>;
const markupLimit = props.product.rat / props.product.mku;
const [value, setValue] = useState(props.product.pCost);
const rerender = useRerender(); const rerender = useRerender();
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setValue(parseFloat(event.target.value));
}
function onCheckedChange(event: React.ChangeEvent<HTMLInputElement>): void { function onCheckedChange(event: React.ChangeEvent<HTMLInputElement>): void {
props.product.marketTa2 = event.target.checked; props.product.marketTa2 = event.target.checked;
rerender(); rerender();
} }
const sCost = value;
let markup = 1;
if (sCost > props.product.pCost) {
if (sCost - props.product.pCost > markupLimit) {
markup = markupLimit / (sCost - props.product.pCost);
}
}
return ( return (
<> <>
<Typography variant="h4">Market-TA.II</Typography> <Typography variant="h4">Market-TA.II</Typography>
<br /> <br />
<Typography> <Typography>
If you sell at {formatMoney(sCost)}, then you will sell x{formatPreciseMultiplier(markup)} as much compared to If this is enabled, then this product will automatically be sold at the optimal price such that the amount sold
if you sold at market price. matches the amount produced. (i.e. the highest possible price, while still ensuring that all produced materials
will be sold)
</Typography> </Typography>
<TextField type="number" onChange={onChange} value={value} />
<br /> <br />
<FormControlLabel <FormControlLabel
control={<Switch checked={props.product.marketTa2} onChange={onCheckedChange} />} control={<Switch checked={props.product.marketTa2} onChange={onCheckedChange} />}
label={ label={<Typography>Use Market-TA.II for Auto-Sale Price</Typography>}
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the optimal price such that the
amount sold matches the amount produced. (i.e. the highest possible price, while still ensuring that all
produced materials will be sold)
</Typography>
}
>
<Typography>Use Market-TA.II for Auto-Sale Price</Typography>
</Tooltip>
}
/> />
</> </>
); );
@ -94,22 +66,15 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
<Typography> <Typography>
The maximum sale price you can mark this up to is {formatMoney(props.product.pCost + markupLimit)}. This means The maximum sale price you can mark this up to is {formatMoney(props.product.pCost + markupLimit)}. This means
that if you set the sale price higher than this, you will begin to experience a loss in number of sales that if you set the sale price higher than this, you will begin to experience a loss in number of sales
<br></br>
<br></br>
If this is enabled, then this product will automatically be sold at the price identified by Market-TA.I (i.e.
the price shown above)
</Typography> </Typography>
<FormControlLabel <FormControlLabel
control={<Switch checked={props.product.marketTa1} onChange={onChange} />} control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
label={ label={<Typography>Use Market-TA.I for Auto-Sale Price</Typography>}
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the price identified by
Market-TA.I (i.e. the price shown above)
</Typography>
}
>
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
</Tooltip>
}
/> />
</> </>

@ -6,7 +6,7 @@ import { Material } from "../../Material";
import { formatMatPurchaseAmount, formatMoney } from "../../../ui/formatNumber"; import { formatMatPurchaseAmount, formatMoney } from "../../../ui/formatNumber";
import { BulkPurchase, BuyMaterial } from "../../Actions"; import { BulkPurchase, BuyMaterial } from "../../Actions";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useCorporation, useDivision } from "../Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -110,7 +110,6 @@ interface IProps {
// Create a popup that lets the player purchase a Material // Create a popup that lets the player purchase a Material
export function PurchaseMaterialModal(props: IProps): React.ReactElement { export function PurchaseMaterialModal(props: IProps): React.ReactElement {
const division = useDivision();
const [buyAmt, setBuyAmt] = useState(props.mat.buy ? props.mat.buy : 0); const [buyAmt, setBuyAmt] = useState(props.mat.buy ? props.mat.buy : 0);
function purchaseMaterial(): void { function purchaseMaterial(): void {
@ -160,9 +159,7 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement {
<Button disabled={props.disablePurchaseLimit} onClick={clearPurchase}> <Button disabled={props.disablePurchaseLimit} onClick={clearPurchase}>
Clear Purchase Clear Purchase
</Button> </Button>
{division.hasResearch("Bulk Purchasing") && ( {<BulkPurchaseSection onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />}
<BulkPurchaseSection onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />
)}
</> </>
</Modal> </Modal>
); );

@ -0,0 +1,71 @@
import React, { useState } from "react";
import { Money } from "../../../ui/React/Money";
import { Modal } from "../../../ui/React/Modal";
import { Router } from "../../../ui/GameRoot";
import { Page } from "../../../ui/Router";
import { Player } from "@player";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
interface IProps {
open: boolean;
onClose: () => void;
}
export function SellCorporationModal(props: IProps): React.ReactElement {
let cost = 150e9;
if (!Player.corporation?.seedFunded) {
cost /= 3;
}
const canSelfFund = Player.canAfford(cost);
const [name, setName] = useState("");
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value);
}
function selfFund(): void {
if (!canSelfFund) return;
if (name == "") return;
Player.startCorporation(name, false);
Player.loseMoney(cost, "corporation");
props.onClose();
Router.toPage(Page.Corporation);
}
function seed(): void {
if (name == "") {
return;
}
Player.startCorporation(name, true);
props.onClose();
Router.toPage(Page.Corporation);
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Would you like to sell your position as CEO and start a new corporation? Everything from your current
corporation will be gone and you start fresh.
<br />
<br />
If you would like to start new one, please enter a name for your corporation below:
</Typography>
<TextField autoFocus={true} placeholder="Corporation Name" onChange={onChange} value={name} />
{Player.bitNodeN === 3 && (
<Button onClick={seed} disabled={name == ""}>
Use seed money
</Button>
)}
<Button onClick={selfFund} disabled={name == "" || !canSelfFund}>
Self-Fund (<Money money={cost} forPurchase={true} />)
</Button>
</Modal>
);
}

@ -0,0 +1,100 @@
import React, { useState } from "react";
import { Modal } from "../../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { useCorporation } from "../../ui/Context";
import { Industry } from "src/Corporation/Industry";
import { CityName } from "@nsdefs";
import * as corpConstants from "../../data/Constants";
import { formatMoney, formatNumber } from "../../../ui/formatNumber";
import { removeIndustry } from "../../Actions";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
interface IProps {
open: boolean;
onClose: () => void;
}
export function SellDivisionModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const allIndustries = Object.values(corp.divisions).sort();
const [industry, setIndustry] = useState(allIndustries[0]);
const price = calculatePrice();
function calculatePrice() {
let price = industry.startingCost;
for (const city in industry.offices) {
if (industry.offices[city as CityName] === 0) continue;
if (city === "Sector-12") continue;
price += corpConstants.officeInitialCost;
if (industry.warehouses[city as CityName] !== 0) price += corpConstants.warehouseInitialCost;
}
price /= 2;
return price;
}
function onIndustryChange(event: SelectChangeEvent<string>): void {
setIndustry(corp.divisions.find((div) => div.name === event.target.value) as Industry);
}
function sum(total: number, num: number) {
return total + num;
}
function sellDivision() {
removeIndustry(corp, industry.name);
corp.funds += price;
props.onClose();
dialogBoxCreate(
<>
Sold {industry.name} for {formatMoney(price)}, you now have space for{" "}
{corp.maxDivisions - corp.divisions.length} more divisions.
</>,
);
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Would you like to sell a division?
<br></br>
You'll get back half the money you've spent on starting the division and expanding to offices and warehouses.
</Typography>
<Select value={industry.name} onChange={onIndustryChange}>
{allIndustries.map((industry) => (
<MenuItem key={industry.name} value={industry.name}>
{industry.name}
</MenuItem>
))}
</Select>
<Typography>
Division {industry.name} has:
<br></br>- Profit: ${formatNumber((industry.lastCycleRevenue - industry.lastCycleExpenses) / 10)} / sec
<br></br>- Cities:{" "}
{Object.keys(industry.offices)
.map((city) => (!!industry.offices[city as CityName] ? 1 : 0))
.reduce(sum, 0)}
<br></br>- Warehouses:{" "}
{Object.keys(industry.warehouses)
.map((city) => (!!industry.warehouses[city as CityName] ? 1 : 0))
.reduce(sum, 0)}
{industry.makesProducts ?? (
<Typography>
{" "}
<br></br>- Products: {industry.products.length}{" "}
</Typography>
)}
<br></br>
<br></br>
Sell price: {formatNumber(price)}
</Typography>
<Button disabled={false} onClick={sellDivision}>
Sell division
</Button>
</Modal>
);
}

@ -11,8 +11,8 @@ import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch"; import Switch from "@mui/material/Switch";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
function initialPrice(product: Product): string { function initialPrice(product: Product, city: string): string {
let val = product.sCost ? product.sCost + "" : ""; let val = product.sCost[city] ? product.sCost[city] + "" : "";
if (product.marketTa2) { if (product.marketTa2) {
val += " (Market-TA.II)"; val += " (Market-TA.II)";
} else if (product.marketTa1) { } else if (product.marketTa1) {
@ -34,7 +34,7 @@ export function SellProductModal(props: IProps): React.ReactElement {
const [iQty, setQty] = useState<string>( const [iQty, setQty] = useState<string>(
props.product.sllman[props.city][1] ? props.product.sllman[props.city][1] : "", props.product.sllman[props.city][1] ? props.product.sllman[props.city][1] : "",
); );
const [px, setPx] = useState<string>(initialPrice(props.product)); const [px, setPx] = useState<string>(initialPrice(props.product, props.city));
function onCheckedChange(event: React.ChangeEvent<HTMLInputElement>): void { function onCheckedChange(event: React.ChangeEvent<HTMLInputElement>): void {
setChecked(event.target.checked); setChecked(event.target.checked);

@ -10,6 +10,7 @@ import { Money } from "../../../ui/React/Money";
import { SellShares } from "../../Actions"; import { SellShares } from "../../Actions";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
import { NumberInput } from "../../../ui/React/NumberInput"; import { NumberInput } from "../../../ui/React/NumberInput";
import { isInteger } from "lodash";
interface IProps { interface IProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
@ -22,15 +23,19 @@ export function SellSharesModal(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState<number>(NaN); const [shares, setShares] = useState<number>(NaN);
const disabled = isNaN(shares) || shares <= 0 || shares > corp.numShares; const disabled = isNaN(shares) || shares <= 0 || shares >= corp.numShares;
function ProfitIndicator(props: { shares: number | null; corp: Corporation }): React.ReactElement { function ProfitIndicator(props: { shares: number | null; corp: Corporation }): React.ReactElement {
if (props.shares === null) return <></>; if (props.shares === null) return <></>;
let text = ""; let text = "";
if (isNaN(props.shares) || props.shares <= 0) { if (isNaN(props.shares) || props.shares <= 0 || !isInteger(props.shares)) {
text = `ERROR: Invalid value entered for number of shares to sell`; text = `ERROR: Invalid value entered for number of shares to sell`;
} else if (props.shares > corp.numShares) { } else if (props.shares > corp.numShares) {
text = `You don't have this many shares to sell!`; text = `You don't have this many shares to sell!`;
} else if (props.shares === corp.numShares) {
text = `You can not sell all your shares!`;
} else if (props.shares > 1e14) {
text = `You can't sell more than 100t shares at once!`;
} else { } else {
const stockSaleResults = corp.calculateShareSale(props.shares); const stockSaleResults = corp.calculateShareSale(props.shares);
const profit = stockSaleResults[0]; const profit = stockSaleResults[0];
@ -74,6 +79,9 @@ export function SellSharesModal(props: IProps): React.ReactElement {
(NOT your Corporation). (NOT your Corporation).
<br /> <br />
<br /> <br />
The amount sold must be an integer between 1 and 100t.
<br />
<br />
Selling your shares will cause your corporation's stock price to fall due to dilution. Furthermore, selling a Selling your shares will cause your corporation's stock price to fall due to dilution. Furthermore, selling a
large number of shares all at once will have an immediate effect in reducing your stock price. large number of shares all at once will have an immediate effect in reducing your stock price.
<br /> <br />

@ -1,7 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Warehouse } from "../../Warehouse"; import { Warehouse } from "../../Warehouse";
import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../../Actions"; import { SetSmartSupply, SetSmartSupplyOption } from "../../Actions";
import { dialogBoxCreate } from "../../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal"; import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context"; import { useDivision } from "../Context";
@ -12,30 +12,50 @@ import { CorpMaterialName } from "@nsdefs";
import { materialNames } from "../../data/Constants"; import { materialNames } from "../../data/Constants";
import { useRerender } from "../../../ui/React/hooks"; import { useRerender } from "../../../ui/React/hooks";
interface ILeftoverProps { interface ISSoptionProps {
matName: CorpMaterialName; matName: CorpMaterialName;
warehouse: Warehouse; warehouse: Warehouse;
} }
function Leftover(props: ILeftoverProps): React.ReactElement { function SSoption(props: ISSoptionProps): React.ReactElement {
const [checked, setChecked] = useState(!!props.warehouse.smartSupplyUseLeftovers[props.matName]); const [value, setChecked] = useState(props.warehouse.smartSupplyOptions[props.matName]);
function onChange(event: React.ChangeEvent<HTMLInputElement>): void { //leftover switch
function onLOChange(): void {
const newValue = value != "leftovers" ? "leftovers" : "none";
try { try {
const matName = props.matName; const matName = props.matName;
const material = props.warehouse.materials[matName]; const material = props.warehouse.materials[matName];
SetSmartSupplyUseLeftovers(props.warehouse, material, event.target.checked); SetSmartSupplyOption(props.warehouse, material, newValue);
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }
setChecked(event.target.checked); setChecked(newValue);
}
//imports switch
function onIChange(): void {
const newValue = value != "imports" ? "imports" : "none";
try {
const matName = props.matName;
const material = props.warehouse.materials[matName];
SetSmartSupplyOption(props.warehouse, material, newValue);
} catch (err) {
dialogBoxCreate(err + "");
}
setChecked(newValue);
} }
return ( return (
<> <>
label={<Typography>{props.warehouse.materials[props.matName].name}</Typography>}
<FormControlLabel <FormControlLabel
control={<Switch checked={checked} onChange={onChange} />} control={<Switch checked={value == "leftovers"} onChange={onLOChange} />}
label={<Typography>{props.warehouse.materials[props.matName].name}</Typography>} label={<Typography>{"Use leftovers"}</Typography>}
/>
<FormControlLabel
control={<Switch checked={value == "imports"} onChange={onIChange} />}
label={<Typography>{"Use imported"}</Typography>}
/> />
<br /> <br />
</> </>
@ -63,18 +83,29 @@ export function SmartSupplyModal(props: IProps): React.ReactElement {
for (const matName of Object.values(materialNames)) { for (const matName of Object.values(materialNames)) {
if (!props.warehouse.materials[matName]) continue; if (!props.warehouse.materials[matName]) continue;
if (!Object.keys(division.reqMats).includes(matName)) continue; if (!Object.keys(division.reqMats).includes(matName)) continue;
mats.push(<Leftover key={matName} warehouse={props.warehouse} matName={matName} />); mats.push(<SSoption key={matName} warehouse={props.warehouse} matName={matName} />);
} }
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<> <>
<Typography>Smart Supply purchases the exact amount of materials needed for maximal production.</Typography>
<br />
<FormControlLabel <FormControlLabel
control={<Switch checked={props.warehouse.smartSupplyEnabled} onChange={smartSupplyOnChange} />} control={<Switch checked={props.warehouse.smartSupplyEnabled} onChange={smartSupplyOnChange} />}
label={<Typography>Enable Smart Supply</Typography>} label={<Typography>Enable Smart Supply</Typography>}
/> />
<br /> <br />
<Typography>Use materials already in the warehouse instead of buying new ones, if available:</Typography> <Typography>
Options:
<br />
- Use leftovers takes the amount of that material already in storage into account when purchasing new ones.
<br />
- Use imported takes the amount of that materials that was imported in previous cycle into account.
<br />
if neither is toggled on, Smart Supply will ignore any materials in store and attempts to buy as much as is
needed for production.
</Typography>
{mats} {mats}
</> </>
</Modal> </Modal>

@ -38,12 +38,12 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
dialogBoxCreate("You don't have enough company funds to throw a party!"); dialogBoxCreate("You don't have enough company funds to throw a party!");
} else { } else {
const mult = ThrowParty(corp, props.office, cost); const mult = ThrowParty(corp, props.office, cost);
// Each 5% multiplier gives an extra flat +1 to morale and happiness to make recovering from low morale easier. // Each 5% multiplier gives an extra flat +1 to morale to make recovering from low morale easier.
const increase = mult > 1 ? (mult - 1) * 0.2 : 0; const increase = mult > 1 ? (mult - 1) * 0.2 : 0;
if (mult > 0) { if (mult > 0) {
dialogBoxCreate( dialogBoxCreate(
"You threw a party for the office! The morale and happiness of each employee increased by " + "You threw a party for the office! The morale of each employee increased by " +
formatPercent(increase) + formatPercent(increase) +
" and was multiplied by " + " and was multiplied by " +
formatMultiplier(mult), formatMultiplier(mult),

@ -37,7 +37,7 @@ export function General(): React.ReactElement {
// Corp functions // Corp functions
const createCorporation = () => { const createCorporation = () => {
Player.startCorporation(corporationName); Player.startCorporation(corporationName, false);
// Rerender so the corp menu option will show up immediately on the devmenu page selection // Rerender so the corp menu option will show up immediately on the devmenu page selection
ThemeEvents.emit(); ThemeEvents.emit();
}; };

@ -34,49 +34,54 @@ export const Literatures: Record<string, Literature> = {};
fn = LiteratureNames.CorporationManagementHandbook; fn = LiteratureNames.CorporationManagementHandbook;
txt = txt =
"<u>Getting Started with Corporations</u><br>" + "<u>Getting Started with Corporations</u><br>" +
"To get started, visit the City Hall in Sector-12 in order to create a Corporation. This requires " + "To get started, visit the City Hall in Sector-12 in order to create a Corporation. This requires $150b of your own money, " +
"$150b of your own money, but this $150b will get put into your Corporation's funds. " + "but this $150b will get put into your Corporation's funds. If you're in BitNode 3 you also have option to get seed money from " +
"Your Corporation can have many different divisions, each in a different Industry. There are many different " + "the government in exchange for 500m shares. Your Corporation can have many different divisions, each in a different Industry. " +
"types of Industries, each with different properties. To create your first division, click the " + "There are many different types of Industries, each with different properties. To create your first division, click the 'Expand' " +
"'Expand into new Industry' button at the top of the management UI. The Agriculture " + "(into new Industry) button at the top of the management UI. The Agriculture industry is recommended for your first division.<br><br>" +
"and Software industries are recommended for your first division.<br><br>" + "The first thing you'll need to do is hire some employees. Employees can be assigned to five different positions. Each position has a " +
"The first thing you'll need to do is hire some employees. Employees can be assigned to five different positions. " + "different effect on various aspects of your Corporation. It is recommended to have at least one employee at each position.<br><br>" +
"Each position has a different effect on various aspects of your Corporation. It is recommended to have at least " + "Each industry uses some combination of Materials in order to produce other Materials and/or create Products. Specific information " +
"one employee at each position.<br><br>" + "about this is displayed in each of your divisions' UI.<br><br>" +
"Each industry uses some combination of Materials in order to produce other Materials and/or create Products. " + "Products are special, industry-specific objects. They are different than Materials because you must manually choose to develop them, " +
"Specific information about this is displayed in each of your divisions' UI.<br><br>" + "and you can choose to develop any number of Products. Developing a Product takes time, but a Product typically generates significantly " +
"Products are special, industry-specific objects. They are different than Materials because you " + "more revenue than any Material. Not all industries allow you to create Products. To create a Product, look for a button in the top-left " +
"must manually choose to develop them, and you can choose to develop any number of Products. Developing " + "panel of the division UI (e.g. For the Software Industry, the button says 'Develop Software').<br><br>" +
"a Product takes time, but a Product typically generates significantly more revenue than any Material. " + "To get your supply chain system started, purchase the Materials that your industry needs to produce other Materials/Products. This can be " +
"Not all industries allow you to create Products. To create a Product, look for a button " + "done by clicking the 'Buy' button next to the corresponding Material(s). After you have the required Materials, you will immediately start " +
"in the top-left panel of the division UI (e.g. For the Software Industry, the button says 'Develop Software').<br><br>" + "production. The amount and quality/effective rating of Materials/Products you produce is based on a variety of factors, such as your employees " +
"To get your supply chain system started, " + "and their productivity and the quality of materials used for production.<br><br>" +
"purchase the Materials that your industry needs to produce other Materials/Products. This can be done " + "Once you start producing Materials/Products, you can sell them in order to start earning revenue. This can be done by clicking the 'Sell' " +
"by clicking the 'Buy' button next to the corresponding Material(s). After you have the required Materials, " + "button next to the corresponding Material or Product. The amount of Material/Product you sell is dependent on a wide variety of different factors. " +
"you will immediately start production. The amount of Materials/Products you produce is based on a variety of factors, " + "In order to produce and sell a Product you'll have to fully develop it first.<br><br>" +
"one of which is your employees and their productivity.<br><br>" + "These are the basics of getting your Corporation up and running! Now, you can start purchasing upgrades to improve your bottom line. " +
"Once you start producing Materials/Products, you can sell them in order to start earning revenue. This can be done " + "If you need money, consider looking for seed investors, who will give you money in exchange for stock shares. Otherwise, once you feel " +
"by clicking the 'Sell' button next to the corresponding Material or Product. The amount of Material/Product you sell is dependent " + "you are ready, take your Corporation public! Once your Corporation goes public, you can no longer find investors. Instead, your Corporation " +
"on a wide variety of different factors.<br><br>" + "will be publicly traded and its stock price will change based on how well it's performing financially. In order to make money for yourself you " +
"These are the basics of getting your Corporation up and running! Now, you can start purchasing upgrades to improve " + "can set dividends for a solid reliable income or you can sell your stock shares in order to make quick money.<br><br>" +
"your bottom line. If you need money, consider looking for seed investors, who will give you money in exchange for stock shares. " +
"Otherwise, once you feel you are ready, take your Corporation public! Once your Corporation goes public, you can no longer " +
"find investors. Instead, your Corporation will be publicly traded and its stock price will change based on how well " +
"it's performing financially. You can then sell your stock shares in order to make money.<br><br>" +
"<u>Tips/Pointers</u><br>" + "<u>Tips/Pointers</u><br>" +
"-Start with one division, such as Agriculture. Get it profitable on it's own, then expand to a division that consumes/produces " +
"a material that the division you selected produces/consumes.<br><br>" +
"-Materials are profitable, but Products are where the real money is, although if the product had a low development budget or is " +
"produced with low quality materials it won't sell well.<br><br>" +
"-The 'Smart Supply' upgrade is extremely useful. Consider purchasing it as soon as possible.<br><br>" + "-The 'Smart Supply' upgrade is extremely useful. Consider purchasing it as soon as possible.<br><br>" +
"-Purchasing Hardware, Robots, AI Cores, and Real Estate can potentially increase your production. " + "-Purchasing Hardware, Robots, AI Cores, and Real Estate can potentially increase your production. The effects of these depend on " +
"The effects of these depend on what industry you are in.<br><br>" + "what industry you are in.<br><br>" +
"-In order to optimize your production, you will need a good balance of Operators, Managers, and Engineers<br><br>" + "-In order to optimize your production, you will need a good balance of all employee positions, about 1/9 should be interning<br><br>" +
"-Different employees excel in different jobs. For example, the highly intelligent employees will probably do best " + "-Quality of materials used for production affects the quality/effective rating of materials/products produced, so vertical integration " +
"if they are assigned to do Engineering work or Research & Development.<br><br>" + "is important for high profits.<br><br>" +
"-If your employees have low morale, energy, or happiness, their production will greatly suffer.<br><br>" + "-Materials purchased from the open market are always of quality 1.<br><br>" +
"-Tech is important, but don't neglect sales! Having several Businessmen can boost your sales and your bottom line.<br><br>" + "-The price at which you can sell your Materials/Products is highly affected by the quality/effective rating<br><br>" +
"-When developing a product, different employee positions affect the development process differently, " +
"some improve the development speed, some improve the rating of the finished product.<br><br>" +
"-If your employees have low morale or energy, their production will greatly suffer. Having enough interns will make sure those stats get " +
"high and stay high.<br><br>" +
"-Don't forget to advertise your company. You won't have any business if nobody knows you.<br><br>" + "-Don't forget to advertise your company. You won't have any business if nobody knows you.<br><br>" +
"-Having company awareness is great, but what's really important is your company's popularity. Try to keep " + "-Having company awareness is great, but what's really important is your company's popularity. Try to keep your popularity as high as " +
"your popularity as high as possible to see the biggest benefit for your sales<br><br>" + "possible to see the biggest benefit for your sales<br><br>" +
"-Remember, you need to spend money to make money!<br><br>" + "-Remember, you need to spend money to make money!<br><br>" +
"-Corporations do not reset when installing Augmentations, but they do reset when destroying a BitNode"; "-Corporations do not reset when installing Augmentations, but they do reset when destroying a BitNode";
Literatures[fn] = new Literature(title, fn, txt); Literatures[fn] = new Literature(title, fn, txt);
title = "A Brief History of Synthoids"; title = "A Brief History of Synthoids";

@ -373,7 +373,7 @@ const corporation = {
sellProduct: RamCostConstants.Corporation, sellProduct: RamCostConstants.Corporation,
discontinueProduct: RamCostConstants.Corporation, discontinueProduct: RamCostConstants.Corporation,
setSmartSupply: RamCostConstants.Corporation, setSmartSupply: RamCostConstants.Corporation,
setSmartSupplyUseLeftovers: RamCostConstants.Corporation, setSmartSupplyOption: RamCostConstants.Corporation,
buyMaterial: RamCostConstants.Corporation, buyMaterial: RamCostConstants.Corporation,
bulkPurchase: RamCostConstants.Corporation, bulkPurchase: RamCostConstants.Corporation,
getWarehouse: RamCostConstants.Corporation, getWarehouse: RamCostConstants.Corporation,
@ -395,7 +395,7 @@ const corporation = {
hireEmployee: RamCostConstants.Corporation, hireEmployee: RamCostConstants.Corporation,
upgradeOfficeSize: RamCostConstants.Corporation, upgradeOfficeSize: RamCostConstants.Corporation,
throwParty: RamCostConstants.Corporation, throwParty: RamCostConstants.Corporation,
buyCoffee: RamCostConstants.Corporation, buyTea: RamCostConstants.Corporation,
hireAdVert: RamCostConstants.Corporation, hireAdVert: RamCostConstants.Corporation,
research: RamCostConstants.Corporation, research: RamCostConstants.Corporation,
getOffice: RamCostConstants.Corporation, getOffice: RamCostConstants.Corporation,

@ -32,7 +32,7 @@ import {
UpgradeOfficeSize, UpgradeOfficeSize,
PurchaseWarehouse, PurchaseWarehouse,
UpgradeWarehouse, UpgradeWarehouse,
BuyCoffee, BuyTea,
ThrowParty, ThrowParty,
HireAdVert, HireAdVert,
MakeProduct, MakeProduct,
@ -46,7 +46,7 @@ import {
BulkPurchase, BulkPurchase,
SellShares, SellShares,
BuyBackShares, BuyBackShares,
SetSmartSupplyUseLeftovers, SetSmartSupplyOption,
LimitMaterialProduction, LimitMaterialProduction,
LimitProductProduction, LimitProductProduction,
UpgradeWarehouseCost, UpgradeWarehouseCost,
@ -76,10 +76,10 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
if (selfFund) { if (selfFund) {
if (!player.canAfford(150e9)) return false; if (!player.canAfford(150e9)) return false;
player.startCorporation(corporationName); player.startCorporation(corporationName, false);
player.loseMoney(150e9, "corporation"); player.loseMoney(150e9, "corporation");
} else { } else {
player.startCorporation(corporationName, 500e6); player.startCorporation(corporationName, true);
} }
return true; return true;
} }
@ -243,7 +243,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
return material; return material;
} }
function getProduct(divisionName: string, productName: string): Product { function getProduct(divisionName: string, cityName: string, productName: string): Product {
const division = getDivision(divisionName); const division = getDivision(divisionName);
const product = division.products[productName]; const product = division.products[productName];
if (product === undefined) throw new Error(`Invalid product name: '${productName}'`); if (product === undefined) throw new Error(`Invalid product name: '${productName}'`);
@ -331,6 +331,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
return { return {
cost: material.bCost, cost: material.bCost,
sCost: material.sCost, sCost: material.sCost,
sAmt: material.sllman[1],
name: material.name, name: material.name,
qty: material.qty, qty: material.qty,
qlt: material.qlt, qlt: material.qlt,
@ -341,17 +342,19 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
exp: exports, exp: exports,
}; };
}, },
getProduct: (ctx) => (_divisionName, _productName) => { getProduct: (ctx) => (_divisionName, _cityName, _productName) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
const divisionName = helpers.string(ctx, "divisionName", _divisionName); const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const productName = helpers.string(ctx, "productName", _productName); const productName = helpers.string(ctx, "productName", _productName);
const product = getProduct(divisionName, productName); const cityName = helpers.city(ctx, "cityName", _cityName);
const product = getProduct(divisionName, cityName, productName);
const corporation = getCorporation(); const corporation = getCorporation();
return { return {
name: product.name, name: product.name,
dmd: corporation.unlockUpgrades[2] ? product.dmd : undefined, dmd: corporation.unlockUpgrades[2] ? product.dmd : undefined,
cmp: corporation.unlockUpgrades[3] ? product.cmp : undefined, cmp: corporation.unlockUpgrades[3] ? product.cmp : undefined,
rat: product.rat, rat: product.rat,
effRat: product.data[cityName][3],
properties: { properties: {
qlt: product.qlt, qlt: product.qlt,
per: product.per, per: product.per,
@ -361,8 +364,11 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
fea: product.fea, fea: product.fea,
}, },
pCost: product.pCost, pCost: product.pCost,
sCost: product.sCost, sCost: product.sCost[cityName],
cityData: product.data, sAmt: product.sllman[cityName][1],
qty: product.data[cityName][0],
prod: product.data[cityName][1],
sell: product.data[cityName][2],
developmentProgress: product.prog, developmentProgress: product.prog,
}; };
}, },
@ -406,14 +412,14 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
const amt = helpers.string(ctx, "amt", _amt); const amt = helpers.string(ctx, "amt", _amt);
const price = helpers.string(ctx, "price", _price); const price = helpers.string(ctx, "price", _price);
const all = !!_all; const all = !!_all;
const product = getProduct(divisionName, productName); const product = getProduct(divisionName, cityName, productName);
SellProduct(product, cityName, amt, price, all); SellProduct(product, cityName, amt, price, all);
}, },
discontinueProduct: (ctx) => (_divisionName, _productName) => { discontinueProduct: (ctx) => (_divisionName, _productName) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
const divisionName = helpers.string(ctx, "divisionName", _divisionName); const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const productName = helpers.string(ctx, "productName", _productName); const productName = helpers.string(ctx, "productName", _productName);
getDivision(divisionName).discontinueProduct(getProduct(divisionName, productName)); getDivision(divisionName).discontinueProduct(getProduct(divisionName, "Sector-12", productName));
}, },
setSmartSupply: (ctx) => (_divisionName, _cityName, _enabled) => { setSmartSupply: (ctx) => (_divisionName, _cityName, _enabled) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
@ -425,17 +431,17 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
throw helpers.makeRuntimeErrorMsg(ctx, `You have not purchased the Smart Supply upgrade!`); throw helpers.makeRuntimeErrorMsg(ctx, `You have not purchased the Smart Supply upgrade!`);
SetSmartSupply(warehouse, enabled); SetSmartSupply(warehouse, enabled);
}, },
setSmartSupplyUseLeftovers: (ctx) => (_divisionName, _cityName, materialName, _enabled) => { setSmartSupplyOption: (ctx) => (_divisionName, _cityName, materialName, _option) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
const divisionName = helpers.string(ctx, "divisionName", _divisionName); const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const cityName = helpers.city(ctx, "cityName", _cityName); const cityName = helpers.city(ctx, "cityName", _cityName);
assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName); assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName);
const enabled = !!_enabled;
const warehouse = getWarehouse(divisionName, cityName); const warehouse = getWarehouse(divisionName, cityName);
const material = getMaterial(divisionName, cityName, materialName); const material = getMaterial(divisionName, cityName, materialName);
const option = helpers.string(ctx, "option", _option);
if (!hasUnlockUpgrade("Smart Supply")) if (!hasUnlockUpgrade("Smart Supply"))
throw helpers.makeRuntimeErrorMsg(ctx, `You have not purchased the Smart Supply upgrade!`); throw helpers.makeRuntimeErrorMsg(ctx, `You have not purchased the Smart Supply upgrade!`);
SetSmartSupplyUseLeftovers(warehouse, material, enabled); SetSmartSupplyOption(warehouse, material, option);
}, },
buyMaterial: (ctx) => (_divisionName, _cityName, materialName, _amt) => { buyMaterial: (ctx) => (_divisionName, _cityName, materialName, _amt) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
@ -451,8 +457,6 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
bulkPurchase: (ctx) => (_divisionName, _cityName, materialName, _amt) => { bulkPurchase: (ctx) => (_divisionName, _cityName, materialName, _amt) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
const divisionName = helpers.string(ctx, "divisionName", _divisionName); const divisionName = helpers.string(ctx, "divisionName", _divisionName);
if (!hasResearched(getDivision(divisionName), "Bulk Purchasing"))
throw new Error(`You have not researched Bulk Purchasing in ${divisionName}`);
const corporation = getCorporation(); const corporation = getCorporation();
const cityName = helpers.city(ctx, "cityName", _cityName); const cityName = helpers.city(ctx, "cityName", _cityName);
assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName); assertMember(ctx, corpConstants.materialNames, "Material Name", "materialName", materialName);
@ -479,7 +483,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
const cityName = helpers.city(ctx, "cityName", _cityName); const cityName = helpers.city(ctx, "cityName", _cityName);
const productName = helpers.string(ctx, "productName", _productName); const productName = helpers.string(ctx, "productName", _productName);
const qty = helpers.number(ctx, "qty", _qty); const qty = helpers.number(ctx, "qty", _qty);
LimitProductProduction(getProduct(divisionName, productName), cityName, qty); LimitProductProduction(getProduct(divisionName, cityName, productName), cityName, qty);
}, },
exportMaterial: exportMaterial:
(ctx) => (ctx) =>
@ -539,23 +543,25 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.II for division: ${divisionName}`); throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.II for division: ${divisionName}`);
SetMaterialMarketTA2(getMaterial(divisionName, cityName, materialName), on); SetMaterialMarketTA2(getMaterial(divisionName, cityName, materialName), on);
}, },
setProductMarketTA1: (ctx) => (_divisionName, _productName, _on) => { setProductMarketTA1: (ctx) => (_divisionName, _cityName, _productName, _on) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
const divisionName = helpers.string(ctx, "divisionName", _divisionName); const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const cityName = helpers.city(ctx, "cityName", _cityName);
const productName = helpers.string(ctx, "productName", _productName); const productName = helpers.string(ctx, "productName", _productName);
const on = !!_on; const on = !!_on;
if (!getDivision(divisionName).hasResearch("Market-TA.I")) if (!getDivision(divisionName).hasResearch("Market-TA.I"))
throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.I for division: ${divisionName}`); throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.I for division: ${divisionName}`);
SetProductMarketTA1(getProduct(divisionName, productName), on); SetProductMarketTA1(getProduct(divisionName, cityName, productName), on);
}, },
setProductMarketTA2: (ctx) => (_divisionName, _productName, _on) => { setProductMarketTA2: (ctx) => (_divisionName, _cityName, _productName, _on) => {
checkAccess(ctx, 7); checkAccess(ctx, 7);
const divisionName = helpers.string(ctx, "divisionName", _divisionName); const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const cityName = helpers.city(ctx, "cityName", _cityName);
const productName = helpers.string(ctx, "productName", _productName); const productName = helpers.string(ctx, "productName", _productName);
const on = !!_on; const on = !!_on;
if (!getDivision(divisionName).hasResearch("Market-TA.II")) if (!getDivision(divisionName).hasResearch("Market-TA.II"))
throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.II for division: ${divisionName}`); throw helpers.makeRuntimeErrorMsg(ctx, `You have not researched MarketTA.II for division: ${divisionName}`);
SetProductMarketTA2(getProduct(divisionName, productName), on); SetProductMarketTA2(getProduct(divisionName, cityName, productName), on);
}, },
}; };
@ -660,14 +666,14 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
return ThrowParty(corporation, office, costPerEmployee); return ThrowParty(corporation, office, costPerEmployee);
}, },
buyCoffee: (ctx) => (_divisionName, _cityName) => { buyTea: (ctx) => (_divisionName, _cityName) => {
checkAccess(ctx, 8); checkAccess(ctx, 8);
const divisionName = helpers.string(ctx, "divisionName", _divisionName); const divisionName = helpers.string(ctx, "divisionName", _divisionName);
const cityName = helpers.city(ctx, "cityName", _cityName); const cityName = helpers.city(ctx, "cityName", _cityName);
const corporation = getCorporation(); const corporation = getCorporation();
const office = getOffice(divisionName, cityName); const office = getOffice(divisionName, cityName);
return BuyCoffee(corporation, office); return BuyTea(corporation, office);
}, },
hireAdVert: (ctx) => (_divisionName) => { hireAdVert: (ctx) => (_divisionName) => {
checkAccess(ctx, 8); checkAccess(ctx, 8);
@ -690,11 +696,9 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
loc: office.loc, loc: office.loc,
size: office.size, size: office.size,
maxEne: office.maxEne, maxEne: office.maxEne,
maxHap: office.maxHap,
maxMor: office.maxMor, maxMor: office.maxMor,
employees: office.totalEmployees, employees: office.totalEmployees,
avgEne: office.avgEne, avgEne: office.avgEne,
avgHap: office.avgHap,
avgMor: office.avgMor, avgMor: office.avgMor,
totalExperience: office.totalExp, totalExperience: office.totalExp,
employeeProd: Object.assign({}, office.employeeProd), employeeProd: Object.assign({}, office.employeeProd),
@ -761,7 +765,7 @@ export function NetscriptCorporation(): InternalAPI<NSCorporation> {
const corporation = getCorporation(); const corporation = getCorporation();
const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade.name === upgradeName); const upgrade = Object.values(CorporationUpgrades).find((upgrade) => upgrade.name === upgradeName);
if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`); if (upgrade === undefined) throw new Error(`No upgrade named '${upgradeName}'`);
LevelUpgrade(corporation, upgrade); LevelUpgrade(corporation, upgrade, 1);
}, },
issueDividends: (ctx) => (_rate) => { issueDividends: (ctx) => (_rate) => {
checkAccess(ctx); checkAccess(ctx);

@ -4,15 +4,19 @@ import {
CorporationUnlockUpgrades, CorporationUnlockUpgrades,
} from "../../Corporation/data/CorporationUnlockUpgrades"; } from "../../Corporation/data/CorporationUnlockUpgrades";
import { PlayerObject } from "./PlayerObject"; import { PlayerObject } from "./PlayerObject";
import { resetIndustryResearchTrees } from "../../Corporation/IndustryData";
export function canAccessCorporation(this: PlayerObject): boolean { export function canAccessCorporation(this: PlayerObject): boolean {
return this.bitNodeN === 3 || this.sourceFileLvl(3) > 0; return this.bitNodeN === 3 || this.sourceFileLvl(3) > 0;
} }
export function startCorporation(this: PlayerObject, corpName: string, additionalShares = 0): void { export function startCorporation(this: PlayerObject, corpName: string, seedFunded: boolean): void {
this.corporation = new Corporation({ this.corporation = new Corporation({
name: corpName, name: corpName,
seedFunded: seedFunded,
}); });
//reset the research tree in case the corporation was restarted
resetIndustryResearchTrees();
if (this.bitNodeN === 3 || this.sourceFileLvl(3) === 3) { if (this.bitNodeN === 3 || this.sourceFileLvl(3) === 3) {
const warehouseApi = CorporationUnlockUpgrades[CorporationUnlockUpgradeIndex.WarehouseAPI].index; const warehouseApi = CorporationUnlockUpgrades[CorporationUnlockUpgradeIndex.WarehouseAPI].index;
@ -22,5 +26,5 @@ export function startCorporation(this: PlayerObject, corpName: string, additiona
this.corporation.unlockUpgrades[OfficeApi] = 1; this.corporation.unlockUpgrades[OfficeApi] = 1;
} }
this.corporation.totalShares += additionalShares; this.corporation.totalShares += seedFunded ? 500_000_000 : 0;
} }

@ -248,8 +248,11 @@ export function prestigeSourceFile(flume: boolean): void {
homeComp.programs.push(Programs.Formulas.name); homeComp.programs.push(Programs.Formulas.name);
} }
console.log(Player.bitNodeN);
dialogBoxCreate("hello");
// BitNode 3: Corporatocracy // BitNode 3: Corporatocracy
if (Player.bitNodeN === 3) { if (Player.bitNodeN === 3) {
console.log("why isn't the dialogbox happening?");
homeComp.messages.push(LiteratureNames.CorporationManagementHandbook); homeComp.messages.push(LiteratureNames.CorporationManagementHandbook);
dialogBoxCreate( dialogBoxCreate(
"You received a copy of the Corporation Management Handbook on your home computer. " + "You received a copy of the Corporation Management Handbook on your home computer. " +

@ -6887,17 +6887,18 @@ type CorpEmployeePosition =
| "Business" | "Business"
| "Management" | "Management"
| "Research & Development" | "Research & Development"
| "Training" | "Intern"
| "Unassigned"; | "Unassigned";
/** @public */ /** @public */
type CorpIndustryName = type CorpIndustryName =
| "Energy" | "Spring Water"
| "Water Utilities" | "Water Utilities"
| "Agriculture" | "Agriculture"
| "Fishing" | "Fishing"
| "Mining" | "Mining"
| "Food" | "Refinery"
| "Restaurant"
| "Tobacco" | "Tobacco"
| "Chemical" | "Chemical"
| "Pharmaceutical" | "Pharmaceutical"
@ -7026,16 +7027,16 @@ export interface OfficeAPI {
* @param divisionName - Name of the division * @param divisionName - Name of the division
* @param city - Name of the city * @param city - Name of the city
* @param costPerEmployee - Amount to spend per employee. * @param costPerEmployee - Amount to spend per employee.
* @returns Multiplier for happiness and morale, or zero on failure * @returns Multiplier for morale, or zero on failure
*/ */
throwParty(divisionName: string, city: CityName | `${CityName}`, costPerEmployee: number): number; throwParty(divisionName: string, city: CityName | `${CityName}`, costPerEmployee: number): number;
/** /**
* Buy coffee for your employees * Buy tea for your employees
* @param divisionName - Name of the division * @param divisionName - Name of the division
* @param city - Name of the city * @param city - Name of the city
* @returns true if buying coffee was successful, false otherwise * @returns true if buying tea was successful, false otherwise
*/ */
buyCoffee(divisionName: string, city: CityName | `${CityName}`): boolean; buyTea(divisionName: string, city: CityName | `${CityName}`): boolean;
/** /**
* Hire AdVert. * Hire AdVert.
* @param divisionName - Name of the division * @param divisionName - Name of the division
@ -7156,13 +7157,13 @@ export interface WarehouseAPI {
* @param divisionName - Name of the division * @param divisionName - Name of the division
* @param city - Name of the city * @param city - Name of the city
* @param materialName - Name of the material * @param materialName - Name of the material
* @param enabled - smart supply use leftovers enabled * @param option - smart supply option, "leftovers" to use leftovers, "imports" to use only imported materials, "none" to not use materials from store
*/ */
setSmartSupplyUseLeftovers( setSmartSupplyOption(
divisionName: string, divisionName: string,
city: CityName | `${CityName}`, city: CityName | `${CityName}`,
materialName: string, materialName: string,
enabled: boolean, option: string,
): void; ): void;
/** /**
* Set material buy data * Set material buy data
@ -7190,10 +7191,11 @@ export interface WarehouseAPI {
/** /**
* Get product data * Get product data
* @param divisionName - Name of the division * @param divisionName - Name of the division
* @param city - Name of the city
* @param productName - Name of the product * @param productName - Name of the product
* @returns product data * @returns product data
*/ */
getProduct(divisionName: string, productName: string): Product; getProduct(divisionName: string, city: CityName | `${CityName}`, productName: string): Product;
/** /**
* Get material data * Get material data
* @param divisionName - Name of the division * @param divisionName - Name of the division
@ -7221,17 +7223,19 @@ export interface WarehouseAPI {
/** /**
* Set market TA 1 for a product. * Set market TA 1 for a product.
* @param divisionName - Name of the division * @param divisionName - Name of the division
* @param city - Name of the city
* @param productName - Name of the product * @param productName - Name of the product
* @param on - market ta enabled * @param on - market ta enabled
*/ */
setProductMarketTA1(divisionName: string, productName: string, on: boolean): void; setProductMarketTA1(divisionName: string, city: CityName | `${CityName}`, productName: string, on: boolean): void;
/** /**
* Set market TA 2 for a product. * Set market TA 2 for a product.
* @param divisionName - Name of the division * @param divisionName - Name of the division
* @param city - Name of the city
* @param productName - Name of the product * @param productName - Name of the product
* @param on - market ta enabled * @param on - market ta enabled
*/ */
setProductMarketTA2(divisionName: string, productName: string, on: boolean): void; setProductMarketTA2(divisionName: string, city: CityName | `${CityName}`, productName: string, on: boolean): void;
/** /**
* Set material export data * Set material export data
* @param sourceDivision - Source division * @param sourceDivision - Source division
@ -7433,11 +7437,11 @@ export interface Corporation extends WarehouseAPI, OfficeAPI {
issueNewShares(amount?: number): number; issueNewShares(amount?: number): number;
/** Buyback Shares /** Buyback Shares
* @param amount - Amount of shares to buy back. */ * @param amount - Amount of shares to buy back, must be integer and larger than 0 */
buyBackShares(amount: number): void; buyBackShares(amount: number): void;
/** Sell Shares /** Sell Shares
* @param amount - Amount of shares to sell. */ * @param amount - Amount of shares to sell, must be integer between 1 and 100t */
sellShares(amount: number): void; sellShares(amount: number): void;
/** Get bonus time. /** Get bonus time.
@ -7555,7 +7559,7 @@ interface CorpConstants {
issueNewSharesCooldown: number; issueNewSharesCooldown: number;
/** Cooldown for selling shares in game cycles (1 game cycle = 200ms) */ /** Cooldown for selling shares in game cycles (1 game cycle = 200ms) */
sellSharesCooldown: number; sellSharesCooldown: number;
coffeeCostPerEmployee: number; teaCostPerEmployee: number;
gameCyclesPerMarketCycle: number; gameCyclesPerMarketCycle: number;
gameCyclesPerCorpStateCycle: number; gameCyclesPerCorpStateCycle: number;
secondsPerMarketCycle: number; secondsPerMarketCycle: number;
@ -7575,7 +7579,7 @@ interface CorpConstants {
employeeRaiseAmount: number; employeeRaiseAmount: number;
/** Max products for a division without upgrades */ /** Max products for a division without upgrades */
maxProductsBase: number; maxProductsBase: number;
/** The minimum decay value for happiness/morale/energy */ /** The minimum decay value for morale/energy */
minEmployeeDecay: number; minEmployeeDecay: number;
} }
/** @public */ /** @public */
@ -7583,8 +7587,9 @@ type CorpStateName = "START" | "PURCHASE" | "PRODUCTION" | "EXPORT" | "SALE";
/** @public */ /** @public */
type CorpMaterialName = type CorpMaterialName =
| "Minerals"
| "Ore"
| "Water" | "Water"
| "Energy"
| "Food" | "Food"
| "Plants" | "Plants"
| "Metal" | "Metal"
@ -7699,16 +7704,22 @@ interface Product {
cmp: number | undefined; cmp: number | undefined;
/** Product Rating */ /** Product Rating */
rat: number; rat: number;
/** Effective rating */
effRat: number;
/** Product Properties. The data is \{qlt, per, dur, rel, aes, fea\} */ /** Product Properties. The data is \{qlt, per, dur, rel, aes, fea\} */
properties: { [key: string]: number }; properties: { [key: string]: number };
/** Production cost */ /** Production cost */
pCost: number; pCost: number;
/** Sell cost, can be "MP+5" */ /** Sell cost, can be "MP+5" */
sCost: string | number; sCost: string;
/** Data refers to the production, sale, and quantity of the products /** Sell amount, can be "PROD/2" */
* These values are specific to a city sAmt: string;
* For each city, the data is [qty, prod, sell] */ /** Amount of product */
cityData: Record<CityName | `${CityName}`, number[]>; qty: number;
/** Amount of product produced */
prod: number;
/** Amount of product sold */
sell: number;
/** Creation progress - A number between 0-100 representing percentage */ /** Creation progress - A number between 0-100 representing percentage */
developmentProgress: number; developmentProgress: number;
} }
@ -7736,6 +7747,8 @@ interface Material {
cost: number; cost: number;
/** Sell cost, can be "MP+5" */ /** Sell cost, can be "MP+5" */
sCost: string | number; sCost: string | number;
/** Sell amount, can be "PROD/2" */
sAmt: string | number;
/** Export orders */ /** Export orders */
exp: Export[]; exp: Export[];
} }
@ -7781,16 +7794,12 @@ export interface Office {
size: number; size: number;
/** Maximum amount of energy of the employees */ /** Maximum amount of energy of the employees */
maxEne: number; maxEne: number;
/** Maximum happiness of the employees */
maxHap: number;
/** Maximum morale of the employees */ /** Maximum morale of the employees */
maxMor: number; maxMor: number;
/** Amount of employees */ /** Amount of employees */
employees: number; employees: number;
/** Average energy of the employees */ /** Average energy of the employees */
avgEne: number; avgEne: number;
/** Average happiness of the employees */
avgHap: number;
/** Average morale of the employees */ /** Average morale of the employees */
avgMor: number; avgMor: number;
/** Total experience of all employees */ /** Total experience of all employees */