mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-27 18:13:52 +01:00
fix research tree, kinda
This commit is contained in:
parent
826357e8b8
commit
fae6e6d22f
@ -8,8 +8,7 @@ import { IndustryUpgrades } from "../IndustryUpgrades";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { MakeProductModal } from "./MakeProductModal";
|
||||
import { ResearchPopup } from "./ResearchPopup";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { ResearchModal } from "./ResearchModal";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { MoneyRate } from "../../ui/React/MoneyRate";
|
||||
import { StatsTable } from "../../ui/React/StatsTable";
|
||||
@ -96,6 +95,7 @@ function Text(): React.ReactElement {
|
||||
const corp = useCorporation();
|
||||
const division = useDivision();
|
||||
const [helpOpen, setHelpOpen] = useState(false);
|
||||
const [researchOpen, setResearchOpen] = useState(false);
|
||||
const vechain = corp.unlockUpgrades[4] === 1;
|
||||
const profit = division.lastCycleRevenue.minus(division.lastCycleExpenses).toNumber();
|
||||
|
||||
@ -116,14 +116,6 @@ function Text(): React.ReactElement {
|
||||
});
|
||||
}
|
||||
|
||||
function openResearchPopup(): void {
|
||||
const popupId = "corporation-research-popup-box";
|
||||
createPopup(popupId, ResearchPopup, {
|
||||
industry: division,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Typography>
|
||||
@ -214,9 +206,10 @@ function Text(): React.ReactElement {
|
||||
>
|
||||
<Typography>Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000a")}</Typography>
|
||||
</Tooltip>
|
||||
<Button sx={{ mx: 1 }} onClick={openResearchPopup}>
|
||||
<Button sx={{ mx: 1 }} onClick={() => setResearchOpen(true)}>
|
||||
Research
|
||||
</Button>
|
||||
<ResearchModal open={researchOpen} onClose={() => setResearchOpen(false)} industry={division} />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
122
src/Corporation/ui/ResearchModal.tsx
Normal file
122
src/Corporation/ui/ResearchModal.tsx
Normal file
@ -0,0 +1,122 @@
|
||||
import React, { useState } from "react";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { IndustryResearchTrees } from "../IndustryData";
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
import { IIndustry } from "../IIndustry";
|
||||
import { Research } from "../Actions";
|
||||
import { Node } from "../ResearchTree";
|
||||
import { ResearchMap } from "../ResearchMap";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Button from "@mui/material/Button";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
interface INodeProps {
|
||||
n: Node | null;
|
||||
division: IIndustry;
|
||||
}
|
||||
function Upgrade({ n, division }: INodeProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(false);
|
||||
if (n === null) return <></>;
|
||||
const r = ResearchMap[n.text];
|
||||
let disabled = division.sciResearch.qty < r.cost;
|
||||
const parent = n.parent;
|
||||
if (parent !== null) {
|
||||
disabled = disabled || !parent.researched;
|
||||
}
|
||||
|
||||
function research(): void {
|
||||
if (n === null || disabled) return;
|
||||
try {
|
||||
Research(division, n.text);
|
||||
} catch (err) {
|
||||
dialogBoxCreate(err + "");
|
||||
return;
|
||||
}
|
||||
|
||||
dialogBoxCreate(
|
||||
`Researched ${n.text}. It may take a market cycle ` +
|
||||
`(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
|
||||
`the Research apply.`,
|
||||
);
|
||||
}
|
||||
|
||||
const but = (
|
||||
<Box>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Research points: {r.cost}
|
||||
<br />
|
||||
{r.desc}
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<Button disabled={disabled} onClick={research}>
|
||||
{n.text}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
);
|
||||
|
||||
if (n.children.length === 0) return but;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box display="flex">
|
||||
{but}
|
||||
<ListItemButton onClick={() => setOpen((old) => !old)}>
|
||||
<ListItemText />
|
||||
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
|
||||
</ListItemButton>
|
||||
</Box>
|
||||
<Collapse in={open} unmountOnExit>
|
||||
<Box m={4}>
|
||||
{n.children.map((m) => (
|
||||
<Upgrade key={m.text} division={division} n={m} />
|
||||
))}
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
industry: IIndustry;
|
||||
}
|
||||
|
||||
// Create the Research Tree UI for this Industry
|
||||
export function ResearchModal(props: IProps): React.ReactElement {
|
||||
const researchTree = IndustryResearchTrees[props.industry.type];
|
||||
if (researchTree === undefined) return <></>;
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Upgrade division={props.industry} n={researchTree.root} />
|
||||
<Typography>
|
||||
Research points: {props.industry.sciResearch.qty}
|
||||
<br />
|
||||
Multipliers from research:
|
||||
<br />* Advertising Multiplier: x{researchTree.getAdvertisingMultiplier()}
|
||||
<br />* Employee Charisma Multiplier: x{researchTree.getEmployeeChaMultiplier()}
|
||||
<br />* Employee Creativity Multiplier: x{researchTree.getEmployeeCreMultiplier()}
|
||||
<br />* Employee Efficiency Multiplier: x{researchTree.getEmployeeEffMultiplier()}
|
||||
<br />* Employee Intelligence Multiplier: x{researchTree.getEmployeeIntMultiplier()}
|
||||
<br />* Production Multiplier: x{researchTree.getProductionMultiplier()}
|
||||
<br />* Sales Multiplier: x{researchTree.getSalesMultiplier()}
|
||||
<br />* Scientific Research Multiplier: x{researchTree.getScientificResearchMultiplier()}
|
||||
<br />* Storage Multiplier: x{researchTree.getStorageMultiplier()}
|
||||
</Typography>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { IndustryResearchTrees } from "../IndustryData";
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
import { Treant } from "treant-js";
|
||||
import { IIndustry } from "../IIndustry";
|
||||
import { Research } from "../Actions";
|
||||
|
||||
interface IProps {
|
||||
industry: IIndustry;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
// Create the Research Tree UI for this Industry
|
||||
export function ResearchPopup(props: IProps): React.ReactElement {
|
||||
const researchTree = IndustryResearchTrees[props.industry.type];
|
||||
if (researchTree === undefined) return <></>;
|
||||
useEffect(() => {
|
||||
{
|
||||
const boxContent = document.getElementById(`${props.popupId}-content`);
|
||||
if (boxContent != null) {
|
||||
boxContent.style.minHeight = "80vh";
|
||||
}
|
||||
}
|
||||
|
||||
// Get the tree's markup (i.e. config) for Treant
|
||||
const markup = researchTree.createTreantMarkup();
|
||||
markup.chart.container = "#" + props.popupId + "-content";
|
||||
markup.chart.nodeAlign = "BOTTOM";
|
||||
markup.chart.rootOrientation = "WEST";
|
||||
markup.chart.siblingSeparation = 40;
|
||||
markup.chart.connectors = {
|
||||
type: "step",
|
||||
style: {
|
||||
"arrow-end": "block-wide-long",
|
||||
stroke: "white",
|
||||
"stroke-width": 2,
|
||||
},
|
||||
};
|
||||
|
||||
Treant(markup);
|
||||
|
||||
// Add Event Listeners for all Nodes
|
||||
const allResearch = researchTree.getAllNodes();
|
||||
for (let i = 0; i < allResearch.length; ++i) {
|
||||
// If this is already Researched, skip it
|
||||
if (props.industry.researched[allResearch[i]] === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the DOM Element to add a click listener to it
|
||||
const sanitizedName = allResearch[i].replace(/\s/g, "");
|
||||
const div = document.getElementById(sanitizedName + "-corp-research-click-listener");
|
||||
if (div == null) {
|
||||
console.warn(`Could not find Research Tree div for ${sanitizedName}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
div.addEventListener("click", () => {
|
||||
try {
|
||||
Research(props.industry, allResearch[i]);
|
||||
} catch (err) {
|
||||
dialogBoxCreate(err + "");
|
||||
return;
|
||||
}
|
||||
|
||||
dialogBoxCreate(
|
||||
`Researched ${allResearch[i]}. It may take a market cycle ` +
|
||||
`(~${CorporationConstants.SecsPerMarketCycle} seconds) before the effects of ` +
|
||||
`the Research apply.`,
|
||||
);
|
||||
removePopup(props.popupId);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div id={props.popupId}>
|
||||
<div>
|
||||
Research points: {props.industry.sciResearch.qty}
|
||||
<br />
|
||||
Multipliers from research:
|
||||
<br />* Advertising Multiplier: x{researchTree.getAdvertisingMultiplier()}
|
||||
<br />* Employee Charisma Multiplier: x{researchTree.getEmployeeChaMultiplier()}
|
||||
<br />* Employee Creativity Multiplier: x{researchTree.getEmployeeCreMultiplier()}
|
||||
<br />* Employee Efficiency Multiplier: x{researchTree.getEmployeeEffMultiplier()}
|
||||
<br />* Employee Intelligence Multiplier: x{researchTree.getEmployeeIntMultiplier()}
|
||||
<br />* Production Multiplier: x{researchTree.getProductionMultiplier()}
|
||||
<br />* Sales Multiplier: x{researchTree.getSalesMultiplier()}
|
||||
<br />* Scientific Research Multiplier: x{researchTree.getScientificResearchMultiplier()}
|
||||
<br />* Storage Multiplier: x{researchTree.getStorageMultiplier()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -150,10 +150,6 @@ import { LogBoxEvents } from "./ui/React/LogBoxManager";
|
||||
import { arrayToString } from "./utils/helpers/arrayToString";
|
||||
import { isString } from "./utils/helpers/isString";
|
||||
|
||||
import { createElement } from "./ui/uiHelpers/createElement";
|
||||
import { createPopup } from "./ui/uiHelpers/createPopup";
|
||||
import { removeElementById } from "./ui/uiHelpers/removeElementById";
|
||||
|
||||
import { OfficeSpace } from "./Corporation/OfficeSpace";
|
||||
import { Employee } from "./Corporation/Employee";
|
||||
import { Product } from "./Corporation/Product";
|
||||
|
4887
src/ThirdParty/raphael.min.js
vendored
4887
src/ThirdParty/raphael.min.js
vendored
File diff suppressed because it is too large
Load Diff
1
src/ThirdParty/treant-js.d.ts
vendored
1
src/ThirdParty/treant-js.d.ts
vendored
@ -1 +0,0 @@
|
||||
declare module "treant-js";
|
@ -53,5 +53,4 @@
|
||||
<body>
|
||||
<div id="root" />
|
||||
</body>
|
||||
<script src="src/ThirdParty/raphael.min.js"></script>
|
||||
</html>
|
||||
|
@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Wrapper around material-ui's TextField component that styles it with
|
||||
* Bitburner's UI theme
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { TextField, TextFieldProps } from "@mui/material";
|
||||
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
|
||||
const backgroundColorStyles = {
|
||||
backgroundColor: "rgba(57, 54, 54, 0.9)",
|
||||
"&:hover": {
|
||||
backgroundColor: "rgba(70, 70, 70, 0.9)",
|
||||
},
|
||||
};
|
||||
|
||||
const formControlStyles = {
|
||||
border: "1px solid #e2e2e1",
|
||||
overflow: "hidden",
|
||||
borderRadius: 4,
|
||||
color: "white",
|
||||
...backgroundColorStyles,
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
...formControlStyles,
|
||||
},
|
||||
});
|
||||
|
||||
const useInputStyles = makeStyles({
|
||||
root: {
|
||||
...backgroundColorStyles,
|
||||
color: "white",
|
||||
},
|
||||
focused: {
|
||||
backgroundColor: "rgba(70, 70, 70, 0.9)",
|
||||
},
|
||||
disabled: {
|
||||
color: "white",
|
||||
},
|
||||
});
|
||||
|
||||
const useLabelStyles = makeStyles({
|
||||
root: {
|
||||
color: "white",
|
||||
},
|
||||
focused: {
|
||||
color: "white !important", // Need important to override styles from FormLabel
|
||||
},
|
||||
disabled: {
|
||||
color: "white !important", // Need important to override styles from FormLabel
|
||||
},
|
||||
});
|
||||
|
||||
export const MuiTextField: React.FC<TextFieldProps> = (props: TextFieldProps) => {
|
||||
return (
|
||||
<TextField
|
||||
{...props}
|
||||
classes={{
|
||||
...useStyles(),
|
||||
...props.classes,
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
...useInputStyles(),
|
||||
...props.InputProps?.classes,
|
||||
},
|
||||
...props.InputProps,
|
||||
}}
|
||||
InputLabelProps={{
|
||||
classes: {
|
||||
...useLabelStyles(),
|
||||
...props.InputLabelProps?.classes,
|
||||
},
|
||||
...props.InputLabelProps,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Wrapper around material-ui's Button component that styles it with
|
||||
* Bitburner's UI theme
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Select as MuiSelect, SelectProps } from "@mui/material";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
color: theme.palette.primary.dark,
|
||||
margin: "5px",
|
||||
padding: "3px 5px",
|
||||
"&:after": {
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
},
|
||||
|
||||
borderRadius: 0,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export const Select: React.FC<SelectProps> = (props: SelectProps) => {
|
||||
return (
|
||||
<MuiSelect
|
||||
{...props}
|
||||
classes={{
|
||||
...useStyles(),
|
||||
...props.classes,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* Create a pop-up dialog box using React.
|
||||
*
|
||||
* Calling this function with the same ID and React Root Component will trigger a re-render
|
||||
*
|
||||
* @param id The (hopefully) unique identifier for the popup container
|
||||
* @param rootComponent Root React Component for the content (NOT the popup containers themselves)
|
||||
*/
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
import { Popup } from "./Popup";
|
||||
|
||||
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);
|
||||
})();
|
||||
|
||||
// This variable is used to avoid setting the semi-transparent background
|
||||
// several times on top of one another. Sometimes there's several popup at once.
|
||||
let deepestPopupId = "";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function createPopup<T>(
|
||||
id: string,
|
||||
rootComponent: (props: T) => React.ReactElement,
|
||||
props: T,
|
||||
onClose?: () => void,
|
||||
): HTMLElement | null {
|
||||
let container = document.getElementById(id);
|
||||
if (container == null) {
|
||||
function onClick(this: HTMLElement, event: MouseEvent): void {
|
||||
if (!event.srcElement) return;
|
||||
if (!(event.srcElement instanceof HTMLElement)) return;
|
||||
const clickedId = (event.srcElement as HTMLElement).id;
|
||||
if (clickedId !== id) return;
|
||||
removePopup(id);
|
||||
if (onClose) onClose();
|
||||
}
|
||||
const backgroundColor = deepestPopupId === "" ? "rgba(0,0,0,0.5)" : "rgba(0,0,0,0)";
|
||||
container = createElement("div", {
|
||||
class: "popup-box-container",
|
||||
display: "flex",
|
||||
id: id,
|
||||
backgroundColor: backgroundColor,
|
||||
mouseDown: onClick,
|
||||
});
|
||||
|
||||
gameContainer.appendChild(container);
|
||||
}
|
||||
|
||||
if (deepestPopupId === "") deepestPopupId = id;
|
||||
ReactDOM.render(
|
||||
<Popup
|
||||
content={rootComponent}
|
||||
id={id}
|
||||
props={props}
|
||||
removePopup={() => {
|
||||
removePopup(id);
|
||||
if (onClose) onClose();
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a popup created with the createPopup() function above
|
||||
*/
|
||||
export function removePopup(id: string): void {
|
||||
const content = document.getElementById(`${id}`);
|
||||
if (content == null) return;
|
||||
|
||||
ReactDOM.unmountComponentAtNode(content);
|
||||
|
||||
removeElementById(id);
|
||||
removeElementById(`${id}-close`);
|
||||
if (id === deepestPopupId) deepestPopupId = "";
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import { setTimeoutRef } from "../utils/SetTimeoutRef";
|
||||
import { getElementById } from "./uiHelpers/getElementById";
|
||||
import { Action } from "../types";
|
||||
|
||||
const threeSeconds = 3000;
|
||||
let x: number | undefined;
|
||||
|
||||
/**
|
||||
* Displays a status message to the player for approximately 3 seconds.
|
||||
* @param text The status text to display
|
||||
*/
|
||||
export function createStatusText(text: string): void {
|
||||
const statusElement: HTMLElement = getElementById("status-text");
|
||||
const handler: Action = () => {
|
||||
statusElement.innerText = "";
|
||||
statusElement.style.display = "none";
|
||||
statusElement.classList.remove("status-text");
|
||||
};
|
||||
|
||||
if (x !== undefined) {
|
||||
clearTimeout(x);
|
||||
// Likely not needed due to clearTimeout, but just in case...
|
||||
x = undefined;
|
||||
// reset the element's animation
|
||||
statusElement.style.animation = "none";
|
||||
setTimeout(function () {
|
||||
statusElement.style.animation = "";
|
||||
}, 10);
|
||||
}
|
||||
|
||||
statusElement.style.display = "block";
|
||||
statusElement.classList.add("status-text");
|
||||
statusElement.innerText = text;
|
||||
|
||||
x = setTimeoutRef(handler, threeSeconds);
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/**
|
||||
* The full-screen page the player is currently be on.
|
||||
* These pages are mutually exclusive.
|
||||
*/
|
||||
export enum Page {
|
||||
/**
|
||||
* (Default) The terminal is where the player issues all commands, executes scripts, etc.
|
||||
*/
|
||||
Terminal = "Terminal",
|
||||
|
||||
/**
|
||||
* Displays most of the statistics about the player.
|
||||
*/
|
||||
CharacterInfo = "CharacterInfo",
|
||||
|
||||
/**
|
||||
* The console for editing Netscript files.
|
||||
*/
|
||||
ScriptEditor = "ScriptEditor",
|
||||
|
||||
/**
|
||||
* Monitor the scripts currently executing across the servers.
|
||||
*/
|
||||
ActiveScripts = "ActiveScripts",
|
||||
|
||||
/**
|
||||
* View, purchase, and upgrade Hacknet nodes.
|
||||
*/
|
||||
HacknetNodes = "HacknetNodes",
|
||||
|
||||
/**
|
||||
* The list of programs the player could potentially build.
|
||||
*/
|
||||
CreateProgram = "CreateProgram",
|
||||
|
||||
/**
|
||||
* The list of all factions, and invites, available to the player.
|
||||
*/
|
||||
Factions = "Factions",
|
||||
|
||||
/**
|
||||
* Information about a specific faction.
|
||||
*/
|
||||
Faction = "Faction",
|
||||
|
||||
/**
|
||||
* The list of installed, and yet-to-be installed, augmentations the player has purchased.
|
||||
*/
|
||||
Augmentations = "Augmentations",
|
||||
|
||||
/**
|
||||
* List of milestones that players should follow.
|
||||
*/
|
||||
Milestones = "Milestones",
|
||||
|
||||
/**
|
||||
* A collection of in-game material to learn about the game.
|
||||
*/
|
||||
Tutorial = "Tutorial",
|
||||
|
||||
/**
|
||||
* A collection of items to manipulate the state of the game. Useful for development.
|
||||
*/
|
||||
DevMenu = "Dev Menu",
|
||||
|
||||
/**
|
||||
* Visiting a location in the world
|
||||
*/
|
||||
Location = "Location",
|
||||
|
||||
/**
|
||||
* A blocking page to show the player they are currently doing some action (building a program, working, etc.).
|
||||
*/
|
||||
workInProgress = "WorkInProgress",
|
||||
|
||||
/**
|
||||
* A special screen to show the player they've reached a certain point in the game.
|
||||
*/
|
||||
RedPill = "RedPill",
|
||||
|
||||
/**
|
||||
* A special screen to show the player they've reached a certain point in the game.
|
||||
*/
|
||||
CinematicText = "CinematicText",
|
||||
|
||||
/**
|
||||
* Mini-game to infiltrate a company, gaining experience from successful progress.
|
||||
*/
|
||||
Infiltration = "Infiltration",
|
||||
|
||||
/**
|
||||
* View the in-game stock market.
|
||||
*/
|
||||
StockMarket = "StockMarket",
|
||||
|
||||
/**
|
||||
* Manage gang actions and members.
|
||||
*/
|
||||
Gang = "Gang",
|
||||
|
||||
/**
|
||||
* Perform missions for a Faction.
|
||||
*/
|
||||
Mission = "Mission",
|
||||
|
||||
/**
|
||||
* Manage a corporation.
|
||||
*/
|
||||
Corporation = "Corporation",
|
||||
|
||||
/**
|
||||
* Manage special Bladeburner activities.
|
||||
*/
|
||||
Bladeburner = "Bladeburner",
|
||||
|
||||
/**
|
||||
* Manage your Sleeves
|
||||
*/
|
||||
Sleeves = "Sleeves",
|
||||
|
||||
/**
|
||||
* Purchase Resleeves
|
||||
*/
|
||||
Resleeves = "Re-sleeving",
|
||||
|
||||
/**
|
||||
* Game options
|
||||
*/
|
||||
GameOptions = "GameOptions",
|
||||
}
|
||||
|
||||
/**
|
||||
* This class keeps track of player navigation/routing within the game.
|
||||
*/
|
||||
class Routing {
|
||||
/**
|
||||
* Tracking the what page the user is currently on.
|
||||
*/
|
||||
private currentPage: Page | null = null;
|
||||
|
||||
/**
|
||||
* Determines if the player is currently on the specified page.
|
||||
* @param page The page to compare against the current state.
|
||||
*/
|
||||
isOn(page: Page): boolean {
|
||||
return this.currentPage === page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes the player to the appropriate page.
|
||||
* @param page The page to navigate to.
|
||||
*/
|
||||
navigateTo(page: Page): void {
|
||||
this.currentPage = page;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The routing instance for tracking page navigation.
|
||||
*/
|
||||
export const routing: Routing = new Routing();
|
@ -1,29 +0,0 @@
|
||||
import { getElementById } from "./getElementById";
|
||||
|
||||
/**
|
||||
* Given an element by its ID, removes all event listeners from that element by cloning and
|
||||
* replacing. Then returns the new cloned element.
|
||||
* @param elemId The HTML ID to retrieve the element by.
|
||||
*/
|
||||
export function clearEventListeners(elemId: string | HTMLElement): HTMLElement | null {
|
||||
try {
|
||||
let elem: HTMLElement;
|
||||
if (typeof elemId === "string") {
|
||||
elem = getElementById(elemId);
|
||||
} else {
|
||||
elem = elemId;
|
||||
}
|
||||
|
||||
const newElem: HTMLElement = elem.cloneNode(true) as HTMLElement;
|
||||
if (elem.parentNode !== null) {
|
||||
elem.parentNode.replaceChild(newElem, elem);
|
||||
}
|
||||
|
||||
return newElem;
|
||||
} catch (e) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.error(e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,308 +0,0 @@
|
||||
/**
|
||||
* Options specific to creating an anchor ("<a>") element.
|
||||
*/
|
||||
interface ICreateElementAnchorOptions {
|
||||
href?: string;
|
||||
target?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options specific to creating an input ("<input>") element.
|
||||
*/
|
||||
interface ICreateElementInputOptions {
|
||||
checked?: boolean;
|
||||
max?: string;
|
||||
maxLength?: number;
|
||||
min?: string;
|
||||
name?: string;
|
||||
pattern?: string;
|
||||
placeholder?: string;
|
||||
step?: string;
|
||||
type?: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options specific to creating a label ("<label>") element.
|
||||
*/
|
||||
interface ICreateElementLabelOptions {
|
||||
for?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for setting up event listeners on the element.
|
||||
*/
|
||||
interface ICreateElementListenerOptions {
|
||||
changeListener?(this: HTMLElement, ev: Event): any;
|
||||
clickListener?(this: HTMLElement, ev: MouseEvent): any;
|
||||
mouseDown?(this: HTMLElement, ev: MouseEvent): any;
|
||||
inputListener?(this: HTMLElement, ev: Event): any;
|
||||
onfocus?(this: HTMLElement, ev: FocusEvent): any;
|
||||
onkeydown?(this: HTMLElement, ev: KeyboardEvent): any;
|
||||
onkeyup?(this: HTMLElement, ev: KeyboardEvent): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for setting up the inline-styling of element.
|
||||
* NOTE: Relying on CSS styling should be preferred over forcing the higher specificity via inline styles.
|
||||
*/
|
||||
interface ICreateElementStyleOptions {
|
||||
backgroundColor?: string;
|
||||
border?: string;
|
||||
color?: string;
|
||||
display?: string;
|
||||
float?: string;
|
||||
fontSize?: string;
|
||||
margin?: string;
|
||||
marginLeft?: string;
|
||||
marginTop?: string;
|
||||
overflow?: string;
|
||||
padding?: string;
|
||||
position?: string;
|
||||
visibility?: string;
|
||||
whiteSpace?: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
top?: string;
|
||||
left?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for adding an in-game tooltip to the element.
|
||||
*/
|
||||
interface ICreateElementTooltipOptions {
|
||||
tooltip?: string;
|
||||
tooltipleft?: string;
|
||||
tooltipsmall?: string;
|
||||
tooltiplow?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* All possible configuration options when creating an element.
|
||||
*/
|
||||
interface ICreateElementOptions
|
||||
extends ICreateElementStyleOptions,
|
||||
ICreateElementListenerOptions,
|
||||
ICreateElementInputOptions,
|
||||
ICreateElementAnchorOptions,
|
||||
ICreateElementLabelOptions,
|
||||
ICreateElementTooltipOptions {
|
||||
/**
|
||||
* CSS Class(es) to initially set.
|
||||
*/
|
||||
class?: string;
|
||||
|
||||
/**
|
||||
* A (hopefully) unique identifier for the element.
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
innerHTML?: string;
|
||||
innerText?: string;
|
||||
tabIndex?: number;
|
||||
}
|
||||
|
||||
function setElementAnchor(el: HTMLAnchorElement, params: ICreateElementAnchorOptions): void {
|
||||
if (params.text !== undefined) {
|
||||
el.text = params.text;
|
||||
}
|
||||
if (params.href !== undefined) {
|
||||
el.href = params.href;
|
||||
}
|
||||
if (params.target !== undefined) {
|
||||
el.target = params.target;
|
||||
}
|
||||
}
|
||||
|
||||
function setElementInput(el: HTMLInputElement, params: ICreateElementInputOptions): void {
|
||||
if (params.name !== undefined) {
|
||||
el.name = params.name;
|
||||
}
|
||||
if (params.value !== undefined) {
|
||||
el.value = params.value;
|
||||
}
|
||||
if (params.type !== undefined) {
|
||||
el.type = params.type;
|
||||
}
|
||||
if (params.checked !== undefined) {
|
||||
el.checked = params.checked;
|
||||
}
|
||||
if (params.pattern !== undefined) {
|
||||
el.pattern = params.pattern;
|
||||
}
|
||||
if (params.maxLength !== undefined) {
|
||||
el.maxLength = params.maxLength;
|
||||
}
|
||||
if (params.placeholder !== undefined) {
|
||||
el.placeholder = params.placeholder;
|
||||
}
|
||||
if (params.max !== undefined) {
|
||||
el.max = params.max;
|
||||
}
|
||||
if (params.min !== undefined) {
|
||||
el.min = params.min;
|
||||
}
|
||||
if (params.step !== undefined) {
|
||||
el.step = params.step;
|
||||
}
|
||||
}
|
||||
|
||||
function setElementLabel(el: HTMLLabelElement, params: ICreateElementLabelOptions): void {
|
||||
if (params.for !== undefined) {
|
||||
el.htmlFor = params.for;
|
||||
}
|
||||
}
|
||||
|
||||
function setElementListeners(el: HTMLElement, params: ICreateElementListenerOptions): void {
|
||||
// tslint:disable:no-unbound-method
|
||||
if (params.clickListener !== undefined) {
|
||||
el.addEventListener("click", params.clickListener);
|
||||
}
|
||||
if (params.mouseDown !== undefined) {
|
||||
el.addEventListener("mousedown", params.mouseDown);
|
||||
}
|
||||
if (params.inputListener !== undefined) {
|
||||
el.addEventListener("input", params.inputListener);
|
||||
}
|
||||
if (params.changeListener !== undefined) {
|
||||
el.addEventListener("change", params.changeListener);
|
||||
}
|
||||
if (params.onkeyup !== undefined) {
|
||||
el.addEventListener("keyup", params.onkeyup);
|
||||
}
|
||||
if (params.onkeydown !== undefined) {
|
||||
el.addEventListener("keydown", params.onkeydown);
|
||||
}
|
||||
if (params.onfocus !== undefined) {
|
||||
el.addEventListener("focus", params.onfocus);
|
||||
}
|
||||
// tslint:enable:no-unbound-method
|
||||
}
|
||||
|
||||
function setElementStyle(el: HTMLElement, params: ICreateElementStyleOptions): void {
|
||||
if (params.display !== undefined) {
|
||||
el.style.display = params.display;
|
||||
}
|
||||
if (params.visibility !== undefined) {
|
||||
el.style.visibility = params.visibility;
|
||||
}
|
||||
if (params.margin !== undefined) {
|
||||
el.style.margin = params.margin;
|
||||
}
|
||||
if (params.marginLeft !== undefined) {
|
||||
el.style.marginLeft = params.marginLeft;
|
||||
}
|
||||
if (params.marginTop !== undefined) {
|
||||
el.style.marginTop = params.marginTop;
|
||||
}
|
||||
if (params.padding !== undefined) {
|
||||
el.style.padding = params.padding;
|
||||
}
|
||||
if (params.color !== undefined) {
|
||||
el.style.color = params.color;
|
||||
}
|
||||
if (params.border !== undefined) {
|
||||
el.style.border = params.border;
|
||||
}
|
||||
if (params.float !== undefined) {
|
||||
el.style.cssFloat = params.float;
|
||||
}
|
||||
if (params.fontSize !== undefined) {
|
||||
el.style.fontSize = params.fontSize;
|
||||
}
|
||||
if (params.whiteSpace !== undefined) {
|
||||
el.style.whiteSpace = params.whiteSpace;
|
||||
}
|
||||
if (params.width !== undefined) {
|
||||
el.style.width = params.width;
|
||||
}
|
||||
if (params.height !== undefined) {
|
||||
el.style.height = params.height;
|
||||
}
|
||||
if (params.top !== undefined) {
|
||||
el.style.top = params.top;
|
||||
}
|
||||
if (params.left !== undefined) {
|
||||
el.style.left = params.left;
|
||||
}
|
||||
if (params.backgroundColor !== undefined) {
|
||||
el.style.backgroundColor = params.backgroundColor;
|
||||
}
|
||||
if (params.position !== undefined) {
|
||||
el.style.position = params.position;
|
||||
}
|
||||
if (params.overflow !== undefined) {
|
||||
el.style.overflow = params.overflow;
|
||||
}
|
||||
}
|
||||
|
||||
function setElementTooltip(el: HTMLElement, params: ICreateElementTooltipOptions): void {
|
||||
if (params.tooltip !== undefined && params.tooltip !== "") {
|
||||
el.className += " tooltip";
|
||||
el.appendChild(
|
||||
createElement("span", {
|
||||
class: "tooltiptext",
|
||||
innerHTML: params.tooltip,
|
||||
}),
|
||||
);
|
||||
} else if (params.tooltipleft !== undefined) {
|
||||
el.className += " tooltip";
|
||||
el.appendChild(
|
||||
createElement("span", {
|
||||
class: "tooltiptextleft",
|
||||
innerHTML: params.tooltipleft,
|
||||
}),
|
||||
);
|
||||
} else if (params.tooltipsmall !== undefined) {
|
||||
el.className += " tooltip";
|
||||
el.appendChild(
|
||||
createElement("span", {
|
||||
class: "tooltiptext smallfont",
|
||||
innerHTML: params.tooltipsmall,
|
||||
}),
|
||||
);
|
||||
} else if (params.tooltiplow !== undefined) {
|
||||
el.className += "tooltip";
|
||||
el.appendChild(
|
||||
createElement("span", {
|
||||
class: "tooltiptextlow",
|
||||
innerHTML: params.tooltiplow,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An all-in-one-call way of creating an element to be added to the DOM at some point.
|
||||
* @param tagName The HTML tag/element name
|
||||
* @param params Additional parameters to set on the element
|
||||
*/
|
||||
export function createElement(tagName: string, params: ICreateElementOptions = {}): HTMLElement {
|
||||
const el: HTMLElement = document.createElement(tagName);
|
||||
|
||||
if (params.id !== undefined) {
|
||||
el.id = params.id;
|
||||
}
|
||||
if (params.class !== undefined) {
|
||||
el.className = params.class;
|
||||
}
|
||||
if (params.innerHTML !== undefined) {
|
||||
el.innerHTML = params.innerHTML;
|
||||
}
|
||||
if (params.innerText !== undefined) {
|
||||
el.innerText = params.innerText;
|
||||
}
|
||||
if (params.tabIndex !== undefined) {
|
||||
el.tabIndex = params.tabIndex;
|
||||
}
|
||||
|
||||
setElementAnchor(el as HTMLAnchorElement, params);
|
||||
setElementInput(el as HTMLInputElement, params);
|
||||
setElementLabel(el as HTMLLabelElement, params);
|
||||
setElementListeners(el, params);
|
||||
setElementStyle(el, params);
|
||||
setElementTooltip(el, params);
|
||||
|
||||
return el;
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
import { createElement } from "./createElement";
|
||||
import { getElementById } from "./getElementById";
|
||||
|
||||
interface ICreatePopupOptions {
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the necessary DOM elements to present an in-game popup to the player.
|
||||
* @param id The (hopefully) unique identifier for the popup container.
|
||||
* @param elems The collection of HTML Elements to show within the popup.
|
||||
*/
|
||||
export function createPopup(id: string, elems: HTMLElement[], options: ICreatePopupOptions = {}): HTMLDivElement {
|
||||
const container: HTMLDivElement = createElement("div", {
|
||||
class: "popup-box-container",
|
||||
display: "flex",
|
||||
id: id,
|
||||
}) as HTMLDivElement;
|
||||
const content: HTMLElement = createElement("div", {
|
||||
class: "popup-box-content",
|
||||
id: `${id}-content`,
|
||||
});
|
||||
|
||||
for (const elem of elems) {
|
||||
content.appendChild(elem);
|
||||
}
|
||||
|
||||
// Configurable Options
|
||||
if (options.backgroundColor) {
|
||||
content.style.backgroundColor = options.backgroundColor;
|
||||
}
|
||||
|
||||
container.appendChild(content);
|
||||
getElementById("root").appendChild(container);
|
||||
|
||||
return container;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Returns a reference to the first object with the specified value of the ID or NAME attribute,
|
||||
* throwing an error if it is unable to find it.
|
||||
* @param elementId The HTML ID to retrieve the element by.
|
||||
* @throws {Error} When the 'elementId' cannot be found.
|
||||
*/
|
||||
export function getElementById(elementId: string): HTMLElement {
|
||||
const el: HTMLElement | null = document.getElementById(elementId);
|
||||
if (el === null) {
|
||||
throw new Error(`Unable to find element with id '${elementId}'`);
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
export function getSelectValue(selector: HTMLSelectElement | null): string {
|
||||
if (selector == null) {
|
||||
return "";
|
||||
}
|
||||
if (selector.options.length <= 0) {
|
||||
return "";
|
||||
}
|
||||
if (selector.selectedIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return selector.options[selector.selectedIndex].value;
|
||||
}
|
||||
|
||||
export function getSelectText(selector: HTMLSelectElement | null): string {
|
||||
if (selector == null) {
|
||||
return "";
|
||||
}
|
||||
if (selector.options.length <= 0) {
|
||||
return "";
|
||||
}
|
||||
if (selector.selectedIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return selector.options[selector.selectedIndex].text;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { isString } from "../../utils/helpers/isString";
|
||||
import { getElementById } from "./getElementById";
|
||||
|
||||
/**
|
||||
* Clears out all children from the provided element.
|
||||
* If a string is passed in, it will treat it as an ID and search for the element to delete all children from.
|
||||
* @param el The element or ID of an element to remove all children from.
|
||||
*/
|
||||
export function removeChildrenFromElement(el: string | null | Element): void {
|
||||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const elem: HTMLElement | Element = isString(el) ? getElementById(el as string) : (el as Element);
|
||||
|
||||
if (elem instanceof Element) {
|
||||
while (elem.firstChild !== null) {
|
||||
elem.removeChild(elem.firstChild);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.debug(e);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* For a given element, this function removes it AND its children
|
||||
* @param elem The element to remove.
|
||||
*/
|
||||
export function removeElement(elem: Element | null): void {
|
||||
if (elem === null) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.debug("The element passed into 'removeElement' was null.");
|
||||
|
||||
return;
|
||||
}
|
||||
if (!(elem instanceof Element)) {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.debug("The element passed into 'removeElement' was not an instance of an Element.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (elem.firstChild !== null) {
|
||||
elem.removeChild(elem.firstChild);
|
||||
}
|
||||
|
||||
if (elem.parentNode !== null) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { getElementById } from "./getElementById";
|
||||
import { removeElement } from "./removeElement";
|
||||
|
||||
/**
|
||||
* Given its id, this function removes an element AND its children
|
||||
* @param id The HTML identifier to search for and remove.
|
||||
*/
|
||||
export function removeElementById(id: string): void {
|
||||
try {
|
||||
const elem: HTMLElement = getElementById(id);
|
||||
removeElement(elem);
|
||||
} catch (e) {
|
||||
// Probably should log this as we're trying to remove elements that don't exist.
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user