diff --git a/markdown/bitburner.corporation.buybackshares.md b/markdown/bitburner.corporation.buybackshares.md
index 0e16a29ca..6faa0f583 100644
--- a/markdown/bitburner.corporation.buybackshares.md
+++ b/markdown/bitburner.corporation.buybackshares.md
@@ -4,7 +4,7 @@
## Corporation.buyBackShares() method
-Buyback Shares
+Buyback Shares. Spend money from the player's wallet to transfer shares from public traders to the CEO.
**Signature:**
diff --git a/markdown/bitburner.corporation.md b/markdown/bitburner.corporation.md
index 42afe0a27..36c5d6a25 100644
--- a/markdown/bitburner.corporation.md
+++ b/markdown/bitburner.corporation.md
@@ -19,7 +19,7 @@ export interface Corporation extends WarehouseAPI, OfficeAPI
| --- | --- |
| [acceptInvestmentOffer()](./bitburner.corporation.acceptinvestmentoffer.md) | Accept investment based on you companies current valuation |
| [bribe(factionName, amountCash)](./bitburner.corporation.bribe.md) | Bribe a faction |
-| [buyBackShares(amount)](./bitburner.corporation.buybackshares.md) | Buyback Shares |
+| [buyBackShares(amount)](./bitburner.corporation.buybackshares.md) | Buyback Shares. Spend money from the player's wallet to transfer shares from public traders to the CEO. |
| [createCorporation(corporationName, selfFund)](./bitburner.corporation.createcorporation.md) | Create a Corporation |
| [expandCity(divisionName, city)](./bitburner.corporation.expandcity.md) | Expand to a new city |
| [expandIndustry(industryType, divisionName)](./bitburner.corporation.expandindustry.md) | Expand to a new industry |
@@ -40,5 +40,5 @@ export interface Corporation extends WarehouseAPI, OfficeAPI
| [issueNewShares(amount)](./bitburner.corporation.issuenewshares.md) | Issue new shares |
| [levelUpgrade(upgradeName)](./bitburner.corporation.levelupgrade.md) | Level an upgrade. |
| [purchaseUnlock(upgradeName)](./bitburner.corporation.purchaseunlock.md) | Unlock an upgrade |
-| [sellShares(amount)](./bitburner.corporation.sellshares.md) | Sell Shares |
+| [sellShares(amount)](./bitburner.corporation.sellshares.md) | Sell Shares. Transfer shares from the CEO to public traders to receive money in the player's wallet. |
diff --git a/markdown/bitburner.corporation.sellshares.md b/markdown/bitburner.corporation.sellshares.md
index 803e0931a..ccd2ac609 100644
--- a/markdown/bitburner.corporation.sellshares.md
+++ b/markdown/bitburner.corporation.sellshares.md
@@ -4,7 +4,7 @@
## Corporation.sellShares() method
-Sell Shares
+Sell Shares. Transfer shares from the CEO to public traders to receive money in the player's wallet.
**Signature:**
diff --git a/markdown/bitburner.corporationinfo.investorshares.md b/markdown/bitburner.corporationinfo.investorshares.md
new file mode 100644
index 000000000..e4edf1ee7
--- /dev/null
+++ b/markdown/bitburner.corporationinfo.investorshares.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [bitburner](./bitburner.md) > [CorporationInfo](./bitburner.corporationinfo.md) > [investorShares](./bitburner.corporationinfo.investorshares.md)
+
+## CorporationInfo.investorShares property
+
+Amount of shares owned by private investors. Not available for public sale or CEO buyback.
+
+**Signature:**
+
+```typescript
+investorShares: number;
+```
diff --git a/markdown/bitburner.corporationinfo.issuedshares.md b/markdown/bitburner.corporationinfo.issuedshares.md
index 673228d79..40b5f8f61 100644
--- a/markdown/bitburner.corporationinfo.issuedshares.md
+++ b/markdown/bitburner.corporationinfo.issuedshares.md
@@ -4,7 +4,7 @@
## CorporationInfo.issuedShares property
-Amount of acquirable shares.
+Amount of shares owned by public traders. Available for CEO buyback.
**Signature:**
diff --git a/markdown/bitburner.corporationinfo.md b/markdown/bitburner.corporationinfo.md
index e648131c2..13c50301b 100644
--- a/markdown/bitburner.corporationinfo.md
+++ b/markdown/bitburner.corporationinfo.md
@@ -22,14 +22,15 @@ interface CorporationInfo
| [divisions](./bitburner.corporationinfo.divisions.md) | | string\[\] | Array of all division names |
| [expenses](./bitburner.corporationinfo.expenses.md) | | number | Expenses per second this cycle |
| [funds](./bitburner.corporationinfo.funds.md) | | number | Funds available |
-| [issuedShares](./bitburner.corporationinfo.issuedshares.md) | | number | Amount of acquirable shares. |
+| [investorShares](./bitburner.corporationinfo.investorshares.md) | | number | Amount of shares owned by private investors. Not available for public sale or CEO buyback. |
+| [issuedShares](./bitburner.corporationinfo.issuedshares.md) | | number | Amount of shares owned by public traders. Available for CEO buyback. |
| [issueNewSharesCooldown](./bitburner.corporationinfo.issuenewsharescooldown.md) | | number | Cooldown until new shares can be issued |
| [name](./bitburner.corporationinfo.name.md) | | string | Name of the corporation |
-| [numShares](./bitburner.corporationinfo.numshares.md) | | number | Amount of share owned |
+| [numShares](./bitburner.corporationinfo.numshares.md) | | number | Amount of shares owned by the CEO. |
| [public](./bitburner.corporationinfo.public.md) | | boolean | Indicating if the company is public |
| [revenue](./bitburner.corporationinfo.revenue.md) | | number | Revenue per second this cycle |
| [sharePrice](./bitburner.corporationinfo.shareprice.md) | | number | Price of the shares |
| [shareSaleCooldown](./bitburner.corporationinfo.sharesalecooldown.md) | | number | Cooldown until shares can be sold again |
| [state](./bitburner.corporationinfo.state.md) | | string |
The next state to be processed.
I.e. when the state is PURCHASE, it means purchasing will occur during the next state transition.
Possible states are START, PURCHASE, PRODUCTION, EXPORT, SALE.
|
-| [totalShares](./bitburner.corporationinfo.totalshares.md) | | number | Total number of shares issues by this corporation |
+| [totalShares](./bitburner.corporationinfo.totalshares.md) | | number | Total number of shares issued by this corporation. |
diff --git a/markdown/bitburner.corporationinfo.numshares.md b/markdown/bitburner.corporationinfo.numshares.md
index d85f3fec2..6ac749ed7 100644
--- a/markdown/bitburner.corporationinfo.numshares.md
+++ b/markdown/bitburner.corporationinfo.numshares.md
@@ -4,7 +4,7 @@
## CorporationInfo.numShares property
-Amount of share owned
+Amount of shares owned by the CEO.
**Signature:**
diff --git a/markdown/bitburner.corporationinfo.totalshares.md b/markdown/bitburner.corporationinfo.totalshares.md
index 2a87395d3..009bf0234 100644
--- a/markdown/bitburner.corporationinfo.totalshares.md
+++ b/markdown/bitburner.corporationinfo.totalshares.md
@@ -4,7 +4,7 @@
## CorporationInfo.totalShares property
-Total number of shares issues by this corporation
+Total number of shares issued by this corporation.
**Signature:**
diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts
index afcb0c888..73bedfd95 100644
--- a/src/Corporation/Actions.ts
+++ b/src/Corporation/Actions.ts
@@ -1,5 +1,3 @@
-import { isInteger } from "lodash";
-
import { Player } from "@player";
import { CorpResearchName, CorpSmartSupplyOption } from "@nsdefs";
@@ -18,6 +16,7 @@ import { isRelevantMaterial } from "./ui/Helpers";
import { CityName } from "@enums";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { getRecordValues } from "../Types/Record";
+import { sellSharesFailureReason, buybackSharesFailureReason, issueNewSharesFailureReason } from "./helpers";
export function NewDivision(corporation: Corporation, industry: IndustryType, name: string): void {
if (corporation.divisions.size >= corporation.maxDivisions)
@@ -85,33 +84,72 @@ export function IssueDividends(corporation: Corporation, rate: number): void {
corporation.dividendRate = rate;
}
-export function IssueNewShares(corporation: Corporation, amount: number): [number, number, number] {
- const max = corporation.calculateMaxNewShares();
+export function GoPublic(corporation: Corporation, numShares: number): void {
+ const ceoOwnership = (corporation.numShares - numShares) / corporation.totalShares;
+ const initialSharePrice = corporation.getTargetSharePrice(ceoOwnership);
- // Round to nearest ten-millionth
- amount = Math.round(amount / 10e6) * 10e6;
-
- if (isNaN(amount) || amount < 10e6 || amount > max) {
- throw new Error(`Invalid value. Must be an number between 10m and ${max} (20% of total shares)`);
+ if (isNaN(numShares) || numShares < 0) {
+ throw new Error("Invalid value for number of issued shares");
}
+ if (numShares > corporation.numShares) {
+ throw new Error("You don't have that many shares to issue!");
+ }
+ corporation.public = true;
+ corporation.sharePrice = initialSharePrice;
+ corporation.issuedShares += numShares;
+ corporation.numShares -= numShares;
+ corporation.addNonIncomeFunds(numShares * initialSharePrice);
+}
- const newSharePrice = Math.round(corporation.sharePrice * 0.8);
+export function IssueNewShares(
+ corporation: Corporation,
+ amount: number,
+): [profit: number, amount: number, privateShares: number] {
+ const failureReason = issueNewSharesFailureReason(corporation, amount);
+ if (failureReason) throw new Error(failureReason);
- const profit = amount * newSharePrice;
- corporation.issueNewSharesCooldown = corpConstants.issueNewSharesCooldown;
+ const ceoOwnership = corporation.numShares / (corporation.totalShares + amount);
+ const newSharePrice = corporation.getTargetSharePrice(ceoOwnership);
- const privateOwnedRatio = 1 - (corporation.numShares + corporation.issuedShares) / corporation.totalShares;
+ const profit = (amount * (corporation.sharePrice + newSharePrice)) / 2;
+
+ const cooldownMultiplier = corporation.totalShares / corpConstants.initialShares;
+ corporation.issueNewSharesCooldown = corpConstants.issueNewSharesCooldown * cooldownMultiplier;
+
+ const privateOwnedRatio = corporation.investorShares / corporation.totalShares;
const maxPrivateShares = Math.round((amount / 2) * privateOwnedRatio);
const privateShares = Math.round(getRandomInt(0, maxPrivateShares) / 10e6) * 10e6;
corporation.issuedShares += amount - privateShares;
+ corporation.investorShares += privateShares;
corporation.totalShares += amount;
corporation.addNonIncomeFunds(profit);
- corporation.immediatelyUpdateSharePrice();
+ // Set sharePrice directly because all formulas will be based on stale cycleValuation data
+ corporation.sharePrice = newSharePrice;
return [profit, amount, privateShares];
}
+export function AcceptInvestmentOffer(corporation: Corporation): void {
+ if (
+ corporation.fundingRound >= corpConstants.fundingRoundShares.length ||
+ corporation.fundingRound >= corpConstants.fundingRoundMultiplier.length ||
+ corporation.public
+ ) {
+ throw new Error("No more investment offers are available.");
+ }
+ const val = corporation.valuation;
+ const percShares = corpConstants.fundingRoundShares[corporation.fundingRound];
+ const roundMultiplier = corpConstants.fundingRoundMultiplier[corporation.fundingRound];
+ const funding = val * percShares * roundMultiplier;
+ const investShares = Math.floor(corpConstants.initialShares * percShares);
+ corporation.fundingRound++;
+ corporation.addNonIncomeFunds(funding);
+
+ corporation.numShares -= investShares;
+ corporation.investorShares += investShares;
+}
+
export function SellMaterial(material: Material, amount: string, price: string): void {
if (price === "") price = "0";
if (amount === "") amount = "0";
@@ -280,17 +318,10 @@ export function BulkPurchase(
}
export function SellShares(corporation: Corporation, numShares: number): number {
- 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);
- const profit = stockSaleResults[0];
- const newSharePrice = stockSaleResults[1];
- const newSharesUntilUpdate = stockSaleResults[2];
+ const failureReason = sellSharesFailureReason(corporation, numShares);
+ if (failureReason) throw new Error(failureReason);
+
+ const [profit, newSharePrice, newSharesUntilUpdate] = corporation.calculateShareSale(numShares);
corporation.numShares -= numShares;
corporation.issuedShares += numShares;
@@ -302,15 +333,16 @@ export function SellShares(corporation: Corporation, numShares: number): number
}
export function BuyBackShares(corporation: Corporation, numShares: number): boolean {
- 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;
- if (Player.money < numShares * buybackPrice) throw new Error("You cant afford that many shares!");
+ const failureReason = buybackSharesFailureReason(corporation, numShares);
+ if (failureReason) throw new Error(failureReason);
+
+ const [cost, newSharePrice, newSharesUntilUpdate] = corporation.calculateShareBuyback(numShares);
+
corporation.numShares += numShares;
corporation.issuedShares -= numShares;
- Player.loseMoney(numShares * buybackPrice, "corporation");
+ corporation.sharePrice = newSharePrice;
+ corporation.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
+ Player.loseMoney(cost, "corporation");
return true;
}
diff --git a/src/Corporation/Corporation.ts b/src/Corporation/Corporation.ts
index 5156f0b10..653c1f21d 100644
--- a/src/Corporation/Corporation.ts
+++ b/src/Corporation/Corporation.ts
@@ -12,7 +12,7 @@ import { showLiterature } from "../Literature/LiteratureHelpers";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
-import { CorpStateName } from "@nsdefs";
+import { CorpStateName, InvestmentOffer } from "@nsdefs";
import { calculateUpgradeCost } from "./helpers";
import { JSONMap, JSONSet } from "../Types/Jsonable";
import { formatMoney } from "../ui/formatNumber";
@@ -46,6 +46,7 @@ export class Corporation {
issueNewSharesCooldown = 0; // Game cycles until player can issue shares again
dividendRate = 0;
dividendTax = 1 - currentNodeMults.CorporationSoftcap + 0.15;
+ investorShares = 0;
issuedShares = 0;
sharePrice = 0;
storedCycles = 0;
@@ -232,10 +233,17 @@ export class Corporation {
this.totalAssets = assets;
}
- getTargetSharePrice(): number {
- // Note: totalShares - numShares is not the same as issuedShares because
- // issuedShares does not account for private investors
- return this.valuation / (2 * (this.totalShares - this.numShares) + 1);
+ getTargetSharePrice(ceoOwnership: number | null = null): number {
+ // Share price is proportional to total corporation valuation.
+ // When the CEO owns 0% of the company, market cap is 0.5x valuation.
+ // When the CEO owns 25% of the company, market cap is 1.0x valuation.
+ // When the CEO owns 100% of shares, market cap is 1.5x valuation.
+ if (ceoOwnership === null) {
+ ceoOwnership = this.numShares / this.totalShares;
+ }
+ const ceoConfidence = 0.5 + Math.sqrt(Math.max(0, ceoOwnership));
+ const marketCap = this.valuation * ceoConfidence;
+ return marketCap / this.totalShares;
}
updateSharePrice(): void {
@@ -250,10 +258,6 @@ export class Corporation {
}
}
- immediatelyUpdateSharePrice(): void {
- this.sharePrice = this.getTargetSharePrice();
- }
-
calculateMaxNewShares(): number {
const maxNewSharesUnrounded = Math.round(this.totalShares * 0.2);
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 10e6);
@@ -261,17 +265,18 @@ export class Corporation {
}
// Calculates how much money will be made and what the resulting stock price
- // will be when the player sells his/her shares
+ // will be when the player sells their shares
// @return - [Player profit, final stock price, end shareSalesUntilPriceUpdate property]
- calculateShareSale(numShares: number): [number, number, number] {
- let sharesTracker = numShares;
+ calculateShareSale(numShares: number): [profit: number, sharePrice: number, sharesUntilUpdate: number] {
+ let sharesRemaining = numShares;
let sharesUntilUpdate = this.shareSalesUntilPriceUpdate;
let sharePrice = this.sharePrice;
let sharesSold = 0;
let profit = 0;
- let targetPrice = this.getTargetSharePrice();
- const maxIterations = Math.ceil(numShares / corpConstants.sharesPerPriceUpdate);
+ const sharesPerStep = Math.sign(numShares || 1) * corpConstants.sharesPerPriceUpdate;
+ const maxIterations = Math.ceil(numShares / sharesPerStep);
+
if (isNaN(maxIterations) || maxIterations > 10e6) {
console.error(
`Something went wrong or unexpected when calculating share sale. Max iterations calculated to be ${maxIterations}`,
@@ -280,28 +285,59 @@ export class Corporation {
}
for (let i = 0; i < maxIterations; ++i) {
- if (sharesTracker < sharesUntilUpdate) {
- profit += sharePrice * sharesTracker;
- sharesUntilUpdate -= sharesTracker;
+ if (Math.abs(sharesRemaining) < Math.abs(sharesUntilUpdate)) {
+ profit += sharePrice * sharesRemaining;
+ sharesUntilUpdate -= sharesRemaining;
break;
} else {
- profit += sharePrice * sharesUntilUpdate;
- sharesUntilUpdate = corpConstants.sharesPerPriceUpdate;
- sharesTracker -= sharesUntilUpdate;
- sharesSold += sharesUntilUpdate;
- targetPrice = this.valuation / (2 * (this.totalShares + sharesSold - this.numShares));
- // Calculate what new share price would be
+ profit += sharePrice * sharesPerStep;
+ sharesRemaining -= sharesPerStep;
+ sharesSold += sharesPerStep;
+
+ // Update the share price
+ const ceoOwnership = (this.numShares - sharesSold) / this.totalShares;
+ const targetPrice = this.getTargetSharePrice(ceoOwnership);
if (sharePrice <= targetPrice) {
sharePrice *= 1 + 0.5 * 0.01;
} else {
sharePrice *= 1 - 0.5 * 0.01;
}
+ sharesUntilUpdate = corpConstants.sharesPerPriceUpdate;
}
}
return [profit, sharePrice, sharesUntilUpdate];
}
+ calculateShareBuyback(numShares: number): [cost: number, sharePrice: number, sharesUntilUpdate: number] {
+ const [profit, sharePrice, sharesUntilUpdate] = this.calculateShareSale(-numShares);
+ const cost = -1.1 * profit;
+ return [cost, sharePrice, sharesUntilUpdate];
+ }
+
+ getInvestmentOffer(): InvestmentOffer {
+ if (
+ this.fundingRound >= corpConstants.fundingRoundShares.length ||
+ this.fundingRound >= corpConstants.fundingRoundMultiplier.length ||
+ this.public
+ )
+ return {
+ funds: 0,
+ shares: 0,
+ round: this.fundingRound + 1, // Make more readable
+ }; // Don't throw an error here, no reason to have a second function to check if you can get investment.
+ const val = this.valuation;
+ const percShares = corpConstants.fundingRoundShares[this.fundingRound];
+ const roundMultiplier = corpConstants.fundingRoundMultiplier[this.fundingRound];
+ const funding = val * percShares * roundMultiplier;
+ const investShares = Math.floor(corpConstants.initialShares * percShares);
+ return {
+ funds: funding,
+ shares: investShares,
+ round: this.fundingRound + 1, // Make more readable
+ };
+ }
+
convertCooldownToString(cd: number): string {
// The cooldown value is based on game cycles. Convert to a simple string
const seconds = cd / 5;
diff --git a/src/Corporation/data/Constants.ts b/src/Corporation/data/Constants.ts
index 2dc986b74..3c31970fc 100644
--- a/src/Corporation/data/Constants.ts
+++ b/src/Corporation/data/Constants.ts
@@ -33,7 +33,7 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
/** Names of all one-time corporation-wide unlocks */
unlockNames: APIUnlockName[] = Object.values(CorpUnlockName),
upgradeNames: APIUpgradeName[] = Object.values(CorpUpgradeName),
- /** Names of all reasearches common to all industries */
+ /** Names of all researches common to all industries */
researchNamesBase: CorpResearchName[] = Object.values(CorpBaseResearchName),
/** Names of all researches only available to product industries */
researchNamesProductOnly: CorpResearchName[] = Object.values(CorpProductResearchName),
@@ -42,8 +42,8 @@ export const stateNames: CorpStateName[] = ["START", "PURCHASE", "PRODUCTION", "
initialShares = 1e9,
/** When selling large number of shares, price is dynamically updated for every batch of this amount */
sharesPerPriceUpdate = 1e6,
- /** Cooldown for issue new shares cooldown in game cycles. 12 hours. */
- issueNewSharesCooldown = 216e3,
+ /** Cooldown for issue new shares cooldown in game cycles. Initially 4 hours. */
+ issueNewSharesCooldown = 72e3,
/** Cooldown for selling shares in game cycles. 1 hour. */
sellSharesCooldown = 18e3,
teaCostPerEmployee = 500e3,
diff --git a/src/Corporation/helpers.ts b/src/Corporation/helpers.ts
index ec3085cd0..2f0511f2e 100644
--- a/src/Corporation/helpers.ts
+++ b/src/Corporation/helpers.ts
@@ -1,4 +1,6 @@
-import { PositiveInteger } from "../types";
+import { Player } from "@player";
+import { PositiveInteger, isPositiveInteger } from "../types";
+import { formatShares } from "../ui/formatNumber";
import { Corporation } from "./Corporation";
import { CorpUpgrade } from "./data/CorporationUpgrades";
@@ -29,3 +31,43 @@ export function calculateMaxAffordableUpgrade(corp: Corporation, upgrade: CorpUp
const sanitizedValue = maxAffordableUpgrades >= 0 ? maxAffordableUpgrades : 0;
return sanitizedValue as PositiveInteger | 0;
}
+
+/** Returns a string representing the reason a share sale should fail, or empty string if there is no issue. */
+export function sellSharesFailureReason(corp: Corporation, numShares: number): string {
+ if (!isPositiveInteger(numShares)) return "Number of shares must be a positive integer.";
+ else if (numShares > corp.numShares) return "You do not have that many shares to sell.";
+ else if (numShares === corp.numShares) return "You cannot sell all your shares.";
+ else if (numShares > 1e14) return `Cannot sell more than ${formatShares(1e14)} shares at a time.`;
+ else if (!corp.public) return "Cannot sell shares before going public.";
+ else if (corp.shareSaleCooldown)
+ return `Cannot sell shares for another ${corp.convertCooldownToString(corp.shareSaleCooldown)}.`;
+ return "";
+}
+
+/** Returns a string representing the reason a share buyback should fail, or empty string if there is no issue. */
+export function buybackSharesFailureReason(corp: Corporation, numShares: number): string {
+ if (!isPositiveInteger(numShares)) return "Number of shares must be a positive integer.";
+ if (numShares > corp.issuedShares) return "Not enough shares are available for buyback.";
+ if (numShares > 1e14) return `Cannot buy more than ${formatShares(1e14)} shares at a time.`;
+ if (!corp.public) return "Cannot buy back shares before going public.";
+
+ const [cost] = corp.calculateShareBuyback(numShares);
+ if (Player.money < cost) return "You cannot afford that many shares.";
+
+ return "";
+}
+
+/** Returns a string representing the reason issuing new shares should fail, or empty string if there is no issue. */
+export function issueNewSharesFailureReason(corp: Corporation, numShares: number): string {
+ if (!isPositiveInteger(numShares)) return "Number of shares must be a positive integer.";
+ if (numShares % 10e6 !== 0) return "Number of shares must be a multiple of 10 million.";
+ if (!corp.public) return "Cannot issue new shares before going public.";
+
+ const maxNewShares = corp.calculateMaxNewShares();
+ if (numShares > maxNewShares) return `Number of shares cannot exceed ${maxNewShares} (20% of total shares).`;
+
+ const cooldown = corp.issueNewSharesCooldown;
+ if (cooldown > 0) return `Cannot issue new shares for another ${corp.convertCooldownToString(cooldown)}.`;
+
+ return "";
+}
diff --git a/src/Corporation/ui/MaterialElem.tsx b/src/Corporation/ui/MaterialElem.tsx
index ab21e69dc..46a377e71 100644
--- a/src/Corporation/ui/MaterialElem.tsx
+++ b/src/Corporation/ui/MaterialElem.tsx
@@ -8,7 +8,7 @@ import { Warehouse } from "../Warehouse";
import { ExportModal } from "./modals/ExportModal";
import { SellMaterialModal } from "./modals/SellMaterialModal";
import { PurchaseMaterialModal } from "./modals/PurchaseMaterialModal";
-import { formatBigNumber, formatCorpStat, formatMoney, formatQuality } from "../../ui/formatNumber";
+import { formatBigNumber, formatCorpStat, formatQuality } from "../../ui/formatNumber";
import { isString } from "../../utils/helpers/string";
import { Money } from "../../ui/React/Money";
import { useCorporation, useDivision } from "./Context";
@@ -118,7 +118,9 @@ export function MaterialElem(props: IMaterialProps): React.ReactElement {
}
>
- MP: {formatMoney(mat.marketPrice)}
+
+ MP:
+ The quality of your material. Higher quality will lead to more sales}
diff --git a/src/Corporation/ui/Overview.tsx b/src/Corporation/ui/Overview.tsx
index 9df0ae8a3..6ba08887c 100644
--- a/src/Corporation/ui/Overview.tsx
+++ b/src/Corporation/ui/Overview.tsx
@@ -77,8 +77,21 @@ export function Overview({ rerender }: IProps): React.ReactElement {
title={
{formatShares(corp.numShares)} >,
+ <>({formatPercent(corp.numShares / corp.totalShares)})>,
+ ],
+ [
+ "Outstanding Shares:",
+ <> {formatShares(corp.issuedShares)} >,
+ <>({formatPercent(corp.issuedShares / corp.totalShares)})>,
+ ],
+ [
+ "Private Shares:",
+ <> {formatShares(corp.investorShares)} >,
+ <>({formatPercent(corp.investorShares / corp.totalShares)})>,
+ ],
]}
/>
}
@@ -96,8 +109,8 @@ export function Overview({ rerender }: IProps): React.ReactElement {
- Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' This is a .lit file
- that guides you through the beginning of setting up a Corporation and provides some tips/pointers for
+ Get a copy of and read The Complete Handbook for Creating a Successful Corporation. This is a .lit
+ file that guides you through the beginning of setting up a Corporation and provides some tips/pointers for
helping you get started with managing it.
>
}
@@ -125,7 +138,7 @@ function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement
const [findInvestorsopen, setFindInvestorsopen] = useState(false);
const [goPublicopen, setGoPublicopen] = useState(false);
- const fundingAvailable = corp.fundingRound < 4;
+ const fundingAvailable = corp.fundingRound < corpConstants.fundingRoundShares.length;
const findInvestorsTooltip = fundingAvailable
? "Search for private investors who will give you startup funding in exchange for equity (stock shares) in your company"
: "";
@@ -213,29 +226,27 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
const [issueDividendsOpen, setIssueDividendsOpen] = useState(false);
const sellSharesOnCd = corp.shareSaleCooldown > 0;
- const sellSharesTooltip = sellSharesOnCd
- ? "Cannot sell shares for " + corp.convertCooldownToString(corp.shareSaleCooldown)
- : "Sell your shares in the company. The money earned from selling your " +
- "shares goes into your personal account, not the Corporation's. " +
- "This is one of the only ways to profit from your business venture.";
+ const sellSharesTooltip =
+ "Sell your shares in the company. The money earned from selling your " +
+ "shares goes into your personal account, not the Corporation's. " +
+ "This is one of the only ways to profit from your business venture.";
const issueNewSharesOnCd = corp.issueNewSharesCooldown > 0;
- const issueNewSharesTooltip = issueNewSharesOnCd
- ? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown)
- : "Issue new equity shares to raise capital.";
return (
<>
setSellSharesOpen(true)}
>
Sell Shares
setSellSharesOpen(false)} rerender={rerender} />
setBuybackSharesOpen(true)}
>
@@ -243,13 +254,15 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
setBuybackSharesOpen(false)} rerender={rerender} />
setIssueNewSharesOpen(true)}
>
Issue New Shares
- setIssueNewSharesOpen(false)} />
+ setIssueNewSharesOpen(false)} rerender={rerender} />
setIssueDividendsOpen(true)}
@@ -306,13 +319,22 @@ function SellDivisionButton(): React.ReactElement {
function RestartButton(): React.ReactElement {
const [open, setOpen] = useState(false);
+ const corp = useCorporation();
+ const sellSharesOnCd = corp.shareSaleCooldown > 0;
+
function restart(): void {
setOpen(true);
}
return (
<>
-
+
Sell CEO position
setOpen(false)} />
diff --git a/src/Corporation/ui/ProductElem.tsx b/src/Corporation/ui/ProductElem.tsx
index 66990c03b..a5fa62833 100644
--- a/src/Corporation/ui/ProductElem.tsx
+++ b/src/Corporation/ui/ProductElem.tsx
@@ -8,7 +8,7 @@ import { LimitProductProductionModal } from "./modals/LimitProductProductionModa
import { SellProductModal } from "./modals/SellProductModal";
import { CancelProductModal } from "./modals/CancelProductModal";
-import { formatBigNumber, formatMoney, formatPercent } from "../../ui/formatNumber";
+import { formatBigNumber, formatPercent } from "../../ui/formatNumber";
import { isString } from "../../utils/helpers/string";
import { Money } from "../../ui/React/Money";
@@ -135,7 +135,7 @@ export function ProductElem(props: IProductProps): React.ReactElement {
An estimate of the material cost it takes to create this Product.}>
- Est. Production Cost: {formatMoney(product.productionCost / corpConstants.baseProductProfitMult)}
+ Est. Production Cost:
@@ -148,7 +148,9 @@ export function ProductElem(props: IProductProps): React.ReactElement {
}
>
- Est. Market Price: {formatMoney(product.productionCost)}
+
+ Est. Market Price:
+
diff --git a/src/Corporation/ui/modals/BuybackSharesModal.tsx b/src/Corporation/ui/modals/BuybackSharesModal.tsx
index d1e70356f..427e8d404 100644
--- a/src/Corporation/ui/modals/BuybackSharesModal.tsx
+++ b/src/Corporation/ui/modals/BuybackSharesModal.tsx
@@ -1,15 +1,15 @@
import React, { useState } from "react";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal";
-import { formatBigNumber, formatMoney } from "../../../ui/formatNumber";
-import { Player } from "@player";
+import { Money } from "../../../ui/React/Money";
+import { formatShares } from "../../../ui/formatNumber";
import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography";
import { ButtonWithTooltip } from "../../../ui/Components/ButtonWithTooltip";
import { NumberInput } from "../../../ui/React/NumberInput";
import { BuyBackShares } from "../../Actions";
-import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { KEY } from "../../../utils/helpers/keyCodes";
-import { isPositiveInteger } from "../../../types";
+import { buybackSharesFailureReason } from "../../helpers";
interface IProps {
open: boolean;
@@ -23,44 +23,28 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const [shares, setShares] = useState(NaN);
- const currentStockPrice = corp.sharePrice;
- const buybackPrice = currentStockPrice * 1.1;
- const disabledText = !isPositiveInteger(shares)
- ? "Number of shares must be a positive integer"
- : shares > corp.issuedShares
- ? "There are not enough shares available to buyback this many"
- : shares * buybackPrice > Player.money
- ? "Insufficient player funds"
- : "";
+ const [cost, sharePrice] = corp.calculateShareBuyback((props.open && shares) || 0);
+ const disabledText = buybackSharesFailureReason(corp, shares);
function buy(): void {
if (disabledText) return;
try {
BuyBackShares(corp, shares);
+ dialogBoxCreate(
+ <>
+
+ You bought {formatShares(shares)} shares for .
+
+
+ {corp.name}'s stock price rose to per share.
+
+ >,
+ );
+ props.onClose();
+ props.rerender();
+ setShares(NaN);
} catch (err) {
- dialogBoxCreate(err + "");
- }
- props.onClose();
- props.rerender();
- }
-
- function CostIndicator(): React.ReactElement {
- if (shares === null) return <>>;
- if (isNaN(shares) || shares <= 0) {
- return <>ERROR: Invalid value entered for number of shares to buyback>;
- } else if (shares > corp.issuedShares) {
- return (
- <>
- There are not this many shares available to buy back. There are only {formatBigNumber(corp.issuedShares)}{" "}
- outstanding shares.
- >
- );
- } else {
- return (
- <>
- Purchase {shares} shares for a total of {formatMoney(shares * buybackPrice)}
- >
- );
+ dialogBoxCreate(`${err}`);
}
}
@@ -70,23 +54,45 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
return (
-
- Enter the number of outstanding shares you would like to buy back. These shares must be bought at a 10% premium.
- However, repurchasing shares from the market tends to lead to an increase in stock price.
-
-
- To purchase these shares, you must use your own money (NOT your Corporation's funds).
-
-
- The current buyback price of your company's stock is {formatMoney(buybackPrice)}. Your company currently has{" "}
- {formatBigNumber(corp.issuedShares)} outstanding stock shares.
+
+ Enter the number of outstanding shares you would like to buy back.
+
+
Buying back shares will cause the stock price to rise due to market forces.
+
These shares must be bought at a 10% premium over the market price.
+
You purchase these shares with your own money (NOT your Corporation's funds).
+
+ {corp.name} currently has {formatShares(corp.issuedShares)} outstanding stock shares, valued at{" "}
+ per share.
-
-
+
Buy shares
+ {cost > 0 ? (
+ <>
+ -
+ {" "}
+ >
+ ) : (
+ <>>
+ )}
+
+
+ {!shares ? null : disabledText ? (
+ disabledText
+ ) : (
+ <>
+ {corp.name}'s stock price will rise to per share.
+ >
+ )}
+
);
}
diff --git a/src/Corporation/ui/modals/CreateCorporationModal.tsx b/src/Corporation/ui/modals/CreateCorporationModal.tsx
index 756c137b0..023f2e0e6 100644
--- a/src/Corporation/ui/modals/CreateCorporationModal.tsx
+++ b/src/Corporation/ui/modals/CreateCorporationModal.tsx
@@ -4,6 +4,7 @@ import { Money } from "../../../ui/React/Money";
import { Modal } from "../../../ui/React/Modal";
import { Router } from "../../../ui/GameRoot";
import { Page } from "../../../ui/Router";
+import { formatShares } from "../../../ui/formatNumber";
import { Player } from "@player";
import Typography from "@mui/material/Typography";
import { ButtonWithTooltip } from "../../../ui/Components/ButtonWithTooltip";
@@ -54,15 +55,19 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
return (
- Would you like to start a corporation? This will require $150b for registration and initial funding.{" "}
- {Player.bitNodeN === 3 &&
- `This $150b
- can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million
- shares`}
+ Would you like to start a corporation? This will require for
+ registration and initial funding.{" "}
+ {Player.bitNodeN === 3 && (
+ <>
+ This can either be self-funded, or you can obtain the seed money from the government
+ in exchange for {formatShares(500e6)} shares (a 33.3% stake in the company).
+ >
+ )}
If you would like to start one, please enter a name for your corporation below:
+
{Player.bitNodeN === 3 && (
@@ -71,7 +76,7 @@ export function CreateCorporationModal(props: IProps): React.ReactElement {
)}
Self-Fund ()
diff --git a/src/Corporation/ui/modals/FindInvestorsModal.tsx b/src/Corporation/ui/modals/FindInvestorsModal.tsx
index b7657e32e..9d3504289 100644
--- a/src/Corporation/ui/modals/FindInvestorsModal.tsx
+++ b/src/Corporation/ui/modals/FindInvestorsModal.tsx
@@ -1,8 +1,10 @@
import React from "react";
-import { formatMoney, formatPercent, formatShares } from "../../../ui/formatNumber";
-import * as corpConstants from "../../data/Constants";
+import { dialogBoxCreate } from "../../../ui/React/DialogBox";
+import { formatPercent, formatShares } from "../../../ui/formatNumber";
import { Modal } from "../../../ui/React/Modal";
+import { Money } from "../../../ui/React/Money";
import { useCorporation } from "../Context";
+import { AcceptInvestmentOffer } from "../../Actions";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@@ -13,40 +15,52 @@ interface IProps {
rerender: () => void;
}
-// Create a popup that lets the player manage exports
+// Create a popup that lets the player manage investment offers
export function FindInvestorsModal(props: IProps): React.ReactElement {
- const corporation = useCorporation();
- const val = corporation.valuation;
- if (
- corporation.fundingRound >= corpConstants.fundingRoundShares.length ||
- corporation.fundingRound >= corpConstants.fundingRoundMultiplier.length
- )
- return <>>;
- const percShares = corpConstants.fundingRoundShares[corporation.fundingRound];
- const roundMultiplier = corpConstants.fundingRoundMultiplier[corporation.fundingRound];
- const funding = val * percShares * roundMultiplier;
- const investShares = Math.floor(corpConstants.initialShares * percShares);
+ const corp = useCorporation();
+ const { funds, shares } = corp.getInvestmentOffer();
function findInvestors(): void {
- corporation.fundingRound++;
- corporation.addNonIncomeFunds(funding);
- corporation.numShares -= investShares;
- props.rerender();
- props.onClose();
+ if (shares === 0) return;
+ try {
+ AcceptInvestmentOffer(corp);
+ dialogBoxCreate(
+ <>
+ You accepted the investment offer.
+
+ {corp.name} received .
+
+
+ Your remaining equity is {formatPercent(corp.numShares / corp.totalShares, 1)}.
+
+ >,
+ );
+ props.onClose();
+ props.rerender();
+ } catch (err) {
+ dialogBoxCreate(`${err}`);
+ }
}
+
return (
- An investment firm has offered you {formatMoney(funding)} in funding in exchange for a{" "}
- {formatPercent(percShares, 3)} stake in the company ({formatShares(investShares)} shares).
+ An investment firm has offered to buy {formatShares(shares)} shares of stock (a{" "}
+ {formatPercent(shares / corp.totalShares, 1)} stake in the company).
- Do you accept or reject this offer?
+ {corp.name} will receive .
+
+ Your equity will fall to {formatPercent((corp.numShares - shares) / corp.totalShares, 1)}.
- Hint: Investment firms will offer more money if your corporation is turning a profit
+ Hint: Investment firms will offer more money if your Corporation is turning a profit.
+
+
+ Do you accept this offer?
-
+
+
);
}
diff --git a/src/Corporation/ui/modals/GoPublicModal.tsx b/src/Corporation/ui/modals/GoPublicModal.tsx
index b3872c1a2..2b02b241e 100644
--- a/src/Corporation/ui/modals/GoPublicModal.tsx
+++ b/src/Corporation/ui/modals/GoPublicModal.tsx
@@ -1,7 +1,8 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal";
-import { formatMoney, formatShares } from "../../../ui/formatNumber";
+import { Money } from "../../../ui/React/Money";
+import { formatShares } from "../../../ui/formatNumber";
import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography";
import { ButtonWithTooltip } from "../../../ui/Components/ButtonWithTooltip";
@@ -9,6 +10,7 @@ import { NumberInput } from "../../../ui/React/NumberInput";
import Box from "@mui/material/Box";
import { KEY } from "../../../utils/helpers/keyCodes";
import { isPositiveInteger } from "../../../types";
+import { GoPublic } from "../../Actions";
interface IProps {
open: boolean;
@@ -20,26 +22,9 @@ interface IProps {
export function GoPublicModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const [shares, setShares] = useState(NaN);
- const initialSharePrice = corp.valuation / corp.totalShares;
- function goPublic(): void {
- const initialSharePrice = corp.valuation / corp.totalShares;
- if (shares >= corp.numShares || (shares !== 0 && !isPositiveInteger(shares))) return;
- corp.public = true;
- corp.sharePrice = initialSharePrice;
- corp.issuedShares = shares;
- corp.numShares -= shares;
- corp.addFunds(shares * initialSharePrice);
- props.rerender();
- dialogBoxCreate(
- `You took your ${corp.name} public and earned ` + `${formatMoney(shares * initialSharePrice)} in your IPO`,
- );
- props.onClose();
- }
-
- function onKeyDown(event: React.KeyboardEvent): void {
- if (event.key === KEY.ENTER) goPublic();
- }
+ const ceoOwnership = (corp.numShares - (shares || 0)) / corp.totalShares;
+ const initialSharePrice = corp.getTargetSharePrice(ceoOwnership);
const disabledText =
shares >= corp.numShares
@@ -48,22 +33,62 @@ export function GoPublicModal(props: IProps): React.ReactElement {
? "Must issue an non-negative integer number of shares"
: "";
+ function goPublic(): void {
+ if (disabledText) return;
+ try {
+ GoPublic(corp, shares);
+ dialogBoxCreate(
+
+ {corp.name} went public and earned in its IPO.
+ ,
+ );
+ props.onClose();
+ props.rerender();
+ setShares(NaN);
+ } catch (err) {
+ dialogBoxCreate(`${err}`);
+ }
+ }
+
+ function onKeyDown(event: React.KeyboardEvent): void {
+ if (event.key === KEY.ENTER) goPublic();
+ }
+
return (
-
- Enter the number of shares you would like to issue for your IPO. These shares will be publicly sold and you will
- no longer own them. Your Corporation will receive {formatMoney(initialSharePrice)} per share (the IPO money will
- be deposited directly into your Corporation's funds).
-
-
+
+ Enter the number of shares you would like to issue for your IPO.
+
+
These shares will be publicly sold and you will no longer own them.
+
The IPO money will be deposited directly into your Corporation's funds.
+
You can issue some, but not all, of your {formatShares(corp.numShares)} shares.
+
-
+
Go Public
+
+
+ {isNaN(shares) ? null : disabledText ? (
+ disabledText
+ ) : (
+ <>
+ Go public at per share?
+
+ {corp.name} will receive .
+ >
+ )}
+
);
}
diff --git a/src/Corporation/ui/modals/IssueDividendsModal.tsx b/src/Corporation/ui/modals/IssueDividendsModal.tsx
index 43e1dd616..3b60b44dd 100644
--- a/src/Corporation/ui/modals/IssueDividendsModal.tsx
+++ b/src/Corporation/ui/modals/IssueDividendsModal.tsx
@@ -1,6 +1,8 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { Modal } from "../../../ui/React/Modal";
+import { Money } from "../../../ui/React/Money";
+import { MoneyRate } from "../../../ui/React/MoneyRate";
import * as corpConstants from "../../data/Constants";
import { IssueDividends } from "../../Actions";
import { useCorporation } from "../Context";
@@ -53,20 +55,19 @@ export function IssueDividendsModal(props: IProps): React.ReactElement {
yourself, as well.
- In order to issue dividends, simply allocate some percentage of your corporation's profits to dividends. This
- percentage must be an integer between 0 and 100. (A percentage of 0 means no dividends will be issued)
+ Note that issuing dividends will negatively affect {corp.name}'s stock price.
- Two important things to note:
-
- * Issuing dividends will negatively affect your corporation's stock price
+ In order to issue dividends, simply allocate some percentage of your Corporation's profits to dividends. This
+ percentage must be an integer between 0 and 100. (A percentage of 0 means no dividends will be issued.)
- Example: Assume your corporation makes $100m / sec in profit and you allocate 40% of that towards dividends.
- That means your corporation will gain $60m / sec in funds and the remaining $40m / sec will be paid as
- dividends. Since your corporation starts with 1 billion shares, every shareholder will be paid $0.04 per share
- per second before taxes.
+ Example: Assume your corporation makes in profit and you allocate 40% of that
+ towards dividends. That means your corporation will gain in funds and the remaining{" "}
+ will be paid as dividends. Since your corporation starts with 1 billion shares, every
+ shareholder will be paid per share per second before taxes.
+ >;
- const newSharePrice = Math.round(corp.sharePrice * 0.9);
- const maxNewShares = corp.calculateMaxNewShares();
- let newShares = props.shares;
- if (isNaN(newShares)) {
- return Invalid input;
- }
-
- // Round to nearest ten-millionth
- newShares /= 10e6;
- newShares = Math.round(newShares) * 10e6;
-
- if (newShares < 10e6) {
- return Must issue at least 10 million new shares;
- }
-
- if (newShares > maxNewShares) {
- return You cannot issue that many shares;
- }
-
- return (
-
- Issue {formatShares(newShares)} new shares for {formatMoney(newShares * newSharePrice)}?
-
- );
-}
+import * as corpConstants from "../../data/Constants";
+import { issueNewSharesFailureReason } from "../../helpers";
interface IProps {
open: boolean;
onClose: () => void;
+ rerender: () => void;
}
// Create a popup that lets the player issue new shares
@@ -52,56 +23,103 @@ interface IProps {
export function IssueNewSharesModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const [shares, setShares] = useState(NaN);
- const maxNewShares = corp.calculateMaxNewShares();
+ const maxNewShares = corp.calculateMaxNewShares();
const newShares = Math.round((shares || 0) / 10e6) * 10e6;
- const disabled = isNaN(shares) || isNaN(newShares) || newShares < 10e6 || newShares > maxNewShares;
+
+ const ceoOwnership = corp.numShares / (corp.totalShares + (newShares || 0));
+ const newSharePrice = corp.getTargetSharePrice(ceoOwnership);
+ const profit = ((shares || 0) * (corp.sharePrice + newSharePrice)) / 2;
+
+ const privateOwnedRatio = corp.investorShares / corp.totalShares;
+ const maxPrivateShares = Math.round(((newShares / 2) * privateOwnedRatio) / 10e6) * 10e6;
+
+ const disabledText = issueNewSharesFailureReason(corp, shares);
function issueNewShares(): void {
- if (isNaN(shares)) return;
- if (disabled) return;
- const [profit, newShares, privateShares] = IssueNewShares(corp, shares);
-
- props.onClose();
-
- let dialogContents =
- `Issued ${formatShares(newShares)} new shares` + ` and raised ${formatMoney(profit)}.` + (privateShares > 0)
- ? "\n" + formatShares(privateShares) + " of these shares were bought by private investors."
- : "";
- dialogContents += `\n\nStock price decreased to ${formatMoney(corp.sharePrice)}`;
- dialogBoxCreate(dialogContents);
+ if (disabledText) return;
+ try {
+ const [profit, newShares, privateShares] = IssueNewShares(corp, shares);
+ dialogBoxCreate(
+ <>
+
+ Issued {formatShares(newShares)} new shares and raised .
+
+ {privateShares > 0 ? (
+ {formatShares(privateShares)} of these shares were bought by private investors.
+ ) : null}
+
+ {corp.name}'s stock price fell to .
+
+ >,
+ );
+ props.onClose();
+ props.rerender();
+ } catch (err) {
+ dialogBoxCreate(`${err}`);
+ }
}
function onKeyDown(event: React.KeyboardEvent): void {
if (event.key === KEY.ENTER) issueNewShares();
}
+ const nextCooldown = corpConstants.issueNewSharesCooldown * (corp.totalShares / corpConstants.initialShares);
+
return (
-
- You can issue new equity shares (i.e. stocks) in order to raise capital for your corporation.
+
+ You can issue new equity shares (i.e. stocks) in order to raise capital.
+
+
Issuing new shares will cause dilution, lowering stock price and reducing dividends per share.
+
New shares are sold between the current price and the updated price.
+
The money from issuing new shares will be deposited directly into your Corporation's funds.
+
+ Private shareholders have first priority for buying new shares, up to half of their existing stake in the
+ company ({formatPercent(privateOwnedRatio / 2, 1)}).
+
+ If they choose to exercise this option, these newly issued shares become private, restricted shares, which
+ means you cannot buy them back.
+
+
+ You will not be able to issue new shares again for {corp.convertCooldownToString(nextCooldown)}.
+
+
+ You can issue at most {formatShares(maxNewShares)} new shares.
-
- * You can issue at most {formatShares(maxNewShares)} new shares
-
- * New shares are sold at a 10% discount
-
- * You can only issue new shares once every 12 hours
-
- * Issuing new shares causes dilution, resulting in a decrease in stock price and lower dividends per share
-
- * Number of new shares issued must be a multiple of 10 million
-
-
- When you choose to issue new equity, private shareholders have first priority for up to 0.5n% of the new shares,
- where n is the percentage of the company currently owned by private shareholders. If they choose to exercise
- this option, these newly issued shares become private, restricted shares, which means you cannot buy them back.
+ The number of new shares issued must be a multiple of 10 million.
-
-
-
+
+
+
+ {disabledText ? (
+ disabledText
+ ) : (
+ <>
+ Issue {formatShares(newShares)} new shares?
+
+ {maxPrivateShares > 0
+ ? `Private investors may buy up to ${formatShares(
+ maxPrivateShares,
+ )} of these shares and keep them off the market.`
+ : null}
+
+ {corp.name} will receive .
+
+ {corp.name}'s stock price will fall to per share.
+ >
+ )}
+
);
}
diff --git a/src/Corporation/ui/modals/PurchaseMaterialModal.tsx b/src/Corporation/ui/modals/PurchaseMaterialModal.tsx
index f206d1185..a1249125e 100644
--- a/src/Corporation/ui/modals/PurchaseMaterialModal.tsx
+++ b/src/Corporation/ui/modals/PurchaseMaterialModal.tsx
@@ -3,9 +3,10 @@ import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { MaterialInfo } from "../../MaterialInfo";
import { Warehouse } from "../../Warehouse";
import { Material } from "../../Material";
-import { formatMatPurchaseAmount, formatMoney } from "../../../ui/formatNumber";
+import { formatMatPurchaseAmount } from "../../../ui/formatNumber";
import { BulkPurchase, BuyMaterial } from "../../Actions";
import { Modal } from "../../../ui/React/Modal";
+import { Money } from "../../../ui/React/Money";
import { useCorporation, useDivision } from "../Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
@@ -56,7 +57,7 @@ function BulkPurchaseSection(props: IBPProps): React.ReactElement {
return (
<>
- Purchasing {formatMatPurchaseAmount(parsedAmt)} of {props.mat.name} will cost {formatMoney(cost)}
+ Purchasing {formatMatPurchaseAmount(parsedAmt)} of {props.mat.name} will cost
>
);
diff --git a/src/Corporation/ui/modals/SellCorporationModal.tsx b/src/Corporation/ui/modals/SellCorporationModal.tsx
index 5145a42d8..d6d701164 100644
--- a/src/Corporation/ui/modals/SellCorporationModal.tsx
+++ b/src/Corporation/ui/modals/SellCorporationModal.tsx
@@ -57,6 +57,7 @@ export function SellCorporationModal(props: IProps): React.ReactElement {
If you would like to start new one, please enter a name for your corporation below:
+
{Player.bitNodeN === 3 && (