From 62cd8ffcc6f5815749800f3fd4e344a2da42d7e4 Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Fri, 1 Oct 2021 01:00:50 -0400 Subject: [PATCH] pre-dialogbox-convert --- package.json | 1 + src/Casino/Blackjack.tsx | 81 +-- src/Casino/CardDeck/ReactCard.tsx | 37 +- src/Casino/CoinFlip.tsx | 116 ++-- src/Casino/Game.tsx | 13 + src/Casino/Roulette.tsx | 586 +++++++++--------- src/Casino/SlotMachine.tsx | 228 +++---- src/Locations/ui/CasinoLocation.tsx | 95 +-- src/Locations/ui/City.tsx | 20 +- src/Locations/ui/CompanyLocation.tsx | 2 +- src/Locations/ui/GenericLocation.tsx | 2 +- src/Locations/ui/TechVendorLocation.tsx | 2 +- src/Locations/ui/TravelAgencyRoot.tsx | 3 +- src/NetscriptFunctions.ts | 4 +- .../Resleeving/ui/ResleeveElem.tsx | 239 +++---- .../Resleeving/ui/ResleeveRoot.tsx | 47 +- src/Terminal/commands/runScript.ts | 4 +- src/Terminal/commands/tail.ts | 8 +- src/engine.tsx | 18 +- .../ActiveScripts/WorkerScriptAccordion.tsx | 6 +- src/ui/GameRoot.tsx | 6 +- src/ui/React/AlertManager.tsx | 50 ++ src/ui/React/LogBox.tsx | 134 ---- src/ui/React/LogBoxManager.tsx | 161 +++++ src/ui/React/WorldMap.tsx | 4 +- webpack.config.js | 5 +- 26 files changed, 989 insertions(+), 883 deletions(-) create mode 100644 src/ui/React/AlertManager.tsx delete mode 100644 src/ui/React/LogBox.tsx create mode 100644 src/ui/React/LogBoxManager.tsx diff --git a/package.json b/package.json index 28c86f604..a7a3d5464 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/Casino/Blackjack.tsx b/src/Casino/Blackjack.tsx index 03112e74c..fb55a8320 100644 --- a/src/Casino/Blackjack.tsx +++ b/src/Casino/Blackjack.tsx @@ -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; @@ -308,7 +310,7 @@ export class Blackjack extends Game {
{/* Wager input */}
- @@ -322,12 +324,15 @@ export class Blackjack extends Game { error={wagerInvalid} helperText={wagerInvalid ? wagerInvalidHelperText : ""} type="number" - variant="filled" style={{ width: "200px", }} InputProps={{ - startAdornment: $, + startAdornment: ( + + $ + + ), }} /> @@ -340,16 +345,16 @@ export class Blackjack extends Game { {/* Buttons */} {!gameInProgress ? (
- +
) : (
- Hit - + +
)} @@ -357,36 +362,40 @@ export class Blackjack extends Game { * the cards that led to that result. */} {(gameInProgress || result !== Result.Pending) && (
- -
Player
- {playerHand.cards.map((card, i) => ( - - ))} + + +
Player
+ {playerHand.cards.map((card, i) => ( + + ))} -
Value(s): 
- {playerHandValues.map((value, i) => ( -
{value}
- ))} -
+
Value(s): 
+ {playerHandValues.map((value, i) => ( +
{value}
+ ))} + +
- -
Dealer
- {dealerHand.cards.map((card, i) => ( - // Hide every card except the first while game is in progress -
+ {!gameInProgress && ( + <> +
Value(s): 
+ {dealerHandValues.map((value, i) => ( +
{value}
+ ))} + + )} + +
)} diff --git a/src/Casino/CardDeck/ReactCard.tsx b/src/Casino/CardDeck/ReactCard.tsx index eb5437cb9..de40783d9 100644 --- a/src/Casino/CardDeck/ReactCard.tsx +++ b/src/Casino/CardDeck/ReactCard.tsx @@ -1,12 +1,43 @@ import React, { FC } from "react"; import { Card, Suit } from "./Card"; +import { Theme } from "@mui/material"; +import makeStyles from "@mui/styles/makeStyles"; +import createStyles from "@mui/styles/createStyles"; 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 = ({ card, hidden }) => { + const classes = useStyles(); let suit: React.ReactNode; switch (card.suit) { case Suit.Clubs: @@ -25,10 +56,10 @@ export const ReactCard: FC = ({ card, hidden }) => { throw new Error(`MissingCaseException: ${card.suit}`); } return ( -
+
<> -
{hidden ? " - " : card.formatValue()}
-
{hidden ? " - " : suit}
+
{hidden ? " - " : card.formatValue()}
+
{hidden ? " - " : suit}
); diff --git a/src/Casino/CoinFlip.tsx b/src/Casino/CoinFlip.tsx index ac31463d0..3676d971b 100644 --- a/src/Casino/CoinFlip.tsx +++ b/src/Casino/CoinFlip.tsx @@ -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 { - constructor(props: IProps) { - super(props); +export function CoinFlip(props: IProps): React.ReactElement { + const [investment, setInvestment] = useState(1000); + const [result, setResult] = useState( ); + const [status, setStatus] = useState(""); + const [playLock, setPlayLock] = useState(false); - this.state = { - investment: 1000, - result: , - status: "", - playLock: false, - }; - - this.play = this.play.bind(this); - this.updateInvestment = this.updateInvestment.bind(this); - } - - updateInvestment(e: React.FormEvent): void { + function updateInvestment(e: React.ChangeEvent): void { let investment: number = parseInt(e.currentTarget.value); if (isNaN(investment)) { investment = minPlay; @@ -51,11 +39,11 @@ export class CoinFlip extends Game { 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,51 @@ export class CoinFlip extends Game { letter = "T"; } const correct: boolean = guess === letter; - this.setState({ - result: {letter}, - status: correct ? " win!" : "lose!", - playLock: true, - }); - setTimeout(() => this.setState({ playLock: false }), 250); + + setResult({letter}); + 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 ( - <> -
{`+———————+`}
-
{`| |   | |`}
-
-          {`| | `}
-          {this.state.result}
-          {` | |`}
-        
-
{`| |   | |`}
-
{`+———————+`}
- Play for: - -
- this.play("H"))} text={"Head!"} disabled={this.state.playLock} /> - this.play("T"))} text={"Tail!"} disabled={this.state.playLock} /> -

{this.state.status}

- - ); - } + return ( + <> + {`+———————+`} + {`| | | |`} + + {`| | `} + {result} + {` | |`} + + {`| | | |`} + {`+———————+`} + + + + + + ), + }} + /> + + + {status} + + ); } diff --git a/src/Casino/Game.tsx b/src/Casino/Game.tsx index 404356241..4e5e19aa5 100644 --- a/src/Casino/Game.tsx +++ b/src/Casino/Game.tsx @@ -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 extends React.Component { win(p: IPlayer, n: number): void { p.gainMoney(n); diff --git a/src/Casino/Roulette.tsx b/src/Casino/Roulette.tsx index 29ab02310..e9964d5f7 100644 --- a/src/Casino/Roulette.tsx +++ b/src/Casino/Roulette.tsx @@ -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 { - 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("waiting"); + const [n, setN] = useState(0); + const [lock, setLock] = useState(true); + const [strategy, setStrategy] = useState({ + 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): void { + function updateInvestment(e: React.ChangeEvent): void { let investment: number = parseInt(e.currentTarget.value); if (isNaN(investment)) { investment = minPlay; @@ -170,265 +147,312 @@ export class Roulette extends Game { 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 ); } else { - gain = -this.state.investment; + gain = -investment; status = ( <> lost ); } - 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 ( - <> -

{this.currentNumber()}

- -

{this.state.status}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- this.play(Single(3)))} /> - - this.play(Single(6)))} /> - - this.play(Single(9)))} /> - - this.play(Single(12)))} /> - - this.play(Single(15)))} /> - - this.play(Single(18)))} /> - - this.play(Single(21)))} /> - - this.play(Single(24)))} /> - - this.play(Single(27)))} /> - - this.play(Single(30)))} /> - - this.play(Single(33)))} /> - - this.play(Single(36)))} /> -
- this.play(Single(2)))} /> - - this.play(Single(5)))} /> - - this.play(Single(8)))} /> - - this.play(Single(11)))} /> - - this.play(Single(14)))} /> - - this.play(Single(17)))} /> - - this.play(Single(20)))} /> - - this.play(Single(23)))} /> - - this.play(Single(26)))} /> - - this.play(Single(29)))} /> - - this.play(Single(32)))} /> - - this.play(Single(35)))} /> -
- this.play(Single(1)))} /> - - this.play(Single(4)))} /> - - this.play(Single(7)))} /> - - this.play(Single(10)))} /> - - this.play(Single(13)))} /> - - this.play(Single(16)))} /> - - this.play(Single(19)))} /> - - this.play(Single(22)))} /> - - this.play(Single(25)))} /> - - this.play(Single(28)))} /> - - this.play(Single(31)))} /> - - this.play(Single(34)))} /> -
- this.play(strategies.Third1))} - /> - - this.play(strategies.Third2))} - /> - - this.play(strategies.Third3))} - /> -
- this.play(strategies.Red))} - /> - - this.play(strategies.Black))} - /> - - this.play(strategies.Odd))} - /> - - this.play(strategies.Even))} - /> - - this.play(strategies.High))} - /> - - this.play(strategies.Low))} - /> -
- this.play(Single(0)))} /> -
- - ); - } + return ( + <> + {currentNumber()} + + {status} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + ); } diff --git a/src/Casino/SlotMachine.tsx b/src/Casino/SlotMachine.tsx index d2dd79ff8..ba807927e 100644 --- a/src/Casino/SlotMachine.tsx +++ b/src/Casino/SlotMachine.tsx @@ -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 { - rng: WHRNG; - interval = -1; +export function SlotMachine(props: IProps): React.ReactElement { + const [rng] = useState(new WHRNG(props.p.totalPlaytime)); + const [index, setIndex] = useState([0, 0, 0, 0, 0]); + const [locks, setLocks] = useState([0, 0, 0, 0, 0]); + const [investment, setInvestment] = useState(1000); + const [canPlay, setCanPlay] = useState(true); + const [status, setStatus] = useState("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 { 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"} - - ), - canPlay: true, - }); - if (this.reachedLimit(this.props.p)) return; + setStatus( + <> + {gains > 0 ? "gained" : "lost"} + , + ); + 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): void { + function updateInvestment(e: React.ChangeEvent): void { let investment: number = parseInt(e.currentTarget.value); if (isNaN(investment)) { investment = minPlay; @@ -302,53 +272,49 @@ export class SlotMachine extends Game { 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 ( <> -
+———————————————————————+
-
| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][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]} | |
-
+———————————————————————+
- +———————————————————————+ +| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | | +| | | | | | | | +| | {symbols[index[0]]} | {symbols[index[1]]} | {symbols[index[2]]} | {symbols[index[3]]} | {symbols[index[4]]} | | +| | | | | | | | +| | {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]} | | ++———————————————————————+ + Spin!)}} /> - -

{this.state.status}

-

Pay lines

+ + {status} + Pay lines -
-----   ·····   ·····
-
·····   -----   ·····
-
·····   ·····   -----
+----- ····· ····· +····· ----- ····· +····· ····· -----
-
··^··   \···/   \···/
-
·/·\·   ·\·/·   ·---·
-
/···\   ··v··   ·····
+··^·· \···/ \···/ +·/·\· ·\·/· ·---· +/···\ ··v·· ·····
-
·····   ·---·   ·····
-
·---·   /···\   \···/
-
/···\   ·····   ·---·
+····· ·---· ····· +·---· /···\ \···/ +/···\ ····· ·---· ); - } } // https://felgo.com/doc/how-to-make-a-slot-game-tutorial/ diff --git a/src/Locations/ui/CasinoLocation.tsx b/src/Locations/ui/CasinoLocation.tsx index 326a9f3ff..78e73b1be 100644 --- a/src/Locations/ui/CasinoLocation.tsx +++ b/src/Locations/ui/CasinoLocation.tsx @@ -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 { - 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 ( - <> - -
- -
- -
- - - ); - } - - renderGame(): React.ReactNode { - let elem = null; - switch (this.state.game) { - case GameType.Coin: - elem = ; - break; - case GameType.Slots: - elem = ; - break; - case GameType.Roulette: - elem = ; - break; - case GameType.Blackjack: - elem = ; - break; - case GameType.None: - break; - default: - throw new Error(`MissingCaseException: ${this.state.game}`); - } - - return ( - <> - 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 && ( + <> + +
+ +
+ +
+ + + )} + {game !== GameType.None && ( + <> + + {game === GameType.Coin && } + {game === GameType.Slots && } + {game === GameType.Roulette && } + {game === GameType.Blackjack && } + + )} + + ); } diff --git a/src/Locations/ui/City.tsx b/src/Locations/ui/City.tsx index 0c3ce8b08..89f1b3356 100644 --- a/src/Locations/ui/City.tsx +++ b/src/Locations/ui/City.tsx @@ -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; @@ -111,29 +112,30 @@ function ASCIICity(props: IProps): React.ReactElement { elems.push(
{lineElems(lines[i])}
); } - return
{elems}
; + return <>{elems}; } function ListCity(props: IProps): React.ReactElement { const router = use.Router(); const locationButtons = props.city.locations.map((locName) => { return ( -
  • - toLocation(router, Locations[locName])} text={locName} /> -
  • + + +
    +
    ); }); - return
      {locationButtons}
    ; + return <>{locationButtons}; } export function LocationCity(): React.ReactElement { const player = use.Player(); const city = Cities[player.city]; return ( -
    -

    {city.name}

    + <> + {city.name} {Settings.DisableASCIIArt ? : } -
    + ); } diff --git a/src/Locations/ui/CompanyLocation.tsx b/src/Locations/ui/CompanyLocation.tsx index ef6ccccf9..85db125c3 100644 --- a/src/Locations/ui/CompanyLocation.tsx +++ b/src/Locations/ui/CompanyLocation.tsx @@ -212,7 +212,7 @@ export function CompanyLocation(props: IProps): React.ReactElement { } > - Company Favor: {Favor(company.favor)} + Company Favor: {Favor(company.favor)} ------------------------- diff --git a/src/Locations/ui/GenericLocation.tsx b/src/Locations/ui/GenericLocation.tsx index 05e627c76..41677a9bd 100644 --- a/src/Locations/ui/GenericLocation.tsx +++ b/src/Locations/ui/GenericLocation.tsx @@ -90,7 +90,7 @@ export function GenericLocation({ loc }: IProps): React.ReactElement { return ( <> - + {backdoorInstalled && !Settings.DisableTextEffects ? : loc.name} {locContent} diff --git a/src/Locations/ui/TechVendorLocation.tsx b/src/Locations/ui/TechVendorLocation.tsx index 8359767c2..6aada005b 100644 --- a/src/Locations/ui/TechVendorLocation.tsx +++ b/src/Locations/ui/TechVendorLocation.tsx @@ -70,7 +70,7 @@ export function TechVendorLocation(props: IProps): React.ReactElement {
    {purchaseServerButtons}
    - + "You can order bigger servers via scripts. We don't take custom order in person."
    diff --git a/src/Locations/ui/TravelAgencyRoot.tsx b/src/Locations/ui/TravelAgencyRoot.tsx index de50df1a1..e7c949cc1 100644 --- a/src/Locations/ui/TravelAgencyRoot.tsx +++ b/src/Locations/ui/TravelAgencyRoot.tsx @@ -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(You are now in {to}!); + dialogBoxCreate(<>You are now in {to}!); router.toCity(); } diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index aa6a3fa71..f9755b191 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -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")); diff --git a/src/PersonObjects/Resleeving/ui/ResleeveElem.tsx b/src/PersonObjects/Resleeving/ui/ResleeveElem.tsx index aaac275e3..4f2f0b7e6 100644 --- a/src/PersonObjects/Resleeving/ui/ResleeveElem.tsx +++ b/src/PersonObjects/Resleeving/ui/ResleeveElem.tsx @@ -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( <> -

    - Total Multipliers: -

    - Hacking Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_mult)} -
    - Hacking Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_exp_mult)} -
    - Strength Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_mult)} -
    - Strength Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_exp_mult)} -
    - Defense Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_mult)} -
    - Defense Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_exp_mult)} -
    - Dexterity Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_mult)} -
    - Dexterity Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_exp_mult)} -
    - Agility Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_mult)} -
    - Agility Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_exp_mult)} -
    - Charisma Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_mult)} -
    - Charisma Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_exp_mult)} -
    - Hacking Chance multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_chance_mult)} -
    - Hacking Speed multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_speed_mult)} -
    - Hacking Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_money_mult)} -
    - Hacking Growth multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_grow_mult)} -
    - Salary multiplier: {numeralWrapper.formatPercentage(props.resleeve.work_money_mult)} -
    - Company Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.company_rep_mult)} -
    - Faction Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.faction_rep_mult)} -
    - Crime Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_money_mult)} -
    - Crime Success multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_success_mult)} -
    - Hacknet Income multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_money_mult)} -
    - Hacknet Purchase Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_purchase_cost_mult)} -
    - Hacknet Level Upgrade Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_level_cost_mult)} -
    - Hacknet Ram Upgrade Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_ram_cost_mult)} -
    - Hacknet Core Upgrade Cost multiplier: - {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_core_cost_mult)} -
    - Bladeburner Max Stamina multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_max_stamina_mult)} -
    - Bladeburner Stamina Gain multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_stamina_gain_mult)} -
    - Bladeburner Field Analysis multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_analysis_mult)} -
    - Bladeburner Success Chance multiplier: - {numeralWrapper.formatPercentage(props.resleeve.bladeburner_success_chance_mult)} + + Total Multipliers: + + + Hacking Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_mult)} +
    + Hacking Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_exp_mult)} +
    + Strength Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_mult)} +
    + Strength Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.strength_exp_mult)} +
    + Defense Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_mult)} +
    + Defense Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.defense_exp_mult)} +
    + Dexterity Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_mult)} +
    + Dexterity Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.dexterity_exp_mult)} +
    + Agility Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_mult)} +
    + Agility Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.agility_exp_mult)} +
    + Charisma Level multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_mult)} +
    + Charisma Experience multiplier: {numeralWrapper.formatPercentage(props.resleeve.charisma_exp_mult)} +
    + Hacking Chance multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_chance_mult)} +
    + Hacking Speed multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_speed_mult)} +
    + Hacking Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_money_mult)} +
    + Hacking Growth multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacking_grow_mult)} +
    + Salary multiplier: {numeralWrapper.formatPercentage(props.resleeve.work_money_mult)} +
    + Company Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.company_rep_mult)} +
    + Faction Reputation Gain multiplier: {numeralWrapper.formatPercentage(props.resleeve.faction_rep_mult)} +
    + Crime Money multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_money_mult)} +
    + Crime Success multiplier: {numeralWrapper.formatPercentage(props.resleeve.crime_success_mult)} +
    + Hacknet Income multiplier: {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_money_mult)} +
    + Hacknet Purchase Cost multiplier: + {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_purchase_cost_mult)} +
    + Hacknet Level Upgrade Cost multiplier: + {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_level_cost_mult)} +
    + Hacknet Ram Upgrade Cost multiplier: + {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_ram_cost_mult)} +
    + Hacknet Core Upgrade Cost multiplier: + {numeralWrapper.formatPercentage(props.resleeve.hacknet_node_core_cost_mult)} +
    + Bladeburner Max Stamina multiplier: + {numeralWrapper.formatPercentage(props.resleeve.bladeburner_max_stamina_mult)} +
    + Bladeburner Stamina Gain multiplier: + {numeralWrapper.formatPercentage(props.resleeve.bladeburner_stamina_gain_mult)} +
    + Bladeburner Field Analysis multiplier: + {numeralWrapper.formatPercentage(props.resleeve.bladeburner_analysis_mult)} +
    + Bladeburner Success Chance multiplier: + {numeralWrapper.formatPercentage(props.resleeve.bladeburner_success_chance_mult)} +
    , ); } - function onAugChange(event: React.ChangeEvent): void { + function onAugChange(event: SelectChangeEvent): void { setAug(event.target.value); } @@ -110,50 +119,48 @@ export function ResleeveElem(props: IProps): React.ReactElement { } return ( -
    -
    -

    - Hacking: {numeralWrapper.formatSkill(props.resleeve.hacking_skill)} ( - {numeralWrapper.formatExp(props.resleeve.hacking_exp)} exp) -
    - Strength: {numeralWrapper.formatSkill(props.resleeve.strength)} ( - {numeralWrapper.formatExp(props.resleeve.strength_exp)} exp) -
    - Defense: {numeralWrapper.formatSkill(props.resleeve.defense)} ( - {numeralWrapper.formatExp(props.resleeve.defense_exp)} exp) -
    - Dexterity: {numeralWrapper.formatSkill(props.resleeve.dexterity)} ( - {numeralWrapper.formatExp(props.resleeve.dexterity_exp)} exp) -
    - Agility: {numeralWrapper.formatSkill(props.resleeve.agility)} ( - {numeralWrapper.formatExp(props.resleeve.agility_exp)} exp) -
    - Charisma: {numeralWrapper.formatSkill(props.resleeve.charisma)} ( - {numeralWrapper.formatExp(props.resleeve.charisma_exp)} exp) -
    # Augmentations: {props.resleeve.augmentations.length} -

    - -
    -
    - -

    {currentAug !== undefined && currentAug.info}

    -
    -
    -

    - It costs to purchase this Sleeve. -

    - -
    -
    + + + + + Hacking: {numeralWrapper.formatSkill(props.resleeve.hacking_skill)} ( + {numeralWrapper.formatExp(props.resleeve.hacking_exp)} exp) +
    + Strength: {numeralWrapper.formatSkill(props.resleeve.strength)} ( + {numeralWrapper.formatExp(props.resleeve.strength_exp)} exp) +
    + Defense: {numeralWrapper.formatSkill(props.resleeve.defense)} ( + {numeralWrapper.formatExp(props.resleeve.defense_exp)} exp) +
    + Dexterity: {numeralWrapper.formatSkill(props.resleeve.dexterity)} ( + {numeralWrapper.formatExp(props.resleeve.dexterity_exp)} exp) +
    + Agility: {numeralWrapper.formatSkill(props.resleeve.agility)} ( + {numeralWrapper.formatExp(props.resleeve.agility_exp)} exp) +
    + Charisma: {numeralWrapper.formatSkill(props.resleeve.charisma)} ( + {numeralWrapper.formatExp(props.resleeve.charisma_exp)} exp) +
    # Augmentations: {props.resleeve.augmentations.length} +
    + +
    + + + {currentAug !== undefined && currentAug.info} + + + + It costs to purchase this Sleeve. + + + +
    +
    ); } diff --git a/src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx b/src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx index 0c17b70a9..7fb47048e 100644 --- a/src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx +++ b/src/PersonObjects/Resleeving/ui/ResleeveRoot.tsx @@ -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): void { + function onSortChange(event: SelectChangeEvent): 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 ( <> -

    + 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 {

    NOTE: The stats and multipliers displayed on this page do NOT include your bonuses from Source-File. -

    -

    Sort By:

    - - {props.player.resleeves.map((resleeve, i) => ( - +
    + + Sort By: + + + {player.resleeves.map((resleeve, i) => ( + ))} ); diff --git a/src/Terminal/commands/runScript.ts b/src/Terminal/commands/runScript.ts index dce0d11c1..cd3956458 100644 --- a/src/Terminal/commands/runScript.ts +++ b/src/Terminal/commands/runScript.ts @@ -2,7 +2,7 @@ import { ITerminal } from "../ITerminal"; import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { BaseServer } from "../../Server/BaseServer"; -import { logBoxCreate } from "../../ui/React/LogBox"; +import { LogBoxEvents } from "../../ui/React/LogBoxManager"; import { startWorkerScript } from "../../NetscriptWorker"; import { RunningScript } from "../../Script/RunningScript"; import { findRunningScript } from "../../Script/ScriptHelpers"; @@ -84,7 +84,7 @@ export function runScript( `Running script with ${numThreads} thread(s), pid ${runningScript.pid} and args: ${JSON.stringify(args)}.`, ); if (tailFlag) { - logBoxCreate(runningScript); + LogBoxEvents.emit(runningScript); } return; } diff --git a/src/Terminal/commands/tail.ts b/src/Terminal/commands/tail.ts index 34169be6d..0f959a64b 100644 --- a/src/Terminal/commands/tail.ts +++ b/src/Terminal/commands/tail.ts @@ -2,10 +2,10 @@ import { ITerminal } from "../ITerminal"; import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { BaseServer } from "../../Server/BaseServer"; -import { logBoxCreate } from "../../ui/React/LogBox"; import { findRunningScriptByPid } from "../../Script/ScriptHelpers"; import { isScriptFilename } from "../../Script/isScriptFilename"; import { compareArrays } from "../../utils/helpers/compareArrays"; +import { LogBoxEvents } from "../../ui/React/LogBoxManager"; export function tail( terminal: ITerminal, @@ -34,7 +34,7 @@ export function tail( // match, use it! for (let i = 0; i < server.runningScripts.length; ++i) { if (server.runningScripts[i].filename === scriptName && compareArrays(server.runningScripts[i].args, args)) { - logBoxCreate(server.runningScripts[i]); + LogBoxEvents.emit(server.runningScripts[i]); return; } } @@ -53,7 +53,7 @@ export function tail( // If there's only 1 possible choice, use that. if (candidates.length === 1) { - logBoxCreate(candidates[0]); + LogBoxEvents.emit(candidates[0]); return; } @@ -73,7 +73,7 @@ export function tail( terminal.error("No such script exists"); return; } - logBoxCreate(runningScript); + LogBoxEvents.emit(runningScript); } } catch (e) { terminal.error(e + ""); diff --git a/src/engine.tsx b/src/engine.tsx index 7923809d4..8d5cb65cf 100644 --- a/src/engine.tsx +++ b/src/engine.tsx @@ -42,7 +42,7 @@ import { Money } from "./ui/React/Money"; import { Hashes } from "./ui/React/Hashes"; import { Reputation } from "./ui/React/Reputation"; -import { dialogBoxCreate } from "./ui/React/DialogBox"; +import { AlertEvents } from "./ui/React/AlertManager"; import { exceptionAlert } from "./utils/helpers/exceptionAlert"; import { startTampering } from "./Exploits/tampering"; @@ -399,12 +399,16 @@ const Engine: { Player.lastUpdate = Engine._lastUpdate; Engine.start(); // Run main game loop and Scripts loop const timeOfflineString = convertTimeMsToTimeElapsedString(time); - dialogBoxCreate( - <> - Offline for {timeOfflineString}. While you were offline, your scripts generated{" "} - , your Hacknet Nodes generated {hacknetProdInfo} and you gained{" "} - {Reputation(offlineReputation)} divided amongst your factions. - , + setTimeout( + () => + AlertEvents.emit( + <> + Offline for {timeOfflineString}. While you were offline, your scripts generated{" "} + , your Hacknet Nodes generated {hacknetProdInfo} and you gained{" "} + {Reputation(offlineReputation)} divided amongst your factions. + , + ), + 250, ); } else { // No save found, start new game diff --git a/src/ui/ActiveScripts/WorkerScriptAccordion.tsx b/src/ui/ActiveScripts/WorkerScriptAccordion.tsx index 117510b35..189c90676 100644 --- a/src/ui/ActiveScripts/WorkerScriptAccordion.tsx +++ b/src/ui/ActiveScripts/WorkerScriptAccordion.tsx @@ -28,7 +28,7 @@ import { killWorkerScript } from "../../Netscript/killWorkerScript"; import { WorkerScript } from "../../Netscript/WorkerScript"; import { dialogBoxCreate } from "../React/DialogBox"; -import { logBoxCreate } from "../React/LogBox"; +import { LogBoxEvents } from "../React/LogBoxManager"; import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; import { arrayToString } from "../../utils/helpers/arrayToString"; import { Money } from "../React/Money"; @@ -50,7 +50,9 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement { const workerScript = props.workerScript; const scriptRef = workerScript.scriptRef; - const logClickHandler = logBoxCreate.bind(null, scriptRef); + function logClickHandler(): void { + LogBoxEvents.emit(scriptRef); + } const killScript = killWorkerScript.bind(null, scriptRef as any, scriptRef.server); function killScriptClickHandler(): void { diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 1da5def41..07c0b1636 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -67,6 +67,8 @@ import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic"; import { workerScripts } from "../Netscript/WorkerScripts"; import { Unclickable } from "../Exploits/Unclickable"; import { Snackbar } from "./React/Snackbar"; +import { LogBoxManager } from "./React/LogBoxManager"; +import { AlertManager } from "./React/AlertManager"; import { enterBitNode } from "../RedPill"; import { Context } from "./Context"; @@ -334,7 +336,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme ) : page === Page.Bladeburner ? ( ) : page === Page.Resleeves ? ( - + ) : page === Page.Travel ? ( ) : page === Page.StockMarket ? ( @@ -394,6 +396,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme )} + + ); diff --git a/src/ui/React/AlertManager.tsx b/src/ui/React/AlertManager.tsx new file mode 100644 index 000000000..a75a9c15b --- /dev/null +++ b/src/ui/React/AlertManager.tsx @@ -0,0 +1,50 @@ +import React, { useState, useEffect } from "react"; +import { EventEmitter } from "../../utils/EventEmitter"; +import { Modal } from "../../ui/React/Modal"; +import Typography from "@mui/material/Typography"; + +export const AlertEvents = new EventEmitter<[string | JSX.Element]>(); + +interface Alert { + id: string; + text: string | JSX.Element; +} + +let i = 0; +export function AlertManager(): React.ReactElement { + const [alerts, setAlerts] = useState([]); + useEffect( + () => + AlertEvents.subscribe((text: string | JSX.Element) => { + const id = i + ""; + i++; + setAlerts((old) => { + return [ + ...old, + { + id: id, + text: text, + }, + ]; + }); + }), + [], + ); + + function close(): void { + console.log("close"); + setAlerts((old) => { + return old.slice(0, -1); + }); + } + + return ( + <> + {alerts.length > 0 && ( + + {alerts[0].text} + + )} + + ); +} diff --git a/src/ui/React/LogBox.tsx b/src/ui/React/LogBox.tsx deleted file mode 100644 index 3c467d316..000000000 --- a/src/ui/React/LogBox.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, { useState, useEffect } from "react"; -import ReactDOM from "react-dom"; -import { killWorkerScript } from "../../Netscript/killWorkerScript"; -import { RunningScript } from "../../Script/RunningScript"; - -import { createElement } from "../uiHelpers/createElement"; -import { removeElementById } from "../uiHelpers/removeElementById"; - -let gameContainer: HTMLElement; - -(function () { - function getGameContainer(): void { - const container = document.getElementById("root"); - if (container == null) { - throw new Error(`Failed to find game container DOM element`); - } - - gameContainer = container; - document.removeEventListener("DOMContentLoaded", getGameContainer); - } - - document.addEventListener("DOMContentLoaded", getGameContainer); -})(); - -interface IProps { - script: RunningScript; - container: HTMLElement; - id: string; -} - -function ScriptLogPopup(props: IProps): React.ReactElement { - const setRerender = useState(false)[1]; - - function rerender(): void { - setRerender((old) => !old); - } - - useEffect(() => { - const id = setInterval(rerender, 1000); - return () => clearInterval(id); - }, []); - - function close(): void { - const content = document.getElementById(props.id); - if (content == null) return; - ReactDOM.unmountComponentAtNode(content); - removeElementById(props.id); - } - - useEffect(() => { - function closeHandler(event: KeyboardEvent): void { - if (event.keyCode === 27) { - close(); - } - } - - document.addEventListener("keydown", closeHandler); - - return () => { - document.removeEventListener("keydown", closeHandler); - }; - }, []); - - function kill(): void { - killWorkerScript(props.script, props.script.server, true); - close(); - } - - function drag(event: React.MouseEvent): void { - event.preventDefault(); - let x = event.clientX; - let y = event.clientY; - let left = props.container.offsetLeft + props.container.clientWidth / 2; - let top = props.container.offsetTop + props.container.clientWidth / 5; - function mouseMove(event: MouseEvent): void { - left += event.clientX - x; - top += event.clientY - y; - props.container.style.left = left + "px"; - props.container.style.top = top + "px"; - // reset right and bottom to avoid the window stretching - props.container.style.right = ""; - props.container.style.bottom = ""; - x = event.clientX; - y = event.clientY; - } - function mouseUp(): void { - document.removeEventListener("mouseup", mouseUp); - document.removeEventListener("mousemove", mouseMove); - } - document.addEventListener("mouseup", mouseUp); - document.addEventListener("mousemove", mouseMove); - } - - return ( - <> -
    -

    - {props.script.filename} {props.script.args.map((x: any): string => `${x}`).join(" ")} -

    -
    - - -
    -
    -
    -

    - {props.script.logs.map( - (line: string, i: number): JSX.Element => ( - - {line} -
    -
    - ), - )} -

    -
    - - ); -} - -export function logBoxCreate(script: RunningScript): void { - const id = script.server + "-" + script.filename + script.args.map((x: any): string => `${x}`).join("-"); - if (document.getElementById(id) !== null) return; - const container = createElement("div", { - class: "log-box-container", - id: id, - }); - gameContainer.appendChild(container); - ReactDOM.render(, container); -} diff --git a/src/ui/React/LogBoxManager.tsx b/src/ui/React/LogBoxManager.tsx new file mode 100644 index 000000000..6772f2409 --- /dev/null +++ b/src/ui/React/LogBoxManager.tsx @@ -0,0 +1,161 @@ +import React, { useState, useEffect, useRef } from "react"; +import { EventEmitter } from "../../utils/EventEmitter"; +import { RunningScript } from "../../Script/RunningScript"; +import { killWorkerScript } from "../../Netscript/killWorkerScript"; +import Typography from "@mui/material/Typography"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Paper from "@mui/material/Paper"; + +export const LogBoxEvents = new EventEmitter<[RunningScript]>(); + +interface Log { + id: string; + script: RunningScript; +} + +export function LogBoxManager(): React.ReactElement { + const [logs, setLogs] = useState([]); + useEffect( + () => + LogBoxEvents.subscribe((script: RunningScript) => { + const id = script.server + "-" + script.filename + script.args.map((x: any): string => `${x}`).join("-"); + setLogs((old) => { + return [ + ...old, + { + id: id, + script: script, + }, + ]; + }); + }), + [], + ); + + function close(id: string): void { + setLogs((old) => old.filter((l) => l.id !== id)); + } + + return ( + <> + {logs.map((log) => ( + close(log.id)} /> + ))} + + ); +} + +interface IProps { + script: RunningScript; + id: string; + onClose: () => void; +} + +function LogWindow(props: IProps): React.ReactElement { + const container = useRef(null); + const setRerender = useState(false)[1]; + function rerender(): void { + setRerender((old) => !old); + } + + useEffect(() => { + const id = setInterval(rerender, 1000); + return () => clearInterval(id); + }, []); + + useEffect(() => { + function closeHandler(event: KeyboardEvent): void { + if (event.keyCode === 27) { + props.onClose(); + } + } + + document.addEventListener("keydown", closeHandler); + + return () => { + document.removeEventListener("keydown", closeHandler); + }; + }, []); + + function kill(): void { + killWorkerScript(props.script, props.script.server, true); + props.onClose(); + } + + function drag(event: React.MouseEvent): void { + const c = container.current; + if (c === null) return; + event.preventDefault(); + let x = event.clientX; + let y = event.clientY; + let left = c.offsetLeft + c.clientWidth / 2; + let top = c.offsetTop + c.clientWidth / 5; + function mouseMove(event: MouseEvent): void { + const c = container.current; + if (c === null) return; + left += event.clientX - x; + top += event.clientY - y; + c.style.left = left + "px"; + c.style.top = top + "px"; + // reset right and bottom to avoid the window stretching + c.style.right = ""; + c.style.bottom = ""; + x = event.clientX; + y = event.clientY; + } + function mouseUp(): void { + document.removeEventListener("mouseup", mouseUp); + document.removeEventListener("mousemove", mouseMove); + } + document.addEventListener("mouseup", mouseUp); + document.addEventListener("mousemove", mouseMove); + } + + return ( + + + + + {props.script.filename} {props.script.args.map((x: any): string => `${x}`).join(" ")} + + + + + + + + + + {props.script.logs.map( + (line: string, i: number): JSX.Element => ( + + {line} +
    +
    + ), + )} +
    +
    + ); +} diff --git a/src/ui/React/WorldMap.tsx b/src/ui/React/WorldMap.tsx index 9dff41ee6..fb9d6b0be 100644 --- a/src/ui/React/WorldMap.tsx +++ b/src/ui/React/WorldMap.tsx @@ -33,7 +33,7 @@ interface IProps { export function WorldMap(props: IProps): React.ReactElement { // prettier-ignore return ( -
    + <> ,_ . ._. _. . , _-\','|~\~ ~/ ;-'_ _-' ,;_;_, ~~- /~~-\_/-'~'--' \~~| ', ,' / / ~|-_\_/~/~ ~~--~~~~'--_ @@ -56,6 +56,6 @@ export function WorldMap(props: IProps): React.ReactElement { / ,' ~ ',| ~ ~' -
    + ); } diff --git a/webpack.config.js b/webpack.config.js index ef16393e6..775f1bd78 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,6 +12,7 @@ module.exports = (env, argv) => { const isDevServer = (env || {}).devServer === true; const runInContainer = (env || {}).runInContainer === true; const isDevelopment = argv.mode === "development"; + const isFastRefresh = argv.fast === "true"; const outputDirectory = isDevServer ? "dist-dev" : "dist"; const entry = "./src/index.tsx"; @@ -130,7 +131,7 @@ module.exports = (env, argv) => { columns: true, module: true, }), - isDevelopment && new ReactRefreshWebpackPlugin(), + isFastRefresh && new ReactRefreshWebpackPlugin(), new DeadCodePlugin({ patterns: ["src/**/*.(js|jsx|css|ts|tsx)"], exclude: ["**/*.(stories|spec).(js|jsx)"], @@ -150,7 +151,7 @@ module.exports = (env, argv) => { use: { loader: "babel-loader", options: { - plugins: [isDevelopment && require.resolve("react-refresh/babel")].filter(Boolean), + plugins: [isFastRefresh && require.resolve("react-refresh/babel")].filter(Boolean), cacheDirectory: true, }, },