diff --git a/src/CodingContract/CodingContractEventEmitter.ts b/src/CodingContract/CodingContractEventEmitter.ts new file mode 100644 index 000000000..1f1c8a230 --- /dev/null +++ b/src/CodingContract/CodingContractEventEmitter.ts @@ -0,0 +1,19 @@ +import { EventEmitter } from "../utils/EventEmitter"; +import type { CodingContract } from "./Contract"; + +export type CodingContractEventData = { + codingContract: CodingContract; + onClose: () => void; + onAttempt: (answer: string) => void; +}; + +type CodingContractEvent = + | { + type: "run"; + data: CodingContractEventData; + } + | { + type: "close"; + }; + +export const CodingContractEventEmitter = new EventEmitter<[CodingContractEvent]>(); diff --git a/src/CodingContract/Contract.ts b/src/CodingContract/Contract.ts index 3a17dc9fc..02e4f7a35 100644 --- a/src/CodingContract/Contract.ts +++ b/src/CodingContract/Contract.ts @@ -2,10 +2,10 @@ import { FactionName, CodingContractName } from "@enums"; import { CodingContractTypes } from "./ContractTypes"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver"; -import { CodingContractEvent } from "../ui/React/CodingContractModal"; import { ContractFilePath, resolveContractFilePath } from "../Paths/ContractFilePath"; import { assertObject } from "../utils/TypeAssertion"; import { Result } from "../types"; +import { CodingContractEventEmitter } from "./CodingContractEventEmitter"; // Numeric enum /** Enum representing the different types of rewards a Coding Contract can give */ @@ -150,13 +150,16 @@ export class CodingContract { /** Creates a popup to prompt the player to solve the problem */ async prompt(): Promise<{ result: CodingContractResult; message?: string }> { return new Promise((resolve) => { - CodingContractEvent.emit({ - c: this, - onClose: () => { - resolve({ result: CodingContractResult.Cancelled }); - }, - onAttempt: (val: string) => { - resolve(this.isSolution(val)); + CodingContractEventEmitter.emit({ + type: "run", + data: { + codingContract: this, + onClose: () => { + resolve({ result: CodingContractResult.Cancelled }); + }, + onAttempt: (val: string) => { + resolve(this.isSolution(val)); + }, }, }); }); diff --git a/src/Prestige.ts b/src/Prestige.ts index 9cf707bc9..c9bf4a01b 100644 --- a/src/Prestige.ts +++ b/src/Prestige.ts @@ -30,6 +30,7 @@ import { calculateExp } from "./PersonObjects/formulas/skill"; import { currentNodeMults } from "./BitNode/BitNodeMultipliers"; import { canAccessBitNodeFeature } from "./BitNode/BitNodeUtils"; import { pendingUIShareJobIds } from "./NetworkShare/Share"; +import { CodingContractEventEmitter } from "./CodingContract/CodingContractEventEmitter"; const BitNode8StartingMoney = 250e6; function delayedDialog(message: string, canBeDismissedEasily = true) { @@ -105,6 +106,9 @@ export function prestigeAugmentation(): void { Terminal.clear(); LogBoxClearEvents.emit(); + // Close coding contract modal + CodingContractEventEmitter.emit({ type: "close" }); + // Recalculate the bonus for circadian modulator aug initCircadianModulator(); @@ -209,6 +213,9 @@ export function prestigeSourceFile(isFlume: boolean): void { Terminal.clear(); LogBoxClearEvents.emit(); + // Close coding contract modal + CodingContractEventEmitter.emit({ type: "close" }); + // Delete all servers except home computer prestigeAllServers(); // Must be done before initForeignServers() diff --git a/src/ui/React/CodingContractModal.tsx b/src/ui/React/CodingContractModal.tsx index e61dd387a..0e7141b86 100644 --- a/src/ui/React/CodingContractModal.tsx +++ b/src/ui/React/CodingContractModal.tsx @@ -1,38 +1,50 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import { KEY } from "../../utils/KeyboardEventKey"; -import { CodingContract } from "../../CodingContract/Contract"; import { CodingContractTypes } from "../../CodingContract/ContractTypes"; import { CopyableText } from "./CopyableText"; import { Modal } from "./Modal"; -import { EventEmitter } from "../../utils/EventEmitter"; import Typography from "@mui/material/Typography"; import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; import { pluralize } from "../../utils/I18nUtils"; - -interface CodingContractProps { - c: CodingContract; - onClose: () => void; - onAttempt: (answer: string) => void; -} - -export const CodingContractEvent = new EventEmitter<[CodingContractProps]>(); +import { + type CodingContractEventData, + CodingContractEventEmitter, +} from "../../CodingContract/CodingContractEventEmitter"; export function CodingContractModal(): React.ReactElement { - const [contract, setContract] = useState(null); + const [eventData, setEventData] = useState(null); const [answer, setAnswer] = useState(""); - useEffect(() => { - return CodingContractEvent.subscribe((props) => setContract(props)); + const close = useCallback(() => { + setEventData((old) => { + old?.onClose(); + return null; + }); }, []); + + useEffect( + () => + CodingContractEventEmitter.subscribe((event) => { + switch (event.type) { + case "run": + setEventData(event.data); + break; + case "close": + close(); + break; + } + }), + [close], + ); useEffect(() => { return () => { - contract?.onClose(); + eventData?.onClose(); }; - }, [contract]); + }, [eventData]); - if (contract === null) { + if (eventData === null) { return <>; } @@ -41,30 +53,22 @@ export function CodingContractModal(): React.ReactElement { } function onKeyDown(event: React.KeyboardEvent): void { - if (contract === null) { + if (eventData === null) { return; } const value = event.currentTarget.value; if (event.key === KEY.ENTER && value !== "") { event.preventDefault(); - contract.onAttempt(answer); + eventData.onAttempt(answer); setAnswer(""); close(); } } - function close(): void { - if (contract === null) { - return; - } - contract.onClose(); - setContract(null); - } - - const contractType = CodingContractTypes[contract.c.type]; + const contractType = CodingContractTypes[eventData.codingContract.type]; const description = []; - for (const [i, value] of contractType.desc(contract.c.getData()).split("\n").entries()) { + for (const [i, value] of contractType.desc(eventData.codingContract.getData()).split("\n").entries()) { description.push( {value}
@@ -72,12 +76,12 @@ export function CodingContractModal(): React.ReactElement { ); } return ( - - + + You are attempting to solve a Coding Contract. You have{" "} - {pluralize(contract.c.getMaxNumTries() - contract.c.tries, "try", "tries")} remaining, after which the contract - will self-destruct. + {pluralize(eventData.codingContract.getMaxNumTries() - eventData.codingContract.tries, "try", "tries")}{" "} + remaining, after which the contract will self-destruct.
{description} @@ -96,7 +100,7 @@ export function CodingContractModal(): React.ReactElement { endAdornment: (