diff --git a/src/Corporation/Actions.ts b/src/Corporation/Actions.ts new file mode 100644 index 000000000..bfa6be10e --- /dev/null +++ b/src/Corporation/Actions.ts @@ -0,0 +1,73 @@ +import { ICorporation } from "./ICorporation"; +import { IIndustry } from "./IIndustry"; +import { IndustryStartingCosts } from "./IndustryData"; +import { Industry } from "./Industry"; +import { CorporationConstants } from "./data/Constants"; +import { OfficeSpace } from "./OfficeSpace"; +import { CorporationUnlockUpgrade } from "./data/CorporationUnlockUpgrades"; +import { CorporationUpgrade } from "./data/CorporationUpgrades"; + +export function NewIndustry(corporation: ICorporation, industry: string, name: string): void { + for (let i = 0; i < corporation.divisions.length; ++i) { + if (corporation.divisions[i].name === name) { + throw new Error("This division name is already in use!"); + return; + } + } + + const cost = IndustryStartingCosts[industry]; + if(cost === undefined) { + throw new Error("Invalid industry: ${industry}"); + } + if (corporation.funds.lt(cost)) { + throw new Error("Not enough money to create a new division in this industry"); + } else if (name === "") { + throw new Error("New division must have a name!"); + } else { + corporation.funds = corporation.funds.minus(cost); + corporation.divisions.push(new Industry({ + corp: corporation, + name: name, + type: industry, + })); + } +} + +export function NewCity(corporation: ICorporation, division: IIndustry, city: string): void { + if (corporation.funds.lt(CorporationConstants.OfficeInitialCost)) { + throw new Error("You don't have enough company funds to open a new office!"); + } else { + corporation.funds = corporation.funds.minus(CorporationConstants.OfficeInitialCost); + division.offices[city] = new OfficeSpace({ + loc: city, + size: CorporationConstants.OfficeInitialSize, + }); + } +} + +export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnlockUpgrade): void { + if (corporation.funds.lt(upgrade[1])) { + throw new Error("Insufficient funds"); + } + corporation.unlock(upgrade); +} + +export function LevelUpgrade(corporation: ICorporation, upgrade: CorporationUpgrade): void { + const baseCost = upgrade[1]; + const priceMult = upgrade[2]; + const level = corporation.upgrades[upgrade[0]]; + const cost = baseCost * Math.pow(priceMult, level); + if (corporation.funds.lt(cost)) { + throw new Error("Insufficient funds"); + } else { + corporation.upgrade(upgrade); + } +} + +export function IssueDividends(corporation: ICorporation, percent: number): void { + if (isNaN(percent) || percent < 0 || percent > CorporationConstants.DividendMaxPercentage) { + throw new Error(`Invalid value. Must be an integer between 0 and ${CorporationConstants.DividendMaxPercentage}`); + } + + corporation.dividendPercentage = percent*100; +} \ No newline at end of file diff --git a/src/Corporation/Corporation.tsx b/src/Corporation/Corporation.tsx index f7f3df41c..139d9cdf5 100644 --- a/src/Corporation/Corporation.tsx +++ b/src/Corporation/Corporation.tsx @@ -132,7 +132,7 @@ export class Corporation { // Process dividends if (this.dividendPercentage > 0 && cycleProfit > 0) { // Validate input again, just to be safe - if (isNaN(this.dividendPercentage) || this.dividendPercentage < 0 || this.dividendPercentage > CorporationConstants.DividendMaxPercentage) { + if (isNaN(this.dividendPercentage) || this.dividendPercentage < 0 || this.dividendPercentage > CorporationConstants.DividendMaxPercentage*100) { console.error(`Invalid Corporation dividend percentage: ${this.dividendPercentage}`); } else { const totalDividends = (this.dividendPercentage / 100) * cycleProfit; diff --git a/src/Corporation/data/Constants.ts b/src/Corporation/data/Constants.ts index 3dce0b1a9..3b0692a9e 100644 --- a/src/Corporation/data/Constants.ts +++ b/src/Corporation/data/Constants.ts @@ -49,7 +49,7 @@ export const CorporationConstants: { ProductProductionCostRatio: 5, //Ratio of material cost of a product to its production cost - DividendMaxPercentage: 50, + DividendMaxPercentage: .5, EmployeeSalaryMultiplier: 3, // Employee stats multiplied by this to determine initial salary CyclesPerEmployeeRaise: 400, // All employees get a raise every X market cycles diff --git a/src/Corporation/ui/CityTabs.tsx b/src/Corporation/ui/CityTabs.tsx index 17cc606f1..fa2b5d3e7 100644 --- a/src/Corporation/ui/CityTabs.tsx +++ b/src/Corporation/ui/CityTabs.tsx @@ -31,7 +31,8 @@ export function CityTabs(props: IProps): React.ReactElement { return <> { - Object.keys(props.onClicks).map((cityName: string) => , + Object.keys(props.onClicks).map((cityName: string) => + , ) } diff --git a/src/Corporation/ui/IndustryOverview.tsx b/src/Corporation/ui/IndustryOverview.tsx index d9bfec9c1..422a8cee5 100644 --- a/src/Corporation/ui/IndustryOverview.tsx +++ b/src/Corporation/ui/IndustryOverview.tsx @@ -11,6 +11,7 @@ import { createProgressBarText } from "../../../utils/helpers/createProgressB import { MakeProductPopup } from "./MakeProductPopup"; import { ResearchPopup } from "./ResearchPopup"; import { createPopup } from "../../ui/React/createPopup"; +import { Money } from "../../ui/React/Money"; import { ICorporation } from "../ICorporation"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { CorporationRouting } from "./Routing"; @@ -114,10 +115,6 @@ export function IndustryOverview(props: IProps): React.ReactElement { const vechain = (corp.unlockUpgrades[4] === 1); const profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber(); - const genInfo = `Industry: ${division.type} (Corp Funds: ${numeralWrapper.formatMoney(corp.funds.toNumber())})`; - const awareness = `Awareness: ${numeralWrapper.format(division.awareness, "0.000")}`; - const popularity = `Popularity: ${numeralWrapper.format(division.popularity, "0.000")}`; - let advertisingInfo = false; const advertisingFactors = division.getAdvertisingFactors(); const awarenessFac = advertisingFactors[1]; @@ -126,10 +123,6 @@ export function IndustryOverview(props: IProps): React.ReactElement { const totalAdvertisingFac = advertisingFactors[0]; if (vechain) { advertisingInfo = true; } - const revenue = `Revenue: ${numeralWrapper.formatMoney(division.lastCycleRevenue.toNumber())} / s`; - const expenses = `Expenses: ${numeralWrapper.formatMoney(division.lastCycleExpenses.toNumber())} /s`; - const profitStr = `Profit: ${numeralWrapper.formatMoney(profit)} / s`; - function productionMultHelpTipOnClick(): void { if(division === null) return; // Wrapper for createProgressBarText() @@ -171,10 +164,10 @@ export function IndustryOverview(props: IProps): React.ReactElement { return (
- {genInfo} + Industry: {division.type} (Corp Funds: {Money(corp.funds.toNumber())})

- {awareness}
- {popularity}
+ Awareness: {numeralWrapper.format(division.awareness, "0.000")}
+ Popularity: {numeralWrapper.format(division.popularity, "0.000")}
{ (advertisingInfo !== false) &&

Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")} @@ -191,9 +184,9 @@ export function IndustryOverview(props: IProps): React.ReactElement { } {advertisingInfo}

- {revenue}
- {expenses}
- {profitStr} + Revenue: {Money(division.lastCycleRevenue.toNumber())} / s
+ Expenses: {Money(division.lastCycleExpenses.toNumber())} /s
+ Profit: {Money(profit)} / s

Production Multiplier: {numeralWrapper.format(division.prodMult, "0.00")} @@ -264,8 +257,9 @@ export function IndustryOverview(props: IProps): React.ReactElement { } upgrades.push(renderUpgrade({ + key: index, onClick: onClick, - text: `${upgrade[4]} - ${numeralWrapper.formatMoney(cost)}`, + text: <>{upgrade[4]} - {Money(cost)}, tooltip: upgrade[5], })); } @@ -274,14 +268,15 @@ export function IndustryOverview(props: IProps): React.ReactElement { } interface IRenderUpgradeProps { + key: string; onClick: () => void; - text: string; + text: JSX.Element; tooltip: string; } function renderUpgrade(props: IRenderUpgradeProps): React.ReactElement { return ( -

+
{props.text} { props.tooltip != null && diff --git a/src/Corporation/ui/IssueDividendsPopup.tsx b/src/Corporation/ui/IssueDividendsPopup.tsx index 2ca74f964..eb022d58e 100644 --- a/src/Corporation/ui/IssueDividendsPopup.tsx +++ b/src/Corporation/ui/IssueDividendsPopup.tsx @@ -3,6 +3,7 @@ import { removePopup } from "../../ui/React/createPopup"; import { dialogBoxCreate } from "../../../utils/DialogBox"; import { CorporationConstants } from "../data/Constants"; import { ICorporation } from "../ICorporation"; +import { IssueDividends } from "../Actions"; interface IProps { popupId: string; @@ -16,13 +17,12 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement { function issueDividends(): void { if(percent === null) return; - if (isNaN(percent) || percent < 0 || percent > CorporationConstants.DividendMaxPercentage) { - dialogBoxCreate(`Invalid value. Must be an integer between 0 and ${CorporationConstants.DividendMaxPercentage}`); - return; + try { + IssueDividends(props.corp, percent/100); + } catch(err) { + dialogBoxCreate(err); } - props.corp.dividendPercentage = percent; - removePopup(props.popupId); } @@ -37,7 +37,7 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement { return (<>

-"Dividends are a distribution of a portion of the corporation's +Dividends are a distribution of a portion of the corporation's profits to the shareholders. This includes yourself, as well.

In order to issue dividends, simply allocate some percentage of your corporation's profits to dividends. This percentage must be an diff --git a/src/Corporation/ui/LevelableUpgrade.tsx b/src/Corporation/ui/LevelableUpgrade.tsx index b3a8ca93d..a1a9e5170 100644 --- a/src/Corporation/ui/LevelableUpgrade.tsx +++ b/src/Corporation/ui/LevelableUpgrade.tsx @@ -6,32 +6,32 @@ import { dialogBoxCreate } from "../../../utils/DialogBox"; import { ICorporation } from "../ICorporation"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { CorporationUpgrade } from "../data/CorporationUpgrades"; +import { LevelUpgrade } from "../Actions"; +import { Money } from "../../ui/React/Money"; interface IProps { - upgradeData: CorporationUpgrade; - upgradeLevel: number; + upgrade: CorporationUpgrade; corp: ICorporation; player: IPlayer; } export function LevelableUpgrade(props: IProps): React.ReactElement { - const data = props.upgradeData; - const level = props.upgradeLevel; + const data = props.upgrade; + const level = props.corp.upgrades[data[0]]; const baseCost = data[1]; const priceMult = data[2]; const cost = baseCost * Math.pow(priceMult, level); - const text = `${data[4]} - ${numeralWrapper.formatMoney(cost)}` + const text = <>{data[4]} - {Money(cost)} const tooltip = data[5]; function onClick(): void { - const corp = props.corp; - if (corp.funds.lt(cost)) { - dialogBoxCreate("Insufficient funds"); - } else { - corp.upgrade(data); - corp.rerender(props.player); + try { + LevelUpgrade(props.corp, props.upgrade); + } catch(err) { + dialogBoxCreate(err); } + props.corp.rerender(props.player); } return ( diff --git a/src/Corporation/ui/NewIndustryPopup.tsx b/src/Corporation/ui/NewIndustryPopup.tsx index f619e5423..a841aa295 100644 --- a/src/Corporation/ui/NewIndustryPopup.tsx +++ b/src/Corporation/ui/NewIndustryPopup.tsx @@ -9,6 +9,7 @@ import { Industry } from "../Industry"; import { ICorporation } from "../ICorporation"; import { IIndustry } from "../IIndustry"; import { CorporationRouting } from "./Routing"; +import { NewIndustry } from "../Actions"; interface IProps { corp: ICorporation; @@ -24,33 +25,17 @@ export function NewIndustryPopup(props: IProps): React.ReactElement { const [name, setName] = useState(''); function newIndustry(): void { - const ind = industry; - const newDivisionName = name; - - for (let i = 0; i < props.corp.divisions.length; ++i) { - if (props.corp.divisions[i].name === newDivisionName) { - dialogBoxCreate("This name is already in use!"); - return; - } + try { + NewIndustry(props.corp, industry, name); + } catch(err) { + dialogBoxCreate(err); + return; } - if (props.corp.funds.lt(IndustryStartingCosts[ind])) { - dialogBoxCreate("Not enough money to create a new division in this industry"); - } else if (newDivisionName === "") { - dialogBoxCreate("New division must have a name!"); - } else { - props.corp.funds = props.corp.funds.minus(IndustryStartingCosts[ind]); - const newInd = new Industry({ - corp: props.corp, - name: newDivisionName, - type: ind, - }); - props.corp.divisions.push(newInd); - // Set routing to the new division so that the UI automatically switches to it - props.routing.routeTo(newDivisionName); + // Set routing to the new division so that the UI automatically switches to it + props.routing.routeTo(name); - removePopup(props.popupId); - } + removePopup(props.popupId); } function onNameChange(event: React.ChangeEvent): void { diff --git a/src/Corporation/ui/Overview.tsx b/src/Corporation/ui/Overview.tsx index 39e9ad6b6..15f19a746 100644 --- a/src/Corporation/ui/Overview.tsx +++ b/src/Corporation/ui/Overview.tsx @@ -11,7 +11,9 @@ import { FindInvestorsPopup } from "./FindInvestorsPopup"; import { GoPublicPopup } from "./GoPublicPopup"; import { CorporationConstants } from "../data/Constants"; -import { CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades"; +import { + CorporationUnlockUpgrade, + CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades"; import { CorporationUpgrade, CorporationUpgrades } from "../data/CorporationUpgrades"; @@ -20,6 +22,7 @@ import { CONSTANTS } from "../../Constants"; import { numeralWrapper } from "../../ui/numeralFormat"; import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; import { createPopup } from "../../ui/React/createPopup"; +import { Money } from "../../ui/React/Money"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { ICorporation } from "../ICorporation"; @@ -30,7 +33,6 @@ interface IProps { interface GeneralBtns { bribeFactions: React.ReactElement; - getStarterGuide: React.ReactElement; } export function Overview(props: IProps): React.ReactElement { @@ -38,10 +40,32 @@ export function Overview(props: IProps): React.ReactElement { interface ICreateButtonProps { text: string; class?: string; + className?: string; display?: string; tooltip?: string; onClick?: (event: React.MouseEvent) => void; } + + function Button(props: ICreateButtonProps): React.ReactElement { + let className = props.className ? props.className : "std-button"; + const hasTooltip = props.tooltip != null; + if(hasTooltip) className += " tooltip"; + return ( + + {props.text} + { + hasTooltip && + + {props.tooltip} + + } + + ); + } + function createButton(props: ICreateButtonProps): React.ReactElement { let className = props.class ? props.class : "std-button"; const displayStyle = props.display ? props.display : "block"; @@ -63,176 +87,143 @@ export function Overview(props: IProps): React.ReactElement { ) } - // Returns a string with general information about Corporation - function getOverviewText(): string { - // Formatted text for profit - const profit = props.corp.revenue.minus(props.corp.expenses).toNumber(), - profitStr = profit >= 0 ? numeralWrapper.formatMoney(profit) : "-" + numeralWrapper.format(-1 * profit, "$0.000a"); - - // Formatted text for dividend information, if applicable - let dividendStr = ""; - if (props.corp.dividendPercentage > 0 && profit > 0) { - const totalDividends = (props.corp.dividendPercentage / 100) * profit; - const retainedEarnings = profit - totalDividends; - const dividendsPerShare = totalDividends / props.corp.totalShares; - const playerEarnings = props.corp.numShares * dividendsPerShare; - - dividendStr = `Retained Profits (after dividends): ${numeralWrapper.format(retainedEarnings, "$0.000a")} / s

` + - `Dividend Percentage: ${numeralWrapper.format(props.corp.dividendPercentage / 100, "0%")}
` + - `Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s
` + - `Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s
` + - `Dividend Tax Rate: ${props.corp.dividendTaxPercentage}%
` + - `Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (props.corp.dividendTaxPercentage / 100)), "$0.000a")} / s

`; - } - - let txt = "Total Funds: " + numeralWrapper.format(props.corp.funds.toNumber(), '$0.000a') + "
" + - "Total Revenue: " + numeralWrapper.format(props.corp.revenue.toNumber(), "$0.000a") + " / s
" + - "Total Expenses: " + numeralWrapper.format(props.corp.expenses.toNumber(), "$0.000a") + " / s
" + - "Total Profits: " + profitStr + " / s
" + - dividendStr + - "Publicly Traded: " + (props.corp.public ? "Yes" : "No") + "
" + - "Owned Stock Shares: " + numeralWrapper.format(props.corp.numShares, '0.000a') + "
" + - "Stock Price: " + (props.corp.public ? numeralWrapper.formatMoney(props.corp.sharePrice) : "N/A") + "
" + - "

Total Stock Shares: " + numeralWrapper.format(props.corp.totalShares, "0.000a") + - "" + - `Outstanding Shares: ${numeralWrapper.format(props.corp.issuedShares, "0.000a")}
` + - `Private Shares: ${numeralWrapper.format(props.corp.totalShares - props.corp.issuedShares - props.corp.numShares, "0.000a")}` + - "



"; - - const storedTime = props.corp.storedCycles * CONSTANTS.MilliPerCycle; - if (storedTime > 15000) { - txt += `Bonus time: ${convertTimeMsToTimeElapsedString(storedTime)}

`; - } - - const prodMult = props.corp.getProductionMultiplier(), - storageMult = props.corp.getStorageMultiplier(), - advMult = props.corp.getAdvertisingMultiplier(), - empCreMult = props.corp.getEmployeeCreMultiplier(), - empChaMult = props.corp.getEmployeeChaMultiplier(), - empIntMult = props.corp.getEmployeeIntMultiplier(), - empEffMult = props.corp.getEmployeeEffMultiplier(), - salesMult = props.corp.getSalesMultiplier(), - sciResMult = props.corp.getScientificResearchMultiplier(); - if (prodMult > 1) {txt += "Production Multiplier: " + numeralWrapper.format(prodMult, "0.000") + "
";} - if (storageMult > 1) {txt += "Storage Multiplier: " + numeralWrapper.format(storageMult, "0.000") + "
";} - if (advMult > 1) {txt += "Advertising Multiplier: " + numeralWrapper.format(advMult, "0.000") + "
";} - if (empCreMult > 1) {txt += "Empl. Creativity Multiplier: " + numeralWrapper.format(empCreMult, "0.000") + "
";} - if (empChaMult > 1) {txt += "Empl. Charisma Multiplier: " + numeralWrapper.format(empChaMult, "0.000") + "
";} - if (empIntMult > 1) {txt += "Empl. Intelligence Multiplier: " + numeralWrapper.format(empIntMult, "0.000") + "
";} - if (empEffMult > 1) {txt += "Empl. Efficiency Multiplier: " + numeralWrapper.format(empEffMult, "0.000") + "
";} - if (salesMult > 1) {txt += "Sales Multiplier: " + numeralWrapper.format(salesMult, "0.000") + "
";} - if (sciResMult > 1) {txt += "Scientific Research Multiplier: " + numeralWrapper.format(sciResMult, "0.000") + "
";} - - return txt; + function openBribeFactionPopup(): void { + const popupId = "corp-bribe-popup"; + createPopup(popupId, BribeFactionPopup, { + player: props.player, + popupId: popupId, + corp: props.corp, + }); } - // Render the buttons that lie below the overview text. - // These are mainly for things such as managing finances/stock - function renderButtons(): React.ReactElement { - // Create a "Getting Started Guide" button that lets player view the - // handbook and adds it to the players home computer - const getStarterGuideBtn = createButton({ - class: "a-link-button", - display: "inline-block", - onClick: () => props.corp.getStarterGuide(props.player), - text: "Getting Started Guide", - tooltip: "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.", - }); + const profit: number = props.corp.revenue.minus(props.corp.expenses).toNumber(); - function openBribeFactionPopup(): void { - const popupId = "corp-bribe-popup"; - createPopup(popupId, BribeFactionPopup, { - player: props.player, - popupId: popupId, - corp: props.corp, - }); - } + function DividendsStats() { + if(props.corp.dividendPercentage <= 0 || profit <= 0) return (<>); + const totalDividends = (props.corp.dividendPercentage / 100) * profit; + const retainedEarnings = profit - totalDividends; + const dividendsPerShare = totalDividends / props.corp.totalShares; + const playerEarnings = props.corp.numShares * dividendsPerShare; + return (<> + Retained Profits (after dividends): {Money(retainedEarnings)} / s

+ Dividend Percentage: {numeralWrapper.format(props.corp.dividendPercentage / 100, "0%")}
+ Dividends per share: {Money(dividendsPerShare)} / s
+ Your earnings as a shareholder (Pre-Tax): {Money(playerEarnings)} / s
+ Dividend Tax Rate: {props.corp.dividendTaxPercentage}%
+ Your earnings as a shareholder (Post-Tax): {Money(playerEarnings * (1 - (props.corp.dividendTaxPercentage / 100)))} / s

+ ); + } - // Create a "Bribe Factions" button if your Corporation is powerful enough. - // This occurs regardless of whether you're public or private + function Mult(props: {name: string, mult: number}): React.ReactElement { + if(props.mult <= 1) return (<>); + return (

{props.name}{numeralWrapper.format(props.mult, "0.000")}

); + } + + // Returns a string with general information about Corporation + function BonusTime(): React.ReactElement { + const storedTime = props.corp.storedCycles * CONSTANTS.MilliPerCycle; + if (storedTime <= 15000) return (<>); + return (

Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}

); + } + + function BribeButton(): React.ReactElement { const canBribe = (props.corp.determineValuation() >= CorporationConstants.BribeThreshold) || true; const bribeFactionsClass = (canBribe ? "a-link-button" : "a-link-button-inactive"); - const bribeFactionsBtn = createButton({ - class: bribeFactionsClass, - display: "inline-block", - onClick: openBribeFactionPopup, - text: "Bribe Factions", - tooltip: (canBribe + return
+ ) } + function openSellSharesPopup(): void { + const popupId = "cmpy-mgmt-sell-shares-popup"; + createPopup(popupId, SellSharesPopup, { + corp: props.corp, + player: props.player, + popupId: popupId, + }); + } + + function openBuybackSharesPopup(): void { + const popupId = "corp-buyback-shares-popup"; + createPopup(popupId, BuybackSharesPopup, { + player: props.player, + popupId: popupId, + corp: props.corp, + }); + } + + function openIssueNewSharesPopup(): void { + const popupId = "cmpy-mgmt-issue-new-shares-popup"; + createPopup(popupId, IssueNewSharesPopup, { + popupId: popupId, + corp: props.corp, + }); + } + + function openIssueDividendsPopup(): void { + const popupId = "cmpy-mgmt-issue-dividends-popup"; + createPopup(popupId, IssueDividendsPopup, { + popupId: popupId, + corp: props.corp, + }); + } + // Render the buttons for when your Corporation has gone public - function renderPublicButtons(generalBtns: GeneralBtns): React.ReactElement { + function PublicButtons(): React.ReactElement { const corp = props.corp; const sellSharesOnCd = (corp.shareSaleCooldown > 0); @@ -242,153 +233,135 @@ export function Overview(props: IProps): React.ReactElement { : "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 sellSharesBtn = createButton({ - class: sellSharesClass, - display: "inline-block", - onClick: function(event: React.MouseEvent) { - if(!event.isTrusted) return; - const popupId = "cmpy-mgmt-sell-shares-popup"; - createPopup(popupId, SellSharesPopup, { - corp: props.corp, - player: props.player, - popupId: popupId, - }); - }, - text: "Sell Shares", - tooltip: sellSharesTooltip, - }); - - function openBuybackSharesPopup(): void { - const popupId = "corp-buyback-shares-popup"; - createPopup(popupId, BuybackSharesPopup, { - player: props.player, - popupId: popupId, - corp: props.corp, - }); - } - - const buybackSharesBtn = createButton({ - class: "std-button", - display: "inline-block", - onClick: openBuybackSharesPopup, - text: "Buyback shares", - tooltip: "Buy back shares you that previously issued or sold at market price.", - }); - - function openIssueNewSharesPopup(): void { - const popupId = "cmpy-mgmt-issue-new-shares-popup"; - createPopup(popupId, IssueNewSharesPopup, { - popupId: popupId, - corp: props.corp, - }); - } const issueNewSharesOnCd = (corp.issueNewSharesCooldown > 0); const issueNewSharesClass = issueNewSharesOnCd ? "a-link-button-inactive" : "std-button"; const issueNewSharesTooltip = issueNewSharesOnCd ? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown) : "Issue new equity shares to raise capital."; - const issueNewSharesBtn = createButton({ - class: issueNewSharesClass, - display: "inline-block", - onClick: openIssueNewSharesPopup, - text: "Issue New Shares", - tooltip: issueNewSharesTooltip, - }); - - function openIssueDividendsPopup(): void { - const popupId = "cmpy-mgmt-issue-dividends-popup"; - createPopup(popupId, IssueDividendsPopup, { - popupId: popupId, - corp: props.corp, - }); - } - - const issueDividendsBtn = createButton({ - class: "std-button", - display: "inline-block", - onClick: openIssueDividendsPopup, - text: "Issue Dividends", - tooltip: "Manage the dividends that are paid out to shareholders (including yourself)", - }); return ( -
- {generalBtns.getStarterGuide} - {sellSharesBtn} - {buybackSharesBtn} + <> +
+ ) } // Render the UI for Corporation upgrades - function renderUpgrades(): React.ReactElement { + function Upgrades(): React.ReactElement { // Don't show upgrades if (props.corp.divisions.length <= 0) { return (<>); } - // Create an array of all Unlocks - const unlockUpgrades: React.ReactElement[] = []; - Object.values(CorporationUnlockUpgrades).forEach((unlockData) => { - if (props.corp.unlockUpgrades[unlockData[0]] === 0) { - unlockUpgrades.push(); - } - }); - - interface UpgradeData { - upgradeData: CorporationUpgrade; - upgradeLevel: number; - } - - // Create an array of properties of all unlocks - const levelableUpgradeProps: UpgradeData[] = []; - for (let i = 0; i < props.corp.upgrades.length; ++i) { - const upgradeData = CorporationUpgrades[i]; - const level = props.corp.upgrades[i]; - - levelableUpgradeProps.push({ - upgradeData: upgradeData, - upgradeLevel: level, - }); - } - - return (

Unlocks

- {unlockUpgrades} + { + Object.values(CorporationUnlockUpgrades) + .filter((upgrade: CorporationUnlockUpgrade) => props.corp.unlockUpgrades[upgrade[0]] === 0) + .map((upgrade: CorporationUnlockUpgrade) => + ) + }

Upgrades

{ - levelableUpgradeProps.map((data: UpgradeData) => CorporationUpgrades[i]) + .map((upgrade: CorporationUpgrade) => , ) }
- ) + ); } return (
-

- {renderButtons()} +

+ Total Funds: {Money(props.corp.funds.toNumber())}
+ Total Revenue: {Money(props.corp.revenue.toNumber())} / s
+ Total Expenses: {Money(props.corp.expenses.toNumber())} / s
+ Total Profits: {Money(profit)} / s
+ + Publicly Traded: {(props.corp.public ? "Yes" : "No")}
+ Owned Stock Shares: {numeralWrapper.format(props.corp.numShares, '0.000a')}
+ Stock Price: {(props.corp.public ? Money(props.corp.sharePrice) : "N/A")}
+

+

+ Total Stock Shares: {numeralWrapper.format(props.corp.totalShares, "0.000a")} + + Outstanding Shares: {numeralWrapper.format(props.corp.issuedShares, "0.000a")}
+ Private Shares: {numeralWrapper.format(props.corp.totalShares - props.corp.issuedShares - props.corp.numShares, "0.000a")} +
+

+

+ + + + + + + + +
- {renderUpgrades()} + +
+
+
+
) } diff --git a/src/Corporation/ui/UnlockUpgrade.tsx b/src/Corporation/ui/UnlockUpgrade.tsx index 5749bb180..785893d83 100644 --- a/src/Corporation/ui/UnlockUpgrade.tsx +++ b/src/Corporation/ui/UnlockUpgrade.tsx @@ -6,6 +6,8 @@ import { dialogBoxCreate } from "../../../utils/DialogBox"; import { CorporationUnlockUpgrade } from "../data/CorporationUnlockUpgrades"; import { ICorporation } from "../ICorporation"; import { IPlayer } from "../../PersonObjects/IPlayer"; +import { UnlockUpgrade as UU } from "../Actions"; +import { Money } from "../../ui/React/Money"; interface IProps { upgradeData: CorporationUnlockUpgrade; @@ -15,16 +17,15 @@ interface IProps { export function UnlockUpgrade(props: IProps): React.ReactElement { const data = props.upgradeData; - const text = `${data[2]} - ${numeralWrapper.formatMoney(data[1])}`; + const text = <>{data[2]} - {Money(data[1])}; const tooltip = data[3]; function onClick(): void { - const corp = props.corp; - if (corp.funds.lt(data[1])) { - dialogBoxCreate("Insufficient funds"); - } else { - corp.unlock(data); - corp.rerender(props.player); + try { + UU(props.corp, props.upgradeData); + } catch(err) { + dialogBoxCreate(err); } + props.corp.rerender(props.player); } return ( diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index 05b56914b..cdba2f86d 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -20,6 +20,14 @@ import { CompanyPosition } from "./Company/CompanyPosition"; import { CompanyPositions } from "./Company/CompanyPositions"; import { CONSTANTS } from "./Constants"; import { DarkWebItems } from "./DarkWeb/DarkWebItems"; +import { + NewIndustry, + NewCity, + UnlockUpgrade, + LevelUpgrade, + IssueDividends } from "./Corporation/Actions"; +import { CorporationUnlockUpgrades } from "./Corporation/data/CorporationUnlockUpgrades"; +import { CorporationUpgrades } from "./Corporation/data/CorporationUpgrades"; import { calculateHackingChance, calculateHackingExpGain, @@ -4097,6 +4105,32 @@ function NetscriptFunctions(workerScript) { }, }, // End Bladeburner + corporation: { + expandIndustry: function(industryName, divisionName) { + NewIndustry(Player.corporation, industryName, divisionName); + }, + expandCity: function(divisionName, cityName) { + const division = Player.corporation.divisions.find(div => div.name === divisionName); + if(division === undefined) throw new Error("No division named '${divisionName}'"); + NewCity(Player.corporation, division, cityName); + }, + unlockUpgrade: function(upgradeName) { + const upgrade = Object.values(CorporationUnlockUpgrades). + find(upgrade => upgrade[2] === upgradeName); + if(upgrade === undefined) throw new Error("No upgrade named '${upgradeName}'") + UnlockUpgrade(Player.corporation, upgrade); + }, + levelUpgrade: function(upgradeName) { + const upgrade = Object.values(CorporationUpgrades). + find(upgrade => upgrade[4] === upgradeName); + if(upgrade === undefined) throw new Error("No upgrade named '${upgradeName}'") + LevelUpgrade(Player.corporation, upgrade); + }, + issueDividends: function(percent) { + IssueDividends(Player.corporation, percent); + }, + }, // End Corporation API + // Coding Contract API codingcontract: { attempt: function(answer, fn, ip=workerScript.serverIp, { returnReward } = {}) { diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index 76894ed75..bed14af84 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -19,7 +19,8 @@ import { LocationName } from "../Locations/data/LocationNames"; import { Server } from "../Server/Server"; import { IPlayerOwnedSourceFile } from "../SourceFile/PlayerOwnedSourceFile"; import { MoneySourceTracker } from "../utils/MoneySourceTracker"; -import { Exploit } from "../Exploits/Exploit"; +import { Exploit } from "../Exploits/Exploit"; +import { ICorporation } from "../Corporation/ICorporation"; export interface IPlayer { // Class members @@ -28,7 +29,7 @@ export interface IPlayer { bitNodeN: number; city: CityName; companyName: string; - corporation: any; + corporation: ICorporation; currentServer: string; factions: string[]; factionInvitations: string[];