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 { BitNodes } from "../BitNode";
import { enterBitNode, setRedPillFlag } from "../../RedPill";
import { PortalPopup } from "./PortalPopup";
import { createPopup } from "../../ui/React/createPopup";
import { PortalModal } from "./PortalModal";
import { CinematicText } from "../../ui/React/CinematicText";
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 {
n: number;
@ -16,51 +49,51 @@ interface IPortalProps {
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
function BitNodePortal(props: IPortalProps): React.ReactElement {
const [portalOpen, setPortalOpen] = useState(false);
const classes = useStyles();
const router = use.Router();
const bitNode = BitNodes[`BitNode${props.n}`];
if (bitNode == null) {
return <>O</>;
}
let cssClass;
let cssClass = classes.level0;
if (props.n === 12 && props.level >= 2) {
// Repeating BitNode
cssClass = "level-2";
cssClass = classes.level2;
} else if (props.level === 1) {
cssClass = classes.level1;
} else {
cssClass = `level-${props.level}`;
}
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,
});
cssClass = classes.level3;
}
return (
<button
className={`bitnode ${cssClass} tooltip`}
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
onClick={openPortalPopup}
>
<strong>O</strong>
<span className="tooltiptext">
<strong>
BitNode-{bitNode.number.toString()}
<br />
{bitNode.name}
</strong>
<br />
{bitNode.desc}
<br />
</span>
</button>
<>
<Tooltip
title={
<Typography>
<strong>
BitNode-{bitNode.number.toString()}: {bitNode.name}
</strong>
<br />
{bitNode.desc}
</Typography>
}
>
<span onClick={() => setPortalOpen(true)} className={cssClass}>
<b>O</b>
</span>
</Tooltip>
<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 (
// prettier-ignore
<div className="noselect">
<pre> O </pre>
<pre> | O O | O O | </pre>
<pre> O | | / __| \ | | O </pre>
<pre> O | O | | O / | O | | O | O </pre>
<pre> | | | | |_/ |/ | \_ \_| | | | | </pre>
<pre> O | | | O | | O__/ | / \__ | | O | | | O </pre>
<pre> | | | | | | | / /| O / \| | | | | | | </pre>
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre>
<pre>| | | |O / | | O / | O O | | \ O| | | |</pre>
<pre>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</pre>
<pre> \| O | |_/ |\| \ O \__| \_| | O |/ </pre>
<pre> | | |_/ | | \| / | \_| | | </pre>
<pre> \| / \| | / / \ |/ </pre>
<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>
<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>
<pre> | | | / / \ \ | | | </pre>
<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>
<pre> \ | / / | | \ \ | / </pre>
<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>
<pre> \|| | | | | | | | | ||/ </pre>
<pre> \| \_ | | | | | | _/ |/ </pre>
<pre> \ \| / \ / \ |/ / </pre>
<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>
<pre> | | | | | | | | </pre>
<pre> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </pre>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | O | | O__/ | / \__ | | O | | | O </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ O \__| \_| | O |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
<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>
<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>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
<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>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
<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>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
<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>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
<br />
<br />
<br />

@ -2,18 +2,23 @@ import React from "react";
import { BitNodes } from "../BitNode";
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 {
open: boolean;
onClose: () => void;
n: number;
level: number;
destroyedBitNode: number;
flume: boolean;
router: IRouter;
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 bitNode = BitNodes[bitNodeKey];
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);
return (
<>
<h1>
<Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">
BitNode-{props.n}: {bitNode.name}
</h1>
</Typography>
<br />
Source-File Level: {props.level} / {maxSourceFileLevel}
<Typography>
Source-File Level: {props.level} / {maxSourceFileLevel}
</Typography>
<br />
<br />
Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}
<Typography> Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}</Typography>
<br />
<br />
{bitNode.info}
<Typography>{bitNode.info}</Typography>
<br />
<br />
<button
className="std-button"
<Button
onClick={() => {
props.enter(props.router, props.flume, props.destroyedBitNode, props.n);
removePopup(props.popupId);
props.enter(router, props.flume, props.destroyedBitNode, props.n);
props.onClose();
}}
>
Enter BN{props.n}.{newLevel}
</button>
</>
</Button>
</Modal>
);
}

@ -139,74 +139,6 @@ export class Employee {
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 {
const employee = new Employee();
employee.name = this.name;

@ -385,9 +385,6 @@ export class Industry implements IIndustry {
const prod = this.products[prodName];
if (prod === undefined) continue;
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;
//Generate three random employees (meh, decent, amazing)
const mult = getRandomInt(76, 100) / 100;
const int = getRandomInt(50, 100),
cha = getRandomInt(50, 100),
exp = getRandomInt(50, 100),
@ -131,12 +130,12 @@ export class OfficeSpace {
sal = CorporationConstants.EmployeeSalaryMultiplier * (int + cha + exp + cre + eff);
const emp = new Employee({
intelligence: int * mult,
charisma: cha * mult,
experience: exp * mult,
creativity: cre * mult,
efficiency: eff * mult,
salary: sal * mult,
intelligence: int,
charisma: cha,
experience: exp,
creativity: cre,
efficiency: eff,
salary: sal,
});
const name = generateRandomString(7);

@ -15,10 +15,6 @@ interface IConstructorParams {
}
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
level = 1;
@ -90,14 +86,10 @@ export class Warehouse {
// Re-calculate how much space is being used by this Warehouse
updateMaterialSizeUsed(): void {
this.sizeUsed = 0;
this.breakdown = "";
for (const matName in this.materials) {
const mat = this.materials[matName];
if (MaterialSizes.hasOwnProperty(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) {

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

@ -4,6 +4,9 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { use } from "../../ui/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 {
open: boolean;
@ -25,38 +28,30 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
const currentStockPrice = corp.sharePrice;
const buybackPrice = currentStockPrice * 1.1;
const disabled =
shares === null ||
isNaN(shares) ||
shares <= 0 ||
shares > corp.issuedShares ||
shares * buybackPrice > player.money;
function buy(): void {
if (shares === null) return;
const tempStockPrice = corp.sharePrice;
const buybackPrice = tempStockPrice * 1.1;
if (isNaN(shares) || shares <= 0) {
dialogBoxCreate("ERROR: Invalid value for number of shares");
} else if (shares > corp.issuedShares) {
dialogBoxCreate("ERROR: There are not this many oustanding shares to buy back");
} else if (shares * buybackPrice > player.money) {
dialogBoxCreate(
"ERROR: You do not have enough money to purchase this many shares (you need " +
numeralWrapper.format(shares * buybackPrice, "$0.000a") +
")",
);
} else {
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.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 {
@ -85,7 +80,7 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
return (
<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.
However, repurchasing shares from the market tends to lead to an increase in stock price.
<br />
@ -95,21 +90,19 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
<br />
The current buyback price of your company's stock is {numeralWrapper.formatMoney(buybackPrice)}. Your company
currently has {numeralWrapper.formatBigNumber(corp.issuedShares)} outstanding stock shares.
</p>
</Typography>
<CostIndicator />
<br />
<input
<TextField
autoFocus={true}
className="text-input"
type="number"
placeholder="Shares to buyback"
style={{ margin: "5px" }}
onChange={changeShares}
onKeyDown={onKeyDown}
/>
<button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}>
<Button disabled={disabled} onClick={buy}>
Buy shares
</button>
</Button>
</Modal>
);
}

@ -1,29 +1,27 @@
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { ICorporation } from "../ICorporation";
import { Product } from "../Product";
import { IIndustry } from "../IIndustry";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Modal } from "../../ui/React/Modal";
import { useDivision } from "./Context";
interface IProps {
open: boolean;
onClose: () => void;
product: Product;
industry: IIndustry;
corp: ICorporation;
popupId: string;
player: IPlayer;
rerender: () => void;
}
// 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 {
props.industry.discontinueProduct(props.product);
removePopup(props.popupId);
division.discontinueProduct(props.product);
props.onClose();
props.rerender();
}
return (
<>
<Modal open={props.open} onClose={props.onClose}>
<p>
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
@ -31,6 +29,6 @@ export function DiscontinueProductPopup(props: IProps): React.ReactElement {
<button className="popup-box-button" onClick={discontinue}>
Discontinue
</button>
</>
</Modal>
);
}

@ -1,6 +1,6 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { Industries, IndustryDescriptions } from "../IndustryData";
import { IndustryStartingCosts, Industries, IndustryDescriptions } from "../IndustryData";
import { useCorporation } from "./Context";
import { IIndustry } from "../IIndustry";
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 [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 {
if (disabled) return;
try {
NewIndustry(corp, industry, name);
} catch (err) {
@ -74,7 +81,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
<Box display="flex" alignItems="center">
<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
</Button>
</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 [city, setCity] = useState(possibleCities[0]);
const disabled = corp.funds.lt(CorporationConstants.OfficeInitialCost);
function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value);
}
@ -48,7 +50,7 @@ export function ExpandNewCity(props: IProps): React.ReactElement {
</MenuItem>
))}
</Select>
<Button onClick={expand} disabled={corp.funds.lt(0)}>
<Button onClick={expand} disabled={disabled}>
Confirm
</Button>
</>

@ -1,25 +1,32 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { ICorporation } from "../ICorporation";
import { Material } from "../Material";
import { Export } from "../Export";
import { IIndustry } from "../IIndustry";
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 {
open: boolean;
onClose: () => void;
mat: Material;
corp: ICorporation;
popupId: string;
}
// Create a popup that lets the player manage exports
export function ExportPopup(props: IProps): React.ReactElement {
if (props.corp.divisions.length === 0) throw new Error("Export popup created with no divisions.");
if (Object.keys(props.corp.divisions[0].warehouses).length === 0)
export function ExportModal(props: IProps): React.ReactElement {
const corp = useCorporation();
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.");
const [industry, setIndustry] = useState<string>(props.corp.divisions[0].name);
const [city, setCity] = useState<string>(Object.keys(props.corp.divisions[0].warehouses)[0]);
const [industry, setIndustry] = useState<string>(corp.divisions[0].name);
const [city, setCity] = useState<string>(Object.keys(corp.divisions[0].warehouses)[0]);
const [amt, setAmt] = useState("");
const setRerender = useState(false)[1];
@ -27,11 +34,11 @@ export function ExportPopup(props: IProps): React.ReactElement {
setRerender((old) => !old);
}
function onCityChange(event: React.ChangeEvent<HTMLSelectElement>): void {
function onCityChange(event: SelectChangeEvent<string>): void {
setCity(event.target.value);
}
function onIndustryChange(event: React.ChangeEvent<HTMLSelectElement>): void {
function onIndustryChange(event: SelectChangeEvent<string>): void {
setIndustry(event.target.value);
}
@ -45,7 +52,7 @@ export function ExportPopup(props: IProps): React.ReactElement {
} catch (err) {
dialogBoxCreate(err + "");
}
removePopup(props.popupId);
props.onClose();
}
function removeExport(exp: Export): void {
@ -58,7 +65,7 @@ export function ExportPopup(props: IProps): React.ReactElement {
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)
throw new Error(`Export popup somehow ended up with undefined division '${currentDivision}'`);
const possibleCities = Object.keys(currentDivision.warehouses).filter(
@ -69,45 +76,48 @@ export function ExportPopup(props: IProps): React.ReactElement {
}
return (
<>
<p>
<Modal open={props.open} onClose={props.onClose}>
<Typography>
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.
</p>
<select className="dropdown" onChange={onIndustryChange} defaultValue={industry}>
{props.corp.divisions.map((division: IIndustry) => (
<option key={division.name} value={division.name}>
</Typography>
<Select onChange={onIndustryChange} value={industry}>
{corp.divisions.map((division: IIndustry) => (
<MenuItem key={division.name} value={division.name}>
{division.name}
</option>
</MenuItem>
))}
</select>
<select className="dropdown" onChange={onCityChange} defaultValue={city}>
</Select>
<Select onChange={onCityChange} value={city}>
{possibleCities.map((cityName: string) => {
if (currentDivision.warehouses[cityName] === 0) return;
return (
<option key={cityName} value={cityName}>
<MenuItem key={cityName} value={cityName}>
{cityName}
</option>
</MenuItem>
);
})}
</select>
<input className="text-input" placeholder="Export amount / s" onChange={onAmtChange} />
<button className="std-button" style={{ display: "inline-block" }} onClick={exportMaterial}>
Export
</button>
<p>
</Select>
<TextField placeholder="Export amount / s" onChange={onAmtChange} value={amt} />
<Button onClick={exportMaterial}>Export</Button>
<Typography>
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.
</p>
</Typography>
{props.mat.exp.map((exp: Export, index: number) => (
<div key={index} className="cmpy-mgmt-existing-export" onClick={() => removeExport(exp)}>
Industry: {exp.ind}
<br />
City: {exp.city}
<br />
Amount/s: {exp.amt}
</div>
<Box display="flex" alignItems="center" key={index}>
<Button sx={{ mx: 2 }} onClick={() => removeExport(exp)}>
delete
</Button>
<Typography>
Industry: {exp.ind}
<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 { use } from "../../ui/Context";
import { useCorporation, useDivision } from "./Context";
import Box from "@mui/material/Box";
interface IProps {
city: string;
@ -22,25 +23,12 @@ export function Industry(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
return (
<div>
<div className={"cmpy-mgmt-industry-left-panel"}>
<IndustryOverview
rerender={props.rerender}
player={player}
corp={corp}
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"}>
<Box display="flex">
<Box sx={{ width: "50%" }}>
<IndustryOverview rerender={props.rerender} currentCity={props.city} office={props.office} />
<IndustryOffice rerender={props.rerender} office={props.office} />
</Box>
<Box sx={{ width: "50%" }}>
<IndustryWarehouse
rerender={props.rerender}
player={player}
@ -49,7 +37,7 @@ export function Industry(props: IProps): React.ReactElement {
division={division}
warehouse={props.warehouse}
/>
</div>
</div>
</Box>
</Box>
);
}

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

@ -1,333 +1,293 @@
// React Component for displaying an Industry's overview information
// (top-left panel in the Industry UI)
import React from "react";
import React, { useState } from "react";
import { OfficeSpace } from "../OfficeSpace";
import { Industries } from "../IndustryData";
import { IndustryUpgrades } from "../IndustryUpgrades";
import { IIndustry } from "../IIndustry";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { MakeProductPopup } from "./MakeProductPopup";
import { MakeProductModal } from "./MakeProductModal";
import { ResearchPopup } from "./ResearchPopup";
import { createPopup } from "../../ui/React/createPopup";
import { Money } from "../../ui/React/Money";
import { ICorporation } from "../ICorporation";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsTable } from "../../ui/React/StatsTable";
import { StaticModal } from "../../ui/React/StaticModal";
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 {
corp: ICorporation;
currentCity: string;
division: IIndustry;
office: OfficeSpace;
player: IPlayer;
rerender: () => void;
}
export function IndustryOverview(props: IProps): React.ReactElement {
function renderMakeProductButton(): React.ReactElement {
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();
const division = useDivision();
return (
<div className={"cmpy-mgmt-industry-overview-panel"}>
{renderText()}
<Paper>
<Text />
<br />
<u className={"industry-purchases-and-upgrades-header"}>Purchases & Upgrades</u>
<br />
{renderUpgrades()} <br />
{props.division.makesProducts && makeProductButton}
</div>
<Typography>Purchases & Upgrades</Typography>
<Upgrades office={props.office} rerender={props.rerender} /> <br />
{division.makesProducts && <MakeProductButton />}
</Paper>
);
}

@ -1,445 +1,32 @@
// React Component for displaying an Industry's warehouse information
// (right-side panel in the Industry UI)
import React from "react";
import React, { useState } from "react";
import { CorporationConstants } from "../data/Constants";
import { OfficeSpace } from "../OfficeSpace";
import { Material } from "../Material";
import { Product } from "../Product";
import { Warehouse } from "../Warehouse";
import { DiscontinueProductPopup } from "./DiscontinueProductPopup";
import { ExportPopup } from "./ExportPopup";
import { LimitProductProductionPopup } from "./LimitProductProductionPopup";
import { MaterialMarketTaPopup } from "./MaterialMarketTaPopup";
import { SellMaterialPopup } from "./SellMaterialPopup";
import { SellProductPopup } from "./SellProductPopup";
import { PurchaseMaterialPopup } from "./PurchaseMaterialPopup";
import { ProductMarketTaPopup } from "./ProductMarketTaPopup";
import { SmartSupplyPopup } from "./SmartSupplyPopup";
import { SmartSupplyModal } from "./SmartSupplyModal";
import { ProductElem } from "./ProductElem";
import { MaterialElem } from "./MaterialElem";
import { MaterialSizes } from "../MaterialSizes";
import { numeralWrapper } from "../../ui/numeralFormat";
import { createPopup } from "../../ui/React/createPopup";
import { isString } from "../../utils/helpers/isString";
import { ICorporation } from "../ICorporation";
import { IIndustry } from "../IIndustry";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money";
import { MoneyCost } from "./MoneyCost";
import { isRelevantMaterial } from "./Helpers";
import { IndustryProductEquation } from "./IndustryProductEquation";
import { PurchaseWarehouse } from "../Actions";
import { useCorporation, useDivision } from "./Context";
interface IProductProps {
corp: ICorporation;
division: IIndustry;
city: string;
product: Product;
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>
);
}
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 IProps {
corp: ICorporation;
@ -450,177 +37,180 @@ interface IProps {
rerender: () => void;
}
export function IndustryWarehouse(props: IProps): React.ReactElement {
function renderWarehouseUI(): React.ReactElement {
if (props.warehouse === 0) return <></>;
// General Storage information at the top
const sizeUsageStyle = {
color: props.warehouse.sizeUsed >= props.warehouse.size ? "red" : "white",
margin: "5px",
};
function WarehouseRoot(props: IProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
const [smartSupplyOpen, setSmartSupplyOpen] = useState(false);
if (props.warehouse === 0) return <></>;
// Upgrade Warehouse size button
const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, props.warehouse.level + 1);
const canAffordUpgrade = props.corp.funds.gt(sizeUpgradeCost);
const upgradeWarehouseClass = canAffordUpgrade ? "std-button" : "a-link-button-inactive";
function upgradeWarehouseOnClick(): void {
if (props.division === null) return;
if (props.warehouse === 0) return;
++props.warehouse.level;
props.warehouse.updateSize(props.corp, props.division);
props.corp.funds = props.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);
// Upgrade Warehouse size button
const sizeUpgradeCost = CorporationConstants.WarehouseUpgradeBaseCost * Math.pow(1.07, props.warehouse.level + 1);
const canAffordUpgrade = corp.funds.gt(sizeUpgradeCost);
function upgradeWarehouseOnClick(): void {
if (division === null) return;
if (props.warehouse === 0) return;
if (!canAffordUpgrade) return;
++props.warehouse.level;
props.warehouse.updateSize(corp, division);
corp.funds = corp.funds.minus(sizeUpgradeCost);
props.rerender();
}
if (props.warehouse instanceof Warehouse) {
return renderWarehouseUI();
} else {
return (
<div className={"cmpy-mgmt-warehouse-panel"}>
<button
className={"std-button"}
onClick={() => purchaseWarehouse(props.division, props.currentCity)}
disabled={props.corp.funds.lt(CorporationConstants.WarehouseInitialCost)}
>
Purchase Warehouse (
<MoneyCost money={CorporationConstants.WarehouseInitialCost} corp={props.corp} />)
</button>
</div>
const ratioLines = [];
for (const matName in division.reqMats) {
if (division.reqMats.hasOwnProperty(matName)) {
const text = [" *", division.reqMats[matName], matName].join(" ");
ratioLines.push(
<div key={matName}>
<p>{text}</p>
</div>,
);
}
}
// Current State:
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 {
let p = parseFloat(event.target.value);
if (p > 50) p = 50;
if (p < 0) p = 0;
setPercent(p);
}
}

@ -57,23 +57,19 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
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 {
if (shares === null) return;
if (disabled) return;
const newSharePrice = Math.round(corp.sharePrice * 0.9);
let newShares = shares;
if (isNaN(newShares)) {
dialogBoxCreate("Invalid input for number of new shares");
return;
}
// Round to nearest ten-millionth
newShares = Math.round(newShares / 10e6) * 10e6;
if (newShares < 10e6 || newShares > maxNewShares) {
dialogBoxCreate("Invalid input for number of new shares");
return;
}
const profit = newShares * newSharePrice;
corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
corp.totalShares += newShares;
@ -128,7 +124,7 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
</Typography>
<EffectText shares={shares} />
<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
</Button>
</Modal>

@ -5,8 +5,12 @@ import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { CorporationUpgrade } from "../data/CorporationUpgrades";
import { LevelUpgrade } from "../Actions";
import { MoneyCost } from "./MoneyCost";
import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
interface IProps {
upgrade: CorporationUpgrade;
@ -14,7 +18,6 @@ interface IProps {
}
export function LevelableUpgrade(props: IProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const data = props.upgrade;
const level = corp.upgrades[data[0]];
@ -23,11 +26,6 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
const priceMult = data[2];
const cost = baseCost * Math.pow(priceMult, level);
const text = (
<>
{data[4]} - <MoneyCost money={cost} corp={corp} />
</>
);
const tooltip = data[5];
function onClick(): void {
if (corp.funds.lt(cost)) return;
@ -40,9 +38,15 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
}
return (
<button className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}>
{text}
<span className={"tooltiptext"}>{tooltip}</span>
</button>
<Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds.lt(cost)} sx={{ mx: 1 }} onClick={onClick}>
<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 { removePopup } from "../../ui/React/createPopup";
import { Product } from "../Product";
import { LimitProductProduction } from "../Actions";
import { Modal } from "../../ui/React/Modal";
interface IProps {
open: boolean;
onClose: () => void;
product: Product;
city: string;
popupId: string;
}
// 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);
function limitProductProduction(): void {
let qty = limit;
if (qty === null) qty = -1;
LimitProductProduction(props.product, props.city, qty);
removePopup(props.popupId);
props.onClose();
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
@ -30,7 +31,7 @@ export function LimitProductProductionPopup(props: IProps): React.ReactElement {
}
return (
<>
<Modal open={props.open} onClose={props.onClose}>
<p>
Enter a limit to the amount of this product you would like to product per second. Leave the box empty to set no
limit.
@ -51,6 +52,6 @@ export function LimitProductProductionPopup(props: IProps): React.ReactElement {
>
Limit production
</button>
</>
</Modal>
);
}

@ -25,9 +25,7 @@ export function MainPanel(props: IProps): React.ReactElement {
if (division === undefined) throw new Error("Cannot find division");
return (
<Context.Division.Provider value={division}>
<div id="cmpy-mgmt-panel">
<CityTabs rerender={props.rerender} city={CityName.Sector12} />
</div>
<CityTabs rerender={props.rerender} city={CityName.Sector12} />
</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 { numeralWrapper } from "../../ui/numeralFormat";
import { IIndustry } from "../IIndustry";
import { ICorporation } from "../ICorporation";
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 {
industry: IIndustry;
mat: Material;
}
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 setRerender = useState(false)[1];
@ -47,50 +52,47 @@ function MarketTA2(props: IMarketTA2Props): React.ReactElement {
return (
<>
<p>
<br />
<u>
<strong>Market-TA.II</strong>
</u>
<br />
<Typography variant="h4">Market-TA.II</Typography>
<br />
<Typography>
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.
</p>
<input className="text-input" type="number" style={{ marginTop: "4px" }} onChange={onChange} value={newCost} />
<div style={{ display: "block" }}>
<label className="tooltip" htmlFor="cmpy-mgmt-marketa2-checkbox" style={{ color: "white" }}>
Use Market-TA.II for Auto-Sale Price
<span className="tooltiptext">
If this is enabled, then this Material will automatically be sold at the optimal price such that the amount
sold matches the amount produced. (i.e. the highest possible price, while still ensuring that all produced
materials will be sold)
</span>
</label>
<input
id="cmpy-mgmt-marketa2-checkbox"
type="checkbox"
onChange={onMarketTA2}
checked={props.mat.marketTa2}
style={{ margin: "3px" }}
/>
</div>
<p>
</Typography>
<TextField type="number" onChange={onChange} value={newCost} />
<br />
<FormControlLabel
control={<Switch checked={props.mat.marketTa2} onChange={onMarketTA2} />}
label={
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the optimal price such that the
amount sold matches the amount produced. (i.e. the highest possible price, while still ensuring that all
produced materials will be sold)
</Typography>
}
>
<Typography>Use Market-TA.II for Auto-Sale Price</Typography>
</Tooltip>
}
/>
<Typography>
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
</p>
</Typography>
</>
);
}
interface IProps {
open: boolean;
onClose: () => void;
mat: Material;
industry: IIndustry;
corp: ICorporation;
popupId: string;
}
// 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];
function rerender(): void {
setRerender((old) => !old);
@ -103,33 +105,30 @@ export function MaterialMarketTaPopup(props: IProps): React.ReactElement {
}
return (
<>
<p>
<u>
<strong>Market-TA.I</strong>
</u>
<br />
<Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">Market-TA.I</Typography>
<Typography>
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
sales
</p>
<div style={{ display: "block" }}>
<label className="tooltip" htmlFor="cmpy-mgmt-marketa1-checkbox" style={{ color: "white" }}>
Use Market-TA.I for Auto-Sale Price
<span className="tooltiptext">
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I
(i.e. the price shown above)
</span>
</label>
<input
id="cmpy-mgmt-marketa1-checkbox"
type="checkbox"
onChange={onMarketTA1}
checked={props.mat.marketTa1}
style={{ margin: "3px" }}
/>
</div>
<MarketTA2 mat={props.mat} industry={props.industry} />
</>
</Typography>
<FormControlLabel
control={<Switch checked={props.mat.marketTa1} onChange={onMarketTA1} />}
label={
<Tooltip
title={
<Typography>
If this is enabled, then this Material will automatically be sold at the price identified by Market-TA.I
(i.e. the price shown above)
</Typography>
}
>
<Typography>Use Market-TA.I for Auto-Sale Price</Typography>
</Tooltip>
}
/>
<MarketTA2 mat={props.mat} />
</Modal>
);
}

@ -19,11 +19,16 @@ import { CONSTANTS } from "../../Constants";
import { numeralWrapper } from "../../ui/numeralFormat";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { Money } from "../../ui/React/Money";
import { MoneyRate } from "../../ui/React/MoneyRate";
import { StatsTable } from "../../ui/React/StatsTable";
import { use } from "../../ui/Context";
import { useCorporation } from "./Context";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
interface IProps {
rerender: () => void;
@ -33,67 +38,73 @@ export function Overview({ rerender }: IProps): React.ReactElement {
const corp = useCorporation();
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 (
<div>
<Typography>
Total Funds: <Money money={corp.funds.toNumber()} />
<br />
Total Revenue: <Money money={corp.revenue.toNumber()} /> / s<br />
Total Expenses: <Money money={corp.expenses.toNumber()} /> / s
<br />
Total Profits: <Money money={profit} /> / s<br />
<DividendsStats profit={profit} />
Publicly Traded: {corp.public ? "Yes" : "No"}
<br />
Owned Stock Shares: {numeralWrapper.format(corp.numShares, "0.000a")}
<br />
Stock Price: {corp.public ? <Money money={corp.sharePrice} /> : "N/A"}
<br />
</Typography>
<>
<StatsTable
rows={[
["Total Funds:", <Money money={corp.funds.toNumber()} />],
["Total Revenue:", <MoneyRate money={corp.revenue.toNumber()} />],
["Total Expenses:", <MoneyRate money={corp.expenses.toNumber()} />],
["Publicly Traded:", corp.public ? "Yes" : "No"],
["Owned Stock Shares:", numeralWrapper.format(corp.numShares, "0.000a")],
["Stock Price:", corp.public ? <Money money={corp.sharePrice} /> : "N/A"],
]}
/>
<br />
<Box display="flex">
<Tooltip
title={
<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
title={
<Typography>
Outstanding Shares: {numeralWrapper.format(corp.issuedShares, "0.000a")}
<br />
Private Shares: {numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a")}
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>
}
>
<Typography className="tooltip">
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
</Typography>
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
</Tooltip>
<br />
<br />
<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>
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
<BribeButton />
<br />
<Upgrades rerender={rerender} />
</div>
</>
);
}
@ -102,7 +113,6 @@ interface IPrivateButtonsProps {
}
// Render the buttons for when your Corporation is still private
function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement {
const player = use.Player();
const corp = useCorporation();
const [findInvestorsopen, setFindInvestorsopen] = useState(false);
const [goPublicopen, setGoPublicopen] = useState(false);
@ -115,9 +125,11 @@ function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement
return (
<>
<Tooltip title={<Typography>{findInvestorsTooltip}</Typography>}>
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
Find Investors
</Button>
<span>
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
Find Investors
</Button>
</span>
</Tooltip>
<Tooltip
title={
@ -148,21 +160,28 @@ function Upgrades({ rerender }: IUpgradeProps): React.ReactElement {
}
return (
<div className={"cmpy-mgmt-upgrade-container"}>
<h1 className={"cmpy-mgmt-upgrade-header"}> Unlocks </h1>
{Object.values(CorporationUnlockUpgrades)
.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
.map((level: number, i: number) => CorporationUpgrades[i])
.map((upgrade: CorporationUpgrade) => (
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade[0]} />
))}
</div>
<>
<Paper sx={{ p: 1, my: 1 }}>
<Typography variant="h4">Unlocks</Typography>
<Grid container>
{Object.values(CorporationUnlockUpgrades)
.filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0)
.map((upgrade: CorporationUnlockUpgrade) => (
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade[0]} />
))}
</Grid>
</Paper>
<Paper sx={{ p: 1, my: 1 }}>
<Typography variant="h4">Upgrades</Typography>
<Grid container>
{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 (
<>
<Tooltip title={<Typography>{sellSharesTooltip}</Typography>}>
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
Sell Shares
</Button>
<span>
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
Sell Shares
</Button>
</span>
</Tooltip>
<SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} />
<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>
<BuybackSharesModal open={buybackSharesOpen} onClose={() => setBuybackSharesOpen(false)} rerender={rerender} />
<br />
<Tooltip title={<Typography>{issueNewSharesTooltip}</Typography>}>
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
Issue New Shares
</Button>
<span>
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
Issue New Shares
</Button>
</span>
</Tooltip>
<IssueNewSharesModal open={issueNewSharesOpen} onClose={() => setIssueNewSharesOpen(false)} />
<Tooltip
@ -264,34 +291,19 @@ function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement {
const dividendsPerShare = totalDividends / corp.totalShares;
const playerEarnings = corp.numShares * dividendsPerShare;
return (
<>
Retained Profits (after dividends): <Money money={retainedEarnings} /> / s
<br />
<br />
Dividend Percentage: {numeralWrapper.format(corp.dividendPercentage / 100, "0%")}
<br />
Dividends per share: <Money money={dividendsPerShare} /> / s<br />
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):{" "}
<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>
<StatsTable
rows={[
["Retained Profits (after dividends):", <MoneyRate money={retainedEarnings} />],
["Dividend Percentage:", numeralWrapper.format(corp.dividendPercentage / 100, "0%")],
["Dividends per share:", <MoneyRate money={dividendsPerShare} />],
["Your earnings as a shareholder (Pre-Tax):", <MoneyRate money={playerEarnings} />],
["Dividend Tax Rate:", <>{corp.dividendTaxPercentage}%</>],
[
"Your earnings as a shareholder (Post-Tax):",
<MoneyRate money={playerEarnings * (1 - corp.dividendTaxPercentage / 100)} />,
],
]}
/>
);
}

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

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

@ -1,9 +1,11 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { ICorporation } from "../ICorporation";
import { Material } from "../Material";
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 {
let val = mat.sCost ? mat.sCost + "" : "";
@ -16,13 +18,13 @@ function initialPrice(mat: Material): string {
}
interface IProps {
open: boolean;
onClose: () => void;
mat: Material;
corp: ICorporation;
popupId: string;
}
// 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 [price, setPrice] = useState<string>(initialPrice(props.mat));
@ -32,8 +34,7 @@ export function SellMaterialPopup(props: IProps): React.ReactElement {
} catch (err) {
dialogBoxCreate(err + "");
}
removePopup(props.popupId);
props.onClose();
}
function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {
@ -49,8 +50,8 @@ export function SellMaterialPopup(props: IProps): React.ReactElement {
}
return (
<>
<p>
<Modal open={props.open} onClose={props.onClose}>
<Typography>
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.
<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
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.
</p>
</Typography>
<br />
<input
className="text-input"
<TextField
value={amt}
autoFocus={true}
type="text"
placeholder="Sell amount"
style={{ marginTop: "4px" }}
onChange={onAmtChange}
onKeyDown={onKeyDown}
/>
<input
className="text-input"
value={price}
type="text"
placeholder="Sell price"
style={{ marginTop: "4px" }}
onChange={onPriceChange}
onKeyDown={onKeyDown}
/>
<button className="std-button" onClick={sellMaterial}>
Confirm
</button>
</>
<TextField value={price} type="text" placeholder="Sell price" onChange={onPriceChange} onKeyDown={onKeyDown} />
<Button onClick={sellMaterial}>Confirm</Button>
</Modal>
);
}

@ -1,8 +1,14 @@
import React, { useState } from "react";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { removePopup } from "../../ui/React/createPopup";
import { Product } from "../Product";
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 {
let val = product.sCost ? product.sCost + "" : "";
@ -15,13 +21,14 @@ function initialPrice(product: Product): string {
}
interface IProps {
open: boolean;
onClose: () => void;
product: Product;
city: string;
popupId: string;
}
// 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 [iQty, setQty] = useState<string>(
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 + "");
}
removePopup(props.popupId);
props.onClose();
}
function onAmtChange(event: React.ChangeEvent<HTMLInputElement>): void {
@ -55,8 +62,8 @@ export function SellProductPopup(props: IProps): React.ReactElement {
}
return (
<>
<p>
<Modal open={props.open} onClose={props.onClose}>
<Typography>
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.
<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
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.
</p>
</Typography>
<br />
<input
className="text-input"
<TextField
value={iQty}
autoFocus={true}
type="text"
placeholder="Sell amount"
style={{ marginTop: "4px" }}
onChange={onAmtChange}
onKeyDown={onKeyDown}
/>
<input
className="text-input"
value={px}
type="text"
placeholder="Sell price"
style={{ marginTop: "4px" }}
onChange={onPriceChange}
onKeyDown={onKeyDown}
<TextField value={px} type="text" placeholder="Sell price" onChange={onPriceChange} onKeyDown={onKeyDown} />
<Button onClick={sellProduct}>Confirm</Button>
<FormControlLabel
control={<Switch checked={checked} onChange={onCheckedChange} />}
label={<Typography>Use same 'Sell Amount' for all cities</Typography>}
/>
<button className="std-button" onClick={sellProduct}>
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>
</>
</Modal>
);
}

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

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

@ -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 { UnlockUpgrade as UU } from "../Actions";
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 {
upgradeData: CorporationUnlockUpgrade;
@ -15,11 +20,6 @@ interface IProps {
export function UnlockUpgrade(props: IProps): React.ReactElement {
const corp = useCorporation();
const data = props.upgradeData;
const text = (
<>
{data[2]} - <MoneyCost money={data[1]} corp={corp} />
</>
);
const tooltip = data[3];
function onClick(): void {
if (corp.funds.lt(data[1])) return;
@ -32,9 +32,15 @@ export function UnlockUpgrade(props: IProps): React.ReactElement {
}
return (
<button className={"cmpy-mgmt-upgrade-div tooltip"} style={{ width: "45%" }} onClick={onClick}>
{text}
<span className={"tooltiptext"}>{tooltip}</span>
</button>
<Grid item xs={4}>
<Box display="flex" alignItems="center" flexDirection="row-reverse">
<Button disabled={corp.funds.lt(data[1])} sx={{ mx: 1 }} onClick={onClick}>
<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",
];
export function FactionRoot(props: IProps): React.ReactElement {
const [sleevesOpen, setSleevesOpen] = useState(false);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
// Enabling this breaks donations.
// useEffect(() => {
// const id = setInterval(rerender, 200);
// return () => clearInterval(id);
// }, []);
const faction = props.faction;
interface IMainProps {
faction: Faction;
rerender: () => void;
onAugmentations: () => void;
}
function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.ReactElement {
const player = use.Player();
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 {
// 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 {
player.startFactionFieldWork(router, faction);
}
@ -126,85 +110,96 @@ export function FactionRoot(props: IProps): React.ReactElement {
player.startFactionSecurityWork(router, faction);
}
function MainPage({ faction }: { faction: Faction }): React.ReactElement {
const p = player;
const factionInfo = faction.getInfo();
// We have a special flag for whether the player this faction is the player's
// gang faction because if the player has a gang, they cannot do any other action
const isPlayersGang = p.inGang() && p.getGangName() === faction.name;
// We have a special flag for whether the player this faction is the player's
// gang faction because if the player has a gang, they cannot do any other action
const isPlayersGang = p.inGang() && p.getGangName() === faction.name;
// Flags for whether special options (gang, sleeve purchases, donate, etc.)
// should be shown
const favorToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
const canDonate = faction.favor >= favorToDonate;
// Flags for whether special options (gang, sleeve purchases, donate, etc.)
// 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];
const canPurchaseSleeves = faction.name === "The Covenant" && p.bitNodeN >= 10 && SourceFileFlags[10];
let canAccessGang = p.canAccessGang() && GangNames.includes(faction.name);
if (p.inGang()) {
if (p.getGangName() !== faction.name) {
canAccessGang = false;
} else if (p.getGangName() === faction.name) {
canAccessGang = true;
}
let canAccessGang = p.canAccessGang() && GangNames.includes(faction.name);
if (p.inGang()) {
if (p.getGangName() !== faction.name) {
canAccessGang = false;
} 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 ? (
<AugmentationsPage faction={faction} routeToMainPage={routeToMain} />
) : (
<MainPage faction={faction} />
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={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
*/
import React from "react";
import { Player } from "./Player";
import { prestigeSourceFile } from "./Prestige";
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
@ -58,7 +59,15 @@ function giveSourceFile(bitNodeNumber: number): void {
Player.intelligence = 1;
}
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 {
open: boolean;
onClose: () => void;
children: JSX.Element[] | JSX.Element | React.ReactElement[] | React.ReactElement;
children: React.ReactNode;
}
export const Modal = (props: IProps): React.ReactElement => {
const classes = useStyles();
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}>
<div className={classes.paper}>
<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>
);
}