Merge pull request #1393 from danielyxie/dev

More Mui
This commit is contained in:
hydroflame 2021-10-01 13:40:37 -04:00 committed by GitHub
commit 3cd54603fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
141 changed files with 2568 additions and 3087 deletions

38
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -148,6 +148,7 @@
"format": "prettier --write .",
"start": "http-server -p 8000",
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
"start:dev-fast": "webpack-dev-server --progress --env.devServer --mode development --fast true",
"start:container": "webpack-dev-server --progress --env.devServer --mode development --env.runInContainer",
"build": "webpack --mode production",
"build:dev": "webpack --mode development",

@ -0,0 +1,33 @@
import React, { useState, useEffect } from "react";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import { EventEmitter } from "../../utils/EventEmitter";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
export const BitFlumeEvent = new EventEmitter<[]>();
export function BitFlumeModal(): React.ReactElement {
const router = use.Router();
const [open, setOpen] = useState(false);
function flume(): void {
router.toBitVerse(true, false);
setOpen(false);
}
useEffect(() => BitFlumeEvent.subscribe(() => setOpen(true)), []);
return (
<Modal open={open} onClose={() => setOpen(false)}>
<Typography>
WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.
<br />
<br />
Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode and select a new one.
</Typography>
<br />
<br />
<Button onClick={flume}>Travel to the BitVerse</Button>
</Modal>
);
}

@ -1,30 +0,0 @@
import React from "react";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
router: IRouter;
popupId: string;
}
export function BitFlumePopup(props: IProps): React.ReactElement {
function flume(): void {
props.router.toBitVerse(true, false);
removePopup(props.popupId);
}
return (
<>
WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.
<br />
<br />
Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode and select a new one.
<br />
<br />
<button className="std-button" onClick={flume}>
Travel to the BitVerse
</button>
</>
);
}

@ -51,7 +51,6 @@ interface IPortalProps {
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</>;
@ -63,9 +62,12 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
cssClass = classes.level2;
} else if (props.level === 1) {
cssClass = classes.level1;
} else {
} else if (props.level === 3) {
cssClass = classes.level3;
}
if (props.level === 2) {
cssClass = classes.level2;
}
return (
<>
@ -148,7 +150,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
return (
// prettier-ignore
<div className="noselect">
<>
<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>
@ -201,7 +203,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
"> ",
"> (Enter a new BitNode using the image above)",
]} />
</div>
</>
);
return <></>;

@ -7,9 +7,11 @@ import { Deck } from "./CardDeck/Deck";
import { Hand } from "./CardDeck/Hand";
import { InputAdornment } from "@mui/material";
import { ReactCard } from "./CardDeck/ReactCard";
import { MuiTextField } from "../ui/React/MuiTextField";
import { MuiButton } from "../ui/React/MuiButton";
import { MuiPaper } from "../ui/React/MuiPaper";
import Button from "@mui/material/Button";
import Paper from "@mui/material/Paper";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
const MAX_BET = 100e6;
@ -305,10 +307,10 @@ export class Blackjack extends Game<Props, State> {
const dealerHandValues = this.getHandDisplayValues(dealerHand);
return (
<div>
<>
{/* Wager input */}
<div>
<MuiTextField
<Box>
<TextField
value={betInput}
label={
<>
@ -322,93 +324,88 @@ export class Blackjack extends Game<Props, State> {
error={wagerInvalid}
helperText={wagerInvalid ? wagerInvalidHelperText : ""}
type="number"
variant="filled"
style={{
width: "200px",
}}
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
startAdornment: (
<InputAdornment position="start">
<Typography>$</Typography>
</InputAdornment>
),
}}
/>
<p>
<Typography>
{"Total earnings this session: "}
<Money money={gains} />
</p>
</div>
</Typography>
</Box>
{/* Buttons */}
{!gameInProgress ? (
<div>
<MuiButton onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
Start
</MuiButton>
</div>
<Button onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
Start
</Button>
) : (
<div>
<MuiButton onClick={this.playerHit}>Hit</MuiButton>
<MuiButton color="secondary" onClick={this.playerStay}>
<>
<Button onClick={this.playerHit}>Hit</Button>
<Button color="secondary" onClick={this.playerStay}>
Stay
</MuiButton>
</div>
</Button>
</>
)}
{/* Main game part. Displays both if the game is in progress OR if there's a result so you can see
* the cards that led to that result. */}
{(gameInProgress || result !== Result.Pending) && (
<div>
<MuiPaper variant="outlined" elevation={2}>
<pre>Player</pre>
{playerHand.cards.map((card, i) => (
<ReactCard card={card} key={i} />
))}
<>
<Box display="flex">
<Paper elevation={2}>
<pre>Player</pre>
{playerHand.cards.map((card, i) => (
<ReactCard card={card} key={i} />
))}
<pre>Value(s): </pre>
{playerHandValues.map((value, i) => (
<pre key={i}>{value}</pre>
))}
</MuiPaper>
<pre>Value(s): </pre>
{playerHandValues.map((value, i) => (
<pre key={i}>{value}</pre>
))}
</Paper>
</Box>
<br />
<MuiPaper variant="outlined" elevation={2}>
<pre>Dealer</pre>
{dealerHand.cards.map((card, i) => (
// Hide every card except the first while game is in progress
<ReactCard card={card} hidden={gameInProgress && i !== 0} key={i} />
))}
<Box display="flex">
<Paper elevation={2}>
<pre>Dealer</pre>
{dealerHand.cards.map((card, i) => (
// Hide every card except the first while game is in progress
<ReactCard card={card} hidden={gameInProgress && i !== 0} key={i} />
))}
{!gameInProgress && (
<>
<pre>Value(s): </pre>
{dealerHandValues.map((value, i) => (
<pre key={i}>{value}</pre>
))}
</>
)}
</MuiPaper>
</div>
{!gameInProgress && (
<>
<pre>Value(s): </pre>
{dealerHandValues.map((value, i) => (
<pre key={i}>{value}</pre>
))}
</>
)}
</Paper>
</Box>
</>
)}
{/* Results from previous round */}
{result !== Result.Pending && (
<p>
<Typography>
{result}
{this.isPlayerWinResult(result) && (
<>
{" You gained "}
<Money money={this.state.bet} />
</>
)}
{result === Result.DealerWon && (
<>
{" You lost "}
<Money money={this.state.bet} />
</>
)}
</p>
{this.isPlayerWinResult(result) && <Money money={this.state.bet} />}
{result === Result.DealerWon && <Money money={this.state.bet} />}
</Typography>
)}
</div>
</>
);
}
}

@ -1,12 +1,45 @@
import React, { FC } from "react";
import { Card, Suit } from "./Card";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
type Props = {
card: Card;
hidden?: boolean;
};
const useStyles = makeStyles(() =>
createStyles({
card: {
padding: "10px",
border: "solid 1px #808080",
backgroundColor: "white",
display: "inline-block",
borderRadius: "10px",
fontSize: "18.5px",
textAlign: "center",
margin: "3px",
fontWeight: "bold",
},
red: {
color: "red",
},
black: {
color: "black",
},
value: {
fontSize: "20px",
fontFamily: "sans-serif",
},
}),
);
export const ReactCard: FC<Props> = ({ card, hidden }) => {
const classes = useStyles();
let suit: React.ReactNode;
switch (card.suit) {
case Suit.Clubs:
@ -25,11 +58,11 @@ export const ReactCard: FC<Props> = ({ card, hidden }) => {
throw new Error(`MissingCaseException: ${card.suit}`);
}
return (
<div className={`casino-card ${card.isRedSuit() ? "red" : "black"}`}>
<Paper className={`${classes.card} ${card.isRedSuit() ? classes.red : classes.black}`}>
<>
<div className="value">{hidden ? " - " : card.formatValue()}</div>
<div className={`suit`}>{hidden ? " - " : suit}</div>
<span className={classes.value}>{hidden ? " - " : card.formatValue()}</span>
<span>{hidden ? " - " : suit}</span>
</>
</div>
</Paper>
);
};

@ -3,44 +3,32 @@
*
* This subcomponent renders all of the buttons for training at the gym
*/
import * as React from "react";
import React, { useState } from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { BadRNG } from "./RNG";
import { Game } from "./Game";
import { win, reachedLimit } from "./Game";
import { trusted } from "./utils";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import Box from "@mui/material/Box";
type IProps = {
p: IPlayer;
};
type IState = {
investment: number;
result: any;
status: string;
playLock: boolean;
};
const minPlay = 0;
const maxPlay = 10e3;
export class CoinFlip extends Game<IProps, IState> {
constructor(props: IProps) {
super(props);
export function CoinFlip(props: IProps): React.ReactElement {
const [investment, setInvestment] = useState(1000);
const [result, setResult] = useState(<span> </span>);
const [status, setStatus] = useState("");
const [playLock, setPlayLock] = useState(false);
this.state = {
investment: 1000,
result: <span> </span>,
status: "",
playLock: false,
};
this.play = this.play.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = minPlay;
@ -51,11 +39,11 @@ export class CoinFlip extends Game<IProps, IState> {
if (investment < minPlay) {
investment = minPlay;
}
this.setState({ investment: investment });
setInvestment(investment);
}
play(guess: string): void {
if (this.reachedLimit(this.props.p)) return;
function play(guess: string): void {
if (reachedLimit(props.p)) return;
const v = BadRNG.random();
let letter: string;
if (v < 0.5) {
@ -64,39 +52,48 @@ export class CoinFlip extends Game<IProps, IState> {
letter = "T";
}
const correct: boolean = guess === letter;
this.setState({
result: <span className={correct ? "text" : "failure"}>{letter}</span>,
status: correct ? " win!" : "lose!",
playLock: true,
});
setTimeout(() => this.setState({ playLock: false }), 250);
setResult(
<Box display="flex">
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }} color={correct ? "primary" : "error"}>
{letter}
</Typography>
</Box>,
);
setStatus(correct ? " win!" : "lose!");
setPlayLock(true);
setTimeout(() => setPlayLock(false), 250);
if (correct) {
this.win(this.props.p, this.state.investment);
win(props.p, investment);
} else {
this.win(this.props.p, -this.state.investment);
win(props.p, -investment);
}
if (this.reachedLimit(this.props.p)) return;
if (reachedLimit(props.p)) return;
}
render(): React.ReactNode {
return (
<>
<pre>{`+———————+`}</pre>
<pre>{`| | | |`}</pre>
<pre>
{`| | `}
{this.state.result}
{` | |`}
</pre>
<pre>{`| | | |`}</pre>
<pre>{`+———————+`}</pre>
<span className="text">Play for: </span>
<input type="number" className="text-input" onChange={this.updateInvestment} value={this.state.investment} />
<br />
<StdButton onClick={trusted(() => this.play("H"))} text={"Head!"} disabled={this.state.playLock} />
<StdButton onClick={trusted(() => this.play("T"))} text={"Tail!"} disabled={this.state.playLock} />
<h1>{this.state.status}</h1>
</>
);
}
return (
<>
<Typography>Result:</Typography> {result}
<Box display="flex" alignItems="center">
<TextField
type="number"
onChange={updateInvestment}
InputProps={{
endAdornment: (
<>
<Button onClick={trusted(() => play("H"))} disabled={playLock}>
Head!
</Button>
<Button onClick={trusted(() => play("T"))} disabled={playLock}>
Tail!
</Button>
</>
),
}}
/>
</Box>
<Typography variant="h3">{status}</Typography>
</>
);
}

@ -4,6 +4,19 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
const gainLimit = 10e9;
export function win(p: IPlayer, n: number): void {
p.gainMoney(n);
p.recordMoneySource(n, "casino");
}
export function reachedLimit(p: IPlayer): boolean {
const reached = p.getCasinoWinnings() > gainLimit;
if (reached) {
dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.</>);
}
return reached;
}
export class Game<T, U> extends React.Component<T, U> {
win(p: IPlayer, n: number): void {
p.gainMoney(n);

@ -1,25 +1,18 @@
import * as React from "react";
import React, { useState, useEffect } from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { Money } from "../ui/React/Money";
import { Game } from "./Game";
import { win, reachedLimit } from "./Game";
import { WHRNG } from "./RNG";
import { trusted } from "./utils";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
type IProps = {
p: IPlayer;
};
type IState = {
investment: number;
canPlay: boolean;
status: string | JSX.Element;
n: number;
lock: boolean;
strategy: Strategy;
};
const minPlay = 0;
const maxPlay = 1e7;
@ -118,48 +111,32 @@ function Single(s: number): Strategy {
};
}
export class Roulette extends Game<IProps, IState> {
interval = -1;
rng: WHRNG;
export function Roulette(props: IProps): React.ReactElement {
const [rng] = useState(new WHRNG(new Date().getTime()));
const [investment, setInvestment] = useState(1000);
const [canPlay, setCanPlay] = useState(true);
const [status, setStatus] = useState<string | JSX.Element>("waiting");
const [n, setN] = useState(0);
const [lock, setLock] = useState(true);
const [strategy, setStrategy] = useState<Strategy>({
payout: 0,
match: (): boolean => {
return false;
},
});
constructor(props: IProps) {
super(props);
useEffect(() => {
const i = window.setInterval(step, 50);
return () => clearInterval(i);
});
this.rng = new WHRNG(new Date().getTime());
this.state = {
investment: 1000,
canPlay: true,
status: "waiting",
n: 0,
lock: true,
strategy: {
payout: 0,
match: (): boolean => {
return false;
},
},
};
this.step = this.step.bind(this);
this.currentNumber = this.currentNumber.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
componentDidMount(): void {
this.interval = window.setInterval(this.step, 50);
}
step(): void {
if (!this.state.lock) {
this.setState({ n: Math.floor(Math.random() * 37) });
function step(): void {
if (!lock) {
setN(Math.floor(Math.random() * 37));
}
}
componentWillUnmount(): void {
clearInterval(this.interval);
}
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = minPlay;
@ -170,265 +147,312 @@ export class Roulette extends Game<IProps, IState> {
if (investment < minPlay) {
investment = minPlay;
}
this.setState({ investment: investment });
setInvestment(investment);
}
currentNumber(): string {
if (this.state.n === 0) return "0";
const color = isRed(this.state.n) ? "R" : "B";
return `${this.state.n}${color}`;
function currentNumber(): string {
if (n === 0) return "0";
const color = isRed(n) ? "R" : "B";
return `${n}${color}`;
}
play(s: Strategy): void {
if (this.reachedLimit(this.props.p)) return;
this.setState({
canPlay: false,
lock: false,
status: "playing",
strategy: s,
});
function play(s: Strategy): void {
if (reachedLimit(props.p)) return;
setCanPlay(false);
setLock(false);
setStatus("playing");
setStrategy(s);
setTimeout(() => {
let n = Math.floor(this.rng.random() * 37);
let n = Math.floor(rng.random() * 37);
let status = <></>;
let gain = 0;
let playerWin = this.state.strategy.match(n);
let playerWin = strategy.match(n);
// oh yeah, the house straight up cheats. Try finding the seed now!
if (playerWin && Math.random() > 0.9) {
playerWin = false;
while (this.state.strategy.match(n)) {
while (strategy.match(n)) {
n = (n + 1) % 36;
}
}
if (playerWin) {
gain = this.state.investment * this.state.strategy.payout;
gain = investment * strategy.payout;
status = (
<>
won <Money money={gain} />
</>
);
} else {
gain = -this.state.investment;
gain = -investment;
status = (
<>
lost <Money money={-gain} />
</>
);
}
this.win(this.props.p, gain);
this.setState({
canPlay: true,
lock: true,
status: status,
n: n,
});
this.reachedLimit(this.props.p);
win(props.p, gain);
setCanPlay(true);
setLock(true);
setStatus(status);
setN(n);
reachedLimit(props.p);
}, 1600);
}
render(): React.ReactNode {
return (
<>
<h1>{this.currentNumber()}</h1>
<input
type="number"
className="text-input"
onChange={this.updateInvestment}
placeholder={"Amount to play"}
value={this.state.investment}
disabled={!this.state.canPlay}
/>
<h1>{this.state.status}</h1>
<table>
<tbody>
<tr>
<td>
<StdButton text={"3"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(3)))} />
</td>
<td>
<StdButton text={"6"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(6)))} />
</td>
<td>
<StdButton text={"9"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(9)))} />
</td>
<td>
<StdButton text={"12"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(12)))} />
</td>
<td>
<StdButton text={"15"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(15)))} />
</td>
<td>
<StdButton text={"18"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(18)))} />
</td>
<td>
<StdButton text={"21"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(21)))} />
</td>
<td>
<StdButton text={"24"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(24)))} />
</td>
<td>
<StdButton text={"27"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(27)))} />
</td>
<td>
<StdButton text={"30"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(30)))} />
</td>
<td>
<StdButton text={"33"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(33)))} />
</td>
<td>
<StdButton text={"36"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(36)))} />
</td>
</tr>
<tr>
<td>
<StdButton text={"2"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(2)))} />
</td>
<td>
<StdButton text={"5"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(5)))} />
</td>
<td>
<StdButton text={"8"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(8)))} />
</td>
<td>
<StdButton text={"11"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(11)))} />
</td>
<td>
<StdButton text={"14"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(14)))} />
</td>
<td>
<StdButton text={"17"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(17)))} />
</td>
<td>
<StdButton text={"20"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(20)))} />
</td>
<td>
<StdButton text={"23"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(23)))} />
</td>
<td>
<StdButton text={"26"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(26)))} />
</td>
<td>
<StdButton text={"29"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(29)))} />
</td>
<td>
<StdButton text={"32"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(32)))} />
</td>
<td>
<StdButton text={"35"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(35)))} />
</td>
</tr>
<tr>
<td>
<StdButton text={"1"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(1)))} />
</td>
<td>
<StdButton text={"4"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(4)))} />
</td>
<td>
<StdButton text={"7"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(7)))} />
</td>
<td>
<StdButton text={"10"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(10)))} />
</td>
<td>
<StdButton text={"13"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(13)))} />
</td>
<td>
<StdButton text={"16"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(16)))} />
</td>
<td>
<StdButton text={"19"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(19)))} />
</td>
<td>
<StdButton text={"22"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(22)))} />
</td>
<td>
<StdButton text={"25"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(25)))} />
</td>
<td>
<StdButton text={"28"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(28)))} />
</td>
<td>
<StdButton text={"31"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(31)))} />
</td>
<td>
<StdButton text={"34"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(34)))} />
</td>
</tr>
<tr>
<td colSpan={4}>
<StdButton
text={"1 to 12"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Third1))}
/>
</td>
<td colSpan={4}>
<StdButton
text={"13 to 24"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Third2))}
/>
</td>
<td colSpan={4}>
<StdButton
text={"25 to 36"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Third3))}
/>
</td>
</tr>
<tr>
<td colSpan={2}>
<StdButton
text={"Red"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Red))}
/>
</td>
<td colSpan={2}>
<StdButton
text={"Black"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Black))}
/>
</td>
<td colSpan={2}>
<StdButton
text={"Odd"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Odd))}
/>
</td>
<td colSpan={2}>
<StdButton
text={"Even"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Even))}
/>
</td>
<td colSpan={2}>
<StdButton
text={"High"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.High))}
/>
</td>
<td colSpan={2}>
<StdButton
text={"Low"}
disabled={!this.state.canPlay}
onClick={trusted(() => this.play(strategies.Low))}
/>
</td>
</tr>
<tr>
<td>
<StdButton text={"0"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(0)))} />
</td>
</tr>
</tbody>
</table>
</>
);
}
return (
<>
<Typography variant="h4">{currentNumber()}</Typography>
<TextField type="number" onChange={updateInvestment} placeholder={"Amount to play"} disabled={!canPlay} />
<Typography variant="h4">{status}</Typography>
<table>
<tbody>
<tr>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(3)))}>
3
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(6)))}>
6
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(9)))}>
9
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(12)))}>
12
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(15)))}>
15
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(18)))}>
18
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(21)))}>
21
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(24)))}>
24
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(27)))}>
27
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(30)))}>
30
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(33)))}>
33
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(36)))}>
36
</Button>
</td>
</tr>
<tr>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(2)))}>
2
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(5)))}>
5
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(8)))}>
8
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(11)))}>
11
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(14)))}>
14
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(17)))}>
17
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(20)))}>
20
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(23)))}>
23
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(26)))}>
26
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(29)))}>
29
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(32)))}>
32
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(35)))}>
35
</Button>
</td>
</tr>
<tr>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(1)))}>
1
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(4)))}>
4
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(7)))}>
7
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(10)))}>
10
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(13)))}>
13
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(16)))}>
16
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(19)))}>
19
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(22)))}>
22
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(25)))}>
25
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(28)))}>
28
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(31)))}>
31
</Button>
</td>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(34)))}>
34
</Button>
</td>
</tr>
<tr>
<td colSpan={4}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third1))}>
1 to 12
</Button>
</td>
<td colSpan={4}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third2))}>
13 to 24
</Button>
</td>
<td colSpan={4}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third3))}>
25 to 36
</Button>
</td>
</tr>
<tr>
<td colSpan={2}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Red))}>
Red
</Button>
</td>
<td colSpan={2}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Black))}>
Black
</Button>
</td>
<td colSpan={2}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Odd))}>
Odd
</Button>
</td>
<td colSpan={2}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Even))}>
Even
</Button>
</td>
<td colSpan={2}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.High))}>
High
</Button>
</td>
<td colSpan={2}>
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Low))}>
Low
</Button>
</td>
</tr>
<tr>
<td>
<Button disabled={!canPlay} onClick={trusted(() => play(Single(0)))}>
0
</Button>
</td>
</tr>
</tbody>
</table>
</>
);
}

@ -1,11 +1,13 @@
import * as React from "react";
import React, { useState, useEffect } from "react";
import { IPlayer } from "../PersonObjects/IPlayer";
import { StdButton } from "../ui/React/StdButton";
import { Money } from "../ui/React/Money";
import { WHRNG } from "./RNG";
import { Game } from "./Game";
import { win, reachedLimit } from "./Game";
import { trusted } from "./utils";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
type IProps = {
p: IPlayer;
@ -147,104 +149,76 @@ const payLines = [
const minPlay = 0;
const maxPlay = 1e6;
export class SlotMachine extends Game<IProps, IState> {
rng: WHRNG;
interval = -1;
export function SlotMachine(props: IProps): React.ReactElement {
const [rng] = useState(new WHRNG(props.p.totalPlaytime));
const [index, setIndex] = useState<number[]>([0, 0, 0, 0, 0]);
const [locks, setLocks] = useState<number[]>([0, 0, 0, 0, 0]);
const [investment, setInvestment] = useState(1000);
const [canPlay, setCanPlay] = useState(true);
const [status, setStatus] = useState<string | JSX.Element>("waiting");
constructor(props: IProps) {
super(props);
this.rng = new WHRNG(this.props.p.totalPlaytime);
useEffect(() => {
const i = window.setInterval(step, 50);
return () => clearInterval(i);
});
this.state = {
index: [0, 0, 0, 0, 0],
investment: 1000,
locks: [0, 0, 0, 0, 0],
canPlay: true,
status: "waiting",
};
this.play = this.play.bind(this);
this.lock = this.lock.bind(this);
this.unlock = this.unlock.bind(this);
this.step = this.step.bind(this);
this.checkWinnings = this.checkWinnings.bind(this);
this.getTable = this.getTable.bind(this);
this.updateInvestment = this.updateInvestment.bind(this);
}
componentDidMount(): void {
this.interval = window.setInterval(this.step, 50);
}
step(): void {
function step(): void {
let stoppedOne = false;
const index = this.state.index.slice();
for (const i in index) {
if (index[i] === this.state.locks[i] && !stoppedOne) continue;
index[i] = (index[i] + 1) % symbols.length;
const copy = index.slice();
for (const i in copy) {
if (copy[i] === locks[i] && !stoppedOne) continue;
copy[i] = (copy[i] + 1) % symbols.length;
stoppedOne = true;
}
this.setState({ index: index });
setIndex(copy);
if (stoppedOne && index.every((e, i) => e === this.state.locks[i])) {
this.checkWinnings();
if (stoppedOne && copy.every((e, i) => e === locks[i])) {
checkWinnings();
}
}
componentWillUnmount(): void {
clearInterval(this.interval);
}
getTable(): string[][] {
function getTable(): string[][] {
return [
[
symbols[(this.state.index[0] + symbols.length - 1) % symbols.length],
symbols[(this.state.index[1] + symbols.length - 1) % symbols.length],
symbols[(this.state.index[2] + symbols.length - 1) % symbols.length],
symbols[(this.state.index[3] + symbols.length - 1) % symbols.length],
symbols[(this.state.index[4] + symbols.length - 1) % symbols.length],
symbols[(index[0] + symbols.length - 1) % symbols.length],
symbols[(index[1] + symbols.length - 1) % symbols.length],
symbols[(index[2] + symbols.length - 1) % symbols.length],
symbols[(index[3] + symbols.length - 1) % symbols.length],
symbols[(index[4] + symbols.length - 1) % symbols.length],
],
[symbols[index[0]], symbols[index[1]], symbols[index[2]], symbols[index[3]], symbols[index[4]]],
[
symbols[this.state.index[0]],
symbols[this.state.index[1]],
symbols[this.state.index[2]],
symbols[this.state.index[3]],
symbols[this.state.index[4]],
],
[
symbols[(this.state.index[0] + 1) % symbols.length],
symbols[(this.state.index[1] + 1) % symbols.length],
symbols[(this.state.index[2] + 1) % symbols.length],
symbols[(this.state.index[3] + 1) % symbols.length],
symbols[(this.state.index[4] + 1) % symbols.length],
symbols[(index[0] + 1) % symbols.length],
symbols[(index[1] + 1) % symbols.length],
symbols[(index[2] + 1) % symbols.length],
symbols[(index[3] + 1) % symbols.length],
symbols[(index[4] + 1) % symbols.length],
],
];
}
play(): void {
if (this.reachedLimit(this.props.p)) return;
this.setState({ status: "playing" });
this.win(this.props.p, -this.state.investment);
if (!this.state.canPlay) return;
this.unlock();
setTimeout(this.lock, this.rng.random() * 2000 + 1000);
function play(): void {
if (reachedLimit(props.p)) return;
setStatus("playing");
win(props.p, -investment);
if (!canPlay) return;
unlock();
setTimeout(lock, rng.random() * 2000 + 1000);
}
lock(): void {
this.setState({
locks: [
Math.floor(this.rng.random() * symbols.length),
Math.floor(this.rng.random() * symbols.length),
Math.floor(this.rng.random() * symbols.length),
Math.floor(this.rng.random() * symbols.length),
Math.floor(this.rng.random() * symbols.length),
],
});
function lock(): void {
setLocks([
Math.floor(rng.random() * symbols.length),
Math.floor(rng.random() * symbols.length),
Math.floor(rng.random() * symbols.length),
Math.floor(rng.random() * symbols.length),
Math.floor(rng.random() * symbols.length),
]);
}
checkWinnings(): void {
const t = this.getTable();
function checkWinnings(): void {
const t = getTable();
const getPaylineData = function (payline: number[][]): string[] {
const data = [];
for (const point of payline) {
@ -263,35 +237,31 @@ export class SlotMachine extends Game<IProps, IState> {
return count;
};
let gains = -this.state.investment;
let gains = -investment;
for (const payline of payLines) {
const data = getPaylineData(payline);
const count = countSequence(data);
if (count < 3) continue;
const payout = getPayout(data[0], count - 3);
gains += this.state.investment * payout;
this.win(this.props.p, this.state.investment * payout);
gains += investment * payout;
win(props.p, investment * payout);
}
this.setState({
status: (
<>
{gains > 0 ? "gained" : "lost"} <Money money={Math.abs(gains)} />
</>
),
canPlay: true,
});
if (this.reachedLimit(this.props.p)) return;
setStatus(
<>
{gains > 0 ? "gained" : "lost"} <Money money={Math.abs(gains)} />
</>,
);
setCanPlay(true);
if (reachedLimit(props.p)) return;
}
unlock(): void {
this.setState({
locks: [-1, -1, -1, -1, -1],
canPlay: false,
});
function unlock(): void {
setLocks([-1, -1, -1, -1, -1]);
setCanPlay(false);
}
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
let investment: number = parseInt(e.currentTarget.value);
if (isNaN(investment)) {
investment = minPlay;
@ -302,53 +272,49 @@ export class SlotMachine extends Game<IProps, IState> {
if (investment < minPlay) {
investment = minPlay;
}
this.setState({ investment: investment });
setInvestment(investment);
}
render(): React.ReactNode {
const t = this.getTable();
// prettier-ignore
return (
const t = getTable();
// prettier-ignore
return (
<>
<pre>++</pre>
<pre>| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |</pre>
<pre>| | | | | | | |</pre>
<pre>| | {symbols[this.state.index[0]]} | {symbols[this.state.index[1]]} | {symbols[this.state.index[2]]} | {symbols[this.state.index[3]]} | {symbols[this.state.index[4]]} | |</pre>
<pre>| | | | | | | |</pre>
<pre>| | {symbols[(this.state.index[0]+1)%symbols.length]} | {symbols[(this.state.index[1]+1)%symbols.length]} | {symbols[(this.state.index[2]+1)%symbols.length]} | {symbols[(this.state.index[3]+1)%symbols.length]} | {symbols[(this.state.index[4]+1)%symbols.length]} | |</pre>
<pre>++</pre>
<input
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>++</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | | | | | | |</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {symbols[index[0]]} | {symbols[index[1]]} | {symbols[index[2]]} | {symbols[index[3]]} | {symbols[index[4]]} | |</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | | | | | | |</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {symbols[(index[0]+1)%symbols.length]} | {symbols[(index[1]+1)%symbols.length]} | {symbols[(index[2]+1)%symbols.length]} | {symbols[(index[3]+1)%symbols.length]} | {symbols[(index[4]+1)%symbols.length]} | |</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>++</Typography>
<TextField
type="number"
className="text-input"
onChange={this.updateInvestment}
onChange={updateInvestment}
placeholder={"Amount to play"}
value={this.state.investment}
disabled={!this.state.canPlay}
disabled={!canPlay}
InputProps={{endAdornment:(<Button
onClick={trusted(play)}
disabled={!canPlay}
>Spin!</Button>)}}
/>
<StdButton
onClick={trusted(this.play)}
text={"Spin!"}
disabled={!this.state.canPlay}
/>
<h1>{this.state.status}</h1>
<h2>Pay lines</h2>
<Typography variant="h4">{status}</Typography>
<Typography>Pay lines</Typography>
<pre>----- ····· ·····</pre>
<pre>····· ----- ·····</pre>
<pre>····· ····· -----</pre>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>----- ····· ·····</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ----- ·····</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ····· -----</Typography>
<br />
<pre>··^·· \···/ \···/</pre>
<pre>·/·\· ·\·/· ·---·</pre>
<pre>/···\ ··v·· ·····</pre>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>··^·· \···/ \···/</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>·/·\· ·\·/· ·---·</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>/···\ ··v·· ·····</Typography>
<br />
<pre>····· ·---· ·····</pre>
<pre>·---· /···\ \···/</pre>
<pre>/···\ ····· ·---·</pre>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ·---· ·····</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>·---· /···\ \···/</Typography>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>/···\ ····· ·---·</Typography>
</>
);
}
}
// https://felgo.com/doc/how-to-make-a-slot-game-tutorial/

@ -3,8 +3,7 @@ import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc
import { IMap } from "./types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "./utils/JSONReviver";
import { createPopup, removePopup } from "./ui/React/createPopup";
import { CodingContractPopup } from "./ui/React/CodingContractPopup";
import { CodingContractEvent } from "./ui/React/CodingContractModal";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
@ -166,29 +165,21 @@ export class CodingContract {
* Creates a popup to prompt the player to solve the problem
*/
async prompt(): Promise<CodingContractResult> {
const popupId = `coding-contract-prompt-popup-${this.fn}`;
return new Promise<CodingContractResult>((resolve) => {
createPopup(
popupId,
CodingContractPopup,
{
c: this,
popupId: popupId,
onClose: () => {
resolve(CodingContractResult.Cancelled);
removePopup(popupId);
},
onAttempt: (val: string) => {
if (this.isSolution(val)) {
resolve(CodingContractResult.Success);
} else {
resolve(CodingContractResult.Failure);
}
removePopup(popupId);
},
const props = {
c: this,
onClose: () => {
resolve(CodingContractResult.Cancelled);
},
() => resolve(CodingContractResult.Cancelled),
);
onAttempt: (val: string) => {
if (this.isSolution(val)) {
resolve(CodingContractResult.Success);
} else {
resolve(CodingContractResult.Failure);
}
},
};
CodingContractEvent.emit(props);
});
}

@ -0,0 +1,32 @@
import React from "react";
import { Company } from "../Company";
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;
locName: string;
company: Company;
onQuit: () => void;
}
export function QuitJobModal(props: IProps): React.ReactElement {
const player = use.Player();
function quit(): void {
player.quitJob(props.locName);
props.onQuit();
props.onClose();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography> Would you like to quit your job at {props.company.name}?</Typography>
<br />
<br />
<Button onClick={quit}>Quit</Button>
</Modal>
);
}

@ -1,31 +0,0 @@
import React from "react";
import { Company } from "../Company";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
locName: string;
company: Company;
player: IPlayer;
onQuit: () => void;
popupId: string;
}
export function QuitJobPopup(props: IProps): React.ReactElement {
function quit(): void {
props.player.quitJob(props.locName);
props.onQuit();
removePopup(props.popupId);
}
return (
<>
Would you like to quit your job at {props.company.name}?
<br />
<br />
<button autoFocus={true} className="std-button" onClick={quit}>
Quit
</button>
</>
);
}

@ -3,6 +3,8 @@ import React from "react";
import { Product } from "../Product";
import { Modal } from "../../ui/React/Modal";
import { useDivision } from "./Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps {
open: boolean;
@ -22,13 +24,11 @@ export function DiscontinueProductModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<p>
<Typography>
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
</p>
<button className="popup-box-button" onClick={discontinue}>
Discontinue
</button>
</Typography>
<Button onClick={discontinue}>Discontinue</Button>
</Modal>
);
}

@ -1,20 +0,0 @@
import React from "react";
interface IProps {
current: boolean;
text: string;
onClick: () => void;
}
export function HeaderTab(props: IProps): React.ReactElement {
let className = "cmpy-mgmt-header-tab";
if (props.current) {
className += " current";
}
return (
<button className={className} onClick={props.onClick}>
{props.text}
</button>
);
}

@ -73,7 +73,7 @@ function MakeProductButton(): React.ReactElement {
title={
hasMaxProducts ? (
<Typography>
ou have reached the maximum number of products: {division.getMaximumNumberProducts()}
You have reached the maximum number of products: {division.getMaximumNumberProducts()}
</Typography>
) : (
""
@ -125,7 +125,7 @@ function Text(): React.ReactElement {
}
return (
<div>
<>
<Typography>
Industry: {division.type} (Corp Funds: <Money money={corp.funds.toNumber()} />)
</Typography>
@ -218,7 +218,7 @@ function Text(): React.ReactElement {
Research
</Button>
</Box>
</div>
</>
);
}

@ -19,11 +19,9 @@ export function IndustryProductEquation(props: IProps): React.ReactElement {
}
return (
<span className="text">
<MathComponent
display={false}
tex={reqs.join("+") + String.raw`\Rightarrow` + prod.map((p) => String.raw`1\text{ }${p}`).join("+")}
/>
</span>
<MathComponent
display={false}
tex={reqs.join("+") + String.raw`\Rightarrow` + prod.map((p) => String.raw`1\text{ }${p}`).join("+")}
/>
);
}

@ -56,18 +56,6 @@ function WarehouseRoot(props: IProps): React.ReactElement {
props.rerender();
}
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) {

@ -2,6 +2,9 @@ import React, { useState } from "react";
import { Product } from "../Product";
import { LimitProductProduction } from "../Actions";
import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
interface IProps {
open: boolean;
@ -32,26 +35,12 @@ export function LimitProductProductionModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<p>
<Typography>
Enter a limit to the amount of this product you would like to product per second. Leave the box empty to set no
limit.
</p>
<input
autoFocus={true}
className="text-input"
style={{ margin: "5px" }}
placeholder="Limit"
type="number"
onChange={onChange}
onKeyDown={onKeyDown}
/>
<button
className="std-button"
style={{ margin: "5px", display: "inline-block" }}
onClick={limitProductProduction}
>
Limit production
</button>
</Typography>
<TextField autoFocus={true} placeholder="Limit" type="number" onChange={onChange} onKeyDown={onKeyDown} />
<Button onClick={limitProductProduction}>Limit production</Button>
</Modal>
);
}

@ -1,14 +1,30 @@
import * as React from "react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { ICorporation } from "../ICorporation";
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
unbuyable: {
color: theme.palette.action.disabled,
},
money: {
color: theme.colors.money,
},
}),
);
interface IProps {
money: number;
corp: ICorporation;
}
export function MoneyCost(props: IProps): JSX.Element {
if (!props.corp.funds.gt(props.money))
return <span className={"unbuyable samefont"}>{numeralWrapper.formatMoney(props.money)}</span>;
return <span className={"money-gold samefont"}>{numeralWrapper.formatMoney(props.money)}</span>;
export function MoneyCost(props: IProps): React.ReactElement {
const classes = useStyles();
if (!props.corp.funds.gt(props.money))
return <span className={classes.unbuyable}>{numeralWrapper.formatMoney(props.money)}</span>;
return <span className={classes.money}>{numeralWrapper.formatMoney(props.money)}</span>;
}

@ -3,12 +3,19 @@ import { numeralWrapper } from "../../ui/numeralFormat";
import { Product } from "../Product";
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 ITa2Props {
product: Product;
}
function MarketTA2(props: ITa2Props): React.ReactElement {
const division = useDivision();
if (!division.hasResearch("Market-TA.II")) return <></>;
const markupLimit = props.product.rat / props.product.mku;
const [value, setValue] = useState(props.product.pCost);
const setRerender = useState(false)[1];
@ -35,38 +42,35 @@ function MarketTA2(props: ITa2Props): 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" onChange={onChange} value={value} type="number" style={{ marginTop: "4px" }} />
<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 Product 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
className="text-input"
onChange={onCheckedChange}
id="cmpy-mgmt-marketa2-checkbox"
style={{ margin: "3px" }}
type="checkbox"
checked={props.product.marketTa2}
/>
</div>
<p>
</Typography>
<TextField type="number" onChange={onChange} value={value} />
<br />
<FormControlLabel
control={<Switch checked={props.product.marketTa2} onChange={onCheckedChange} />}
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>
</>
);
}
@ -93,35 +97,30 @@ export function ProductMarketTaModal(props: IProps): React.ReactElement {
return (
<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} />}
</>
<Typography variant="h4">Market-TA.I</Typography>
<Typography>
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
</Typography>
<FormControlLabel
control={<Switch checked={props.product.marketTa1} onChange={onChange} />}
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 product={props.product} />
</Modal>
);
}

@ -50,7 +50,7 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
}
function EffectText(): React.ReactElement {
if (isNaN(cost) || cost < 0) return <p>Invalid value entered!</p>;
if (isNaN(cost) || cost < 0) return <Typography>Invalid value entered!</Typography>;
return (
<Typography>
Throwing this party will cost a total of <Money money={totalCost} />

@ -17,7 +17,7 @@ export function determineCrimeSuccess(p: IPlayer, type: string): boolean {
}
if (!found) {
dialogBoxCreate(`ERR: Unrecognized crime type: ${type} This is probably a bug please contact the developer`, false);
dialogBoxCreate(`ERR: Unrecognized crime type: ${type} This is probably a bug please contact the developer`);
return false;
}

@ -8,6 +8,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import Typography from "@mui/material/Typography";
import MenuItem from "@mui/material/MenuItem";
import IconButton from "@mui/material/IconButton";
import ReplyAllIcon from "@mui/icons-material/ReplyAll";
@ -49,7 +50,7 @@ export function Augmentations(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Aug:</span>
<Typography>Aug:</Typography>
</td>
<td>
<Select

@ -1,5 +1,6 @@
import React from "react";
import Typography from "@mui/material/Typography";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
@ -63,7 +64,7 @@ export function Bladeburner(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Rank:</span>
<Typography>Rank:</Typography>
</td>
<td>
<Adjuster
@ -78,7 +79,7 @@ export function Bladeburner(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Cycles:</span>
<Typography>Cycles:</Typography>
</td>
<td>
<Adjuster

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { Companies as AllCompanies } from "../../Company/Companies";
@ -78,7 +79,7 @@ export function Companies(): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Company:</span>
<Typography>Company:</Typography>
</td>
<td colSpan={3}>
<Select id="dev-companies-dropdown" onChange={setCompanyDropdown} value={company}>
@ -92,7 +93,7 @@ export function Companies(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Reputation:</span>
<Typography>Reputation:</Typography>
</td>
<td>
<Adjuster
@ -107,7 +108,7 @@ export function Companies(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Favor:</span>
<Typography>Favor:</Typography>
</td>
<td>
<Adjuster
@ -122,7 +123,7 @@ export function Companies(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">All Reputation:</span>
<Typography>All Reputation:</Typography>
</td>
<td>
<Button onClick={tonsOfRepCompanies}>Tons</Button>
@ -131,7 +132,7 @@ export function Companies(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">All Favor:</span>
<Typography>All Favor:</Typography>
</td>
<td>
<Button onClick={tonsOfFavorCompanies}>Tons</Button>

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { Adjuster } from "./Adjuster";
import { IPlayer } from "../../PersonObjects/IPlayer";
@ -82,7 +83,7 @@ export function Corporation(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Cycles:</span>
<Typography>Cycles:</Typography>
</td>
<td>
<Adjuster

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { Adjuster } from "./Adjuster";
@ -106,7 +107,7 @@ export function Factions(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Faction:</span>
<Typography>Faction:</Typography>
</td>
<td>
<FormControl>
@ -138,7 +139,7 @@ export function Factions(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Reputation:</span>
<Typography>Reputation:</Typography>
</td>
<td>
<Adjuster
@ -153,7 +154,7 @@ export function Factions(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Favor:</span>
<Typography>Favor:</Typography>
</td>
<td>
<Adjuster
@ -168,7 +169,7 @@ export function Factions(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">All Reputation:</span>
<Typography>All Reputation:</Typography>
</td>
<td>
<Button onClick={tonsOfRep}>Tons</Button>
@ -177,7 +178,7 @@ export function Factions(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">All Favor:</span>
<Typography>All Favor:</Typography>
</td>
<td>
<Button onClick={tonsOfFactionFavor}>Tons</Button>

@ -1,5 +1,6 @@
import React from "react";
import Typography from "@mui/material/Typography";
import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
@ -45,7 +46,7 @@ export function Gang(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Cycles:</span>
<Typography>Cycles:</Typography>
</td>
<td>
<Adjuster

@ -48,40 +48,38 @@ export function General(props: IProps): React.ReactElement {
<h2>General</h2>
</AccordionSummary>
<AccordionDetails>
<div>
<Button onClick={addMoney(1e6)}>
<pre>
+ <Money money={1e6} />
</pre>
</Button>
<Button onClick={addMoney(1e9)}>
<pre>
+ <Money money={1e9} />
</pre>
</Button>
<Button onClick={addMoney(1e12)}>
<pre>
+ <Money money={1e12} />
</pre>
</Button>
<Button onClick={addMoney(1e15)}>
<pre>
+ <Money money={1000e12} />
</pre>
</Button>
<Button onClick={addMoney(Infinity)}>
<pre>
+ <Money money={Infinity} />
</pre>
</Button>
<Button onClick={upgradeRam}>+ RAM</Button>
</div>
<div>
<Button onClick={quickB1tFlum3}>Quick b1t_flum3.exe</Button>
<Button onClick={b1tflum3}>Run b1t_flum3.exe</Button>
<Button onClick={quickHackW0r1dD43m0n}>Quick w0rld_d34m0n</Button>
<Button onClick={hackW0r1dD43m0n}>Hack w0rld_d34m0n</Button>
</div>
<Button onClick={addMoney(1e6)}>
<pre>
+ <Money money={1e6} />
</pre>
</Button>
<Button onClick={addMoney(1e9)}>
<pre>
+ <Money money={1e9} />
</pre>
</Button>
<Button onClick={addMoney(1e12)}>
<pre>
+ <Money money={1e12} />
</pre>
</Button>
<Button onClick={addMoney(1e15)}>
<pre>
+ <Money money={1000e12} />
</pre>
</Button>
<Button onClick={addMoney(Infinity)}>
<pre>
+ <Money money={Infinity} />
</pre>
</Button>
<Button onClick={upgradeRam}>+ RAM</Button>
<br />
<Button onClick={quickB1tFlum3}>Quick b1t_flum3.exe</Button>
<Button onClick={b1tflum3}>Run b1t_flum3.exe</Button>
<Button onClick={quickHackW0r1dD43m0n}>Quick w0rld_d34m0n</Button>
<Button onClick={hackW0r1dD43m0n}>Hack w0rld_d34m0n</Button>
</AccordionDetails>
</Accordion>
);

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { IPlayer } from "../../PersonObjects/IPlayer";
@ -44,7 +45,7 @@ export function Programs(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Program:</span>
<Typography>Program:</Typography>
</td>
<td>
<Select onChange={setProgramDropdown} value={program}>
@ -58,7 +59,7 @@ export function Programs(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Add:</span>
<Typography>Add:</Typography>
</td>
<td>
<Button onClick={addProgram}>One</Button>

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { AllServers } from "../../Server/AllServers";
@ -84,7 +85,7 @@ export function Servers(): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Server:</span>
<Typography>Server:</Typography>
</td>
<td colSpan={2}>
<Select id="dev-servers-dropdown" onChange={setServerDropdown} value={server}>
@ -98,7 +99,7 @@ export function Servers(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Root:</span>
<Typography>Root:</Typography>
</td>
<td>
<Button onClick={rootServer}>Root one</Button>
@ -109,7 +110,7 @@ export function Servers(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Security:</span>
<Typography>Security:</Typography>
</td>
<td>
<Button onClick={minSecurity}>Min one</Button>
@ -120,7 +121,7 @@ export function Servers(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Money:</span>
<Typography>Money:</Typography>
</td>
<td>
<Button onClick={maxMoney}>Max one</Button>

@ -6,6 +6,7 @@ import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface IProps {
@ -47,7 +48,7 @@ export function Sleeves(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Shock:</span>
<Typography>Shock:</Typography>
</td>
<td>
<Button onClick={sleeveMaxAllShock}>Max all</Button>
@ -58,7 +59,7 @@ export function Sleeves(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Sync:</span>
<Typography>Sync:</Typography>
</td>
<td>
<Button onClick={sleeveSyncMaxAll}>Max all</Button>

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { PlayerOwnedSourceFile } from "../../SourceFile/PlayerOwnedSourceFile";
import { IPlayer } from "../../PersonObjects/IPlayer";
@ -60,7 +61,7 @@ export function SourceFiles(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Exploits:</span>
<Typography>Exploits:</Typography>
</td>
<td>
<Button onClick={clearExploits}>Clear</Button>
@ -68,7 +69,7 @@ export function SourceFiles(props: IProps): React.ReactElement {
</tr>
<tr key={"sf-all"}>
<td>
<span className="text">All:</span>
<Typography>All:</Typography>
</td>
<td>
<ButtonGroup>
@ -90,7 +91,7 @@ export function SourceFiles(props: IProps): React.ReactElement {
{validSFN.map((i) => (
<tr key={"sf-" + i}>
<td>
<span className="text">SF-{i}:</span>
<Typography>SF-{i}:</Typography>
</td>
<td>
<ButtonGroup>

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import { Adjuster } from "./Adjuster";
import { IPlayer } from "../../PersonObjects/IPlayer";
@ -145,7 +146,7 @@ export function Stats(props: IProps): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text text-center">All:</span>
<Typography>All:</Typography>
</td>
<td>
<Button onClick={tonsOfExp}>Tons of exp</Button>
@ -154,7 +155,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Hacking:</span>
<Typography>Hacking:</Typography>
</td>
<td>
<Adjuster
@ -169,7 +170,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Strength:</span>
<Typography>Strength:</Typography>
</td>
<td>
<Adjuster
@ -184,7 +185,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Defense:</span>
<Typography>Defense:</Typography>
</td>
<td>
<Adjuster
@ -199,7 +200,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Dexterity:</span>
<Typography>Dexterity:</Typography>
</td>
<td>
<Adjuster
@ -214,7 +215,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Agility:</span>
<Typography>Agility:</Typography>
</td>
<td>
<Adjuster
@ -229,7 +230,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Charisma:</span>
<Typography>Charisma:</Typography>
</td>
<td>
<Adjuster
@ -244,7 +245,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Intelligence:</span>
<Typography>Intelligence:</Typography>
</td>
<td>
<Adjuster
@ -265,7 +266,7 @@ export function Stats(props: IProps): React.ReactElement {
</tr>
<tr>
<td>
<span className="text text-center">Karma:</span>
<Typography>Karma:</Typography>
</td>
<td>
<Adjuster

@ -5,6 +5,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
import AccordionDetails from "@mui/material/AccordionDetails";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import { Money } from "../../ui/React/Money";
@ -89,7 +90,7 @@ export function StockMarket(): React.ReactElement {
<tbody>
<tr>
<td>
<span className="text">Symbol:</span>
<Typography>Symbol:</Typography>
</td>
<td>
<TextField placeholder="symbol/'all'" onChange={setStockSymbolField} />
@ -97,7 +98,7 @@ export function StockMarket(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Price:</span>
<Typography>Price:</Typography>
</td>
<td>
<TextField placeholder="$$$" onChange={setStockPriceField} />
@ -106,7 +107,7 @@ export function StockMarket(): React.ReactElement {
</tr>
<tr>
<td>
<span className="text">Caps:</span>
<Typography>Caps:</Typography>
</td>
<td>
<Button onClick={viewStockCaps}>View stock caps</Button>

@ -18,19 +18,13 @@ import {
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { createPopup } from "../ui/React/createPopup";
import { InvitationPopup } from "./ui/InvitationPopup";
import { InvitationEvent } from "./ui/InvitationModal";
export function inviteToFaction(faction: Faction): void {
Player.factionInvitations.push(faction.name);
faction.alreadyInvited = true;
if (!Settings.SuppressFactionInvites) {
const popupId = "faction-invitation";
createPopup(popupId, InvitationPopup, {
player: Player,
faction: faction,
popupId: popupId,
});
InvitationEvent.emit(faction);
}
}

@ -175,7 +175,7 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
}
return (
<div>
<>
<Button onClick={props.routeToMainPage}>Back</Button>
<Typography variant="h4">Faction Augmentations</Typography>
<Typography>
@ -197,6 +197,6 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
<Table size="small" padding="none">
<TableBody>{ownedElem}</TableBody>
</Table>
</div>
</>
);
}

@ -0,0 +1,64 @@
/**
* React Component for the popup used to create a new gang.
*/
import React from "react";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps {
open: boolean;
onClose: () => void;
facName: string;
}
export function CreateGangModal(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const combatGangText =
"This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.";
const hackingGangText =
"This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.";
function isHacking(): boolean {
return ["NiteSec", "The Black Hand"].includes(props.facName);
}
function createGang(): void {
player.startGang(props.facName, isHacking());
props.onClose();
router.toGang();
}
function onKeyUp(event: React.KeyboardEvent): void {
if (event.keyCode === 13) createGang();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Would you like to create a new Gang with {props.facName}?
<br />
<br />
Note that this will prevent you from creating a Gang with any other Faction until this BitNode is destroyed. It
also resets your reputation with this faction.
<br />
<br />
{isHacking() ? hackingGangText : combatGangText}
<br />
<br />
Other than hacking vs combat, there are NO differences between the Factions you can create a Gang with, and each
of these Factions have all Augmentations available.
</Typography>
<Button onClick={createGang} onKeyUp={onKeyUp} autoFocus>
Create Gang
</Button>
</Modal>
);
}

@ -1,69 +0,0 @@
/**
* React Component for the popup used to create a new gang.
*/
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { StdButton } from "../../ui/React/StdButton";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
interface ICreateGangPopupProps {
popupId: string;
facName: string;
player: IPlayer;
router: IRouter;
}
export function CreateGangPopup(props: ICreateGangPopupProps): React.ReactElement {
const player = props.player;
const router = props.router;
const combatGangText =
"This is a COMBAT gang. Members in this gang will have different tasks than HACKING gangs. " +
"Compared to hacking gangs, progression with combat gangs can be more difficult as territory management " +
"is more important. However, well-managed combat gangs can progress faster than hacking ones.";
const hackingGangText =
"This is a HACKING gang. Members in this gang will have different tasks than COMBAT gangs. " +
"Compared to combat gangs, progression with hacking gangs is more straightforward as territory warfare " +
"is not as important.";
function isHacking(): boolean {
return ["NiteSec", "The Black Hand"].includes(props.facName);
}
function createGang(): void {
player.startGang(props.facName, isHacking());
removePopup(props.popupId);
router.toGang();
}
function onKeyUp(event: React.KeyboardEvent): void {
if (event.keyCode === 13) createGang();
}
return (
<>
Would you like to create a new Gang with {props.facName}?
<br />
<br />
Note that this will prevent you from creating a Gang with any other Faction until this BitNode is destroyed. It
also resets your reputation with this faction.
<br />
<br />
{isHacking() ? hackingGangText : combatGangText}
<br />
<br />
Other than hacking vs combat, there are NO differences between the Factions you can create a Gang with, and each
of these Factions have all Augmentations available.
<div className="popup-box-input-div">
<StdButton
onClick={createGang}
onKeyUp={onKeyUp}
text="Create Gang"
style={{ float: "right" }}
autoFocus={true}
/>
</div>
</>
);
}

@ -57,7 +57,7 @@ export function DonateOption(props: IProps): React.ReactElement {
props.faction.playerReputation += repGain;
dialogBoxCreate(
<>
You just donated <Money money={amt} /> to {fac.name} to gain {Reputation(repGain)} reputation.
You just donated <Money money={amt} /> to {fac.name} to gain <Reputation reputation={repGain} /> reputation.
</>,
);
props.rerender();
@ -71,7 +71,7 @@ export function DonateOption(props: IProps): React.ReactElement {
}
return (
<Typography>
This donation will result in {Reputation(repFromDonation(donateAmt, props.p))} reputation gain
This donation will result in <Reputation reputation={repFromDonation(donateAmt, props.p)} /> reputation gain
</Typography>
);
}
@ -81,7 +81,7 @@ export function DonateOption(props: IProps): React.ReactElement {
<Status />
{props.disabled ? (
<Typography>
Unlock donations at {Favor(props.favorToDonate)} favor with {props.faction.name}
Unlock donations at <Favor favor={props.favorToDonate} /> favor with {props.faction.name}
</Typography>
) : (
<>

@ -18,7 +18,7 @@ import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context";
import { CreateGangPopup } from "./CreateGangPopup";
import { CreateGangModal } from "./CreateGangModal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
@ -75,22 +75,17 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
const player = use.Player();
const router = use.Router();
const [sleevesOpen, setSleevesOpen] = useState(false);
const [gangOpen, setGangOpen] = useState(false);
const p = player;
const factionInfo = faction.getInfo();
function manageGang(faction: Faction): void {
function manageGang(): void {
// If player already has a gang, just go to the gang UI
if (player.inGang()) {
return router.toGang();
}
const popupId = "create-gang-popup";
createPopup(popupId, CreateGangPopup, {
popupId: popupId,
facName: faction.name,
player: player,
router: router,
});
setGangOpen(true);
}
function startFieldWork(faction: Faction): void {
@ -137,7 +132,12 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
{faction.name}
</Typography>
<Info faction={faction} factionInfo={factionInfo} />
{canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={() => manageGang(faction)} />}
{canAccessGang && (
<>
<Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={manageGang} />
<CreateGangModal facName={faction.name} open={gangOpen} onClose={() => setGangOpen(false)} />
</>
)}
{!isPlayersGang && factionInfo.offerHackingMission && (
<Option
buttonText={"Hacking Mission"}

@ -54,7 +54,8 @@ export function Info(props: IProps): React.ReactElement {
title={
<>
<Typography>
You will have {Favor(props.faction.favor + favorGain)} faction favor after installing an Augmentation.
You will have <Favor favor={props.faction.favor + favorGain} /> faction favor after installing an
Augmentation.
</Typography>
<MathComponent tex={String.raw`\large{r = \text{total faction reputation}}`} />
<MathComponent
@ -63,7 +64,9 @@ export function Info(props: IProps): React.ReactElement {
</>
}
>
<Typography>Reputation: {Reputation(props.faction.playerReputation)}</Typography>
<Typography>
Reputation: <Reputation reputation={props.faction.playerReputation} />
</Typography>
</Tooltip>
</Box>
@ -83,7 +86,9 @@ export function Info(props: IProps): React.ReactElement {
</>
}
>
<Typography>Faction Favor: {Favor(props.faction.favor)}</Typography>
<Typography>
Faction Favor: <Favor favor={props.faction.favor} />
</Typography>
</Tooltip>
</Box>

@ -0,0 +1,40 @@
import React, { useState, useEffect } from "react";
import { joinFaction } from "../FactionHelpers";
import { Faction } from "../Faction";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import { EventEmitter } from "../../utils/EventEmitter";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
export const InvitationEvent = new EventEmitter<[Faction]>();
export function InvitationModal(): React.ReactElement {
const [faction, setFaction] = useState<Faction | null>(null);
const player = use.Player();
function join(): void {
if (faction === null) return;
//Remove from invited factions
const i = player.factionInvitations.findIndex((facName) => facName === faction.name);
if (i === -1) {
console.error("Could not find faction in Player.factionInvitations");
}
joinFaction(faction);
setFaction(null);
}
useEffect(() => InvitationEvent.subscribe((faction) => setFaction(faction)), []);
return (
<Modal open={faction !== null} onClose={() => setFaction(null)}>
<Typography variant="h4">You have received a faction invitation.</Typography>
<Typography>
Would you like to join {(faction || { name: "" }).name}? <br />
<br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</Typography>
<Button onClick={join}>Join!</Button>
<Button onClick={() => setFaction(null)}>Decide later</Button>
</Modal>
);
}

@ -1,40 +0,0 @@
import React from "react";
import { joinFaction } from "../../Faction/FactionHelpers";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
faction: Faction;
popupId: string;
}
export function InvitationPopup(props: IProps): React.ReactElement {
function join(): void {
//Remove from invited factions
const i = props.player.factionInvitations.findIndex((facName) => facName === props.faction.name);
if (i === -1) {
console.error("Could not find faction in Player.factionInvitations");
}
joinFaction(props.faction);
removePopup(props.popupId);
}
return (
<>
<h1>You have received a faction invitation.</h1>
<p>
Would you like to join {props.faction.name}? <br />
<br />
Warning: Joining this faction may prevent you from joining other factions during this run!
</p>
<button className="std-button" onClick={join}>
Join!
</button>
<button className="std-button" onClick={() => removePopup(props.popupId)}>
Decide later
</button>
</>
);
}

@ -0,0 +1,55 @@
import React from "react";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Faction } from "../Faction";
import { purchaseAugmentation } from "../FactionHelpers";
import { isRepeatableAug } from "../../Augmentation/AugmentationHelpers";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps {
open: boolean;
onClose: () => void;
faction: Faction;
aug: Augmentation;
rerender: () => void;
}
export function PurchaseAugmentationModal(props: IProps): React.ReactElement {
const player = use.Player();
const factionInfo = props.faction.getInfo();
function buy(): void {
if (!isRepeatableAug(props.aug) && player.hasAugmentation(props.aug)) {
return;
}
purchaseAugmentation(props.aug, props.faction);
props.rerender();
props.onClose();
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">{props.aug.name}</Typography>
<Typography>
{props.aug.info}
<br />
<br />
{props.aug.stats}
<br />
<br />
Would you like to purchase the {props.aug.name} Augmentation for&nbsp;
<Money money={props.aug.baseCost * factionInfo.augmentationPriceMult} />?
<br />
<br />
</Typography>
<Button autoFocus onClick={buy}>
Purchase
</Button>
</Modal>
);
}

@ -1,51 +0,0 @@
import React from "react";
import { Augmentation } from "../../Augmentation/Augmentation";
import { Faction } from "../../Faction/Faction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { purchaseAugmentation } from "../FactionHelpers";
import { isRepeatableAug } from "../../Augmentation/AugmentationHelpers";
import { Money } from "../../ui/React/Money";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
player: IPlayer;
faction: Faction;
aug: Augmentation;
rerender: () => void;
popupId: string;
}
export function PurchaseAugmentationPopup(props: IProps): React.ReactElement {
const factionInfo = props.faction.getInfo();
function buy(): void {
if (!isRepeatableAug(props.aug) && props.player.hasAugmentation(props.aug)) {
return;
}
purchaseAugmentation(props.aug, props.faction);
props.rerender();
removePopup(props.popupId);
}
return (
<>
<h2>{props.aug.name}</h2>
<br />
{props.aug.info}
<br />
<br />
{props.aug.stats}
<br />
<br />
Would you like to purchase the {props.aug.name} Augmentation for&nbsp;
<Money money={props.aug.baseCost * factionInfo.augmentationPriceMult} />?
<br />
<br />
<button autoFocus={true} className="std-button" onClick={buy}>
Purchase
</button>
</>
);
}

@ -2,10 +2,10 @@
* React component for displaying a single augmentation for purchase through
* the faction UI
*/
import * as React from "react";
import React, { useState } from "react";
import { getNextNeurofluxLevel, hasAugmentationPrereqs, purchaseAugmentation } from "../FactionHelpers";
import { PurchaseAugmentationPopup } from "./PurchaseAugmentationPopup";
import { PurchaseAugmentationModal } from "./PurchaseAugmentationModal";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
@ -14,7 +14,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { createPopup } from "../../ui/React/createPopup";
import { Augmentation as AugFormat } from "../../ui/React/Augmentation";
import Button from "@mui/material/Button";
@ -58,7 +57,7 @@ function Requirements(props: IReqProps): React.ReactElement {
</TableCell>
<TableCell key={2}>
<Typography color={props.hasRep ? "primary" : "error"}>
Requires {Reputation(props.rep)} faction reputation
Requires <Reputation reputation={props.rep} /> faction reputation
</Typography>
</TableCell>
</React.Fragment>
@ -74,6 +73,7 @@ interface IProps {
}
export function PurchaseableAugmentation(props: IProps): React.ReactElement {
const [open, setOpen] = useState(false);
const aug = Augmentations[props.augName];
if (aug == null) throw new Error(`aug ${props.augName} does not exists`);
@ -122,14 +122,7 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
function handleClick(): void {
if (color === "error") return;
if (!Settings.SuppressBuyAugmentationConfirmation) {
const popupId = "purchase-augmentation-popup";
createPopup(popupId, PurchaseAugmentationPopup, {
aug: aug,
faction: props.faction,
player: props.p,
rerender: props.rerender,
popupId: popupId,
});
setOpen(true);
} else {
purchaseAugmentation(aug, props.faction);
props.rerender();
@ -143,6 +136,13 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
<Button onClick={handleClick} color={color}>
Buy
</Button>
<PurchaseAugmentationModal
open={open}
onClose={() => setOpen(false)}
aug={aug}
faction={props.faction}
rerender={props.rerender}
/>
</TableCell>
)}
<TableCell key={1}>

@ -66,7 +66,7 @@ export function GangStats(props: IProps): React.ReactElement {
</p>
<br />
<p style={{ display: "inline-block" }}>
Faction reputation: {Reputation(Factions[props.gang.facName].playerReputation)}
Faction reputation: <Reputation reputation={Factions[props.gang.facName].playerReputation} />
</p>
<br />
<BonusTime gang={props.gang} />

@ -107,7 +107,7 @@ export class HacknetNode implements IHacknetNode {
this.moneyGainRatePerSecond = calculateMoneyGainRate(this.level, this.ram, this.cores, prodMult);
if (isNaN(this.moneyGainRatePerSecond)) {
this.moneyGainRatePerSecond = 0;
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer", false);
dialogBoxCreate("Error in calculating Hacknet Node production. Please report to game developer");
}
}

@ -183,7 +183,7 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
</TableCell>
<TableCell colSpan={2}>
<Typography>
{Hashes(node.totalHashesGenerated)} ({HashRate(node.hashRate)})
<Hashes hashes={node.totalHashesGenerated} /> (<HashRate hashes={node.hashRate} />)
</Typography>
</TableCell>
</TableRow>
@ -192,7 +192,9 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
<Typography>Hash Capacity:</Typography>
</TableCell>
<TableCell colSpan={2}>
<Typography>{Hashes(node.hashCapacity)}</Typography>
<Typography>
<Hashes hashes={node.hashCapacity} />
</Typography>
</TableCell>
</TableRow>
<TableRow>

@ -60,7 +60,7 @@ export function HacknetUpgradeElem(props: IProps): React.ReactElement {
<CopyableText value={upg.name} />
</Typography>
<Typography>
Cost: {Hashes(cost)}, Bought: {level} times
Cost: <Hashes hashes={cost} />, Bought: {level} times
</Typography>
<Typography>{upg.desc}</Typography>

@ -6,8 +6,6 @@ import React, { useState, useEffect } from "react";
import { HashManager } from "../HashManager";
import { HashUpgrades } from "../HashUpgrades";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Hashes } from "../../ui/React/Hashes";
import { HacknetUpgradeElem } from "./HacknetUpgradeElem";
import { Modal } from "../../ui/React/Modal";
@ -40,7 +38,9 @@ export function HashUpgradeModal(props: IProps): React.ReactElement {
<Modal open={props.open} onClose={props.onClose}>
<>
<Typography>Spend your hashes on a variety of different upgrades</Typography>
<Typography>Hashes: {Hashes(player.hashManager.hashes)}</Typography>
<Typography>
Hashes: <Hashes hashes={player.hashManager.hashes} />
</Typography>
{Object.keys(HashUpgrades).map((upgName) => {
const upg = HashUpgrades[upgName];
return (

@ -9,14 +9,17 @@ import { PurchaseMultipliers } from "../data/Constants";
import Button from "@mui/material/Button";
interface IMultiplierProps {
className: string;
key: string;
disabled: boolean;
onClick: () => void;
text: string;
}
function MultiplierButton(props: IMultiplierProps): React.ReactElement {
return <Button onClick={props.onClick}>{props.text}</Button>;
return (
<Button disabled={props.disabled} onClick={props.onClick}>
{props.text}
</Button>
);
}
interface IProps {
@ -35,13 +38,12 @@ export function MultiplierButtons(props: IProps): React.ReactElement {
for (let i = 0; i < mults.length; ++i) {
const mult = mults[i];
const btnProps = {
className: props.purchaseMultiplier === PurchaseMultipliers[mult] ? "std-button-disabled" : "std-button",
key: mult,
disabled: props.purchaseMultiplier === PurchaseMultipliers[mult],
onClick: onClicks[i],
text: mult,
};
buttons.push(<MultiplierButton {...btnProps} />);
buttons.push(<MultiplierButton key={mult} {...btnProps} />);
}
return <>{buttons}</>;

@ -24,7 +24,7 @@ export function PlayerInfo(props: IProps): React.ReactElement {
let prod;
if (hasServers) {
prod = HashRate(props.totalProduction);
prod = <HashRate hashes={props.totalProduction} />;
} else {
prod = <MoneyRate money={props.totalProduction} />;
}
@ -39,7 +39,8 @@ export function PlayerInfo(props: IProps): React.ReactElement {
{hasServers && (
<>
<Typography>
Hashes: {Hashes(props.player.hashManager.hashes)} / {Hashes(props.player.hashManager.capacity)}
Hashes: <Hashes hashes={props.player.hashManager.hashes} /> /{" "}
<Hashes hashes={props.player.hashManager.capacity} />
</Typography>
</>
)}

@ -18,14 +18,10 @@ interface IProps {
export function PurchaseButton(props: IProps): React.ReactElement {
const cost = props.cost;
let className = Player.canAfford(cost) ? "std-button" : "std-button-disabled";
let text;
let style = {};
if (hasHacknetServers(Player)) {
if (hasMaxNumberHacknetServers(Player)) {
className = "std-button-disabled";
text = <>Hacknet Server limit reached</>;
style = { color: "red" };
} else {
text = (
<>
@ -43,5 +39,9 @@ export function PurchaseButton(props: IProps): React.ReactElement {
);
}
return <Button onClick={props.onClick}>{text}</Button>;
return (
<Button disabled={!Player.canAfford(cost)} onClick={props.onClick}>
{text}
</Button>
);
}

@ -6,6 +6,7 @@ import { GameTimer } from "./GameTimer";
import { random } from "../utils";
import { interpolate } from "./Difficulty";
import { BlinkingCursor } from "./BlinkingCursor";
import Typography from "@mui/material/Typography";
interface Difficulty {
[key: string]: number;
@ -46,17 +47,17 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>Type it backward</h1>
<Typography variant="h4">Type it backward</Typography>
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
<Grid item xs={6}>
<p style={{ transform: "scaleX(-1)" }}>{answer}</p>
<Typography style={{ transform: "scaleX(-1)" }}>{answer}</Typography>
</Grid>
<Grid item xs={6}>
<p>
<Typography>
{guess}
<BlinkingCursor />
</p>
</Typography>
</Grid>
</Grid>
);

@ -1,9 +1,10 @@
import React from "react";
import React, { useState, useEffect } from "react";
export function BlinkingCursor(): React.ReactElement {
return (
<span style={{ fontSize: "1em" }} className={"blinking-cursor"}>
|
</span>
);
const [on, setOn] = useState(true);
useEffect(() => {
const i = setInterval(() => setOn((old) => !old), 1000);
return () => clearInterval(i);
});
return <>{on ? "|" : ""}</>;
}

@ -6,6 +6,7 @@ import { GameTimer } from "./GameTimer";
import { random } from "../utils";
import { interpolate } from "./Difficulty";
import { BlinkingCursor } from "./BlinkingCursor";
import Typography from "@mui/material/Typography";
interface Difficulty {
[key: string]: number;
@ -79,11 +80,11 @@ export function BracketGame(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>Close the brackets</h1>
<p style={{ fontSize: "5em" }}>
<Typography variant="h4">Close the brackets</Typography>
<Typography style={{ fontSize: "5em" }}>
{`${left}${right}`}
<BlinkingCursor />
</p>
</Typography>
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>

@ -4,6 +4,7 @@ import { IMinigameProps } from "./IMinigameProps";
import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty";
import Typography from "@mui/material/Typography";
interface Difficulty {
[key: string]: number;
@ -51,13 +52,19 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1>Say something nice about the guard.</h1>
<Typography variant="h4">Say something nice about the guard.</Typography>
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
<Grid item xs={6}>
<h2 style={{ fontSize: "2em" }}></h2>
<h2 style={{ fontSize: "2em" }}>{choices[index]}</h2>
<h2 style={{ fontSize: "2em" }}></h2>
<Typography variant="h5" color="primary">
</Typography>
<Typography variant="h5" color="primary">
{choices[index]}
</Typography>
<Typography variant="h5" color="primary">
</Typography>
</Grid>
</Grid>
);

@ -5,6 +5,7 @@ import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer";
import { random, getArrow } from "../utils";
import { interpolate } from "./Difficulty";
import Typography from "@mui/material/Typography";
interface Difficulty {
[key: string]: number;
@ -46,8 +47,8 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>Enter the Code!</h1>
<p style={{ fontSize: "5em" }}>{code[index]}</p>
<Typography variant="h4">Enter the Code!</Typography>
<Typography variant="h4">{code[index]}</Typography>
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
interface IProps {
onFinish: () => void;
}
@ -19,8 +20,8 @@ export function Countdown(props: IProps): React.ReactElement {
<>
<Grid container spacing={3}>
<Grid item xs={12}>
<h1>Get Ready!</h1>
<h1>{x}</h1>
<Typography variant="h4">Get Ready!</Typography>
<Typography variant="h4">{x}</Typography>
</Grid>
</Grid>
</>

@ -5,6 +5,7 @@ import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty";
import { getArrow } from "../utils";
import Typography from "@mui/material/Typography";
interface Difficulty {
[key: string]: number;
@ -75,8 +76,8 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>Match the symbols!</h1>
<h2 style={{ fontSize: fontSize }}>
<Typography variant="h4">Match the symbols!</Typography>
<Typography variant="h5" color="primary">
Targets:{" "}
{answer.map((a, i) => {
if (i == index)
@ -91,7 +92,7 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
</span>
);
})}
</h2>
</Typography>
<br />
{grid.map((line, y) => (
<div key={y}>

@ -11,6 +11,7 @@ import { Cyberpunk2077Game } from "./Cyberpunk2077Game";
import { MinesweeperGame } from "./MinesweeperGame";
import { WireCuttingGame } from "./WireCuttingGame";
import { Victory } from "./Victory";
import Typography from "@mui/material/Typography";
interface IProps {
StartingDifficulty: number;
@ -130,9 +131,9 @@ export function Game(props: IProps): React.ReactElement {
<>
<Grid container spacing={3}>
<Grid item xs={3}>
<h3>
<Typography>
Level: {level}&nbsp;/&nbsp;{props.MaxLevel}
</h3>
</Typography>
<Progress />
</Grid>

@ -1,7 +1,8 @@
import React from "react";
import { StdButton } from "../../ui/React/StdButton";
import { Location } from "../../Locations/Location";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
interface IProps {
Location: Location;
@ -52,37 +53,45 @@ export function Intro(props: IProps): React.ReactElement {
<>
<Grid container spacing={3}>
<Grid item xs={10}>
<h1>Infiltrating {props.Location.name}</h1>
<Typography variant="h4">Infiltrating {props.Location.name}</Typography>
</Grid>
<Grid item xs={10}>
<h2>Maximum level: {props.MaxLevel}</h2>
<Typography variant="h5" color="primary">
Maximum level: {props.MaxLevel}
</Typography>
</Grid>
<Grid item xs={10}>
<pre>[{coloredArrow(props.Difficulty)}]</pre>
<pre>{` ^ ^ ^ ^`}</pre>
<pre>{` Trivial Normal Hard Impossible`}</pre>
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>[{coloredArrow(props.Difficulty)}]</Typography>
<Typography
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
>{` ^ ^ ^ ^`}</Typography>
<Typography
sx={{ lineHeight: "1em", whiteSpace: "pre" }}
>{` Trivial Normal Hard Impossible`}</Typography>
</Grid>
<Grid item xs={10}>
<p>
<Typography>
Infiltration is a series of short minigames that get progressively harder. You take damage for failing them.
Reaching the maximum level rewards you with intel you can trade for money or reputation.
</p>
</Typography>
<br />
<p>The minigames you play are randomly selected. It might take you few tries to get used to them.</p>
<Typography>
The minigames you play are randomly selected. It might take you few tries to get used to them.
</Typography>
<br />
<p>No game require use of the mouse.</p>
<Typography>No game require use of the mouse.</Typography>
<br />
<p>Spacebar is the default action/confirm button.</p>
<Typography>Spacebar is the default action/confirm button.</Typography>
<br />
<p>Everything that uses arrow can also use WASD</p>
<Typography>Everything that uses arrow can also use WASD</Typography>
<br />
<p>Sometimes the rest of the keyboard is used.</p>
<Typography>Sometimes the rest of the keyboard is used.</Typography>
</Grid>
<Grid item xs={3}>
<StdButton onClick={props.start} text={"Start"} />
<Button onClick={props.start}>Start</Button>
</Grid>
<Grid item xs={3}>
<StdButton onClick={props.cancel} text={"Cancel"} />
<Button onClick={props.cancel}>Cancel</Button>
</Grid>
</Grid>
</>

@ -5,6 +5,7 @@ import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty";
import { getArrow } from "../utils";
import Typography from "@mui/material/Typography";
interface Difficulty {
[key: string]: number;
@ -81,7 +82,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>{memoryPhase ? "Remember all the mines!" : "Mark all the mines!"}</h1>
<Typography variant="h4">{memoryPhase ? "Remember all the mines!" : "Mark all the mines!"}</Typography>
{minefield.map((line, y) => (
<div key={y}>
<pre>

@ -4,6 +4,7 @@ import { IMinigameProps } from "./IMinigameProps";
import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer";
import { interpolate } from "./Difficulty";
import Typography from "@mui/material/Typography";
interface Difficulty {
[key: string]: number;
@ -53,8 +54,8 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={5000} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>Slash when his guard is down!</h1>
<p style={{ fontSize: "5em" }}>{guarding ? "!Guarding!" : "!ATTACKING!"}</p>
<Typography variant="h4">Slash when his guard is down!</Typography>
<Typography variant="h4">{guarding ? "!Guarding!" : "!ATTACKING!"}</Typography>
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>

@ -1,11 +1,14 @@
import { Factions } from "../../Faction/Factions";
import React, { useState } from "react";
import { StdButton } from "../../ui/React/StdButton";
import Grid from "@mui/material/Grid";
import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
interface IProps {
StartingDifficulty: number;
@ -50,7 +53,7 @@ export function Victory(props: IProps): React.ReactElement {
quitInfiltration();
}
function changeDropdown(event: React.ChangeEvent<HTMLSelectElement>): void {
function changeDropdown(event: SelectChangeEvent<string>): void {
setFaction(event.target.value);
}
@ -58,46 +61,36 @@ export function Victory(props: IProps): React.ReactElement {
<>
<Grid container spacing={3}>
<Grid item xs={10}>
<h1>Infiltration successful!</h1>
<Typography variant="h4">Infiltration successful!</Typography>
</Grid>
<Grid item xs={10}>
<h2>You can trade the confidential information you found for money or reputation.</h2>
<select className={"dropdown"} onChange={changeDropdown}>
<option key={"none"} value={"none"}>
<Typography variant="h5" color="primary">
You can trade the confidential information you found for money or reputation.
</Typography>
<Select value={faction} onChange={changeDropdown}>
<MenuItem key={"none"} value={"none"}>
{"none"}
</option>
</MenuItem>
{player.factions
.filter((f) => Factions[f].getInfo().offersWork())
.map((f) => (
<option key={f} value={f}>
<MenuItem key={f} value={f}>
{f}
</option>
</MenuItem>
))}
</select>
<StdButton
onClick={trade}
text={
<>
{"Trade for "}
{Reputation(repGain)}
{" reputation"}
</>
}
/>
</Select>
<Button onClick={trade}>
Trade for <Reputation reputation={repGain} /> reputation
</Button>
</Grid>
<Grid item xs={3}>
<StdButton
onClick={sell}
text={
<>
{"Sell for "}
<Money money={moneyGain} />
</>
}
/>
<Button onClick={sell}>
Sell for&nbsp;
<Money money={moneyGain} />
</Button>
</Grid>
<Grid item xs={3}>
<StdButton onClick={quitInfiltration} text={"Quit"} />
<Button onClick={quitInfiltration}>Quit</Button>
</Grid>
</Grid>
</>

@ -1,5 +1,6 @@
import React, { useState } from "react";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import { IMinigameProps } from "./IMinigameProps";
import { KeyHandler } from "./KeyHandler";
import { GameTimer } from "./GameTimer";
@ -93,9 +94,9 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
<Grid container spacing={3}>
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>Cut the wires with the following properties! (keyboard 1 to 9)</h1>
<Typography variant="h4">Cut the wires with the following properties! (keyboard 1 to 9)</Typography>
{questions.map((question, i) => (
<h3 key={i}>{question.toString()}</h3>
<Typography key={i}>{question.toString()}</Typography>
))}
<pre>
{new Array(wires.length).fill(0).map((_, i) => (

@ -3,14 +3,13 @@
*
* This subcomponent renders all of the buttons for training at the gym
*/
import * as React from "react";
import React, { useState } from "react";
import Button from "@mui/material/Button";
import { Blackjack } from "../../Casino/Blackjack";
import { CoinFlip } from "../../Casino/CoinFlip";
import { Roulette } from "../../Casino/Roulette";
import { SlotMachine } from "../../Casino/SlotMachine";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton";
enum GameType {
None = "none",
@ -28,71 +27,35 @@ type IState = {
game: GameType;
};
export class CasinoLocation extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
export function CasinoLocation(props: IProps): React.ReactElement {
const [game, setGame] = useState(GameType.None);
this.state = {
game: GameType.None,
};
this.updateGame = this.updateGame.bind(this);
function updateGame(game: GameType): void {
setGame(game);
}
updateGame(game: GameType): void {
this.setState({
game,
});
}
renderGames(): React.ReactNode {
return (
<>
<Button onClick={() => this.updateGame(GameType.Coin)}>Play coin flip</Button>
<br />
<Button onClick={() => this.updateGame(GameType.Slots)}>Play slots</Button>
<br />
<Button onClick={() => this.updateGame(GameType.Roulette)}>Play roulette</Button>
<br />
<Button onClick={() => this.updateGame(GameType.Blackjack)}>Play blackjack</Button>
</>
);
}
renderGame(): React.ReactNode {
let elem = null;
switch (this.state.game) {
case GameType.Coin:
elem = <CoinFlip p={this.props.p} />;
break;
case GameType.Slots:
elem = <SlotMachine p={this.props.p} />;
break;
case GameType.Roulette:
elem = <Roulette p={this.props.p} />;
break;
case GameType.Blackjack:
elem = <Blackjack p={this.props.p} />;
break;
case GameType.None:
break;
default:
throw new Error(`MissingCaseException: ${this.state.game}`);
}
return (
<>
<StdButton onClick={() => this.updateGame(GameType.None)} text={"Stop playing"} />
{elem}
</>
);
}
render(): React.ReactNode {
if (this.state.game === GameType.None) {
return this.renderGames();
} else {
return this.renderGame();
}
}
return (
<>
{game === GameType.None && (
<>
<Button onClick={() => updateGame(GameType.Coin)}>Play coin flip</Button>
<br />
<Button onClick={() => updateGame(GameType.Slots)}>Play slots</Button>
<br />
<Button onClick={() => updateGame(GameType.Roulette)}>Play roulette</Button>
<br />
<Button onClick={() => updateGame(GameType.Blackjack)}>Play blackjack</Button>
</>
)}
{game !== GameType.None && (
<>
<Button onClick={() => updateGame(GameType.None)}>Stop playing</Button>
{game === GameType.Coin && <CoinFlip p={props.p} />}
{game === GameType.Slots && <SlotMachine p={props.p} />}
{game === GameType.Roulette && <Roulette p={props.p} />}
{game === GameType.Blackjack && <Blackjack p={props.p} />}
</>
)}
</>
);
}

@ -12,9 +12,10 @@ import { Locations } from "../Locations";
import { Location } from "../Location";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
import { IRouter } from "../../ui/Router";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
type IProps = {
city: City;
@ -37,7 +38,6 @@ function LocationLetter(location: Location): React.ReactElement {
<span
aria-label={location.name}
key={location.name}
className="tooltip"
style={{
color: "white",
whiteSpace: "nowrap",
@ -112,29 +112,30 @@ function ASCIICity(props: IProps): React.ReactElement {
elems.push(<pre key={i}>{lineElems(lines[i])}</pre>);
}
return <div className="noselect">{elems}</div>;
return <>{elems}</>;
}
function ListCity(props: IProps): React.ReactElement {
const router = use.Router();
const locationButtons = props.city.locations.map((locName) => {
return (
<li key={locName}>
<StdButton onClick={() => toLocation(router, Locations[locName])} text={locName} />
</li>
<React.Fragment key={locName}>
<Button onClick={() => toLocation(router, Locations[locName])}>{locName}</Button>
<br />
</React.Fragment>
);
});
return <ul>{locationButtons}</ul>;
return <>{locationButtons}</>;
}
export function LocationCity(): React.ReactElement {
const player = use.Player();
const city = Cities[player.city];
return (
<div className="noselect">
<h2>{city.name}</h2>
<>
<Typography>{city.name}</Typography>
{Settings.DisableASCIIArt ? <ListCity city={city} /> : <ASCIICity city={city} />}
</div>
</>
);
}

@ -21,9 +21,8 @@ import * as posNames from "../../Company/data/companypositionnames";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
import { createPopup } from "../../ui/React/createPopup";
import { use } from "../../ui/Context";
import { QuitJobPopup } from "../../Company/ui/QuitJobPopup";
import { QuitJobModal } from "../../Company/ui/QuitJobModal";
type IProps = {
locName: LocationName;
@ -32,6 +31,7 @@ type IProps = {
export function CompanyLocation(props: IProps): React.ReactElement {
const p = use.Player();
const router = use.Router();
const [quitOpen, setQuitOpen] = useState(false);
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
@ -179,37 +179,27 @@ export function CompanyLocation(props: IProps): React.ReactElement {
}
}
function quit(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) return;
const popupId = `quit-job-popup`;
createPopup(popupId, QuitJobPopup, {
locName: props.locName,
company: company,
player: p,
onQuit: rerender,
popupId: popupId,
});
}
const isEmployedHere = jobTitle != null;
const favorGain = company.getFavorGain();
return (
<div>
<>
{isEmployedHere && (
<div>
<>
<Typography>Job Title: {jobTitle}</Typography>
<Typography>-------------------------</Typography>
<Box display="flex">
<Tooltip
title={
<>
You will have {Favor(company.favor + favorGain[0])} company favor upon resetting after installing
Augmentations
You will have <Favor favor={company.favor + favorGain[0]} /> company favor upon resetting after
installing Augmentations
</>
}
>
<Typography>Company reputation: {Reputation(company.playerReputation)}</Typography>
<Typography>
Company reputation: <Reputation reputation={company.playerReputation} />
</Typography>
</Tooltip>
</Box>
<Typography>-------------------------</Typography>
@ -223,16 +213,26 @@ export function CompanyLocation(props: IProps): React.ReactElement {
</>
}
>
<Typography className={"tooltip"}>Company Favor: {Favor(company.favor)}</Typography>
<Typography>
Company Favor: <Favor favor={company.favor} />
</Typography>
</Tooltip>
</Box>
<Typography>-------------------------</Typography>
<br />
<Button onClick={work}>Work</Button>
&nbsp;&nbsp;&nbsp;&nbsp;
<Button onClick={quit}>Quit</Button>
</div>
<Button onClick={() => setQuitOpen(true)}>Quit</Button>
<QuitJobModal
locName={props.locName}
company={company}
onQuit={rerender}
open={quitOpen}
onClose={() => setQuitOpen(false)}
/>
</>
)}
<br />
{company.hasAgentPositions() && (
<ApplyToJobButton
company={company}
@ -322,6 +322,6 @@ export function CompanyLocation(props: IProps): React.ReactElement {
/>
)}
{location.infiltrationData != null && <Button onClick={startInfiltration}>Infiltrate Company</Button>}
</div>
</>
);
}

@ -31,10 +31,12 @@ export function CoresButton(props: IProps): React.ReactElement {
return (
<Tooltip title={<MathComponent tex={String.raw`\large{cost = 10^9 \times 7.5 ^{\text{cores}}}`} />}>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' cores ({homeComputer.cpuCores} -&gt; {homeComputer.cpuCores + 1}) -&nbsp;
<Money money={cost} player={props.p} />
</Button>
<span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' cores ({homeComputer.cpuCores} -&gt; {homeComputer.cpuCores + 1}) -&nbsp;
<Money money={cost} player={props.p} />
</Button>
</span>
</Tooltip>
);
}

@ -64,7 +64,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
}
if (loc.types.includes(LocationType.TechVendor)) {
content.push(<TechVendorLocation key={"techvendorlocation"} loc={loc} p={player} />);
content.push(<TechVendorLocation key={"techvendorlocation"} loc={loc} />);
}
if (loc.types.includes(LocationType.TravelAgency)) {
@ -90,7 +90,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement {
return (
<>
<Button onClick={() => router.toCity()}>Return to World</Button>
<Typography variant="h4" className="noselect">
<Typography variant="h4">
{backdoorInstalled && !Settings.DisableTextEffects ? <CorruptableText content={loc.name} /> : loc.name}
</Typography>
{locContent}

@ -9,7 +9,6 @@ import Button from "@mui/material/Button";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { getHospitalizationCost } from "../../Hospital/Hospital";
import { AutoupdatingStdButton } from "../../ui/React/AutoupdatingStdButton";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../ui/React/DialogBox";

@ -0,0 +1,67 @@
/**
* React Component for the popup used to purchase a new server.
*/
import React, { useState } from "react";
import { purchaseServer } from "../../Server/ServerPurchases";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { Modal } from "../../ui/React/Modal";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
interface IProps {
open: boolean;
onClose: () => void;
ram: number;
cost: number;
rerender: () => void;
}
export function PurchaseServerModal(props: IProps): React.ReactElement {
const player = use.Player();
const [hostname, setHostname] = useState("");
function tryToPurchaseServer(): void {
purchaseServer(hostname, props.ram, props.cost, player);
props.onClose();
}
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) tryToPurchaseServer();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setHostname(event.target.value);
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
Would you like to purchase a new server with {numeralWrapper.formatRAM(props.ram)} of RAM for{" "}
<Money money={props.cost} player={player} />?
</Typography>
<br />
<br />
<Typography> Please enter the server hostname below:</Typography>
<br />
<TextField
autoFocus
onKeyUp={onKeyUp}
onChange={onChange}
type="text"
placeholder="Unique Hostname"
InputProps={{
endAdornment: (
<Button onClick={tryToPurchaseServer} disabled={!player.canAfford(props.cost) || hostname === ""}>
Buy
</Button>
),
}}
/>
</Modal>
);
}

@ -1,58 +0,0 @@
/**
* React Component for the popup used to purchase a new server.
*/
import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
import { purchaseServer } from "../../Server/ServerPurchases";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Money } from "../../ui/React/Money";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton";
interface IPurchaseServerPopupProps {
ram: number;
cost: number;
p: IPlayer;
popupId: string;
rerender: () => void;
}
export function PurchaseServerPopup(props: IPurchaseServerPopupProps): React.ReactElement {
const [hostname, setHostname] = useState("");
function tryToPurchaseServer(): void {
purchaseServer(hostname, props.ram, props.cost, props.p);
removePopup(props.popupId);
}
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) tryToPurchaseServer();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setHostname(event.target.value);
}
return (
<>
Would you like to purchase a new server with {numeralWrapper.formatRAM(props.ram)} of RAM for{" "}
<Money money={props.cost} player={props.p} />?
<br />
<br />
Please enter the server hostname below:
<br />
<div className="popup-box-input-div">
<input
autoFocus
onKeyUp={onKeyUp}
onChange={onChange}
className="text-input noselect"
type="text"
placeholder="Unique Hostname"
/>
<StdButton onClick={tryToPurchaseServer} text="Purchase Server" disabled={!props.p.canAfford(props.cost)} />
</div>
</>
);
}

@ -29,10 +29,12 @@ export function RamButton(props: IProps): React.ReactElement {
return (
<Tooltip title={<MathComponent tex={String.raw`\large{cost = 3.2 \times 10^3 \times 1.58^{log_2{(ram)}}}`} />}>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' RAM ({homeComputer.maxRam}GB -&gt;&nbsp;{homeComputer.maxRam * 2}GB) -&nbsp;
<Money money={cost} player={props.p} />
</Button>
<span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' RAM ({homeComputer.maxRam}GB -&gt;&nbsp;{homeComputer.maxRam * 2}GB) -&nbsp;
<Money money={cost} player={props.p} />
</Button>
</span>
</Tooltip>
);
}

@ -113,7 +113,7 @@ export function SlumsLocation(): React.ReactElement {
const heistChance = Crimes.Heist.successRate(player);
return (
<div>
<>
<Tooltip title={<>Attempt to shoplift from a low-end retailer</>}>
<Button onClick={shoplift}>
Shoplift ({numeralWrapper.formatPercentage(shopliftChance)} chance of success)
@ -180,6 +180,6 @@ export function SlumsLocation(): React.ReactElement {
<Button onClick={heist}>Heist ({numeralWrapper.formatPercentage(heistChance)} chance of success)</Button>
</Tooltip>
<br />
</div>
</>
);
}

@ -12,36 +12,50 @@ import { RamButton } from "./RamButton";
import { TorButton } from "./TorButton";
import { CoresButton } from "./CoresButton";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { getPurchaseServerCost } from "../../Server/ServerPurchases";
import { StdButton } from "../../ui/React/StdButton";
import { Money } from "../../ui/React/Money";
import { createPopup } from "../../ui/React/createPopup";
import { PurchaseServerPopup } from "./PurchaseServerPopup";
import { use } from "../../ui/Context";
import { PurchaseServerModal } from "./PurchaseServerModal";
interface IServerProps {
ram: number;
rerender: () => void;
}
function ServerButton(props: IServerProps): React.ReactElement {
const [open, setOpen] = useState(false);
const player = use.Player();
const cost = getPurchaseServerCost(props.ram);
return (
<>
<Button onClick={() => setOpen(true)} disabled={!player.canAfford(cost)}>
Purchase {props.ram}GB Server&nbsp;-&nbsp;
<Money money={cost} player={player} />
</Button>
<PurchaseServerModal
open={open}
onClose={() => setOpen(false)}
ram={props.ram}
cost={cost}
rerender={props.rerender}
/>
<br />
</>
);
}
type IProps = {
loc: Location;
p: IPlayer;
};
export function TechVendorLocation(props: IProps): React.ReactElement {
const player = use.Player();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
function openPurchaseServer(ram: number, cost: number, p: IPlayer): void {
const popupId = "purchase-server-popup";
createPopup(popupId, PurchaseServerPopup, {
ram: ram,
cost: cost,
p: p,
popupId: popupId,
rerender: rerender,
});
}
useEffect(() => {
const id = setInterval(rerender, 1000);
return () => clearInterval(id);
@ -49,31 +63,22 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
const purchaseServerButtons: React.ReactNode[] = [];
for (let i = props.loc.techVendorMinRam; i <= props.loc.techVendorMaxRam; i *= 2) {
const cost = getPurchaseServerCost(i);
purchaseServerButtons.push(
<>
<Button key={i} onClick={() => openPurchaseServer(i, cost, props.p)} disabled={!props.p.canAfford(cost)}>
Purchase {i}GB Server&nbsp;-&nbsp;
<Money money={cost} player={props.p} />
</Button>
<br />
</>,
);
purchaseServerButtons.push(<ServerButton key={i} ram={i} rerender={rerender} />);
}
return (
<div>
<>
{purchaseServerButtons}
<br />
<Typography className="noselect">
<Typography>
<i>"You can order bigger servers via scripts. We don't take custom order in person."</i>
</Typography>
<br />
<TorButton p={props.p} rerender={rerender} />
<TorButton p={player} rerender={rerender} />
<br />
<RamButton p={props.p} rerender={rerender} />
<RamButton p={player} rerender={rerender} />
<br />
<CoresButton p={props.p} rerender={rerender} />
</div>
<CoresButton p={player} rerender={rerender} />
</>
);
}

@ -25,7 +25,8 @@ export function TorButton(props: IProps): React.ReactElement {
return (
<Button disabled={!props.p.canAfford(CONSTANTS.TorRouterCost)} onClick={buy}>
Purchase TOR router - <Money money={CONSTANTS.TorRouterCost} player={props.p} />
Purchase TOR router -&nbsp;
<Money money={CONSTANTS.TorRouterCost} player={props.p} />
</Button>
);
}

@ -13,7 +13,6 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { IRouter } from "../../ui/Router";
import { Settings } from "../../Settings/Settings";
import { StdButton } from "../../ui/React/StdButton";
import { use } from "../../ui/Context";
import { Money } from "../../ui/React/Money";
import { WorldMap } from "../../ui/React/WorldMap";
@ -36,7 +35,7 @@ function travel(p: IPlayer, router: IRouter, to: CityName): void {
p.loseMoney(cost);
p.travel(to);
dialogBoxCreate(<span className="noselect">You are now in {to}!</span>);
dialogBoxCreate(<>You are now in {to}!</>);
router.toCity();
}
@ -77,7 +76,7 @@ export function TravelAgencyRoot(props: IProps): React.ReactElement {
<Money money={CONSTANTS.TravelCost} player={props.p} />.
</Typography>
{Settings.DisableASCIIArt ? (
<div>
<>
{Object.values(CityName)
.filter((city: string) => city != props.p.city)
.map((city: string) => {
@ -92,7 +91,7 @@ export function TravelAgencyRoot(props: IProps): React.ReactElement {
</React.Fragment>
);
})}
</div>
</>
) : (
<WorldMap currentCity={props.p.city} onTravel={(city: CityName) => startTravel(city)} />
)}

@ -146,7 +146,7 @@ import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { is2DArray } from "./utils/helpers/is2DArray";
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
import { logBoxCreate } from "./ui/React/LogBox";
import { LogBoxEvents } from "./ui/React/LogBoxManager";
import { arrayToString } from "./utils/helpers/arrayToString";
import { isString } from "./utils/helpers/isString";
@ -1203,7 +1203,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
return;
}
logBoxCreate(runningScriptObj);
LogBoxEvents.emit(runningScriptObj);
},
nuke: function (ip: any): any {
updateDynamicRam("nuke", getRamCost("nuke"));

@ -626,7 +626,7 @@ export function finishWork(this: IPlayer, cancelled: boolean, sing = false): str
You earned a total of: <br />
<Money money={this.workMoneyGained} />
<br />
{Reputation(this.workRepGained)} reputation for the company <br />
<Reputation reputation={this.workRepGained} /> reputation for the company <br />
{numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
{numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
@ -747,7 +747,7 @@ export function finishWorkPartTime(this: IPlayer, sing = false): string {
You earned a total of: <br />
<Money money={this.workMoneyGained} />
<br />
{Reputation(this.workRepGained)} reputation for the company <br />
<Reputation reputation={this.workRepGained} /> reputation for the company <br />
{numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
{numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />
@ -919,7 +919,7 @@ export function finishFactionWork(this: IPlayer, cancelled: boolean, sing = fals
You earned a total of: <br />
<Money money={this.workMoneyGained} />
<br />
{Reputation(this.workRepGained)} reputation for the faction <br />
<Reputation reputation={this.workRepGained} /> reputation for the faction <br />
{numeralWrapper.formatExp(this.workHackExpGained)} hacking exp <br />
{numeralWrapper.formatExp(this.workStrExpGained)} strength exp <br />
{numeralWrapper.formatExp(this.workDefExpGained)} defense exp <br />

@ -8,6 +8,13 @@ import { Money } from "../../../ui/React/Money";
import { numeralWrapper } from "../../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import Grid from "@mui/material/Grid";
interface IProps {
resleeve: Resleeve;
player: IPlayer;
@ -19,81 +26,83 @@ export function ResleeveElem(props: IProps): React.ReactElement {
function openStats(): void {
dialogBoxCreate(
<>
<h2>
<u>Total Multipliers:</u>
</h2>
Hacking Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_mult)}
<br />
Hacking Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_exp_mult)}
<br />
Strength Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_mult)}
<br />
Strength Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_exp_mult)}
<br />
Defense Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_mult)}
<br />
Defense Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_exp_mult)}
<br />
Dexterity Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_mult)}
<br />
Dexterity Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_exp_mult)}
<br />
Agility Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_mult)}
<br />
Agility Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_exp_mult)}
<br />
Charisma Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_mult)}
<br />
Charisma Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_exp_mult)}
<br />
Hacking Chance multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_chance_mult)}
<br />
Hacking Speed multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_speed_mult)}
<br />
Hacking Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_money_mult)}
<br />
Hacking Growth multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_grow_mult)}
<br />
Salary multiplier: {numeralWrapper.formatPercentage(props.resleeve.work_money_mult)}
<br />
Company Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.company_rep_mult)}
<br />
Faction Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.faction_rep_mult)}
<br />
Crime Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_money_mult)}
<br />
Crime Success multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_success_mult)}
<br />
Hacknet Income multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_money_mult)}
<br />
Hacknet Purchase Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_purchase_cost_mult)}
<br />
Hacknet Level Upgrade Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_level_cost_mult)}
<br />
Hacknet Ram Upgrade Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_ram_cost_mult)}
<br />
Hacknet Core Upgrade Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_core_cost_mult)}
<br />
Bladeburner Max Stamina multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_max_stamina_mult)}
<br />
Bladeburner Stamina Gain multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_stamina_gain_mult)}
<br />
Bladeburner Field Analysis multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_analysis_mult)}
<br />
Bladeburner Success Chance multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_success_chance_mult)}
<Typography variant="h5" color="primary">
Total Multipliers:
</Typography>
<Typography>
Hacking Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_mult)}
<br />
Hacking Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_exp_mult)}
<br />
Strength Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_mult)}
<br />
Strength Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_exp_mult)}
<br />
Defense Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_mult)}
<br />
Defense Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_exp_mult)}
<br />
Dexterity Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_mult)}
<br />
Dexterity Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_exp_mult)}
<br />
Agility Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_mult)}
<br />
Agility Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_exp_mult)}
<br />
Charisma Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_mult)}
<br />
Charisma Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_exp_mult)}
<br />
Hacking Chance multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_chance_mult)}
<br />
Hacking Speed multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_speed_mult)}
<br />
Hacking Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_money_mult)}
<br />
Hacking Growth multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_grow_mult)}
<br />
Salary multiplier: {numeralWrapper.formatPercentage(props.resleeve.work_money_mult)}
<br />
Company Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.company_rep_mult)}
<br />
Faction Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.faction_rep_mult)}
<br />
Crime Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_money_mult)}
<br />
Crime Success multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_success_mult)}
<br />
Hacknet Income multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_money_mult)}
<br />
Hacknet Purchase Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_purchase_cost_mult)}
<br />
Hacknet Level Upgrade Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_level_cost_mult)}
<br />
Hacknet Ram Upgrade Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_ram_cost_mult)}
<br />
Hacknet Core Upgrade Cost multiplier:
{numeralWrapper.formatPercentage(props.resleeve.hacknet_node_core_cost_mult)}
<br />
Bladeburner Max Stamina multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_max_stamina_mult)}
<br />
Bladeburner Stamina Gain multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_stamina_gain_mult)}
<br />
Bladeburner Field Analysis multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_analysis_mult)}
<br />
Bladeburner Success Chance multiplier:
{numeralWrapper.formatPercentage(props.resleeve.bladeburner_success_chance_mult)}
</Typography>
</>,
);
}
function onAugChange(event: React.ChangeEvent<HTMLSelectElement>): void {
function onAugChange(event: SelectChangeEvent<string>): void {
setAug(event.target.value);
}
@ -110,50 +119,48 @@ export function ResleeveElem(props: IProps): React.ReactElement {
}
return (
<div className="resleeve-elem" style={{ display: "block" }}>
<div className="resleeve-panel" style={{ width: "30%" }}>
<p>
Hacking: {numeralWrapper.formatSkill(props.resleeve.hacking_skill)} (
{numeralWrapper.formatExp(props.resleeve.hacking_exp)} exp)
<br />
Strength: {numeralWrapper.formatSkill(props.resleeve.strength)} (
{numeralWrapper.formatExp(props.resleeve.strength_exp)} exp)
<br />
Defense: {numeralWrapper.formatSkill(props.resleeve.defense)} (
{numeralWrapper.formatExp(props.resleeve.defense_exp)} exp)
<br />
Dexterity: {numeralWrapper.formatSkill(props.resleeve.dexterity)} (
{numeralWrapper.formatExp(props.resleeve.dexterity_exp)} exp)
<br />
Agility: {numeralWrapper.formatSkill(props.resleeve.agility)} (
{numeralWrapper.formatExp(props.resleeve.agility_exp)} exp)
<br />
Charisma: {numeralWrapper.formatSkill(props.resleeve.charisma)} (
{numeralWrapper.formatExp(props.resleeve.charisma_exp)} exp)
<br /># Augmentations: {props.resleeve.augmentations.length}
</p>
<button className="std-button" onClick={openStats}>
Multipliers
</button>
</div>
<div className="resleeve-panel" style={{ width: "50%" }}>
<select className="resleeve-aug-selector dropdown" onChange={onAugChange}>
{props.resleeve.augmentations.map((aug) => (
<option key={aug.name} value={aug.name}>
{aug.name}
</option>
))}
</select>
<p>{currentAug !== undefined && currentAug.info}</p>
</div>
<div className="resleeve-panel" style={{ width: "20%" }}>
<p>
It costs <Money money={cost} player={props.player} /> to purchase this Sleeve.
</p>
<button className="std-button" onClick={purchase}>
Purchase
</button>
</div>
</div>
<Paper sx={{ my: 1 }}>
<Grid container>
<Grid item xs={3}>
<Typography>
Hacking: {numeralWrapper.formatSkill(props.resleeve.hacking_skill)} (
{numeralWrapper.formatExp(props.resleeve.hacking_exp)} exp)
<br />
Strength: {numeralWrapper.formatSkill(props.resleeve.strength)} (
{numeralWrapper.formatExp(props.resleeve.strength_exp)} exp)
<br />
Defense: {numeralWrapper.formatSkill(props.resleeve.defense)} (
{numeralWrapper.formatExp(props.resleeve.defense_exp)} exp)
<br />
Dexterity: {numeralWrapper.formatSkill(props.resleeve.dexterity)} (
{numeralWrapper.formatExp(props.resleeve.dexterity_exp)} exp)
<br />
Agility: {numeralWrapper.formatSkill(props.resleeve.agility)} (
{numeralWrapper.formatExp(props.resleeve.agility_exp)} exp)
<br />
Charisma: {numeralWrapper.formatSkill(props.resleeve.charisma)} (
{numeralWrapper.formatExp(props.resleeve.charisma_exp)} exp)
<br /># Augmentations: {props.resleeve.augmentations.length}
</Typography>
<Button onClick={openStats}>Multipliers</Button>
</Grid>
<Grid item xs={6}>
<Select value={aug} onChange={onAugChange}>
{props.resleeve.augmentations.map((aug) => (
<MenuItem key={aug.name} value={aug.name}>
{aug.name}
</MenuItem>
))}
</Select>
<Typography>{currentAug !== undefined && currentAug.info}</Typography>
</Grid>
<Grid item xs={3}>
<Typography>
It costs <Money money={cost} player={props.player} /> to purchase this Sleeve.
</Typography>
<Button onClick={purchase}>Purchase</Button>
</Grid>
</Grid>
</Paper>
);
}

@ -1,9 +1,13 @@
import React, { useState } from "react";
import { IPlayer } from "../../IPlayer";
import { generateResleeves } from "../Resleeving";
import { Resleeve } from "../Resleeve";
import { ResleeveElem } from "./ResleeveElem";
import { use } from "../../../ui/Context";
import Typography from "@mui/material/Typography";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import Box from "@mui/material/Box";
const SortOption: {
[key: string]: string | undefined;
@ -69,28 +73,25 @@ const SortFunctions: {
TotalNumAugmentations: (a: Resleeve, b: Resleeve): number => a.augmentations.length - b.augmentations.length,
};
interface IProps {
player: IPlayer;
}
export function ResleeveRoot(props: IProps): React.ReactElement {
export function ResleeveRoot(): React.ReactElement {
const player = use.Player();
const [sort, setSort] = useState(SortOption.Cost);
// Randomly create all Resleeves if they dont already exist
if (props.player.resleeves.length === 0) {
props.player.resleeves = generateResleeves();
if (player.resleeves.length === 0) {
player.resleeves = generateResleeves();
}
function onSortChange(event: React.ChangeEvent<HTMLSelectElement>): void {
function onSortChange(event: SelectChangeEvent<string>): void {
setSort(event.target.value);
}
const sortFunction = SortFunctions[sort];
if (sortFunction === undefined) throw new Error(`sort function '${sort}' is undefined`);
props.player.resleeves.sort(sortFunction);
player.resleeves.sort(sortFunction);
return (
<>
<p style={{ display: "block", width: "75%" }}>
<Typography>
Re-sleeving is the process of digitizing and transferring your consciousness into a new human body, or 'sleeve'.
Here at VitaLife, you can purchase new specially-engineered bodies for the re-sleeve process. Many of these
bodies even come with genetic and cybernetic Augmentations!
@ -104,17 +105,19 @@ export function ResleeveRoot(props: IProps): React.ReactElement {
<br />
<br />
NOTE: The stats and multipliers displayed on this page do NOT include your bonuses from Source-File.
</p>
<p style={{ display: "inline-block" }}>Sort By: </p>
<select className="dropdown" defaultValue={sort} onChange={onSortChange}>
{Object.keys(SortOption).map((opt) => (
<option key={opt} value={opt}>
{SortOption[opt]}
</option>
))}
</select>
{props.player.resleeves.map((resleeve, i) => (
<ResleeveElem key={i} player={props.player} resleeve={resleeve} />
</Typography>
<Box display="flex" alignItems="center">
<Typography>Sort By: </Typography>
<Select value={sort} onChange={onSortChange}>
{Object.keys(SortOption).map((opt) => (
<MenuItem key={opt} value={opt}>
{SortOption[opt]}
</MenuItem>
))}
</Select>
</Box>
{player.resleeves.map((resleeve, i) => (
<ResleeveElem key={i} player={player} resleeve={resleeve} />
))}
</>
);

@ -58,7 +58,7 @@ export function CovenantPurchasesRoot(props: IProps): React.ReactElement {
player.sleeves.push(new Sleeve(player));
rerender();
} else {
dialogBoxCreate(`You cannot afford to purchase a Duplicate Sleeve`, false);
dialogBoxCreate(`You cannot afford to purchase a Duplicate Sleeve`);
}
}

@ -161,7 +161,7 @@ export function SleeveElem(props: IProps): React.ReactElement {
];
if (props.sleeve.currentTask === SleeveTaskType.Company || props.sleeve.currentTask === SleeveTaskType.Faction) {
const repGain: number = props.sleeve.getRepGain(player);
data.push([`Reputation:`, ReputationRate(5 * repGain)]);
data.push([`Reputation:`, <ReputationRate reputation={5 * repGain} />]);
}
}

@ -10,9 +10,7 @@ import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFuncti
import { getServer } from "../../Server/ServerHelpers";
import { numeralWrapper } from "../../ui/numeralFormat";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { createPopup } from "../../ui/React/createPopup";
import { BitFlumePopup } from "../../BitNode/ui/BitFlumePopup";
import { BitFlumeEvent } from "../../BitNode/ui/BitFlumeModal";
import { calculateHackingTime, calculateGrowTime, calculateWeakenTime } from "../../Hacking";
function requireHackingLevel(lvl: number) {
@ -286,12 +284,7 @@ export const programsMetadata: IProgramCreationParams[] = [
time: CONSTANTS.MillisecondsPerFiveMinutes / 20,
},
run: (router: IRouter, terminal: ITerminal, player: IPlayer): void => {
const popupId = "bitflume-popup";
createPopup(popupId, BitFlumePopup, {
player: player,
router: router,
popupId: popupId,
});
BitFlumeEvent.emit();
},
},
{

@ -307,7 +307,7 @@ export function Root(props: IProps): React.ReactElement {
<Editor
beforeMount={beforeMount}
onMount={onMount}
loading={<p>Loading script editor!</p>}
loading={<Typography>Loading script editor!</Typography>}
height="90%"
defaultLanguage="javascript"
defaultValue={code}

@ -46,7 +46,7 @@ export function getPurchaseServerMaxRam(): number {
export function purchaseServer(hostname: string, ram: number, cost: number, p: IPlayer): void {
//Check if player has enough money
if (!p.canAfford(cost)) {
dialogBoxCreate("You don't have enough money to purchase this server!", false);
dialogBoxCreate("You don't have enough money to purchase this server!");
return;
}

@ -3,17 +3,22 @@
* general information about the stock market, buttons for the various purchases,
* and a link to the documentation (Investopedia)
*/
import * as React from "react";
import React, { useState } from "react";
import { getStockMarket4SDataCost, getStockMarket4STixApiCost } from "../StockMarketCosts";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { StdButton } from "../../ui/React/StdButton";
import { StdButtonPurchased } from "../../ui/React/StdButtonPurchased";
import { Money } from "../../ui/React/Money";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import Typography from "@mui/material/Typography";
import Link from "@mui/material/Link";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import IconButton from "@mui/material/IconButton";
import HelpIcon from "@mui/icons-material/Help";
import CheckIcon from "@mui/icons-material/Check";
import { StaticModal } from "../../ui/React/StaticModal";
type IProps = {
initStockMarket: () => void;
@ -21,228 +26,210 @@ type IProps = {
rerender: () => void;
};
const blockStyleMarkup = {
display: "block",
};
export class InfoAndPurchases extends React.Component<IProps, any> {
constructor(props: IProps) {
super(props);
this.handleClick4SMarketDataHelpTip = this.handleClick4SMarketDataHelpTip.bind(this);
this.purchaseWseAccount = this.purchaseWseAccount.bind(this);
this.purchaseTixApiAccess = this.purchaseTixApiAccess.bind(this);
this.purchase4SMarketData = this.purchase4SMarketData.bind(this);
this.purchase4SMarketDataTixApiAccess = this.purchase4SMarketDataTixApiAccess.bind(this);
}
handleClick4SMarketDataHelpTip(): void {
dialogBoxCreate(
"Access to the 4S Market Data feed will display two additional pieces " +
"of information about each stock: Price Forecast & Volatility<br><br>" +
"Price Forecast indicates the probability the stock has of increasing or " +
"decreasing. A '+' forecast means the stock has a higher chance of increasing " +
"than decreasing, and a '-' means the opposite. The number of '+/-' symbols " +
"is used to illustrate the magnitude of these probabilities. For example, " +
"'+++' means that the stock has a significantly higher chance of increasing " +
"than decreasing, while '+' means that the stock only has a slightly higher chance " +
"of increasing than decreasing.<br><br>" +
"Volatility represents the maximum percentage by which a stock's price " +
"can change every tick (a tick occurs every few seconds while the game " +
"is running).<br><br>" +
"A stock's price forecast can change over time. This is also affected by volatility. " +
"The more volatile a stock is, the more its price forecast will change.",
);
}
purchaseWseAccount(): void {
if (this.props.p.hasWseAccount) {
function Purchase4SMarketDataTixApiAccessButton(props: IProps): React.ReactElement {
function purchase4SMarketDataTixApiAccess(): void {
if (props.p.has4SDataTixApi) {
return;
}
if (!this.props.p.canAfford(CONSTANTS.WSEAccountCost)) {
if (!props.p.canAfford(getStockMarket4STixApiCost())) {
return;
}
this.props.p.hasWseAccount = true;
this.props.initStockMarket();
this.props.p.loseMoney(CONSTANTS.WSEAccountCost);
this.props.rerender();
const worldHeader = document.getElementById("world-menu-header");
if (worldHeader instanceof HTMLElement) {
worldHeader.click();
worldHeader.click();
}
props.p.has4SDataTixApi = true;
props.p.loseMoney(getStockMarket4STixApiCost());
props.rerender();
}
purchaseTixApiAccess(): void {
if (this.props.p.hasTixApiAccess) {
return;
}
if (!this.props.p.canAfford(CONSTANTS.TIXAPICost)) {
return;
}
this.props.p.hasTixApiAccess = true;
this.props.p.loseMoney(CONSTANTS.TIXAPICost);
this.props.rerender();
}
purchase4SMarketData(): void {
if (this.props.p.has4SData) {
return;
}
if (!this.props.p.canAfford(getStockMarket4SDataCost())) {
return;
}
this.props.p.has4SData = true;
this.props.p.loseMoney(getStockMarket4SDataCost());
this.props.rerender();
}
purchase4SMarketDataTixApiAccess(): void {
if (this.props.p.has4SDataTixApi) {
return;
}
if (!this.props.p.canAfford(getStockMarket4STixApiCost())) {
return;
}
this.props.p.has4SDataTixApi = true;
this.props.p.loseMoney(getStockMarket4STixApiCost());
this.props.rerender();
}
renderPurchaseWseAccountButton(): React.ReactElement {
if (this.props.p.hasWseAccount) {
return <StdButtonPurchased text={"WSE Account - Purchased"} />;
} else {
const cost = CONSTANTS.WSEAccountCost;
return (
<StdButton
disabled={!this.props.p.canAfford(cost)}
onClick={this.purchaseWseAccount}
text={
<>
Buy WSE Account - <Money money={cost} player={this.props.p} />
</>
}
/>
);
}
}
renderPurchaseTixApiAccessButton(): React.ReactElement {
if (this.props.p.hasTixApiAccess) {
return <StdButtonPurchased text={"TIX API Access - Purchased"} />;
} else {
const cost = CONSTANTS.TIXAPICost;
return (
<StdButton
disabled={!this.props.p.canAfford(cost) || !this.props.p.hasWseAccount}
onClick={this.purchaseTixApiAccess}
style={blockStyleMarkup}
text={
<>
Buy Trade Information eXchange (TIX) API Access - <Money money={cost} player={this.props.p} />
</>
}
/>
);
}
}
renderPurchase4SMarketDataButton(): React.ReactElement {
if (this.props.p.has4SData) {
return (
<StdButtonPurchased
text={"4S Market Data - Purchased"}
tooltip={"Lets you view additional pricing and volatility information about stocks"}
/>
);
} else {
const cost = getStockMarket4SDataCost();
return (
<StdButton
disabled={!this.props.p.canAfford(cost) || !this.props.p.hasWseAccount}
onClick={this.purchase4SMarketData}
text={
<>
Buy 4S Market Data Access - <Money money={cost} player={this.props.p} />
</>
}
tooltip={"Lets you view additional pricing and volatility information about stocks"}
/>
);
}
}
renderPurchase4SMarketDataTixApiAccessButton(): React.ReactElement {
if (!this.props.p.hasTixApiAccess) {
return (
<StdButton disabled={true} text={`Buy 4S Market Data TIX API Access`} tooltip={"Requires TIX API Access"} />
);
} else if (this.props.p.has4SDataTixApi) {
return (
<StdButtonPurchased
text={"4S Market Data TIX API - Purchased"}
tooltip={"Let you access 4S Market Data through Netscript"}
/>
);
} else {
const cost = getStockMarket4STixApiCost();
return (
<StdButton
disabled={!this.props.p.canAfford(cost)}
onClick={this.purchase4SMarketDataTixApiAccess}
text={
<>
Buy 4S Market Data TIX API Access - <Money money={cost} player={this.props.p} />
</>
}
tooltip={"Let you access 4S Market Data through Netscript"}
/>
);
}
}
render(): React.ReactNode {
const documentationLink = "https://bitburner.readthedocs.io/en/latest/basicgameplay/stockmarket.html";
if (props.p.has4SDataTixApi) {
return (
<div className={"stock-market-info-and-purchases"}>
<p>Welcome to the World Stock Exchange (WSE)!</p>
<button className={"std-button"}>
<a href={documentationLink} target={"_blank"}>
Investopedia
</a>
</button>
<br />
<p>To begin trading, you must first purchase an account:</p>
{this.renderPurchaseWseAccountButton()}
<h2>Trade Information eXchange (TIX) API</h2>
<p>
TIX, short for Trade Information eXchange, is the communications protocol used by the WSE. Purchasing access
to the TIX API lets you write code to create your own algorithmic/automated trading strategies.
</p>
{this.renderPurchaseTixApiAccessButton()}
<h2>Four Sigma (4S) Market Data Feed</h2>
<p>
Four Sigma's (4S) Market Data Feed provides information about stocks that will help your trading strategies.
</p>
{this.renderPurchase4SMarketDataButton()}
<button className={"help-tip-big"} onClick={this.handleClick4SMarketDataHelpTip}>
?
</button>
{this.renderPurchase4SMarketDataTixApiAccessButton()}
<p>
Commission Fees: Every transaction you make has a{" "}
<Money money={CONSTANTS.StockMarketCommission} player={this.props.p} /> commission fee.
</p>
<br />
<p>
WARNING: When you reset after installing Augmentations, the Stock Market is reset. You will retain your WSE
Account, access to the TIX API, and 4S Market Data access. However, all of your stock positions are lost, so
make sure to sell your stocks before installing Augmentations!
</p>
</div>
<Typography>
Market Data TIX API Access <CheckIcon />
</Typography>
);
} else {
const cost = getStockMarket4STixApiCost();
return (
<Tooltip
title={
!props.p.hasTixApiAccess ? (
<Typography>Requires TIX API Access</Typography>
) : (
<Typography>Let you access 4S Market Data through Netscript</Typography>
)
}
>
<span>
<Button
disabled={!props.p.hasTixApiAccess || !props.p.canAfford(cost)}
onClick={purchase4SMarketDataTixApiAccess}
>
Buy 4S Market Data TIX API Access -&nbsp;
<Money money={cost} player={props.p} />
</Button>
</span>
</Tooltip>
);
}
}
function PurchaseWseAccountButton(props: IProps): React.ReactElement {
if (props.p.hasWseAccount) {
return (
<Typography>
WSE Account <CheckIcon />
</Typography>
);
}
function purchaseWseAccount(): void {
if (props.p.hasWseAccount) {
return;
}
if (!props.p.canAfford(CONSTANTS.WSEAccountCost)) {
return;
}
props.p.hasWseAccount = true;
props.initStockMarket();
props.p.loseMoney(CONSTANTS.WSEAccountCost);
props.rerender();
}
const cost = CONSTANTS.WSEAccountCost;
return (
<>
<Typography>To begin trading, you must first purchase an account:</Typography>
<Button disabled={!props.p.canAfford(cost)} onClick={purchaseWseAccount}>
Buy WSE Account -&nbsp;
<Money money={cost} player={props.p} />
</Button>
</>
);
}
function PurchaseTixApiAccessButton(props: IProps): React.ReactElement {
function purchaseTixApiAccess(): void {
if (props.p.hasTixApiAccess) {
return;
}
if (!props.p.canAfford(CONSTANTS.TIXAPICost)) {
return;
}
props.p.hasTixApiAccess = true;
props.p.loseMoney(CONSTANTS.TIXAPICost);
props.rerender();
}
if (props.p.hasTixApiAccess) {
return (
<Typography>
TIX API Access <CheckIcon />
</Typography>
);
} else {
const cost = CONSTANTS.TIXAPICost;
return (
<Button disabled={!props.p.canAfford(cost) || !props.p.hasWseAccount} onClick={purchaseTixApiAccess}>
Buy Trade Information eXchange (TIX) API Access -&nbsp;
<Money money={cost} player={props.p} />
</Button>
);
}
}
function Purchase4SMarketDataButton(props: IProps): React.ReactElement {
function purchase4SMarketData(): void {
if (props.p.has4SData) {
return;
}
if (!props.p.canAfford(getStockMarket4SDataCost())) {
return;
}
props.p.has4SData = true;
props.p.loseMoney(getStockMarket4SDataCost());
props.rerender();
}
if (props.p.has4SData) {
return (
<Typography>
4S Market Data Access <CheckIcon />
</Typography>
);
} else {
const cost = getStockMarket4SDataCost();
return (
<Tooltip
title={<Typography>Lets you view additional pricing and volatility information about stocks</Typography>}
>
<span>
<Button disabled={!props.p.canAfford(cost) || !props.p.hasWseAccount} onClick={purchase4SMarketData}>
Buy 4S Market Data Access -&nbsp;
<Money money={cost} player={props.p} />
</Button>
</span>
</Tooltip>
);
}
}
export function InfoAndPurchases(props: IProps): React.ReactElement {
const [helpOpen, setHelpOpen] = useState(false);
const documentationLink = "https://bitburner.readthedocs.io/en/latest/basicgameplay/stockmarket.html";
return (
<>
<Typography>Welcome to the World Stock Exchange (WSE)!</Typography>
<Link href={documentationLink} target={"_blank"}>
Investopedia
</Link>
<br />
<PurchaseWseAccountButton {...props} />
<Typography variant="h5" color="primary">
Trade Information eXchange (TIX) API
</Typography>
<Typography>
TIX, short for Trade Information eXchange, is the communications protocol used by the WSE. Purchasing access to
the TIX API lets you write code to create your own algorithmic/automated trading strategies.
</Typography>
<PurchaseTixApiAccessButton {...props} />
<Typography variant="h5" color="primary">
Four Sigma (4S) Market Data Feed
</Typography>
<Typography>
Four Sigma's (4S) Market Data Feed provides information about stocks that will help your trading strategies.
<IconButton onClick={() => setHelpOpen(true)}>
<HelpIcon />
</IconButton>
</Typography>
<Purchase4SMarketDataTixApiAccessButton {...props} />
<Purchase4SMarketDataButton {...props} />
<Typography>
Commission Fees: Every transaction you make has a{" "}
<Money money={CONSTANTS.StockMarketCommission} player={props.p} /> commission fee.
</Typography>
<br />
<Typography>
WARNING: When you reset after installing Augmentations, the Stock Market is reset. You will retain your WSE
Account, access to the TIX API, and 4S Market Data access. However, all of your stock positions are lost, so
make sure to sell your stocks before installing Augmentations!
</Typography>
<StaticModal open={helpOpen} onClose={() => setHelpOpen(false)}>
<Typography>
Access to the 4S Market Data feed will display two additional pieces of information about each stock: Price
Forecast & Volatility
<br />
<br />
Price Forecast indicates the probability the stock has of increasing or decreasing. A '+' forecast means the
stock has a higher chance of increasing than decreasing, and a '-' means the opposite. The number of '+/-'
symbols is used to illustrate the magnitude of these probabilities. For example, '+++' means that the stock
has a significantly higher chance of increasing than decreasing, while '+' means that the stock only has a
slightly higher chance of increasing than decreasing.
<br />
<br />
Volatility represents the maximum percentage by which a stock's price can change every tick (a tick occurs
every few seconds while the game is running).
<br />
<br />A stock's price forecast can change over time. This is also affected by volatility. The more volatile a
stock is, the more its price forecast will change.
</Typography>
</StaticModal>
</>
);
}

@ -0,0 +1,43 @@
import React, { useState } from "react";
import { Modal } from "../../ui/React/Modal";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
interface IProps {
open: boolean;
onClose: () => void;
text: string;
placeText: string;
place: (price: number) => void;
}
export function PlaceOrderModal(props: IProps): React.ReactElement {
const [price, setPrice] = useState<number | null>(null);
function onClick(): void {
if (price === null) return;
if (isNaN(price)) return;
props.place(price);
props.onClose();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setPrice(null);
else setPrice(parseFloat(event.target.value));
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>{props.text}</Typography>
<TextField
autoFocus
type="number"
onChange={onChange}
placeholder="price"
InputProps={{
endAdornment: <Button onClick={onClick}>{props.placeText}</Button>,
}}
/>
</Modal>
);
}

@ -1,34 +0,0 @@
import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
text: string;
placeText: string;
place: (price: number) => void;
popupId: string;
}
export function PlaceOrderPopup(props: IProps): React.ReactElement {
const [price, setPrice] = useState<number | null>(null);
function onClick(): void {
if (price === null) return;
if (isNaN(price)) return;
props.place(price);
removePopup(props.popupId);
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setPrice(null);
else setPrice(parseFloat(event.target.value));
}
return (
<>
<p>{props.text}</p>
<input autoFocus={true} className="text-input" type="number" onChange={onChange} placeholder="price" />
<button className="std-button" onClick={onClick}>
{props.placeText}
</button>
</>
);
}

@ -47,7 +47,7 @@ export function StockMarketRoot(props: IProps): React.ReactElement {
return () => clearInterval(id);
}, []);
return (
<div className="stock-market-container">
<>
<InfoAndPurchases initStockMarket={props.initStockMarket} p={props.p} rerender={rerender} />
{props.p.hasWseAccount && (
<StockTickers
@ -62,6 +62,6 @@ export function StockMarketRoot(props: IProps): React.ReactElement {
stockMarket={props.stockMarket}
/>
)}
</div>
</>
);
}

@ -1,13 +1,13 @@
/**
* React Component for a single stock ticker in the Stock Market UI
*/
import * as React from "react";
import React, { useState } from "react";
import { StockTickerHeaderText } from "./StockTickerHeaderText";
import { StockTickerOrderList } from "./StockTickerOrderList";
import { StockTickerPositionText } from "./StockTickerPositionText";
import { StockTickerTxButton } from "./StockTickerTxButton";
import { PlaceOrderPopup } from "./PlaceOrderPopup";
import { PlaceOrderModal } from "./PlaceOrderModal";
import { Order } from "../Order";
import { Stock } from "../Stock";
@ -18,11 +18,20 @@ import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { numeralWrapper } from "../../ui/numeralFormat";
import { BBAccordion } from "../../ui/React/BBAccordion";
import { Money } from "../../ui/React/Money";
import { createPopup } from "../../ui/React/createPopup";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Paper from "@mui/material/Paper";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
enum SelectorOrderType {
Market = "Market Order",
@ -52,66 +61,55 @@ type IProps = {
stock: Stock;
};
type IState = {
orderType: SelectorOrderType;
position: PositionTypes;
qty: string;
};
export function StockTicker(props: IProps): React.ReactElement {
const [orderType, setOrderType] = useState(SelectorOrderType.Market);
const [position, setPosition] = useState(PositionTypes.Long);
const [qty, setQty] = useState("");
const [open, setOpen] = useState(false);
const [tickerOpen, setTicketOpen] = useState(false);
export class StockTicker extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
const [modalProps, setModalProps] = useState<{
text: string;
placeText: string;
place: (n: number) => boolean;
}>({
text: "",
placeText: "",
place: () => false,
});
this.state = {
orderType: SelectorOrderType.Market,
position: PositionTypes.Long,
qty: "",
};
this.getBuyTransactionCostContent = this.getBuyTransactionCostContent.bind(this);
this.getSellTransactionCostContent = this.getSellTransactionCostContent.bind(this);
this.handleBuyButtonClick = this.handleBuyButtonClick.bind(this);
this.handleBuyMaxButtonClick = this.handleBuyMaxButtonClick.bind(this);
this.handleHeaderClick = this.handleHeaderClick.bind(this);
this.handleOrderTypeChange = this.handleOrderTypeChange.bind(this);
this.handlePositionTypeChange = this.handlePositionTypeChange.bind(this);
this.handleQuantityChange = this.handleQuantityChange.bind(this);
this.handleSellButtonClick = this.handleSellButtonClick.bind(this);
this.handleSellAllButtonClick = this.handleSellAllButtonClick.bind(this);
}
getBuyTransactionCostContent(): JSX.Element | null {
const stock = this.props.stock;
const qty: number = this.getQuantity();
function getBuyTransactionCostContent(): JSX.Element | null {
const stock = props.stock;
const qty: number = getQuantity();
if (isNaN(qty)) {
return null;
}
const cost = getBuyTransactionCost(stock, qty, this.state.position);
const cost = getBuyTransactionCost(stock, qty, position);
if (cost == null) {
return null;
}
return (
<>
Purchasing {numeralWrapper.formatShares(qty)} shares (
{this.state.position === PositionTypes.Long ? "Long" : "Short"}) will cost <Money money={cost} />.
Purchasing {numeralWrapper.formatShares(qty)} shares ({position === PositionTypes.Long ? "Long" : "Short"}
) will cost <Money money={cost} />.
</>
);
}
getQuantity(): number {
return Math.round(parseFloat(this.state.qty));
function getQuantity(): number {
return Math.round(parseFloat(qty));
}
getSellTransactionCostContent(): JSX.Element | null {
const stock = this.props.stock;
const qty: number = this.getQuantity();
function getSellTransactionCostContent(): JSX.Element | null {
const stock = props.stock;
const qty: number = getQuantity();
if (isNaN(qty)) {
return null;
}
if (this.state.position === PositionTypes.Long) {
if (position === PositionTypes.Long) {
if (qty > stock.playerShares) {
return <>You do not have this many shares in the Long position</>;
}
@ -121,56 +119,51 @@ export class StockTicker extends React.Component<IProps, IState> {
}
}
const cost = getSellTransactionGain(stock, qty, this.state.position);
const cost = getSellTransactionGain(stock, qty, position);
if (cost == null) {
return null;
}
return (
<>
Selling {numeralWrapper.formatShares(qty)} shares (
{this.state.position === PositionTypes.Long ? "Long" : "Short"}) will result in a gain of <Money money={cost} />
.
Selling {numeralWrapper.formatShares(qty)} shares ({position === PositionTypes.Long ? "Long" : "Short"}) will
result in a gain of <Money money={cost} />.
</>
);
}
handleBuyButtonClick(): void {
const shares = this.getQuantity();
function handleBuyButtonClick(): void {
const shares = getQuantity();
if (isNaN(shares)) {
dialogBoxCreate(`Invalid input for quantity (number of shares): ${this.state.qty}`);
dialogBoxCreate(`Invalid input for quantity (number of shares): ${qty}`);
return;
}
switch (this.state.orderType) {
switch (orderType) {
case SelectorOrderType.Market: {
if (this.state.position === PositionTypes.Short) {
this.props.buyStockShort(this.props.stock, shares);
if (position === PositionTypes.Short) {
props.buyStockShort(props.stock, shares);
} else {
this.props.buyStockLong(this.props.stock, shares);
props.buyStockLong(props.stock, shares);
}
this.props.rerenderAllTickers();
props.rerenderAllTickers();
break;
}
case SelectorOrderType.Limit: {
const popupId = `place-order-popup`;
createPopup(popupId, PlaceOrderPopup, {
setOpen(true);
setModalProps({
text: "Enter the price for your Limit Order",
placeText: "Place Buy Limit Order",
place: (price: number) =>
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitBuy, this.state.position),
popupId: popupId,
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.LimitBuy, position),
});
break;
}
case SelectorOrderType.Stop: {
const popupId = `place-order-popup`;
createPopup(popupId, PlaceOrderPopup, {
setOpen(true);
setModalProps({
text: "Enter the price for your Stop Order",
placeText: "Place Buy Stop Order",
place: (price: number) =>
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopBuy, this.state.position),
popupId: popupId,
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.StopBuy, position),
});
break;
}
@ -179,21 +172,21 @@ export class StockTicker extends React.Component<IProps, IState> {
}
}
handleBuyMaxButtonClick(): void {
const playerMoney: number = this.props.p.money.toNumber();
function handleBuyMaxButtonClick(): void {
const playerMoney: number = props.p.money.toNumber();
const stock = this.props.stock;
let maxShares = calculateBuyMaxAmount(stock, this.state.position, playerMoney);
const stock = props.stock;
let maxShares = calculateBuyMaxAmount(stock, position, playerMoney);
maxShares = Math.min(maxShares, Math.round(stock.maxShares - stock.playerShares - stock.playerShortShares));
switch (this.state.orderType) {
switch (orderType) {
case SelectorOrderType.Market: {
if (this.state.position === PositionTypes.Short) {
this.props.buyStockShort(stock, maxShares);
if (position === PositionTypes.Short) {
props.buyStockShort(stock, maxShares);
} else {
this.props.buyStockLong(stock, maxShares);
props.buyStockLong(stock, maxShares);
}
this.props.rerenderAllTickers();
props.rerenderAllTickers();
break;
}
default: {
@ -203,98 +196,70 @@ export class StockTicker extends React.Component<IProps, IState> {
}
}
handleHeaderClick(e: React.MouseEvent<HTMLButtonElement>): void {
const elem = e.currentTarget;
elem.classList.toggle("active");
const panel: HTMLElement = elem.nextElementSibling as HTMLElement;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
}
handleOrderTypeChange(e: React.ChangeEvent<HTMLSelectElement>): void {
function handleOrderTypeChange(e: SelectChangeEvent<string>): void {
const val = e.target.value;
// The select value returns a string. Afaik TypeScript doesnt make it easy
// to convert that string back to an enum type so we'll just do this for now
switch (val) {
case SelectorOrderType.Limit:
this.setState({
orderType: SelectorOrderType.Limit,
});
setOrderType(SelectorOrderType.Limit);
break;
case SelectorOrderType.Stop:
this.setState({
orderType: SelectorOrderType.Stop,
});
setOrderType(SelectorOrderType.Stop);
break;
case SelectorOrderType.Market:
default:
this.setState({
orderType: SelectorOrderType.Market,
});
setOrderType(SelectorOrderType.Market);
}
}
handlePositionTypeChange(e: React.ChangeEvent<HTMLSelectElement>): void {
function handlePositionTypeChange(e: SelectChangeEvent<string>): void {
const val = e.target.value;
if (val === PositionTypes.Short) {
this.setState({
position: PositionTypes.Short,
});
setPosition(PositionTypes.Short);
} else {
this.setState({
position: PositionTypes.Long,
});
setPosition(PositionTypes.Long);
}
}
handleQuantityChange(e: React.ChangeEvent<HTMLInputElement>): void {
this.setState({
qty: e.target.value,
});
function handleQuantityChange(e: React.ChangeEvent<HTMLInputElement>): void {
setQty(e.target.value);
}
handleSellButtonClick(): void {
const shares = this.getQuantity();
function handleSellButtonClick(): void {
const shares = getQuantity();
if (isNaN(shares)) {
dialogBoxCreate(`Invalid input for quantity (number of shares): ${this.state.qty}`);
dialogBoxCreate(`Invalid input for quantity (number of shares): ${qty}`);
return;
}
switch (this.state.orderType) {
switch (orderType) {
case SelectorOrderType.Market: {
if (this.state.position === PositionTypes.Short) {
this.props.sellStockShort(this.props.stock, shares);
if (position === PositionTypes.Short) {
props.sellStockShort(props.stock, shares);
} else {
this.props.sellStockLong(this.props.stock, shares);
props.sellStockLong(props.stock, shares);
}
this.props.rerenderAllTickers();
props.rerenderAllTickers();
break;
}
case SelectorOrderType.Limit: {
const popupId = `place-order-popup`;
createPopup(popupId, PlaceOrderPopup, {
setOpen(true);
setModalProps({
text: "Enter the price for your Limit Order",
placeText: "Place Sell Limit Order",
place: (price: number) =>
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitSell, this.state.position),
popupId: popupId,
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.LimitSell, position),
});
break;
}
case SelectorOrderType.Stop: {
const popupId = `place-order-popup`;
createPopup(popupId, PlaceOrderPopup, {
setOpen(true);
setModalProps({
text: "Enter the price for your Stop Order",
placeText: "Place Sell Stop Order",
place: (price: number) =>
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopSell, this.state.position),
popupId: popupId,
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.StopSell, position),
});
break;
}
@ -303,17 +268,17 @@ export class StockTicker extends React.Component<IProps, IState> {
}
}
handleSellAllButtonClick(): void {
const stock = this.props.stock;
function handleSellAllButtonClick(): void {
const stock = props.stock;
switch (this.state.orderType) {
switch (orderType) {
case SelectorOrderType.Market: {
if (this.state.position === PositionTypes.Short) {
this.props.sellStockShort(stock, stock.playerShortShares);
if (position === PositionTypes.Short) {
props.sellStockShort(stock, stock.playerShortShares);
} else {
this.props.sellStockLong(stock, stock.playerShares);
props.sellStockLong(stock, stock.playerShares);
}
this.props.rerenderAllTickers();
props.rerenderAllTickers();
break;
}
default: {
@ -324,69 +289,56 @@ export class StockTicker extends React.Component<IProps, IState> {
}
// Whether the player has access to orders besides market orders (limit/stop)
hasOrderAccess(): boolean {
return this.props.p.bitNodeN === 8 || SourceFileFlags[8] >= 3;
function hasOrderAccess(): boolean {
return props.p.bitNodeN === 8 || SourceFileFlags[8] >= 3;
}
// Whether the player has access to shorting stocks
hasShortAccess(): boolean {
return this.props.p.bitNodeN === 8 || SourceFileFlags[8] >= 2;
function hasShortAccess(): boolean {
return props.p.bitNodeN === 8 || SourceFileFlags[8] >= 2;
}
render(): React.ReactNode {
return (
<li>
<BBAccordion
headerContent={<StockTickerHeaderText p={this.props.p} stock={this.props.stock} />}
panelContent={
<div>
<input
className="stock-market-input"
onChange={this.handleQuantityChange}
placeholder="Quantity (Shares)"
value={this.state.qty}
/>
<select
className="stock-market-input dropdown"
onChange={this.handlePositionTypeChange}
value={this.state.position}
>
<option value={PositionTypes.Long}>Long</option>
{this.hasShortAccess() && <option value={PositionTypes.Short}>Short</option>}
</select>
<select
className="stock-market-input dropdown"
onChange={this.handleOrderTypeChange}
value={this.state.orderType}
>
<option value={SelectorOrderType.Market}>{SelectorOrderType.Market}</option>
{this.hasOrderAccess() && <option value={SelectorOrderType.Limit}>{SelectorOrderType.Limit}</option>}
{this.hasOrderAccess() && <option value={SelectorOrderType.Stop}>{SelectorOrderType.Stop}</option>}
</select>
return (
<Box component={Paper}>
<ListItemButton onClick={() => setTicketOpen((old) => !old)}>
<ListItemText primary={<StockTickerHeaderText p={props.p} stock={props.stock} />} />
{tickerOpen ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={tickerOpen} unmountOnExit>
<Box sx={{ mx: 4 }}>
<Box display="flex" alignItems="center">
<TextField onChange={handleQuantityChange} placeholder="Quantity (Shares)" value={qty} />
<Select onChange={handlePositionTypeChange} value={position}>
<MenuItem value={PositionTypes.Long}>Long</MenuItem>
{hasShortAccess() && <MenuItem value={PositionTypes.Short}>Short</MenuItem>}
</Select>
<Select onChange={handleOrderTypeChange} value={orderType}>
<MenuItem value={SelectorOrderType.Market}>{SelectorOrderType.Market}</MenuItem>
{hasOrderAccess() && <MenuItem value={SelectorOrderType.Limit}>{SelectorOrderType.Limit}</MenuItem>}
{hasOrderAccess() && <MenuItem value={SelectorOrderType.Stop}>{SelectorOrderType.Stop}</MenuItem>}
</Select>
<StockTickerTxButton
onClick={this.handleBuyButtonClick}
text={"Buy"}
tooltip={this.getBuyTransactionCostContent()}
/>
<StockTickerTxButton
onClick={this.handleSellButtonClick}
text={"Sell"}
tooltip={this.getSellTransactionCostContent()}
/>
<StockTickerTxButton onClick={this.handleBuyMaxButtonClick} text={"Buy MAX"} />
<StockTickerTxButton onClick={this.handleSellAllButtonClick} text={"Sell ALL"} />
<StockTickerPositionText p={this.props.p} stock={this.props.stock} />
<StockTickerOrderList
cancelOrder={this.props.cancelOrder}
orders={this.props.orders}
p={this.props.p}
stock={this.props.stock}
/>
</div>
}
/>
</li>
);
}
<StockTickerTxButton onClick={handleBuyButtonClick} text={"Buy"} tooltip={getBuyTransactionCostContent()} />
<StockTickerTxButton
onClick={handleSellButtonClick}
text={"Sell"}
tooltip={getSellTransactionCostContent()}
/>
<StockTickerTxButton onClick={handleBuyMaxButtonClick} text={"Buy MAX"} />
<StockTickerTxButton onClick={handleSellAllButtonClick} text={"Sell ALL"} />
</Box>
<StockTickerPositionText p={props.p} stock={props.stock} />
<StockTickerOrderList cancelOrder={props.cancelOrder} orders={props.orders} p={props.p} stock={props.stock} />
<PlaceOrderModal
text={modalProps.text}
placeText={modalProps.placeText}
place={modalProps.place}
open={open}
onClose={() => setOpen(false)}
/>
</Box>
</Collapse>
</Box>
);
}

@ -11,6 +11,7 @@ import { TickerHeaderFormatData } from "../data/TickerHeaderFormatData";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat";
import Typography from "@mui/material/Typography";
type IProps = {
p: IPlayer;
@ -45,14 +46,16 @@ export function StockTickerHeaderText(props: IProps): React.ReactElement {
// hdrText += ` - ${stock.getAbsoluteForecast()} / ${stock.otlkMagForecast}`;
}
const styleMarkup = {
color: "#66ff33",
};
let color = "primary";
if (stock.lastPrice === stock.price) {
styleMarkup.color = "white";
color = "secondary";
} else if (stock.lastPrice > stock.price) {
styleMarkup.color = "red";
color = "error";
}
return <pre style={styleMarkup}>{hdrText}</pre>;
return (
<Typography style={{ whiteSpace: "pre" }} color={color}>
{hdrText}
</Typography>
);
}

Some files were not shown because too many files have changed in this diff Show More