Merge pull request #1392 from danielyxie/dev

Corporations in Mui
This commit is contained in:
hydroflame 2021-09-30 17:24:32 -04:00 committed by GitHub
commit d4a28cd0b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2352 additions and 2520 deletions

38
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -3,10 +3,43 @@ import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { BitNodes } from "../BitNode"; import { BitNodes } from "../BitNode";
import { enterBitNode, setRedPillFlag } from "../../RedPill"; import { enterBitNode, setRedPillFlag } from "../../RedPill";
import { PortalPopup } from "./PortalPopup"; import { PortalModal } from "./PortalModal";
import { createPopup } from "../../ui/React/createPopup";
import { CinematicText } from "../../ui/React/CinematicText"; import { CinematicText } from "../../ui/React/CinematicText";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { Theme } from "@mui/material";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
level0: {
color: "red",
"&:hover": {
color: "#fff",
},
},
level1: {
color: "yellow",
"&:hover": {
color: "#fff",
},
},
level2: {
color: "#48d1cc",
"&:hover": {
color: "#fff",
},
},
level3: {
color: "blue",
"&:hover": {
color: "#fff",
},
},
}),
);
interface IPortalProps { interface IPortalProps {
n: number; n: number;
@ -16,51 +49,51 @@ interface IPortalProps {
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void; enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
} }
function BitNodePortal(props: IPortalProps): React.ReactElement { function BitNodePortal(props: IPortalProps): React.ReactElement {
const [portalOpen, setPortalOpen] = useState(false);
const classes = useStyles();
const router = use.Router(); const router = use.Router();
const bitNode = BitNodes[`BitNode${props.n}`]; const bitNode = BitNodes[`BitNode${props.n}`];
if (bitNode == null) { if (bitNode == null) {
return <>O</>; return <>O</>;
} }
let cssClass; let cssClass = classes.level0;
if (props.n === 12 && props.level >= 2) { if (props.n === 12 && props.level >= 2) {
// Repeating BitNode // Repeating BitNode
cssClass = "level-2"; cssClass = classes.level2;
} else if (props.level === 1) {
cssClass = classes.level1;
} else { } else {
cssClass = `level-${props.level}`; cssClass = classes.level3;
}
function openPortalPopup(): void {
const popupId = "bitverse-portal-popup";
createPopup(popupId, PortalPopup, {
n: props.n,
level: props.level,
enter: props.enter,
router: router,
destroyedBitNode: props.destroyedBitNode,
flume: props.flume,
popupId: popupId,
});
} }
return ( return (
<button <>
className={`bitnode ${cssClass} tooltip`} <Tooltip
aria-label={`enter-bitnode-${bitNode.number.toString()}`} title={
onClick={openPortalPopup} <Typography>
> <strong>
<strong>O</strong> BitNode-{bitNode.number.toString()}: {bitNode.name}
<span className="tooltiptext"> </strong>
<strong> <br />
BitNode-{bitNode.number.toString()} {bitNode.desc}
<br /> </Typography>
{bitNode.name} }
</strong> >
<br /> <span onClick={() => setPortalOpen(true)} className={cssClass}>
{bitNode.desc} <b>O</b>
<br /> </span>
</span> </Tooltip>
</button> <PortalModal
open={portalOpen}
onClose={() => setPortalOpen(false)}
n={props.n}
level={props.level}
enter={props.enter}
destroyedBitNode={props.destroyedBitNode}
flume={props.flume}
/>
</>
); );
} }
@ -116,31 +149,31 @@ export function BitverseRoot(props: IProps): React.ReactElement {
return ( return (
// prettier-ignore // prettier-ignore
<div className="noselect"> <div className="noselect">
<pre> O </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
<pre> | O O | O O | </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
<pre> O | | / __| \ | | O </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
<pre> O | O | | O / | O | | O | O </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
<pre> | | | | |_/ |/ | \_ \_| | | | | </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
<pre> O | | | O | | O__/ | / \__ | | O | | | O </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | O | | O__/ | / \__ | | O | | | O </Typography>
<pre> | | | | | | | / /| O / \| | | | | | | </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<pre>| | | |O / | | O / | O O | | \ O| | | |</pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<pre>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<pre> \| O | |_/ |\| \ O \__| \_| | O |/ </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ O \__| \_| | O |/ </Typography>
<pre> | | |_/ | | \| / | \_| | | </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<pre> \| / \| | / / \ |/ </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<pre> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
<pre> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<pre> | | | / / \ \ | | | </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
<pre> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
<pre> \ | / / | | \ \ | / </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
<pre> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
<pre> \|| | | | | | | | | ||/ </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
<pre> \| \_ | | | | | | _/ |/ </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
<pre> \ \| / \ / \ |/ / </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
<pre> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
<pre> | | | | | | | | </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
<pre> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </pre> <Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
<br /> <br />
<br /> <br />
<br /> <br />

@ -2,18 +2,23 @@ import React from "react";
import { BitNodes } from "../BitNode"; import { BitNodes } from "../BitNode";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { removePopup } from "../../ui/React/createPopup"; import { use } from "../../ui/Context";
import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps { interface IProps {
open: boolean;
onClose: () => void;
n: number; n: number;
level: number; level: number;
destroyedBitNode: number; destroyedBitNode: number;
flume: boolean; flume: boolean;
router: IRouter;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void; enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
popupId: string;
} }
export function PortalPopup(props: IProps): React.ReactElement { export function PortalModal(props: IProps): React.ReactElement {
const router = use.Router();
const bitNodeKey = "BitNode" + props.n; const bitNodeKey = "BitNode" + props.n;
const bitNode = BitNodes[bitNodeKey]; const bitNode = BitNodes[bitNodeKey];
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`); if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
@ -21,29 +26,30 @@ export function PortalPopup(props: IProps): React.ReactElement {
const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3); const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3);
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<h1> <Typography variant="h4">
BitNode-{props.n}: {bitNode.name} BitNode-{props.n}: {bitNode.name}
</h1> </Typography>
<br /> <br />
Source-File Level: {props.level} / {maxSourceFileLevel} <Typography>
Source-File Level: {props.level} / {maxSourceFileLevel}
</Typography>
<br /> <br />
<br /> <br />
Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]} <Typography> Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}</Typography>
<br /> <br />
<br /> <br />
{bitNode.info} <Typography>{bitNode.info}</Typography>
<br /> <br />
<br /> <br />
<button <Button
className="std-button"
onClick={() => { onClick={() => {
props.enter(props.router, props.flume, props.destroyedBitNode, props.n); props.enter(router, props.flume, props.destroyedBitNode, props.n);
removePopup(props.popupId); props.onClose();
}} }}
> >
Enter BN{props.n}.{newLevel} Enter BN{props.n}.{newLevel}
</button> </Button>
</> </Modal>
); );
} }

@ -139,74 +139,6 @@ export class Employee {
return mult; return mult;
} }
//'panel' is the DOM element on which to create the UI
createUI(panel: HTMLElement, corporation: ICorporation, industry: IIndustry): void {
const effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(),
effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(),
effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(),
effEff = this.eff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier();
panel.style.color = "white";
panel.appendChild(
createElement("p", {
id: "cmpy-mgmt-employee-" + this.name + "-panel-text",
innerHTML:
"Morale: " +
formatNumber(this.mor, 3) +
"<br>" +
"Happiness: " +
formatNumber(this.hap, 3) +
"<br>" +
"Energy: " +
formatNumber(this.ene, 3) +
"<br>" +
"Intelligence: " +
formatNumber(effInt, 3) +
"<br>" +
"Charisma: " +
formatNumber(effCha, 3) +
"<br>" +
"Experience: " +
formatNumber(this.exp, 3) +
"<br>" +
"Creativity: " +
formatNumber(effCre, 3) +
"<br>" +
"Efficiency: " +
formatNumber(effEff, 3) +
"<br>" +
"Salary: " +
numeralWrapper.format(this.sal, "$0.000a") +
"/ s<br>",
}),
);
//Selector for employee position
const selector = createElement("select", {}) as HTMLSelectElement;
for (const key in EmployeePositions) {
if (EmployeePositions.hasOwnProperty(key)) {
selector.add(
createElement("option", {
text: EmployeePositions[key],
value: EmployeePositions[key],
}) as HTMLOptionElement,
);
}
}
selector.addEventListener("change", () => {
this.pos = selector.options[selector.selectedIndex].value;
});
//Set initial value of selector
for (let i = 0; i < selector.length; ++i) {
if (selector.options[i].value === this.pos) {
selector.selectedIndex = i;
break;
}
}
panel.appendChild(selector);
}
copy(): Employee { copy(): Employee {
const employee = new Employee(); const employee = new Employee();
employee.name = this.name; employee.name = this.name;

@ -385,9 +385,6 @@ export class Industry implements IIndustry {
const prod = this.products[prodName]; const prod = this.products[prodName];
if (prod === undefined) continue; if (prod === undefined) continue;
warehouse.sizeUsed += prod.data[warehouse.loc][0] * prod.siz; warehouse.sizeUsed += prod.data[warehouse.loc][0] * prod.siz;
if (prod.data[warehouse.loc][0] > 0) {
warehouse.breakdown += prodName + ": " + formatNumber(prod.data[warehouse.loc][0] * prod.siz, 0) + "<br>";
}
} }
} }
} }

@ -122,7 +122,6 @@ export class OfficeSpace {
if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) return; if (document.getElementById("cmpy-mgmt-hire-employee-popup") != null) return;
//Generate three random employees (meh, decent, amazing) //Generate three random employees (meh, decent, amazing)
const mult = getRandomInt(76, 100) / 100;
const int = getRandomInt(50, 100), const int = getRandomInt(50, 100),
cha = getRandomInt(50, 100), cha = getRandomInt(50, 100),
exp = getRandomInt(50, 100), exp = getRandomInt(50, 100),
@ -131,12 +130,12 @@ export class OfficeSpace {
sal = CorporationConstants.EmployeeSalaryMultiplier * (int + cha + exp + cre + eff); sal = CorporationConstants.EmployeeSalaryMultiplier * (int + cha + exp + cre + eff);
const emp = new Employee({ const emp = new Employee({
intelligence: int * mult, intelligence: int,
charisma: cha * mult, charisma: cha,
experience: exp * mult, experience: exp,
creativity: cre * mult, creativity: cre,
efficiency: eff * mult, efficiency: eff,
salary: sal * mult, salary: sal,
}); });
const name = generateRandomString(7); const name = generateRandomString(7);

@ -15,10 +15,6 @@ interface IConstructorParams {
} }
export class Warehouse { export class Warehouse {
// Text that describes how the space in this Warehouse is being used
// Used to create a tooltip in the UI
breakdown = "";
// Warehouse's level, which affects its maximum size // Warehouse's level, which affects its maximum size
level = 1; level = 1;
@ -90,14 +86,10 @@ export class Warehouse {
// Re-calculate how much space is being used by this Warehouse // Re-calculate how much space is being used by this Warehouse
updateMaterialSizeUsed(): void { updateMaterialSizeUsed(): void {
this.sizeUsed = 0; this.sizeUsed = 0;
this.breakdown = "";
for (const matName in this.materials) { for (const matName in this.materials) {
const mat = this.materials[matName]; const mat = this.materials[matName];
if (MaterialSizes.hasOwnProperty(matName)) { if (MaterialSizes.hasOwnProperty(matName)) {
this.sizeUsed += mat.qty * MaterialSizes[matName]; this.sizeUsed += mat.qty * MaterialSizes[matName];
if (mat.qty > 0) {
this.breakdown += matName + ": " + numeralWrapper.format(mat.qty * MaterialSizes[matName], "0,0.0") + "<br>";
}
} }
} }
if (this.sizeUsed > this.size) { if (this.sizeUsed > this.size) {

@ -20,12 +20,26 @@ interface IProps {
export function BribeFactionModal(props: IProps): React.ReactElement { export function BribeFactionModal(props: IProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
const factions = player.factions.filter((name: string) => {
const info = Factions[name].getInfo();
if (!info.offersWork()) return false;
if (player.hasGangWith(name)) return false;
return true;
});
const corp = useCorporation(); const corp = useCorporation();
const [money, setMoney] = useState<number | null>(0); const [money, setMoney] = useState<number | null>(0);
const [stock, setStock] = useState<number | null>(0); const [stock, setStock] = useState<number | null>(0);
const [selectedFaction, setSelectedFaction] = useState( const [selectedFaction, setSelectedFaction] = useState(factions.length > 0 ? factions[0] : "");
player.factions.length > 0 ? player.factions.filter((faction) => Factions[faction].getInfo().offersWork())[0] : "", const disabled =
); money === null ||
stock === null ||
(money === 0 && stock === 0) ||
isNaN(money) ||
isNaN(stock) ||
money < 0 ||
stock < 0 ||
corp.funds.lt(money) ||
stock > corp.numShares;
function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void { function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void {
setMoney(parseFloat(event.target.value)); setMoney(parseFloat(event.target.value));
@ -64,22 +78,15 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
function bribe(money: number, stock: number): void { function bribe(money: number, stock: number): void {
const fac = Factions[selectedFaction]; const fac = Factions[selectedFaction];
if (fac == null) { if (disabled) return;
dialogBoxCreate("ERROR: You must select a faction to bribe"); const rep = repGain(money, stock);
} dialogBoxCreate(
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) { "You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
} else if (corp.funds.lt(money)) { );
} else if (stock > corp.numShares) { fac.playerReputation += rep;
} else { corp.funds = corp.funds.minus(money);
const rep = repGain(money, stock); corp.numShares -= stock;
dialogBoxCreate( props.onClose();
"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 ( return (
@ -90,9 +97,10 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Typography>Faction:</Typography> <Typography>Faction:</Typography>
<Select value={selectedFaction} onChange={changeFaction}> <Select value={selectedFaction} onChange={changeFaction}>
{player.factions.map((name: string) => { {factions.map((name: string) => {
const info = Factions[name].getInfo(); const info = Factions[name].getInfo();
if (!info.offersWork()) return; if (!info.offersWork()) return;
if (player.hasGangWith(name)) return;
return ( return (
<MenuItem key={name} value={name}> <MenuItem key={name} value={name}>
{name} {name}
@ -104,7 +112,7 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
<Typography>{getRepText(money ? money : 0, stock ? stock : 0)}</Typography> <Typography>{getRepText(money ? money : 0, stock ? stock : 0)}</Typography>
<TextField onChange={onMoneyChange} placeholder="Corporation funds" /> <TextField onChange={onMoneyChange} placeholder="Corporation funds" />
<TextField sx={{ mx: 1 }} onChange={onStockChange} placeholder="Stock Shares" /> <TextField sx={{ mx: 1 }} onChange={onStockChange} placeholder="Stock Shares" />
<Button sx={{ mx: 1 }} onClick={() => bribe(money ? money : 0, stock ? stock : 0)}> <Button disabled={disabled} sx={{ mx: 1 }} onClick={() => bribe(money ? money : 0, stock ? stock : 0)}>
Bribe Bribe
</Button> </Button>
</Modal> </Modal>

@ -4,6 +4,9 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
interface IProps { interface IProps {
open: boolean; open: boolean;
@ -25,38 +28,30 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
const currentStockPrice = corp.sharePrice; const currentStockPrice = corp.sharePrice;
const buybackPrice = currentStockPrice * 1.1; const buybackPrice = currentStockPrice * 1.1;
const disabled =
shares === null ||
isNaN(shares) ||
shares <= 0 ||
shares > corp.issuedShares ||
shares * buybackPrice > player.money;
function buy(): void { function buy(): void {
if (shares === null) return; if (shares === null) return;
const tempStockPrice = corp.sharePrice; corp.numShares += shares;
const buybackPrice = tempStockPrice * 1.1; if (isNaN(corp.issuedShares)) {
if (isNaN(shares) || shares <= 0) { console.warn("Corporation issuedShares is NaN: " + corp.issuedShares);
dialogBoxCreate("ERROR: Invalid value for number of shares"); console.warn("Converting to number now");
} else if (shares > corp.issuedShares) { const res = corp.issuedShares;
dialogBoxCreate("ERROR: There are not this many oustanding shares to buy back"); if (isNaN(res)) {
} else if (shares * buybackPrice > player.money) { corp.issuedShares = 0;
dialogBoxCreate( } else {
"ERROR: You do not have enough money to purchase this many shares (you need " + corp.issuedShares = res;
numeralWrapper.format(shares * buybackPrice, "$0.000a") +
")",
);
} else {
corp.numShares += shares;
if (isNaN(corp.issuedShares)) {
console.warn("Corporation issuedShares is NaN: " + corp.issuedShares);
console.warn("Converting to number now");
const res = corp.issuedShares;
if (isNaN(res)) {
corp.issuedShares = 0;
} else {
corp.issuedShares = res;
}
} }
corp.issuedShares -= shares;
player.loseMoney(shares * buybackPrice);
props.onClose();
props.rerender();
} }
corp.issuedShares -= shares;
player.loseMoney(shares * buybackPrice);
props.onClose();
props.rerender();
} }
function CostIndicator(): React.ReactElement { function CostIndicator(): React.ReactElement {
@ -85,7 +80,7 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<p> <Typography>
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.
<br /> <br />
@ -95,21 +90,19 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
<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(corp.issuedShares)} outstanding stock shares. currently has {numeralWrapper.formatBigNumber(corp.issuedShares)} outstanding stock shares.
</p> </Typography>
<CostIndicator /> <CostIndicator />
<br /> <br />
<input <TextField
autoFocus={true} autoFocus={true}
className="text-input"
type="number" type="number"
placeholder="Shares to buyback" placeholder="Shares to buyback"
style={{ margin: "5px" }}
onChange={changeShares} onChange={changeShares}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}> <Button disabled={disabled} onClick={buy}>
Buy shares Buy shares
</button> </Button>
</Modal> </Modal>
); );
} }

@ -1,29 +1,27 @@
import React from "react"; import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { ICorporation } from "../ICorporation";
import { Product } from "../Product"; import { Product } from "../Product";
import { IIndustry } from "../IIndustry"; import { Modal } from "../../ui/React/Modal";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { useDivision } from "./Context";
interface IProps { interface IProps {
open: boolean;
onClose: () => void;
product: Product; product: Product;
industry: IIndustry;
corp: ICorporation;
popupId: string;
player: IPlayer;
rerender: () => void; rerender: () => void;
} }
// Create a popup that lets the player discontinue a product // Create a popup that lets the player discontinue a product
export function DiscontinueProductPopup(props: IProps): React.ReactElement { export function DiscontinueProductModal(props: IProps): React.ReactElement {
const division = useDivision();
function discontinue(): void { function discontinue(): void {
props.industry.discontinueProduct(props.product); division.discontinueProduct(props.product);
removePopup(props.popupId); props.onClose();
props.rerender(); props.rerender();
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <p>
Are you sure you want to do this? Discontinuing a product removes it completely and permanently. You will no Are you sure you want to do this? Discontinuing a product removes it completely and permanently. You will no
longer produce this product and all of its existing stock will be removed and left unsold longer produce this product and all of its existing stock will be removed and left unsold
@ -31,6 +29,6 @@ export function DiscontinueProductPopup(props: IProps): React.ReactElement {
<button className="popup-box-button" onClick={discontinue}> <button className="popup-box-button" onClick={discontinue}>
Discontinue Discontinue
</button> </button>
</> </Modal>
); );
} }

@ -1,6 +1,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Industries, IndustryDescriptions } from "../IndustryData"; import { IndustryStartingCosts, Industries, IndustryDescriptions } from "../IndustryData";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { NewIndustry } from "../Actions"; import { NewIndustry } from "../Actions";
@ -28,7 +28,14 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : ""); const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : "");
const [name, setName] = useState(""); const [name, setName] = useState("");
const cost = IndustryStartingCosts[industry];
if (cost === undefined) {
throw new Error(`Invalid industry: '${industry}'`);
}
const disabled = corp.funds.lt(cost) || name === "";
function newIndustry(): void { function newIndustry(): void {
if (disabled) return;
try { try {
NewIndustry(corp, industry, name); NewIndustry(corp, industry, name);
} catch (err) { } catch (err) {
@ -74,7 +81,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<TextField autoFocus={true} value={name} onChange={onNameChange} onKeyDown={onKeyDown} type="text" /> <TextField autoFocus={true} value={name} onChange={onNameChange} onKeyDown={onKeyDown} type="text" />
<Button sx={{ mx: 1 }} onClick={newIndustry}> <Button disabled={disabled} sx={{ mx: 1 }} onClick={newIndustry}>
Create Division Create Division
</Button> </Button>
</Box> </Box>

@ -19,6 +19,8 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
const possibleCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] === 0); const possibleCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] === 0);
const [city, setCity] = useState(possibleCities[0]); const [city, setCity] = useState(possibleCities[0]);
const disabled = corp.funds.lt(CorporationConstants.OfficeInitialCost);
function onCityChange(event: SelectChangeEvent<string>): void { function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value); setCity(event.target.value);
} }
@ -48,7 +50,7 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
<Button onClick={expand} disabled={corp.funds.lt(0)}> <Button onClick={expand} disabled={disabled}>
Confirm Confirm
</Button> </Button>
</> </>

@ -1,25 +1,32 @@
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 { ICorporation } from "../ICorporation";
import { Material } from "../Material"; import { Material } from "../Material";
import { Export } from "../Export"; import { Export } from "../Export";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { ExportMaterial } from "../Actions"; import { ExportMaterial } from "../Actions";
import { Modal } from "../../ui/React/Modal";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
interface IProps { interface IProps {
open: boolean;
onClose: () => void;
mat: Material; mat: Material;
corp: ICorporation;
popupId: string;
} }
// Create a popup that lets the player manage exports // Create a popup that lets the player manage exports
export function ExportPopup(props: IProps): React.ReactElement { export function ExportModal(props: IProps): React.ReactElement {
if (props.corp.divisions.length === 0) throw new Error("Export popup created with no divisions."); const corp = useCorporation();
if (Object.keys(props.corp.divisions[0].warehouses).length === 0) if (corp.divisions.length === 0) throw new Error("Export popup created with no divisions.");
if (Object.keys(corp.divisions[0].warehouses).length === 0)
throw new Error("Export popup created in a division with no warehouses."); throw new Error("Export popup created in a division with no warehouses.");
const [industry, setIndustry] = useState<string>(props.corp.divisions[0].name); const [industry, setIndustry] = useState<string>(corp.divisions[0].name);
const [city, setCity] = useState<string>(Object.keys(props.corp.divisions[0].warehouses)[0]); const [city, setCity] = useState<string>(Object.keys(corp.divisions[0].warehouses)[0]);
const [amt, setAmt] = useState(""); const [amt, setAmt] = useState("");
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
@ -27,11 +34,11 @@ export function ExportPopup(props: IProps): React.ReactElement {
setRerender((old) => !old); setRerender((old) => !old);
} }
function onCityChange(event: React.ChangeEvent<HTMLSelectElement>): void { function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value); setCity(event.target.value);
} }
function onIndustryChange(event: React.ChangeEvent<HTMLSelectElement>): void { function onIndustryChange(event: SelectChangeEvent<string>): void {
setIndustry(event.target.value); setIndustry(event.target.value);
} }
@ -45,7 +52,7 @@ export function ExportPopup(props: IProps): React.ReactElement {
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }
removePopup(props.popupId); props.onClose();
} }
function removeExport(exp: Export): void { function removeExport(exp: Export): void {
@ -58,7 +65,7 @@ export function ExportPopup(props: IProps): React.ReactElement {
rerender(); rerender();
} }
const currentDivision = props.corp.divisions.find((division: IIndustry) => division.name === industry); const currentDivision = corp.divisions.find((division: IIndustry) => division.name === industry);
if (currentDivision === undefined) if (currentDivision === undefined)
throw new Error(`Export popup somehow ended up with undefined division '${currentDivision}'`); throw new Error(`Export popup somehow ended up with undefined division '${currentDivision}'`);
const possibleCities = Object.keys(currentDivision.warehouses).filter( const possibleCities = Object.keys(currentDivision.warehouses).filter(
@ -69,45 +76,48 @@ export function ExportPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <Typography>
Select the industry and city to export this material to, as well as how much of this material to export per Select the industry and city to export this material to, as well as how much of this material to export per
second. You can set the export amount to 'MAX' to export all of the materials in this warehouse. second. You can set the export amount to 'MAX' to export all of the materials in this warehouse.
</p> </Typography>
<select className="dropdown" onChange={onIndustryChange} defaultValue={industry}> <Select onChange={onIndustryChange} value={industry}>
{props.corp.divisions.map((division: IIndustry) => ( {corp.divisions.map((division: IIndustry) => (
<option key={division.name} value={division.name}> <MenuItem key={division.name} value={division.name}>
{division.name} {division.name}
</option> </MenuItem>
))} ))}
</select> </Select>
<select className="dropdown" onChange={onCityChange} defaultValue={city}> <Select onChange={onCityChange} value={city}>
{possibleCities.map((cityName: string) => { {possibleCities.map((cityName: string) => {
if (currentDivision.warehouses[cityName] === 0) return; if (currentDivision.warehouses[cityName] === 0) return;
return ( return (
<option key={cityName} value={cityName}> <MenuItem key={cityName} value={cityName}>
{cityName} {cityName}
</option> </MenuItem>
); );
})} })}
</select> </Select>
<input className="text-input" placeholder="Export amount / s" onChange={onAmtChange} /> <TextField placeholder="Export amount / s" onChange={onAmtChange} value={amt} />
<button className="std-button" style={{ display: "inline-block" }} onClick={exportMaterial}> <Button onClick={exportMaterial}>Export</Button>
Export <Typography>
</button>
<p>
Below is a list of all current exports of this material from this warehouse. Clicking on one of the exports Below is a list of all current exports of this material from this warehouse. Clicking on one of the exports
below will REMOVE that export. below will REMOVE that export.
</p> </Typography>
{props.mat.exp.map((exp: Export, index: number) => ( {props.mat.exp.map((exp: Export, index: number) => (
<div key={index} className="cmpy-mgmt-existing-export" onClick={() => removeExport(exp)}> <Box display="flex" alignItems="center" key={index}>
Industry: {exp.ind} <Button sx={{ mx: 2 }} onClick={() => removeExport(exp)}>
<br /> delete
City: {exp.city} </Button>
<br /> <Typography>
Amount/s: {exp.amt} Industry: {exp.ind}
</div> <br />
City: {exp.city}
<br />
Amount/s: {exp.amt}
</Typography>
</Box>
))} ))}
</> </Modal>
); );
} }

@ -1,182 +0,0 @@
import React, { useState } from "react";
import { createPopup, removePopup } from "../../ui/React/createPopup";
import { numeralWrapper } from "../../ui/numeralFormat";
import { CorporationConstants } from "../data/Constants";
import { ICorporation } from "../ICorporation";
import { OfficeSpace } from "../OfficeSpace";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { formatNumber } from "../../utils/StringHelperFunctions";
import { Employee } from "../Employee";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
interface INameEmployeeProps {
office: OfficeSpace;
corp: ICorporation;
popupId: string;
employee: Employee;
player: IPlayer;
rerender: () => void;
}
function NameEmployeePopup(props: INameEmployeeProps): React.ReactElement {
const [name, setName] = useState("");
function nameEmployee(): void {
for (let i = 0; i < props.office.employees.length; ++i) {
if (props.office.employees[i].name === name) {
dialogBoxCreate("You already have an employee with this nickname!");
return;
}
}
props.employee.name = name;
props.office.employees.push(props.employee);
props.rerender();
removePopup(props.popupId);
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) nameEmployee();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value);
}
return (
<>
<p>Give your employee a nickname!</p>
<input
value={name}
className="text-input"
type="text"
placeholder="Employee nickname"
onKeyDown={onKeyDown}
onChange={onChange}
/>
<button className="std-button" onClick={nameEmployee}>
Hire!
</button>
</>
);
}
interface IHireEmployeeProps {
employee: Employee;
office: OfficeSpace;
popupId: string;
player: IPlayer;
corp: ICorporation;
rerender: () => void;
}
function HireEmployeeButton(props: IHireEmployeeProps): React.ReactElement {
function hire(): void {
const popupId = "cmpy-mgmt-name-employee-popup";
createPopup(popupId, NameEmployeePopup, {
rerender: props.rerender,
office: props.office,
corp: props.corp,
popupId: popupId,
player: props.player,
employee: props.employee,
});
removePopup(props.popupId);
}
return (
<div onClick={hire} className="cmpy-mgmt-find-employee-option">
Intelligence: {formatNumber(props.employee.int, 1)}
<br />
Charisma: {formatNumber(props.employee.cha, 1)}
<br />
Experience: {formatNumber(props.employee.exp, 1)}
<br />
Creativity: {formatNumber(props.employee.cre, 1)}
<br />
Efficiency: {formatNumber(props.employee.eff, 1)}
<br />
Salary: {numeralWrapper.formatMoney(props.employee.sal)} \ s<br />
</div>
);
}
interface IProps {
office: OfficeSpace;
corp: ICorporation;
popupId: string;
player: IPlayer;
rerender: () => void;
}
// Create a popup that lets the player manage exports
export function HireEmployeePopup(props: IProps): React.ReactElement {
if (props.office.atCapacity()) return <></>;
//Generate three random employees (meh, decent, amazing)
const mult1 = getRandomInt(25, 50) / 100;
const mult2 = getRandomInt(51, 75) / 100;
const mult3 = getRandomInt(76, 100) / 100;
const int = getRandomInt(50, 100);
const cha = getRandomInt(50, 100);
const exp = getRandomInt(50, 100);
const cre = getRandomInt(50, 100);
const eff = getRandomInt(50, 100);
const sal = CorporationConstants.EmployeeSalaryMultiplier * (int + cha + exp + cre + eff);
const emp1 = new Employee({
intelligence: int * mult1,
charisma: cha * mult1,
experience: exp * mult1,
creativity: cre * mult1,
efficiency: eff * mult1,
salary: sal * mult1,
});
const emp2 = new Employee({
intelligence: int * mult2,
charisma: cha * mult2,
experience: exp * mult2,
creativity: cre * mult2,
efficiency: eff * mult2,
salary: sal * mult2,
});
const emp3 = new Employee({
intelligence: int * mult3,
charisma: cha * mult3,
experience: exp * mult3,
creativity: cre * mult3,
efficiency: eff * mult3,
salary: sal * mult3,
});
return (
<>
<h1>Select one of the following candidates for hire:</h1>
<HireEmployeeButton
rerender={props.rerender}
employee={emp1}
office={props.office}
corp={props.corp}
popupId={props.popupId}
player={props.player}
/>
<HireEmployeeButton
rerender={props.rerender}
employee={emp2}
office={props.office}
corp={props.corp}
popupId={props.popupId}
player={props.player}
/>
<HireEmployeeButton
rerender={props.rerender}
employee={emp3}
office={props.office}
corp={props.corp}
popupId={props.popupId}
player={props.player}
/>
</>
);
}

@ -9,6 +9,7 @@ import { Warehouse } from "../Warehouse";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { useCorporation, useDivision } from "./Context"; import { useCorporation, useDivision } from "./Context";
import Box from "@mui/material/Box";
interface IProps { interface IProps {
city: string; city: string;
@ -22,25 +23,12 @@ export function Industry(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const division = useDivision(); const division = useDivision();
return ( return (
<div> <Box display="flex">
<div className={"cmpy-mgmt-industry-left-panel"}> <Box sx={{ width: "50%" }}>
<IndustryOverview <IndustryOverview rerender={props.rerender} currentCity={props.city} office={props.office} />
rerender={props.rerender} <IndustryOffice rerender={props.rerender} office={props.office} />
player={player} </Box>
corp={corp} <Box sx={{ width: "50%" }}>
division={division}
currentCity={props.city}
office={props.office}
/>
<IndustryOffice
rerender={props.rerender}
player={player}
corp={corp}
division={division}
office={props.office}
/>
</div>
<div className={"cmpy-mgmt-industry-right-panel"}>
<IndustryWarehouse <IndustryWarehouse
rerender={props.rerender} rerender={props.rerender}
player={player} player={player}
@ -49,7 +37,7 @@ export function Industry(props: IProps): React.ReactElement {
division={division} division={division}
warehouse={props.warehouse} warehouse={props.warehouse}
/> />
</div> </Box>
</div> </Box>
); );
} }

@ -3,26 +3,32 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { IIndustry } from "../IIndustry";
import { Employee } from "../Employee"; import { Employee } from "../Employee";
import { EmployeePositions } from "../EmployeePositions"; import { EmployeePositions } from "../EmployeePositions";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { getSelectText } from "../../ui/uiHelpers/getSelectData"; import { UpgradeOfficeSizeModal } from "./UpgradeOfficeSizeModal";
import { createPopup } from "../../ui/React/createPopup"; import { ThrowPartyModal } from "./ThrowPartyModal";
import { UpgradeOfficeSizePopup } from "./UpgradeOfficeSizePopup";
import { HireEmployeePopup } from "./HireEmployeePopup";
import { ThrowPartyPopup } from "./ThrowPartyPopup";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import Paper from "@mui/material/Paper";
import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import Tooltip from "@mui/material/Tooltip";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableRow from "@mui/material/TableRow";
import { TableCell } from "../../ui/React/Table";
interface IProps { interface IProps {
corp: ICorporation;
division: IIndustry;
office: OfficeSpace; office: OfficeSpace;
player: IPlayer;
rerender: () => void; rerender: () => void;
} }
@ -42,45 +48,51 @@ interface ISwitchProps {
function SwitchButton(props: ISwitchProps): React.ReactElement { function SwitchButton(props: ISwitchProps): React.ReactElement {
if (props.manualMode) { if (props.manualMode) {
return ( return (
<button className={"std-button tooltip"} onClick={() => props.switchMode((old) => !old)}> <Tooltip
Switch to Auto Mode title={
<span className={"tooltiptext"}> <Typography>
Switch to Automatic Assignment Mode, which will automatically assign employees to your selected jobs. You Switch to Automatic Assignment Mode, which will automatically assign employees to your selected jobs. You
simply have to select the number of assignments for each job simply have to select the number of assignments for each job
</span> </Typography>
</button> }
>
<Button onClick={() => props.switchMode((old) => !old)}>Switch to Auto Mode</Button>
</Tooltip>
); );
} else { } else {
return ( return (
<button className={"std-button tooltip"} onClick={() => props.switchMode((old) => !old)}> <Tooltip
Switch to Manual Mode title={
<span className={"tooltiptext"}> <Typography>
Switch to Manual Assignment Mode, which allows you to specify which employees should get which jobs Switch to Manual Assignment Mode, which allows you to specify which employees should get which jobs
</span> </Typography>
</button> }
>
<Button onClick={() => props.switchMode((old) => !old)}>Switch to Manual Mode</Button>
</Tooltip>
); );
} }
} }
function ManualManagement(props: IProps): React.ReactElement { function ManualManagement(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [employee, setEmployee] = useState<Employee | null>( const [employee, setEmployee] = useState<Employee | null>(
props.office.employees.length > 0 ? props.office.employees[0] : null, props.office.employees.length > 0 ? props.office.employees[0] : null,
); );
const employeeInfoDivStyle = {
color: "white",
margin: "4px",
padding: "4px",
};
// Employee Selector // Employee Selector
const employees = []; const employees = [];
for (let i = 0; i < props.office.employees.length; ++i) { for (let i = 0; i < props.office.employees.length; ++i) {
employees.push(<option key={props.office.employees[i].name}>{props.office.employees[i].name}</option>); employees.push(
<MenuItem key={props.office.employees[i].name} value={props.office.employees[i].name}>
{props.office.employees[i].name}
</MenuItem>,
);
} }
function employeeSelectorOnChange(e: React.ChangeEvent<HTMLSelectElement>): void { function employeeSelectorOnChange(e: SelectChangeEvent<string>): void {
const name = getSelectText(e.target); const name = e.target.value;
for (let i = 0; i < props.office.employees.length; ++i) { for (let i = 0; i < props.office.employees.length; ++i) {
if (name === props.office.employees[i].name) { if (name === props.office.employees[i].name) {
setEmployee(props.office.employees[i]); setEmployee(props.office.employees[i]);
@ -98,19 +110,18 @@ function ManualManagement(props: IProps): React.ReactElement {
const positionNames = Object.values(EmployeePositions); const positionNames = Object.values(EmployeePositions);
for (let i = 0; i < positionNames.length; ++i) { for (let i = 0; i < positionNames.length; ++i) {
employeePositions.push( employeePositions.push(
<option key={positionNames[i]} value={positionNames[i]}> <MenuItem key={positionNames[i]} value={positionNames[i]}>
{positionNames[i]} {positionNames[i]}
</option>, </MenuItem>,
); );
if (emp != null && emp.pos === positionNames[i]) { if (emp != null && emp.pos === positionNames[i]) {
employeePositionSelectorInitialValue = positionNames[i]; employeePositionSelectorInitialValue = positionNames[i];
} }
} }
function employeePositionSelectorOnChange(e: React.ChangeEvent<HTMLSelectElement>): void { function employeePositionSelectorOnChange(e: SelectChangeEvent<string>): void {
if (employee === null) return; if (employee === null) return;
const pos = getSelectText(e.target); employee.pos = e.target.value;
employee.pos = pos;
props.rerender(); props.rerender();
} }
@ -118,18 +129,19 @@ function ManualManagement(props: IProps): React.ReactElement {
const nf = "0.000"; const nf = "0.000";
// Employee stats (after applying multipliers) // Employee stats (after applying multipliers)
const effCre = emp ? emp.cre * props.corp.getEmployeeCreMultiplier() * props.division.getEmployeeCreMultiplier() : 0; const effCre = emp ? emp.cre * corp.getEmployeeCreMultiplier() * division.getEmployeeCreMultiplier() : 0;
const effCha = emp ? emp.cha * props.corp.getEmployeeChaMultiplier() * props.division.getEmployeeChaMultiplier() : 0; const effCha = emp ? emp.cha * corp.getEmployeeChaMultiplier() * division.getEmployeeChaMultiplier() : 0;
const effInt = emp ? emp.int * props.corp.getEmployeeIntMultiplier() * props.division.getEmployeeIntMultiplier() : 0; const effInt = emp ? emp.int * corp.getEmployeeIntMultiplier() * division.getEmployeeIntMultiplier() : 0;
const effEff = emp ? emp.eff * props.corp.getEmployeeEffMultiplier() * props.division.getEmployeeEffMultiplier() : 0; const effEff = emp ? emp.eff * corp.getEmployeeEffMultiplier() * division.getEmployeeEffMultiplier() : 0;
return ( return (
<div style={employeeInfoDivStyle}> <>
<select className="dropdown" onChange={employeeSelectorOnChange}> <br />
<Select value={employee !== null ? employee.name : ""} onChange={employeeSelectorOnChange}>
{employees} {employees}
</select> </Select>
{employee != null && ( {employee != null && (
<p> <Typography>
Morale: {numeralWrapper.format(employee.mor, nf)} Morale: {numeralWrapper.format(employee.mor, nf)}
<br /> <br />
Happiness: {numeralWrapper.format(employee.hap, nf)} Happiness: {numeralWrapper.format(employee.hap, nf)}
@ -147,32 +159,27 @@ function ManualManagement(props: IProps): React.ReactElement {
Efficiency: {numeralWrapper.format(effEff, nf)} Efficiency: {numeralWrapper.format(effEff, nf)}
<br /> <br />
Salary: <Money money={employee.sal} /> Salary: <Money money={employee.sal} />
</p> </Typography>
)} )}
{employee != null && ( {employee != null && (
<select <Select onChange={employeePositionSelectorOnChange} value={employeePositionSelectorInitialValue}>
className="dropdown"
onChange={employeePositionSelectorOnChange}
value={employeePositionSelectorInitialValue}
>
{employeePositions} {employeePositions}
</select> </Select>
)} )}
</div> </>
); );
} }
interface IAutoAssignProps { interface IAutoAssignProps {
office: OfficeSpace; office: OfficeSpace;
corp: ICorporation;
division: IIndustry;
player: IPlayer;
job: string; job: string;
desc: string; desc: string;
rerender: () => void; rerender: () => void;
} }
function AutoAssignJob(props: IAutoAssignProps): React.ReactElement { function AutoAssignJob(props: IAutoAssignProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const numJob = countEmployee(props.office.employees, props.job); const numJob = countEmployee(props.office.employees, props.job);
const numUnassigned = countEmployee(props.office.employees, EmployeePositions.Unassigned); const numUnassigned = countEmployee(props.office.employees, EmployeePositions.Unassigned);
function assignEmployee(): void { function assignEmployee(): void {
@ -182,42 +189,43 @@ function AutoAssignJob(props: IAutoAssignProps): React.ReactElement {
} }
props.office.assignEmployeeToJob(props.job); props.office.assignEmployeeToJob(props.job);
props.office.calculateEmployeeProductivity(props.corp, props.division); props.office.calculateEmployeeProductivity(corp, division);
props.rerender(); props.rerender();
} }
function unassignEmployee(): void { function unassignEmployee(): void {
props.office.unassignEmployeeFromJob(props.job); props.office.unassignEmployeeFromJob(props.job);
props.office.calculateEmployeeProductivity(props.corp, props.division); props.office.calculateEmployeeProductivity(corp, division);
props.rerender(); props.rerender();
} }
const positionHeaderStyle = {
fontSize: "15px",
margin: "5px 0px 5px 0px",
width: "50%",
};
return ( return (
<> <TableRow>
<h2 className={"tooltip"} style={positionHeaderStyle}> <TableCell>
{props.job} ({numJob})<span className={"tooltiptext"}>{props.desc}</span> <Tooltip title={props.desc}>
</h2> <Typography>
<button className={numUnassigned > 0 ? "std-button" : "a-link-button-inactive"} onClick={assignEmployee}> {props.job} ({numJob})
+ </Typography>
</button> </Tooltip>
<button className={numJob > 0 ? "std-button" : "a-link-button-inactive"} onClick={unassignEmployee}> </TableCell>
- <TableCell>
</button> <IconButton disabled={numUnassigned === 0} onClick={assignEmployee}>
<br /> <ArrowDropUpIcon />
</> </IconButton>
<IconButton disabled={numJob === 0} onClick={unassignEmployee}>
<ArrowDropDownIcon />
</IconButton>
</TableCell>
</TableRow>
); );
} }
function AutoManagement(props: IProps): React.ReactElement { function AutoManagement(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const numUnassigned = countEmployee(props.office.employees, EmployeePositions.Unassigned); const numUnassigned = countEmployee(props.office.employees, EmployeePositions.Unassigned);
const vechain = props.corp.unlockUpgrades[4] === 1; // Has Vechain upgrade const vechain = corp.unlockUpgrades[4] === 1; // Has Vechain upgrade
// Calculate average morale, happiness, and energy. Also salary // Calculate average morale, happiness, energy, and salary.
// TODO is this efficient?
let totalMorale = 0, let totalMorale = 0,
totalHappiness = 0, totalHappiness = 0,
totalEnergy = 0, totalEnergy = 0,
@ -240,297 +248,238 @@ function AutoManagement(props: IProps): React.ReactElement {
return ( return (
<> <>
<p> <Table padding="none">
<strong>Unassigned Employees: {numUnassigned}</strong> <TableBody>
</p> <TableRow>
<br /> <TableCell>
<table> <Typography>Unassigned Employees:</Typography>
<tbody> </TableCell>
<tr> <TableCell>
<td> <Typography>{numUnassigned}</Typography>
<p>Avg Employee Morale:</p> </TableCell>
</td> </TableRow>
<td> <TableRow>
<p>{numeralWrapper.format(avgMorale, "0.000")}</p> <TableCell>
</td> <Typography>Avg Employee Morale:</Typography>
</tr> </TableCell>
<tr> <TableCell>
<td> <Typography>{numeralWrapper.format(avgMorale, "0.000")}</Typography>
<p>Avg Employee Happiness:</p> </TableCell>
</td> </TableRow>
<td> <TableRow>
<p>{numeralWrapper.format(avgHappiness, "0.000")}</p> <TableCell>
</td> <Typography>Avg Employee Happiness:</Typography>
</tr> </TableCell>
<tr> <TableCell>
<td> <Typography>{numeralWrapper.format(avgHappiness, "0.000")}</Typography>
<p>Avg Employee Energy:</p> </TableCell>
</td> </TableRow>
<td> <TableRow>
<p>{numeralWrapper.format(avgEnergy, "0.000")}</p> <TableCell>
</td> <Typography>Avg Employee Energy:</Typography>
</tr> </TableCell>
<tr> <TableCell>
<td> <Typography>{numeralWrapper.format(avgEnergy, "0.000")}</Typography>
<p>Total Employee Salary:</p> </TableCell>
</td> </TableRow>
<td> <TableRow>
<p> <TableCell>
<Typography>Total Employee Salary:</Typography>
</TableCell>
<TableCell>
<Typography>
<Money money={totalSalary} /> <Money money={totalSalary} />
</p> </Typography>
</td> </TableCell>
</tr> </TableRow>
{vechain && ( {vechain && (
<> <>
<tr> <TableRow>
<td> <TableCell>
<p className={"tooltip"} style={{ display: "inline-block" }}> <Tooltip
Material Production: title={
<span className={"tooltiptext"}> <Typography>
The base amount of material this office can produce. Does not include production multipliers from The base amount of material this office can produce. Does not include production multipliers
upgrades and materials. This value is based off the productivity of your Operations, Engineering, from upgrades and materials. This value is based off the productivity of your Operations,
and Management employees Engineering, and Management employees
</span> </Typography>
</p> }
</td> >
<td> <Typography>Material Production:</Typography>
<p>{numeralWrapper.format(props.division.getOfficeProductivity(props.office), "0.000")}</p> </Tooltip>
</td> </TableCell>
</tr> <TableCell>
<tr> <Typography>
<td> {numeralWrapper.format(division.getOfficeProductivity(props.office), "0.000")}
<p className={"tooltip"} style={{ display: "inline-block" }}> </Typography>
Product Production: </TableCell>
<span className={"tooltiptext"}> </TableRow>
The base amount of any given Product this office can produce. Does not include production <TableRow>
multipliers from upgrades and materials. This value is based off the productivity of your <TableCell>
Operations, Engineering, and Management employees <Tooltip
</span> title={
</p> <Typography>
</td> The base amount of any given Product this office can produce. Does not include production
<td> multipliers from upgrades and materials. This value is based off the productivity of your
<p> Operations, Engineering, and Management employees
</Typography>
}
>
<Typography>Product Production:</Typography>
</Tooltip>
</TableCell>
<TableCell>
<Typography>
{numeralWrapper.format( {numeralWrapper.format(
props.division.getOfficeProductivity(props.office, { division.getOfficeProductivity(props.office, {
forProduct: true, forProduct: true,
}), }),
"0.000", "0.000",
)} )}
</p> </Typography>
</td> </TableCell>
</tr> </TableRow>
<tr> <TableRow>
<td> <TableCell>
<p className={"tooltip"} style={{ display: "inline-block" }}> <Tooltip
Business Multiplier: title={<Typography>The effect this office's 'Business' employees has on boosting sales</Typography>}
<span className={"tooltiptext"}> >
The effect this office's 'Business' employees has on boosting sales <Typography> Business Multiplier:</Typography>
</span> </Tooltip>
</p> </TableCell>
</td> <TableCell>
<td> <Typography>x{numeralWrapper.format(division.getBusinessFactor(props.office), "0.000")}</Typography>
<p>x{numeralWrapper.format(props.division.getBusinessFactor(props.office), "0.000")}</p> </TableCell>
</td> </TableRow>
</tr>
</> </>
)} )}
</tbody> </TableBody>
</table> </Table>
<AutoAssignJob
rerender={props.rerender}
office={props.office}
corp={props.corp}
division={props.division}
player={props.player}
job={EmployeePositions.Operations}
desc={"Manages supply chain operations. Improves the amount of Materials and Products you produce."}
/>
<AutoAssignJob <Table padding="none">
rerender={props.rerender} <TableBody>
office={props.office} <AutoAssignJob
corp={props.corp} rerender={props.rerender}
division={props.division} office={props.office}
player={props.player} job={EmployeePositions.Operations}
job={EmployeePositions.Engineer} desc={"Manages supply chain operations. Improves the amount of Materials and Products you produce."}
desc={ />
"Develops and maintains products and production systems. Increases the quality of everything you produce. Also increases the amount you produce (not as much as Operations, however)"
}
/>
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
corp={props.corp} job={EmployeePositions.Engineer}
division={props.division} desc={
player={props.player} "Develops and maintains products and production systems. Increases the quality of everything you produce. Also increases the amount you produce (not as much as Operations, however)"
job={EmployeePositions.Business} }
desc={"Handles sales and finances. Improves the amount of Materials and Products you can sell."} />
/>
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
corp={props.corp} job={EmployeePositions.Business}
division={props.division} desc={"Handles sales and finances. Improves the amount of Materials and Products you can sell."}
player={props.player} />
job={EmployeePositions.Management}
desc={
"Leads and oversees employees and office operations. Improves the effectiveness of Engineer and Operations employees."
}
/>
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
corp={props.corp} job={EmployeePositions.Management}
division={props.division} desc={
player={props.player} "Leads and oversees employees and office operations. Improves the effectiveness of Engineer and Operations employees."
job={EmployeePositions.RandD} }
desc={"Research new innovative ways to improve the company. Generates Scientific Research."} />
/>
<AutoAssignJob <AutoAssignJob
rerender={props.rerender} rerender={props.rerender}
office={props.office} office={props.office}
corp={props.corp} job={EmployeePositions.RandD}
division={props.division} desc={"Research new innovative ways to improve the company. Generates Scientific Research."}
player={props.player} />
job={EmployeePositions.Training}
desc={ <AutoAssignJob
"Set employee to training, which will increase some of their stats. Employees in training do not affect any company operations." rerender={props.rerender}
} office={props.office}
/> job={EmployeePositions.Training}
desc={
"Set employee to training, which will increase some of their stats. Employees in training do not affect any company operations."
}
/>
</TableBody>
</Table>
</> </>
); );
} }
export function IndustryOffice(props: IProps): React.ReactElement { export function IndustryOffice(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [upgradeOfficeSizeOpen, setUpgradeOfficeSizeOpen] = useState(false);
const [throwPartyOpen, setThrowPartyOpen] = useState(false);
const [employeeManualAssignMode, setEmployeeManualAssignMode] = useState(false); const [employeeManualAssignMode, setEmployeeManualAssignMode] = useState(false);
const buttonStyle = {
fontSize: "13px",
};
// Hire Employee button
let hireEmployeeButtonClass = "tooltip";
if (props.office.atCapacity()) {
hireEmployeeButtonClass += " a-link-button-inactive";
} else {
hireEmployeeButtonClass += " std-button";
if (props.office.employees.length === 0) {
hireEmployeeButtonClass += " flashing-button";
}
}
function openHireEmployeePopup(): void {
const popupId = "cmpy-mgmt-hire-employee-popup";
createPopup(popupId, HireEmployeePopup, {
rerender: props.rerender,
office: props.office,
corp: props.corp,
popupId: popupId,
player: props.player,
});
}
// Autohire employee button
let autohireEmployeeButtonClass = "tooltip";
if (props.office.atCapacity()) {
autohireEmployeeButtonClass += " a-link-button-inactive";
} else {
autohireEmployeeButtonClass += " std-button";
}
function autohireEmployeeButtonOnClick(): void { function autohireEmployeeButtonOnClick(): void {
if (props.office.atCapacity()) return; if (props.office.atCapacity()) return;
props.office.hireRandomEmployee(); props.office.hireRandomEmployee();
props.rerender(); props.rerender();
} }
function openUpgradeOfficeSizePopup(): void {
const popupId = "cmpy-mgmt-upgrade-office-size-popup";
createPopup(popupId, UpgradeOfficeSizePopup, {
rerender: props.rerender,
office: props.office,
corp: props.corp,
popupId: popupId,
player: props.player,
});
}
function openThrowPartyPopup(): void {
const popupId = "cmpy-mgmt-throw-office-party-popup";
createPopup(popupId, ThrowPartyPopup, {
office: props.office,
corp: props.corp,
popupId: popupId,
});
}
return ( return (
<div className={"cmpy-mgmt-employee-panel"}> <Paper>
<h1 style={{ margin: "4px 0px 5px 0px" }}>Office Space</h1> <Typography>Office Space</Typography>
<p> <Typography>
Size: {props.office.employees.length} / {props.office.size} employees Size: {props.office.employees.length} / {props.office.size} employees
</p> </Typography>
<button className={hireEmployeeButtonClass} onClick={openHireEmployeePopup} style={buttonStyle}> <Tooltip title={<Typography>Automatically hires an employee and gives him/her a random name</Typography>}>
Hire Employee <span>
{props.office.employees.length === 0 && ( <Button disabled={props.office.atCapacity()} onClick={autohireEmployeeButtonOnClick}>
<span className={"tooltiptext"}> Hire Employee
You'll need to hire some employees to get your operations started! It's recommended to have at least one </Button>
employee in every position </span>
</span> </Tooltip>
)}
</button>
<button className={autohireEmployeeButtonClass} onClick={autohireEmployeeButtonOnClick} style={buttonStyle}>
Autohire Employee
<span className={"tooltiptext"}>Automatically hires an employee and gives him/her a random name</span>
</button>
<br /> <br />
<button <Tooltip title={<Typography>Upgrade the office's size so that it can hold more employees!</Typography>}>
className={"std-button tooltip"} <span>
onClick={openUpgradeOfficeSizePopup} <Button disabled={corp.funds.lt(0)} onClick={() => setUpgradeOfficeSizeOpen(true)}>
style={buttonStyle} Upgrade size
disabled={props.corp.funds.lt(0)} </Button>
> </span>
Upgrade size </Tooltip>
<span className={"tooltiptext"}>Upgrade the office's size so that it can hold more employees!</span> <UpgradeOfficeSizeModal
</button> rerender={props.rerender}
{!props.division.hasResearch("AutoPartyManager") && ( office={props.office}
<button open={upgradeOfficeSizeOpen}
className={"std-button tooltip"} onClose={() => setUpgradeOfficeSizeOpen(false)}
onClick={openThrowPartyPopup} />
style={buttonStyle}
disabled={props.corp.funds.lt(0)} {!division.hasResearch("AutoPartyManager") && (
> <>
Throw Party <Tooltip
<span className={"tooltiptext"}> title={<Typography>Throw an office party to increase your employee's morale and happiness</Typography>}
"Throw an office party to increase your employee's morale and happiness" >
</span> <span>
</button> <Button disabled={corp.funds.lt(0)} onClick={() => setThrowPartyOpen(true)}>
Throw Party
</Button>
</span>
</Tooltip>
<ThrowPartyModal
rerender={props.rerender}
office={props.office}
open={throwPartyOpen}
onClose={() => setThrowPartyOpen(false)}
/>
</>
)} )}
<br /> <br />
<div> <SwitchButton manualMode={employeeManualAssignMode} switchMode={setEmployeeManualAssignMode} />
<SwitchButton manualMode={employeeManualAssignMode} switchMode={setEmployeeManualAssignMode} />
</div>
{employeeManualAssignMode ? ( {employeeManualAssignMode ? (
<ManualManagement <ManualManagement rerender={props.rerender} office={props.office} />
rerender={props.rerender}
corp={props.corp}
division={props.division}
office={props.office}
player={props.player}
/>
) : ( ) : (
<AutoManagement <AutoManagement rerender={props.rerender} office={props.office} />
rerender={props.rerender}
corp={props.corp}
division={props.division}
office={props.office}
player={props.player}
/>
)} )}
</div> </Paper>
); );
} }

@ -1,333 +1,293 @@
// React Component for displaying an Industry's overview information // React Component for displaying an Industry's overview information
// (top-left panel in the Industry UI) // (top-left panel in the Industry UI)
import React from "react"; import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace"; import { OfficeSpace } from "../OfficeSpace";
import { Industries } from "../IndustryData"; import { Industries } from "../IndustryData";
import { IndustryUpgrades } from "../IndustryUpgrades"; import { IndustryUpgrades } from "../IndustryUpgrades";
import { IIndustry } from "../IIndustry";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText"; import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { MakeProductPopup } from "./MakeProductPopup"; import { MakeProductModal } from "./MakeProductModal";
import { ResearchPopup } from "./ResearchPopup"; import { ResearchPopup } from "./ResearchPopup";
import { createPopup } from "../../ui/React/createPopup"; import { createPopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { ICorporation } from "../ICorporation"; import { MoneyRate } from "../../ui/React/MoneyRate";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { StatsTable } from "../../ui/React/StatsTable";
import { StaticModal } from "../../ui/React/StaticModal";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper";
import IconButton from "@mui/material/IconButton";
import HelpIcon from "@mui/icons-material/Help";
import Box from "@mui/material/Box";
function MakeProductButton(): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [makeOpen, setMakeOpen] = useState(false);
const hasMaxProducts = division.hasMaximumNumberProducts();
function shouldFlash(): boolean {
return Object.keys(division.products).length === 0;
}
let createProductButtonText = "";
switch (division.type) {
case Industries.Food:
createProductButtonText = "Build Restaurant";
break;
case Industries.Tobacco:
createProductButtonText = "Create Product";
break;
case Industries.Pharmaceutical:
createProductButtonText = "Create Drug";
break;
case Industries.Computer:
case "Computer":
createProductButtonText = "Create Product";
break;
case Industries.Robotics:
createProductButtonText = "Design Robot";
break;
case Industries.Software:
createProductButtonText = "Develop Software";
break;
case Industries.Healthcare:
createProductButtonText = "Build Hospital";
break;
case Industries.RealEstate:
createProductButtonText = "Develop Property";
break;
default:
createProductButtonText = "Create Product";
return <></>;
}
return (
<>
<Tooltip
title={
hasMaxProducts ? (
<Typography>
ou have reached the maximum number of products: {division.getMaximumNumberProducts()}
</Typography>
) : (
""
)
}
>
<Button
color={shouldFlash() ? "error" : "primary"}
onClick={() => setMakeOpen(true)}
disabled={corp.funds.lt(0)}
>
{createProductButtonText}
</Button>
</Tooltip>
<MakeProductModal open={makeOpen} onClose={() => setMakeOpen(false)} />
</>
);
}
function Text(): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [helpOpen, setHelpOpen] = useState(false);
const vechain = corp.unlockUpgrades[4] === 1;
const profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber();
let advertisingInfo = false;
const advertisingFactors = division.getAdvertisingFactors();
const awarenessFac = advertisingFactors[1];
const popularityFac = advertisingFactors[2];
const ratioFac = advertisingFactors[3];
const totalAdvertisingFac = advertisingFactors[0];
if (vechain) {
advertisingInfo = true;
}
function convertEffectFacToGraphic(fac: number): string {
return createProgressBarText({
progress: fac,
totalTicks: 20,
});
}
function openResearchPopup(): void {
const popupId = "corporation-research-popup-box";
createPopup(popupId, ResearchPopup, {
industry: division,
popupId: popupId,
});
}
return (
<div>
<Typography>
Industry: {division.type} (Corp Funds: <Money money={corp.funds.toNumber()} />)
</Typography>
<br />
<StatsTable
rows={[
["Awareness:", numeralWrapper.format(division.awareness, "0.000")],
["Popularity:", numeralWrapper.format(division.popularity, "0.000")],
]}
/>
{advertisingInfo !== false && (
<Tooltip
title={
<>
<Typography>Total multiplier for this industrys sales due to its awareness and popularity</Typography>
<StatsTable
rows={[
["Awareness Bonus:", "x" + numeralWrapper.format(Math.pow(awarenessFac, 0.85), "0.000")],
["Popularity Bonus:", "x" + numeralWrapper.format(Math.pow(popularityFac, 0.85), "0.000")],
["Ratio Multiplier:", "x" + numeralWrapper.format(Math.pow(ratioFac, 0.85), "0.000")],
]}
/>
</>
}
>
<Typography>Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")}</Typography>
</Tooltip>
)}
<br />
<StatsTable
rows={[
["Revenue:", <MoneyRate money={division.lastCycleRevenue.toNumber()} />],
["Expenses:", <MoneyRate money={division.lastCycleExpenses.toNumber()} />],
["Profit:", <MoneyRate money={profit} />],
]}
/>
<br />
<Box display="flex" alignItems="center">
<Tooltip
title={
<Typography>
Production gain from owning production-boosting materials such as hardware, Robots, AI Cores, and Real
Estate.
</Typography>
}
>
<Typography>Production Multiplier: {numeralWrapper.format(division.prodMult, "0.00")}</Typography>
</Tooltip>
<IconButton onClick={() => setHelpOpen(true)}>
<HelpIcon />
</IconButton>
<StaticModal open={helpOpen} onClose={() => setHelpOpen(false)}>
<Typography>
Owning Hardware, Robots, AI Cores, and Real Estate can boost your Industry's production. The effect these
materials have on your production varies between Industries. For example, Real Estate may be very effective
for some Industries, but ineffective for others.
<br />
<br />
This division's production multiplier is calculated by summing the individual production multiplier of each
of its office locations. This production multiplier is applied to each office. Therefore, it is beneficial
to expand into new cities as this can greatly increase the production multiplier of your entire Division.
<br />
<br />
Below are approximations for how effective each material is at boosting this industry's production
multiplier (Bigger bars = more effective):
<br />
<br />
Hardware:&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.hwFac)}
<br />
Robots:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.robFac)}
<br />
AI Cores:&nbsp;&nbsp;&nbsp; {convertEffectFacToGraphic(division.aiFac)}
<br />
Real Estate: {convertEffectFacToGraphic(division.reFac)}
</Typography>
</StaticModal>
</Box>
<Box display="flex" alignItems="center">
<Tooltip
title={
<Typography>
Scientific Research increases the quality of the materials and products that you produce.
</Typography>
}
>
<Typography>Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000a")}</Typography>
</Tooltip>
<Button sx={{ mx: 1 }} onClick={openResearchPopup}>
Research
</Button>
</Box>
</div>
);
}
function Upgrades(props: { office: OfficeSpace; rerender: () => void }): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const upgrades = [];
for (const index in IndustryUpgrades) {
const upgrade = IndustryUpgrades[index];
// AutoBrew research disables the Coffee upgrade
if (division.hasResearch("AutoBrew") && upgrade[4] === "Coffee") {
continue;
}
const i = upgrade[0];
const baseCost = upgrade[1];
const priceMult = upgrade[2];
let cost = 0;
switch (i) {
case 0: //Coffee, cost is static per employee
cost = props.office.employees.length * baseCost;
break;
default:
cost = baseCost * Math.pow(priceMult, division.upgrades[i]);
break;
}
function onClick(): void {
if (corp.funds.lt(cost)) return;
corp.funds = corp.funds.minus(cost);
division.upgrade(upgrade, {
corporation: corp,
office: props.office,
});
props.rerender();
}
upgrades.push(
<Tooltip key={index} title={upgrade[5]}>
<span>
<Button disabled={corp.funds.lt(cost)} onClick={onClick}>
{upgrade[4]} -&nbsp;
<MoneyCost money={cost} corp={corp} />
</Button>
</span>
</Tooltip>,
);
}
return <>{upgrades}</>;
}
interface IProps { interface IProps {
corp: ICorporation;
currentCity: string; currentCity: string;
division: IIndustry;
office: OfficeSpace; office: OfficeSpace;
player: IPlayer;
rerender: () => void; rerender: () => void;
} }
export function IndustryOverview(props: IProps): React.ReactElement { export function IndustryOverview(props: IProps): React.ReactElement {
function renderMakeProductButton(): React.ReactElement { const division = useDivision();
let createProductButtonText = "";
let createProductPopupText = "";
switch (props.division.type) {
case Industries.Food:
createProductButtonText = "Build Restaurant";
createProductPopupText = "Build and manage a new restaurant!";
break;
case Industries.Tobacco:
createProductButtonText = "Create Product";
createProductPopupText = "Create a new tobacco product!";
break;
case Industries.Pharmaceutical:
createProductButtonText = "Create Drug";
createProductPopupText = "Design and develop a new pharmaceutical drug!";
break;
case Industries.Computer:
case "Computer":
createProductButtonText = "Create Product";
createProductPopupText = "Design and manufacture a new computer hardware product!";
break;
case Industries.Robotics:
createProductButtonText = "Design Robot";
createProductPopupText = "Design and create a new robot or robotic system!";
break;
case Industries.Software:
createProductButtonText = "Develop Software";
createProductPopupText = "Develop a new piece of software!";
break;
case Industries.Healthcare:
createProductButtonText = "Build Hospital";
createProductPopupText = "Build and manage a new hospital!";
break;
case Industries.RealEstate:
createProductButtonText = "Develop Property";
createProductPopupText = "Develop a new piece of real estate property!";
break;
default:
createProductButtonText = "Create Product";
createProductPopupText = "Create a new product!";
return <></>;
}
createProductPopupText +=
"<br><br>To begin developing a product, " +
"first choose the city in which it will be designed. The stats of your employees " +
"in the selected city affect the properties of the finished product, such as its " +
"quality, performance, and durability.<br><br>" +
"You can also choose to invest money in the design and marketing of " +
"the product. Investing money in its design will result in a superior product. " +
"Investing money in marketing the product will help the product's sales.";
const hasMaxProducts = props.division.hasMaximumNumberProducts();
const className = hasMaxProducts ? "a-link-button-inactive tooltip" : "std-button";
const buttonStyle = {
margin: "6px",
display: "inline-block",
};
function openMakeProductPopup(): void {
const popupId = "cmpy-mgmt-create-product-popup";
createPopup(popupId, MakeProductPopup, {
popupText: createProductPopupText,
division: props.division,
corp: props.corp,
popupId: popupId,
});
}
function shouldFlash(): boolean {
return Object.keys(props.division.products).length === 0;
}
return (
<button
className={className + (shouldFlash() ? " flashing-button" : "")}
onClick={openMakeProductPopup}
style={buttonStyle}
disabled={props.corp.funds.lt(0)}
>
{createProductButtonText}
{hasMaxProducts && (
<span className={"tooltiptext"}>
You have reached the maximum number of products: {props.division.getMaximumNumberProducts()}
</span>
)}
</button>
);
}
function renderText(): React.ReactElement {
const vechain = props.corp.unlockUpgrades[4] === 1;
const profit = props.division.lastCycleRevenue.minus(props.division.lastCycleExpenses).toNumber();
let advertisingInfo = false;
const advertisingFactors = props.division.getAdvertisingFactors();
const awarenessFac = advertisingFactors[1];
const popularityFac = advertisingFactors[2];
const ratioFac = advertisingFactors[3];
const totalAdvertisingFac = advertisingFactors[0];
if (vechain) {
advertisingInfo = true;
}
function productionMultHelpTipOnClick(): void {
// Wrapper for createProgressBarText()
// Converts the industry's "effectiveness factors"
// into a graphic (string) depicting how high that effectiveness is
function convertEffectFacToGraphic(fac: number): string {
return createProgressBarText({
progress: fac,
totalTicks: 20,
});
}
dialogBoxCreate(
"Owning Hardware, Robots, AI Cores, and Real Estate " +
"can boost your Industry's production. The effect these " +
"materials have on your production varies between Industries. " +
"For example, Real Estate may be very effective for some Industries, " +
"but ineffective for others.<br><br>" +
"This division's production multiplier is calculated by summing " +
"the individual production multiplier of each of its office locations. " +
"This production multiplier is applied to each office. Therefore, it is " +
"beneficial to expand into new cities as this can greatly increase the " +
"production multiplier of your entire Division.<br><br>" +
"Below are approximations for how effective each material is at boosting " +
"this industry's production multiplier (Bigger bars = more effective):<br><br>" +
`Hardware:&nbsp;&nbsp;&nbsp; ${convertEffectFacToGraphic(props.division.hwFac)}<br>` +
`Robots:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ${convertEffectFacToGraphic(props.division.robFac)}<br>` +
`AI Cores:&nbsp;&nbsp;&nbsp; ${convertEffectFacToGraphic(props.division.aiFac)}<br>` +
`Real Estate: ${convertEffectFacToGraphic(props.division.reFac)}`,
);
}
function openResearchPopup(): void {
const popupId = "corporation-research-popup-box";
createPopup(popupId, ResearchPopup, {
industry: props.division,
popupId: popupId,
});
}
return (
<div>
Industry: {props.division.type} (Corp Funds: <Money money={props.corp.funds.toNumber()} />)
<br /> <br />
Awareness: {numeralWrapper.format(props.division.awareness, "0.000")} <br />
Popularity: {numeralWrapper.format(props.division.popularity, "0.000")} <br />
{advertisingInfo !== false && (
<p className={"tooltip"}>
Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")}
<span className={"tooltiptext cmpy-mgmt-advertising-info"}>
Total multiplier for this industrys sales due to its awareness and popularity
<br />
Awareness Bonus: x{numeralWrapper.format(Math.pow(awarenessFac, 0.85), "0.000")}
<br />
Popularity Bonus: x{numeralWrapper.format(Math.pow(popularityFac, 0.85), "0.000")}
<br />
Ratio Multiplier: x{numeralWrapper.format(Math.pow(ratioFac, 0.85), "0.000")}
</span>
</p>
)}
{advertisingInfo}
<br />
<br />
<table>
<tbody>
<tr>
<td>
<p>Revenue: </p>
</td>
<td>
<p>
<Money money={props.division.lastCycleRevenue.toNumber()} /> / s
</p>
</td>
</tr>
<tr>
<td>
<p>Expenses: </p>
</td>
<td>
<p>
<Money money={props.division.lastCycleExpenses.toNumber()} /> / s
</p>
</td>
</tr>
<tr>
<td>
<p>Profit: </p>
</td>
<td>
<p>
<Money money={profit} /> / s
</p>
</td>
</tr>
</tbody>
</table>
<br />
<p className={"tooltip"}>
Production Multiplier: {numeralWrapper.format(props.division.prodMult, "0.00")}
<span className={"tooltiptext"}>
Production gain from owning production-boosting materials such as hardware, Robots, AI Cores, and Real
Estate
</span>
</p>
<div className={"help-tip"} onClick={productionMultHelpTipOnClick}>
?
</div>
<br /> <br />
<p className={"tooltip"}>
Scientific Research: {numeralWrapper.format(props.division.sciResearch.qty, "0.000a")}
<span className={"tooltiptext"}>
Scientific Research increases the quality of the materials and products that you produce.
</span>
</p>
<button className={"help-tip"} onClick={openResearchPopup}>
Research
</button>
</div>
);
}
function renderUpgrades(): React.ReactElement[] {
const upgrades = [];
for (const index in IndustryUpgrades) {
const upgrade = IndustryUpgrades[index];
// AutoBrew research disables the Coffee upgrade
if (props.division.hasResearch("AutoBrew") && upgrade[4] === "Coffee") {
continue;
}
const i = upgrade[0];
const baseCost = upgrade[1];
const priceMult = upgrade[2];
let cost = 0;
switch (i) {
case 0: //Coffee, cost is static per employee
cost = props.office.employees.length * baseCost;
break;
default:
cost = baseCost * Math.pow(priceMult, props.division.upgrades[i]);
break;
}
function onClick(): void {
if (props.corp.funds.lt(cost)) return;
props.corp.funds = props.corp.funds.minus(cost);
props.division.upgrade(upgrade, {
corporation: props.corp,
office: props.office,
});
props.rerender();
}
upgrades.push(
renderUpgrade({
key: index,
onClick: onClick,
text: (
<>
{upgrade[4]} - <MoneyCost money={cost} corp={props.corp} />
</>
),
tooltip: upgrade[5],
}),
);
}
return upgrades;
}
interface IRenderUpgradeProps {
key: string;
onClick: () => void;
text: JSX.Element;
tooltip: string;
}
function renderUpgrade(props: IRenderUpgradeProps): React.ReactElement {
return (
<div className={"cmpy-mgmt-upgrade-div tooltip"} onClick={props.onClick} key={props.key}>
{props.text}
{props.tooltip != null && <span className={"tooltiptext"}>{props.tooltip}</span>}
</div>
);
}
const makeProductButton = renderMakeProductButton();
return ( return (
<div className={"cmpy-mgmt-industry-overview-panel"}> <Paper>
{renderText()} <Text />
<br /> <br />
<u className={"industry-purchases-and-upgrades-header"}>Purchases & Upgrades</u> <Typography>Purchases & Upgrades</Typography>
<br /> <Upgrades office={props.office} rerender={props.rerender} /> <br />
{renderUpgrades()} <br /> {division.makesProducts && <MakeProductButton />}
{props.division.makesProducts && makeProductButton} </Paper>
</div>
); );
} }

@ -1,445 +1,32 @@
// React Component for displaying an Industry's warehouse information // React Component for displaying an Industry's warehouse information
// (right-side panel in the Industry UI) // (right-side panel in the Industry UI)
import React from "react"; import React, { useState } from "react";
import { CorporationConstants } from "../data/Constants"; import { CorporationConstants } from "../data/Constants";
import { OfficeSpace } from "../OfficeSpace";
import { Material } from "../Material"; import { Material } from "../Material";
import { Product } from "../Product"; import { Product } from "../Product";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { DiscontinueProductPopup } from "./DiscontinueProductPopup"; import { SmartSupplyModal } from "./SmartSupplyModal";
import { ExportPopup } from "./ExportPopup"; import { ProductElem } from "./ProductElem";
import { LimitProductProductionPopup } from "./LimitProductProductionPopup"; import { MaterialElem } from "./MaterialElem";
import { MaterialMarketTaPopup } from "./MaterialMarketTaPopup"; import { MaterialSizes } from "../MaterialSizes";
import { SellMaterialPopup } from "./SellMaterialPopup";
import { SellProductPopup } from "./SellProductPopup";
import { PurchaseMaterialPopup } from "./PurchaseMaterialPopup";
import { ProductMarketTaPopup } from "./ProductMarketTaPopup";
import { SmartSupplyPopup } from "./SmartSupplyPopup";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { createPopup } from "../../ui/React/createPopup";
import { isString } from "../../utils/helpers/isString";
import { ICorporation } from "../ICorporation"; import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import { isRelevantMaterial } from "./Helpers"; import { isRelevantMaterial } from "./Helpers";
import { IndustryProductEquation } from "./IndustryProductEquation"; import { IndustryProductEquation } from "./IndustryProductEquation";
import { PurchaseWarehouse } from "../Actions"; import { PurchaseWarehouse } from "../Actions";
import { useCorporation, useDivision } from "./Context";
interface IProductProps { import Typography from "@mui/material/Typography";
corp: ICorporation; import Tooltip from "@mui/material/Tooltip";
division: IIndustry; import Paper from "@mui/material/Paper";
city: string; import Button from "@mui/material/Button";
product: Product; import Box from "@mui/material/Box";
player: IPlayer;
rerender: () => void;
}
// Creates the UI for a single Product type
function ProductComponent(props: IProductProps): React.ReactElement {
const corp = props.corp;
const division = props.division;
const city = props.city;
const product = props.product;
// Numeraljs formatters
const nf = "0.000";
const nfB = "0.000a"; // For numbers that might be big
const hasUpgradeDashboard = division.hasResearch("uPgrade: Dashboard");
// Total product gain = production - sale
const totalGain = product.data[city][1] - product.data[city][2];
// Sell button
let sellButtonText: JSX.Element;
if (product.sllman[city][0]) {
if (isString(product.sllman[city][1])) {
sellButtonText = (
<>
Sell ({numeralWrapper.format(product.data[city][2], nfB)}/{product.sllman[city][1]})
</>
);
} else {
sellButtonText = (
<>
Sell ({numeralWrapper.format(product.data[city][2], nfB)}/
{numeralWrapper.format(product.sllman[city][1], nfB)})
</>
);
}
} else {
sellButtonText = <>Sell (0.000/0.000)</>;
}
if (product.marketTa2) {
sellButtonText = (
<>
{sellButtonText} @ <Money money={product.marketTa2Price[city]} />
</>
);
} else if (product.marketTa1) {
const markupLimit = product.rat / product.mku;
sellButtonText = (
<>
{sellButtonText} @ <Money money={product.pCost + markupLimit} />
</>
);
} else if (product.sCost) {
if (isString(product.sCost)) {
const sCost = (product.sCost as string).replace(/MP/g, product.pCost + "");
sellButtonText = (
<>
{sellButtonText} @ <Money money={eval(sCost)} />
</>
);
} else {
sellButtonText = (
<>
{sellButtonText} @ <Money money={product.sCost} />
</>
);
}
}
function openSellProductPopup(): void {
const popupId = "cmpy-mgmt-limit-product-production-popup";
createPopup(popupId, SellProductPopup, {
product: product,
city: city,
popupId: popupId,
});
}
// Limit Production button
let limitProductionButtonText = "Limit Production";
if (product.prdman[city][0]) {
limitProductionButtonText += " (" + numeralWrapper.format(product.prdman[city][1], nf) + ")";
}
function openLimitProductProdutionPopup(): void {
const popupId = "cmpy-mgmt-limit-product-production-popup";
createPopup(popupId, LimitProductProductionPopup, {
product: product,
city: city,
popupId: popupId,
});
}
function openDiscontinueProductPopup(): void {
const popupId = "cmpy-mgmt-discontinue-product-popup";
createPopup(popupId, DiscontinueProductPopup, {
rerender: props.rerender,
product: product,
industry: division,
corp: props.corp,
popupId: popupId,
player: props.player,
});
}
function openProductMarketTaPopup(): void {
const popupId = "cmpy-mgmt-marketta-popup";
createPopup(popupId, ProductMarketTaPopup, {
product: product,
industry: division,
popupId: popupId,
});
}
// Unfinished Product
if (!product.fin) {
return (
<div className={"cmpy-mgmt-warehouse-product-div"}>
<p>
Designing {product.name} (req. Operations/Engineers in {product.createCity})...
</p>
<br />
<p>{numeralWrapper.format(product.prog, "0.00")}% complete</p>
<br />
{hasUpgradeDashboard && (
<div>
<button className={"std-button"} onClick={openSellProductPopup}>
{sellButtonText}
</button>
<br />
<button className={"std-button"} onClick={openLimitProductProdutionPopup}>
{limitProductionButtonText}
</button>
<button className={"std-button"} onClick={openDiscontinueProductPopup}>
Discontinue
</button>
{division.hasResearch("Market-TA.I") && (
<button className={"std-button"} onClick={openProductMarketTaPopup}>
Market-TA
</button>
)}
</div>
)}
</div>
);
}
return (
<div className={"cmpy-mgmt-warehouse-product-div"}>
<p className={"tooltip"}>
{product.name}: {numeralWrapper.format(product.data[city][0], nfB)} ({numeralWrapper.format(totalGain, nfB)}/s)
<span className={"tooltiptext"}>
Prod: {numeralWrapper.format(product.data[city][1], nfB)}/s
<br />
Sell: {numeralWrapper.format(product.data[city][2], nfB)} /s
</span>
</p>
<br />
<p className={"tooltip"}>
Rating: {numeralWrapper.format(product.rat, nf)}
<span className={"tooltiptext"}>
Quality: {numeralWrapper.format(product.qlt, nf)} <br />
Performance: {numeralWrapper.format(product.per, nf)} <br />
Durability: {numeralWrapper.format(product.dur, nf)} <br />
Reliability: {numeralWrapper.format(product.rel, nf)} <br />
Aesthetics: {numeralWrapper.format(product.aes, nf)} <br />
Features: {numeralWrapper.format(product.fea, nf)}
{corp.unlockUpgrades[2] === 1 && <br />}
{corp.unlockUpgrades[2] === 1 && "Demand: " + numeralWrapper.format(product.dmd, nf)}
{corp.unlockUpgrades[3] === 1 && <br />}
{corp.unlockUpgrades[3] === 1 && "Competition: " + numeralWrapper.format(product.cmp, nf)}
</span>
</p>
<br />
<p className={"tooltip"}>
Est. Production Cost:{" "}
{numeralWrapper.formatMoney(product.pCost / CorporationConstants.ProductProductionCostRatio)}
<span className={"tooltiptext"}>An estimate of the material cost it takes to create this Product.</span>
</p>
<br />
<p className={"tooltip"}>
Est. Market Price: {numeralWrapper.formatMoney(product.pCost)}
<span className={"tooltiptext"}>
An estimate of how much consumers are willing to pay for this product. Setting the sale price above this may
result in less sales. Setting the sale price below this may result in more sales.
</span>
</p>
<div>
<button className={"std-button"} onClick={openSellProductPopup}>
{sellButtonText}
</button>
<br />
<button className={"std-button"} onClick={openLimitProductProdutionPopup}>
{limitProductionButtonText}
</button>
<button className={"std-button"} onClick={openDiscontinueProductPopup}>
Discontinue
</button>
{division.hasResearch("Market-TA.I") && (
<button className={"std-button"} onClick={openProductMarketTaPopup}>
Market-TA
</button>
)}
</div>
</div>
);
}
interface IMaterialProps {
corp: ICorporation;
division: IIndustry;
warehouse: Warehouse;
city: string;
mat: Material;
rerender: () => void;
}
// Creates the UI for a single Material type
function MaterialComponent(props: IMaterialProps): React.ReactElement {
const corp = props.corp;
const division = props.division;
const warehouse = props.warehouse;
const city = props.city;
const mat = props.mat;
const markupLimit = mat.getMarkupLimit();
const office = division.offices[city];
if (!(office instanceof OfficeSpace)) {
throw new Error(`Could not get OfficeSpace object for this city (${city})`);
}
// Numeraljs formatter
const nf = "0.000";
const nfB = "0.000a"; // For numbers that might be biger
// Total gain or loss of this material (per second)
const totalGain = mat.buy + mat.prd + mat.imp - mat.sll - mat.totalExp;
// Flag that determines whether this industry is "new" and the current material should be
// marked with flashing-red lights
const tutorial =
division.newInd && Object.keys(division.reqMats).includes(mat.name) && mat.buy === 0 && mat.imp === 0;
// Purchase material button
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nfB)})`;
const purchaseButtonClass = tutorial ? "std-button flashing-button tooltip" : "std-button";
function openPurchaseMaterialPopup(): void {
const popupId = "cmpy-mgmt-material-purchase-popup";
createPopup(popupId, PurchaseMaterialPopup, {
mat: mat,
industry: division,
warehouse: warehouse,
corp: props.corp,
popupId: popupId,
});
}
function openExportPopup(): void {
const popupId = "cmpy-mgmt-export-popup";
createPopup(popupId, ExportPopup, {
mat: mat,
corp: props.corp,
popupId: popupId,
});
}
// Sell material button
let sellButtonText: JSX.Element;
if (mat.sllman[0]) {
if (isString(mat.sllman[1])) {
sellButtonText = (
<>
Sell ({numeralWrapper.format(mat.sll, nfB)}/{mat.sllman[1]})
</>
);
} else {
sellButtonText = (
<>
Sell ({numeralWrapper.format(mat.sll, nfB)}/{numeralWrapper.format(mat.sllman[1] as number, nfB)})
</>
);
}
if (mat.marketTa2) {
sellButtonText = (
<>
{sellButtonText} @ <Money money={mat.marketTa2Price} />
</>
);
} else if (mat.marketTa1) {
sellButtonText = (
<>
{sellButtonText} @ <Money money={mat.bCost + markupLimit} />
</>
);
} else if (mat.sCost) {
if (isString(mat.sCost)) {
const sCost = (mat.sCost as string).replace(/MP/g, mat.bCost + "");
sellButtonText = (
<>
{sellButtonText} @ <Money money={eval(sCost)} />
</>
);
} else {
sellButtonText = (
<>
{sellButtonText} @ <Money money={mat.sCost} />
</>
);
}
}
} else {
sellButtonText = <>Sell (0.000/0.000)</>;
}
function openSellMaterialPopup(): void {
const popupId = "cmpy-mgmt-material-sell-popup";
createPopup(popupId, SellMaterialPopup, {
mat: mat,
corp: props.corp,
popupId: popupId,
});
}
function openMaterialMarketTaPopup(): void {
const popupId = "cmpy-mgmt-export-popup";
createPopup(popupId, MaterialMarketTaPopup, {
mat: mat,
industry: division,
corp: props.corp,
popupId: popupId,
});
}
function shouldFlash(): boolean {
return props.division.prodMats.includes(props.mat.name) && !mat.sllman[0];
}
return (
<div className={"cmpy-mgmt-warehouse-material-div"}>
<div style={{ display: "inline-block" }}>
<p className={"tooltip"}>
{mat.name}: {numeralWrapper.format(mat.qty, nfB)} ({numeralWrapper.format(totalGain, nfB)}/s)
<span className={"tooltiptext"}>
Buy: {numeralWrapper.format(mat.buy, nfB)} <br />
Prod: {numeralWrapper.format(mat.prd, nfB)} <br />
Sell: {numeralWrapper.format(mat.sll, nfB)} <br />
Export: {numeralWrapper.format(mat.totalExp, nfB)} <br />
Import: {numeralWrapper.format(mat.imp, nfB)}
{corp.unlockUpgrades[2] === 1 && <br />}
{corp.unlockUpgrades[2] === 1 && "Demand: " + numeralWrapper.format(mat.dmd, nf)}
{corp.unlockUpgrades[3] === 1 && <br />}
{corp.unlockUpgrades[3] === 1 && "Competition: " + numeralWrapper.format(mat.cmp, nf)}
</span>
</p>
<br />
<p className={"tooltip"}>
MP: {numeralWrapper.formatMoney(mat.bCost)}
<span className={"tooltiptext"}>
Market Price: The price you would pay if you were to buy this material on the market
</span>
</p>{" "}
<br />
<p className={"tooltip"}>
Quality: {numeralWrapper.format(mat.qlt, "0.00a")}
<span className={"tooltiptext"}>The quality of your material. Higher quality will lead to more sales</span>
</p>
</div>
<div style={{ display: "inline-block" }}>
<button
className={purchaseButtonClass}
onClick={openPurchaseMaterialPopup}
disabled={props.warehouse.smartSupplyEnabled && Object.keys(props.division.reqMats).includes(props.mat.name)}
>
{purchaseButtonText}
{tutorial && (
<span className={"tooltiptext"}>Purchase your required materials to get production started!</span>
)}
</button>
{corp.unlockUpgrades[0] === 1 && (
<button className={"std-button"} onClick={openExportPopup}>
Export
</button>
)}
<br />
<button className={`std-button${shouldFlash() ? " flashing-button" : ""}`} onClick={openSellMaterialPopup}>
{sellButtonText}
</button>
{division.hasResearch("Market-TA.I") && (
<button className={"std-button"} onClick={openMaterialMarketTaPopup}>
Market-TA
</button>
)}
</div>
</div>
);
}
interface IProps { interface IProps {
corp: ICorporation; corp: ICorporation;
@ -450,177 +37,180 @@ interface IProps {
rerender: () => void; rerender: () => void;
} }
export function IndustryWarehouse(props: IProps): React.ReactElement { function WarehouseRoot(props: IProps): React.ReactElement {
function renderWarehouseUI(): React.ReactElement { const corp = useCorporation();
if (props.warehouse === 0) return <></>; const division = useDivision();
// General Storage information at the top const [smartSupplyOpen, setSmartSupplyOpen] = useState(false);
const sizeUsageStyle = { if (props.warehouse === 0) return <></>;
color: props.warehouse.sizeUsed >= props.warehouse.size ? "red" : "white",
margin: "5px",
};
// Upgrade Warehouse size button // Upgrade Warehouse size button
const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, props.warehouse.level + 1); const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, props.warehouse.level + 1);
const canAffordUpgrade = props.corp.funds.gt(sizeUpgradeCost); const canAffordUpgrade = corp.funds.gt(sizeUpgradeCost);
const upgradeWarehouseClass = canAffordUpgrade ? "std-button" : "a-link-button-inactive"; function upgradeWarehouseOnClick(): void {
function upgradeWarehouseOnClick(): void { if (division === null) return;
if (props.division === null) return; if (props.warehouse === 0) return;
if (props.warehouse === 0) return; if (!canAffordUpgrade) return;
++props.warehouse.level; ++props.warehouse.level;
props.warehouse.updateSize(props.corp, props.division); props.warehouse.updateSize(corp, division);
props.corp.funds = props.corp.funds.minus(sizeUpgradeCost); corp.funds = corp.funds.minus(sizeUpgradeCost);
props.rerender();
}
function openSmartSupplyPopup(): void {
if (props.warehouse === 0) return;
const popupId = "cmpy-mgmt-smart-supply-popup";
createPopup(popupId, SmartSupplyPopup, {
division: props.division,
warehouse: props.warehouse,
corp: props.corp,
player: props.player,
popupId: popupId,
});
}
const ratioLines = [];
for (const matName in props.division.reqMats) {
if (props.division.reqMats.hasOwnProperty(matName)) {
const text = [" *", props.division.reqMats[matName], matName].join(" ");
ratioLines.push(
<div key={matName}>
<p>{text}</p>
</div>,
);
}
}
// Current State:
let stateText;
switch (props.division.state) {
case "START":
stateText = "Current state: Preparing...";
break;
case "PURCHASE":
stateText = "Current state: Purchasing materials...";
break;
case "PRODUCTION":
stateText = "Current state: Producing materials and/or products...";
break;
case "SALE":
stateText = "Current state: Selling materials and/or products...";
break;
case "EXPORT":
stateText = "Current state: Exporting materials and/or products...";
break;
default:
console.error(`Invalid state: ${props.division.state}`);
break;
}
// Create React components for materials
const mats = [];
for (const matName in props.warehouse.materials) {
if (props.warehouse.materials[matName] instanceof Material) {
// Only create UI for materials that are relevant for the industry
if (isRelevantMaterial(matName, props.division)) {
mats.push(
<MaterialComponent
rerender={props.rerender}
city={props.currentCity}
corp={props.corp}
division={props.division}
key={matName}
mat={props.warehouse.materials[matName]}
warehouse={props.warehouse}
/>,
);
}
}
}
// Create React components for products
const products = [];
if (props.division.makesProducts && Object.keys(props.division.products).length > 0) {
for (const productName in props.division.products) {
const product = props.division.products[productName];
if (product instanceof Product) {
products.push(
<ProductComponent
rerender={props.rerender}
player={props.player}
city={props.currentCity}
corp={props.corp}
division={props.division}
key={productName}
product={product}
/>,
);
}
}
}
return (
<div className={"cmpy-mgmt-warehouse-panel"}>
<p className={"tooltip"} style={sizeUsageStyle}>
Storage: {numeralWrapper.formatBigNumber(props.warehouse.sizeUsed)} /{" "}
{numeralWrapper.formatBigNumber(props.warehouse.size)}
<span className={"tooltiptext"} dangerouslySetInnerHTML={{ __html: props.warehouse.breakdown }}></span>
</p>
<button className={upgradeWarehouseClass} onClick={upgradeWarehouseOnClick}>
Upgrade Warehouse Size - <MoneyCost money={sizeUpgradeCost} corp={props.corp} />
</button>
<p>This industry uses the following equation for it's production: </p>
<br />
<br />
<IndustryProductEquation division={props.division} />
<br />
<br />
<p>
To get started with production, purchase your required materials or import them from another of your company's
divisions.
</p>
<br />
<p>{stateText}</p>
{props.corp.unlockUpgrades[1] && (
<>
<button className="std-button" onClick={openSmartSupplyPopup}>
Configure Smart Supply
</button>
</>
)}
{mats}
{products}
</div>
);
}
function purchaseWarehouse(division: IIndustry, city: string): void {
PurchaseWarehouse(props.corp, division, city);
props.rerender(); props.rerender();
} }
if (props.warehouse instanceof Warehouse) { const ratioLines = [];
return renderWarehouseUI(); for (const matName in division.reqMats) {
} else { if (division.reqMats.hasOwnProperty(matName)) {
return ( const text = [" *", division.reqMats[matName], matName].join(" ");
<div className={"cmpy-mgmt-warehouse-panel"}> ratioLines.push(
<button <div key={matName}>
className={"std-button"} <p>{text}</p>
onClick={() => purchaseWarehouse(props.division, props.currentCity)} </div>,
disabled={props.corp.funds.lt(CorporationConstants.WarehouseInitialCost)} );
> }
Purchase Warehouse ( }
<MoneyCost money={CorporationConstants.WarehouseInitialCost} corp={props.corp} />)
</button> // Current State:
</div> let stateText;
switch (division.state) {
case "START":
stateText = "Current state: Preparing...";
break;
case "PURCHASE":
stateText = "Current state: Purchasing materials...";
break;
case "PRODUCTION":
stateText = "Current state: Producing materials and/or products...";
break;
case "SALE":
stateText = "Current state: Selling materials and/or products...";
break;
case "EXPORT":
stateText = "Current state: Exporting materials and/or products...";
break;
default:
console.error(`Invalid state: ${division.state}`);
break;
}
// Create React components for materials
const mats = [];
for (const matName in props.warehouse.materials) {
if (!(props.warehouse.materials[matName] instanceof Material)) continue;
// Only create UI for materials that are relevant for the industry
if (!isRelevantMaterial(matName, division)) continue;
mats.push(
<MaterialElem
rerender={props.rerender}
city={props.currentCity}
key={matName}
mat={props.warehouse.materials[matName]}
warehouse={props.warehouse}
/>,
); );
} }
// Create React components for products
const products = [];
if (division.makesProducts && Object.keys(division.products).length > 0) {
for (const productName in division.products) {
const product = division.products[productName];
if (!(product instanceof Product)) continue;
products.push(
<ProductElem rerender={props.rerender} city={props.currentCity} key={productName} product={product} />,
);
}
}
let breakdown = <></>;
for (const matName in props.warehouse.materials) {
if (matName === "RealEstate") continue;
const mat = props.warehouse.materials[matName];
if (!MaterialSizes.hasOwnProperty(matName)) continue;
if (mat.qty === 0) continue;
breakdown = (
<>
{breakdown}
{matName}: {numeralWrapper.format(mat.qty * MaterialSizes[matName], "0,0.0")}
<br />
</>
);
}
return (
<Paper>
<Box display="flex" alignItems="center">
<Tooltip title={props.warehouse.sizeUsed !== 0 ? breakdown : ""}>
<Typography color={props.warehouse.sizeUsed >= props.warehouse.size ? "error" : "primary"}>
Storage: {numeralWrapper.formatBigNumber(props.warehouse.sizeUsed)} /{" "}
{numeralWrapper.formatBigNumber(props.warehouse.size)}
</Typography>
</Tooltip>
<Button disabled={!canAffordUpgrade} onClick={upgradeWarehouseOnClick}>
Upgrade Warehouse Size -&nbsp;
<MoneyCost money={sizeUpgradeCost} corp={corp} />
</Button>
</Box>
<Typography>This industry uses the following equation for it's production: </Typography>
<br />
<Typography>
<IndustryProductEquation division={division} />
</Typography>
<br />
<Typography>
To get started with production, purchase your required materials or import them from another of your company's
divisions.
</Typography>
<br />
<Typography>{stateText}</Typography>
{corp.unlockUpgrades[1] && (
<>
<Button onClick={() => setSmartSupplyOpen(true)}>Configure Smart Supply</Button>
<SmartSupplyModal
open={smartSupplyOpen}
onClose={() => setSmartSupplyOpen(false)}
warehouse={props.warehouse}
/>
</>
)}
{mats}
{products}
</Paper>
);
}
export function IndustryWarehouse(props: IProps): React.ReactElement {
if (props.warehouse instanceof Warehouse) {
return <WarehouseRoot {...props} />;
} else {
return <EmptyWarehouse rerender={props.rerender} city={props.currentCity} />;
}
}
interface IEmptyProps {
city: string;
rerender: () => void;
}
function EmptyWarehouse(props: IEmptyProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const disabled = corp.funds.lt(CorporationConstants.WarehouseInitialCost);
function purchaseWarehouse(): void {
if (disabled) return;
PurchaseWarehouse(corp, division, props.city);
props.rerender();
}
return (
<Paper>
<Button onClick={purchaseWarehouse} disabled={disabled}>
Purchase Warehouse (
<MoneyCost money={CorporationConstants.WarehouseInitialCost} corp={corp} />)
</Button>
</Paper>
);
} }

@ -40,6 +40,7 @@ export function IssueDividendsModal(props: IProps): React.ReactElement {
else { else {
let p = parseFloat(event.target.value); let p = parseFloat(event.target.value);
if (p > 50) p = 50; if (p > 50) p = 50;
if (p < 0) p = 0;
setPercent(p); setPercent(p);
} }
} }

@ -57,23 +57,19 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2); const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6); const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
const newShares = Math.round((shares || 0) / 10e6) * 10e6;
const disabled = shares === null || isNaN(newShares) || newShares < 10e6 || newShares > maxNewShares;
function issueNewShares(): void { function issueNewShares(): void {
if (shares === null) return; if (shares === null) return;
if (disabled) return;
const newSharePrice = Math.round(corp.sharePrice * 0.9); const newSharePrice = Math.round(corp.sharePrice * 0.9);
let newShares = shares; let newShares = shares;
if (isNaN(newShares)) {
dialogBoxCreate("Invalid input for number of new shares");
return;
}
// Round to nearest ten-millionth // Round to nearest ten-millionth
newShares = Math.round(newShares / 10e6) * 10e6; newShares = Math.round(newShares / 10e6) * 10e6;
if (newShares < 10e6 || newShares > maxNewShares) {
dialogBoxCreate("Invalid input for number of new shares");
return;
}
const profit = newShares * newSharePrice; const profit = newShares * newSharePrice;
corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown; corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
corp.totalShares += newShares; corp.totalShares += newShares;
@ -128,7 +124,7 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
</Typography> </Typography>
<EffectText shares={shares} /> <EffectText shares={shares} />
<TextField autoFocus placeholder="# New Shares" onChange={onChange} onKeyDown={onKeyDown} /> <TextField autoFocus placeholder="# New Shares" onChange={onChange} onKeyDown={onKeyDown} />
<Button onClick={issueNewShares} sx={{ mx: 1 }}> <Button disabled={disabled} onClick={issueNewShares} sx={{ mx: 1 }}>
Issue New Shares Issue New Shares
</Button> </Button>
</Modal> </Modal>

@ -5,8 +5,12 @@ import { dialogBoxCreate } from "../../ui/React/DialogBox";
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"; import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
interface IProps { interface IProps {
upgrade: CorporationUpgrade; upgrade: CorporationUpgrade;
@ -14,7 +18,6 @@ interface IProps {
} }
export function LevelableUpgrade(props: IProps): React.ReactElement { export function LevelableUpgrade(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const data = props.upgrade; const data = props.upgrade;
const level = corp.upgrades[data[0]]; const level = corp.upgrades[data[0]];
@ -23,11 +26,6 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
const priceMult = data[2]; const priceMult = data[2];
const cost = baseCost * Math.pow(priceMult, level); const cost = baseCost * Math.pow(priceMult, level);
const text = (
<>
{data[4]} - <MoneyCost money={cost} corp={corp} />
</>
);
const tooltip = data[5]; const tooltip = data[5];
function onClick(): void { function onClick(): void {
if (corp.funds.lt(cost)) return; if (corp.funds.lt(cost)) return;
@ -40,9 +38,15 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
} }
return ( return (
<button className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}> <Grid item xs={4}>
{text} <Box display="flex" alignItems="center" flexDirection="row-reverse">
<span className={"tooltiptext"}>{tooltip}</span> <Button disabled={corp.funds.lt(cost)} sx={{ mx: 1 }} onClick={onClick}>
</button> <MoneyCost money={cost} corp={corp} />
</Button>
<Tooltip title={tooltip}>
<Typography>{data[4]} </Typography>
</Tooltip>
</Box>
</Grid>
); );
} }

@ -1,23 +1,24 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
import { Product } from "../Product"; import { Product } from "../Product";
import { LimitProductProduction } from "../Actions"; import { LimitProductProduction } from "../Actions";
import { Modal } from "../../ui/React/Modal";
interface IProps { interface IProps {
open: boolean;
onClose: () => void;
product: Product; product: Product;
city: string; city: string;
popupId: string;
} }
// Create a popup that lets the player limit the production of a product // Create a popup that lets the player limit the production of a product
export function LimitProductProductionPopup(props: IProps): React.ReactElement { export function LimitProductProductionModal(props: IProps): React.ReactElement {
const [limit, setLimit] = useState<number | null>(null); const [limit, setLimit] = useState<number | null>(null);
function limitProductProduction(): void { function limitProductProduction(): void {
let qty = limit; let qty = limit;
if (qty === null) qty = -1; if (qty === null) qty = -1;
LimitProductProduction(props.product, props.city, qty); LimitProductProduction(props.product, props.city, qty);
removePopup(props.popupId); props.onClose();
} }
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void { function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
@ -30,7 +31,7 @@ export function LimitProductProductionPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <p>
Enter a limit to the amount of this product you would like to product per second. Leave the box empty to set no Enter a limit to the amount of this product you would like to product per second. Leave the box empty to set no
limit. limit.
@ -51,6 +52,6 @@ export function LimitProductProductionPopup(props: IProps): React.ReactElement {
> >
Limit production Limit production
</button> </button>
</> </Modal>
); );
} }

@ -25,9 +25,7 @@ export function MainPanel(props: IProps): React.ReactElement {
if (division === undefined) throw new Error("Cannot find division"); if (division === undefined) throw new Error("Cannot find division");
return ( return (
<Context.Division.Provider value={division}> <Context.Division.Provider value={division}>
<div id="cmpy-mgmt-panel"> <CityTabs rerender={props.rerender} city={CityName.Sector12} />
<CityTabs rerender={props.rerender} city={CityName.Sector12} />
</div>
</Context.Division.Provider> </Context.Division.Provider>
); );
} }

@ -0,0 +1,193 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal";
import { Industries } from "../IndustryData";
import { MakeProduct } from "../Actions";
import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
interface IProps {
open: boolean;
onClose: () => void;
}
function productPlaceholder(tpe: string): string {
if (tpe === Industries.Food) {
return "Restaurant Name";
} else if (tpe === Industries.Healthcare) {
return "Hospital Name";
} else if (tpe === Industries.RealEstate) {
return "Property Name";
}
return "Product Name";
}
// Create a popup that lets the player create a product for their current industry
export function MakeProductModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const allCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] !== 0);
const [city, setCity] = useState(allCities.length > 0 ? allCities[0] : "");
const [name, setName] = useState("");
const [design, setDesign] = useState<number | null>(null);
const [marketing, setMarketing] = useState<number | null>(null);
if (division.hasMaximumNumberProducts()) return <></>;
let createProductPopupText = <></>;
switch (division.type) {
case Industries.Food:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Build and manage a new restaurant!
</>
);
break;
case Industries.Tobacco:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Create a new tobacco product!
</>
);
break;
case Industries.Pharmaceutical:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Design and develop a new pharmaceutical drug!
</>
);
break;
case Industries.Computer:
case "Computer":
createProductPopupText = (
<>
{createProductPopupText}
<br />
Design and manufacture a new computer hardware product!
</>
);
break;
case Industries.Robotics:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Design and create a new robot or robotic system!
</>
);
break;
case Industries.Software:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Develop a new piece of software!
</>
);
break;
case Industries.Healthcare:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Build and manage a new hospital!
</>
);
break;
case Industries.RealEstate:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Develop a new piece of real estate property!
</>
);
break;
default:
createProductPopupText = (
<>
{createProductPopupText}
<br />
Create a new product!
</>
);
return <></>;
}
createProductPopupText = (
<>
{createProductPopupText}
<br />
<br />
To begin developing a product, first choose the city in which it will be designed. The stats of your employees in
the selected city affect the properties of the finished product, such as its quality, performance, and durability.
<br />
<br />
You can also choose to invest money in the design and marketing of the product. Investing money in its design will
result in a superior product. Investing money in marketing the product will help the product's sales.
</>
);
function makeProduct(): void {
if (design === null || marketing === null) return;
try {
MakeProduct(corp, division, city, name, design, marketing);
} catch (err) {
dialogBoxCreate(err + "");
}
props.onClose();
}
function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value);
}
function onProductNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value);
}
function onDesignChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setDesign(null);
else setDesign(parseFloat(event.target.value));
}
function onMarketingChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setMarketing(null);
else setMarketing(parseFloat(event.target.value));
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) makeProduct();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>{createProductPopupText}</Typography>
<Select style={{ margin: "5px" }} onChange={onCityChange} defaultValue={city}>
{allCities.map((cityName: string) => (
<MenuItem key={cityName} value={cityName}>
{cityName}
</MenuItem>
))}
</Select>
<TextField onChange={onProductNameChange} placeholder={productPlaceholder(division.type)} />
<br />
<TextField onChange={onDesignChange} autoFocus={true} type="number" placeholder={"Design investment"} />
<TextField
onChange={onMarketingChange}
onKeyDown={onKeyDown}
type="number"
placeholder={"Marketing investment"}
/>
<Button onClick={makeProduct}>Develop Product</Button>
</Modal>
);
}

@ -1,108 +0,0 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { Industries } from "../IndustryData";
import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry";
import { MakeProduct } from "../Actions";
interface IProps {
popupText: string;
division: IIndustry;
corp: ICorporation;
popupId: string;
}
function productPlaceholder(tpe: string): string {
if (tpe === Industries.Food) {
return "Restaurant Name";
} else if (tpe === Industries.Healthcare) {
return "Hospital Name";
} else if (tpe === Industries.RealEstate) {
return "Property Name";
}
return "Product Name";
}
// Create a popup that lets the player create a product for their current industry
export function MakeProductPopup(props: IProps): React.ReactElement {
const allCities = Object.keys(props.division.offices).filter(
(cityName: string) => props.division.offices[cityName] !== 0,
);
const [city, setCity] = useState(allCities.length > 0 ? allCities[0] : "");
const [name, setName] = useState("");
const [design, setDesign] = useState<number | null>(null);
const [marketing, setMarketing] = useState<number | null>(null);
if (props.division.hasMaximumNumberProducts()) return <></>;
function makeProduct(): void {
if (design === null || marketing === null) return;
try {
MakeProduct(props.corp, props.division, city, name, design, marketing);
} catch (err) {
dialogBoxCreate(err + "");
}
removePopup(props.popupId);
}
function onCityChange(event: React.ChangeEvent<HTMLSelectElement>): void {
setCity(event.target.value);
}
function onProductNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
setName(event.target.value);
}
function onDesignChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setDesign(null);
else setDesign(parseFloat(event.target.value));
}
function onMarketingChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setMarketing(null);
else setMarketing(parseFloat(event.target.value));
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) makeProduct();
}
return (
<>
<p dangerouslySetInnerHTML={{ __html: props.popupText }} />
<select className="dropdown" style={{ margin: "5px" }} onChange={onCityChange} defaultValue={city}>
{allCities.map((cityName: string) => (
<option key={cityName} value={cityName}>
{cityName}
</option>
))}
</select>
<input
onChange={onProductNameChange}
className="text-input"
style={{ margin: "5px" }}
placeholder={productPlaceholder(props.division.type)}
/>
<br />
<input
onChange={onDesignChange}
autoFocus={true}
type="number"
className="text-input"
style={{ margin: "5px" }}
placeholder={"Design investment"}
/>
<input
onChange={onMarketingChange}
onKeyDown={onKeyDown}
type="number"
className="text-input"
style={{ margin: "5px" }}
placeholder={"Marketing investment"}
/>
<button className="std-button" onClick={makeProduct}>
Develop Product
</button>
</>
);
}

@ -0,0 +1,204 @@
// React Component for displaying an Industry's warehouse information
// (right-side panel in the Industry UI)
import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace";
import { Material } from "../Material";
import { Warehouse } from "../Warehouse";
import { ExportModal } from "./ExportModal";
import { MaterialMarketTaModal } from "./MaterialMarketTaModal";
import { SellMaterialModal } from "./SellMaterialModal";
import { PurchaseMaterialModal } from "./PurchaseMaterialModal";
import { numeralWrapper } from "../../ui/numeralFormat";
import { isString } from "../../utils/helpers/isString";
import { Money } from "../../ui/React/Money";
import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
interface IMaterialProps {
warehouse: Warehouse;
city: string;
mat: Material;
rerender: () => void;
}
// Creates the UI for a single Material type
export function MaterialElem(props: IMaterialProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [purchaseMaterialOpen, setPurchaseMaterialOpen] = useState(false);
const [exportOpen, setExportOpen] = useState(false);
const [sellMaterialOpen, setSellMaterialOpen] = useState(false);
const [materialMarketTaOpen, setMaterialMarketTaOpen] = useState(false);
const warehouse = props.warehouse;
const city = props.city;
const mat = props.mat;
const markupLimit = mat.getMarkupLimit();
const office = division.offices[city];
if (!(office instanceof OfficeSpace)) {
throw new Error(`Could not get OfficeSpace object for this city (${city})`);
}
// Numeraljs formatter
const nf = "0.000";
const nfB = "0.000a"; // For numbers that might be biger
// Total gain or loss of this material (per second)
const totalGain = mat.buy + mat.prd + mat.imp - mat.sll - mat.totalExp;
// Flag that determines whether this industry is "new" and the current material should be
// marked with flashing-red lights
const tutorial =
division.newInd && Object.keys(division.reqMats).includes(mat.name) && mat.buy === 0 && mat.imp === 0;
// Purchase material button
const purchaseButtonText = `Buy (${numeralWrapper.format(mat.buy, nfB)})`;
// Sell material button
let sellButtonText: JSX.Element;
if (mat.sllman[0]) {
if (isString(mat.sllman[1])) {
sellButtonText = (
<>
Sell ({numeralWrapper.format(mat.sll, nfB)}/{mat.sllman[1]})
</>
);
} else {
sellButtonText = (
<>
Sell ({numeralWrapper.format(mat.sll, nfB)}/{numeralWrapper.format(mat.sllman[1] as number, nfB)})
</>
);
}
if (mat.marketTa2) {
sellButtonText = (
<>
{sellButtonText} @ <Money money={mat.marketTa2Price} />
</>
);
} else if (mat.marketTa1) {
sellButtonText = (
<>
{sellButtonText} @ <Money money={mat.bCost + markupLimit} />
</>
);
} else if (mat.sCost) {
if (isString(mat.sCost)) {
const sCost = (mat.sCost as string).replace(/MP/g, mat.bCost + "");
sellButtonText = (
<>
{sellButtonText} @ <Money money={eval(sCost)} />
</>
);
} else {
sellButtonText = (
<>
{sellButtonText} @ <Money money={mat.sCost} />
</>
);
}
}
} else {
sellButtonText = <>Sell (0.000/0.000)</>;
}
return (
<Paper>
<Box display="flex">
<Box>
<Tooltip
title={
<Typography>
Buy: {numeralWrapper.format(mat.buy, nfB)} <br />
Prod: {numeralWrapper.format(mat.prd, nfB)} <br />
Sell: {numeralWrapper.format(mat.sll, nfB)} <br />
Export: {numeralWrapper.format(mat.totalExp, nfB)} <br />
Import: {numeralWrapper.format(mat.imp, nfB)}
{corp.unlockUpgrades[2] === 1 && <br />}
{corp.unlockUpgrades[2] === 1 && "Demand: " + numeralWrapper.format(mat.dmd, nf)}
{corp.unlockUpgrades[3] === 1 && <br />}
{corp.unlockUpgrades[3] === 1 && "Competition: " + numeralWrapper.format(mat.cmp, nf)}
</Typography>
}
>
<Typography>
{mat.name}: {numeralWrapper.format(mat.qty, nfB)} ({numeralWrapper.format(totalGain, nfB)}/s)
</Typography>
</Tooltip>
<Tooltip
title={
<Typography>
Market Price: The price you would pay if you were to buy this material on the market
</Typography>
}
>
<Typography>MP: {numeralWrapper.formatMoney(mat.bCost)}</Typography>
</Tooltip>
<Tooltip
title={<Typography>The quality of your material. Higher quality will lead to more sales</Typography>}
>
<Typography>Quality: {numeralWrapper.format(mat.qlt, "0.00a")}</Typography>
</Tooltip>
</Box>
<Box>
<Tooltip
title={tutorial ? <Typography>Purchase your required materials to get production started!</Typography> : ""}
>
<span>
<Button
color={tutorial ? "error" : "primary"}
onClick={() => setPurchaseMaterialOpen(true)}
disabled={props.warehouse.smartSupplyEnabled && Object.keys(division.reqMats).includes(props.mat.name)}
>
{purchaseButtonText}
</Button>
</span>
</Tooltip>
<PurchaseMaterialModal
mat={mat}
warehouse={warehouse}
open={purchaseMaterialOpen}
onClose={() => setPurchaseMaterialOpen(false)}
/>
{corp.unlockUpgrades[0] === 1 && (
<>
<Button onClick={() => setExportOpen(true)}>Export</Button>
<ExportModal mat={mat} open={exportOpen} onClose={() => setExportOpen(false)} />
</>
)}
<br />
<Button
color={division.prodMats.includes(props.mat.name) && !mat.sllman[0] ? "error" : "primary"}
onClick={() => setSellMaterialOpen(true)}
>
{sellButtonText}
</Button>
<SellMaterialModal mat={mat} open={sellMaterialOpen} onClose={() => setSellMaterialOpen(false)} />
{division.hasResearch("Market-TA.I") && (
<>
<Button onClick={() => setMaterialMarketTaOpen(true)}>Market-TA</Button>
<MaterialMarketTaModal
mat={mat}
open={materialMarketTaOpen}
onClose={() => setMaterialMarketTaOpen(false)}
/>
</>
)}
</Box>
</Box>
</Paper>
);
}

@ -1,16 +1,21 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { IIndustry } from "../IIndustry";
import { ICorporation } from "../ICorporation";
import { Material } from "../Material"; import { Material } from "../Material";
import { Modal } from "../../ui/React/Modal";
import { useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import Tooltip from "@mui/material/Tooltip";
interface IMarketTA2Props { interface IMarketTA2Props {
industry: IIndustry;
mat: Material; mat: Material;
} }
function MarketTA2(props: IMarketTA2Props): React.ReactElement { function MarketTA2(props: IMarketTA2Props): React.ReactElement {
if (!props.industry.hasResearch("Market-TA.II")) return <></>; const division = useDivision();
if (!division.hasResearch("Market-TA.II")) return <></>;
const [newCost, setNewCost] = useState<number>(props.mat.bCost); const [newCost, setNewCost] = useState<number>(props.mat.bCost);
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
@ -47,50 +52,47 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
return ( return (
<> <>
<p> <Typography variant="h4">Market-TA.II</Typography>
<br /> <br />
<u> <Typography>
<strong>Market-TA.II</strong>
</u>
<br />
If you sell at {numeralWrapper.formatMoney(sCost)}, then you will sell{" "} If you sell at {numeralWrapper.formatMoney(sCost)}, then you will sell{" "}
{numeralWrapper.format(markup, "0.00000")}x as much compared to if you sold at market price. {numeralWrapper.format(markup, "0.00000")}x as much compared to if you sold at market price.
</p> </Typography>
<input className="text-input" type="number" style={{ marginTop: "4px" }} onChange={onChange} value={newCost} /> <TextField type="number" onChange={onChange} value={newCost} />
<div style={{ display: "block" }}> <br />
<label className="tooltip" htmlFor="cmpy-mgmt-marketa2-checkbox" style={{ color: "white" }}> <FormControlLabel
Use Market-TA.II for Auto-Sale Price control={<Switch checked={props.mat.marketTa2} onChange={onMarketTA2} />}
<span className="tooltiptext"> label={
If this is enabled, then this Material will automatically be sold at the optimal price such that the amount <Tooltip
sold matches the amount produced. (i.e. the highest possible price, while still ensuring that all produced title={
materials will be sold) <Typography>
</span> If this is enabled, then this Material will automatically be sold at the optimal price such that the
</label> amount sold matches the amount produced. (i.e. the highest possible price, while still ensuring that all
<input produced materials will be sold)
id="cmpy-mgmt-marketa2-checkbox" </Typography>
type="checkbox" }
onChange={onMarketTA2} >
checked={props.mat.marketTa2} <Typography>Use Market-TA.II for Auto-Sale Price</Typography>
style={{ margin: "3px" }} </Tooltip>
/> }
</div> />
<p>
<Typography>
Note that Market-TA.II overrides Market-TA.I. This means that if both are enabled, then Market-TA.II will take Note that Market-TA.II overrides Market-TA.I. This means that if both are enabled, then Market-TA.II will take
effect, not Market-TA.I effect, not Market-TA.I
</p> </Typography>
</> </>
); );
} }
interface IProps { interface IProps {
open: boolean;
onClose: () => void;
mat: Material; mat: Material;
industry: IIndustry;
corp: ICorporation;
popupId: string;
} }
// Create a popup that lets the player use the Market TA research for Materials // Create a popup that lets the player use the Market TA research for Materials
export function MaterialMarketTaPopup(props: IProps): React.ReactElement { export function MaterialMarketTaModal(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
@ -103,33 +105,30 @@ export function MaterialMarketTaPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <Typography variant="h4">Market-TA.I</Typography>
<u> <Typography>
<strong>Market-TA.I</strong>
</u>
<br />
The maximum sale price you can mark this up to is {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}. The maximum sale price you can mark this up to is {numeralWrapper.formatMoney(props.mat.bCost + markupLimit)}.
This means that if you set the sale price higher than this, you will begin to experience a loss in number of This means that if you set the sale price higher than this, you will begin to experience a loss in number of
sales sales
</p> </Typography>
<div style={{ display: "block" }}>
<label className="tooltip" htmlFor="cmpy-mgmt-marketa1-checkbox" style={{ color: "white" }}> <FormControlLabel
Use Market-TA.I for Auto-Sale Price control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
<span className="tooltiptext"> label={
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I <Tooltip
(i.e. the price shown above) title={
</span> <Typography>
</label> If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I
<input (i.e. the price shown above)
id="cmpy-mgmt-marketa1-checkbox" </Typography>
type="checkbox" }
onChange={onMarketTA1} >
checked={props.mat.marketTa1} <Typography>Use Market-TA.I for Auto-Sale Price</Typography>
style={{ margin: "3px" }} </Tooltip>
/> }
</div> />
<MarketTA2 mat={props.mat} industry={props.industry} /> <MarketTA2 mat={props.mat} />
</> </Modal>
); );
} }

@ -19,11 +19,16 @@ 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 { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsTable } from "../../ui/React/StatsTable";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
interface IProps { interface IProps {
rerender: () => void; rerender: () => void;
@ -33,67 +38,73 @@ export function Overview({ rerender }: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const profit: number = corp.revenue.minus(corp.expenses).toNumber(); const profit: number = corp.revenue.minus(corp.expenses).toNumber();
const multRows: any[][] = [];
function appendMult(name: string, value: number): void {
if (value === 1) return;
multRows.push([name, numeralWrapper.format(value, "0.000")]);
}
appendMult("Production Multiplier: ", corp.getProductionMultiplier());
appendMult("Storage Multiplier: ", corp.getStorageMultiplier());
appendMult("Advertising Multiplier: ", corp.getAdvertisingMultiplier());
appendMult("Empl. Creativity Multiplier: ", corp.getEmployeeCreMultiplier());
appendMult("Empl. Charisma Multiplier: ", corp.getEmployeeChaMultiplier());
appendMult("Empl. Intelligence Multiplier: ", corp.getEmployeeIntMultiplier());
appendMult("Empl. Efficiency Multiplier: ", corp.getEmployeeEffMultiplier());
appendMult("Sales Multiplier: ", corp.getSalesMultiplier());
appendMult("Scientific Research Multiplier: ", corp.getScientificResearchMultiplier());
return ( return (
<div> <>
<Typography> <StatsTable
Total Funds: <Money money={corp.funds.toNumber()} /> rows={[
<br /> ["Total Funds:", <Money money={corp.funds.toNumber()} />],
Total Revenue: <Money money={corp.revenue.toNumber()} /> / s<br /> ["Total Revenue:", <MoneyRate money={corp.revenue.toNumber()} />],
Total Expenses: <Money money={corp.expenses.toNumber()} /> / s ["Total Expenses:", <MoneyRate money={corp.expenses.toNumber()} />],
<br /> ["Publicly Traded:", corp.public ? "Yes" : "No"],
Total Profits: <Money money={profit} /> / s<br /> ["Owned Stock Shares:", numeralWrapper.format(corp.numShares, "0.000a")],
<DividendsStats profit={profit} /> ["Stock Price:", corp.public ? <Money money={corp.sharePrice} /> : "N/A"],
Publicly Traded: {corp.public ? "Yes" : "No"} ]}
<br /> />
Owned Stock Shares: {numeralWrapper.format(corp.numShares, "0.000a")} <br />
<br /> <Box display="flex">
Stock Price: {corp.public ? <Money money={corp.sharePrice} /> : "N/A"} <Tooltip
<br /> title={
</Typography> <StatsTable
rows={[
["Outstanding Shares:", numeralWrapper.format(corp.issuedShares, "0.000a")],
[
"Private Shares:",
numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a"),
],
]}
/>
}
>
<Typography>Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}</Typography>
</Tooltip>
</Box>
<br />
<DividendsStats profit={profit} />
<br />
<StatsTable rows={multRows} />
<br />
<BonusTime />
<Tooltip <Tooltip
title={ title={
<Typography> <Typography>
Outstanding Shares: {numeralWrapper.format(corp.issuedShares, "0.000a")} Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' This is a .lit file
<br /> that guides you through the beginning of setting up a Corporation and provides some tips/pointers for
Private Shares: {numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a")} helping you get started with managing it.
</Typography> </Typography>
} }
> >
<Typography className="tooltip"> <Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
</Typography>
</Tooltip> </Tooltip>
<br /> {corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
<br /> <BribeButton />
<Mult name="Production Multiplier: " mult={corp.getProductionMultiplier()} />
<Mult name="Storage Multiplier: " mult={corp.getStorageMultiplier()} />
<Mult name="Advertising Multiplier: " mult={corp.getAdvertisingMultiplier()} />
<Mult name="Empl. Creativity Multiplier: " mult={corp.getEmployeeCreMultiplier()} />
<Mult name="Empl. Charisma Multiplier: " mult={corp.getEmployeeChaMultiplier()} />
<Mult name="Empl. Intelligence Multiplier: " mult={corp.getEmployeeIntMultiplier()} />
<Mult name="Empl. Efficiency Multiplier: " mult={corp.getEmployeeEffMultiplier()} />
<Mult name="Sales Multiplier: " mult={corp.getSalesMultiplier()} />
<Mult name="Scientific Research Multiplier: " mult={corp.getScientificResearchMultiplier()} />
<br />
<BonusTime />
<div>
<Tooltip
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>
}
>
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
</Tooltip>
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
<BribeButton />
</div>
<br /> <br />
<Upgrades rerender={rerender} /> <Upgrades rerender={rerender} />
</div> </>
); );
} }
@ -102,7 +113,6 @@ interface IPrivateButtonsProps {
} }
// Render the buttons for when your Corporation is still private // Render the buttons for when your Corporation is still private
function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement { function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const [findInvestorsopen, setFindInvestorsopen] = useState(false); const [findInvestorsopen, setFindInvestorsopen] = useState(false);
const [goPublicopen, setGoPublicopen] = useState(false); const [goPublicopen, setGoPublicopen] = useState(false);
@ -115,9 +125,11 @@ function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement
return ( return (
<> <>
<Tooltip title={<Typography>{findInvestorsTooltip}</Typography>}> <Tooltip title={<Typography>{findInvestorsTooltip}</Typography>}>
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}> <span>
Find Investors <Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
</Button> Find Investors
</Button>
</span>
</Tooltip> </Tooltip>
<Tooltip <Tooltip
title={ title={
@ -148,21 +160,28 @@ function Upgrades({ rerender }: IUpgradeProps): React.ReactElement {
} }
return ( return (
<div className={"cmpy-mgmt-upgrade-container"}> <>
<h1 className={"cmpy-mgmt-upgrade-header"}> Unlocks </h1> <Paper sx={{ p: 1, my: 1 }}>
{Object.values(CorporationUnlockUpgrades) <Typography variant="h4">Unlocks</Typography>
.filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0) <Grid container>
.map((upgrade: CorporationUnlockUpgrade) => ( {Object.values(CorporationUnlockUpgrades)
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade[0]} /> .filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0)
))} .map((upgrade: CorporationUnlockUpgrade) => (
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade[0]} />
<h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1> ))}
{corp.upgrades </Grid>
.map((level: number, i: number) => CorporationUpgrades[i]) </Paper>
.map((upgrade: CorporationUpgrade) => ( <Paper sx={{ p: 1, my: 1 }}>
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade[0]} /> <Typography variant="h4">Upgrades</Typography>
))} <Grid container>
</div> {corp.upgrades
.map((level: number, i: number) => CorporationUpgrades[i])
.map((upgrade: CorporationUpgrade) => (
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade[0]} />
))}
</Grid>
</Paper>
</>
); );
} }
@ -193,20 +212,28 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
return ( return (
<> <>
<Tooltip title={<Typography>{sellSharesTooltip}</Typography>}> <Tooltip title={<Typography>{sellSharesTooltip}</Typography>}>
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}> <span>
Sell Shares <Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
</Button> Sell Shares
</Button>
</span>
</Tooltip> </Tooltip>
<SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} /> <SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} />
<Tooltip title={<Typography>Buy back shares you that previously issued or sold at market price.</Typography>}> <Tooltip title={<Typography>Buy back shares you that previously issued or sold at market price.</Typography>}>
<Button onClick={() => setBuybackSharesOpen(true)}>Buyback shares</Button> <span>
<Button disabled={corp.issuedShares > 0.5} onClick={() => setBuybackSharesOpen(true)}>
Buyback shares
</Button>
</span>
</Tooltip> </Tooltip>
<BuybackSharesModal open={buybackSharesOpen} onClose={() => setBuybackSharesOpen(false)} rerender={rerender} /> <BuybackSharesModal open={buybackSharesOpen} onClose={() => setBuybackSharesOpen(false)} rerender={rerender} />
<br /> <br />
<Tooltip title={<Typography>{issueNewSharesTooltip}</Typography>}> <Tooltip title={<Typography>{issueNewSharesTooltip}</Typography>}>
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}> <span>
Issue New Shares <Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
</Button> Issue New Shares
</Button>
</span>
</Tooltip> </Tooltip>
<IssueNewSharesModal open={issueNewSharesOpen} onClose={() => setIssueNewSharesOpen(false)} /> <IssueNewSharesModal open={issueNewSharesOpen} onClose={() => setIssueNewSharesOpen(false)} />
<Tooltip <Tooltip
@ -264,34 +291,19 @@ function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement {
const dividendsPerShare = totalDividends / corp.totalShares; const dividendsPerShare = totalDividends / corp.totalShares;
const playerEarnings = corp.numShares * dividendsPerShare; const playerEarnings = corp.numShares * dividendsPerShare;
return ( return (
<> <StatsTable
Retained Profits (after dividends): <Money money={retainedEarnings} /> / s rows={[
<br /> ["Retained Profits (after dividends):", <MoneyRate money={retainedEarnings} />],
<br /> ["Dividend Percentage:", numeralWrapper.format(corp.dividendPercentage / 100, "0%")],
Dividend Percentage: {numeralWrapper.format(corp.dividendPercentage / 100, "0%")} ["Dividends per share:", <MoneyRate money={dividendsPerShare} />],
<br /> ["Your earnings as a shareholder (Pre-Tax):", <MoneyRate money={playerEarnings} />],
Dividends per share: <Money money={dividendsPerShare} /> / s<br /> ["Dividend Tax Rate:", <>{corp.dividendTaxPercentage}%</>],
Your earnings as a shareholder (Pre-Tax): <Money money={playerEarnings} /> / s<br /> [
Dividend Tax Rate: {corp.dividendTaxPercentage}%<br /> "Your earnings as a shareholder (Post-Tax):",
Your earnings as a shareholder (Post-Tax):{" "} <MoneyRate money={playerEarnings * (1 - corp.dividendTaxPercentage / 100)} />,
<Money money={playerEarnings * (1 - corp.dividendTaxPercentage / 100)} /> / s<br /> ],
<br /> ]}
</> />
);
}
interface IMultProps {
name: string;
mult: number;
}
function Mult({ name, mult }: IMultProps): React.ReactElement {
if (mult <= 1) return <></>;
return (
<Typography>
{name}
{numeralWrapper.format(mult, "0.000")}
<br />
</Typography>
); );
} }

@ -0,0 +1,207 @@
import React, { useState } from "react";
import { CorporationConstants } from "../data/Constants";
import { Product } from "../Product";
import { DiscontinueProductModal } from "./DiscontinueProductModal";
import { LimitProductProductionModal } from "./LimitProductProductionModal";
import { SellProductModal } from "./SellProductModal";
import { ProductMarketTaModal } from "./ProductMarketTaModal";
import { numeralWrapper } from "../../ui/numeralFormat";
import { isString } from "../../utils/helpers/isString";
import { Money } from "../../ui/React/Money";
import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Paper from "@mui/material/Paper";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
interface IProductProps {
city: string;
product: Product;
rerender: () => void;
}
// Creates the UI for a single Product type
export function ProductElem(props: IProductProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [sellOpen, setSellOpen] = useState(false);
const [limitOpen, setLimitOpen] = useState(false);
const [discontinueOpen, setDiscontinueOpen] = useState(false);
const [marketTaOpen, setMarketTaOpen] = useState(false);
const city = props.city;
const product = props.product;
// Numeraljs formatters
const nf = "0.000";
const nfB = "0.000a"; // For numbers that might be big
const hasUpgradeDashboard = division.hasResearch("uPgrade: Dashboard");
// Total product gain = production - sale
const totalGain = product.data[city][1] - product.data[city][2];
// Sell button
let sellButtonText: JSX.Element;
if (product.sllman[city][0]) {
if (isString(product.sllman[city][1])) {
sellButtonText = (
<>
Sell ({numeralWrapper.format(product.data[city][2], nfB)}/{product.sllman[city][1]})
</>
);
} else {
sellButtonText = (
<>
Sell ({numeralWrapper.format(product.data[city][2], nfB)}/
{numeralWrapper.format(product.sllman[city][1], nfB)})
</>
);
}
} else {
sellButtonText = <>Sell (0.000/0.000)</>;
}
if (product.marketTa2) {
sellButtonText = (
<>
{sellButtonText} @ <Money money={product.marketTa2Price[city]} />
</>
);
} else if (product.marketTa1) {
const markupLimit = product.rat / product.mku;
sellButtonText = (
<>
{sellButtonText} @ <Money money={product.pCost + markupLimit} />
</>
);
} else if (product.sCost) {
if (isString(product.sCost)) {
const sCost = (product.sCost as string).replace(/MP/g, product.pCost + "");
sellButtonText = (
<>
{sellButtonText} @ <Money money={eval(sCost)} />
</>
);
} else {
sellButtonText = (
<>
{sellButtonText} @ <Money money={product.sCost} />
</>
);
}
}
// Limit Production button
let limitProductionButtonText = "Limit Production";
if (product.prdman[city][0]) {
limitProductionButtonText += " (" + numeralWrapper.format(product.prdman[city][1], nf) + ")";
}
return (
<Paper>
{!product.fin ? (
<>
<Typography>
Designing {product.name} (req. Operations/Engineers in {product.createCity})...
</Typography>
<br />
<Typography>{numeralWrapper.format(product.prog, "0.00")}% complete</Typography>
</>
) : (
<>
<Box display="flex">
<Tooltip
title={
<Typography>
Prod: {numeralWrapper.format(product.data[city][1], nfB)}/s
<br />
Sell: {numeralWrapper.format(product.data[city][2], nfB)} /s
</Typography>
}
>
<Typography>
{product.name}: {numeralWrapper.format(product.data[city][0], nfB)} (
{numeralWrapper.format(totalGain, nfB)}
/s)
</Typography>
</Tooltip>
</Box>
<Box display="flex">
<Tooltip
title={
<Typography>
Quality: {numeralWrapper.format(product.qlt, nf)} <br />
Performance: {numeralWrapper.format(product.per, nf)} <br />
Durability: {numeralWrapper.format(product.dur, nf)} <br />
Reliability: {numeralWrapper.format(product.rel, nf)} <br />
Aesthetics: {numeralWrapper.format(product.aes, nf)} <br />
Features: {numeralWrapper.format(product.fea, nf)}
{corp.unlockUpgrades[2] === 1 && <br />}
{corp.unlockUpgrades[2] === 1 && "Demand: " + numeralWrapper.format(product.dmd, nf)}
{corp.unlockUpgrades[3] === 1 && <br />}
{corp.unlockUpgrades[3] === 1 && "Competition: " + numeralWrapper.format(product.cmp, nf)}
</Typography>
}
>
<Typography>Rating: {numeralWrapper.format(product.rat, nf)}</Typography>
</Tooltip>
</Box>
<Box display="flex">
<Tooltip title={<Typography>An estimate of the material cost it takes to create this Product.</Typography>}>
<Typography>
Est. Production Cost:{" "}
{numeralWrapper.formatMoney(product.pCost / CorporationConstants.ProductProductionCostRatio)}
</Typography>
</Tooltip>
</Box>
<Box display="flex">
<Tooltip
title={
<Typography>
An estimate of how much consumers are willing to pay for this product. Setting the sale price above
this may result in less sales. Setting the sale price below this may result in more sales.
</Typography>
}
>
<Typography>Est. Market Price: {numeralWrapper.formatMoney(product.pCost)}</Typography>
</Tooltip>
</Box>
</>
)}
{(hasUpgradeDashboard || product.fin) && (
<>
<Button onClick={() => setSellOpen(true)}>{sellButtonText}</Button>
<SellProductModal product={product} city={city} open={sellOpen} onClose={() => setSellOpen(false)} />
<br />
<Button onClick={() => setLimitOpen(true)}>{limitProductionButtonText}</Button>
<LimitProductProductionModal
product={product}
city={city}
open={limitOpen}
onClose={() => setLimitOpen(false)}
/>
<Button onClick={() => setDiscontinueOpen(true)}>Discontinue</Button>
<DiscontinueProductModal
product={product}
rerender={props.rerender}
open={discontinueOpen}
onClose={() => setDiscontinueOpen(false)}
/>
{division.hasResearch("Market-TA.I") && (
<>
<Button onClick={() => setMarketTaOpen(true)}>Market-TA</Button>
<ProductMarketTaModal product={product} open={marketTaOpen} onClose={() => setMarketTaOpen(false)} />
</>
)}
</>
)}
</Paper>
);
}

@ -1,15 +1,14 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { IIndustry } from "../IIndustry";
import { Product } from "../Product"; import { Product } from "../Product";
import { Modal } from "../../ui/React/Modal";
import { useDivision } from "./Context";
interface IProps { interface ITa2Props {
product: Product; product: Product;
industry: IIndustry;
popupId: string;
} }
function MarketTA2(props: IProps): React.ReactElement { function MarketTA2(props: ITa2Props): React.ReactElement {
const markupLimit = props.product.rat / props.product.mku; const markupLimit = props.product.rat / props.product.mku;
const [value, setValue] = useState(props.product.pCost); const [value, setValue] = useState(props.product.pCost);
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
@ -72,8 +71,15 @@ function MarketTA2(props: IProps): React.ReactElement {
); );
} }
interface IProps {
open: boolean;
onClose: () => void;
product: Product;
}
// Create a popup that lets the player use the Market TA research for Products // Create a popup that lets the player use the Market TA research for Products
export function ProductMarketTaPopup(props: IProps): React.ReactElement { export function ProductMarketTaModal(props: IProps): React.ReactElement {
const division = useDivision();
const markupLimit = props.product.rat / props.product.mku; const markupLimit = props.product.rat / props.product.mku;
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
@ -86,36 +92,36 @@ export function ProductMarketTaPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <>
<u> <p>
<strong>Market-TA.I</strong> <u>
</u> <strong>Market-TA.I</strong>
<br /> </u>
The maximum sale price you can mark this up to is{" "} <br />
{numeralWrapper.formatMoney(props.product.pCost + markupLimit)}. This means that if you set the sale price The maximum sale price you can mark this up to is{" "}
higher than this, you will begin to experience a loss in number of sales. {numeralWrapper.formatMoney(props.product.pCost + markupLimit)}. This means that if you set the sale price
</p> higher than this, you will begin to experience a loss in number of sales.
<div style={{ display: "block" }}> </p>
<label className="tooltip" htmlFor="cmpy-mgmt-marketa1-checkbox" style={{ color: "white" }}> <div style={{ display: "block" }}>
Use Market-TA.I for Auto-Sale Price <label className="tooltip" htmlFor="cmpy-mgmt-marketa1-checkbox" style={{ color: "white" }}>
<span className="tooltiptext"> Use Market-TA.I for Auto-Sale Price
If this is enabled, then this Product will automatically be sold at the price identified by Market-TA.I <span className="tooltiptext">
(i.e. the price shown above). If this is enabled, then this Product will automatically be sold at the price identified by Market-TA.I
</span> (i.e. the price shown above).
</label> </span>
<input </label>
onChange={onChange} <input
className="text-input" onChange={onChange}
id="cmpy-mgmt-marketa1-checkbox" className="text-input"
style={{ margin: "3px" }} id="cmpy-mgmt-marketa1-checkbox"
type="checkbox" style={{ margin: "3px" }}
checked={props.product.marketTa1} type="checkbox"
/> checked={props.product.marketTa1}
</div> />
{props.industry.hasResearch("Market-TA.II") && ( </div>
<MarketTA2 product={props.product} industry={props.industry} popupId={props.popupId} /> {division.hasResearch("Market-TA.II") && <MarketTA2 product={props.product} />}
)} </>
</> </Modal>
); );
} }

@ -1,13 +1,15 @@
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 { MaterialSizes } from "../MaterialSizes"; import { MaterialSizes } from "../MaterialSizes";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { Material } from "../Material"; import { Material } from "../Material";
import { IIndustry } from "../IIndustry";
import { ICorporation } from "../ICorporation";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { BuyMaterial } from "../Actions"; import { BuyMaterial } from "../Actions";
import { Modal } from "../../ui/React/Modal";
import { useCorporation, useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
interface IBulkPurchaseTextProps { interface IBulkPurchaseTextProps {
warehouse: Warehouse; warehouse: Warehouse;
@ -36,15 +38,14 @@ function BulkPurchaseText(props: IBulkPurchaseTextProps): React.ReactElement {
} }
} }
interface IProps { interface IBPProps {
onClose: () => void;
mat: Material; mat: Material;
industry: IIndustry;
warehouse: Warehouse; warehouse: Warehouse;
corp: ICorporation;
popupId: string;
} }
function BulkPurchase(props: IProps): React.ReactElement { function BulkPurchase(props: IBPProps): React.ReactElement {
const corp = useCorporation();
const [buyAmt, setBuyAmt] = useState(""); const [buyAmt, setBuyAmt] = useState("");
function bulkPurchase(): void { function bulkPurchase(): void {
@ -61,15 +62,14 @@ function BulkPurchase(props: IProps): React.ReactElement {
dialogBoxCreate("Invalid input amount"); dialogBoxCreate("Invalid input amount");
} else { } else {
const cost = amount * props.mat.bCost; const cost = amount * props.mat.bCost;
if (props.corp.funds.gt(cost)) { if (corp.funds.gt(cost)) {
props.corp.funds = props.corp.funds.minus(cost); corp.funds = corp.funds.minus(cost);
props.mat.qty += amount; props.mat.qty += amount;
} else { } else {
dialogBoxCreate(`You cannot afford this purchase.`); dialogBoxCreate(`You cannot afford this purchase.`);
return; return;
} }
props.onClose();
removePopup(props.popupId);
} }
} }
@ -83,28 +83,34 @@ function BulkPurchase(props: IProps): React.ReactElement {
return ( return (
<> <>
<p> <Typography>
Enter the amount of {props.mat.name} you would like to bulk purchase. This purchases the specified amount Enter the amount of {props.mat.name} you would like to bulk purchase. This purchases the specified amount
instantly (all at once). instantly (all at once).
</p> </Typography>
<BulkPurchaseText warehouse={props.warehouse} mat={props.mat} amount={buyAmt} /> <BulkPurchaseText warehouse={props.warehouse} mat={props.mat} amount={buyAmt} />
<input <TextField
value={buyAmt}
onChange={onChange} onChange={onChange}
type="number" type="number"
placeholder="Bulk Purchase amount" placeholder="Bulk Purchase amount"
style={{ margin: "5px" }}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<button className="std-button" onClick={bulkPurchase}> <Button onClick={bulkPurchase}>Confirm Bulk Purchase</Button>
Confirm Bulk Purchase
</button>
</> </>
); );
} }
interface IProps {
open: boolean;
onClose: () => void;
mat: Material;
warehouse: Warehouse;
}
// Create a popup that lets the player purchase a Material // Create a popup that lets the player purchase a Material
export function PurchaseMaterialPopup(props: IProps): React.ReactElement { export function PurchaseMaterialModal(props: IProps): React.ReactElement {
const [buyAmt, setBuyAmt] = useState(props.mat.buy ? props.mat.buy : null); const division = useDivision();
const [buyAmt, setBuyAmt] = useState(props.mat.buy ? props.mat.buy : 0);
function purchaseMaterial(): void { function purchaseMaterial(): void {
if (buyAmt === null) return; if (buyAmt === null) return;
@ -114,12 +120,12 @@ export function PurchaseMaterialPopup(props: IProps): React.ReactElement {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }
removePopup(props.popupId); props.onClose();
} }
function clearPurchase(): void { function clearPurchase(): void {
props.mat.buy = 0; props.mat.buy = 0;
removePopup(props.popupId); props.onClose();
} }
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void { function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
@ -131,35 +137,26 @@ export function PurchaseMaterialPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <>
Enter the amount of {props.mat.name} you would like to purchase per second. This material's cost changes <Typography>
constantly. Enter the amount of {props.mat.name} you would like to purchase per second. This material's cost changes
</p> constantly.
<input </Typography>
onChange={onChange} <TextField
className="text-input" value={buyAmt}
autoFocus={true} onChange={onChange}
placeholder="Purchase amount" autoFocus={true}
type="number" placeholder="Purchase amount"
style={{ margin: "5px" }} type="number"
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/>
<button onClick={purchaseMaterial} className="std-button">
Confirm
</button>
<button onClick={clearPurchase} className="std-button">
Clear Purchase
</button>
{props.industry.hasResearch("Bulk Purchasing") && (
<BulkPurchase
corp={props.corp}
mat={props.mat}
industry={props.industry}
warehouse={props.warehouse}
popupId={props.popupId}
/> />
)} <Button onClick={purchaseMaterial}>Confirm</Button>
</> <Button onClick={clearPurchase}>Clear Purchase</Button>
{division.hasResearch("Bulk Purchasing") && (
<BulkPurchase onClose={props.onClose} mat={props.mat} warehouse={props.warehouse} />
)}
</>
</Modal>
); );
} }

@ -6,6 +6,7 @@ import { CorporationConstants } from "../data/Constants";
import { Treant } from "treant-js"; import { Treant } from "treant-js";
import { IIndustry } from "../IIndustry"; import { IIndustry } from "../IIndustry";
import { Research } from "../Actions"; import { Research } from "../Actions";
import Typography from "@mui/material/Typography";
interface IProps { interface IProps {
industry: IIndustry; industry: IIndustry;

@ -1,9 +1,11 @@
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 { ICorporation } from "../ICorporation";
import { Material } from "../Material"; import { Material } from "../Material";
import { SellMaterial } from "../Actions"; import { SellMaterial } from "../Actions";
import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
function initialPrice(mat: Material): string { function initialPrice(mat: Material): string {
let val = mat.sCost ? mat.sCost + "" : ""; let val = mat.sCost ? mat.sCost + "" : "";
@ -16,13 +18,13 @@ function initialPrice(mat: Material): string {
} }
interface IProps { interface IProps {
open: boolean;
onClose: () => void;
mat: Material; mat: Material;
corp: ICorporation;
popupId: string;
} }
// Create a popup that let the player manage sales of a material // Create a popup that let the player manage sales of a material
export function SellMaterialPopup(props: IProps): React.ReactElement { export function SellMaterialModal(props: IProps): React.ReactElement {
const [amt, setAmt] = useState<string>(props.mat.sllman[1] ? props.mat.sllman[1] + "" : ""); const [amt, setAmt] = useState<string>(props.mat.sllman[1] ? props.mat.sllman[1] + "" : "");
const [price, setPrice] = useState<string>(initialPrice(props.mat)); const [price, setPrice] = useState<string>(initialPrice(props.mat));
@ -32,8 +34,7 @@ export function SellMaterialPopup(props: IProps): React.ReactElement {
} catch (err) { } catch (err) {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }
props.onClose();
removePopup(props.popupId);
} }
function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void { function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {
@ -49,8 +50,8 @@ export function SellMaterialPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <Typography>
Enter the maximum amount of {props.mat.name} you would like to sell per second, as well as the price at which Enter the maximum amount of {props.mat.name} you would like to sell per second, as well as the price at which
you would like to sell at. you would like to sell at.
<br /> <br />
@ -70,30 +71,18 @@ export function SellMaterialPopup(props: IProps): React.ReactElement {
When setting the sell price, you can use the 'MP' variable to designate a dynamically changing price that When setting the sell price, you can use the 'MP' variable to designate a dynamically changing price that
depends on the market price. For example, if you set the sell price to 'MP+10' then it will always be sold at depends on the market price. For example, if you set the sell price to 'MP+10' then it will always be sold at
$10 above the market price. $10 above the market price.
</p> </Typography>
<br /> <br />
<input <TextField
className="text-input"
value={amt} value={amt}
autoFocus={true} autoFocus={true}
type="text" type="text"
placeholder="Sell amount" placeholder="Sell amount"
style={{ marginTop: "4px" }}
onChange={onAmtChange} onChange={onAmtChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<input <TextField value={price} type="text" placeholder="Sell price" onChange={onPriceChange} onKeyDown={onKeyDown} />
className="text-input" <Button onClick={sellMaterial}>Confirm</Button>
value={price} </Modal>
type="text"
placeholder="Sell price"
style={{ marginTop: "4px" }}
onChange={onPriceChange}
onKeyDown={onKeyDown}
/>
<button className="std-button" onClick={sellMaterial}>
Confirm
</button>
</>
); );
} }

@ -1,8 +1,14 @@
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 { Product } from "../Product"; import { Product } from "../Product";
import { SellProduct } from "../Actions"; import { SellProduct } from "../Actions";
import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
function initialPrice(product: Product): string { function initialPrice(product: Product): string {
let val = product.sCost ? product.sCost + "" : ""; let val = product.sCost ? product.sCost + "" : "";
@ -15,13 +21,14 @@ function initialPrice(product: Product): string {
} }
interface IProps { interface IProps {
open: boolean;
onClose: () => void;
product: Product; product: Product;
city: string; city: string;
popupId: string;
} }
// Create a popup that let the player manage sales of a material // Create a popup that let the player manage sales of a material
export function SellProductPopup(props: IProps): React.ReactElement { export function SellProductModal(props: IProps): React.ReactElement {
const [checked, setChecked] = useState(true); const [checked, setChecked] = useState(true);
const [iQty, setQty] = useState<string>( const [iQty, setQty] = useState<string>(
props.product.sllman[props.city][1] ? props.product.sllman[props.city][1] : "", props.product.sllman[props.city][1] ? props.product.sllman[props.city][1] : "",
@ -39,7 +46,7 @@ export function SellProductPopup(props: IProps): React.ReactElement {
dialogBoxCreate(err + ""); dialogBoxCreate(err + "");
} }
removePopup(props.popupId); props.onClose();
} }
function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void { function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {
@ -55,8 +62,8 @@ export function SellProductPopup(props: IProps): React.ReactElement {
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<p> <Typography>
Enter the maximum amount of {props.product.name} you would like to sell per second, as well as the price at Enter the maximum amount of {props.product.name} you would like to sell per second, as well as the price at
which you would like to sell it at. which you would like to sell it at.
<br /> <br />
@ -76,40 +83,22 @@ export function SellProductPopup(props: IProps): React.ReactElement {
When setting the sell price, you can use the 'MP' variable to set a dynamically changing price that depends on When setting the sell price, you can use the 'MP' variable to set a dynamically changing price that depends on
the Product's estimated market price. For example, if you set it to 'MP*5' then it will always be sold at five the Product's estimated market price. For example, if you set it to 'MP*5' then it will always be sold at five
times the estimated market price. times the estimated market price.
</p> </Typography>
<br /> <br />
<input <TextField
className="text-input"
value={iQty} value={iQty}
autoFocus={true} autoFocus={true}
type="text" type="text"
placeholder="Sell amount" placeholder="Sell amount"
style={{ marginTop: "4px" }}
onChange={onAmtChange} onChange={onAmtChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<input <TextField value={px} type="text" placeholder="Sell price" onChange={onPriceChange} onKeyDown={onKeyDown} />
className="text-input" <Button onClick={sellProduct}>Confirm</Button>
value={px} <FormControlLabel
type="text" control={<Switch checked={checked} onChange={onCheckedChange} />}
placeholder="Sell price" label={<Typography>Use same 'Sell Amount' for all cities</Typography>}
style={{ marginTop: "4px" }}
onChange={onPriceChange}
onKeyDown={onKeyDown}
/> />
<button className="std-button" onClick={sellProduct}> </Modal>
Confirm
</button>
<div style={{ border: "1px solid white", display: "inline-block" }}>
<label htmlFor={props.popupId + "-checkbox"}>Use same 'Sell Amount' for all cities</label>
<input
checked={checked}
onChange={onCheckedChange}
id={props.popupId + "-checkbox"}
style={{ margin: "2px" }}
type="checkbox"
/>
</div>
</>
); );
} }

@ -23,6 +23,8 @@ export function SellSharesModal(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState<number | null>(null); const [shares, setShares] = useState<number | null>(null);
const disabled = shares === null || isNaN(shares) || shares <= 0 || shares > corp.numShares;
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void { function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setShares(null); if (event.target.value === "") setShares(null);
else setShares(Math.round(parseFloat(event.target.value))); else setShares(Math.round(parseFloat(event.target.value)));
@ -47,42 +49,37 @@ export function SellSharesModal(props: IProps): React.ReactElement {
function sell(): void { function sell(): void {
if (shares === null) return; if (shares === null) return;
if (isNaN(shares) || shares <= 0) { if (disabled) return;
dialogBoxCreate("ERROR: Invalid value for number of shares"); const stockSaleResults = corp.calculateShareSale(shares);
} else if (shares > corp.numShares) { const profit = stockSaleResults[0];
dialogBoxCreate("ERROR: You don't have this many shares to sell"); const newSharePrice = stockSaleResults[1];
} else { const newSharesUntilUpdate = stockSaleResults[2];
const stockSaleResults = corp.calculateShareSale(shares);
const profit = stockSaleResults[0];
const newSharePrice = stockSaleResults[1];
const newSharesUntilUpdate = stockSaleResults[2];
corp.numShares -= shares; corp.numShares -= shares;
if (isNaN(corp.issuedShares)) { if (isNaN(corp.issuedShares)) {
console.error(`Corporation issuedShares is NaN: ${corp.issuedShares}`); console.error(`Corporation issuedShares is NaN: ${corp.issuedShares}`);
const res = corp.issuedShares; const res = corp.issuedShares;
if (isNaN(res)) { if (isNaN(res)) {
corp.issuedShares = 0; corp.issuedShares = 0;
} else { } else {
corp.issuedShares = res; corp.issuedShares = res;
}
} }
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(corp.sharePrice)} ` +
`as a result of dilution.`,
);
props.rerender();
} }
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(corp.sharePrice)} ` +
`as a result of dilution.`,
);
props.rerender();
} }
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void { function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
@ -112,7 +109,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
onChange={changeShares} onChange={changeShares}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<Button onClick={sell} sx={{ mx: 1 }}> <Button disabled={disabled} onClick={sell} sx={{ mx: 1 }}>
Sell shares Sell shares
</Button> </Button>
</Modal> </Modal>

@ -1,12 +1,14 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Warehouse } from "../Warehouse"; import { Warehouse } from "../Warehouse";
import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry";
import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../Actions"; import { SetSmartSupply, SetSmartSupplyUseLeftovers } from "../Actions";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Material } from "../Material"; import { Material } from "../Material";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Modal } from "../../ui/React/Modal";
import { useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
interface ILeftoverProps { interface ILeftoverProps {
matName: string; matName: string;
@ -26,33 +28,31 @@ function Leftover(props: ILeftoverProps): React.ReactElement {
setChecked(event.target.checked); setChecked(event.target.checked);
} }
const matNameId = `${props.matName}-use-leftovers`;
return ( return (
<div key={props.matName}> <>
<label style={{ color: "white" }} htmlFor={matNameId}> <FormControlLabel
{props.warehouse.materials[props.matName].name} control={<Switch checked={checked} onChange={onChange} />}
</label> label={<Typography>{props.warehouse.materials[props.matName].name}</Typography>}
<input type={"checkbox"} id={matNameId} onChange={onChange} style={{ margin: "3px" }} checked={checked} /> />
<br /> <br />
</div> </>
); );
} }
interface IProps { interface IProps {
division: IIndustry; open: boolean;
onClose: () => void;
warehouse: Warehouse; warehouse: Warehouse;
corp: ICorporation;
player: IPlayer;
popupId: string;
} }
export function SmartSupplyPopup(props: IProps): React.ReactElement { export function SmartSupplyModal(props: IProps): React.ReactElement {
const division = useDivision();
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
function rerender(): void { function rerender(): void {
setRerender((old) => !old); setRerender((old) => !old);
} }
// Smart Supply Checkbox // Smart Supply Checkbox
const smartSupplyCheckboxId = "cmpy-mgmt-smart-supply-checkbox";
function smartSupplyOnChange(e: React.ChangeEvent<HTMLInputElement>): void { function smartSupplyOnChange(e: React.ChangeEvent<HTMLInputElement>): void {
SetSmartSupply(props.warehouse, e.target.checked); SetSmartSupply(props.warehouse, e.target.checked);
rerender(); rerender();
@ -62,25 +62,21 @@ export function SmartSupplyPopup(props: IProps): React.ReactElement {
const mats = []; const mats = [];
for (const matName in props.warehouse.materials) { for (const matName in props.warehouse.materials) {
if (!(props.warehouse.materials[matName] instanceof Material)) continue; if (!(props.warehouse.materials[matName] instanceof Material)) continue;
if (!Object.keys(props.division.reqMats).includes(matName)) continue; if (!Object.keys(division.reqMats).includes(matName)) continue;
mats.push(<Leftover key={matName} warehouse={props.warehouse} matName={matName} />); mats.push(<Leftover key={matName} warehouse={props.warehouse} matName={matName} />);
} }
return ( return (
<> <Modal open={props.open} onClose={props.onClose}>
<label style={{ color: "white" }} htmlFor={smartSupplyCheckboxId}> <>
Enable Smart Supply <FormControlLabel
</label> control={<Switch checked={props.warehouse.smartSupplyEnabled} onChange={smartSupplyOnChange} />}
<input label={<Typography>Enable Smart Supply</Typography>}
type={"checkbox"} />
id={smartSupplyCheckboxId} <br />
onChange={smartSupplyOnChange} <Typography>Use materials already in the warehouse instead of buying new ones, if available:</Typography>
style={{ margin: "3px" }} {mats}
checked={props.warehouse.smartSupplyEnabled} </>
/> </Modal>
<br />
<p>Use materials already in the warehouse instead of buying new ones, if available:</p>
{mats}
</>
); );
} }

@ -0,0 +1,84 @@
import React, { useState } from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { OfficeSpace } from "../OfficeSpace";
import { ThrowParty } from "../Actions";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
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;
office: OfficeSpace;
rerender: () => void;
}
export function ThrowPartyModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const [cost, setCost] = useState(0);
const totalCost = cost * props.office.employees.length;
const canParty = corp.funds.gte(totalCost);
function changeCost(event: React.ChangeEvent<HTMLInputElement>): void {
let x = parseFloat(event.target.value);
if (isNaN(x)) x = 0;
setCost(x);
}
function throwParty(): void {
if (cost === null || isNaN(cost) || cost < 0) {
dialogBoxCreate("Invalid value entered");
} else {
if (!canParty) {
dialogBoxCreate("You don't have enough company funds to throw a party!");
} else {
const mult = ThrowParty(corp, props.office, cost);
dialogBoxCreate(
"You threw a party for the office! The morale and happiness " +
"of each employee increased by " +
numeralWrapper.formatPercentage(mult - 1),
);
props.rerender();
props.onClose();
}
}
}
function EffectText(): React.ReactElement {
if (isNaN(cost) || cost < 0) return <p>Invalid value entered!</p>;
return (
<Typography>
Throwing this party will cost a total of <Money money={totalCost} />
</Typography>
);
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) throwParty();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>Enter the amount of money you would like to spend PER EMPLOYEE on this office party</Typography>
<EffectText />
<Box display="flex" alignItems="center">
<TextField
autoFocus={true}
type="number"
placeholder="$ / employee"
value={cost}
onChange={changeCost}
onKeyDown={onKeyDown}
/>
<Button disabled={!canParty} onClick={throwParty}>
Throw Party
</Button>
</Box>
</Modal>
);
}

@ -1,72 +0,0 @@
import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { OfficeSpace } from "../OfficeSpace";
import { ICorporation } from "../ICorporation";
import { ThrowParty } from "../Actions";
interface IProps {
office: OfficeSpace;
corp: ICorporation;
popupId: string;
}
export function ThrowPartyPopup(props: IProps): React.ReactElement {
const [cost, setCost] = useState<number | null>(null);
function changeCost(event: React.ChangeEvent<HTMLInputElement>): void {
setCost(parseFloat(event.target.value));
}
function throwParty(): void {
if (cost === null || isNaN(cost) || cost < 0) {
dialogBoxCreate("Invalid value entered");
} else {
const totalCost = cost * props.office.employees.length;
if (props.corp.funds.lt(totalCost)) {
dialogBoxCreate("You don't have enough company funds to throw a party!");
} else {
const mult = ThrowParty(props.corp, props.office, cost);
dialogBoxCreate(
"You threw a party for the office! The morale and happiness " +
"of each employee increased by " +
numeralWrapper.formatPercentage(mult - 1),
);
removePopup(props.popupId);
}
}
}
function EffectText(props: { cost: number | null; office: OfficeSpace }): React.ReactElement {
let cost = props.cost;
if (cost !== null && (isNaN(cost) || cost < 0)) return <p>Invalid value entered!</p>;
if (cost === null) cost = 0;
return (
<p>Throwing this party will cost a total of {numeralWrapper.formatMoney(cost * props.office.employees.length)}</p>
);
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) throwParty();
}
return (
<>
<p>Enter the amount of money you would like to spend PER EMPLOYEE on this office party</p>
<EffectText cost={cost} office={props.office} />
<input
autoFocus={true}
className="text-input"
type="number"
style={{ margin: "5px" }}
placeholder="$ / employee"
onChange={changeCost}
onKeyDown={onKeyDown}
/>
<button className="std-button" onClick={throwParty}>
Throw Party
</button>
</>
);
}

@ -6,6 +6,11 @@ import { CorporationUnlockUpgrade } from "../data/CorporationUnlockUpgrades";
import { useCorporation } from "./Context"; import { useCorporation } from "./Context";
import { UnlockUpgrade as UU } from "../Actions"; import { UnlockUpgrade as UU } from "../Actions";
import { MoneyCost } from "./MoneyCost"; import { MoneyCost } from "./MoneyCost";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
interface IProps { interface IProps {
upgradeData: CorporationUnlockUpgrade; upgradeData: CorporationUnlockUpgrade;
@ -15,11 +20,6 @@ interface IProps {
export function UnlockUpgrade(props: IProps): React.ReactElement { export function UnlockUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const data = props.upgradeData; const data = props.upgradeData;
const text = (
<>
{data[2]} - <MoneyCost money={data[1]} corp={corp} />
</>
);
const tooltip = data[3]; const tooltip = data[3];
function onClick(): void { function onClick(): void {
if (corp.funds.lt(data[1])) return; if (corp.funds.lt(data[1])) return;
@ -32,9 +32,15 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
} }
return ( return (
<button className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}> <Grid item xs={4}>
{text} <Box display="flex" alignItems="center" flexDirection="row-reverse">
<span className={"tooltiptext"}>{tooltip}</span> <Button disabled={corp.funds.lt(data[1])} sx={{ mx: 1 }} onClick={onClick}>
</button> <MoneyCost money={data[1]} corp={corp} />
</Button>
<Tooltip title={tooltip}>
<Typography>{data[2]}</Typography>
</Tooltip>
</Box>
</Grid>
); );
} }

@ -0,0 +1,114 @@
import React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { CorporationConstants } from "../data/Constants";
import { OfficeSpace } from "../OfficeSpace";
import { ICorporation } from "../ICorporation";
import { UpgradeOfficeSize } from "../Actions";
import { Modal } from "../../ui/React/Modal";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box";
interface IUpgradeButton {
cost: number;
size: number;
corp: ICorporation;
office: OfficeSpace;
onClose: () => void;
rerender: () => void;
}
function UpgradeSizeButton(props: IUpgradeButton): React.ReactElement {
const corp = useCorporation();
function upgradeSize(cost: number, size: number): void {
if (corp.funds.lt(cost)) {
return;
}
UpgradeOfficeSize(corp, props.office, size);
props.rerender();
props.onClose();
}
return (
<Tooltip title={numeralWrapper.formatMoney(props.cost)}>
<span>
<Button disabled={corp.funds.lt(props.cost)} onClick={() => upgradeSize(props.cost, props.size)}>
+{props.size}
</Button>
</span>
</Tooltip>
);
}
interface IProps {
open: boolean;
onClose: () => void;
office: OfficeSpace;
rerender: () => void;
}
export function UpgradeOfficeSizeModal(props: IProps): React.ReactElement {
const corp = useCorporation();
const initialPriceMult = Math.round(props.office.size / CorporationConstants.OfficeInitialSize);
const costMultiplier = 1.09;
const upgradeCost = CorporationConstants.OfficeInitialCost * Math.pow(costMultiplier, initialPriceMult);
// Calculate cost to upgrade size by 15 employees
let mult = 0;
for (let i = 0; i < 5; ++i) {
mult += Math.pow(costMultiplier, initialPriceMult + i);
}
const upgradeCost15 = CorporationConstants.OfficeInitialCost * mult;
//Calculate max upgrade size and cost
const maxMult = corp.funds.dividedBy(CorporationConstants.OfficeInitialCost).toNumber();
let maxNum = 1;
mult = Math.pow(costMultiplier, initialPriceMult);
while (maxNum < 50) {
//Hard cap of 50x (extra 150 employees)
if (mult >= maxMult) break;
const multIncrease = Math.pow(costMultiplier, initialPriceMult + maxNum);
if (mult + multIncrease > maxMult) {
break;
} else {
mult += multIncrease;
}
++maxNum;
}
const upgradeCostMax = CorporationConstants.OfficeInitialCost * mult;
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>Increase the size of your office space to fit additional employees!</Typography>
<Box display="flex" alignItems="center">
<Typography>Upgrade size: </Typography>
<UpgradeSizeButton
onClose={props.onClose}
rerender={props.rerender}
office={props.office}
corp={corp}
cost={upgradeCost}
size={CorporationConstants.OfficeInitialSize}
/>
<UpgradeSizeButton
onClose={props.onClose}
rerender={props.rerender}
office={props.office}
corp={corp}
cost={upgradeCost15}
size={CorporationConstants.OfficeInitialSize * 5}
/>
<UpgradeSizeButton
onClose={props.onClose}
rerender={props.rerender}
office={props.office}
corp={corp}
cost={upgradeCostMax}
size={maxNum * CorporationConstants.OfficeInitialSize}
/>
</Box>
</Modal>
);
}

@ -1,91 +0,0 @@
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { CorporationConstants } from "../data/Constants";
import { OfficeSpace } from "../OfficeSpace";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { UpgradeOfficeSize } from "../Actions";
interface IProps {
office: OfficeSpace;
corp: ICorporation;
popupId: string;
player: IPlayer;
rerender: () => void;
}
export function UpgradeOfficeSizePopup(props: IProps): React.ReactElement {
const initialPriceMult = Math.round(props.office.size / CorporationConstants.OfficeInitialSize);
const costMultiplier = 1.09;
const upgradeCost = CorporationConstants.OfficeInitialCost * Math.pow(costMultiplier, initialPriceMult);
// Calculate cost to upgrade size by 15 employees
let mult = 0;
for (let i = 0; i < 5; ++i) {
mult += Math.pow(costMultiplier, initialPriceMult + i);
}
const upgradeCost15 = CorporationConstants.OfficeInitialCost * mult;
//Calculate max upgrade size and cost
const maxMult = props.corp.funds.dividedBy(CorporationConstants.OfficeInitialCost).toNumber();
let maxNum = 1;
mult = Math.pow(costMultiplier, initialPriceMult);
while (maxNum < 50) {
//Hard cap of 50x (extra 150 employees)
if (mult >= maxMult) break;
const multIncrease = Math.pow(costMultiplier, initialPriceMult + maxNum);
if (mult + multIncrease > maxMult) {
break;
} else {
mult += multIncrease;
}
++maxNum;
}
const upgradeCostMax = CorporationConstants.OfficeInitialCost * mult;
function upgradeSize(cost: number, size: number): void {
if (props.corp.funds.lt(cost)) {
dialogBoxCreate("You don't have enough company funds to purchase this upgrade!");
} else {
UpgradeOfficeSize(props.corp, props.office, size);
dialogBoxCreate("Office space increased! It can now hold " + props.office.size + " employees");
props.rerender();
}
removePopup(props.popupId);
}
interface IUpgradeButton {
cost: number;
size: number;
corp: ICorporation;
}
function UpgradeSizeButton(props: IUpgradeButton): React.ReactElement {
return (
<button
className={"tooltip " + (props.corp.funds.lt(props.cost) ? "a-link-button-inactive" : "a-link-button")}
style={{ display: "inline-block", margin: "4px" }}
onClick={() => upgradeSize(props.cost, props.size)}
>
by {props.size}
<span className="tooltiptext">{numeralWrapper.formatMoney(props.cost)}</span>
</button>
);
}
return (
<>
<p>Increase the size of your office space to fit additional employees!</p>
<p>Upgrade size: </p>
<UpgradeSizeButton corp={props.corp} cost={upgradeCost} size={CorporationConstants.OfficeInitialSize} />
<UpgradeSizeButton corp={props.corp} cost={upgradeCost15} size={CorporationConstants.OfficeInitialSize * 5} />
<UpgradeSizeButton
corp={props.corp}
cost={upgradeCostMax}
size={maxNum * CorporationConstants.OfficeInitialSize}
/>
</>
);
}

@ -65,24 +65,18 @@ const GangNames = [
"The Black Hand", "The Black Hand",
]; ];
export function FactionRoot(props: IProps): React.ReactElement { interface IMainProps {
const [sleevesOpen, setSleevesOpen] = useState(false); faction: Faction;
const setRerender = useState(false)[1]; rerender: () => void;
function rerender(): void { onAugmentations: () => void;
setRerender((old) => !old); }
}
// Enabling this breaks donations.
// useEffect(() => {
// const id = setInterval(rerender, 200);
// return () => clearInterval(id);
// }, []);
const faction = props.faction;
function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
const router = use.Router(); const router = use.Router();
const [purchasingAugs, setPurchasingAugs] = useState(false); const [sleevesOpen, setSleevesOpen] = useState(false);
const p = player;
const factionInfo = faction.getInfo();
function manageGang(faction: Faction): void { function manageGang(faction: Faction): void {
// If player already has a gang, just go to the gang UI // If player already has a gang, just go to the gang UI
@ -99,16 +93,6 @@ export function FactionRoot(props: IProps): React.ReactElement {
}); });
} }
// Route to the main faction page
function routeToMain(): void {
setPurchasingAugs(false);
}
// Route to the purchase augmentation UI for this faction
function routeToPurchaseAugs(): void {
setPurchasingAugs(true);
}
function startFieldWork(faction: Faction): void { function startFieldWork(faction: Faction): void {
player.startFactionFieldWork(router, faction); player.startFactionFieldWork(router, faction);
} }
@ -126,85 +110,96 @@ export function FactionRoot(props: IProps): React.ReactElement {
player.startFactionSecurityWork(router, faction); player.startFactionSecurityWork(router, faction);
} }
function MainPage({ faction }: { faction: Faction }): React.ReactElement { // We have a special flag for whether the player this faction is the player's
const p = player; // gang faction because if the player has a gang, they cannot do any other action
const factionInfo = faction.getInfo(); const isPlayersGang = p.inGang() && p.getGangName() === faction.name;
// We have a special flag for whether the player this faction is the player's // Flags for whether special options (gang, sleeve purchases, donate, etc.)
// gang faction because if the player has a gang, they cannot do any other action // should be shown
const isPlayersGang = p.inGang() && p.getGangName() === faction.name; const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
const canDonate = faction.favor >= favorToDonate;
// Flags for whether special options (gang, sleeve purchases, donate, etc.) const canPurchaseSleeves = faction.name === "The Covenant" && p.bitNodeN >= 10 && SourceFileFlags[10];
// should be shown
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
const canDonate = faction.favor >= favorToDonate;
const canPurchaseSleeves = faction.name === "The Covenant" && p.bitNodeN >= 10 && SourceFileFlags[10]; let canAccessGang = p.canAccessGang() && GangNames.includes(faction.name);
if (p.inGang()) {
let canAccessGang = p.canAccessGang() && GangNames.includes(faction.name); if (p.getGangName() !== faction.name) {
if (p.inGang()) { canAccessGang = false;
if (p.getGangName() !== faction.name) { } else if (p.getGangName() === faction.name) {
canAccessGang = false; canAccessGang = true;
} else if (p.getGangName() === faction.name) {
canAccessGang = true;
}
} }
return (
<>
<Button onClick={() => router.toFactions()}>Back</Button>
<Typography variant="h4" color="primary">
{faction.name}
</Typography>
<Info faction={faction} factionInfo={factionInfo} />
{canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={() => manageGang(faction)} />}
{!isPlayersGang && factionInfo.offerHackingMission && (
<Option
buttonText={"Hacking Mission"}
infoText={hackingMissionInfo}
onClick={() => startHackingMission(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerHackingWork && (
<Option
buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo}
onClick={() => startHackingContracts(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerFieldWork && (
<Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={() => startFieldWork(faction)} />
)}
{!isPlayersGang && factionInfo.offerSecurityWork && (
<Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={() => startSecurityWork(faction)} />
)}
{!isPlayersGang && factionInfo.offersWork() && (
<DonateOption
faction={faction}
p={player}
rerender={rerender}
favorToDonate={favorToDonate}
disabled={!canDonate}
/>
)}
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={routeToPurchaseAugs} />
{canPurchaseSleeves && (
<>
<Option
buttonText={"Purchase & Upgrade Duplicate Sleeves"}
infoText={sleevePurchasesInfo}
onClick={() => setSleevesOpen(true)}
/>
<CovenantPurchasesRoot open={sleevesOpen} onClose={() => setSleevesOpen(false)} />
</>
)}
</>
);
} }
return purchasingAugs ? ( return (
<AugmentationsPage faction={faction} routeToMainPage={routeToMain} /> <>
) : ( <Button onClick={() => router.toFactions()}>Back</Button>
<MainPage faction={faction} /> <Typography variant="h4" color="primary">
{faction.name}
</Typography>
<Info faction={faction} factionInfo={factionInfo} />
{canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={() => manageGang(faction)} />}
{!isPlayersGang && factionInfo.offerHackingMission && (
<Option
buttonText={"Hacking Mission"}
infoText={hackingMissionInfo}
onClick={() => startHackingMission(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerHackingWork && (
<Option
buttonText={"Hacking Contracts"}
infoText={hackingContractsInfo}
onClick={() => startHackingContracts(faction)}
/>
)}
{!isPlayersGang && factionInfo.offerFieldWork && (
<Option buttonText={"Field Work"} infoText={fieldWorkInfo} onClick={() => startFieldWork(faction)} />
)}
{!isPlayersGang && factionInfo.offerSecurityWork && (
<Option buttonText={"Security Work"} infoText={securityWorkInfo} onClick={() => startSecurityWork(faction)} />
)}
{!isPlayersGang && factionInfo.offersWork() && (
<DonateOption
faction={faction}
p={player}
rerender={rerender}
favorToDonate={favorToDonate}
disabled={!canDonate}
/>
)}
<Option buttonText={"Purchase Augmentations"} infoText={augmentationsInfo} onClick={onAugmentations} />
{canPurchaseSleeves && (
<>
<Option
buttonText={"Purchase & Upgrade Duplicate Sleeves"}
infoText={sleevePurchasesInfo}
onClick={() => setSleevesOpen(true)}
/>
<CovenantPurchasesRoot open={sleevesOpen} onClose={() => setSleevesOpen(false)} />
</>
)}
</>
);
}
export function FactionRoot(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
const faction = props.faction;
const [purchasingAugs, setPurchasingAugs] = useState(false);
return purchasingAugs ? (
<AugmentationsPage faction={faction} routeToMainPage={() => setPurchasingAugs(false)} />
) : (
<MainPage rerender={rerender} faction={faction} onAugmentations={() => setPurchasingAugs(true)} />
); );
} }

@ -1,6 +1,7 @@
/** /**
* Implementation for what happens when you destroy a BitNode * Implementation for what happens when you destroy a BitNode
*/ */
import React from "react";
import { Player } from "./Player"; import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige"; import { prestigeSourceFile } from "./Prestige";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile"; import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
@ -58,7 +59,15 @@ function giveSourceFile(bitNodeNumber: number): void {
Player.intelligence = 1; Player.intelligence = 1;
} }
dialogBoxCreate( dialogBoxCreate(
"You received a Source-File for destroying a BitNode!<br><br>" + sourceFile.name + "<br><br>" + sourceFile.info, <>
You received a Source-File for destroying a BitNode!
<br />
<br />
{sourceFile.name}
<br />
<br />
{sourceFile.info}
</>,
); );
} }
} }

@ -33,13 +33,20 @@ const useStyles = makeStyles((theme: Theme) =>
interface IProps { interface IProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
children: JSX.Element[] | JSX.Element | React.ReactElement[] | React.ReactElement; children: React.ReactNode;
} }
export const Modal = (props: IProps): React.ReactElement => { export const Modal = (props: IProps): React.ReactElement => {
const classes = useStyles(); const classes = useStyles();
return ( return (
<M open={props.open} onClose={props.onClose} closeAfterTransition className={classes.modal}> <M
disableRestoreFocus
disableScrollLock
open={props.open}
onClose={props.onClose}
closeAfterTransition
className={classes.modal}
>
<Fade in={props.open}> <Fade in={props.open}>
<div className={classes.paper}> <div className={classes.paper}>
<Box sx={{ m: 2 }}>{props.children}</Box> <Box sx={{ m: 2 }}>{props.children}</Box>

@ -0,0 +1,16 @@
import React from "react";
import { Modal } from "./Modal";
interface IProps {
open: boolean;
onClose: () => void;
children: JSX.Element[] | JSX.Element | React.ReactElement[] | React.ReactElement;
}
export function StaticModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
{props.children}
</Modal>
);
}