work on corp mui

This commit is contained in:
Olivier Gagnon 2021-09-28 19:38:51 -04:00
parent 86678b6290
commit a6d7f93111
20 changed files with 676 additions and 766 deletions

@ -26,7 +26,7 @@ export function AllPages(props: IProps): React.ReactElement {
return (
<>
<Tabs variant="fullWidth" value={value} onChange={handleChange} aria-label="basic tabs example">
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
<Tab label="General" />
<Tab label="Contracts" />
<Tab label="Operations" />

@ -0,0 +1,112 @@
import React, { useState } from "react";
import { Factions } from "../../Faction/Factions";
import { CorporationConstants } from "../data/Constants";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box";
import Select, { SelectChangeEvent } from "@mui/material/Select";
interface IProps {
open: boolean;
onClose: () => void;
}
export function BribeFactionModal(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const [money, setMoney] = useState<number | null>(0);
const [stock, setStock] = useState<number | null>(0);
const [selectedFaction, setSelectedFaction] = useState(
player.factions.length > 0 ? player.factions.filter((faction) => Factions[faction].getInfo().offersWork())[0] : "",
);
function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void {
setMoney(parseFloat(event.target.value));
}
function onStockChange(event: React.ChangeEvent<HTMLInputElement>): void {
setStock(parseFloat(event.target.value));
}
function changeFaction(event: SelectChangeEvent<string>): void {
setSelectedFaction(event.target.value);
}
function repGain(money: number, stock: number): number {
return (money + stock * corp.sharePrice) / CorporationConstants.BribeToRepRatio;
}
function getRepText(money: number, stock: number): string {
if (money === 0 && stock === 0) return "";
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
return "ERROR: Invalid value(s) entered";
} else if (corp.funds.lt(money)) {
return "ERROR: You do not have this much money to bribe with";
} else if (stock > corp.numShares) {
return "ERROR: You do not have this many shares to bribe with";
} else {
return (
"You will gain " +
numeralWrapper.formatReputation(repGain(money, stock)) +
" reputation with " +
selectedFaction +
" with this bribe"
);
}
}
function bribe(money: number, stock: number): void {
const fac = Factions[selectedFaction];
if (fac == null) {
dialogBoxCreate("ERROR: You must select a faction to bribe");
}
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
} else if (corp.funds.lt(money)) {
} else if (stock > corp.numShares) {
} else {
const rep = repGain(money, stock);
dialogBoxCreate(
"You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
);
fac.playerReputation += rep;
corp.funds = corp.funds.minus(money);
corp.numShares -= stock;
props.onClose();
}
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
You can use Corporation funds or stock shares to bribe Faction Leaders in exchange for faction reputation.
</Typography>
<Box display="flex" alignItems="center">
<Typography>Faction:</Typography>
<Select variant="standard" value={selectedFaction} onChange={changeFaction}>
{player.factions.map((name: string) => {
const info = Factions[name].getInfo();
if (!info.offersWork()) return;
return (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
);
})}
</Select>
</Box>
<Typography>{getRepText(money ? money : 0, stock ? stock : 0)}</Typography>
<TextField variant="standard" onChange={onMoneyChange} placeholder="Corporation funds" />
<TextField sx={{ mx: 1 }} variant="standard" onChange={onStockChange} placeholder="Stock Shares" />
<Button sx={{ mx: 1 }} onClick={() => bribe(money ? money : 0, stock ? stock : 0)}>
Bribe
</Button>
</Modal>
);
}

@ -1,109 +0,0 @@
import React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Factions } from "../../Faction/Factions";
import { CorporationConstants } from "../data/Constants";
import { numeralWrapper } from "../../ui/numeralFormat";
import { removePopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { ICorporation } from "../ICorporation";
interface IProps {
popupId: string;
corp: ICorporation;
player: IPlayer;
}
export function BribeFactionPopup(props: IProps): React.ReactElement {
const [money, setMoney] = useState<number | null>(0);
const [stock, setStock] = useState<number | null>(0);
const [selectedFaction, setSelectedFaction] = useState(
props.player.factions.length > 0 ? props.player.factions.filter(faction => Factions[faction].getInfo().offersWork())[0] : ""
);
function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void {
setMoney(parseFloat(event.target.value));
}
function onStockChange(event: React.ChangeEvent<HTMLInputElement>): void {
setStock(parseFloat(event.target.value));
}
function changeFaction(event: React.ChangeEvent<HTMLSelectElement>): void {
setSelectedFaction(event.target.value);
}
function repGain(money: number, stock: number): number {
return (money + stock * props.corp.sharePrice) / CorporationConstants.BribeToRepRatio;
}
function getRepText(money: number, stock: number): string {
if (money === 0 && stock === 0) return "";
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
return "ERROR: Invalid value(s) entered";
} else if (props.corp.funds.lt(money)) {
return "ERROR: You do not have this much money to bribe with";
} else if (stock > props.corp.numShares) {
return "ERROR: You do not have this many shares to bribe with";
} else {
return (
"You will gain " +
numeralWrapper.formatReputation(repGain(money, stock)) +
" reputation with " +
selectedFaction +
" with this bribe"
);
}
}
function bribe(money: number, stock: number): void {
const fac = Factions[selectedFaction];
if (fac == null) {
dialogBoxCreate("ERROR: You must select a faction to bribe");
}
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
} else if (props.corp.funds.lt(money)) {
} else if (stock > props.corp.numShares) {
} else {
const rep = repGain(money, stock);
dialogBoxCreate(
"You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
);
fac.playerReputation += rep;
props.corp.funds = props.corp.funds.minus(money);
props.corp.numShares -= stock;
removePopup(props.popupId);
}
}
return (
<>
<p>You can use Corporation funds or stock shares to bribe Faction Leaders in exchange for faction reputation.</p>
<select className="dropdown" style={{ margin: "3px" }} defaultValue={selectedFaction} onChange={changeFaction}>
{props.player.factions.map((name: string) => {
const info = Factions[name].getInfo();
if (!info.offersWork()) return;
return (
<option key={name} value={name}>
{name}
</option>
);
})}
</select>
<p>{getRepText(money ? money : 0, stock ? stock : 0)}</p>
<input
className="text-input"
onChange={onMoneyChange}
placeholder="Corporation funds"
style={{ margin: "5px" }}
/>
<input className="text-input" onChange={onStockChange} placeholder="Stock Shares" style={{ margin: "5px" }} />
<button
className="a-link-button"
onClick={() => bribe(money ? money : 0, stock ? stock : 0)}
style={{ display: "inline-block" }}
>
Bribe
</button>
</>
);
}

@ -1,20 +1,21 @@
import React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
import { Modal } from "../../ui/React/Modal";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { ICorporation } from "../ICorporation";
import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
interface IProps {
player: IPlayer;
popupId: string;
corp: ICorporation;
open: boolean;
onClose: () => void;
rerender: () => void;
}
// Create a popup that lets the player buyback shares
// This is created when the player clicks the "Buyback Shares" button in the overview panel
export function BuybackSharesPopup(props: IProps): React.ReactElement {
export function BuybackSharesModal(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const [shares, setShares] = useState<number | null>(null);
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
@ -22,38 +23,38 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
else setShares(Math.round(parseFloat(event.target.value)));
}
const currentStockPrice = props.corp.sharePrice;
const currentStockPrice = corp.sharePrice;
const buybackPrice = currentStockPrice * 1.1;
function buy(): void {
if (shares === null) return;
const tempStockPrice = props.corp.sharePrice;
const tempStockPrice = corp.sharePrice;
const buybackPrice = tempStockPrice * 1.1;
if (isNaN(shares) || shares <= 0) {
dialogBoxCreate("ERROR: Invalid value for number of shares");
} else if (shares > props.corp.issuedShares) {
} else if (shares > corp.issuedShares) {
dialogBoxCreate("ERROR: There are not this many oustanding shares to buy back");
} else if (shares * buybackPrice > props.player.money) {
} else if (shares * buybackPrice > player.money) {
dialogBoxCreate(
"ERROR: You do not have enough money to purchase this many shares (you need " +
numeralWrapper.format(shares * buybackPrice, "$0.000a") +
")",
);
} else {
props.corp.numShares += shares;
if (isNaN(props.corp.issuedShares)) {
console.warn("Corporation issuedShares is NaN: " + props.corp.issuedShares);
corp.numShares += shares;
if (isNaN(corp.issuedShares)) {
console.warn("Corporation issuedShares is NaN: " + corp.issuedShares);
console.warn("Converting to number now");
const res = props.corp.issuedShares;
const res = corp.issuedShares;
if (isNaN(res)) {
props.corp.issuedShares = 0;
corp.issuedShares = 0;
} else {
props.corp.issuedShares = res;
corp.issuedShares = res;
}
}
props.corp.issuedShares -= shares;
props.player.loseMoney(shares * buybackPrice);
removePopup(props.popupId);
corp.issuedShares -= shares;
player.loseMoney(shares * buybackPrice);
props.onClose();
props.rerender();
}
}
@ -62,11 +63,11 @@ export function BuybackSharesPopup(props: IProps): 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 > props.corp.issuedShares) {
} else if (shares > corp.issuedShares) {
return (
<>
There are not this many shares available to buy back. There are only{" "}
{numeralWrapper.formatBigNumber(props.corp.issuedShares)} outstanding shares.
{numeralWrapper.formatBigNumber(corp.issuedShares)} outstanding shares.
</>
);
} else {
@ -83,7 +84,7 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
}
return (
<>
<Modal open={props.open} onClose={props.onClose}>
<p>
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.
@ -93,7 +94,7 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
<br />
<br />
The current buyback price of your company's stock is {numeralWrapper.formatMoney(buybackPrice)}. Your company
currently has {numeralWrapper.formatBigNumber(props.corp.issuedShares)} outstanding stock shares.
currently has {numeralWrapper.formatBigNumber(corp.issuedShares)} outstanding stock shares.
</p>
<CostIndicator />
<br />
@ -109,6 +110,6 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
<button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}>
Buy shares
</button>
</>
</Modal>
);
}

@ -0,0 +1,10 @@
import React, { useContext } from "react";
import { ICorporation } from "../ICorporation";
export const Context: {
Corporation: React.Context<ICorporation>;
} = {
Corporation: React.createContext<ICorporation>({} as ICorporation),
};
export const useCorporation = () => useContext(Context.Corporation);

@ -2,41 +2,16 @@
// These are the tabs at the top of the UI that let you switch to different
// divisions, see an overview of your corporation, or create a new industry
import React, { useState, useEffect } from "react";
import { HeaderTab } from "./HeaderTab";
import { IIndustry } from "../IIndustry";
import { NewIndustryPopup } from "./NewIndustryPopup";
import { createPopup } from "../../ui/React/createPopup";
import { ICorporation } from "../ICorporation";
import { MainPanel } from "./MainPanel";
import { Industries } from "../IndustryData";
import { ExpandIndustryTab } from "./ExpandIndustryTab";
import { use } from "../../ui/Context";
import { Context } from "./Context";
import { Overview } from "./Overview";
interface IExpandButtonProps {
corp: ICorporation;
setDivisionName: (name: string) => void;
}
function ExpandButton(props: IExpandButtonProps): React.ReactElement {
const allIndustries = Object.keys(Industries).sort();
const possibleIndustries = allIndustries
.filter(
(industryType: string) =>
props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
)
.sort();
if (possibleIndustries.length === 0) return <></>;
function openNewIndustryPopup(): void {
const popupId = "cmpy-mgmt-expand-industry-popup";
createPopup(popupId, NewIndustryPopup, {
corp: props.corp,
setDivisionName: props.setDivisionName,
popupId: popupId,
});
}
return <HeaderTab current={false} onClick={openNewIndustryPopup} text={"Expand into new Industry"} />;
}
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
export function CorporationRoot(): React.ReactElement {
const player = use.Player();
@ -46,33 +21,37 @@ export function CorporationRoot(): React.ReactElement {
function rerender(): void {
setRerender((old) => !old);
}
const [divisionName, setDivisionName] = useState("Overview");
const [divisionName, setDivisionName] = useState<string | number>("Overview");
function handleChange(event: React.SyntheticEvent, tab: string | number): void {
setDivisionName(tab);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const canExpand =
Object.keys(Industries).filter(
(industryType: string) =>
corporation.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
).length > 0;
return (
<Context.Corporation.Provider value={corporation}>
<div className="cmpy-mgmt-container">
<div>
<HeaderTab
current={divisionName === "Overview"}
key={"overview"}
onClick={() => setDivisionName("Overview")}
text={corporation.name}
/>
{corporation.divisions.map((division: IIndustry) => (
<HeaderTab
current={division.name === divisionName}
key={division.name}
onClick={() => setDivisionName(division.name)}
text={division.name}
/>
<Tabs variant="fullWidth" value={divisionName} onChange={handleChange}>
<Tab label={corporation.name} value={"Overview"} />
{corporation.divisions.map((div) => (
<Tab key={div.name} label={div.name} value={div.name} />
))}
<ExpandButton corp={corporation} setDivisionName={setDivisionName} />
</div>
<MainPanel rerender={rerender} corp={corporation} divisionName={divisionName} player={player} />
{canExpand && <Tab label={"Expand"} value={-1} />}
</Tabs>
{divisionName === "Overview" && <Overview rerender={rerender} />}
{divisionName === -1 && <ExpandIndustryTab setDivisionName={setDivisionName} />}
{typeof divisionName === "string" && divisionName !== "Overview" && (
<MainPanel rerender={rerender} divisionName={divisionName + ""} />
)}
</div>
</Context.Corporation.Provider>
);
}

@ -0,0 +1,75 @@
import React, { useState } from "react";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
interface IProps {
open: boolean;
onClose: () => void;
}
export function CreateCorporationModal(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const canSelfFund = player.canAfford(150e9);
if (!player.canAccessCorporation() || player.hasCorporation()) {
props.onClose();
return <></>;
}
const [name, setName] = useState("");
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value);
}
function selfFund(): void {
if (!canSelfFund) {
return;
}
if (name == "") {
return;
}
player.startCorporation(name);
player.loseMoney(150e9);
props.onClose();
router.toCorporation();
}
function seed(): void {
if (name == "") {
return;
}
player.startCorporation(name, 500e6);
props.onClose();
router.toCorporation();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Would you like to start a corporation? This will require $150b for registration and initial funding. This $150b
can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million
shares
<br />
<br />
If you would like to start one, please enter a name for your corporation below:
</Typography>
<TextField autoFocus={true} variant="standard" placeholder="Corporation Name" onChange={onChange} value={name} />
<Button onClick={seed} disabled={name == ""}>
Use seed money
</Button>
<Button onClick={selfFund} disabled={name == "" || !canSelfFund}>
Self-Fund (<Money money={150e9} player={player} />)
</Button>
</Modal>
);
}

@ -1,83 +0,0 @@
import React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { IRouter } from "../../ui/Router";
interface IProps {
player: IPlayer;
popupId: string;
router: IRouter;
}
export function CreateCorporationPopup(props: IProps): React.ReactElement {
if (!props.player.canAccessCorporation() || props.player.hasCorporation()) {
removePopup(props.popupId);
return <></>;
}
const [name, setName] = useState("");
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value);
}
function selfFund(): void {
if (!props.player.canAfford(150e9)) {
dialogBoxCreate("You don't have enough money to create a corporation! You need $150b.");
return;
}
if (name == "") {
dialogBoxCreate("Invalid company name!");
return;
}
props.player.startCorporation(name);
props.player.loseMoney(150e9);
dialogBoxCreate(
"Congratulations! You just self-funded your own corporation. You can visit " +
"and manage your company in the City.",
);
removePopup(props.popupId);
props.router.toCorporation();
}
function seed(): void {
if (name == "") {
dialogBoxCreate("Invalid company name!");
return;
}
props.player.startCorporation(name, 500e6);
dialogBoxCreate(
"Congratulations! You just started your own corporation with government seed money. " +
"You can visit and manage your company in the City.",
);
removePopup(props.popupId);
props.router.toCorporation();
}
return (
<>
<p>
Would you like to start a corporation? This will require $150b for registration and initial funding. This $150b
can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million
shares
<br />
<br />
If you would like to start one, please enter a name for your corporation below:
</p>
<input autoFocus={true} className="text-input" placeholder="Corporation Name" onChange={onChange} value={name} />
<button className="std-button" onClick={seed} disabled={name == ""}>
Use seed money
</button>
<button className="std-button" onClick={selfFund} disabled={name == "" || !props.player.canAfford(150e9)}>
Self-Fund (<Money money={150e9} player={props.player} />)
</button>
</>
);
}

@ -1,24 +1,28 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { Industries, IndustryDescriptions } from "../IndustryData";
import { ICorporation } from "../ICorporation";
import { useCorporation } from "./Context";
import { IIndustry } from "../IIndustry";
import { NewIndustry } from "../Actions";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
import Box from "@mui/material/Box";
import Select, { SelectChangeEvent } from "@mui/material/Select";
interface IProps {
corp: ICorporation;
popupId: string;
setDivisionName: (name: string) => void;
}
// Create a popup that lets the player create a new industry.
// This is created when the player clicks the "Expand into new Industry" header tab
export function NewIndustryPopup(props: IProps): React.ReactElement {
export function ExpandIndustryTab(props: IProps): React.ReactElement {
const corp = useCorporation();
const allIndustries = Object.keys(Industries).sort();
const possibleIndustries = allIndustries
.filter(
(industryType: string) =>
props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
)
.sort();
const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : "");
@ -26,7 +30,7 @@ export function NewIndustryPopup(props: IProps): React.ReactElement {
function newIndustry(): void {
try {
NewIndustry(props.corp, industry, name);
NewIndustry(corp, industry, name);
} catch (err) {
dialogBoxCreate(err + "");
return;
@ -34,11 +38,10 @@ export function NewIndustryPopup(props: IProps): React.ReactElement {
// Set routing to the new division so that the UI automatically switches to it
props.setDivisionName(name);
removePopup(props.popupId);
}
function onNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
// [a-zA-Z0-9-_]
setName(event.target.value);
}
@ -46,7 +49,7 @@ export function NewIndustryPopup(props: IProps): React.ReactElement {
if (event.keyCode === 13) newIndustry();
}
function onIndustryChange(event: React.ChangeEvent<HTMLSelectElement>): void {
function onIndustryChange(event: SelectChangeEvent<string>): void {
setIndustry(event.target.value);
}
@ -55,33 +58,33 @@ export function NewIndustryPopup(props: IProps): React.ReactElement {
return (
<>
<p>Create a new division to expand into a new industry:</p>
<select className="dropdown" defaultValue={industry} onChange={onIndustryChange}>
<Typography>Create a new division to expand into a new industry:</Typography>
<Select variant="standard" value={industry} onChange={onIndustryChange}>
{possibleIndustries.map((industry: string) => (
<option key={industry} value={industry}>
<MenuItem key={industry} value={industry}>
{industry}
</option>
</MenuItem>
))}
</select>
<p>{desc(props.corp)}</p>
</Select>
<Typography>{desc(corp)}</Typography>
<br />
<br />
<p>Division name:</p>
<input
<Typography>Division name:</Typography>
<Box display="flex" alignItems="center">
<TextField
variant="standard"
autoFocus={true}
value={name}
onChange={onNameChange}
onKeyDown={onKeyDown}
type="text"
className="text-input"
style={{ display: "block" }}
maxLength={30}
pattern="[a-zA-Z0-9-_]"
/>
<span onClick={newIndustry} className="popup-box-button">
<Button sx={{ mx: 1 }} onClick={newIndustry}>
Create Division
</span>
</Button>
</Box>
</>
);
}

@ -1,23 +1,25 @@
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { numeralWrapper } from "../../ui/numeralFormat";
import { CorporationConstants } from "../data/Constants";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Modal } from "../../ui/React/Modal";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps {
corp: ICorporation;
popupId: string;
player: IPlayer;
open: boolean;
onClose: () => void;
rerender: () => void;
}
// Create a popup that lets the player manage exports
export function FindInvestorsPopup(props: IProps): React.ReactElement {
const val = props.corp.determineValuation();
export function FindInvestorsModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const val = corp.determineValuation();
let percShares = 0;
let roundMultiplier = 4;
switch (props.corp.fundingRound) {
switch (corp.fundingRound) {
case 0: //Seed
percShares = 0.1;
roundMultiplier = 4;
@ -41,15 +43,15 @@ export function FindInvestorsPopup(props: IProps): React.ReactElement {
const investShares = Math.floor(CorporationConstants.INITIALSHARES * percShares);
function findInvestors(): void {
props.corp.fundingRound++;
props.corp.addFunds(funding);
props.corp.numShares -= investShares;
corp.fundingRound++;
corp.addFunds(funding);
corp.numShares -= investShares;
props.rerender();
removePopup(props.popupId);
props.onClose();
}
return (
<>
<p>
<Modal open={props.open} onClose={props.onClose}>
<Typography>
An investment firm has offered you {numeralWrapper.formatMoney(funding)} in funding in exchange for a{" "}
{numeralWrapper.format(percShares * 100, "0.000a")}% stake in the company (
{numeralWrapper.format(investShares, "0.000a")} shares).
@ -59,10 +61,8 @@ export function FindInvestorsPopup(props: IProps): React.ReactElement {
<br />
<br />
Hint: Investment firms will offer more money if your corporation is turning a profit
</p>
<button onClick={findInvestors} className="std-button">
Accept
</button>
</>
</Typography>
<Button onClick={findInvestors}>Accept</Button>
</Modal>
);
}

@ -0,0 +1,81 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal";
import { numeralWrapper } from "../../ui/numeralFormat";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box";
interface IProps {
open: boolean;
onClose: () => void;
rerender: () => void;
}
// Create a popup that lets the player manage exports
export function GoPublicModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const [shares, setShares] = useState("");
const initialSharePrice = corp.determineValuation() / corp.totalShares;
function goPublic(): void {
const numShares = parseFloat(shares);
const initialSharePrice = corp.determineValuation() / corp.totalShares;
if (isNaN(numShares)) {
dialogBoxCreate("Invalid value for number of issued shares");
return;
}
if (numShares > corp.numShares) {
dialogBoxCreate("Error: You don't have that many shares to issue!");
return;
}
corp.public = true;
corp.sharePrice = initialSharePrice;
corp.issuedShares = numShares;
corp.numShares -= numShares;
corp.addFunds(numShares * initialSharePrice);
props.rerender();
dialogBoxCreate(
`You took your ${corp.name} public and earned ` +
`${numeralWrapper.formatMoney(numShares * initialSharePrice)} in your IPO`,
);
props.onClose();
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) goPublic();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setShares(event.target.value);
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
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 {numeralWrapper.formatMoney(initialSharePrice)} per share (the
IPO money will be deposited directly into your Corporation's funds).
<br />
<br />
You have a total of {numeralWrapper.format(corp.numShares, "0.000a")} of shares that you can issue.
</Typography>
<Box display="flex" alignItems="center">
<TextField
variant="standard"
value={shares}
onChange={onChange}
autoFocus
type="number"
placeholder="Shares to issue"
onKeyDown={onKeyDown}
/>
<Button sx={{ mx: 1 }} onClick={goPublic}>
Go Public
</Button>
</Box>
</Modal>
);
}

@ -1,76 +0,0 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { numeralWrapper } from "../../ui/numeralFormat";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps {
corp: ICorporation;
popupId: string;
player: IPlayer;
rerender: () => void;
}
// Create a popup that lets the player manage exports
export function GoPublicPopup(props: IProps): React.ReactElement {
const [shares, setShares] = useState("");
const initialSharePrice = props.corp.determineValuation() / props.corp.totalShares;
function goPublic(): void {
const numShares = parseFloat(shares);
const initialSharePrice = props.corp.determineValuation() / props.corp.totalShares;
if (isNaN(numShares)) {
dialogBoxCreate("Invalid value for number of issued shares");
return;
}
if (numShares > props.corp.numShares) {
dialogBoxCreate("Error: You don't have that many shares to issue!");
return;
}
props.corp.public = true;
props.corp.sharePrice = initialSharePrice;
props.corp.issuedShares = numShares;
props.corp.numShares -= numShares;
props.corp.addFunds(numShares * initialSharePrice);
props.rerender();
dialogBoxCreate(
`You took your ${props.corp.name} public and earned ` +
`${numeralWrapper.formatMoney(numShares * initialSharePrice)} in your IPO`,
);
removePopup(props.popupId);
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) goPublic();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setShares(event.target.value);
}
return (
<>
<p>
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 {numeralWrapper.formatMoney(initialSharePrice)} per share (the
IPO money will be deposited directly into your Corporation's funds).
<br />
<br />
You have a total of {numeralWrapper.format(props.corp.numShares, "0.000a")} of shares that you can issue.
</p>
<input
className="text-input"
value={shares}
onChange={onChange}
autoFocus={true}
type="number"
placeholder="Shares to issue"
onKeyDown={onKeyDown}
/>
<button className="std-button" onClick={goPublic}>
Go Public
</button>
</>
);
}

@ -1,29 +1,34 @@
import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal";
import { CorporationConstants } from "../data/Constants";
import { ICorporation } from "../ICorporation";
import { IssueDividends } from "../Actions";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
interface IProps {
popupId: string;
corp: ICorporation;
open: boolean;
onClose: () => void;
}
// Create a popup that lets the player issue & manage dividends
// This is created when the player clicks the "Issue Dividends" button in the overview panel
export function IssueDividendsPopup(props: IProps): React.ReactElement {
const [percent, setPercent] = useState<number | null>(null);
export function IssueDividendsModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const [percent, setPercent] = useState(0);
const canIssue = !isNaN(percent) && percent >= 0 && percent <= CorporationConstants.DividendMaxPercentage * 100;
function issueDividends(): void {
if (!canIssue) return;
if (percent === null) return;
try {
IssueDividends(props.corp, percent / 100);
IssueDividends(corp, percent / 100);
} catch (err) {
dialogBoxCreate(err + "");
}
removePopup(props.popupId);
props.onClose();
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
@ -31,13 +36,17 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement {
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setPercent(null);
else setPercent(parseFloat(event.target.value));
if (event.target.value === "") setPercent(0);
else {
let p = parseFloat(event.target.value);
if (p > 50) p = 50;
setPercent(p);
}
}
return (
<>
<p>
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Dividends are a distribution of a portion of the corporation's profits to the shareholders. This includes
yourself, as well.
<br />
@ -58,19 +67,19 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement {
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.
</p>
<input
autoFocus={true}
</Typography>
<TextField
variant="standard"
autoFocus
value={percent}
onChange={onChange}
onKeyDown={onKeyDown}
className="text-input"
placeholder="Dividend %"
type="number"
style={{ margin: "5px" }}
/>
<button onClick={issueDividends} className="std-button" style={{ display: "inline-block" }}>
<Button disabled={!canIssue} sx={{ mx: 1 }} onClick={issueDividends}>
Allocate Dividend Percentage
</button>
</>
</Button>
</Modal>
);
}

@ -1,24 +1,27 @@
import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { Modal } from "../../ui/React/Modal";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { CorporationConstants } from "../data/Constants";
import { ICorporation } from "../ICorporation";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
interface IEffectTextProps {
corp: ICorporation;
shares: number | null;
}
function EffectText(props: IEffectTextProps): React.ReactElement {
const corp = useCorporation();
if (props.shares === null) return <></>;
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
const maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
const newSharePrice = Math.round(corp.sharePrice * 0.9);
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
let newShares = props.shares;
if (isNaN(newShares)) {
return <p>Invalid input</p>;
return <Typography>Invalid input</Typography>;
}
// Round to nearest ten-millionth
@ -26,36 +29,37 @@ function EffectText(props: IEffectTextProps): React.ReactElement {
newShares = Math.round(newShares) * 10e6;
if (newShares < 10e6) {
return <p>Must issue at least 10 million new shares</p>;
return <Typography>Must issue at least 10 million new shares</Typography>;
}
if (newShares > maxNewShares) {
return <p>You cannot issue that many shares</p>;
return <Typography>You cannot issue that many shares</Typography>;
}
return (
<p>
<Typography>
Issue ${numeralWrapper.format(newShares, "0.000a")} new shares for{" "}
{numeralWrapper.formatMoney(newShares * newSharePrice)}?
</p>
</Typography>
);
}
interface IProps {
corp: ICorporation;
popupId: string;
open: boolean;
onClose: () => void;
}
// Create a popup that lets the player issue new shares
// This is created when the player clicks the "Issue New Shares" buttons in the overview panel
export function IssueNewSharesPopup(props: IProps): React.ReactElement {
export function IssueNewSharesModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const [shares, setShares] = useState<number | null>(null);
const maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
function issueNewShares(): void {
if (shares === null) return;
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
const newSharePrice = Math.round(corp.sharePrice * 0.9);
let newShares = shares;
if (isNaN(newShares)) {
dialogBoxCreate("Invalid input for number of new shares");
@ -71,8 +75,8 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
}
const profit = newShares * newSharePrice;
props.corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
props.corp.totalShares += newShares;
corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
corp.totalShares += newShares;
// Determine how many are bought by private investors
// Private investors get up to 50% at most
@ -80,16 +84,15 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
let privateShares = getRandomInt(0, Math.round(newShares / 2));
privateShares = Math.round(privateShares / 1e6) * 1e6;
props.corp.issuedShares += newShares - privateShares;
props.corp.funds = props.corp.funds.plus(profit);
props.corp.immediatelyUpdateSharePrice();
removePopup(props.popupId);
corp.issuedShares += newShares - privateShares;
corp.funds = corp.funds.plus(profit);
corp.immediatelyUpdateSharePrice();
props.onClose();
dialogBoxCreate(
`Issued ${numeralWrapper.format(newShares, "0.000a")} and raised ` +
`${numeralWrapper.formatMoney(profit)}. ${numeralWrapper.format(privateShares, "0.000a")} ` +
`of these shares were bought by private investors.<br><br>` +
`Stock price decreased to ${numeralWrapper.formatMoney(props.corp.sharePrice)}`,
`Stock price decreased to ${numeralWrapper.formatMoney(corp.sharePrice)}`,
);
}
@ -103,8 +106,8 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
}
return (
<>
<p>
<Modal open={props.open} onClose={props.onClose}>
<Typography>
You can issue new equity shares (i.e. stocks) in order to raise capital for your corporation.
<br />
<br />
@ -122,19 +125,12 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
When you choose to issue new equity, private shareholders have first priority for up to 50% of the new shares.
If they choose to exercise this option, these newly issued shares become private, restricted shares, which means
you cannot buy them back.
</p>
<EffectText corp={props.corp} shares={shares} />
<input
className="text-input"
autoFocus={true}
placeholder="# New Shares"
style={{ margin: "5px" }}
onChange={onChange}
onKeyDown={onKeyDown}
/>
<button onClick={issueNewShares} className="std-button" style={{ display: "inline-block" }}>
</Typography>
<EffectText shares={shares} />
<TextField variant="standard" autoFocus placeholder="# New Shares" onChange={onChange} onKeyDown={onKeyDown} />
<Button onClick={issueNewShares} sx={{ mx: 1 }}>
Issue New Shares
</button>
</>
</Button>
</Modal>
);
}

@ -2,22 +2,22 @@
import React from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CorporationUpgrade } from "../data/CorporationUpgrades";
import { LevelUpgrade } from "../Actions";
import { MoneyCost } from "./MoneyCost";
import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
interface IProps {
upgrade: CorporationUpgrade;
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
export function LevelableUpgrade(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const data = props.upgrade;
const level = props.corp.upgrades[data[0]];
const level = corp.upgrades[data[0]];
const baseCost = data[1];
const priceMult = data[2];
@ -25,14 +25,14 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
const text = (
<>
{data[4]} - <MoneyCost money={cost} corp={props.corp} />
{data[4]} - <MoneyCost money={cost} corp={corp} />
</>
);
const tooltip = data[5];
function onClick(): void {
if (props.corp.funds.lt(cost)) return;
if (corp.funds.lt(cost)) return;
try {
LevelUpgrade(props.corp, props.upgrade);
LevelUpgrade(corp, props.upgrade);
} catch (err) {
dialogBoxCreate(err + "");
}

@ -5,42 +5,28 @@ import React from "react";
import { CityTabs } from "./CityTabs";
import { IIndustry } from "../IIndustry";
import { Overview } from "./Overview";
import { useCorporation } from "./Context";
import { use } from "../../ui/Context";
import { CityName } from "../../Locations/data/CityNames";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { ICorporation } from "../ICorporation";
interface IProps {
corp: ICorporation;
player: IPlayer;
divisionName: string;
rerender: () => void;
}
export function MainPanel(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const division =
props.divisionName !== "Overview"
? props.corp.divisions.find((division: IIndustry) => division.name === props.divisionName)
? corp.divisions.find((division: IIndustry) => division.name === props.divisionName)
: undefined; // use undefined because find returns undefined
if (division === undefined) {
if (division === undefined) throw new Error("Cannot find division");
return (
<div id="cmpy-mgmt-panel">
<Overview {...props} />
</div>
);
} else {
return (
<div id="cmpy-mgmt-panel">
<CityTabs
rerender={props.rerender}
division={division}
corp={props.corp}
city={CityName.Sector12}
player={props.player}
/>
<CityTabs rerender={props.rerender} division={division} corp={corp} city={CityName.Sector12} player={player} />
</div>
);
}
}

@ -1,14 +1,15 @@
// React Component for displaying Corporation Overview info
import React from "react";
import React, { useState } from "react";
import { LevelableUpgrade } from "./LevelableUpgrade";
import { UnlockUpgrade } from "./UnlockUpgrade";
import { BribeFactionPopup } from "./BribeFactionPopup";
import { SellSharesPopup } from "./SellSharesPopup";
import { BuybackSharesPopup } from "./BuybackSharesPopup";
import { IssueDividendsPopup } from "./IssueDividendsPopup";
import { IssueNewSharesPopup } from "./IssueNewSharesPopup";
import { FindInvestorsPopup } from "./FindInvestorsPopup";
import { GoPublicPopup } from "./GoPublicPopup";
import { BribeFactionModal } from "./BribeFactionModal";
import { SellSharesModal } from "./SellSharesModal";
import { BuybackSharesModal } from "./BuybackSharesModal";
import { IssueDividendsModal } from "./IssueDividendsModal";
import { IssueNewSharesModal } from "./IssueNewSharesModal";
import { FindInvestorsModal } from "./FindInvestorsModal";
import { GoPublicModal } from "./GoPublicModal";
import { Factions } from "../../Faction/Factions";
import { CorporationConstants } from "../data/Constants";
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
@ -17,44 +18,52 @@ import { CorporationUpgrade, CorporationUpgrades } from "../data/CorporationUpgr
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";
import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
interface IProps {
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
export function Overview({ corp, player, rerender }: IProps): React.ReactElement {
export function Overview({ rerender }: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const profit: number = corp.revenue.minus(corp.expenses).toNumber();
return (
<div>
<p>
<Typography>
Total Funds: <Money money={corp.funds.toNumber()} />
<br />
Total Revenue: <Money money={corp.revenue.toNumber()} /> / s<br />
Total Expenses: <Money money={corp.expenses.toNumber()} /> / s
<br />
Total Profits: <Money money={profit} /> / s<br />
<DividendsStats corp={corp} profit={profit} />
<DividendsStats profit={profit} />
Publicly Traded: {corp.public ? "Yes" : "No"}
<br />
Owned Stock Shares: {numeralWrapper.format(corp.numShares, "0.000a")}
<br />
Stock Price: {corp.public ? <Money money={corp.sharePrice} /> : "N/A"}
<br />
</p>
<p className="tooltip">
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
<span className="tooltiptext">
</Typography>
<Tooltip
disableInteractive
title={
<Typography>
Outstanding Shares: {numeralWrapper.format(corp.issuedShares, "0.000a")}
<br />
Private Shares: {numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a")}
</span>
</p>
</Typography>
}
>
<Typography className="tooltip">
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
</Typography>
</Tooltip>
<br />
<br />
<Mult name="Production Multiplier: " mult={corp.getProductionMultiplier()} />
@ -67,100 +76,78 @@ export function Overview({ corp, player, rerender }: IProps): React.ReactElement
<Mult name="Sales Multiplier: " mult={corp.getSalesMultiplier()} />
<Mult name="Scientific Research Multiplier: " mult={corp.getScientificResearchMultiplier()} />
<br />
<BonusTime corp={corp} />
<BonusTime />
<div>
<Button
className="a-link-button"
display="inline-block"
onClick={() => corp.getStarterGuide(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."
<Tooltip
disableInteractive
title={
<Typography>
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.
</Typography>
}
/>
{corp.public ? (
<PublicButtons corp={corp} player={player} rerender={rerender} />
) : (
<PrivateButtons corp={corp} player={player} rerender={rerender} />
)}
<BribeButton corp={corp} player={player} />
>
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
</Tooltip>
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
<BribeButton />
</div>
<br />
<Upgrades corp={corp} player={player} rerender={rerender} />
<Upgrades rerender={rerender} />
</div>
);
}
interface IPrivateButtonsProps {
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
// Render the buttons for when your Corporation is still private
function PrivateButtons({ corp, player, rerender }: IPrivateButtonsProps): React.ReactElement {
function openFindInvestorsPopup(): void {
const popupId = "cmpy-mgmt-find-investors-popup";
createPopup(popupId, FindInvestorsPopup, {
rerender,
player,
popupId,
corp: corp,
});
}
function openGoPublicPopup(): void {
const popupId = "cmpy-mgmt-go-public-popup";
createPopup(popupId, GoPublicPopup, {
rerender,
player,
popupId,
corp: corp,
});
}
function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const [findInvestorsopen, setFindInvestorsopen] = useState(false);
const [goPublicopen, setGoPublicopen] = useState(false);
const fundingAvailable = 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 exchange for equity (stock shares) in your company"
: undefined;
: "";
return (
<>
<Button
className={findInvestorsClassName}
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."
<Tooltip disableInteractive title={<Typography>{findInvestorsTooltip}</Typography>}>
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
Find Investors
</Button>
</Tooltip>
<Tooltip
disableInteractive
title={
<Typography>
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.
</Typography>
}
/>
>
<Button onClick={() => setGoPublicopen(true)}>Go Public</Button>
</Tooltip>
<FindInvestorsModal open={findInvestorsopen} onClose={() => setFindInvestorsopen(false)} rerender={rerender} />
<GoPublicModal open={goPublicopen} onClose={() => setGoPublicopen(false)} rerender={rerender} />
<br />
</>
);
}
interface IUpgradeProps {
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
// Render the UI for Corporation upgrades
function Upgrades({ corp, player, rerender }: IUpgradeProps): React.ReactElement {
function Upgrades({ rerender }: IUpgradeProps): React.ReactElement {
const corp = useCorporation();
// Don't show upgrades
if (corp.divisions.length <= 0) {
return <h1>Upgrades are unlocked once you create an industry.</h1>;
return <Typography variant="h4">Upgrades are unlocked once you create an industry.</Typography>;
}
return (
@ -169,29 +156,32 @@ function Upgrades({ corp, player, rerender }: IUpgradeProps): React.ReactElement
{Object.values(CorporationUnlockUpgrades)
.filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0)
.map((upgrade: CorporationUnlockUpgrade) => (
<UnlockUpgrade rerender={rerender} player={player} corp={corp} upgradeData={upgrade} key={upgrade[0]} />
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade[0]} />
))}
<h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1>
{corp.upgrades
.map((level: number, i: number) => CorporationUpgrades[i])
.map((upgrade: CorporationUpgrade) => (
<LevelableUpgrade rerender={rerender} player={player} corp={corp} upgrade={upgrade} key={upgrade[0]} />
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade[0]} />
))}
</div>
);
}
interface IPublicButtonsProps {
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
// Render the buttons for when your Corporation has gone public
function PublicButtons({ corp, player, rerender }: IPublicButtonsProps): React.ReactElement {
function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
const corp = useCorporation();
const [sellSharesOpen, setSellSharesOpen] = useState(false);
const [buybackSharesOpen, setBuybackSharesOpen] = useState(false);
const [issueNewSharesOpen, setIssueNewSharesOpen] = useState(false);
const [issueDividendsOpen, setIssueDividendsOpen] = useState(false);
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 " +
@ -199,139 +189,83 @@ function PublicButtons({ corp, player, rerender }: IPublicButtonsProps): React.R
"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.";
function openSellSharesPopup(): void {
const popupId = "cmpy-mgmt-sell-shares-popup";
createPopup(popupId, SellSharesPopup, {
corp: corp,
player,
popupId,
rerender,
});
}
function openBuybackSharesPopup(): void {
const popupId = "corp-buyback-shares-popup";
createPopup(popupId, BuybackSharesPopup, {
rerender,
player,
popupId,
corp: corp,
});
}
function openIssueNewSharesPopup(): void {
const popupId = "cmpy-mgmt-issue-new-shares-popup";
createPopup(popupId, IssueNewSharesPopup, {
popupId,
corp: corp,
});
}
function openIssueDividendsPopup(): void {
const popupId = "cmpy-mgmt-issue-dividends-popup";
createPopup(popupId, IssueDividendsPopup, {
popupId,
corp: corp,
});
}
return (
<>
<Button
className={sellSharesClass}
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."
/>
<Tooltip disableInteractive title={<Typography>{sellSharesTooltip}</Typography>}>
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
Sell Shares
</Button>
</Tooltip>
<SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} />
<Tooltip
disableInteractive
title={<Typography>Buy back shares you that previously issued or sold at market price.</Typography>}
>
<Button onClick={() => setBuybackSharesOpen(true)}>Buyback shares</Button>
</Tooltip>
<BuybackSharesModal open={buybackSharesOpen} onClose={() => setBuybackSharesOpen(false)} rerender={rerender} />
<br />
<Button
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)"
/>
<Tooltip disableInteractive title={<Typography>{issueNewSharesTooltip}</Typography>}>
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
Issue New Shares
</Button>
</Tooltip>
<IssueNewSharesModal open={issueNewSharesOpen} onClose={() => setIssueNewSharesOpen(false)} />
<Tooltip
disableInteractive
title={<Typography>Manage the dividends that are paid out to shareholders (including yourself)</Typography>}
>
<Button onClick={() => setIssueDividendsOpen(true)}>Issue Dividends</Button>
</Tooltip>
<IssueDividendsModal open={issueDividendsOpen} onClose={() => setIssueDividendsOpen(false)} />
<br />
</>
);
}
// Generic Function for Creating a button
interface ICreateButtonProps {
text: string;
className?: string;
display?: string;
tooltip?: string;
onClick?: (event: React.MouseEvent) => void;
function BribeButton(): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const [open, setOpen] = useState(false);
const canBribe =
corp.determineValuation() >= CorporationConstants.BribeThreshold &&
player.factions.filter((f) => Factions[f].getInfo().offersWork()).length > 0;
function openBribe(): void {
if (!canBribe) return;
setOpen(true);
}
function Button({ className = "std-button", text, display, tooltip, onClick }: ICreateButtonProps): React.ReactElement {
const hasTooltip = tooltip != null;
if (hasTooltip) className += " tooltip";
return (
<button className={className} onClick={onClick} style={{ display: display ? display : "block" }}>
{text}
{hasTooltip && <span className={"tooltiptext"}>{tooltip}</span>}
</button>
);
}
interface IBribeButtonProps {
player: IPlayer;
corp: ICorporation;
}
function BribeButton({ player, corp }: IBribeButtonProps): React.ReactElement {
function openBribeFactionPopup(): void {
const popupId = "corp-bribe-popup";
createPopup(popupId, BribeFactionPopup, {
player,
popupId,
corp: corp,
});
}
const canBribe = corp.determineValuation() >= CorporationConstants.BribeThreshold || true;
const bribeFactionsClass = canBribe ? "a-link-button" : "a-link-button-inactive";
return (
<Button
className={bribeFactionsClass}
display="inline-block"
onClick={openBribeFactionPopup}
text="Bribe Factions"
tooltip={
<>
<Tooltip
disableInteractive
title={
canBribe
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
: "Your Corporation is not powerful enough to bribe Faction leaders"
}
/>
>
<span>
<Button disabled={!canBribe} onClick={openBribe}>
Bribe Factions
</Button>
</span>
</Tooltip>
<BribeFactionModal open={open} onClose={() => setOpen(false)} />
</>
);
}
interface IDividendsStatsProps {
corp: ICorporation;
profit: number;
}
function DividendsStats({ corp, profit }: IDividendsStatsProps): React.ReactElement {
function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement {
const corp = useCorporation();
if (corp.dividendPercentage <= 0 || profit <= 0) return <></>;
const totalDividends = (corp.dividendPercentage / 100) * profit;
const retainedEarnings = profit - totalDividends;
@ -361,26 +295,24 @@ interface IMultProps {
function Mult({ name, mult }: IMultProps): React.ReactElement {
if (mult <= 1) return <></>;
return (
<p>
<Typography>
{name}
{numeralWrapper.format(mult, "0.000")}
<br />
</p>
</Typography>
);
}
interface IBonusTimeProps {
corp: ICorporation;
}
// Returns a string with general information about Corporation
function BonusTime({ corp }: IBonusTimeProps): React.ReactElement {
function BonusTime(): React.ReactElement {
const corp = useCorporation();
const storedTime = corp.storedCycles * CONSTANTS.MilliPerCycle;
if (storedTime <= 15000) return <></>;
return (
<p>
<Typography>
Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}
<br />
<br />
</p>
</Typography>
);
}

@ -1,21 +1,26 @@
import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
import { CorporationConstants } from "../data/Constants";
import { ICorporation } from "../ICorporation";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
interface IProps {
corp: ICorporation;
player: IPlayer;
popupId: string;
open: boolean;
onClose: () => void;
rerender: () => void;
}
// Create a popup that lets the player sell Corporation shares
// This is created when the player clicks the "Sell Shares" button in the overview panel
export function SellSharesPopup(props: IProps): React.ReactElement {
export function SellSharesModal(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const [shares, setShares] = useState<number | null>(null);
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
@ -27,10 +32,10 @@ export function SellSharesPopup(props: IProps): React.ReactElement {
if (props.shares === null) return <></>;
if (isNaN(props.shares) || props.shares <= 0) {
return <>ERROR: Invalid value entered for number of shares to sell</>;
} else if (props.shares > props.corp.numShares) {
} else if (props.shares > corp.numShares) {
return <>You don't have this many shares to sell!</>;
} else {
const stockSaleResults = props.corp.calculateShareSale(props.shares);
const stockSaleResults = corp.calculateShareSale(props.shares);
const profit = stockSaleResults[0];
return (
<>
@ -44,35 +49,35 @@ export function SellSharesPopup(props: IProps): React.ReactElement {
if (shares === null) return;
if (isNaN(shares) || shares <= 0) {
dialogBoxCreate("ERROR: Invalid value for number of shares");
} else if (shares > props.corp.numShares) {
} else if (shares > corp.numShares) {
dialogBoxCreate("ERROR: You don't have this many shares to sell");
} else {
const stockSaleResults = props.corp.calculateShareSale(shares);
const stockSaleResults = corp.calculateShareSale(shares);
const profit = stockSaleResults[0];
const newSharePrice = stockSaleResults[1];
const newSharesUntilUpdate = stockSaleResults[2];
props.corp.numShares -= shares;
if (isNaN(props.corp.issuedShares)) {
console.error(`Corporation issuedShares is NaN: ${props.corp.issuedShares}`);
const res = props.corp.issuedShares;
corp.numShares -= shares;
if (isNaN(corp.issuedShares)) {
console.error(`Corporation issuedShares is NaN: ${corp.issuedShares}`);
const res = corp.issuedShares;
if (isNaN(res)) {
props.corp.issuedShares = 0;
corp.issuedShares = 0;
} else {
props.corp.issuedShares = res;
corp.issuedShares = res;
}
}
props.corp.issuedShares += shares;
props.corp.sharePrice = newSharePrice;
props.corp.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
props.corp.shareSaleCooldown = CorporationConstants.SellSharesCooldown;
props.player.gainMoney(profit);
props.player.recordMoneySource(profit, "corporation");
removePopup(props.popupId);
corp.issuedShares += shares;
corp.sharePrice = newSharePrice;
corp.shareSalesUntilPriceUpdate = newSharesUntilUpdate;
corp.shareSaleCooldown = CorporationConstants.SellSharesCooldown;
player.gainMoney(profit);
player.recordMoneySource(profit, "corporation");
props.onClose();
dialogBoxCreate(
`Sold ${numeralWrapper.formatMoney(shares)} shares for ` +
`${numeralWrapper.formatMoney(profit)}. ` +
`The corporation's stock price fell to ${numeralWrapper.formatMoney(props.corp.sharePrice)} ` +
`The corporation's stock price fell to ${numeralWrapper.formatMoney(corp.sharePrice)} ` +
`as a result of dilution.`,
);
@ -85,8 +90,8 @@ export function SellSharesPopup(props: IProps): React.ReactElement {
}
return (
<>
<p>
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Enter the number of shares you would like to sell. The money from selling your shares will go directly to you
(NOT your Corporation).
<br />
@ -95,22 +100,21 @@ export function SellSharesPopup(props: IProps): React.ReactElement {
large number of shares all at once will have an immediate effect in reducing your stock price.
<br />
<br />
The current price of your company's stock is {numeralWrapper.formatMoney(props.corp.sharePrice)}
</p>
<ProfitIndicator shares={shares} corp={props.corp} />
The current price of your company's stock is {numeralWrapper.formatMoney(corp.sharePrice)}
</Typography>
<ProfitIndicator shares={shares} corp={corp} />
<br />
<input
autoFocus={true}
className="text-input"
<TextField
variant="standard"
autoFocus
type="number"
placeholder="Shares to sell"
style={{ margin: "5px" }}
onChange={changeShares}
onKeyDown={onKeyDown}
/>
<button onClick={sell} className="a-link-button" style={{ display: "inline-block" }}>
<Button onClick={sell} sx={{ mx: 1 }}>
Sell shares
</button>
</>
</Button>
</Modal>
);
}

@ -3,30 +3,28 @@ import React from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { CorporationUnlockUpgrade } from "../data/CorporationUnlockUpgrades";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { useCorporation } from "./Context";
import { UnlockUpgrade as UU } from "../Actions";
import { MoneyCost } from "./MoneyCost";
interface IProps {
upgradeData: CorporationUnlockUpgrade;
corp: ICorporation;
player: IPlayer;
rerender: () => void;
}
export function UnlockUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation();
const data = props.upgradeData;
const text = (
<>
{data[2]} - <MoneyCost money={data[1]} corp={props.corp} />
{data[2]} - <MoneyCost money={data[1]} corp={corp} />
</>
);
const tooltip = data[3];
function onClick(): void {
if (props.corp.funds.lt(data[1])) return;
if (corp.funds.lt(data[1])) return;
try {
UU(props.corp, props.upgradeData);
UU(corp, props.upgradeData);
} catch (err) {
dialogBoxCreate(err + "");
}

@ -15,8 +15,7 @@ import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { Location } from "../Location";
import { CreateCorporationPopup } from "../../Corporation/ui/CreateCorporationPopup";
import { createPopup } from "../../ui/React/createPopup";
import { CreateCorporationModal } from "../../Corporation/ui/CreateCorporationModal";
import { LocationName } from "../data/LocationNames";
import { use } from "../../ui/Context";
@ -32,17 +31,6 @@ export function SpecialLocation(props: IProps): React.ReactElement {
const router = use.Router();
const setRerender = useState(false)[1];
const inBladeburner = player.inBladeburner();
/**
* Click handler for "Create Corporation" button at Sector-12 City Hall
*/
function createCorporationPopup(): void {
const popupId = `create-start-corporation-popup`;
createPopup(popupId, CreateCorporationPopup, {
player: player,
popupId: popupId,
router: router,
});
}
/**
* Click handler for Bladeburner button at Sector-12 NSA
@ -93,7 +81,8 @@ export function SpecialLocation(props: IProps): React.ReactElement {
return <Button onClick={EatNoodles}>Eat noodles</Button>;
}
function renderCreateCorporation(): React.ReactElement {
function CreateCorporation(): React.ReactElement {
const [open, setOpen] = useState(false);
if (!player.canAccessCorporation()) {
return (
<>
@ -104,9 +93,12 @@ export function SpecialLocation(props: IProps): React.ReactElement {
);
}
return (
<Button disabled={!player.canAccessCorporation() || player.hasCorporation()} onClick={createCorporationPopup}>
<>
<Button disabled={!player.canAccessCorporation() || player.hasCorporation()} onClick={() => setOpen(true)}>
Create a Corporation
</Button>
<CreateCorporationModal open={open} onClose={() => setOpen(false)} />
</>
);
}
@ -122,7 +114,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
return renderResleeving();
}
case LocationName.Sector12CityHall: {
return renderCreateCorporation();
return <CreateCorporation />;
}
case LocationName.Sector12NSA: {
return renderBladeburner();