work on blade to react

This commit is contained in:
Olivier Gagnon 2021-08-15 21:49:08 -04:00
parent 33f0efd49c
commit 99d4f17cdb
17 changed files with 247 additions and 301 deletions

@ -55,7 +55,7 @@ interface IConstructorParams {
}
function generateStatsDescription(mults: IMap<number>, programs?: string[], startingMoney?: number): JSX.Element {
const f = (x: number, decimals: number = 0) => {
const f = (x: number, decimals = 0) => {
// look, I don't know how to make a "smart decimals"
// todo, make it smarter
if(x === 1.0777-1) return "7.77%";

@ -52,24 +52,10 @@ import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
import { SkillElem } from "./Bladeburner/ui/SkillElem";
import { SkillList } from "./Bladeburner/ui/SkillList";
import { BlackOpElem } from "./Bladeburner/ui/BlackOpElem";
import { BlackOpList } from "./Bladeburner/ui/BlackOpList";
import { OperationElem } from "./Bladeburner/ui/OperationElem";
import { OperationList } from "./Bladeburner/ui/OperationList";
import { ContractElem } from "./Bladeburner/ui/ContractElem";
import { ContractList } from "./Bladeburner/ui/ContractList";
import { GeneralActionElem } from "./Bladeburner/ui/GeneralActionElem";
import { GeneralActionList } from "./Bladeburner/ui/GeneralActionList";
import { GeneralActionPage } from "./Bladeburner/ui/GeneralActionPage";
import { ContractPage } from "./Bladeburner/ui/ContractPage";
import { OperationPage } from "./Bladeburner/ui/OperationPage";
import { BlackOpPage } from "./Bladeburner/ui/BlackOpPage";
import { SkillPage } from "./Bladeburner/ui/SkillPage";
import { Stats } from "./Bladeburner/ui/Stats";
import { AllPages } from "./Bladeburner/ui/AllPages";
import { Console } from "./Bladeburner/ui/Console";
import { Root } from "./Bladeburner/ui/Root";
import { StatsTable } from "./ui/React/StatsTable";
import { CopyableText } from "./ui/React/CopyableText";
@ -77,73 +63,6 @@ import { Money } from "./ui/React/Money";
import React from "react";
import ReactDOM from "react-dom";
const stealthIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 166 132" style="fill:#adff2f;"><g><path d="M132.658-0.18l-24.321,24.321c-7.915-2.71-16.342-4.392-25.087-4.392c-45.84,0-83,46-83,46 s14.1,17.44,35.635,30.844L12.32,120.158l12.021,12.021L144.68,11.841L132.658-0.18z M52.033,80.445 c-2.104-4.458-3.283-9.438-3.283-14.695c0-19.054,15.446-34.5,34.5-34.5c5.258,0,10.237,1.179,14.695,3.284L52.033,80.445z"/><path d="M134.865,37.656l-18.482,18.482c0.884,3.052,1.367,6.275,1.367,9.612c0,19.055-15.446,34.5-34.5,34.5 c-3.337,0-6.56-0.483-9.611-1.367l-10.124,10.124c6.326,1.725,12.934,2.743,19.735,2.743c45.84,0,83-46,83-46 S153.987,50.575,134.865,37.656z"/></g></svg>&nbsp;`
const killIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="-22 0 511 511.99561" style="fill:#adff2f;"><path d="m.496094 466.242188 39.902344-39.902344 45.753906 45.753906-39.898438 39.902344zm0 0"/><path d="m468.421875 89.832031-1.675781-89.832031-300.265625 300.265625 45.753906 45.753906zm0 0"/><path d="m95.210938 316.785156 16.84375 16.847656h.003906l83.65625 83.65625 22.753906-22.753906-100.503906-100.503906zm0 0"/><path d="m101.445312 365.300781-39.902343 39.902344 45.753906 45.753906 39.902344-39.902343-39.90625-39.902344zm0 0"/></svg>`
// DOM related variables
const ActiveActionCssClass = "bladeburner-active-action";
// Console related stuff
let consoleHistoryIndex = 0;
// Keypresses for Console
$(document).keydown(function(event) {
if (routing.isOn(Page.Bladeburner)) {
if (!(Player.bladeburner instanceof Bladeburner)) { return; }
let consoleHistory = Player.bladeburner.consoleHistory;
if (event.keyCode === KEY.ENTER) {
event.preventDefault();
var command = DomElems.consoleInput.value;
if (command.length > 0) {
Player.bladeburner.postToConsole("> " + command);
Player.bladeburner.resetConsoleInput();
Player.bladeburner.executeConsoleCommands(command);
}
}
if (event.keyCode === KEY.UPARROW) {
if (DomElems.consoleInput == null) {return;}
var i = consoleHistoryIndex;
var len = consoleHistory.length;
if (len === 0) {return;}
if (i < 0 || i > len) {
consoleHistoryIndex = len;
}
if (i !== 0) {
--consoleHistoryIndex;
}
var prevCommand = consoleHistory[consoleHistoryIndex];
DomElems.consoleInput.value = prevCommand;
setTimeoutRef(function(){DomElems.consoleInput.selectionStart = DomElems.consoleInput.selectionEnd = 10000; }, 0);
}
if (event.keyCode === KEY.DOWNARROW) {
if (DomElems.consoleInput == null) {return;}
var i = consoleHistoryIndex;
var len = consoleHistory.length;
if (len == 0) {return;}
if (i < 0 || i > len) {
consoleHistoryIndex = len;
}
// Latest command, put nothing
if (i == len || i == len-1) {
consoleHistoryIndex = len;
DomElems.consoleInput.value = "";
} else {
++consoleHistoryIndex;
var prevCommand = consoleHistory[consoleHistoryIndex];
DomElems.consoleInput.value = prevCommand;
}
}
}
});
function ActionIdentifier(params={}) {
if (params.name) {this.name = params.name;}
if (params.type) {this.type = params.type;}
@ -1216,67 +1135,19 @@ let DomElems = {};
Bladeburner.prototype.initializeDomElementRefs = function() {
DomElems = {
bladeburnerDiv: null,
// Main Divs
overviewConsoleParentDiv: null,
overviewDiv: null, // Overview of stats that stays fixed on left
actionAndSkillsDiv: null, // Panel for different sections (contracts, ops, skills)
consoleDiv: null,
consoleTable: null,
consoleInputRow: null, // tr
consoleInputCell: null, // td
consoleInputHeader: null, // "> "
consoleInput: null, // Actual input element
bladeburnerDiv: null,
};
}
Bladeburner.prototype.createContent = function() {
DomElems.bladeburnerDiv = createElement("div", {
id:"bladeburner-container", position:"fixed", class:"generic-menupage-container",
});
DomElems.bladeburnerDiv = createElement("div");
// Parent Div for Overview and Console
DomElems.overviewConsoleParentDiv = createElement("div", {
height:"60%", display:"block", position:"relative",
});
// Overview and Action/Skill pane
DomElems.overviewDiv = createElement("div", {
width:"30%", display:"inline-block", border:"1px solid white",
});
DomElems.actionAndSkillsDiv = createElement("div", {
width:"70%", display:"block",
border:"1px solid white", margin:"6px", padding:"6px",
});
ReactDOM.render(<Stats bladeburner={this} player={Player} />, DomElems.overviewDiv);
ReactDOM.render(<AllPages bladeburner={this} />, DomElems.actionAndSkillsDiv);
// Console
DomElems.consoleDiv = createElement("div", {
class:"bladeburner-console-div",
clickListener:() => {
if (DomElems.consoleInput instanceof Element) {
DomElems.consoleInput.focus();
}
return false;
},
});
ReactDOM.render(<Console bladeburner={this} />, DomElems.consoleDiv);
DomElems.overviewConsoleParentDiv.appendChild(DomElems.overviewDiv);
DomElems.overviewConsoleParentDiv.appendChild(DomElems.consoleDiv);
DomElems.bladeburnerDiv.appendChild(DomElems.overviewConsoleParentDiv);
DomElems.bladeburnerDiv.appendChild(DomElems.actionAndSkillsDiv);
ReactDOM.render(<Root bladeburner={this} player={Player} engine={Engine} />, DomElems.bladeburnerDiv);
document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv);
if (this.consoleLogs.length === 0) {
this.postToConsole("Bladeburner Console BETA");
this.postToConsole("Bladeburner Console");
this.postToConsole("Type 'help' to see console commands");
} else {
for (let i = 0; i < this.consoleLogs.length; ++i) {
@ -1323,16 +1194,7 @@ Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) {
}
}
Bladeburner.prototype.resetConsoleInput = function() {
DomElems.consoleInput.value = "";
}
Bladeburner.prototype.clearConsole = function() {
while (DomElems.consoleTable.childNodes.length > 1) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);
}
this.consoleLogs.length = 0;
}
@ -1351,7 +1213,6 @@ Bladeburner.prototype.executeConsoleCommands = function(commands) {
this.consoleHistory.splice(0, 1);
}
}
consoleHistoryIndex = this.consoleHistory.length;
const arrayOfCommands = commands.split(";");
for (let i = 0; i < arrayOfCommands.length; ++i) {

@ -6,6 +6,8 @@ import {
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
import { stealthIcon, killIcon } from "../data/Icons";
import { createPopup } from "../../ui/React/createPopup";
import { TeamSizePopup } from "./TeamSizePopup";
interface IProps {
bladeburner: any;
@ -34,41 +36,12 @@ export function BlackOpElem(props: IProps): React.ReactElement {
}
function onTeam() {
// TODO(hydroflame): this needs some changes that are in the Gang conversion.
// var popupId = "bladeburner-operation-set-team-size-popup";
// var txt = createElement("p", {
// innerText:"Enter the amount of team members you would like to take on this " +
// "BlackOp. If you do not have the specified number of team members, " +
// "then as many as possible will be used. Note that team members may " +
// "be lost during operations.",
// });
// var input = createElement("input", {
// type:"number", placeholder: "Team size", class: "text-input",
// });
// var setBtn = createElement("a", {
// innerText:"Confirm", class:"a-link-button",
// clickListener:() => {
// var num = Math.round(parseFloat(input.value));
// if (isNaN(num) || num < 0) {
// dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)")
// } else {
// action.teamCount = num;
// this.updateBlackOpsUIElement(el, action);
// }
// removeElementById(popupId);
// return false;
// },
// });
// var cancelBtn = createElement("a", {
// innerText:"Cancel", class:"a-link-button",
// clickListener:() => {
// removeElementById(popupId);
// return false;
// },
// });
// createPopup(popupId, [txt, input, setBtn, cancelBtn]);
// input.focus();
const popupId = "bladeburner-operation-set-team-size-popup";
createPopup(popupId, TeamSizePopup, {
bladeburner: props.bladeburner,
action: props.action,
popupId: popupId,
});
}
return (<>

@ -25,18 +25,16 @@ export function BlackOpList(props: IProps): React.ReactElement {
return (a.reqdRank - b.reqdRank);
});
blackops = blackops.filter((blackop: BlackOperation, i: number) =>
!(props.bladeburner.blackops[blackops[i].name] == null &&
blackops = blackops.filter((blackop: BlackOperation, i: number) => !(props.bladeburner.blackops[blackops[i].name] == null &&
i !== 0 &&
props.bladeburner.blackops[blackops[i-1].name] == null));
blackops = blackops.reverse();
return (<>
{blackops.map((blackop: BlackOperation) =>
<li key={blackop.name} className="bladeburner-action">
{blackops.map((blackop: BlackOperation) => <li key={blackop.name} className="bladeburner-action">
<BlackOpElem bladeburner={props.bladeburner} action={blackop} />
</li>
</li>,
)}
</>);
}

@ -15,29 +15,97 @@ interface IProps {
}
export function Console(props: IProps): React.ReactElement {
const lastRef = useRef<HTMLTableDataCellElement>(null);
const lastRef = useRef<HTMLDivElement>(null);
const setRerender = useState(false)[1];
useEffect(() => {
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
// TODO: Figure out how to actually make the scrolling work correctly.
function scrollToBottom() {
if(lastRef.current)
lastRef.current.scrollIntoView({block: "end", inline: "nearest", behavior: "smooth" });
const id = setInterval(() => setRerender(old => !old), 1000);
return () => clearInterval(id);
lastRef.current.scrollTop = lastRef.current.scrollHeight;
}
function rerender() {
setRerender(old => !old);
}
useEffect(() => {
const id = setInterval(rerender, 1000);
const id2 = setInterval(scrollToBottom, 100);
return () => {
clearInterval(id);
clearInterval(id2);
};
}, []);
return (<table className="bladeburner-console-table">
<tbody>
{/*
TODO: optimize this.
using `i` as a key here isn't great because it'll re-render everything
everytime the console reaches max length.
*/}
{props.bladeburner.consoleLogs.map((log: any, i: number) => <Line key={i} content={log} />)}
<tr key='input' id="bladeburner-console-input-row" className="bladeburner-console-input-row">
<td ref={lastRef} className="bladeburner-console-input-cell">
<pre>{"> "}</pre><input autoFocus className="bladeburner-console-input" tabIndex={1} type="text" />
</td>
</tr>
</tbody>
</table>);
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.keyCode === 13) {
event.preventDefault();
const command = event.currentTarget.value;
event.currentTarget.value = "";
if (command.length > 0) {
props.bladeburner.postToConsole("> " + command);
props.bladeburner.executeConsoleCommands(command);
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
rerender();
}
}
const consoleHistory = props.bladeburner.consoleHistory;
if (event.keyCode === 38) { // up
let i = consoleHistoryIndex;
const len = consoleHistory.length;
if (len === 0) {return;}
if (i < 0 || i > len) {
setConsoleHistoryIndex(len);
}
if (i !== 0) {
i = i-1;
}
setConsoleHistoryIndex(i);
const prevCommand = consoleHistory[i];
event.currentTarget.value = prevCommand;
}
if (event.keyCode === 40) {
const i = consoleHistoryIndex;
const len = consoleHistory.length;
if (len == 0) {return;}
if (i < 0 || i > len) {
setConsoleHistoryIndex(len);
}
// Latest command, put nothing
if (i == len || i == len-1) {
setConsoleHistoryIndex(len);
event.currentTarget.value = "";
} else {
setConsoleHistoryIndex(consoleHistoryIndex+1);
const prevCommand = consoleHistory[consoleHistoryIndex+1];
event.currentTarget.value = prevCommand;
}
}
}
return (<div ref={lastRef} className="bladeburner-console-div">
<table className="bladeburner-console-table">
<tbody>
{/*
TODO: optimize this.
using `i` as a key here isn't great because it'll re-render everything
everytime the console reaches max length.
*/}
{props.bladeburner.consoleLogs.map((log: any, i: number) => <Line key={i} content={log} />)}
<tr key="input" id="bladeburner-console-input-row" className="bladeburner-console-input-row">
<td className="bladeburner-console-input-cell">
<pre>{"> "}</pre><input autoFocus className="bladeburner-console-input" tabIndex={1} type="text" onKeyDown={handleKeyDown} />
</td>
</tr>
</tbody>
</table>
</div>);
}

@ -14,10 +14,9 @@ export function ContractList(props: IProps): React.ReactElement {
const names = Object.keys(props.bladeburner.contracts);
const contracts = props.bladeburner.contracts;
return (<>
{names.map((name: string) =>
<li key={name} className="bladeburner-action">
{names.map((name: string) => <li key={name} className="bladeburner-action">
<ContractElem bladeburner={props.bladeburner} action={contracts[name]} />
</li>
</li>,
)}
</>);
}

@ -19,10 +19,9 @@ export function GeneralActionList(props: IProps): React.ReactElement {
}
}
return (<>
{actions.map((action: Action) =>
<li key={action.name} className="bladeburner-action">
{actions.map((action: Action) => <li key={action.name} className="bladeburner-action">
<GeneralActionElem bladeburner={props.bladeburner} action={action} />
</li>
</li>,
)}
</>);
}

@ -7,6 +7,8 @@ import {
} from "../../../utils/StringHelperFunctions";
import { stealthIcon, killIcon } from "../data/Icons";
import { BladeburnerConstants } from "../data/Constants";
import { createPopup } from "../../ui/React/createPopup";
import { TeamSizePopup } from "./TeamSizePopup";
interface IProps {
bladeburner: any;
@ -30,40 +32,12 @@ export function OperationElem(props: IProps): React.ReactElement {
}
function onTeam() {
// var popupId = "bladeburner-operation-set-team-size-popup";
// var txt = createElement("p", {
// innerText:"Enter the amount of team members you would like to take on these " +
// "operations. If you do not have the specified number of team members, " +
// "then as many as possible will be used. Note that team members may " +
// "be lost during operations.",
// });
// var input = createElement("input", {
// type:"number", placeholder: "Team size", class: "text-input",
// });
// var setBtn = createElement("a", {
// innerText:"Confirm", class:"a-link-button",
// clickListener:() => {
// var num = Math.round(parseFloat(input.value));
// if (isNaN(num) || num < 0) {
// dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)")
// } else {
// action.teamCount = num;
// this.updateOperationsUIElement(el, action);
// }
// removeElementById(popupId);
// return false;
// },
// });
// var cancelBtn = createElement("a", {
// innerText:"Cancel", class:"a-link-button",
// clickListener:() => {
// removeElementById(popupId);
// return false;
// },
// });
// createPopup(popupId, [txt, input, setBtn, cancelBtn]);
// input.focus();
const popupId = "bladeburner-operation-set-team-size-popup";
createPopup(popupId, TeamSizePopup, {
bladeburner: props.bladeburner,
action: props.action,
popupId: popupId,
});
}
function increaseLevel() {

@ -14,10 +14,9 @@ export function OperationList(props: IProps): React.ReactElement {
const names = Object.keys(props.bladeburner.operations);
const operations = props.bladeburner.operations;
return (<>
{names.map((name: string) =>
<li key={name} className="bladeburner-action">
{names.map((name: string) => <li key={name} className="bladeburner-action">
<OperationElem bladeburner={props.bladeburner} action={operations[name]} />
</li>
</li>,
)}
</>);
}

@ -0,0 +1,27 @@
import React from "react";
import { Stats } from "./Stats";
import { Console } from "./Console";
import { AllPages } from "./AllPages";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
interface IProps {
bladeburner: any;
engine: IEngine;
player: IPlayer;
}
export function Root(props: IProps): React.ReactElement {
return (<div id="bladeburner-container" className="generic-menupage-container" style={{position:"fixed"}}>
<div style={{height:"60%", display:"block", position:"relative"}}>
<div style={{height: '100%', width:"30%", display:"inline-block", border:"1px solid white"}}>
<Stats bladeburner={props.bladeburner} player={props.player} engine={props.engine} />
</div>
<Console bladeburner={props.bladeburner} />
</div>
<div style={{width:"70%", display:"block", border:"1px solid white", marginTop:"6px", padding: "6px", position:"relative"}}>
<AllPages bladeburner={props.bladeburner} />
</div>
</div>);
}

@ -9,10 +9,9 @@ interface IProps {
export function SkillList(props: IProps): React.ReactElement {
return (<>
{Object.keys(Skills).map((skill: string) =>
<li key={skill} className="bladeburner-action">
{Object.keys(Skills).map((skill: string) => <li key={skill} className="bladeburner-action">
<SkillElem bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
</li>
</li>,
)}
</>);
}

@ -5,13 +5,23 @@ import {
} from "../../../utils/StringHelperFunctions";
import { BladeburnerConstants } from "../data/Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { IEngine } from "../../IEngine";
import { Money } from "../../ui/React/Money";
import { StatsTable } from "../../ui/React/StatsTable";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { createPopup } from "../../ui/React/createPopup";
import { Factions } from "../../Faction/Factions";
import {
joinFaction,
displayFactionContent,
} from "../../Faction/FactionHelpers";
import { TravelPopup } from "./TravelPopup";
interface IProps {
bladeburner: any;
engine: IEngine;
player: IPlayer;
}
@ -53,53 +63,26 @@ export function Stats(props: IProps): React.ReactElement {
}
function openTravel() {
// var popupId = "bladeburner-travel-popup-cancel-btn";
// var popupArguments = [];
// popupArguments.push(createElement("a", { // Cancel Button
// innerText:"Cancel", class:"a-link-button",
// clickListener:() => {
// removeElementById(popupId); return false;
// },
// }))
// popupArguments.push(createElement("p", { // Info Text
// innerText:"Travel to a different city for your Bladeburner " +
// "activities. This does not cost any money. The city you are " +
// "in for your Bladeburner duties does not affect " +
// "your location in the game otherwise",
// }));
// for (var i = 0; i < BladeburnerConstants.CityNames.length; ++i) {
// (function(inst, i) {
// popupArguments.push(createElement("div", {
// // Reusing this css class...it adds a border and makes it
// // so that background color changes when you hover
// class:"cmpy-mgmt-find-employee-option",
// innerText:BladeburnerConstants.CityNames[i],
// clickListener:() => {
// inst.city = BladeburnerConstants.CityNames[i];
// removeElementById(popupId);
// inst.updateOverviewContent();
// return false;
// },
// }));
// })(this, i);
// }
// createPopup(popupId, popupArguments);
const popupId = "bladeburner-travel-popup";
createPopup(popupId, TravelPopup, {
bladeburner: props.bladeburner,
popupId: popupId,
});
}
function openFaction() {
// if (bladeburnerFac.isMember) {
// Engine.loadFactionContent();
// displayFactionContent(bladeburnersFactionName);
// } else {
// if (this.rank >= BladeburnerConstants.RankNeededForFaction) {
// joinFaction(bladeburnerFac);
// dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction");
// removeChildrenFromElement(DomElems.overviewDiv);
// this.createOverviewContent();
// } else {
// dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!")
// }
// }
const faction = Factions["Bladeburners"];
if (faction.isMember) {
props.engine.loadFactionContent();
displayFactionContent("Bladeburners");
} else {
if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) {
joinFaction(faction);
dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction");
} else {
dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!")
}
}
}
return (<>

@ -0,0 +1,37 @@
import React, { useState } from "react";
import { removePopup } from "../../ui/React/createPopup";
import { BladeburnerConstants } from "../data/Constants";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { Action } from "../Action";
interface IProps {
bladeburner: any;
action: Action;
popupId: string;
}
export function TeamSizePopup(props: IProps): React.ReactElement {
const [teamSize, setTeamSize] = useState<number | undefined>();
function confirmTeamSize(): void {
if(teamSize === undefined) return;
const num = Math.round(teamSize);
if (isNaN(num) || num < 0) {
dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)")
} else {
props.action.teamCount = num;
}
removePopup(props.popupId);
}
return (<>
<p>
Enter the amount of team members you would like to take on this
Op. If you do not have the specified number of team members,
then as many as possible will be used. Note that team members may
be lost during operations.
</p>
<input autoFocus type="number" placeholder= "Team size" className= "text-input" onChange={event => setTeamSize(parseFloat(event.target.value))} />
<a className="a-link-button" onClick={confirmTeamSize}>Confirm</a>
</>);
}

@ -0,0 +1,31 @@
import React from "react";
import { removePopup } from "../../ui/React/createPopup";
import { BladeburnerConstants } from "../data/Constants";
interface IProps {
bladeburner: any;
popupId: string;
}
export function TravelPopup(props: IProps): React.ReactElement {
function travel(city: string) {
props.bladeburner.city = city;
removePopup(props.popupId);
}
return (<>
<p>
Travel to a different city for your Bladeburner
activities. This does not cost any money. The city you are
in for your Bladeburner duties does not affect
your location in the game otherwise.
</p>
{BladeburnerConstants.CityNames.map(city =>
// Reusing this css class...it adds a border and makes it
// so that background color changes when you hover
<div className="cmpy-mgmt-find-employee-option"
onClick={() => travel(city)}>
{city}
</div>)}
</>);
}

@ -28,8 +28,7 @@ export function FactionList(props: IProps): React.ReactElement {
<p>Lists all factions you have joined</p>
<br />
<ul>
{props.player.factions.map((faction: string) =>
<li key={faction}><a
{props.player.factions.map((faction: string) => <li key={faction}><a
className="a-link-button"
onClick={() => openFaction(faction)}
style={{padding:"4px", margin:"4px", display:"inline-block"}}>{faction}
@ -39,8 +38,7 @@ export function FactionList(props: IProps): React.ReactElement {
<h1>Outstanding Faction Invitations</h1>
<p style={{width: '70%'}}>Lists factions you have been invited to. You can accept these faction invitations at any time.</p>
<ul>
{props.player.factionInvitations.map((faction: string) =>
<li key={faction} style={{padding:"6px", margin:"6px"}}>
{props.player.factionInvitations.map((faction: string) => <li key={faction} style={{padding:"6px", margin:"6px"}}>
<p style={{display:"inline", margin:"4px", padding:"4px"}}>{faction}</p>
<a
className="a-link-button"

@ -42,6 +42,6 @@ export function TaskSelector(props: IProps): React.ReactElement {
<option key={0} value={"---"}>---</option>
{tasks.map((task: string, i: number) => <option key={i+1} value={task}>{task}</option>)}
</select>
<div>{StatsTable(data, null)}</div>
<div>{StatsTable(data)}</div>
</>);
}

@ -76,7 +76,7 @@ function containsAllStrings(arr: string[]): boolean {
}
// Formats a number with commas and a specific number of decimal digits
function formatNumber(num: number, numFractionDigits: number = 0): string {
function formatNumber(num: number, numFractionDigits = 0): string {
return num.toLocaleString(undefined, {
maximumFractionDigits: numFractionDigits,
minimumFractionDigits: numFractionDigits,