converting more blade to react/ts

This commit is contained in:
Olivier Gagnon 2021-06-18 16:22:12 -04:00
parent 0e9d7450c9
commit 988ca37764
15 changed files with 421 additions and 351 deletions

@ -53,10 +53,22 @@ 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 { StatsTable } from "./ui/React/StatsTable";
import { CopyableText } from "./ui/React/CopyableText";
import { Money } from "./ui/React/Money";
@ -1554,19 +1566,24 @@ Bladeburner.prototype.createActionAndSkillsContent = function() {
switch(currTab) {
case "general":
this.createGeneralActionsContent();
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<GeneralActionPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "contracts":
this.createContractsContent();
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<ContractPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "operations":
this.createOperationsContent();
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<OperationPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "blackops":
this.createBlackOpsContent();
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<BlackOpPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
case "skills":
this.createSkillsContent();
ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
ReactDOM.render(<SkillPage bladeburner={this} />, DomElems.actionsAndSkillsDesc);
break;
default:
throw new Error("Invalid value for DomElems.currentTab in Bladeburner.createActionAndSkillsContent");
@ -1577,351 +1594,18 @@ Bladeburner.prototype.createActionAndSkillsContent = function() {
DomElems.actionAndSkillsDiv.appendChild(DomElems.actionsAndSkillsList);
}
Bladeburner.prototype.createGeneralActionsContent = function() {
if (DomElems.actionsAndSkillsList == null || DomElems.actionsAndSkillsDesc == null) {
throw new Error("Bladeburner.createGeneralActionsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null");
}
DomElems.actionsAndSkillsDesc.innerText =
"These are generic actions that will assist you in your Bladeburner " +
"duties. They will not affect your Bladeburner rank in any way."
for (var actionName in GeneralActions) {
if (GeneralActions.hasOwnProperty(actionName)) {
DomElems.generalActions[actionName] = createElement("div", {
class:"bladeburner-action", name:actionName,
});
DomElems.actionsAndSkillsList.appendChild(DomElems.generalActions[actionName]);
}
}
}
Bladeburner.prototype.createContractsContent = function() {
if (DomElems.actionsAndSkillsList == null || DomElems.actionsAndSkillsDesc == null) {
throw new Error("Bladeburner.createContractsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null");
}
DomElems.actionsAndSkillsDesc.innerHTML =
"Complete contracts in order to increase your Bladeburner rank and earn money. " +
"Failing a contract will cause you to lose HP, which can lead to hospitalization.<br><br>" +
"You can unlock higher-level contracts by successfully completing them. " +
"Higher-level contracts are more difficult, but grant more rank, experience, and money.";
for (var contractName in this.contracts) {
if (this.contracts.hasOwnProperty(contractName)) {
DomElems.contracts[contractName] = createElement("div", {
class:"bladeburner-action", name:contractName,
});
DomElems.actionsAndSkillsList.appendChild(DomElems.contracts[contractName]);
}
}
}
Bladeburner.prototype.createOperationsContent = function() {
if (DomElems.actionsAndSkillsList == null || DomElems.actionsAndSkillsDesc == null) {
throw new Error("Bladeburner.createOperationsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null");
}
DomElems.actionsAndSkillsDesc.innerHTML =
"Carry out operations for the Bladeburner division. " +
"Failing an operation will reduce your Bladeburner rank. It will also " +
"cause you to lose HP, which can lead to hospitalization. In general, " +
"operations are harder and more punishing than contracts, " +
"but are also more rewarding.<br><br>" +
"Operations can affect the chaos level and Synthoid population of your " +
"current city. The exact effects vary between different Operations.<br><br>" +
"For operations, you can use a team. You must first recruit team members. " +
"Having a larger team will improves your chances of success.<br><br>" +
"You can unlock higher-level operations by successfully completing them. " +
"Higher-level operations are more difficult, but grant more rank and experience.";
for (var operationName in this.operations) {
if (this.operations.hasOwnProperty(operationName)) {
DomElems.operations[operationName] = createElement("div", {
class:"bladeburner-action", name:operationName,
});
DomElems.actionsAndSkillsList.appendChild(DomElems.operations[operationName]);
}
}
}
Bladeburner.prototype.createBlackOpsContent = function() {
if (DomElems.actionsAndSkillsList == null || DomElems.actionsAndSkillsDesc == null) {
throw new Error("Bladeburner.createBlackOpsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null");
}
DomElems.actionsAndSkillsDesc.innerHTML =
"Black Operations (Black Ops) are special, one-time covert operations. " +
"Each Black Op must be unlocked successively by completing " +
"the one before it.<br><br>" +
"<b>Your ultimate goal to climb through the ranks of Bladeburners is to complete " +
"all of the Black Ops.</b><br><br>" +
"Like normal operations, you may use a team for Black Ops. Failing " +
"a black op will incur heavy HP and rank losses.";
// Put Black Operations in sequence of required rank
var blackops = [];
for (var blackopName in BlackOperations) {
if (BlackOperations.hasOwnProperty(blackopName)) {
blackops.push(BlackOperations[blackopName]);
}
}
blackops.sort(function(a, b) {
return (a.reqdRank - b.reqdRank);
});
for (var i = blackops.length-1; i >= 0 ; --i) {
if (this.blackops[[blackops[i].name]] == null && i !== 0 && this.blackops[[blackops[i-1].name]] == null) {continue;} // If this one nor the next are completed then this isn't unlocked yet.
DomElems.blackops[blackops[i].name] = createElement("div", {
class:"bladeburner-action", name:blackops[i].name,
});
DomElems.actionsAndSkillsList.appendChild(DomElems.blackops[blackops[i].name]);
}
}
Bladeburner.prototype.createSkillsContent = function() {
if (DomElems.actionsAndSkillsList == null || DomElems.actionsAndSkillsDesc == null) {
throw new Error("Bladeburner.createSkillsContent called with either " +
"DomElems.actionsAndSkillsList or DomElems.actionsAndSkillsDesc = null");
}
// Display Current multipliers
DomElems.actionsAndSkillsDesc.innerHTML =
"You will gain one skill point every " + BladeburnerConstants.RanksPerSkillPoint + " ranks.<br><br>" +
"Note that when upgrading a skill, the benefit for that skill is additive. " +
"However, the effects of different skills with each other is multiplicative.<br><br>"
var multKeys = Object.keys(this.skillMultipliers);
for (var i = 0; i < multKeys.length; ++i) {
var mult = this.skillMultipliers[multKeys[i]];
if (mult && mult !== 1) {
mult = formatNumber(mult, 3);
switch(multKeys[i]) {
case "successChanceAll":
DomElems.actionsAndSkillsDesc.innerHTML += "Total Success Chance: x" + mult + "<br>";
break;
case "successChanceStealth":
DomElems.actionsAndSkillsDesc.innerHTML += "Stealth Success Chance: x" + mult + "<br>";
break;
case "successChanceKill":
DomElems.actionsAndSkillsDesc.innerHTML += "Retirement Success Chance: x" + mult + "<br>";
break;
case "successChanceContract":
DomElems.actionsAndSkillsDesc.innerHTML += "Contract Success Chance: x" + mult + "<br>";
break;
case "successChanceOperation":
DomElems.actionsAndSkillsDesc.innerHTML += "Operation Success Chance: x" + mult + "<br>";
break;
case "successChanceEstimate":
DomElems.actionsAndSkillsDesc.innerHTML += "Synthoid Data Estimate: x" + mult + "<br>";
break;
case "actionTime":
DomElems.actionsAndSkillsDesc.innerHTML += "Action Time: x" + mult + "<br>";
break;
case "effHack":
DomElems.actionsAndSkillsDesc.innerHTML += "Hacking Skill: x" + mult + "<br>";
break;
case "effStr":
DomElems.actionsAndSkillsDesc.innerHTML += "Strength: x" + mult + "<br>";
break;
case "effDef":
DomElems.actionsAndSkillsDesc.innerHTML += "Defense: x" + mult + "<br>";
break;
case "effDex":
DomElems.actionsAndSkillsDesc.innerHTML += "Dexterity: x" + mult + "<br>";
break;
case "effAgi":
DomElems.actionsAndSkillsDesc.innerHTML += "Agility: x" + mult + "<br>";
break;
case "effCha":
DomElems.actionsAndSkillsDesc.innerHTML += "Charisma: x" + mult + "<br>";
break;
case "effInt":
DomElems.actionsAndSkillsDesc.innerHTML += "Intelligence: x" + mult + "<br>";
break;
case "stamina":
DomElems.actionsAndSkillsDesc.innerHTML += "Stamina: x" + mult + "<br>";
break;
case "money":
DomElems.actionsAndSkillsDesc.innerHTML += "Contract Money: x" + mult + "<br>";
break;
case "expGain":
DomElems.actionsAndSkillsDesc.innerHTML += "Exp Gain: x" + mult + "<br>";
break;
default:
console.warn(`Unrecognized SkillMult Key: ${multKeys[i]}`);
break;
}
}
}
// Skill Points
DomElems.skillPointsDisplay = createElement("p", {
innerHTML:"<br><strong>Skill Points: " + formatNumber(this.skillPoints, 0) + "</strong>",
});
DomElems.actionAndSkillsDiv.appendChild(DomElems.skillPointsDisplay);
// UI Element for each skill
for (var skillName in Skills) {
if (Skills.hasOwnProperty(skillName)) {
DomElems.skills[skillName] = createElement("div", {
class:"bladeburner-action", name:skillName,
});
DomElems.actionsAndSkillsList.appendChild(DomElems.skills[skillName]);
}
}
}
Bladeburner.prototype.updateContent = function() {
this.updateOverviewContent();
this.updateActionAndSkillsContent();
}
Bladeburner.prototype.updateOverviewContent = function() {
if (!routing.isOn(Page.Bladeburner)) {return;}
DomElems.overviewRank.childNodes[0].nodeValue = "Rank: " + formatNumber(this.rank, 2);
DomElems.overviewStamina.innerText = "Stamina: " + formatNumber(this.stamina, 3) + " / " + formatNumber(this.maxStamina, 3);
ReactDOM.render(<>
Stamina Penalty: {formatNumber((1-this.calculateStaminaPenalty())*100, 1)}%<br /><br />
Team Size: {formatNumber(this.teamSize, 0)}<br />
Team Members Lost: {formatNumber(this.teamLost, 0)}<br /><br />
Num Times Hospitalized: {this.numHosp}<br />
Money Lost From Hospitalizations: {Money(this.moneyLost)}<br /><br />
Current City: {this.city}<br />
</>, DomElems.overviewGen1);
DomElems.overviewEstPop.childNodes[0].nodeValue = "Est. Synthoid Population: " + numeralWrapper.formatPopulation(this.getCurrentCity().popEst);
DomElems.overviewEstComms.childNodes[0].nodeValue = "Est. Synthoid Communities: " + formatNumber(this.getCurrentCity().comms, 0);
DomElems.overviewChaos.childNodes[0].nodeValue = "City Chaos: " + formatNumber(this.getCurrentCity().chaos);
DomElems.overviewSkillPoints.innerText = "Skill Points: " + formatNumber(this.skillPoints, 0);
DomElems.overviewBonusTime.childNodes[0].nodeValue = "Bonus time: " + convertTimeMsToTimeElapsedString(this.storedCycles/BladeburnerConstants.CyclesPerSecond*1000);
ReactDOM.render(StatsTable([
["Aug. Success Chance mult: ", formatNumber(Player.bladeburner_success_chance_mult*100, 1) + "%"],
["Aug. Max Stamina mult: ", formatNumber(Player.bladeburner_max_stamina_mult*100, 1) + "%"],
["Aug. Stamina Gain mult: ", formatNumber(Player.bladeburner_stamina_gain_mult*100, 1) + "%"],
["Aug. Field Analysis mult: ", formatNumber(Player.bladeburner_analysis_mult*100, 1) + "%"],
]), DomElems.overviewAugMults);
if (!routing.isOn(Page.Bladeburner)) return;
ReactDOM.render(<Stats bladeburner={this} player={Player} />, DomElems.overviewDiv);
}
Bladeburner.prototype.updateActionAndSkillsContent = function() {
if (DomElems.currentTab == null) {DomElems.currentTab = "general";}
switch(DomElems.currentTab.toLowerCase()) {
case "general":
var actionElems = Object.keys(DomElems.generalActions);
for (var i = 0; i < actionElems.length; ++i) {
var actionElem = DomElems.generalActions[actionElems[i]];
var name = actionElem.name;
var actionObj = GeneralActions[name];
if (actionObj == null) {
throw new Error("Could not find Object " + name + " in Bladeburner.updateActionAndSkillsContent()");
}
if (this.action.type === ActionTypes[name]) {
actionElem.classList.add(ActiveActionCssClass);
} else {
actionElem.classList.remove(ActiveActionCssClass);
}
this.updateGeneralActionsUIElement(actionElem, actionObj);
}
break;
case "contracts":
var contractElems = Object.keys(DomElems.contracts);
for (var i = 0; i < contractElems.length; ++i) {
var contractElem = DomElems.contracts[contractElems[i]];
var name = contractElem.name;
if (this.action.type === ActionTypes["Contract"] && name === this.action.name) {
contractElem.classList.add(ActiveActionCssClass);
} else {
contractElem.classList.remove(ActiveActionCssClass);
}
var contract = this.contracts[name];
if (contract == null) {
throw new Error("Could not find Contract " + name + " in Bladeburner.updateActionAndSkillsContent()");
}
this.updateContractsUIElement(contractElem, contract);
}
break;
case "operations":
var operationElems = Object.keys(DomElems.operations);
for (var i = 0; i < operationElems.length; ++i) {
var operationElem = DomElems.operations[operationElems[i]];
var name = operationElem.name;
if (this.action.type === ActionTypes["Operation"] && name === this.action.name) {
operationElem.classList.add(ActiveActionCssClass);
} else {
operationElem.classList.remove(ActiveActionCssClass);
}
var operation = this.operations[name];
if (operation == null) {
throw new Error("Could not find Operation " + name + " in Bladeburner.updateActionAndSkillsContent()");
}
this.updateOperationsUIElement(operationElem, operation);
}
break;
case "blackops":
var blackopsElems = Object.keys(DomElems.blackops);
for (var i = 0; i < blackopsElems.length; ++i) {
var blackopElem = DomElems.blackops[blackopsElems[i]];
var name = blackopElem.name;
if (this.action.type === ActionTypes["BlackOperation"] && name === this.action.name) {
blackopElem.classList.add(ActiveActionCssClass);
} else {
blackopElem.classList.remove(ActiveActionCssClass);
}
var blackop = BlackOperations[name];
if (blackop == null) {
throw new Error("Could not find BlackOperation " + name + " in Bladeburner.updateActionAndSkillsContent()");
}
this.updateBlackOpsUIElement(blackopElem, blackop);
}
break;
case "skills":
DomElems.skillPointsDisplay.innerHTML = "<br><strong>Skill Points: " + formatNumber(this.skillPoints, 0) + "</strong>";
var skillElems = Object.keys(DomElems.skills);
for (var i = 0; i < skillElems.length; ++i) {
var skillElem = DomElems.skills[skillElems[i]];
var name = skillElem.name;
var skill = Skills[name];
if (skill == null) {
throw new Error("Could not find Skill " + name + " in Bladeburner.updateActionAndSkillsContent()");
}
this.updateSkillsUIElement(skillElem, skill);
}
break;
default:
throw new Error("Invalid value for DomElems.currentTab in Bladeburner.createActionAndSkillsContent");
}
}
Bladeburner.prototype.updateGeneralActionsUIElement = function(el, action) {
ReactDOM.unmountComponentAtNode(el);
ReactDOM.render(<GeneralActionElem bladeburner={this} action={action} />, el);
}
Bladeburner.prototype.updateContractsUIElement = function(el, action) {
ReactDOM.unmountComponentAtNode(el);
ReactDOM.render(<ContractElem bladeburner={this} action={action} />, el);
}
Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
ReactDOM.unmountComponentAtNode(el);
ReactDOM.render(<OperationElem bladeburner={this} action={action} />, el);
}
Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
ReactDOM.unmountComponentAtNode(el);
ReactDOM.render(<BlackOpElem bladeburner={this} action={action} />, el);
}
Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
ReactDOM.unmountComponentAtNode(el);
ReactDOM.render(<SkillElem bladeburner={this} skill={skill} />, el);
}
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////HYDRO END OF UI//////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Bladeburner Console Window
Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) {
@ -2382,7 +2066,6 @@ Bladeburner.prototype.executeStartConsoleCommand = function(args) {
this.action.type = ActionTypes[name];
this.action.name = name;
this.startAction(this.action);
this.updateActionAndSkillsContent();
} else {
this.postToConsole("Invalid action name specified: " + args[2]);
}
@ -2393,7 +2076,6 @@ Bladeburner.prototype.executeStartConsoleCommand = function(args) {
this.action.type = ActionTypes.Contract;
this.action.name = name;
this.startAction(this.action);
this.updateActionAndSkillsContent();
} else {
this.postToConsole("Invalid contract name specified: " + args[2]);
}
@ -2406,7 +2088,6 @@ Bladeburner.prototype.executeStartConsoleCommand = function(args) {
this.action.type = ActionTypes.Operation;
this.action.name = name;
this.startAction(this.action);
this.updateActionAndSkillsContent();
} else {
this.postToConsole("Invalid Operation name specified: " + args[2]);
}
@ -2419,7 +2100,6 @@ Bladeburner.prototype.executeStartConsoleCommand = function(args) {
this.action.type = ActionTypes.BlackOperation;
this.action.name = name;
this.startAction(this.action);
this.updateActionAndSkillsContent();
} else {
this.postToConsole("Invalid BlackOp name specified: " + args[2]);
}

@ -0,0 +1,42 @@
import * as React from "react";
import {
formatNumber,
convertTimeMsToTimeElapsedString,
} from "../../../utils/StringHelperFunctions";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
import { stealthIcon, killIcon } from "../data/Icons";
import { BlackOperations } from "../BlackOperations";
import { BlackOperation } from "../BlackOperation";
import { BlackOpElem } from "./BlackOpElem";
interface IProps {
bladeburner: any;
}
export function BlackOpList(props: IProps): React.ReactElement {
let blackops: BlackOperation[] = [];
for (const blackopName in BlackOperations) {
if (BlackOperations.hasOwnProperty(blackopName)) {
blackops.push(BlackOperations[blackopName]);
}
}
blackops.sort(function(a, b) {
return (a.reqdRank - b.reqdRank);
});
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">
<BlackOpElem bladeburner={props.bladeburner} action={blackop} />
</li>
)}
</>);
}

@ -0,0 +1,25 @@
import * as React from "react";
import { BlackOpList } from "./BlackOpList";
interface IProps {
bladeburner: any;
}
export function BlackOpPage(props: IProps): React.ReactElement {
return (<>
<p style={{display: 'block', margin: '4px', padding: '4px'}}>
Black Operations (Black Ops) are special, one-time covert operations.
Each Black Op must be unlocked successively by completing
the one before it.
<br />
<br />
<b>Your ultimate goal to climb through the ranks of Bladeburners is to complete
all of the Black Ops.</b>
<br />
<br />
Like normal operations, you may use a team for Black Ops. Failing
a black op will incur heavy HP and rank losses.
</p>
<BlackOpList bladeburner={props.bladeburner} />
</>);
}

@ -0,0 +1,30 @@
import React, { useState, useEffect } from "react";
import {
formatNumber,
convertTimeMsToTimeElapsedString,
} from "../../../utils/StringHelperFunctions";
import { ContractElem } from "./ContractElem";
import { Contract } from "../Contract";
interface IProps {
bladeburner: any;
}
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 contracts = props.bladeburner.contracts;
return (<>
{names.map((name: string) =>
<li key={name} className="bladeburner-action">
<ContractElem bladeburner={props.bladeburner} action={contracts[name]} />
</li>
)}
</>);
}

@ -0,0 +1,20 @@
import * as React from "react";
import { ContractList } from "./ContractList";
interface IProps {
bladeburner: any;
}
export function ContractPage(props: IProps): React.ReactElement {
return (<>
<p style={{display: 'block', margin: '4px', padding: '4px'}}>
Complete contracts in order to increase your Bladeburner rank and earn money.
Failing a contract will cause you to lose HP, which can lead to hospitalization.
<br />
<br />
You can unlock higher-level contracts by successfully completing them.
Higher-level contracts are more difficult, but grant more rank, experience, and money.
</p>
<ContractList bladeburner={props.bladeburner} />
</>);
}

@ -0,0 +1,35 @@
import React, { useState, useEffect } from "react";
import {
formatNumber,
convertTimeMsToTimeElapsedString,
} from "../../../utils/StringHelperFunctions";
import { GeneralActionElem } from "./GeneralActionElem";
import { Action } from "../Action";
import { GeneralActions } from "../GeneralActions";
interface IProps {
bladeburner: any;
}
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[] = [];
for (const name in GeneralActions) {
if (GeneralActions.hasOwnProperty(name)) {
actions.push(GeneralActions[name]);
}
}
return (<>
{actions.map((action: Action) =>
<li key={action.name} className="bladeburner-action">
<GeneralActionElem bladeburner={props.bladeburner} action={action} />
</li>
)}
</>);
}

@ -0,0 +1,16 @@
import * as React from "react";
import { GeneralActionList } from "./GeneralActionList";
interface IProps {
bladeburner: any;
}
export function GeneralActionPage(props: IProps): React.ReactElement {
return (<>
<p style={{display: 'block', margin: '4px', padding: '4px'}}>
These are generic actions that will assist you in your Bladeburner
duties. They will not affect your Bladeburner rank in any way.
</p>
<GeneralActionList bladeburner={props.bladeburner} />
</>);
}

@ -0,0 +1,30 @@
import React, { useState, useEffect } from "react";
import {
formatNumber,
convertTimeMsToTimeElapsedString,
} from "../../../utils/StringHelperFunctions";
import { OperationElem } from "./OperationElem";
import { Operation } from "../Operation";
interface IProps {
bladeburner: any;
}
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 operations = props.bladeburner.operations;
return (<>
{names.map((name: string) =>
<li key={name} className="bladeburner-action">
<OperationElem bladeburner={props.bladeburner} action={operations[name]} />
</li>
)}
</>);
}

@ -0,0 +1,31 @@
import * as React from "react";
import { OperationList } from "./OperationList";
interface IProps {
bladeburner: any;
}
export function OperationPage(props: IProps): React.ReactElement {
return (<>
<p style={{display: 'block', margin: '4px', padding: '4px'}}>
Carry out operations for the Bladeburner division.
Failing an operation will reduce your Bladeburner rank. It will also
cause you to lose HP, which can lead to hospitalization. In general,
operations are harder and more punishing than contracts,
but are also more rewarding.
<br />
<br />
Operations can affect the chaos level and Synthoid population of your
current city. The exact effects vary between different Operations.
<br />
<br />
For operations, you can use a team. You must first recruit team members.
Having a larger team will improves your chances of success.
<br />
<br />
You can unlock higher-level operations by successfully completing them.
Higher-level operations are more difficult, but grant more rank and experience.
</p>
<OperationList bladeburner={props.bladeburner} />
</>);
}

@ -0,0 +1,17 @@
import * as React from "react";
import { SkillElem } from "./SkillElem";
import { Skills } from "../Skills";
interface IProps {
bladeburner: any;
}
export function SkillList(props: IProps): React.ReactElement {
return (<>
{Object.keys(Skills).map((skill: string) =>
<li key={skill} className="bladeburner-action">
<SkillElem bladeburner={props.bladeburner} skill={Skills[skill]} />
</li>
)}
</>);
}

@ -0,0 +1,66 @@
import * as React from "react";
import { SkillList } from "./SkillList";
import { BladeburnerConstants } from "../data/Constants";
import { formatNumber } from "../../../utils/StringHelperFunctions";
interface IProps {
bladeburner: any;
}
export function SkillPage(props: IProps): React.ReactElement {
const mults = props.bladeburner.skillMultipliers;
function valid(mult: any) {
return mult && mult !== 1
}
return (<>
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
<p>
You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
<br />
<br />
Note that when upgrading a skill, the benefit for that skill is additive.
However, the effects of different skills with each other is multiplicative.
<br />
<br />
</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 />
<SkillList bladeburner={props.bladeburner} />
</>);
}
/*
var multKeys = Object.keys(this.skillMultipliers);
for (var i = 0; i < multKeys.length; ++i) {
var mult = this.skillMultipliers[multKeys[i]];
if (mult && mult !== 1) {
mult = formatNumber(mult, 3);
switch(multKeys[i]) {
}
}
}
*/

@ -0,0 +1,78 @@
import React, { useState, useEffect } from "react";
import {
formatNumber,
convertTimeMsToTimeElapsedString,
} from "../../../utils/StringHelperFunctions";
import { BladeburnerConstants } from "../data/Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money";
import { StatsTable } from "../../ui/React/StatsTable";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../../utils/DialogBox";
interface IProps {
bladeburner: any;
player: IPlayer;
}
export function Stats(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
useEffect(() => {
const id = setInterval(() => setRerender(old => !old), 1000);
return () => clearInterval(id);
}, []);
function openStaminaHelp(): void {
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.");
}
function openPopulationHelp(): void {
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.");
}
return (<p>
Rank: {formatNumber(props.bladeburner.rank, 2)}<br />
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
<div className="help-tip" onClick={openStaminaHelp}>?</div><br />
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
<div className="help-tip" onClick={openPopulationHelp}>?</div><br />
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}<br />
City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}<br />
Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}<br />
Bonus time: {convertTimeMsToTimeElapsedString(props.bladeburner.storedCycles/BladeburnerConstants.CyclesPerSecond*1000)}<br />
Stamina Penalty: {formatNumber((1-props.bladeburner.calculateStaminaPenalty())*100, 1)}%<br /><br />
Team Size: {formatNumber(props.bladeburner.teamSize, 0)}<br />
Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}<br /><br />
Num Times Hospitalized: {props.bladeburner.numHosp}<br />
Money Lost From Hospitalizations: {Money(props.bladeburner.moneyLost)}<br /><br />
Current City: {props.bladeburner.city}<br />
{StatsTable([
["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. Stamina Gain mult: ", formatNumber(props.player.bladeburner_stamina_gain_mult*100, 1) + "%"],
["Aug. Field Analysis mult: ", formatNumber(props.player.bladeburner_analysis_mult*100, 1) + "%"],
])}
</p>);
}

@ -63,7 +63,7 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
if (src.casino) { parts.push([`Casino:`, Money(src.casino)]) }
if (src.sleeves) { parts.push([`Sleeves:`, Money(src.sleeves)]) }
return StatsTable(parts, "");
return StatsTable(parts);
}
function openMoneyModal(): void {
@ -254,7 +254,7 @@ export function CharacterInfo(p: IPlayer): React.ReactElement {
<span>{`Servers owned: ${p.purchasedServers.length} / ${getPurchaseServerLimit()}`}</span><br />
<Hacknet />
<span>{`Augmentations installed: ${p.augmentations.length}`}</span><br /><br />
{StatsTable(timeRows, null)}
{StatsTable(timeRows)}
<br />
<CurrentBitNode />
</pre>

@ -1,6 +1,6 @@
import * as React from "react";
export function StatsTable(rows: any[][], title: string | null): React.ReactElement {
export function StatsTable(rows: any[][], title?: string): React.ReactElement {
let titleElem = <></>
if (title) {
titleElem = <><h2><u>{title}</u></h2><br /></>;

@ -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): string {
function formatNumber(num: number, numFractionDigits: number = 0): string {
return num.toLocaleString(undefined, {
maximumFractionDigits: numFractionDigits,
minimumFractionDigits: numFractionDigits,