diff --git a/src/Bladeburner.jsx b/src/Bladeburner.jsx
index 93d48e668..41f4ddafc 100644
--- a/src/Bladeburner.jsx
+++ b/src/Bladeburner.jsx
@@ -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(, DomElems.actionsAndSkillsDesc);
break;
case "contracts":
- this.createContractsContent();
+ ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
+ ReactDOM.render(, DomElems.actionsAndSkillsDesc);
break;
case "operations":
- this.createOperationsContent();
+ ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
+ ReactDOM.render(, DomElems.actionsAndSkillsDesc);
break;
case "blackops":
- this.createBlackOpsContent();
+ ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
+ ReactDOM.render(, DomElems.actionsAndSkillsDesc);
break;
case "skills":
- this.createSkillsContent();
+ ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc);
+ ReactDOM.render(, 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.
" +
- "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.
" +
- "Operations can affect the chaos level and Synthoid population of your " +
- "current city. The exact effects vary between different Operations.
" +
- "For operations, you can use a team. You must first recruit team members. " +
- "Having a larger team will improves your chances of success.
" +
- "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.
" +
- "Your ultimate goal to climb through the ranks of Bladeburners is to complete " +
- "all of the Black Ops.
" +
- "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.
" +
- "Note that when upgrading a skill, the benefit for that skill is additive. " +
- "However, the effects of different skills with each other is multiplicative.
"
- 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 + "
";
- break;
- case "successChanceStealth":
- DomElems.actionsAndSkillsDesc.innerHTML += "Stealth Success Chance: x" + mult + "
";
- break;
- case "successChanceKill":
- DomElems.actionsAndSkillsDesc.innerHTML += "Retirement Success Chance: x" + mult + "
";
- break;
- case "successChanceContract":
- DomElems.actionsAndSkillsDesc.innerHTML += "Contract Success Chance: x" + mult + "
";
- break;
- case "successChanceOperation":
- DomElems.actionsAndSkillsDesc.innerHTML += "Operation Success Chance: x" + mult + "
";
- break;
- case "successChanceEstimate":
- DomElems.actionsAndSkillsDesc.innerHTML += "Synthoid Data Estimate: x" + mult + "
";
- break;
- case "actionTime":
- DomElems.actionsAndSkillsDesc.innerHTML += "Action Time: x" + mult + "
";
- break;
- case "effHack":
- DomElems.actionsAndSkillsDesc.innerHTML += "Hacking Skill: x" + mult + "
";
- break;
- case "effStr":
- DomElems.actionsAndSkillsDesc.innerHTML += "Strength: x" + mult + "
";
- break;
- case "effDef":
- DomElems.actionsAndSkillsDesc.innerHTML += "Defense: x" + mult + "
";
- break;
- case "effDex":
- DomElems.actionsAndSkillsDesc.innerHTML += "Dexterity: x" + mult + "
";
- break;
- case "effAgi":
- DomElems.actionsAndSkillsDesc.innerHTML += "Agility: x" + mult + "
";
- break;
- case "effCha":
- DomElems.actionsAndSkillsDesc.innerHTML += "Charisma: x" + mult + "
";
- break;
- case "effInt":
- DomElems.actionsAndSkillsDesc.innerHTML += "Intelligence: x" + mult + "
";
- break;
- case "stamina":
- DomElems.actionsAndSkillsDesc.innerHTML += "Stamina: x" + mult + "
";
- break;
- case "money":
- DomElems.actionsAndSkillsDesc.innerHTML += "Contract Money: x" + mult + "
";
- break;
- case "expGain":
- DomElems.actionsAndSkillsDesc.innerHTML += "Exp Gain: x" + mult + "
";
- break;
- default:
- console.warn(`Unrecognized SkillMult Key: ${multKeys[i]}`);
- break;
- }
- }
- }
-
- // Skill Points
- DomElems.skillPointsDisplay = createElement("p", {
- innerHTML:"
Skill Points: " + formatNumber(this.skillPoints, 0) + "",
- });
- 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)}%
- Team Size: {formatNumber(this.teamSize, 0)}
- Team Members Lost: {formatNumber(this.teamLost, 0)}
- Num Times Hospitalized: {this.numHosp}
- Money Lost From Hospitalizations: {Money(this.moneyLost)}
- Current City: {this.city}
- >, 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(, 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 = "
Skill Points: " + formatNumber(this.skillPoints, 0) + "";
-
- 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(, el);
-}
-
-Bladeburner.prototype.updateContractsUIElement = function(el, action) {
- ReactDOM.unmountComponentAtNode(el);
- ReactDOM.render(, el);
-}
-
-Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
- ReactDOM.unmountComponentAtNode(el);
- ReactDOM.render(, el);
-}
-
-Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
- ReactDOM.unmountComponentAtNode(el);
- ReactDOM.render(, el);
-}
-
-Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
- ReactDOM.unmountComponentAtNode(el);
- ReactDOM.render(, 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]);
}
diff --git a/src/Bladeburner/ui/BlackOpList.tsx b/src/Bladeburner/ui/BlackOpList.tsx
new file mode 100644
index 000000000..eeb0f9907
--- /dev/null
+++ b/src/Bladeburner/ui/BlackOpList.tsx
@@ -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) =>
+
+
+
+ )}
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/BlackOpPage.tsx b/src/Bladeburner/ui/BlackOpPage.tsx
new file mode 100644
index 000000000..3682a5398
--- /dev/null
+++ b/src/Bladeburner/ui/BlackOpPage.tsx
@@ -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 (<>
+
+ Black Operations (Black Ops) are special, one-time covert operations.
+ Each Black Op must be unlocked successively by completing
+ the one before it.
+
+
+ Your ultimate goal to climb through the ranks of Bladeburners is to complete
+ all of the Black Ops.
+
+
+ Like normal operations, you may use a team for Black Ops. Failing
+ a black op will incur heavy HP and rank losses.
+
+
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/ContractList.tsx b/src/Bladeburner/ui/ContractList.tsx
new file mode 100644
index 000000000..142eb5ce0
--- /dev/null
+++ b/src/Bladeburner/ui/ContractList.tsx
@@ -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) =>
+
+
+
+ )}
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/ContractPage.tsx b/src/Bladeburner/ui/ContractPage.tsx
new file mode 100644
index 000000000..b64a0b4ae
--- /dev/null
+++ b/src/Bladeburner/ui/ContractPage.tsx
@@ -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 (<>
+
+ 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.
+
+
+ You can unlock higher-level contracts by successfully completing them.
+ Higher-level contracts are more difficult, but grant more rank, experience, and money.
+
+
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/GeneralActionList.tsx b/src/Bladeburner/ui/GeneralActionList.tsx
new file mode 100644
index 000000000..15f329df9
--- /dev/null
+++ b/src/Bladeburner/ui/GeneralActionList.tsx
@@ -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) =>
+
+
+
+ )}
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/GeneralActionPage.tsx b/src/Bladeburner/ui/GeneralActionPage.tsx
new file mode 100644
index 000000000..ea1b601f3
--- /dev/null
+++ b/src/Bladeburner/ui/GeneralActionPage.tsx
@@ -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 (<>
+
+ These are generic actions that will assist you in your Bladeburner
+ duties. They will not affect your Bladeburner rank in any way.
+
+
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/OperationList.tsx b/src/Bladeburner/ui/OperationList.tsx
new file mode 100644
index 000000000..bff9cc3b7
--- /dev/null
+++ b/src/Bladeburner/ui/OperationList.tsx
@@ -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) =>
+
+
+
+ )}
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/OperationPage.tsx b/src/Bladeburner/ui/OperationPage.tsx
new file mode 100644
index 000000000..3da625131
--- /dev/null
+++ b/src/Bladeburner/ui/OperationPage.tsx
@@ -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 (<>
+
+ 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.
+
+
+ Operations can affect the chaos level and Synthoid population of your
+ current city. The exact effects vary between different Operations.
+
+
+ For operations, you can use a team. You must first recruit team members.
+ Having a larger team will improves your chances of success.
+
+
+ You can unlock higher-level operations by successfully completing them.
+ Higher-level operations are more difficult, but grant more rank and experience.
+
+
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/SkillList.tsx b/src/Bladeburner/ui/SkillList.tsx
new file mode 100644
index 000000000..014b6f992
--- /dev/null
+++ b/src/Bladeburner/ui/SkillList.tsx
@@ -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) =>
+
+
+
+ )}
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/SkillPage.tsx b/src/Bladeburner/ui/SkillPage.tsx
new file mode 100644
index 000000000..8a6e7e9bc
--- /dev/null
+++ b/src/Bladeburner/ui/SkillPage.tsx
@@ -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 (<>
+ Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}
+
+ You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
+
+
+ Note that when upgrading a skill, the benefit for that skill is additive.
+ However, the effects of different skills with each other is multiplicative.
+
+
+
+ {valid(mults["successChanceAll"]) && <>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}
>}
+ {valid(mults["successChanceStealth"]) && <>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}
>}
+ {valid(mults["successChanceKill"]) && <>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}
>}
+ {valid(mults["successChanceContract"]) && <>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}
>}
+ {valid(mults["successChanceOperation"]) && <>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}
>}
+ {valid(mults["successChanceEstimate"]) && <>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}
>}
+ {valid(mults["actionTime"]) && <>Action Time: x{formatNumber(mults["actionTime"], 3)}
>}
+ {valid(mults["effHack"]) && <>Hacking Skill: x{formatNumber(mults["effHack"], 3)}
>}
+ {valid(mults["effStr"]) && <>Strength: x{formatNumber(mults["effStr"], 3)}
>}
+ {valid(mults["effDef"]) && <>Defense: x{formatNumber(mults["effDef"], 3)}
>}
+ {valid(mults["effDex"]) && <>Dexterity: x{formatNumber(mults["effDex"], 3)}
>}
+ {valid(mults["effAgi"]) && <>Agility: x{formatNumber(mults["effAgi"], 3)}
>}
+ {valid(mults["effCha"]) && <>Charisma: x{formatNumber(mults["effCha"], 3)}
>}
+ {valid(mults["effInt"]) && <>Intelligence: x{formatNumber(mults["effInt"], 3)}
>}
+ {valid(mults["stamina"]) && <>Stamina: x{formatNumber(mults["stamina"], 3)}
>}
+ {valid(mults["money"]) && <>Contract Money: x{formatNumber(mults["money"], 3)}
>}
+ {valid(mults["expGain"]) && <>Exp Gain: x{formatNumber(mults["expGain"], 3)}
>}
+
+
+ >);
+}
+
+/*
+
+
+
+
+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]) {
+
+ }
+ }
+}
+*/
\ No newline at end of file
diff --git a/src/Bladeburner/ui/Stats.tsx b/src/Bladeburner/ui/Stats.tsx
new file mode 100644
index 000000000..9e072f189
--- /dev/null
+++ b/src/Bladeburner/ui/Stats.tsx
@@ -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.
" +
+ "Your max stamina is determined primarily by your agility stat.
" +
+ "Your stamina gain rate is determined by both your agility and your " +
+ "max stamina. Higher max stamina leads to a higher gain rate.
" +
+ "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).
" +
+ "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.
" +
+ "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.
" +
+ "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 (
+ Rank: {formatNumber(props.bladeburner.rank, 2)}
+ Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
+
?
+ Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
+ ?
+ Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
+ City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}
+ Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}
+ Bonus time: {convertTimeMsToTimeElapsedString(props.bladeburner.storedCycles/BladeburnerConstants.CyclesPerSecond*1000)}
+ Stamina Penalty: {formatNumber((1-props.bladeburner.calculateStaminaPenalty())*100, 1)}%
+ Team Size: {formatNumber(props.bladeburner.teamSize, 0)}
+ Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}
+ Num Times Hospitalized: {props.bladeburner.numHosp}
+ Money Lost From Hospitalizations: {Money(props.bladeburner.moneyLost)}
+ Current City: {props.bladeburner.city}
+ {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) + "%"],
+ ])}
+ );
+}
\ No newline at end of file
diff --git a/src/ui/CharacterInfo.tsx b/src/ui/CharacterInfo.tsx
index 91f335f8f..f89a63c0e 100644
--- a/src/ui/CharacterInfo.tsx
+++ b/src/ui/CharacterInfo.tsx
@@ -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 {
{`Servers owned: ${p.purchasedServers.length} / ${getPurchaseServerLimit()}`}
{`Augmentations installed: ${p.augmentations.length}`}
- {StatsTable(timeRows, null)}
+ {StatsTable(timeRows)}
diff --git a/src/ui/React/StatsTable.tsx b/src/ui/React/StatsTable.tsx
index 9518a9fec..b2db010e6 100644
--- a/src/ui/React/StatsTable.tsx
+++ b/src/ui/React/StatsTable.tsx
@@ -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 = <>{title}
>;
diff --git a/utils/StringHelperFunctions.ts b/utils/StringHelperFunctions.ts
index bf8b395b9..2cde62156 100644
--- a/utils/StringHelperFunctions.ts
+++ b/utils/StringHelperFunctions.ts
@@ -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,