converting more blade to react

This commit is contained in:
Olivier Gagnon 2021-08-10 11:25:21 -04:00
parent 988ca37764
commit 33f0efd49c
13 changed files with 234 additions and 400 deletions

@ -68,6 +68,8 @@ import { OperationPage } from "./Bladeburner/ui/OperationPage";
import { BlackOpPage } from "./Bladeburner/ui/BlackOpPage"; import { BlackOpPage } from "./Bladeburner/ui/BlackOpPage";
import { SkillPage } from "./Bladeburner/ui/SkillPage"; import { SkillPage } from "./Bladeburner/ui/SkillPage";
import { Stats } from "./Bladeburner/ui/Stats"; import { Stats } from "./Bladeburner/ui/Stats";
import { AllPages } from "./Bladeburner/ui/AllPages";
import { Console } from "./Bladeburner/ui/Console";
import { StatsTable } from "./ui/React/StatsTable"; import { StatsTable } from "./ui/React/StatsTable";
import { CopyableText } from "./ui/React/CopyableText"; import { CopyableText } from "./ui/React/CopyableText";
@ -443,9 +445,6 @@ Bladeburner.prototype.process = function() {
} }
} }
if (routing.isOn(Page.Bladeburner)) {
this.updateContent();
}
} }
} }
@ -809,10 +808,6 @@ Bladeburner.prototype.completeAction = function() {
return hackWorldDaemon(Player.bitNodeN); return hackWorldDaemon(Player.bitNodeN);
} }
if (routing.isOn(Page.Bladeburner)) {
this.createActionAndSkillsContent();
}
if (this.logging.blackops) { if (this.logging.blackops) {
this.log(action.name + " successful! Gained " + formatNumber(rankGain, 1) + " rank"); this.log(action.name + " successful! Gained " + formatNumber(rankGain, 1) + " rank");
} }
@ -1228,7 +1223,6 @@ Bladeburner.prototype.initializeDomElementRefs = function() {
overviewDiv: null, // Overview of stats that stays fixed on left overviewDiv: null, // Overview of stats that stays fixed on left
actionAndSkillsDiv: null, // Panel for different sections (contracts, ops, skills) actionAndSkillsDiv: null, // Panel for different sections (contracts, ops, skills)
currentTab: null, // Contracts, Operations, Black Ops, Skills
consoleDiv: null, consoleDiv: null,
consoleTable: null, consoleTable: null,
@ -1236,29 +1230,6 @@ Bladeburner.prototype.initializeDomElementRefs = function() {
consoleInputCell: null, // td consoleInputCell: null, // td
consoleInputHeader: null, // "> " consoleInputHeader: null, // "> "
consoleInput: null, // Actual input element consoleInput: null, // Actual input element
// Overview Content
overviewRank: null,
overviewStamina: null,
overviewStaminaHelpTip: null,
overviewGen1: null, // Stamina Penalty, Team, Hospitalized stats, current city
overviewEstPop: null,
overviewEstPopHelpTip: null,
overviewEstComms: null,
overviewChaos: null,
overviewSkillPoints: null,
overviewBonusTime: null,
overviewAugMults: null,
// Actions and Skills Content
actionsAndSkillsDesc: null,
actionsAndSkillsList: null, // ul element of all UI elements in this panel
generalActions: {},
contracts: {},
operations: {},
blackops: {},
skills: {},
skillPointsDisplay: null,
}; };
} }
@ -1282,10 +1253,8 @@ Bladeburner.prototype.createContent = function() {
border:"1px solid white", margin:"6px", padding:"6px", border:"1px solid white", margin:"6px", padding:"6px",
}); });
DomElems.currentTab = "general"; ReactDOM.render(<Stats bladeburner={this} player={Player} />, DomElems.overviewDiv);
ReactDOM.render(<AllPages bladeburner={this} />, DomElems.actionAndSkillsDiv);
this.createOverviewContent();
this.createActionAndSkillsContent();
// Console // Console
DomElems.consoleDiv = createElement("div", { DomElems.consoleDiv = createElement("div", {
@ -1297,32 +1266,13 @@ Bladeburner.prototype.createContent = function() {
return false; return false;
}, },
}); });
DomElems.consoleTable = createElement("table", {class:"bladeburner-console-table"}); ReactDOM.render(<Console bladeburner={this} />, DomElems.consoleDiv);
DomElems.consoleInputRow = createElement("tr", {class:"bladeburner-console-input-row", id:"bladeburner-console-input-row"});
DomElems.consoleInputCell = createElement("td", {class:"bladeburner-console-input-cell"});
DomElems.consoleInputHeader = createElement("pre", {innerText:"> "});
DomElems.consoleInput = createElement("input", {
type:"text", class:"bladeburner-console-input", tabIndex:1,
onfocus:() => {DomElems.consoleInput.value = DomElems.consoleInput.value},
});
DomElems.consoleInputCell.appendChild(DomElems.consoleInputHeader);
DomElems.consoleInputCell.appendChild(DomElems.consoleInput);
DomElems.consoleInputRow.appendChild(DomElems.consoleInputCell);
DomElems.consoleTable.appendChild(DomElems.consoleInputRow);
DomElems.consoleDiv.appendChild(DomElems.consoleTable);
DomElems.overviewConsoleParentDiv.appendChild(DomElems.overviewDiv); DomElems.overviewConsoleParentDiv.appendChild(DomElems.overviewDiv);
DomElems.overviewConsoleParentDiv.appendChild(DomElems.consoleDiv); DomElems.overviewConsoleParentDiv.appendChild(DomElems.consoleDiv);
DomElems.bladeburnerDiv.appendChild(DomElems.overviewConsoleParentDiv); DomElems.bladeburnerDiv.appendChild(DomElems.overviewConsoleParentDiv);
DomElems.bladeburnerDiv.appendChild(DomElems.actionAndSkillsDiv); DomElems.bladeburnerDiv.appendChild(DomElems.actionAndSkillsDiv);
// legend
const legend = createElement("div")
legend.innerHTML = `<span class="text">${stealthIcon}= This action requires stealth, ${killIcon} = This action involves retirement</span>`
DomElems.bladeburnerDiv.appendChild(legend);
document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv); document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv);
if (this.consoleLogs.length === 0) { if (this.consoleLogs.length === 0) {
@ -1333,8 +1283,6 @@ Bladeburner.prototype.createContent = function() {
this.postToConsole(this.consoleLogs[i], false); this.postToConsole(this.consoleLogs[i], false);
} }
} }
DomElems.consoleInput.focus();
} }
Bladeburner.prototype.clearContent = function() { Bladeburner.prototype.clearContent = function() {
@ -1346,290 +1294,35 @@ Bladeburner.prototype.clearContent = function() {
this.initializeDomElementRefs(); this.initializeDomElementRefs();
} }
Bladeburner.prototype.createOverviewContent = function() { ////////////////////////////////////////////////////////////////////////////////
if (DomElems.overviewDiv == null) { ////////////////////////////////////////////////////////////////////////////////
throw new Error("Bladeburner.createOverviewContent() called with DomElems.overviewDiv = null"); ////////////////////////////////////////////////////////////////////////////////
} ////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
DomElems.overviewRank = createElement("p", { ////////////////////////////////////////////////////////////////////////////////
innerText:"Rank: ", ////////////////////////////////////////////////////////////////////////////////
display:"inline-block",
tooltip:"Your rank within the Bladeburner division",
});
DomElems.overviewStamina = createElement("p", {
display:"inline-block",
});
DomElems.overviewStaminaHelpTip = createElement("div", {
class:"help-tip",
innerText:"?",
clickListener: () => {
dialogBoxCreate("Performing actions will use up your stamina.<br><br>" +
"Your max stamina is determined primarily by your agility stat.<br><br>" +
"Your stamina gain rate is determined by both your agility and your " +
"max stamina. Higher max stamina leads to a higher gain rate.<br><br>" +
"Once your " +
"stamina falls below 50% of its max value, it begins to negatively " +
"affect the success rate of your contracts/operations. This penalty " +
"is shown in the overview panel. If the penalty is 15%, then this means " +
"your success rate would be multipled by 85% (100 - 15).<br><br>" +
"Your max stamina and stamina gain rate can also be increased by " +
"training, or through skills and Augmentation upgrades.");
},
});
DomElems.overviewGen1 = createElement("p", {
display:"block",
});
DomElems.overviewEstPop = createElement("p", {
innerText:"Est. Synthoid Population: ",
display:"inline-block",
tooltip:"This is your Bladeburner division's estimate of how many Synthoids exist " +
"in your current city.",
});
DomElems.overviewEstPopHelpTip = createElement("div", {
innerText:"?", class:"help-tip",
clickListener:() => {
dialogBoxCreate("The success rate of your contracts/operations depends on " +
"the population of Synthoids in your current city. " +
"The success rate that is shown to you is only an estimate, " +
"and it is based on your Synthoid population estimate.<br><br>" +
"Therefore, it is important that this Synthoid population estimate " +
"is accurate so that you have a better idea of your " +
"success rate for contracts/operations. Certain " +
"actions will increase the accuracy of your population " +
"estimate.<br><br>" +
"The Synthoid populations of cities can change due to your " +
"actions or random events. If random events occur, they will " +
"be logged in the Bladeburner Console.");
},
});
DomElems.overviewEstComms = createElement("p", {
innerText:"Est. Synthoid Communities: ",
display:"inline-block",
tooltip:"This is your Bladeburner divison's estimate of how many Synthoid " +
"communities exist in your current city.",
});
DomElems.overviewChaos = createElement("p", {
innerText:"City Chaos: ",
display:"inline-block",
tooltip:"The city's chaos level due to tensions and conflicts between humans and Synthoids. " +
"Having too high of a chaos level can make contracts and operations harder.",
});
DomElems.overviewBonusTime = createElement("p", {
innerText: "Bonus time: ",
display: "inline-block",
tooltip: "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser). " +
"Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.",
});
DomElems.overviewSkillPoints = createElement("p", {display:"block"});
DomElems.overviewAugMults = createElement("div", {display:"block"});
DomElems.overviewDiv.appendChild(DomElems.overviewRank);
appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(DomElems.overviewStamina);
DomElems.overviewDiv.appendChild(DomElems.overviewStaminaHelpTip);
DomElems.overviewDiv.appendChild(DomElems.overviewGen1);
DomElems.overviewDiv.appendChild(DomElems.overviewEstPop);
DomElems.overviewDiv.appendChild(DomElems.overviewEstPopHelpTip);
appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(DomElems.overviewEstComms);
appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(DomElems.overviewChaos);
appendLineBreaks(DomElems.overviewDiv, 2);
DomElems.overviewDiv.appendChild(DomElems.overviewBonusTime);
DomElems.overviewDiv.appendChild(DomElems.overviewSkillPoints);
appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(DomElems.overviewAugMults);
// Travel to new city button
appendLineBreaks(DomElems.overviewDiv, 1);
DomElems.overviewDiv.appendChild(createElement("a", {
innerHTML:"Travel", class:"a-link-button", display:"inline-block",
clickListener:() => {
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);
},
}));
// Faction button
const bladeburnersFactionName = "Bladeburners";
if (factionExists(bladeburnersFactionName)) {
var bladeburnerFac = Factions[bladeburnersFactionName];
if (!(bladeburnerFac instanceof Faction)) {
throw new Error("Could not properly get Bladeburner Faction object in Bladeburner UI Overview Faction button");
}
DomElems.overviewDiv.appendChild(createElement("a", {
innerText:"Faction", class:"a-link-button", display:"inline-block",
tooltip:"Apply to the Bladeburner Faction, or go to the faction page if you are already a member",
clickListener:() => {
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!")
}
}
return false;
},
}));
}
DomElems.overviewDiv.appendChild(createElement("br"));
DomElems.overviewDiv.appendChild(createElement("br"));
this.updateOverviewContent();
}
Bladeburner.prototype.createActionAndSkillsContent = function() {
if (DomElems.currentTab == null) {DomElems.currentTab = "general";}
removeChildrenFromElement(DomElems.actionAndSkillsDiv);
clearObject(DomElems.generalActions);
clearObject(DomElems.contracts);
clearObject(DomElems.operations);
clearObject(DomElems.blackops);
clearObject(DomElems.skills);
//Navigation buttons
var currTab = DomElems.currentTab.toLowerCase();
var buttons = ["General", "Contracts", "Operations", "BlackOps", "Skills"];
for (var i = 0; i < buttons.length; ++i) {
(function(buttons, i, inst, currTab) {
DomElems.actionAndSkillsDiv.appendChild(createElement("a", {
innerText:buttons[i],
class:currTab === buttons[i].toLowerCase() ? "bladeburner-nav-button-inactive" : "bladeburner-nav-button",
clickListener:() => {
DomElems.currentTab = buttons[i].toLowerCase();
inst.createActionAndSkillsContent();
return false;
},
}));
}) (buttons, i, this, currTab);
}
// General info/description for each action
DomElems.actionsAndSkillsDesc = createElement("p", {
display:"block", margin:"4px", padding:"4px",
});
// List for actions/skills
removeChildrenFromElement(DomElems.actionsAndSkillsList);
DomElems.actionsAndSkillsList = createElement("ul");
switch(currTab) {
case "general":
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<GeneralActionPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "contracts":
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<ContractPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "operations":
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<OperationPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "blackops":
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<BlackOpPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "skills":
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<SkillPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
default:
throw new Error("Invalid value for DomElems.currentTab in Bladeburner.createActionAndSkillsContent");
}
this.updateContent();
DomElems.actionAndSkillsDiv.appendChild(DomElems.actionsAndSkillsDesc);
DomElems.actionAndSkillsDiv.appendChild(DomElems.actionsAndSkillsList);
}
Bladeburner.prototype.updateContent = function() {
this.updateOverviewContent();
}
Bladeburner.prototype.updateOverviewContent = function() {
if (!routing.isOn(Page.Bladeburner)) return;
ReactDOM.render(<Stats bladeburner={this} player={Player} />, DomElems.overviewDiv);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
///////////////////////////////HYDRO END OF UI////////////////////////////////// ///////////////////////////////HYDRO END OF UI//////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Bladeburner Console Window // Bladeburner Console Window
Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) { Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) {
const MaxConsoleEntries = 100; const MaxConsoleEntries = 100;
if (saveToLogs === true) { if (saveToLogs) {
this.consoleLogs.push(input); this.consoleLogs.push(input);
if (this.consoleLogs.length > MaxConsoleEntries) { if (this.consoleLogs.length > MaxConsoleEntries) {
this.consoleLogs.shift(); this.consoleLogs.shift();
} }
} }
if (input == null || DomElems.consoleDiv == null) {return;}
$("#bladeburner-console-input-row").before('<tr><td class="bladeburner-console-line" style="color: var(--my-font-color); white-space:pre-wrap;">' + input + '</td></tr>');
if (DomElems.consoleTable.childNodes.length > MaxConsoleEntries) {
DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild);
}
this.updateConsoleScroll();
} }
Bladeburner.prototype.updateConsoleScroll = function() {
DomElems.consoleDiv.scrollTop = DomElems.consoleDiv.scrollHeight;
}
Bladeburner.prototype.resetConsoleInput = function() { Bladeburner.prototype.resetConsoleInput = function() {
DomElems.consoleInput.value = ""; DomElems.consoleInput.value = "";
@ -2035,7 +1728,6 @@ Bladeburner.prototype.executeSkillConsoleCommand = function(args) {
this.skillPoints -= pointCost; this.skillPoints -= pointCost;
this.upgradeSkill(skill); this.upgradeSkill(skill);
this.log(skill.name + " upgraded to Level " + this.skills[skillName]); this.log(skill.name + " upgraded to Level " + this.skills[skillName]);
this.createActionAndSkillsContent();
} else { } else {
this.postToConsole("You do not have enough Skill Points to upgrade this. You need " + formatNumber(pointCost, 0)); this.postToConsole("You do not have enough Skill Points to upgrade this. You need " + formatNumber(pointCost, 0));
} }
@ -2441,9 +2133,6 @@ Bladeburner.prototype.upgradeSkillNetscriptFn = function(skillName, workerScript
this.skillPoints -= cost; this.skillPoints -= cost;
this.upgradeSkill(skill); this.upgradeSkill(skill);
if (routing.isOn(Page.Bladeburner) && DomElems.currentTab.toLowerCase() === "skills") {
this.createActionAndSkillsContent();
}
workerScript.log("bladeburner.upgradeSkill", `'${skillName}' upgraded to level ${this.skills[skillName]}`); workerScript.log("bladeburner.upgradeSkill", `'${skillName}' upgraded to level ${this.skills[skillName]}`);
return true; return true;
} }
@ -2514,10 +2203,6 @@ Bladeburner.prototype.joinBladeburnerFactionNetscriptFn = function(workerScript)
} else if (this.rank >= BladeburnerConstants.RankNeededForFaction) { } else if (this.rank >= BladeburnerConstants.RankNeededForFaction) {
joinFaction(bladeburnerFac); joinFaction(bladeburnerFac);
workerScript.log("bladeburner.joinBladeburnerFaction", "Joined Bladeburners faction."); workerScript.log("bladeburner.joinBladeburnerFaction", "Joined Bladeburners faction.");
if (routing.isOn(Page.Bladeburner)) {
removeChildrenFromElement(DomElems.overviewDiv);
this.createOverviewContent();
}
return true; return true;
} else { } else {
workerScript.log("bladeburner.joinBladeburnerFaction", `You do not have the required rank (${this.rank}/${BladeburnerConstants.RankNeededForFaction}).`); workerScript.log("bladeburner.joinBladeburnerFaction", `You do not have the required rank (${this.rank}/${BladeburnerConstants.RankNeededForFaction}).`);

@ -0,0 +1,46 @@
import React, { useState, useEffect } from "react";
import { GeneralActionPage } from "./GeneralActionPage";
import { ContractPage } from "./ContractPage";
import { OperationPage } from "./OperationPage";
import { BlackOpPage } from "./BlackOpPage";
import { SkillPage } from "./SkillPage";
import { stealthIcon, killIcon } from "../data/Icons";
interface IProps {
bladeburner: any;
}
export function AllPages(props: IProps): React.ReactElement {
const [page, setPage] = useState('General');
const setRerender = useState(false)[1];
useEffect(() => {
const id = setInterval(() => setRerender(old => !old), 1000);
return () => clearInterval(id);
}, []);
function Header(props: {name: string}): React.ReactElement {
return (<a
onClick={()=>setPage(props.name)}
className={page !== props.name ?
"bladeburner-nav-button" :
"bladeburner-nav-button-inactive"}>
{props.name}
</a>);
}
return (<>
<Header name={'General'} />
<Header name={'Contracts'} />
<Header name={'Operations'} />
<Header name={'BlackOps'} />
<Header name={'Skills'} />
<div style={{display:"block", margin:"4px", padding:"4px"}}>
{page === 'General' && <GeneralActionPage bladeburner={props.bladeburner} />}
{page === 'Contracts' && <ContractPage bladeburner={props.bladeburner} />}
{page === 'Operations' && <OperationPage bladeburner={props.bladeburner} />}
{page === 'BlackOps' && <BlackOpPage bladeburner={props.bladeburner} />}
{page === 'Skills' && <SkillPage bladeburner={props.bladeburner} />}
</div>
<span className="text">{stealthIcon}= This action requires stealth, {killIcon} = This action involves retirement</span>
</>);
}

@ -1,4 +1,4 @@
import * as React from "react"; import React, { useState } from "react";
import { import {
formatNumber, formatNumber,
convertTimeMsToTimeElapsedString, convertTimeMsToTimeElapsedString,
@ -13,6 +13,7 @@ interface IProps {
} }
export function BlackOpElem(props: IProps): React.ReactElement { export function BlackOpElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
const isCompleted = (props.bladeburner.blackops[props.action.name] != null); const isCompleted = (props.bladeburner.blackops[props.action.name] != null);
if(isCompleted) { if(isCompleted) {
return ( return (
@ -29,7 +30,7 @@ export function BlackOpElem(props: IProps): React.ReactElement {
props.bladeburner.action.type = ActionTypes.BlackOperation; props.bladeburner.action.type = ActionTypes.BlackOperation;
props.bladeburner.action.name = props.action.name; props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.bladeburner.action); props.bladeburner.startAction(props.bladeburner.action);
props.bladeburner.updateActionAndSkillsContent(); setRerender(old => !old);
} }
function onTeam() { function onTeam() {

@ -0,0 +1,43 @@
import React, { useState, useRef, useEffect } from "react";
interface ILineProps {
content: any;
}
function Line(props: ILineProps): React.ReactElement {
return (<tr>
<td className="bladeburner-console-line" style={{color: 'var(--my-font-color)', whiteSpace: 'pre-wrap'}}>{props.content}</td>
</tr>)
}
interface IProps {
bladeburner: any;
}
export function Console(props: IProps): React.ReactElement {
const lastRef = useRef<HTMLTableDataCellElement>(null);
const setRerender = useState(false)[1];
useEffect(() => {
if(lastRef.current)
lastRef.current.scrollIntoView({block: "end", inline: "nearest", behavior: "smooth" });
const id = setInterval(() => setRerender(old => !old), 1000);
return () => clearInterval(id);
}, []);
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>);
}

@ -26,7 +26,6 @@ export function ContractElem(props: IProps): React.ReactElement {
props.bladeburner.action.type = ActionTypes.Contract; props.bladeburner.action.type = ActionTypes.Contract;
props.bladeburner.action.name = props.action.name; props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.bladeburner.action); props.bladeburner.startAction(props.bladeburner.action);
props.bladeburner.updateActionAndSkillsContent();
setRerender(old => !old); setRerender(old => !old);
} }

@ -11,13 +11,6 @@ interface IProps {
} }
export function ContractList(props: IProps): React.ReactElement { export function ContractList(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
useEffect(() => {
const id = setInterval(() => setRerender(old => !old), 1000);
return () => clearInterval(id);
}, []);
const names = Object.keys(props.bladeburner.contracts); const names = Object.keys(props.bladeburner.contracts);
const contracts = props.bladeburner.contracts; const contracts = props.bladeburner.contracts;
return (<> return (<>

@ -12,13 +12,6 @@ interface IProps {
} }
export function GeneralActionList(props: IProps): React.ReactElement { export function GeneralActionList(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
useEffect(() => {
const id = setInterval(() => setRerender(old => !old), 1000);
return () => clearInterval(id);
}, []);
const actions: Action[] = []; const actions: Action[] = [];
for (const name in GeneralActions) { for (const name in GeneralActions) {
if (GeneralActions.hasOwnProperty(name)) { if (GeneralActions.hasOwnProperty(name)) {

@ -26,7 +26,6 @@ export function OperationElem(props: IProps): React.ReactElement {
props.bladeburner.action.type = ActionTypes.Operation; props.bladeburner.action.type = ActionTypes.Operation;
props.bladeburner.action.name = props.action.name; props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.bladeburner.action); props.bladeburner.startAction(props.bladeburner.action);
props.bladeburner.updateActionAndSkillsContent();
setRerender(old => !old); setRerender(old => !old);
} }

@ -11,13 +11,6 @@ interface IProps {
} }
export function OperationList(props: IProps): React.ReactElement { export function OperationList(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
useEffect(() => {
const id = setInterval(() => setRerender(old => !old), 1000);
return () => clearInterval(id);
}, []);
const names = Object.keys(props.bladeburner.operations); const names = Object.keys(props.bladeburner.operations);
const operations = props.bladeburner.operations; const operations = props.bladeburner.operations;
return (<> return (<>

@ -1,10 +1,11 @@
import * as React from "react"; import React, { useState } from "react";
import { CopyableText } from "../../ui/React/CopyableText"; import { CopyableText } from "../../ui/React/CopyableText";
import { formatNumber } from "../../../utils/StringHelperFunctions"; import { formatNumber } from "../../../utils/StringHelperFunctions";
interface IProps { interface IProps {
skill: any; skill: any;
bladeburner: any; bladeburner: any;
onUpgrade: () => void;
} }
export function SkillElem(props: IProps): React.ReactElement { export function SkillElem(props: IProps): React.ReactElement {
@ -22,7 +23,7 @@ export function SkillElem(props: IProps): React.ReactElement {
if (props.bladeburner.skillPoints < pointCost) return; if (props.bladeburner.skillPoints < pointCost) return;
props.bladeburner.skillPoints -= pointCost; props.bladeburner.skillPoints -= pointCost;
props.bladeburner.upgradeSkill(props.skill); props.bladeburner.upgradeSkill(props.skill);
props.bladeburner.createActionAndSkillsContent(); props.onUpgrade();
} }
return (<> return (<>

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

@ -1,4 +1,4 @@
import * as React from "react"; import React, { useState } from "react";
import { SkillList } from "./SkillList"; import { SkillList } from "./SkillList";
import { BladeburnerConstants } from "../data/Constants"; import { BladeburnerConstants } from "../data/Constants";
import { formatNumber } from "../../../utils/StringHelperFunctions"; import { formatNumber } from "../../../utils/StringHelperFunctions";
@ -9,6 +9,7 @@ interface IProps {
export function SkillPage(props: IProps): React.ReactElement { export function SkillPage(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
const mults = props.bladeburner.skillMultipliers; const mults = props.bladeburner.skillMultipliers;
function valid(mult: any) { function valid(mult: any) {
@ -16,7 +17,9 @@ export function SkillPage(props: IProps): React.ReactElement {
} }
return (<> return (<>
<p>
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong> <strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
</p>
<p> <p>
You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks. You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
<br /> <br />
@ -24,27 +27,27 @@ export function SkillPage(props: IProps): React.ReactElement {
Note that when upgrading a skill, the benefit for that skill is additive. Note that when upgrading a skill, the benefit for that skill is additive.
However, the effects of different skills with each other is multiplicative. However, the effects of different skills with each other is multiplicative.
<br /> <br />
<br />
</p> </p>
{valid(mults["successChanceAll"]) && <>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}<br /></>}
{valid(mults["successChanceStealth"]) && <>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}<br /></>}
{valid(mults["successChanceKill"]) && <>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}<br /></>}
{valid(mults["successChanceContract"]) && <>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}<br /></>}
{valid(mults["successChanceOperation"]) && <>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}<br /></>}
{valid(mults["successChanceEstimate"]) && <>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}<br /></>}
{valid(mults["actionTime"]) && <>Action Time: x{formatNumber(mults["actionTime"], 3)}<br /></>}
{valid(mults["effHack"]) && <>Hacking Skill: x{formatNumber(mults["effHack"], 3)}<br /></>}
{valid(mults["effStr"]) && <>Strength: x{formatNumber(mults["effStr"], 3)}<br /></>}
{valid(mults["effDef"]) && <>Defense: x{formatNumber(mults["effDef"], 3)}<br /></>}
{valid(mults["effDex"]) && <>Dexterity: x{formatNumber(mults["effDex"], 3)}<br /></>}
{valid(mults["effAgi"]) && <>Agility: x{formatNumber(mults["effAgi"], 3)}<br /></>}
{valid(mults["effCha"]) && <>Charisma: x{formatNumber(mults["effCha"], 3)}<br /></>}
{valid(mults["effInt"]) && <>Intelligence: x{formatNumber(mults["effInt"], 3)}<br /></>}
{valid(mults["stamina"]) && <>Stamina: x{formatNumber(mults["stamina"], 3)}<br /></>}
{valid(mults["money"]) && <>Contract Money: x{formatNumber(mults["money"], 3)}<br /></>}
{valid(mults["expGain"]) && <>Exp Gain: x{formatNumber(mults["expGain"], 3)}<br /></>}
<br /> <br />
<SkillList bladeburner={props.bladeburner} /> {valid(mults["successChanceAll"]) && <p>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</p>}
{valid(mults["successChanceStealth"]) && <p>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</p>}
{valid(mults["successChanceKill"]) && <p>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}</p>}
{valid(mults["successChanceContract"]) && <p>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</p>}
{valid(mults["successChanceOperation"]) && <p>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}</p>}
{valid(mults["successChanceEstimate"]) && <p>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}</p>}
{valid(mults["actionTime"]) && <p>Action Time: x{formatNumber(mults["actionTime"], 3)}</p>}
{valid(mults["effHack"]) && <p>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</p>}
{valid(mults["effStr"]) && <p>Strength: x{formatNumber(mults["effStr"], 3)}</p>}
{valid(mults["effDef"]) && <p>Defense: x{formatNumber(mults["effDef"], 3)}</p>}
{valid(mults["effDex"]) && <p>Dexterity: x{formatNumber(mults["effDex"], 3)}</p>}
{valid(mults["effAgi"]) && <p>Agility: x{formatNumber(mults["effAgi"], 3)}</p>}
{valid(mults["effCha"]) && <p>Charisma: x{formatNumber(mults["effCha"], 3)}</p>}
{valid(mults["effInt"]) && <p>Intelligence: x{formatNumber(mults["effInt"], 3)}</p>}
{valid(mults["stamina"]) && <p>Stamina: x{formatNumber(mults["stamina"], 3)}</p>}
{valid(mults["money"]) && <p>Contract Money: x{formatNumber(mults["money"], 3)}</p>}
{valid(mults["expGain"]) && <p>Exp Gain: x{formatNumber(mults["expGain"], 3)}</p>}
<br />
<SkillList bladeburner={props.bladeburner} onUpgrade={() => setRerender(old => !old)} />
</>); </>);
} }

@ -52,27 +52,104 @@ export function Stats(props: IProps): React.ReactElement {
"be logged in the Bladeburner Console."); "be logged in the Bladeburner Console.");
} }
return (<p> function openTravel() {
Rank: {formatNumber(props.bladeburner.rank, 2)}<br /> // var popupId = "bladeburner-travel-popup-cancel-btn";
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)} // 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);
}
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!")
// }
// }
}
return (<>
<p className="tooltip" style={{display: 'inline-block'}}>
Rank: {formatNumber(props.bladeburner.rank, 2)}
<span className="tooltiptext">Your rank within the Bladeburner division.</span>
</p><br />
<p>Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}</p>
<div className="help-tip" onClick={openStaminaHelp}>?</div><br /> <div className="help-tip" onClick={openStaminaHelp}>?</div><br />
<p>Stamina Penalty: {formatNumber((1-props.bladeburner.calculateStaminaPenalty())*100, 1)}%</p><br />
<p>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</p>
<p>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</p><br />
<p>Num Times Hospitalized: {props.bladeburner.numHosp}</p>
<p>Money Lost From Hospitalizations: {Money(props.bladeburner.moneyLost)}</p><br />
<p>Current City: {props.bladeburner.city}</p>
<p className="tooltip" style={{display: 'inline-block'}}>
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)} Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
<span className="tooltiptext">This is your Bladeburner division's estimate of how many Synthoids exist in your current city.</span>
</p>
<div className="help-tip" onClick={openPopulationHelp}>?</div><br /> <div className="help-tip" onClick={openPopulationHelp}>?</div><br />
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}<br /> <p className="tooltip" style={{display: 'inline-block'}}>
City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}<br /> Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}<br /> <span className="tooltiptext">This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.</span>
</p><br />
<p className="tooltip" style={{display: 'inline-block'}}>
City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}
<span className="tooltiptext">The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a chaos level can make contracts and operations harder.</span>
</p><br />
<br />
<p className="tooltip" style={{display: 'inline-block'}}>
Bonus time: {convertTimeMsToTimeElapsedString(props.bladeburner.storedCycles/BladeburnerConstants.CyclesPerSecond*1000)}<br /> Bonus time: {convertTimeMsToTimeElapsedString(props.bladeburner.storedCycles/BladeburnerConstants.CyclesPerSecond*1000)}<br />
Stamina Penalty: {formatNumber((1-props.bladeburner.calculateStaminaPenalty())*100, 1)}%<br /><br /> <span className="tooltiptext">
Team Size: {formatNumber(props.bladeburner.teamSize, 0)}<br /> You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser).
Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}<br /><br /> Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
Num Times Hospitalized: {props.bladeburner.numHosp}<br /> </span>
Money Lost From Hospitalizations: {Money(props.bladeburner.moneyLost)}<br /><br /> </p>
Current City: {props.bladeburner.city}<br /> <p>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</p><br />
{StatsTable([ {StatsTable([
["Aug. Success Chance mult: ", formatNumber(props.player.bladeburner_success_chance_mult*100, 1) + "%"], ["Aug. Success Chance mult: ", formatNumber(props.player.bladeburner_success_chance_mult*100, 1) + "%"],
["Aug. Max Stamina mult: ", formatNumber(props.player.bladeburner_max_stamina_mult*100, 1) + "%"], ["Aug. Max Stamina mult: ", formatNumber(props.player.bladeburner_max_stamina_mult*100, 1) + "%"],
["Aug. Stamina Gain mult: ", formatNumber(props.player.bladeburner_stamina_gain_mult*100, 1) + "%"], ["Aug. Stamina Gain mult: ", formatNumber(props.player.bladeburner_stamina_gain_mult*100, 1) + "%"],
["Aug. Field Analysis mult: ", formatNumber(props.player.bladeburner_analysis_mult*100, 1) + "%"], ["Aug. Field Analysis mult: ", formatNumber(props.player.bladeburner_analysis_mult*100, 1) + "%"],
])} ])}
</p>); <br />
<a onClick={openTravel} className="a-link-button" style={{display:"inline-block"}}>Travel</a>
<a onClick={openFaction} className="a-link-button tooltip" style={{display:"inline-block"}}>
<span className="tooltiptext">Apply to the Bladeburner Faction, or go to the faction page if you are already a member</span>
Faction
</a>
<br />
<br />
</>);
} }