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 ( 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="General" />
<Tab label="Contracts" /> <Tab label="Contracts" />
<Tab label="Operations" /> <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 React, { useState } from "react";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { Modal } from "../../ui/React/Modal";
import { removePopup } from "../../ui/React/createPopup";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { ICorporation } from "../ICorporation"; import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
interface IProps { interface IProps {
player: IPlayer; open: boolean;
popupId: string; onClose: () => void;
corp: ICorporation;
rerender: () => void; rerender: () => void;
} }
// Create a popup that lets the player buyback shares // Create a popup that lets the player buyback shares
// This is created when the player clicks the "Buyback Shares" button in the overview panel // 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); const [shares, setShares] = useState<number | null>(null);
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void { 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))); else setShares(Math.round(parseFloat(event.target.value)));
} }
const currentStockPrice = props.corp.sharePrice; const currentStockPrice = corp.sharePrice;
const buybackPrice = currentStockPrice * 1.1; const buybackPrice = currentStockPrice * 1.1;
function buy(): void { function buy(): void {
if (shares === null) return; if (shares === null) return;
const tempStockPrice = props.corp.sharePrice; const tempStockPrice = corp.sharePrice;
const buybackPrice = tempStockPrice * 1.1; const buybackPrice = tempStockPrice * 1.1;
if (isNaN(shares) || shares <= 0) { if (isNaN(shares) || shares <= 0) {
dialogBoxCreate("ERROR: Invalid value for number of shares"); 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"); 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( dialogBoxCreate(
"ERROR: You do not have enough money to purchase this many shares (you need " + "ERROR: You do not have enough money to purchase this many shares (you need " +
numeralWrapper.format(shares * buybackPrice, "$0.000a") + numeralWrapper.format(shares * buybackPrice, "$0.000a") +
")", ")",
); );
} else { } else {
props.corp.numShares += shares; corp.numShares += shares;
if (isNaN(props.corp.issuedShares)) { if (isNaN(corp.issuedShares)) {
console.warn("Corporation issuedShares is NaN: " + props.corp.issuedShares); console.warn("Corporation issuedShares is NaN: " + corp.issuedShares);
console.warn("Converting to number now"); console.warn("Converting to number now");
const res = props.corp.issuedShares; const res = corp.issuedShares;
if (isNaN(res)) { if (isNaN(res)) {
props.corp.issuedShares = 0; corp.issuedShares = 0;
} else { } else {
props.corp.issuedShares = res; corp.issuedShares = res;
} }
} }
props.corp.issuedShares -= shares; corp.issuedShares -= shares;
props.player.loseMoney(shares * buybackPrice); player.loseMoney(shares * buybackPrice);
removePopup(props.popupId); props.onClose();
props.rerender(); props.rerender();
} }
} }
@ -62,11 +63,11 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
if (shares === null) return <></>; if (shares === null) return <></>;
if (isNaN(shares) || shares <= 0) { if (isNaN(shares) || shares <= 0) {
return <>ERROR: Invalid value entered for number of shares to buyback</>; return <>ERROR: Invalid value entered for number of shares to buyback</>;
} else if (shares > props.corp.issuedShares) { } else if (shares > corp.issuedShares) {
return ( return (
<> <>
There are not this many shares available to buy back. There are only{" "} 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 { } else {
@ -83,7 +84,7 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <p>
Enter the number of outstanding shares you would like to buy back. These shares must be bought at a 10% premium. 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. 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 />
<br /> <br />
The current buyback price of your company's stock is {numeralWrapper.formatMoney(buybackPrice)}. Your company 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> </p>
<CostIndicator /> <CostIndicator />
<br /> <br />
@ -109,6 +110,6 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
<button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}> <button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}>
Buy shares Buy shares
</button> </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 // 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 // divisions, see an overview of your corporation, or create a new industry
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { HeaderTab } from "./HeaderTab";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { NewIndustryPopup } from "./NewIndustryPopup";
import { createPopup } from "../../ui/React/createPopup";
import { ICorporation } from "../ICorporation";
import { MainPanel } from "./MainPanel"; import { MainPanel } from "./MainPanel";
import { Industries } from "../IndustryData"; import { Industries } from "../IndustryData";
import { ExpandIndustryTab } from "./ExpandIndustryTab";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { Context } from "./Context";
import { Overview } from "./Overview";
interface IExpandButtonProps { import Tabs from "@mui/material/Tabs";
corp: ICorporation; import Tab from "@mui/material/Tab";
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"} />;
}
export function CorporationRoot(): React.ReactElement { export function CorporationRoot(): React.ReactElement {
const player = use.Player(); const player = use.Player();
@ -46,33 +21,37 @@ export function CorporationRoot(): React.ReactElement {
function rerender(): void { function rerender(): void {
setRerender((old) => !old); 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(() => { useEffect(() => {
const id = setInterval(rerender, 1000); const id = setInterval(rerender, 200);
return () => clearInterval(id); return () => clearInterval(id);
}, []); }, []);
const canExpand =
Object.keys(Industries).filter(
(industryType: string) =>
corporation.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
).length > 0;
return ( return (
<div className="cmpy-mgmt-container"> <Context.Corporation.Provider value={corporation}>
<div> <div className="cmpy-mgmt-container">
<HeaderTab <Tabs variant="fullWidth" value={divisionName} onChange={handleChange}>
current={divisionName === "Overview"} <Tab label={corporation.name} value={"Overview"} />
key={"overview"} {corporation.divisions.map((div) => (
onClick={() => setDivisionName("Overview")} <Tab key={div.name} label={div.name} value={div.name} />
text={corporation.name} ))}
/> {canExpand && <Tab label={"Expand"} value={-1} />}
{corporation.divisions.map((division: IIndustry) => ( </Tabs>
<HeaderTab {divisionName === "Overview" && <Overview rerender={rerender} />}
current={division.name === divisionName} {divisionName === -1 && <ExpandIndustryTab setDivisionName={setDivisionName} />}
key={division.name} {typeof divisionName === "string" && divisionName !== "Overview" && (
onClick={() => setDivisionName(division.name)} <MainPanel rerender={rerender} divisionName={divisionName + ""} />
text={division.name} )}
/>
))}
<ExpandButton corp={corporation} setDivisionName={setDivisionName} />
</div> </div>
<MainPanel rerender={rerender} corp={corporation} divisionName={divisionName} player={player} /> </Context.Corporation.Provider>
</div>
); );
} }

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

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

@ -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 React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { ICorporation } from "../ICorporation";
import { IssueDividends } from "../Actions"; 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 { interface IProps {
popupId: string; open: boolean;
corp: ICorporation; onClose: () => void;
} }
// Create a popup that lets the player issue & manage dividends // 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 // This is created when the player clicks the "Issue Dividends" button in the overview panel
export function IssueDividendsPopup(props: IProps): React.ReactElement { export function IssueDividendsModal(props: IProps): React.ReactElement {
const [percent, setPercent] = useState<number | null>(null); const corp = useCorporation();
const [percent, setPercent] = useState(0);
const canIssue = !isNaN(percent) && percent >= 0 && percent <= CorporationConstants.DividendMaxPercentage * 100;
function issueDividends(): void { function issueDividends(): void {
if (!canIssue) return;
if (percent === null) return; if (percent === null) return;
try { try {
IssueDividends(props.corp, percent / 100); IssueDividends(corp, percent / 100);
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }
removePopup(props.popupId); props.onClose();
} }
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void { 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 { function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setPercent(null); if (event.target.value === "") setPercent(0);
else setPercent(parseFloat(event.target.value)); else {
let p = parseFloat(event.target.value);
if (p > 50) p = 50;
setPercent(p);
}
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <Typography>
Dividends are a distribution of a portion of the corporation's profits to the shareholders. This includes Dividends are a distribution of a portion of the corporation's profits to the shareholders. This includes
yourself, as well. yourself, as well.
<br /> <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 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 dividends. Since your corporation starts with 1 billion shares, every shareholder will be paid $0.04 per share
per second before taxes. per second before taxes.
</p> </Typography>
<input <TextField
autoFocus={true} variant="standard"
autoFocus
value={percent}
onChange={onChange} onChange={onChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
className="text-input"
placeholder="Dividend %" placeholder="Dividend %"
type="number" 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 Allocate Dividend Percentage
</button> </Button>
</> </Modal>
); );
} }

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

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

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

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

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

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

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