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.*
# tmp folder for build and electron
.app
.package
.build
.app
.package
.build
# editor files
.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 | |
| [bribeAmountPerReputation](./bitburner.corpconstants.bribeamountperreputation.md) | | number | |
| [bribeThreshold](./bitburner.corpconstants.bribethreshold.md) | | number | |
| [coffeeCostPerEmployee](./bitburner.corpconstants.coffeecostperemployee.md) | | number | |
| [dividendMaxRate](./bitburner.corpconstants.dividendmaxrate.md) | | number | |
| [employeeRaiseAmount](./bitburner.corpconstants.employeeraiseamount.md) | | number | |
| [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 | |
| [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 |
| [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 | |
| [officeInitialSize](./bitburner.corpconstants.officeinitialsize.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) |
| [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 |
| [teaCostPerEmployee](./bitburner.corpconstants.teacostperemployee.md) | | number | |
| [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 |
| [warehouseInitialCost](./bitburner.corpconstants.warehouseinitialcost.md) | | number | |

@ -4,7 +4,7 @@
## CorpConstants.minEmployeeDecay property
The minimum decay value for happiness/morale/energy
The minimum decay value for morale/energy
**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"
| "Management"
| "Research & Development"
| "Training"
| "Intern"
| "Unassigned";
```

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

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

@ -16,7 +16,7 @@ buyBackShares(amount: number): void;
| 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:**

@ -16,7 +16,7 @@ sellShares(amount: number): void;
| 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:**

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

@ -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 |
| --- | --- | --- | --- |
| [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 |
| [employeeJobs](./bitburner.office.employeejobs.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Positions of the employees |
| [employeeProd](./bitburner.office.employeeprod.md) | | Record&lt;[CorpEmployeePosition](./bitburner.corpemployeeposition.md)<!-- -->, number&gt; | Production of the employees |
| [employees](./bitburner.office.employees.md) | | number | Amount of employees |
| [loc](./bitburner.office.loc.md) | | [CityName](./bitburner.cityname.md) | City of the office |
| [maxEne](./bitburner.office.maxene.md) | | number | Maximum amount of energy of the employees |
| [maxHap](./bitburner.office.maxhap.md) | | number | Maximum happiness of the employees |
| [maxMor](./bitburner.office.maxmor.md) | | number | Maximum morale of the employees |
| [size](./bitburner.office.size.md) | | number | Maximum number of employee |
| [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. -->
[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:**
```typescript
buyCoffee(divisionName: string, city: CityName | `${CityName}`): boolean;
buyTea(divisionName: string, city: CityName | `${CityName}`): boolean;
```
## Parameters
@ -23,5 +23,5 @@ buyCoffee(divisionName: string, city: CityName | `${CityName}`): 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 |
| --- | --- |
| [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. |
| [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 |

@ -24,5 +24,5 @@ throwParty(divisionName: string, city: CityName | `${CityName}`, costPerEmployee
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 |
| --- | --- | --- | --- |
| [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 |
| [developmentProgress](./bitburner.product.developmentprogress.md) | | number | Creation progress - A number between 0-100 representing percentage |
| [dmd](./bitburner.product.dmd.md) | | number \| undefined | Demand for the product, only present if "Market Research - Demand" unlocked |
| [effRat](./bitburner.product.effrat.md) | | number | Effective rating |
| [name](./bitburner.product.name.md) | | string | Name of the product |
| [pCost](./bitburner.product.pcost.md) | | number | Production cost |
| [prod](./bitburner.product.prod.md) | | number | Amount of product produced |
| [properties](./bitburner.product.properties.md) | | { \[key: string\]: number } | Product Properties. The data is {<!-- -->qlt, per, dur, rel, aes, fea<!-- -->} |
| [qty](./bitburner.product.qty.md) | | number | Amount of product |
| [rat](./bitburner.product.rat.md) | | number | Product Rating |
| [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:**
```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:**
```typescript
getProduct(divisionName: string, productName: string): Product;
getProduct(divisionName: string, city: CityName | `${CityName}`, productName: string): Product;
```
## Parameters
@ -17,6 +17,7 @@ getProduct(divisionName: string, productName: string): Product;
| Parameter | Type | Description |
| --- | --- | --- |
| divisionName | string | Name of the division |
| city | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Name of the city |
| productName | string | Name of the product |
**Returns:**

@ -26,7 +26,7 @@ Requires the Warehouse API upgrade from your corporation.
| [discontinueProduct(divisionName, productName)](./bitburner.warehouseapi.discontinueproduct.md) | Discontinue a product. |
| [exportMaterial(sourceDivision, sourceCity, targetDivision, targetCity, materialName, amt)](./bitburner.warehouseapi.exportmaterial.md) | Set material export data |
| [getMaterial(divisionName, city, materialName)](./bitburner.warehouseapi.getmaterial.md) | Get material data |
| [getProduct(divisionName, 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 |
| [getWarehouse(divisionName, city)](./bitburner.warehouseapi.getwarehouse.md) | Get warehouse data |
| [hasWarehouse(divisionName, city)](./bitburner.warehouseapi.haswarehouse.md) | Check if you have a warehouse in city |
@ -38,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. |
| [setMaterialMarketTA1(divisionName, city, materialName, on)](./bitburner.warehouseapi.setmaterialmarketta1.md) | Set market TA 1 for a material. |
| [setMaterialMarketTA2(divisionName, city, materialName, on)](./bitburner.warehouseapi.setmaterialmarketta2.md) | Set market TA 2 for a material. |
| [setProductMarketTA1(divisionName, 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. |
| [setProductMarketTA1(divisionName, city, productName, on)](./bitburner.warehouseapi.setproductmarketta1.md) | Set market TA 1 for a product. |
| [setProductMarketTA2(divisionName, city, productName, on)](./bitburner.warehouseapi.setproductmarketta2.md) | Set market TA 2 for a product. |
| [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 |

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

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

@ -1,19 +1,19 @@
<!-- 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
**Signature:**
```typescript
setSmartSupplyUseLeftovers(
setSmartSupplyOption(
divisionName: string,
city: CityName | `${CityName}`,
materialName: string,
enabled: boolean,
option: string,
): void;
```
@ -24,7 +24,7 @@ setSmartSupplyUseLeftovers(
| divisionName | string | Name of the division |
| city | [CityName](./bitburner.cityname.md) \| \`${[CityName](./bitburner.cityname.md)<!-- -->}\` | Name of the city |
| 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:**

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

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

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

@ -17,10 +17,12 @@ import { isRelevantMaterial } from "./ui/Helpers";
import { CityName } from "../Enums";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { CorpResearchName } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers";
import { isInteger } from "lodash";
export function NewIndustry(corporation: Corporation, industry: IndustryType, name: string): void {
if (corporation.divisions.find(({ type }) => industry == type))
throw new Error(`You have already expanded into the ${industry} industry!`);
if (corporation.divisions.length >= corporation.maxDivisions)
throw new Error(`Cannot expand into ${industry} industry, too many divisions!`);
for (let i = 0; i < corporation.divisions.length; ++i) {
if (corporation.divisions[i].name === name) {
@ -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 {
if (corporation.funds < corpConstants.officeInitialCost) {
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);
}
export function LevelUpgrade(corporation: Corporation, upgrade: CorporationUpgrade): void {
const baseCost = upgrade.basePrice;
const priceMult = upgrade.priceMult;
const level = corporation.upgrades[upgrade.index];
const cost = baseCost * Math.pow(priceMult, level);
export function LevelUpgrade(corporation: Corporation, upgrade: CorporationUpgrade, amount: number): void {
const cost = calculateUpgradeCost(corporation, upgrade, amount);
if (corporation.funds < cost) {
throw new Error("Insufficient funds");
} else {
corporation.upgrade(upgrade);
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)`);
}
const newSharePrice = Math.round(corporation.sharePrice * 0.9);
const newSharePrice = Math.round(corporation.sharePrice * 0.8);
const profit = amount * newSharePrice;
corporation.issueNewSharesCooldown = corpConstants.issueNewSharesCooldown;
@ -143,21 +149,21 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
//Parse quantity
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, "");
q = q.replace(/[^-()\d/*+.MAXPROD]/g, "");
q = q.replace(/[^-()\d/*+.MAXPRODINV]/g, "");
let tempQty = q.replace(/MAX/g, mat.maxsll.toString());
tempQty = tempQty.replace(/PROD/g, mat.prd.toString());
tempQty = tempQty.replace(/INV/g, mat.prd.toString());
try {
tempQty = eval(tempQty);
} catch (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");
}
mat.sllman[0] = true;
mat.sllman[1] = q; //Use sanitized input
} 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))) {
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 {
const cost = parseFloat(price);
if (isNaN(cost)) {
throw new Error("Invalid value for sell price field");
}
product.sCost = cost;
product.sCost[city] = cost;
}
// Array of all cities. Used later
@ -208,19 +214,20 @@ export function SellProduct(product: Product, city: string, amt: string, price:
// Parse quantity
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
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());
temp = temp.replace(/PROD/g, product.data[city][1].toString());
temp = temp.replace(/INV/g, product.data[city][0].toString());
try {
temp = eval(temp);
} catch (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");
}
if (all) {
@ -268,8 +275,11 @@ export function SetSmartSupply(warehouse: Warehouse, smartSupply: boolean): void
warehouse.smartSupplyEnabled = smartSupply;
}
export function SetSmartSupplyUseLeftovers(warehouse: Warehouse, material: Material, useLeftover: boolean): void {
warehouse.smartSupplyUseLeftovers[material.name] = useLeftover;
export function SetSmartSupplyOption(warehouse: Warehouse, material: Material, useOption: string): void {
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 {
@ -298,9 +308,11 @@ export function BulkPurchase(corp: Corporation, warehouse: Warehouse, material:
}
export function SellShares(corporation: Corporation, numShares: number): number {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) 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 > 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.shareSaleCooldown) throw new Error("Share sale on cooldown!");
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 {
if (isNaN(numShares)) throw new Error("Invalid value for number of shares");
if (numShares < 0) 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 > 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!");
const buybackPrice = corporation.sharePrice * 1.1;
@ -344,9 +356,9 @@ export function UpgradeOfficeSize(corp: Corporation, office: OfficeSpace, size:
corp.funds = corp.funds - cost;
}
export function BuyCoffee(corp: Corporation, office: OfficeSpace): boolean {
const cost = office.getCoffeeCost();
if (corp.funds < cost || !office.setCoffee()) return false;
export function BuyTea(corp: Corporation, office: OfficeSpace): boolean {
const cost = office.getTeaCost();
if (corp.funds < cost || !office.setTea()) return false;
corp.funds -= cost;
return true;
}
@ -495,8 +507,12 @@ export function ExportMaterial(
): void {
// Sanitize amt
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");
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 {
temp = eval(temp);
} catch (e) {
@ -505,7 +521,7 @@ export function ExportMaterial(
const n = parseFloat(temp);
if (n == null || isNaN(n) || n < 0) {
if (n == null || isNaN(n)) {
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 { CityName } from "../Enums";
import { CorpStateName } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers";
interface IParams {
name?: string;
seedFunded?: boolean;
}
export class Corporation {
@ -24,6 +26,7 @@ export class Corporation {
//A division/business sector is represented by the object:
divisions: Industry[] = [];
maxDivisions = 20 * BitNodeMultipliers.CorporationDivisions;
//Financial stats
funds = 150e9;
@ -50,6 +53,8 @@ export class Corporation {
valuationsList = [0];
valuation = 0;
seedFunded: boolean;
state = new CorporationState();
constructor(params: IParams = {}) {
@ -59,6 +64,7 @@ export class Corporation {
this.unlockUpgrades = Array(numUnlockUpgrades).fill(0);
this.upgrades = Array(numUpgrades).fill(0);
this.upgradeMultipliers = Array(numUpgrades).fill(1);
this.seedFunded = params.seedFunded ?? false;
}
addFunds(amt: number): void {
@ -78,6 +84,8 @@ export class Corporation {
}
process(): void {
if (this.storedCycles < 0) this.storedCycles = 0;
if (this.storedCycles >= corpConstants.gameCyclesPerCorpStateCycle) {
const state = this.getState();
const marketCycles = 1;
@ -305,10 +313,9 @@ export class Corporation {
}
//Levelable upgrades
upgrade(upgrade: CorporationUpgrade): void {
upgrade(upgrade: CorporationUpgrade, amount: number): void {
if (amount < 1) amount = 1;
const upgN = upgrade.index,
basePrice = upgrade.basePrice,
priceMult = upgrade.priceMult,
upgradeAmt = upgrade.benefit; //Amount by which the upgrade multiplier gets increased (additive)
while (this.upgrades.length <= upgN) {
this.upgrades.push(0);
@ -316,12 +323,12 @@ export class Corporation {
while (this.upgradeMultipliers.length <= upgN) {
this.upgradeMultipliers.push(1);
}
const totalCost = basePrice * Math.pow(priceMult, this.upgrades[upgN]);
const totalCost = calculateUpgradeCost(this, upgrade, amount);
if (this.funds < totalCost) {
dialogBoxCreate("You don't have enough funds to purchase this!");
return;
}
++this.upgrades[upgN];
this.upgrades[upgN] += amount;
this.funds = this.funds - totalCost;
//Increase upgrade multiplier

@ -321,6 +321,7 @@ export class Industry {
buyAmt = Math.min(buyAmt, maxAmt);
if (buyAmt > 0) {
mat.qlt = Math.max(0.1, (mat.qlt * mat.qty + 1 * buyAmt) / (mat.qty + buyAmt));
mat.qty += buyAmt;
expenses += buyAmt * mat.bCost;
}
@ -382,18 +383,25 @@ export class Industry {
// Use the materials already in the warehouse if the option is on.
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 buyAmt = smartBuy[matName];
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
for (const [matName, buyAmt] of Object.entries(smartBuy) as [CorpMaterialName, number][]) {
const mat = warehouse.materials[matName];
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.buy = buyAmt / 10;
expenses += buyAmt * mat.bCost;
}
break;
@ -463,6 +471,8 @@ export class Industry {
// Make our materials if they are producable
if (producableFrac > 0 && prod > 0) {
let avgQlt = 0;
let divider = 0;
for (const reqMatName of Object.keys(this.reqMats) as CorpMaterialName[]) {
const reqMat = this.reqMats[reqMatName];
if (reqMat === undefined) continue;
@ -471,13 +481,26 @@ export class Industry {
warehouse.materials[reqMatName].prd = 0;
warehouse.materials[reqMatName].prd -=
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) {
warehouse.materials[this.prodMats[j]].qty += prod * producableFrac;
warehouse.materials[this.prodMats[j]].qlt =
let tempQlt =
office.employeeProd[EmployeePositions.Engineer] / 90 +
Math.pow(this.sciResearch, this.sciFac) +
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 {
for (const reqMatName of Object.keys(this.reqMats) as CorpMaterialName[]) {
@ -610,6 +633,28 @@ export class Industry {
corporation.getSalesMultiplier() *
advertisingFactor *
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 = sellAmt * corpConstants.secondsPerMarketCycle * marketCycles;
sellAmt = Math.min(mat.qty, sellAmt);
@ -636,10 +681,27 @@ export class Industry {
mat.totalExp = 0; //Reset export
for (let expI = 0; expI < mat.exp.length; ++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,
(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;
try {
amt = eval(amtStr);
@ -660,38 +722,33 @@ export class Industry {
if (mat.qty < amt) {
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
if (expWarehouse.sizeUsed >= expWarehouse.size) {
// Warehouse at capacity. Exporting doesn't
// affect revenue so just return 0's
return [0, 0];
} else {
const maxAmt = Math.floor(
(expWarehouse.size - expWarehouse.sizeUsed) / MaterialInfo[matName].size,
);
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;
}
// Make sure theres enough space in warehouse
if (expWarehouse.sizeUsed >= expWarehouse.size) {
// Warehouse at capacity. Exporting doesn't
// affect revenue so just return 0's
continue;
} else {
const maxAmt = Math.floor((expWarehouse.size - expWarehouse.sizeUsed) / MaterialInfo[matName].size);
amt = Math.min(maxAmt, amt);
}
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
mat.totalExp /= corpConstants.secondsPerMarketCycle * marketCycles;
@ -814,12 +871,20 @@ export class Industry {
//Make our Products if they are producable
if (producableFrac > 0 && prod > 0) {
let avgQlt = 1;
for (const [reqMatName, reqQty] of Object.entries(product.reqMats) as [CorpMaterialName, number][]) {
const reqMatQtyNeeded = reqQty * prod * producableFrac;
warehouse.materials[reqMatName].qty -= reqMatQtyNeeded;
warehouse.materials[reqMatName].prd -=
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
product.data[city][0] += prod * producableFrac;
}
@ -874,7 +939,7 @@ export class Industry {
}
// 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;
if (product.marketTa2) {
// Reverse engineer the 'maxSell' formula
@ -885,7 +950,7 @@ export class Industry {
const sqrtNumerator = sellAmt;
const sqrtDenominator =
0.5 *
Math.pow(product.rat, 0.65) *
Math.pow(product.data[city][3], 0.65) *
marketFactor *
corporation.getSalesMultiplier() *
businessFactor *
@ -909,16 +974,16 @@ export class Industry {
sCost = optimalPrice;
} else if (product.marketTa1) {
sCost = product.pCost + markupLimit;
} else if (isString(product.sCost)) {
const sCostString = product.sCost as string;
} else if (isString(product.sCost[city])) {
const sCostString = product.sCost[city] as string;
if (product.mku === 0) {
console.error(`mku is zero, reverting to 1 to avoid Infinity`);
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));
} else {
sCost = product.sCost;
sCost = product.sCost[city];
}
let markup = 1;
@ -930,7 +995,7 @@ export class Industry {
product.maxsll =
0.5 *
Math.pow(product.rat, 0.65) *
Math.pow(product.data[city][3], 0.65) *
marketFactor *
corporation.getSalesMultiplier() *
Math.pow(markup, 2) *
@ -996,10 +1061,10 @@ export class Industry {
applyAdVert(corporation: Corporation): void {
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);
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.numAdVerts;
@ -1154,7 +1219,7 @@ export class Industry {
const matNameMap = { AICores: "AI Cores", RealEstate: "Real Estate" };
const indNameMap = {
RealEstate: IndustryType.RealEstate,
Utilities: IndustryType.Utilities,
Water: IndustryType.Water,
Computers: IndustryType.Computers,
Computer: IndustryType.Computers,
};

@ -17,9 +17,35 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.3,
aiCoreFactor: 0.3,
advertisingFactor: 0.04,
requiredMaterials: { Water: 0.5, Energy: 0.5 },
requiredMaterials: { Water: 0.5, Chemicals: 0.2 },
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]: {
startingCost: 70e9,
description: "Produce industrial chemicals.",
@ -30,7 +56,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.25,
aiCoreFactor: 0.2,
advertisingFactor: 0.07,
requiredMaterials: { Plants: 1, Energy: 0.5, Water: 0.5 },
requiredMaterials: { Plants: 1, Water: 0.5 },
producedMaterials: ["Chemicals"],
},
[IndustryType.Computers]: {
@ -55,21 +81,9 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.36,
aiCoreFactor: 0.19,
advertisingFactor: 0.17,
requiredMaterials: { Metal: 2, Energy: 1 },
requiredMaterials: { Metal: 2 },
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]: {
startingCost: 80e9,
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,
aiCoreFactor: 0.2,
advertisingFactor: 0.08,
requiredMaterials: { Energy: 0.5 },
requiredMaterials: { Plants: 0.5 },
producedMaterials: ["Food"],
},
[IndustryType.Food]: {
[IndustryType.Restaurant]: {
startingCost: 10e9,
description: "Create your own restaurants all around the world.",
product: {
@ -103,7 +117,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
aiCoreFactor: 0.25,
advertisingFactor: 0.25,
realEstateFactor: 0.05,
requiredMaterials: { Food: 0.5, Water: 0.5, Energy: 0.2 },
requiredMaterials: { Food: 0.5, Water: 0.5 },
},
[IndustryType.Healthcare]: {
startingCost: 750e9,
@ -127,7 +141,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
hardwareFactor: 0.1,
robotFactor: 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]: {
startingCost: 300e9,
@ -139,8 +153,8 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.45,
aiCoreFactor: 0.45,
advertisingFactor: 0.06,
requiredMaterials: { Energy: 0.8 },
producedMaterials: ["Metal"],
requiredMaterials: { Hardware: 0.1 },
producedMaterials: ["Ore", "Minerals"],
},
[IndustryType.Pharmaceutical]: {
startingCost: 200e9,
@ -164,7 +178,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.25,
aiCoreFactor: 0.2,
advertisingFactor: 0.16,
requiredMaterials: { Chemicals: 2, Energy: 1, Water: 0.5 },
requiredMaterials: { Chemicals: 2, Water: 0.5 },
producedMaterials: ["Drugs"],
},
[IndustryType.RealEstate]: {
@ -188,7 +202,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
advertisingFactor: 0.25,
scienceFactor: 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"],
},
[IndustryType.Robotics]: {
@ -213,7 +227,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
aiCoreFactor: 0.36,
advertisingFactor: 0.18,
hardwareFactor: 0.19,
requiredMaterials: { Hardware: 5, Energy: 3 },
requiredMaterials: { Hardware: 5, "AI Cores": 3 },
producedMaterials: ["Robots"],
},
[IndustryType.Software]: {
@ -238,7 +252,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
realEstateFactor: 0.15,
aiCoreFactor: 0.18,
robotFactor: 0.05,
requiredMaterials: { Hardware: 0.5, Energy: 0.5 },
requiredMaterials: { Hardware: 0.5 },
producedMaterials: ["AI Cores"],
},
[IndustryType.Tobacco]: {
@ -261,9 +275,9 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.2,
aiCoreFactor: 0.15,
advertisingFactor: 0.2,
requiredMaterials: { Plants: 1, Water: 0.2 },
requiredMaterials: { Plants: 1 },
},
[IndustryType.Utilities]: {
[IndustryType.Water]: {
startingCost: 150e9,
description: "Distribute water and provide wastewater services.",
recommendStarting: false,
@ -272,7 +286,7 @@ export const IndustriesData: Record<CorpIndustryName, CorpIndustryData> = {
robotFactor: 0.4,
aiCoreFactor: 0.4,
advertisingFactor: 0.08,
requiredMaterials: { Hardware: 0.1, Metal: 0.1 },
requiredMaterials: { Hardware: 0.1 },
producedMaterials: ["Water"],
},
};

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

@ -13,14 +13,25 @@ export const MaterialInfo: Record<CorpMaterialName, CorpMaterialConstantData> =
maxVolatility: 0.2,
baseMarkup: 6,
},
Energy: {
name: "Energy",
Ore: {
name: "Ore",
size: 0.01,
demandBase: 90,
demandRange: [80, 99],
demandBase: 50,
demandRange: [40, 60],
competitionBase: 80,
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,
baseMarkup: 6,
},

@ -16,11 +16,9 @@ export class OfficeSpace {
size: number;
maxEne = 100;
maxHap = 100;
maxMor = 100;
avgEne = 75;
avgHap = 75;
avgMor = 75;
avgInt = 75;
@ -32,9 +30,9 @@ export class OfficeSpace {
totalEmployees = 0;
totalSalary = 0;
autoCoffee = false;
autoTea = false;
autoParty = false;
coffeePending = false;
teaPending = false;
partyMult = 1;
employeeProd: Record<EmployeePositions | "total", number> = {
@ -43,7 +41,7 @@ export class OfficeSpace {
[EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0,
[EmployeePositions.Training]: 0,
[EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0,
total: 0,
};
@ -53,7 +51,7 @@ export class OfficeSpace {
[EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0,
[EmployeePositions.Training]: 0,
[EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0,
};
employeeNextJobs: Record<EmployeePositions, number> = {
@ -62,7 +60,7 @@ export class OfficeSpace {
[EmployeePositions.Business]: 0,
[EmployeePositions.Management]: 0,
[EmployeePositions.RandD]: 0,
[EmployeePositions.Training]: 0,
[EmployeePositions.Intern]: 0,
[EmployeePositions.Unassigned]: 0,
};
@ -76,10 +74,10 @@ export class OfficeSpace {
}
process(marketCycles = 1, corporation: Corporation, industry: Industry): number {
// HRBuddy AutoRecruitment and training
// HRBuddy AutoRecruitment and Interning
if (industry.hasResearch("HRBuddy-Recruitment") && !this.atCapacity()) {
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
this.maxEne = 100;
this.maxHap = 100;
this.maxMor = 100;
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("AutoBrew")) this.autoCoffee = true;
if (industry.hasResearch("AutoBrew")) this.autoTea = true;
if (industry.hasResearch("AutoPartyManager")) this.autoParty = true;
if (this.totalEmployees > 0) {
/** Multiplier for employee morale/happiness/energy based on company performance */
const perfMult = Math.pow(
1.002 -
(corporation.funds < 0 ? 0.002 : 0) -
(industry.lastCycleRevenue < industry.lastCycleExpenses ? 0.002 : 0),
marketCycles,
);
/** Multiplier for employee morale/energy based on company performance */
let perfMult = 1.002;
if (this.totalEmployees >= 9) {
perfMult = Math.pow(
1 +
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.
// This does not cause a noticable decrease (it's only -.001% per cycle), it only serves
// to make the numbers slightly different between Happiness and Morale.
const reduction = 0.001 * marketCycles;
// This does not cause a noticable decrease (it's only -.001% per cycle).
const reduction = 0.002 * marketCycles;
if (this.autoCoffee) {
if (this.autoTea) {
this.avgEne = this.maxEne;
} else {
// Coffee gives a flat +3 to energy
this.avgEne = (this.avgEne - reduction) * perfMult + (this.coffeePending ? 3 : 0);
// Coffee also halves the difference between current and max energy
if (this.coffeePending) this.avgEne = this.maxEne - (this.maxEne - this.avgEne) / 2;
// Tea gives a flat +2 to energy
this.avgEne = (this.avgEne - reduction * Math.random()) * perfMult + (this.teaPending ? 2 : 0);
}
if (this.autoParty) {
this.avgMor = this.maxMor;
this.avgHap = this.maxHap;
} else {
// Each 5% multiplier gives an extra flat +1 to morale and happiness to make recovering from low morale easier.
const increase = this.partyMult > 1 ? (this.partyMult - 1) * 20 : 0;
this.avgHap = ((this.avgHap - reduction) * perfMult + increase) * this.partyMult;
this.avgMor = (this.avgMor * perfMult + increase) * this.partyMult;
// Each 5% multiplier gives an extra flat +1 to morale to make recovering from low morale easier.
const increase = this.partyMult > 1 ? (this.partyMult - 1) * 10 : 0;
this.avgMor = ((this.avgMor - reduction * Math.random()) * perfMult + increase) * this.partyMult;
}
this.avgEne = Math.max(Math.min(this.avgEne, this.maxEne), 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;
}
// 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 +=
0.0015 *
marketCycles *
(this.totalEmployees -
this.employeeJobs[EmployeePositions.Unassigned] +
this.employeeJobs[EmployeePositions.Training] * 4);
this.employeeJobs[EmployeePositions.Intern] * 9);
this.calculateEmployeeProductivity(corporation, industry);
if (this.totalEmployees === 0) {
@ -165,7 +158,7 @@ export class OfficeSpace {
effCha = this.avgCha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(),
effInt = this.avgInt * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(),
effEff = this.avgEff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier();
const prodBase = this.avgMor * this.avgHap * this.avgEne * 1e-6;
const prodBase = this.avgMor * this.avgEne * 1e-4;
let total = 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;
break;
case EmployeePositions.Unassigned:
case EmployeePositions.Training:
case EmployeePositions.Intern:
case "total":
continue;
default:
@ -213,7 +206,6 @@ export class OfficeSpace {
this.totalExp += getRandomInt(50, 100);
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.avgInt = (this.avgInt * this.totalEmployees + getRandomInt(50, 100)) / (this.totalEmployees + 1);
@ -240,13 +232,13 @@ export class OfficeSpace {
return false;
}
getCoffeeCost(): number {
return corpConstants.coffeeCostPerEmployee * this.totalEmployees;
getTeaCost(): number {
return corpConstants.teaCostPerEmployee * this.totalEmployees;
}
setCoffee(): boolean {
if (!this.coffeePending && !this.autoCoffee && this.totalEmployees > 0) {
this.coffeePending = true;
setTea(): boolean {
if (!this.teaPending && !this.autoTea && this.totalEmployees > 0) {
this.teaPending = true;
return true;
}
return false;
@ -267,11 +259,10 @@ export class OfficeSpace {
static fromJSON(value: IReviverValue): OfficeSpace {
// Convert employees from the old version
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;
const ret = Generic_fromJSON(OfficeSpace, value.data);
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.avgEne = empCopy.reduce((a, b) => a + b.data.ene, 0) / ret.totalEmployees || 75;
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
pCost = 0;
// Sell cost
sCost: string | number = 0;
// Sell costs
sCost: Record<string, any> = createCityMap<any>(0);
// Variables for handling the creation process of this Product
fin = false; // Whether this Product has finished being created
@ -82,8 +82,8 @@ export class Product {
// 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]
data: Record<string, number[]> = createCityMap<number[]>([0, 0, 0]);
// For each city, the data is [qty, prod, sell, effRat]
data: Record<string, number[]> = createCityMap<number[]>([0, 0, 0, 0]);
// Location of this Product
// 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
createProduct(marketCycles: number, employeeProd: typeof this["creationProd"]): void {
createProduct(marketCycles: number, employeeProd: typeof this.creationProd): void {
if (this.fin) {
return;
}

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

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

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

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

@ -6,17 +6,17 @@ export const researchMetadata: IConstructorParams[] = [
cost: 12e3,
desc:
"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. " +
"This will also disable the Coffee upgrade.",
"This will also disable the Tea upgrade.",
},
{
name: "AutoPartyManager",
cost: 15e3,
desc:
"Automatically analyzes your employees' happiness and morale " +
"Automatically analyzes your employees' morale " +
"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. " +
"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 " +
"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",
cost: 25e3,
@ -73,7 +66,7 @@ export const researchMetadata: IConstructorParams[] = [
name: "Go-Juice",
cost: 25e3,
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 " +
"employees by 10.",
},
@ -101,12 +94,7 @@ export const researchMetadata: IConstructorParams[] = [
desc:
"Use automated software to handle the training of employees. With this " +
"research, each employee hired with HRBuddy-Recruitment will automatically " +
"be assigned to 'Training', rather than being unassigned.",
},
{
name: "JoyWire",
cost: 20e3,
desc: "A brain implant which is installed in employees, increasing their maximum happiness by 10.",
"be assigned to 'Intern', rather than being unassigned.",
},
{
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 { IndustryType } from "../data/Enums";
import { useCorporation } from "./Context";
import { Industry } from "../Industry";
import { NewIndustry } from "../Actions";
import Typography from "@mui/material/Typography";
@ -21,20 +20,13 @@ interface IProps {
export function ExpandIndustryTab(props: IProps): React.ReactElement {
const corp = useCorporation();
const allIndustries = Object.values(IndustryType).sort();
const possibleIndustries = allIndustries.filter(
(industryType: IndustryType) =>
corp.divisions.find((division: Industry) => division.type === industryType) === undefined,
);
const [industry, setIndustry] = useState(possibleIndustries[0]);
const [industry, setIndustry] = useState(allIndustries[0]);
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];
if (!data) return <></>;
const disabled = corp.funds < data.startingCost;
const disabled = corp.funds < data.startingCost && corp.divisions.length < corp.maxDivisions;
function newIndustry(): void {
if (disabled) return;
@ -67,9 +59,12 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
return (
<>
<Typography>
{corp.name} has {corp.divisions.length}/{corp.maxDivisions} divisions.
</Typography>
<Typography>Create a new division to expand into a new industry:</Typography>
<Select value={industry} onChange={onIndustryChange}>
{possibleIndustries.map((industry) => (
{allIndustries.map((industry) => (
<MenuItem key={industry} value={industry}>
{industry}
</MenuItem>

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

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

@ -11,9 +11,11 @@ import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
import { calculateMaxAffordableUpgrade, calculateUpgradeCost } from "../helpers";
interface IProps {
upgrade: CorporationUpgrade;
amount: number | "MAX";
rerender: () => void;
}
@ -21,16 +23,15 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation();
const data = props.upgrade;
const level = corp.upgrades[data.index];
const amount = props.amount;
const baseCost = data.basePrice;
const priceMult = data.priceMult;
const cost = baseCost * Math.pow(priceMult, level);
const maxUpgrades = amount === "MAX" ? calculateMaxAffordableUpgrade(corp, data, amount) : amount;
const cost = calculateUpgradeCost(corp, data, maxUpgrades);
const tooltip = data.desc;
function onClick(): void {
if (corp.funds < cost) return;
try {
LevelUpgrade(corp, props.upgrade);
LevelUpgrade(corp, props.upgrade, maxUpgrades);
} catch (err) {
dialogBoxCreate(err + "");
}
@ -41,6 +42,7 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
<Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds < cost} sx={{ mx: 1 }} onClick={onClick}>
+{maxUpgrades} -&nbsp;
<MoneyCost money={cost} corp={corp} />
</Button>
<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 Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
import { MultiplierButtons } from "./MultiplierButtons";
import { SellCorporationModal } from "./modals/SellCorporationModal";
import { SellDivisionModal } from "./modals/SellDivisionModal";
interface IProps {
rerender: () => void;
}
export function Overview({ rerender }: IProps): React.ReactElement {
const corp = useCorporation();
const profit: number = corp.revenue - corp.expenses;
@ -100,6 +104,8 @@ export function Overview({ rerender }: IProps): React.ReactElement {
</Tooltip>
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
<BribeButton />
{corp.divisions.length != 0 ? <SellDivisionButton /> : <></>}
<RestartButton />
</Box>
<br />
<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>;
}
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 (
<>
<Paper sx={{ p: 1, my: 1 }}>
<Typography variant="h4">Unlocks</Typography>
<Grid container>
{Object.values(CorporationUnlockUpgrades)
.filter((upgrade: CorporationUnlockUpgrade) => !corp.unlockUpgrades[upgrade.index])
.map((upgrade: CorporationUnlockUpgrade) => (
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade.index} />
))}
{Object.values(CorporationUnlockUpgrades).map((upgrade: CorporationUnlockUpgrade) => (
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade.index} />
))}
</Grid>
</Paper>
<Paper sx={{ p: 1, my: 1 }}>
<Typography variant="h4">Upgrades</Typography>
<Grid container spacing={2}>
<Grid item xs={6}>
<MultiplierButtons onClicks={purchaseMultiplierOnClicks} purchaseMultiplier={purchaseMultiplier} />
</Grid>
</Grid>
<Grid container>
{corp.upgrades
.map((level: number, i: number) => CorporationUpgrades[i as CorporationUpgradeIndex])
.map((upgrade: CorporationUpgrade) => (
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade.index} />
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade.index} amount={purchaseMultiplier} />
))}
</Grid>
</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 {
profit: number;
}

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

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

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

@ -82,7 +82,25 @@ export function ExportModal(props: IProps): React.ReactElement {
<Modal open={props.open} onClose={props.onClose}>
<Typography>
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>
<Select onChange={onIndustryChange} value={industry}>
{corp.divisions

@ -55,11 +55,11 @@ export function GoPublicModal(props: IProps): React.ReactElement {
be deposited directly into your Corporation's funds).
<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>
<Box display="flex" alignItems="center">
<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
</Button>
</Box>

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

@ -1,13 +1,11 @@
import React, { useState } from "react";
import { formatMoney, formatPreciseMultiplier } from "../../../ui/formatNumber";
import { formatMoney } from "../../../ui/formatNumber";
import { Material } from "../../Material";
import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import Tooltip from "@mui/material/Tooltip";
import { useRerender } from "../../../ui/React/hooks";
interface IMarketTA2Props {
@ -17,30 +15,7 @@ interface IMarketTA2Props {
function MarketTA2(props: IMarketTA2Props): React.ReactElement {
const division = useDivision();
if (!division.hasResearch("Market-TA.II")) return <></>;
const [newCost, setNewCost] = useState<number>(props.mat.bCost);
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 {
props.mat.marketTa2 = event.target.checked;
@ -50,28 +25,15 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
return (
<>
<Typography variant="h4">Market-TA.II</Typography>
<br />
<Typography>
If you sell at {formatMoney(sCost)}, then you will sell x{formatPreciseMultiplier(markup)} as much compared to
if you sold at market price.
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>
<TextField type="number" onChange={onChange} value={newCost} />
<br />
<FormControlLabel
control={<Switch checked={props.mat.marketTa2} onChange={onMarketTA2} />}
label={
<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>
}
label={<Typography>Use Market-TA.II for Auto-Sale Price</Typography>}
/>
</>
);
@ -103,22 +65,15 @@ export function MaterialMarketTaModal(props: IProps): React.ReactElement {
<Typography>
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
<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>
<FormControlLabel
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
label={
<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>
}
label={<Typography>Use Market-TA.I for Auto-Sale Price</Typography>}
/>
</>

@ -1,13 +1,11 @@
import React, { useState } from "react";
import { formatMoney, formatPreciseMultiplier } from "../../../ui/formatNumber";
import { formatMoney } from "../../../ui/formatNumber";
import { Product } from "../../Product";
import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import Tooltip from "@mui/material/Tooltip";
import { useRerender } from "../../../ui/React/hooks";
interface ITa2Props {
@ -17,52 +15,26 @@ interface ITa2Props {
function MarketTA2(props: ITa2Props): React.ReactElement {
const division = useDivision();
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();
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setValue(parseFloat(event.target.value));
}
function onCheckedChange(event: React.ChangeEvent<HTMLInputElement>): void {
props.product.marketTa2 = event.target.checked;
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 (
<>
<Typography variant="h4">Market-TA.II</Typography>
<br />
<Typography>
If you sell at {formatMoney(sCost)}, then you will sell x{formatPreciseMultiplier(markup)} as much compared to
if you sold at market price.
If this is enabled, then this product 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>
<TextField type="number" onChange={onChange} value={value} />
<br />
<FormControlLabel
control={<Switch checked={props.product.marketTa2} onChange={onCheckedChange} />}
label={
<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>
}
label={<Typography>Use Market-TA.II for Auto-Sale Price</Typography>}
/>
</>
);
@ -94,22 +66,15 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
<Typography>
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
<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>
<FormControlLabel
control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
label={
<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>
}
label={<Typography>Use Market-TA.I for Auto-Sale Price</Typography>}
/>
</>

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

@ -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 { KEY } from "../../../utils/helpers/keyCodes";
function initialPrice(product: Product): string {
let val = product.sCost ? product.sCost + "" : "";
function initialPrice(product: Product, city: string): string {
let val = product.sCost[city] ? product.sCost[city] + "" : "";
if (product.marketTa2) {
val += " (Market-TA.II)";
} else if (product.marketTa1) {
@ -34,7 +34,7 @@ export function SellProductModal(props: IProps): React.ReactElement {
const [iQty, setQty] = useState<string>(
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 {
setChecked(event.target.checked);

@ -10,6 +10,7 @@ import { Money } from "../../../ui/React/Money";
import { SellShares } from "../../Actions";
import { KEY } from "../../../utils/helpers/keyCodes";
import { NumberInput } from "../../../ui/React/NumberInput";
import { isInteger } from "lodash";
interface IProps {
open: boolean;
onClose: () => void;
@ -22,15 +23,19 @@ export function SellSharesModal(props: IProps): React.ReactElement {
const corp = useCorporation();
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 {
if (props.shares === null) return <></>;
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`;
} else if (props.shares > corp.numShares) {
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 {
const stockSaleResults = corp.calculateShareSale(props.shares);
const profit = stockSaleResults[0];
@ -74,6 +79,9 @@ export function SellSharesModal(props: IProps): React.ReactElement {
(NOT your Corporation).
<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
large number of shares all at once will have an immediate effect in reducing your stock price.
<br />

@ -1,7 +1,7 @@
import React, { useState } from "react";
import { Warehouse } from "../../Warehouse";
import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../../Actions";
import { SetSmartSupply, SetSmartSupplyOption } from "../../Actions";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal";
import { useDivision } from "../Context";
@ -12,30 +12,50 @@ import { CorpMaterialName } from "@nsdefs";
import { materialNames } from "../../data/Constants";
import { useRerender } from "../../../ui/React/hooks";
interface ILeftoverProps {
interface ISSoptionProps {
matName: CorpMaterialName;
warehouse: Warehouse;
}
function Leftover(props: ILeftoverProps): React.ReactElement {
const [checked, setChecked] = useState(!!props.warehouse.smartSupplyUseLeftovers[props.matName]);
function SSoption(props: ISSoptionProps): React.ReactElement {
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 {
const matName = props.matName;
const material = props.warehouse.materials[matName];
SetSmartSupplyUseLeftovers(props.warehouse, material, event.target.checked);
SetSmartSupplyOption(props.warehouse, material, newValue);
} catch (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 (
<>
label={<Typography>{props.warehouse.materials[props.matName].name}</Typography>}
<FormControlLabel
control={<Switch checked={checked} onChange={onChange} />}
label={<Typography>{props.warehouse.materials[props.matName].name}</Typography>}
control={<Switch checked={value == "leftovers"} onChange={onLOChange} />}
label={<Typography>{"Use leftovers"}</Typography>}
/>
<FormControlLabel
control={<Switch checked={value == "imports"} onChange={onIChange} />}
label={<Typography>{"Use imported"}</Typography>}
/>
<br />
</>
@ -63,18 +83,29 @@ export function SmartSupplyModal(props: IProps): React.ReactElement {
for (const matName of Object.values(materialNames)) {
if (!props.warehouse.materials[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 (
<Modal open={props.open} onClose={props.onClose}>
<>
<Typography>Smart Supply purchases the exact amount of materials needed for maximal production.</Typography>
<br />
<FormControlLabel
control={<Switch checked={props.warehouse.smartSupplyEnabled} onChange={smartSupplyOnChange} />}
label={<Typography>Enable Smart Supply</Typography>}
/>
<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}
</>
</Modal>

@ -38,12 +38,12 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
dialogBoxCreate("You don't have enough company funds to throw a party!");
} else {
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;
if (mult > 0) {
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) +
" and was multiplied by " +
formatMultiplier(mult),

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

@ -34,49 +34,54 @@ export const Literatures: Record<string, Literature> = {};
fn = LiteratureNames.CorporationManagementHandbook;
txt =
"<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 " +
"$150b of your own money, but this $150b will get put into your Corporation's funds. " +
"Your Corporation can have many different divisions, each in a different Industry. There are many different " +
"types of Industries, each with different properties. To create your first division, click the " +
"'Expand into new Industry' button at the top of the management UI. The Agriculture " +
"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 different effect on various aspects of your Corporation. It is recommended to have at least " +
"one employee at each position.<br><br>" +
"Each industry uses some combination of Materials in order to produce other Materials and/or create Products. " +
"Specific information about this is displayed in each of your divisions' UI.<br><br>" +
"Products are special, industry-specific objects. They are different than Materials because you " +
"must manually choose to develop them, and you can choose to develop any number of Products. Developing " +
"a Product takes time, but a Product typically generates significantly 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 panel of the division UI (e.g. For the Software Industry, the button says 'Develop Software').<br><br>" +
"To get your supply chain system started, " +
"purchase the Materials that your industry needs to produce other Materials/Products. This can be done " +
"by clicking the 'Buy' button next to the corresponding Material(s). After you have the required Materials, " +
"you will immediately start production. The amount of Materials/Products you produce is based on a variety of factors, " +
"one of which is your employees and their productivity.<br><br>" +
"Once you start producing Materials/Products, you can sell them in order to start earning revenue. This can be done " +
"by clicking the 'Sell' button next to the corresponding Material or Product. The amount of Material/Product you sell is dependent " +
"on a wide variety of different factors.<br><br>" +
"These are the basics of getting your Corporation up and running! Now, you can start purchasing upgrades to improve " +
"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>" +
"To get started, visit the City Hall in Sector-12 in order to create a Corporation. This requires $150b of your own money, " +
"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 " +
"the government in exchange for 500m shares. Your Corporation can have many different divisions, each in a different Industry. " +
"There are many different types of Industries, each with different properties. To create your first division, click the 'Expand' " +
"(into new Industry) button at the top of the management UI. The Agriculture industry is 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 " +
"different effect on various aspects of your Corporation. It is recommended to have at least one employee at each position.<br><br>" +
"Each industry uses some combination of Materials in order to produce other Materials and/or create Products. Specific information " +
"about this is displayed in each of your divisions' UI.<br><br>" +
"Products are special, industry-specific objects. They are different than Materials because you must manually choose to develop them, " +
"and you can choose to develop any number of Products. Developing a Product takes time, but a Product typically generates significantly " +
"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 " +
"panel of the division UI (e.g. For the Software Industry, the button says 'Develop Software').<br><br>" +
"To get your supply chain system started, purchase the Materials that your industry needs to produce other Materials/Products. This can be " +
"done by clicking the 'Buy' button next to the corresponding Material(s). After you have the required Materials, you will immediately start " +
"production. The amount and quality/effective rating of Materials/Products you produce is based on a variety of factors, such as your employees " +
"and their productivity and the quality of materials used for production.<br><br>" +
"Once you start producing Materials/Products, you can sell them in order to start earning revenue. This can be done by clicking the 'Sell' " +
"button next to the corresponding Material or Product. The amount of Material/Product you sell is dependent on a wide variety of different factors. " +
"In order to produce and sell a Product you'll have to fully develop it first.<br><br>" +
"These are the basics of getting your Corporation up and running! Now, you can start purchasing upgrades to improve 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. In order to make money for yourself you " +
"can set dividends for a solid reliable income or you can sell your stock shares in order to make quick money.<br><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>" +
"-Purchasing Hardware, Robots, AI Cores, and Real Estate can potentially increase your production. " +
"The effects of these depend on 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>" +
"-Different employees excel in different jobs. For example, the highly intelligent employees will probably do best " +
"if they are assigned to do Engineering work or Research & Development.<br><br>" +
"-If your employees have low morale, energy, or happiness, their production will greatly suffer.<br><br>" +
"-Tech is important, but don't neglect sales! Having several Businessmen can boost your sales and your bottom line.<br><br>" +
"-Purchasing Hardware, Robots, AI Cores, and Real Estate can potentially increase your production. The effects of these depend on " +
"what industry you are in.<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>" +
"-Quality of materials used for production affects the quality/effective rating of materials/products produced, so vertical integration " +
"is important for high profits.<br><br>" +
"-Materials purchased from the open market are always of quality 1.<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>" +
"-Having company awareness is great, but what's really important is your company's popularity. Try to keep " +
"your popularity as high as possible to see the biggest benefit for your sales<br><br>" +
"-Having company awareness is great, but what's really important is your company's popularity. Try to keep your popularity as high as " +
"possible to see the biggest benefit for your sales<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";
Literatures[fn] = new Literature(title, fn, txt);
title = "A Brief History of Synthoids";

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

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

@ -4,15 +4,19 @@ import {
CorporationUnlockUpgrades,
} from "../../Corporation/data/CorporationUnlockUpgrades";
import { PlayerObject } from "./PlayerObject";
import { resetIndustryResearchTrees } from "../../Corporation/IndustryData";
export function canAccessCorporation(this: PlayerObject): boolean {
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({
name: corpName,
seedFunded: seedFunded,
});
//reset the research tree in case the corporation was restarted
resetIndustryResearchTrees();
if (this.bitNodeN === 3 || this.sourceFileLvl(3) === 3) {
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.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);
}
console.log(Player.bitNodeN);
dialogBoxCreate("hello");
// BitNode 3: Corporatocracy
if (Player.bitNodeN === 3) {
console.log("why isn't the dialogbox happening?");
homeComp.messages.push(LiteratureNames.CorporationManagementHandbook);
dialogBoxCreate(
"You received a copy of the Corporation Management Handbook on your home computer. " +

@ -6887,17 +6887,18 @@ type CorpEmployeePosition =
| "Business"
| "Management"
| "Research & Development"
| "Training"
| "Intern"
| "Unassigned";
/** @public */
type CorpIndustryName =
| "Energy"
| "Spring Water"
| "Water Utilities"
| "Agriculture"
| "Fishing"
| "Mining"
| "Food"
| "Refinery"
| "Restaurant"
| "Tobacco"
| "Chemical"
| "Pharmaceutical"
@ -7026,16 +7027,16 @@ export interface OfficeAPI {
* @param divisionName - Name of the division
* @param city - Name of the city
* @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;
/**
* Buy coffee for your employees
* Buy tea for your employees
* @param divisionName - Name of the division
* @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.
* @param divisionName - Name of the division
@ -7156,13 +7157,13 @@ export interface WarehouseAPI {
* @param divisionName - Name of the division
* @param city - Name of the city
* @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,
city: CityName | `${CityName}`,
materialName: string,
enabled: boolean,
option: string,
): void;
/**
* Set material buy data
@ -7190,10 +7191,11 @@ export interface WarehouseAPI {
/**
* Get product data
* @param divisionName - Name of the division
* @param city - Name of the city
* @param productName - Name of the product
* @returns product data
*/
getProduct(divisionName: string, productName: string): Product;
getProduct(divisionName: string, city: CityName | `${CityName}`, productName: string): Product;
/**
* Get material data
* @param divisionName - Name of the division
@ -7221,17 +7223,19 @@ export interface WarehouseAPI {
/**
* Set market TA 1 for a product.
* @param divisionName - Name of the division
* @param city - Name of the city
* @param productName - Name of the product
* @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.
* @param divisionName - Name of the division
* @param city - Name of the city
* @param productName - Name of the product
* @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
* @param sourceDivision - Source division
@ -7433,11 +7437,11 @@ export interface Corporation extends WarehouseAPI, OfficeAPI {
issueNewShares(amount?: number): number;
/** 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;
/** 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;
/** Get bonus time.
@ -7555,7 +7559,7 @@ interface CorpConstants {
issueNewSharesCooldown: number;
/** Cooldown for selling shares in game cycles (1 game cycle = 200ms) */
sellSharesCooldown: number;
coffeeCostPerEmployee: number;
teaCostPerEmployee: number;
gameCyclesPerMarketCycle: number;
gameCyclesPerCorpStateCycle: number;
secondsPerMarketCycle: number;
@ -7575,7 +7579,7 @@ interface CorpConstants {
employeeRaiseAmount: number;
/** Max products for a division without upgrades */
maxProductsBase: number;
/** The minimum decay value for happiness/morale/energy */
/** The minimum decay value for morale/energy */
minEmployeeDecay: number;
}
/** @public */
@ -7583,8 +7587,9 @@ type CorpStateName = "START" | "PURCHASE" | "PRODUCTION" | "EXPORT" | "SALE";
/** @public */
type CorpMaterialName =
| "Minerals"
| "Ore"
| "Water"
| "Energy"
| "Food"
| "Plants"
| "Metal"
@ -7699,16 +7704,22 @@ interface Product {
cmp: number | undefined;
/** Product Rating */
rat: number;
/** Effective rating */
effRat: number;
/** Product Properties. The data is \{qlt, per, dur, rel, aes, fea\} */
properties: { [key: string]: number };
/** Production cost */
pCost: number;
/** Sell cost, can be "MP+5" */
sCost: string | number;
/** 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] */
cityData: Record<CityName | `${CityName}`, number[]>;
sCost: string;
/** Sell amount, can be "PROD/2" */
sAmt: string;
/** Amount of product */
qty: number;
/** Amount of product produced */
prod: number;
/** Amount of product sold */
sell: number;
/** Creation progress - A number between 0-100 representing percentage */
developmentProgress: number;
}
@ -7736,6 +7747,8 @@ interface Material {
cost: number;
/** Sell cost, can be "MP+5" */
sCost: string | number;
/** Sell amount, can be "PROD/2" */
sAmt: string | number;
/** Export orders */
exp: Export[];
}
@ -7781,16 +7794,12 @@ export interface Office {
size: number;
/** Maximum amount of energy of the employees */
maxEne: number;
/** Maximum happiness of the employees */
maxHap: number;
/** Maximum morale of the employees */
maxMor: number;
/** Amount of employees */
employees: number;
/** Average energy of the employees */
avgEne: number;
/** Average happiness of the employees */
avgHap: number;
/** Average morale of the employees */
avgMor: number;
/** Total experience of all employees */