mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-28 08:57:32 +01:00
commit
d4a28cd0b4
38
dist/vendor.bundle.js
vendored
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: {convertEffectFacToGraphic(division.hwFac)}
|
||||
<br />
|
||||
Robots: {convertEffectFacToGraphic(division.robFac)}
|
||||
<br />
|
||||
AI Cores: {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]} -
|
||||
<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: ${convertEffectFacToGraphic(props.division.hwFac)}<br>` +
|
||||
`Robots: ${convertEffectFacToGraphic(props.division.robFac)}<br>` +
|
||||
`AI Cores: ${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 -
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
193
src/Corporation/ui/MakeProductModal.tsx
Normal file
193
src/Corporation/ui/MakeProductModal.tsx
Normal file
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
204
src/Corporation/ui/MaterialElem.tsx
Normal file
204
src/Corporation/ui/MaterialElem.tsx
Normal file
@ -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)} />,
|
||||
],
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
207
src/Corporation/ui/ProductElem.tsx
Normal file
207
src/Corporation/ui/ProductElem.tsx
Normal file
@ -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>
|
||||
);
|
||||
}
|
84
src/Corporation/ui/ThrowPartyModal.tsx
Normal file
84
src/Corporation/ui/ThrowPartyModal.tsx
Normal file
@ -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>
|
||||
);
|
||||
}
|
||||
|
114
src/Corporation/ui/UpgradeOfficeSizeModal.tsx
Normal file
114
src/Corporation/ui/UpgradeOfficeSizeModal.tsx
Normal file
@ -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>
|
||||
|
16
src/ui/React/StaticModal.tsx
Normal file
16
src/ui/React/StaticModal.tsx
Normal file
@ -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>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user