finalize corp in mui

This commit is contained in:
Olivier Gagnon 2021-09-30 17:02:07 -04:00
parent 86ddc940aa
commit b0e4a2a775
23 changed files with 688 additions and 636 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

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

@ -136,6 +136,7 @@ function ManualManagement(props: IProps): React.ReactElement {
return (
<>
<br />
<Select value={employee !== null ? employee.name : ""} onChange={employeeSelectorOnChange}>
{employees}
</Select>

@ -259,10 +259,12 @@ function Upgrades(props: { office: OfficeSpace; rerender: () => void }): React.R
upgrades.push(
<Tooltip key={index} title={upgrade[5]}>
<Button onClick={onClick}>
{upgrade[4]} -&nbsp;
<MoneyCost money={cost} corp={corp} />
</Button>
<span>
<Button disabled={corp.funds.lt(cost)} onClick={onClick}>
{upgrade[4]} -&nbsp;
<MoneyCost money={cost} corp={corp} />
</Button>
</span>
</Tooltip>,
);
}

@ -3,26 +3,19 @@
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 { ExportPopup } from "./ExportPopup";
import { MaterialMarketTaPopup } from "./MaterialMarketTaPopup";
import { SellMaterialPopup } from "./SellMaterialPopup";
import { PurchaseMaterialPopup } from "./PurchaseMaterialPopup";
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";
@ -35,190 +28,6 @@ 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
function MaterialComponent(props: IMaterialProps): React.ReactElement {
const corp = useCorporation();
const division = useDivision();
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: corp,
popupId: popupId,
});
}
function openExportPopup(): void {
const popupId = "cmpy-mgmt-export-popup";
createPopup(popupId, ExportPopup, {
mat: mat,
corp: 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: corp,
popupId: popupId,
});
}
function openMaterialMarketTaPopup(): void {
const popupId = "cmpy-mgmt-export-popup";
createPopup(popupId, MaterialMarketTaPopup, {
mat: mat,
industry: division,
corp: corp,
popupId: popupId,
});
}
function shouldFlash(): boolean {
return 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(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 onClick={openExportPopup}>Export</Button>}
<br />
<Button className={`std-button${shouldFlash() ? " flashing-button" : ""}`} onClick={openSellMaterialPopup}>
{sellButtonText}
</Button>
{division.hasResearch("Market-TA.I") && <Button onClick={openMaterialMarketTaPopup}>Market-TA</Button>}
</div>
</div>
);
}
interface IProps {
corp: ICorporation;
division: IIndustry;
@ -289,7 +98,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
// Only create UI for materials that are relevant for the industry
if (!isRelevantMaterial(matName, division)) continue;
mats.push(
<MaterialComponent
<MaterialElem
rerender={props.rerender}
city={props.currentCity}
key={matName}
@ -327,7 +136,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
}
return (
<div className={"cmpy-mgmt-warehouse-panel"}>
<Paper>
<Box display="flex" alignItems="center">
<Tooltip title={props.warehouse.sizeUsed !== 0 ? breakdown : ""}>
<Typography color={props.warehouse.sizeUsed >= props.warehouse.size ? "error" : "primary"}>
@ -370,7 +179,7 @@ function WarehouseRoot(props: IProps): React.ReactElement {
{mats}
{products}
</div>
</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>

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

@ -220,7 +220,11 @@ function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
</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 />

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

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

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

@ -11,6 +11,37 @@ 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;
@ -48,44 +79,35 @@ export function UpgradeOfficeSizeModal(props: IProps): React.ReactElement {
}
const upgradeCostMax = CorporationConstants.OfficeInitialCost * mult;
function upgradeSize(cost: number, size: number): void {
if (corp.funds.lt(cost)) {
return;
}
UpgradeOfficeSize(corp, props.office, size);
props.rerender();
props.rerender();
props.onClose();
}
interface IUpgradeButton {
cost: number;
size: number;
corp: ICorporation;
}
function UpgradeSizeButton(props: IUpgradeButton): React.ReactElement {
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>
);
}
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 corp={corp} cost={upgradeCost} size={CorporationConstants.OfficeInitialSize} />
<UpgradeSizeButton corp={corp} cost={upgradeCost15} size={CorporationConstants.OfficeInitialSize * 5} />
<UpgradeSizeButton corp={corp} cost={upgradeCostMax} size={maxNum * CorporationConstants.OfficeInitialSize} />
<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>
);

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