more conversion

This commit is contained in:
Olivier Gagnon 2021-09-01 22:16:48 -04:00
parent 65158e4db7
commit d3aeda8ad5
13 changed files with 404 additions and 343 deletions

@ -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;
}

@ -132,7 +132,7 @@ export class Corporation {
// Process dividends // Process dividends
if (this.dividendPercentage > 0 && cycleProfit > 0) { if (this.dividendPercentage > 0 && cycleProfit > 0) {
// Validate input again, just to be safe // 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}`); console.error(`Invalid Corporation dividend percentage: ${this.dividendPercentage}`);
} else { } else {
const totalDividends = (this.dividendPercentage / 100) * cycleProfit; const totalDividends = (this.dividendPercentage / 100) * cycleProfit;

@ -49,7 +49,7 @@ export const CorporationConstants: {
ProductProductionCostRatio: 5, //Ratio of material cost of a product to its production cost 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 EmployeeSalaryMultiplier: 3, // Employee stats multiplied by this to determine initial salary
CyclesPerEmployeeRaise: 400, // All employees get a raise every X market cycles CyclesPerEmployeeRaise: 400, // All employees get a raise every X market cycles

@ -31,7 +31,8 @@ export function CityTabs(props: IProps): React.ReactElement {
return <> return <>
{ {
Object.keys(props.onClicks).map((cityName: string) => <CityTab current={props.city === cityName} key={cityName} name={cityName} onClick={props.onClicks[cityName]} />, Object.keys(props.onClicks).map((cityName: string) =>
<CityTab current={props.city === cityName} key={cityName} name={cityName} onClick={props.onClicks[cityName]} />,
) )
} }
<CityTab <CityTab

@ -6,6 +6,7 @@ import { removePopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { NewCity } from "../Actions";
interface IProps { interface IProps {
popupId: string; popupId: string;
@ -19,19 +20,16 @@ export function ExpandNewCityPopup(props: IProps): React.ReactElement {
function expand(): void { function expand(): void {
if(dropdown.current === null) return; if(dropdown.current === null) return;
const city = dropdown.current.value; try {
if (props.corp.funds.lt(CorporationConstants.OfficeInitialCost)) { NewCity(props.corp, props.division, dropdown.current.value);
dialogBoxCreate("You don't have enough company funds to open a new office!"); } catch(err) {
} else { dialogBoxCreate(err);
props.corp.funds = props.corp.funds.minus(CorporationConstants.OfficeInitialCost); return;
dialogBoxCreate(`Opened a new office in ${city}!`);
props.division.offices[city] = new OfficeSpace({
loc: city,
size: CorporationConstants.OfficeInitialSize,
});
} }
props.cityStateSetter(city); dialogBoxCreate(`Opened a new office in ${dropdown.current.value}!`);
props.cityStateSetter(dropdown.current.value);
removePopup(props.popupId); removePopup(props.popupId);
} }
return (<> return (<>

@ -11,6 +11,7 @@ import { createProgressBarText } from "../../../utils/helpers/createProgressB
import { MakeProductPopup } from "./MakeProductPopup"; import { MakeProductPopup } from "./MakeProductPopup";
import { ResearchPopup } from "./ResearchPopup"; import { ResearchPopup } from "./ResearchPopup";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { CorporationRouting } from "./Routing"; import { CorporationRouting } from "./Routing";
@ -114,10 +115,6 @@ export function IndustryOverview(props: IProps): React.ReactElement {
const vechain = (corp.unlockUpgrades[4] === 1); const vechain = (corp.unlockUpgrades[4] === 1);
const profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber(); 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; let advertisingInfo = false;
const advertisingFactors = division.getAdvertisingFactors(); const advertisingFactors = division.getAdvertisingFactors();
const awarenessFac = advertisingFactors[1]; const awarenessFac = advertisingFactors[1];
@ -126,10 +123,6 @@ export function IndustryOverview(props: IProps): React.ReactElement {
const totalAdvertisingFac = advertisingFactors[0]; const totalAdvertisingFac = advertisingFactors[0];
if (vechain) { advertisingInfo = true; } 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 { function productionMultHelpTipOnClick(): void {
if(division === null) return; if(division === null) return;
// Wrapper for createProgressBarText() // Wrapper for createProgressBarText()
@ -171,10 +164,10 @@ export function IndustryOverview(props: IProps): React.ReactElement {
return ( return (
<div> <div>
{genInfo} Industry: {division.type} (Corp Funds: {Money(corp.funds.toNumber())})
<br /> <br /> <br /> <br />
{awareness} <br /> Awareness: {numeralWrapper.format(division.awareness, "0.000")} <br />
{popularity} <br /> Popularity: {numeralWrapper.format(division.popularity, "0.000")} <br />
{ {
(advertisingInfo !== false) && (advertisingInfo !== false) &&
<p className={"tooltip"}>Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")} <p className={"tooltip"}>Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")}
@ -191,9 +184,9 @@ export function IndustryOverview(props: IProps): React.ReactElement {
} }
{advertisingInfo} {advertisingInfo}
<br /><br /> <br /><br />
{revenue} <br /> Revenue: {Money(division.lastCycleRevenue.toNumber())} / s <br />
{expenses} <br /> Expenses: {Money(division.lastCycleExpenses.toNumber())} /s <br />
{profitStr} Profit: {Money(profit)} / s
<br /> <br /> <br /> <br />
<p className={"tooltip"}> <p className={"tooltip"}>
Production Multiplier: {numeralWrapper.format(division.prodMult, "0.00")} Production Multiplier: {numeralWrapper.format(division.prodMult, "0.00")}
@ -264,8 +257,9 @@ export function IndustryOverview(props: IProps): React.ReactElement {
} }
upgrades.push(renderUpgrade({ upgrades.push(renderUpgrade({
key: index,
onClick: onClick, onClick: onClick,
text: `${upgrade[4]} - ${numeralWrapper.formatMoney(cost)}`, text: <>{upgrade[4]} - {Money(cost)}</>,
tooltip: upgrade[5], tooltip: upgrade[5],
})); }));
} }
@ -274,14 +268,15 @@ export function IndustryOverview(props: IProps): React.ReactElement {
} }
interface IRenderUpgradeProps { interface IRenderUpgradeProps {
key: string;
onClick: () => void; onClick: () => void;
text: string; text: JSX.Element;
tooltip: string; tooltip: string;
} }
function renderUpgrade(props: IRenderUpgradeProps): React.ReactElement { function renderUpgrade(props: IRenderUpgradeProps): React.ReactElement {
return ( return (
<div className={"cmpy-mgmt-upgrade-div tooltip"} onClick={props.onClick} key={props.text}> <div className={"cmpy-mgmt-upgrade-div tooltip"} onClick={props.onClick} key={props.key}>
{props.text} {props.text}
{ {
props.tooltip != null && props.tooltip != null &&

@ -3,6 +3,7 @@ import { removePopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../../utils/DialogBox"; import { dialogBoxCreate } from "../../../utils/DialogBox";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IssueDividends } from "../Actions";
interface IProps { interface IProps {
popupId: string; popupId: string;
@ -16,13 +17,12 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement {
function issueDividends(): void { function issueDividends(): void {
if(percent === null) return; if(percent === null) return;
if (isNaN(percent) || percent < 0 || percent > CorporationConstants.DividendMaxPercentage) { try {
dialogBoxCreate(`Invalid value. Must be an integer between 0 and ${CorporationConstants.DividendMaxPercentage}`); IssueDividends(props.corp, percent/100);
return; } catch(err) {
dialogBoxCreate(err);
} }
props.corp.dividendPercentage = percent;
removePopup(props.popupId); removePopup(props.popupId);
} }
@ -37,7 +37,7 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement {
return (<> return (<>
<p> <p>
"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.<br /><br /> profits to the shareholders. This includes yourself, as well.<br /><br />
In order to issue dividends, simply allocate some percentage In order to issue dividends, simply allocate some percentage
of your corporation's profits to dividends. This percentage must be an of your corporation's profits to dividends. This percentage must be an

@ -6,32 +6,32 @@ import { dialogBoxCreate } from "../../../utils/DialogBox";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { CorporationUpgrade } from "../data/CorporationUpgrades"; import { CorporationUpgrade } from "../data/CorporationUpgrades";
import { LevelUpgrade } from "../Actions";
import { Money } from "../../ui/React/Money";
interface IProps { interface IProps {
upgradeData: CorporationUpgrade; upgrade: CorporationUpgrade;
upgradeLevel: number;
corp: ICorporation; corp: ICorporation;
player: IPlayer; player: IPlayer;
} }
export function LevelableUpgrade(props: IProps): React.ReactElement { export function LevelableUpgrade(props: IProps): React.ReactElement {
const data = props.upgradeData; const data = props.upgrade;
const level = props.upgradeLevel; const level = props.corp.upgrades[data[0]];
const baseCost = data[1]; const baseCost = data[1];
const priceMult = data[2]; const priceMult = data[2];
const cost = baseCost * Math.pow(priceMult, level); const cost = baseCost * Math.pow(priceMult, level);
const text = `${data[4]} - ${numeralWrapper.formatMoney(cost)}` const text = <>{data[4]} - {Money(cost)}</>
const tooltip = data[5]; const tooltip = data[5];
function onClick(): void { function onClick(): void {
const corp = props.corp; try {
if (corp.funds.lt(cost)) { LevelUpgrade(props.corp, props.upgrade);
dialogBoxCreate("Insufficient funds"); } catch(err) {
} else { dialogBoxCreate(err);
corp.upgrade(data);
corp.rerender(props.player);
} }
props.corp.rerender(props.player);
} }
return ( return (

@ -9,6 +9,7 @@ import { Industry } from "../Industry";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { CorporationRouting } from "./Routing"; import { CorporationRouting } from "./Routing";
import { NewIndustry } from "../Actions";
interface IProps { interface IProps {
corp: ICorporation; corp: ICorporation;
@ -24,34 +25,18 @@ export function NewIndustryPopup(props: IProps): React.ReactElement {
const [name, setName] = useState(''); const [name, setName] = useState('');
function newIndustry(): void { function newIndustry(): void {
const ind = industry; try {
const newDivisionName = name; NewIndustry(props.corp, industry, name);
} catch(err) {
for (let i = 0; i < props.corp.divisions.length; ++i) { dialogBoxCreate(err);
if (props.corp.divisions[i].name === newDivisionName) {
dialogBoxCreate("This name is already in use!");
return; 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 // Set routing to the new division so that the UI automatically switches to it
props.routing.routeTo(newDivisionName); props.routing.routeTo(name);
removePopup(props.popupId); removePopup(props.popupId);
} }
}
function onNameChange(event: React.ChangeEvent<HTMLInputElement>): void { function onNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value); setName(event.target.value);

@ -11,7 +11,9 @@ import { FindInvestorsPopup } from "./FindInvestorsPopup";
import { GoPublicPopup } from "./GoPublicPopup"; import { GoPublicPopup } from "./GoPublicPopup";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades"; import {
CorporationUnlockUpgrade,
CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
import { import {
CorporationUpgrade, CorporationUpgrade,
CorporationUpgrades } from "../data/CorporationUpgrades"; CorporationUpgrades } from "../data/CorporationUpgrades";
@ -20,6 +22,7 @@ import { CONSTANTS } from "../../Constants";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions"; import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
@ -30,7 +33,6 @@ interface IProps {
interface GeneralBtns { interface GeneralBtns {
bribeFactions: React.ReactElement; bribeFactions: React.ReactElement;
getStarterGuide: React.ReactElement;
} }
export function Overview(props: IProps): React.ReactElement { export function Overview(props: IProps): React.ReactElement {
@ -38,10 +40,32 @@ export function Overview(props: IProps): React.ReactElement {
interface ICreateButtonProps { interface ICreateButtonProps {
text: string; text: string;
class?: string; class?: string;
className?: string;
display?: string; display?: string;
tooltip?: string; tooltip?: string;
onClick?: (event: React.MouseEvent) => void; 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 (
<a
className={className}
onClick={props.onClick}
style={{display: props.display ? props.display : "block"}}>
{props.text}
{
hasTooltip &&
<span className={"tooltiptext"}>
{props.tooltip}
</span>
}
</a>
);
}
function createButton(props: ICreateButtonProps): React.ReactElement { function createButton(props: ICreateButtonProps): React.ReactElement {
let className = props.class ? props.class : "std-button"; let className = props.class ? props.class : "std-button";
const displayStyle = props.display ? props.display : "block"; const displayStyle = props.display ? props.display : "block";
@ -63,84 +87,6 @@ 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<br><br>` +
`Dividend Percentage: ${numeralWrapper.format(props.corp.dividendPercentage / 100, "0%")}<br>` +
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
`Dividend Tax Rate: ${props.corp.dividendTaxPercentage}%<br>` +
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (props.corp.dividendTaxPercentage / 100)), "$0.000a")} / s<br><br>`;
}
let txt = "Total Funds: " + numeralWrapper.format(props.corp.funds.toNumber(), '$0.000a') + "<br>" +
"Total Revenue: " + numeralWrapper.format(props.corp.revenue.toNumber(), "$0.000a") + " / s<br>" +
"Total Expenses: " + numeralWrapper.format(props.corp.expenses.toNumber(), "$0.000a") + " / s<br>" +
"Total Profits: " + profitStr + " / s<br>" +
dividendStr +
"Publicly Traded: " + (props.corp.public ? "Yes" : "No") + "<br>" +
"Owned Stock Shares: " + numeralWrapper.format(props.corp.numShares, '0.000a') + "<br>" +
"Stock Price: " + (props.corp.public ? numeralWrapper.formatMoney(props.corp.sharePrice) : "N/A") + "<br>" +
"<p class='tooltip'>Total Stock Shares: " + numeralWrapper.format(props.corp.totalShares, "0.000a") +
"<span class='tooltiptext'>" +
`Outstanding Shares: ${numeralWrapper.format(props.corp.issuedShares, "0.000a")}<br>` +
`Private Shares: ${numeralWrapper.format(props.corp.totalShares - props.corp.issuedShares - props.corp.numShares, "0.000a")}` +
"</span></p><br><br>";
const storedTime = props.corp.storedCycles * CONSTANTS.MilliPerCycle;
if (storedTime > 15000) {
txt += `Bonus time: ${convertTimeMsToTimeElapsedString(storedTime)}<br><br>`;
}
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") + "<br>";}
if (storageMult > 1) {txt += "Storage Multiplier: " + numeralWrapper.format(storageMult, "0.000") + "<br>";}
if (advMult > 1) {txt += "Advertising Multiplier: " + numeralWrapper.format(advMult, "0.000") + "<br>";}
if (empCreMult > 1) {txt += "Empl. Creativity Multiplier: " + numeralWrapper.format(empCreMult, "0.000") + "<br>";}
if (empChaMult > 1) {txt += "Empl. Charisma Multiplier: " + numeralWrapper.format(empChaMult, "0.000") + "<br>";}
if (empIntMult > 1) {txt += "Empl. Intelligence Multiplier: " + numeralWrapper.format(empIntMult, "0.000") + "<br>";}
if (empEffMult > 1) {txt += "Empl. Efficiency Multiplier: " + numeralWrapper.format(empEffMult, "0.000") + "<br>";}
if (salesMult > 1) {txt += "Sales Multiplier: " + numeralWrapper.format(salesMult, "0.000") + "<br>";}
if (sciResMult > 1) {txt += "Scientific Research Multiplier: " + numeralWrapper.format(sciResMult, "0.000") + "<br>";}
return txt;
}
// 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.",
});
function openBribeFactionPopup(): void { function openBribeFactionPopup(): void {
const popupId = "corp-bribe-popup"; const popupId = "corp-bribe-popup";
createPopup(popupId, BribeFactionPopup, { createPopup(popupId, BribeFactionPopup, {
@ -150,39 +96,49 @@ export function Overview(props: IProps): React.ReactElement {
}); });
} }
// Create a "Bribe Factions" button if your Corporation is powerful enough. const profit: number = props.corp.revenue.minus(props.corp.expenses).toNumber();
// This occurs regardless of whether you're public or private
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<br /><br />
Dividend Percentage: {numeralWrapper.format(props.corp.dividendPercentage / 100, "0%")}<br />
Dividends per share: {Money(dividendsPerShare)} / s<br />
Your earnings as a shareholder (Pre-Tax): {Money(playerEarnings)} / s<br />
Dividend Tax Rate: {props.corp.dividendTaxPercentage}%<br />
Your earnings as a shareholder (Post-Tax): {Money(playerEarnings * (1 - (props.corp.dividendTaxPercentage / 100)))} / s<br /><br />
</>);
}
function Mult(props: {name: string, mult: number}): React.ReactElement {
if(props.mult <= 1) return (<></>);
return (<p>{props.name}{numeralWrapper.format(props.mult, "0.000")}<br /></p>);
}
// Returns a string with general information about Corporation
function BonusTime(): React.ReactElement {
const storedTime = props.corp.storedCycles * CONSTANTS.MilliPerCycle;
if (storedTime <= 15000) return (<></>);
return (<p>Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}<br /><br /></p>);
}
function BribeButton(): React.ReactElement {
const canBribe = (props.corp.determineValuation() >= CorporationConstants.BribeThreshold) || true; const canBribe = (props.corp.determineValuation() >= CorporationConstants.BribeThreshold) || true;
const bribeFactionsClass = (canBribe ? "a-link-button" : "a-link-button-inactive"); const bribeFactionsClass = (canBribe ? "a-link-button" : "a-link-button-inactive");
const bribeFactionsBtn = createButton({ return <Button
class: bribeFactionsClass, className={bribeFactionsClass}
display: "inline-block", display="inline-block"
onClick: openBribeFactionPopup, onClick={openBribeFactionPopup}
text: "Bribe Factions", text="Bribe Factions"
tooltip: (canBribe tooltip={(canBribe
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation" ? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
: "Your Corporation is not powerful enough to bribe Faction leaders"), : "Your Corporation is not powerful enough to bribe Faction leaders")}
/>
});
const generalBtns = {
bribeFactions: bribeFactionsBtn,
getStarterGuide: getStarterGuideBtn,
};
if (props.corp.public) {
return renderPublicButtons(generalBtns);
} else {
return renderPrivateButtons(generalBtns);
} }
}
// Render the buttons for when your Corporation is still private
function renderPrivateButtons(generalBtns: GeneralBtns): React.ReactElement {
const fundingAvailable = (props.corp.fundingRound < 4);
const findInvestorsClassName = fundingAvailable ? "std-button" : "a-link-button-inactive";
const findInvestorsTooltip = fundingAvailable ? "Search for private investors who will give you startup funding in exchangefor equity (stock shares) in your company" : undefined;
function openFindInvestorsPopup(): void { function openFindInvestorsPopup(): void {
const popupId = "cmpy-mgmt-find-investors-popup"; const popupId = "cmpy-mgmt-find-investors-popup";
@ -202,61 +158,44 @@ export function Overview(props: IProps): React.ReactElement {
}); });
} }
const findInvestorsBtn = createButton({ // Render the buttons for when your Corporation is still private
class: findInvestorsClassName, function PrivateButtons(): React.ReactElement {
onClick: openFindInvestorsPopup, const fundingAvailable = (props.corp.fundingRound < 4);
text: "Find Investors", const findInvestorsClassName = fundingAvailable ? "std-button" : "a-link-button-inactive";
tooltip: findInvestorsTooltip, const findInvestorsTooltip = fundingAvailable ? "Search for private investors who will give you startup funding in exchangefor equity (stock shares) in your company" : undefined;
display: "inline-block",
});
const goPublicBtn = createButton({
class: "std-button",
onClick: openGoPublicPopup,
display: "inline-block",
text: "Go Public",
tooltip: "Become a publicly traded and owned entity. Going public " +
"involves issuing shares for an IPO. Once you are a public " +
"company, your shares will be traded on the stock market.",
});
return ( return (
<div> <>
{generalBtns.getStarterGuide} <Button
{findInvestorsBtn} className={findInvestorsClassName}
{goPublicBtn} onClick={openFindInvestorsPopup}
text="Find Investors"
tooltip={findInvestorsTooltip}
display="inline-block"
/>
<Button
className="std-button"
onClick={openGoPublicPopup}
display="inline-block"
text="Go Public"
tooltip={"Become a publicly traded and owned entity. Going public " +
"involves issuing shares for an IPO. Once you are a public " +
"company, your shares will be traded on the stock market."}
/>
<br /> <br />
{generalBtns.bribeFactions} </>
</div>
) )
} }
// Render the buttons for when your Corporation has gone public function openSellSharesPopup(): void {
function renderPublicButtons(generalBtns: GeneralBtns): React.ReactElement {
const corp = props.corp;
const sellSharesOnCd = (corp.shareSaleCooldown > 0);
const sellSharesClass = sellSharesOnCd ? "a-link-button-inactive" : "std-button";
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 sellSharesBtn = createButton({
class: sellSharesClass,
display: "inline-block",
onClick: function(event: React.MouseEvent) {
if(!event.isTrusted) return;
const popupId = "cmpy-mgmt-sell-shares-popup"; const popupId = "cmpy-mgmt-sell-shares-popup";
createPopup(popupId, SellSharesPopup, { createPopup(popupId, SellSharesPopup, {
corp: props.corp, corp: props.corp,
player: props.player, player: props.player,
popupId: popupId, popupId: popupId,
}); });
}, }
text: "Sell Shares",
tooltip: sellSharesTooltip,
});
function openBuybackSharesPopup(): void { function openBuybackSharesPopup(): void {
const popupId = "corp-buyback-shares-popup"; const popupId = "corp-buyback-shares-popup";
@ -267,14 +206,6 @@ export function Overview(props: IProps): React.ReactElement {
}); });
} }
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 { function openIssueNewSharesPopup(): void {
const popupId = "cmpy-mgmt-issue-new-shares-popup"; const popupId = "cmpy-mgmt-issue-new-shares-popup";
createPopup(popupId, IssueNewSharesPopup, { createPopup(popupId, IssueNewSharesPopup, {
@ -283,19 +214,6 @@ export function Overview(props: IProps): React.ReactElement {
}); });
} }
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 { function openIssueDividendsPopup(): void {
const popupId = "cmpy-mgmt-issue-dividends-popup"; const popupId = "cmpy-mgmt-issue-dividends-popup";
createPopup(popupId, IssueDividendsPopup, { createPopup(popupId, IssueDividendsPopup, {
@ -304,91 +222,146 @@ export function Overview(props: IProps): React.ReactElement {
}); });
} }
const issueDividendsBtn = createButton({ // Render the buttons for when your Corporation has gone public
class: "std-button", function PublicButtons(): React.ReactElement {
display: "inline-block", const corp = props.corp;
onClick: openIssueDividendsPopup,
text: "Issue Dividends", const sellSharesOnCd = (corp.shareSaleCooldown > 0);
tooltip: "Manage the dividends that are paid out to shareholders (including yourself)", const sellSharesClass = sellSharesOnCd ? "a-link-button-inactive" : "std-button";
}); 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 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.";
return ( return (
<div> <>
{generalBtns.getStarterGuide} <Button
{sellSharesBtn} className={sellSharesClass}
{buybackSharesBtn} display="inline-block"
onClick={openSellSharesPopup}
text="Sell Shares"
tooltip={sellSharesTooltip}
/>
<Button
className="std-button"
display="inline-block"
onClick={openBuybackSharesPopup}
text="Buyback shares"
tooltip="Buy back shares you that previously issued or sold at market price."
/>
<br /> <br />
{issueNewSharesBtn} <Button
{issueDividendsBtn} className={issueNewSharesClass}
display="inline-block"
onClick={openIssueNewSharesPopup}
text="Issue New Shares"
tooltip={issueNewSharesTooltip}
/>
<Button
className="std-button"
display="inline-block"
onClick={openIssueDividendsPopup}
text="Issue Dividends"
tooltip="Manage the dividends that are paid out to shareholders (including yourself)"
/>
<br /> <br />
{generalBtns.bribeFactions} </>
</div>
) )
} }
// Render the UI for Corporation upgrades // Render the UI for Corporation upgrades
function renderUpgrades(): React.ReactElement { function Upgrades(): React.ReactElement {
// Don't show upgrades // Don't show upgrades
if (props.corp.divisions.length <= 0) { return (<></>); } 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(<UnlockUpgrade
player={props.player}
corp={props.corp}
upgradeData={unlockData}
key={unlockData[0]}
/>);
}
});
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 ( return (
<div className={"cmpy-mgmt-upgrade-container"}> <div className={"cmpy-mgmt-upgrade-container"}>
<h1 className={"cmpy-mgmt-upgrade-header"}> Unlocks </h1> <h1 className={"cmpy-mgmt-upgrade-header"}> Unlocks </h1>
{unlockUpgrades} {
Object.values(CorporationUnlockUpgrades)
.filter((upgrade: CorporationUnlockUpgrade) => props.corp.unlockUpgrades[upgrade[0]] === 0)
.map((upgrade: CorporationUnlockUpgrade) =>
<UnlockUpgrade
player={props.player}
corp={props.corp}
upgradeData={upgrade}
key={upgrade[0]}
/>)
}
<h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1> <h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1>
{ {
levelableUpgradeProps.map((data: UpgradeData) => <LevelableUpgrade props.corp.upgrades
.map((level: number, i: number) => CorporationUpgrades[i])
.map((upgrade: CorporationUpgrade) => <LevelableUpgrade
player={props.player} player={props.player}
corp={props.corp} corp={props.corp}
upgradeData={data.upgradeData} upgrade={upgrade}
upgradeLevel={data.upgradeLevel} key={upgrade[0]}
key={data.upgradeData[0]}
/>, />,
) )
} }
</div> </div>
) );
} }
return ( return (
<div> <div>
<p dangerouslySetInnerHTML={{__html: getOverviewText()}}></p> <p>
{renderButtons()} Total Funds: {Money(props.corp.funds.toNumber())}<br />
Total Revenue: {Money(props.corp.revenue.toNumber())} / s<br />
Total Expenses: {Money(props.corp.expenses.toNumber())} / s<br />
Total Profits: {Money(profit)} / s<br />
<DividendsStats />
Publicly Traded: {(props.corp.public ? "Yes" : "No")}<br />
Owned Stock Shares: {numeralWrapper.format(props.corp.numShares, '0.000a')}<br />
Stock Price: {(props.corp.public ? Money(props.corp.sharePrice) : "N/A")}<br />
</p>
<p className='tooltip'>
Total Stock Shares: {numeralWrapper.format(props.corp.totalShares, "0.000a")}
<span className='tooltiptext'>
Outstanding Shares: {numeralWrapper.format(props.corp.issuedShares, "0.000a")}<br />
Private Shares: {numeralWrapper.format(props.corp.totalShares - props.corp.issuedShares - props.corp.numShares, "0.000a")}
</span>
</p>
<br /><br />
<Mult name="Production Multiplier: " mult={props.corp.getProductionMultiplier()} />
<Mult name="Storage Multiplier: " mult={props.corp.getStorageMultiplier()} />
<Mult name="Advertising Multiplier: " mult={props.corp.getAdvertisingMultiplier()} />
<Mult name="Empl. Creativity Multiplier: " mult={props.corp.getEmployeeCreMultiplier()} />
<Mult name="Empl. Charisma Multiplier: " mult={props.corp.getEmployeeChaMultiplier()} />
<Mult name="Empl. Intelligence Multiplier: " mult={props.corp.getEmployeeIntMultiplier()} />
<Mult name="Empl. Efficiency Multiplier: " mult={props.corp.getEmployeeEffMultiplier()} />
<Mult name="Sales Multiplier: " mult={props.corp.getSalesMultiplier()} />
<Mult name="Scientific Research Multiplier: " mult={props.corp.getScientificResearchMultiplier()} />
<br /> <br />
{renderUpgrades()} <BonusTime />
<div>
<Button
className="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."}
/>
{ props.corp.public ?
<PublicButtons /> :
<PrivateButtons />
}
<BribeButton />
</div>
<br />
<Upgrades />
</div> </div>
) )
} }

@ -6,6 +6,8 @@ import { dialogBoxCreate } from "../../../utils/DialogBox";
import { CorporationUnlockUpgrade } from "../data/CorporationUnlockUpgrades"; import { CorporationUnlockUpgrade } from "../data/CorporationUnlockUpgrades";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { UnlockUpgrade as UU } from "../Actions";
import { Money } from "../../ui/React/Money";
interface IProps { interface IProps {
upgradeData: CorporationUnlockUpgrade; upgradeData: CorporationUnlockUpgrade;
@ -15,16 +17,15 @@ interface IProps {
export function UnlockUpgrade(props: IProps): React.ReactElement { export function UnlockUpgrade(props: IProps): React.ReactElement {
const data = props.upgradeData; const data = props.upgradeData;
const text = `${data[2]} - ${numeralWrapper.formatMoney(data[1])}`; const text = <>{data[2]} - {Money(data[1])}</>;
const tooltip = data[3]; const tooltip = data[3];
function onClick(): void { function onClick(): void {
const corp = props.corp; try {
if (corp.funds.lt(data[1])) { UU(props.corp, props.upgradeData);
dialogBoxCreate("Insufficient funds"); } catch(err) {
} else { dialogBoxCreate(err);
corp.unlock(data);
corp.rerender(props.player);
} }
props.corp.rerender(props.player);
} }
return ( return (

@ -20,6 +20,14 @@ import { CompanyPosition } from "./Company/CompanyPosition";
import { CompanyPositions } from "./Company/CompanyPositions"; import { CompanyPositions } from "./Company/CompanyPositions";
import { CONSTANTS } from "./Constants"; import { CONSTANTS } from "./Constants";
import { DarkWebItems } from "./DarkWeb/DarkWebItems"; 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 { import {
calculateHackingChance, calculateHackingChance,
calculateHackingExpGain, calculateHackingExpGain,
@ -4097,6 +4105,32 @@ function NetscriptFunctions(workerScript) {
}, },
}, // End Bladeburner }, // 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 // Coding Contract API
codingcontract: { codingcontract: {
attempt: function(answer, fn, ip=workerScript.serverIp, { returnReward } = {}) { attempt: function(answer, fn, ip=workerScript.serverIp, { returnReward } = {}) {

@ -20,6 +20,7 @@ import { Server } from "../Server/Server";
import { IPlayerOwnedSourceFile } from "../SourceFile/PlayerOwnedSourceFile"; import { IPlayerOwnedSourceFile } from "../SourceFile/PlayerOwnedSourceFile";
import { MoneySourceTracker } from "../utils/MoneySourceTracker"; import { MoneySourceTracker } from "../utils/MoneySourceTracker";
import { Exploit } from "../Exploits/Exploit"; import { Exploit } from "../Exploits/Exploit";
import { ICorporation } from "../Corporation/ICorporation";
export interface IPlayer { export interface IPlayer {
// Class members // Class members
@ -28,7 +29,7 @@ export interface IPlayer {
bitNodeN: number; bitNodeN: number;
city: CityName; city: CityName;
companyName: string; companyName: string;
corporation: any; corporation: ICorporation;
currentServer: string; currentServer: string;
factions: string[]; factions: string[];
factionInvitations: string[]; factionInvitations: string[];