diff --git a/src/Bladeburner.jsx b/src/Bladeburner.jsx
index 2a3316fd1..93d48e668 100644
--- a/src/Bladeburner.jsx
+++ b/src/Bladeburner.jsx
@@ -52,6 +52,11 @@ import { createPopup } from "../utils/uiHelpers/createPopup";
import { removeElement } from "../utils/uiHelpers/removeElement";
import { removeElementById } from "../utils/uiHelpers/removeElementById";
+import { SkillElem } from "./Bladeburner/ui/SkillElem";
+import { BlackOpElem } from "./Bladeburner/ui/BlackOpElem";
+import { OperationElem } from "./Bladeburner/ui/OperationElem";
+import { ContractElem } from "./Bladeburner/ui/ContractElem";
+import { GeneralActionElem } from "./Bladeburner/ui/GeneralActionElem";
import { StatsTable } from "./ui/React/StatsTable";
import { CopyableText } from "./ui/React/CopyableText";
import { Money } from "./ui/React/Money";
@@ -1894,442 +1899,28 @@ Bladeburner.prototype.updateActionAndSkillsContent = function() {
}
Bladeburner.prototype.updateGeneralActionsUIElement = function(el, action) {
- removeChildrenFromElement(el);
- var isActive = el.classList.contains(ActiveActionCssClass);
- var computedActionTimeCurrent = Math.min(this.actionTimeCurrent+this.actionTimeOverflow,this.actionTimeToComplete);
-
- el.appendChild(createElement("h2", { // Header
- innerText:isActive ? action.name + " (IN PROGRESS - " +
- formatNumber(computedActionTimeCurrent, 0) + " / " +
- formatNumber(this.actionTimeToComplete, 0) + ")"
- : action.name,
- display:"inline-block",
- }));
-
- if (isActive) { // Progress bar if its active
- var progress = computedActionTimeCurrent / this.actionTimeToComplete;
- el.appendChild(createElement("p", {
- display:"block",
- innerText:createProgressBarText({progress:progress}),
- }));
- } else {
- // Start button
- el.appendChild(createElement("a", {
- innerText:"Start", class: "a-link-button",
- margin:"3px", padding:"3px",
- clickListener:() => {
- this.action.type = ActionTypes[action.name];
- this.action.name = action.name;
- this.startAction(this.action);
- this.updateActionAndSkillsContent();
- return false;
- },
- }));
- }
-
- appendLineBreaks(el, 2);
- el.appendChild(createElement("pre", { // Info
- innerHTML:action.desc, display:"inline-block",
- }));
-
-
+ ReactDOM.unmountComponentAtNode(el);
+ ReactDOM.render( , el);
}
Bladeburner.prototype.updateContractsUIElement = function(el, action) {
- removeChildrenFromElement(el);
- var isActive = el.classList.contains(ActiveActionCssClass);
- var estimatedSuccessChance = action.getSuccessChance(this, {est:true});
- var computedActionTimeCurrent = Math.min(this.actionTimeCurrent+this.actionTimeOverflow,this.actionTimeToComplete);
-
- el.appendChild(createElement("h2", { // Header
- innerText:isActive ? action.name + " (IN PROGRESS - " +
- formatNumber(computedActionTimeCurrent, 0) + " / " +
- formatNumber(this.actionTimeToComplete, 0) + ")"
- : action.name,
- display:"inline-block",
- }));
-
- if (isActive) { // Progress bar if its active
- var progress = computedActionTimeCurrent / this.actionTimeToComplete;
- el.appendChild(createElement("p", {
- display:"block",
- innerText:createProgressBarText({progress:progress}),
- }));
- } else { // Start button
- el.appendChild(createElement("a", {
- innerText:"Start", class: "a-link-button",
- padding:"3px", margin:"3px",
- clickListener:() => {
- this.action.type = ActionTypes.Contract;
- this.action.name = action.name;
- this.startAction(this.action);
- this.updateActionAndSkillsContent();
- return false;
- },
- }));
- }
-
- // Level and buttons to change level
- var maxLevel = (action.level >= action.maxLevel);
- appendLineBreaks(el, 2);
- el.appendChild(createElement("pre", {
- display:"inline-block",
- innerText:"Level: " + action.level + " / " + action.maxLevel,
- tooltip:action.getSuccessesNeededForNextLevel(BladeburnerConstants.ContractSuccessesPerLevel) + " successes " +
- "needed for next level",
- }));
- el.appendChild(createElement("a", {
- class: maxLevel ? "a-link-button-inactive" : "a-link-button", innerHTML:"↑",
- padding:"2px", margin:"2px",
- tooltip: isActive ? "WARNING: changing the level will restart the contract" : "",
- display:"inline",
- clickListener:() => {
- ++action.level;
- if (isActive) {this.startAction(this.action);} // Restart Action
- this.updateContractsUIElement(el, action);
- return false;
- },
- }));
- el.appendChild(createElement("a", {
- class: (action.level <= 1) ? "a-link-button-inactive" : "a-link-button", innerHTML:"↓",
- padding:"2px", margin:"2px",
- tooltip: isActive ? "WARNING: changing the level will restart the contract" : "",
- display:"inline",
- clickListener:() => {
- --action.level;
- if (isActive) {this.startAction(this.action);} // Restart Action
- this.updateContractsUIElement(el, action);
- return false;
- },
- }));
-
- var actionTime = action.getActionTime(this);
- appendLineBreaks(el, 2);
- el.appendChild(createElement("pre", { // Info
- display:"inline-block",
- innerHTML:action.desc + "\n\n" +
- `Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
- "Time Required: " + convertTimeMsToTimeElapsedString(actionTime*1000) + "\n" +
- "Contracts remaining: " + Math.floor(action.count) + "\n" +
- "Successes: " + action.successes + "\n" +
- "Failures: " + action.failures,
- }));
-
- // Autolevel Checkbox
- el.appendChild(createElement("br"));
- var autolevelCheckboxId = "bladeburner-" + action.name + "-autolevel-checkbox";
- el.appendChild(createElement("label", {
- for:autolevelCheckboxId, innerText:"Autolevel: ",color:"white",
- tooltip:"Automatically increase contract level when possible",
- }));
-
- const checkboxInput = createElement("input", {
- type:"checkbox",
- id: autolevelCheckboxId,
- checked: action.autoLevel,
- changeListener: () => {
- action.autoLevel = checkboxInput.checked;
- },
- });
-
- el.appendChild(checkboxInput);
+ ReactDOM.unmountComponentAtNode(el);
+ ReactDOM.render( , el);
}
Bladeburner.prototype.updateOperationsUIElement = function(el, action) {
- removeChildrenFromElement(el);
- var isActive = el.classList.contains(ActiveActionCssClass);
- var estimatedSuccessChance = action.getSuccessChance(this, {est:true});
- var computedActionTimeCurrent = Math.min(this.actionTimeCurrent+this.actionTimeOverflow,this.actionTimeToComplete);
-
- el.appendChild(createElement("h2", { // Header
- innerText:isActive ? action.name + " (IN PROGRESS - " +
- formatNumber(computedActionTimeCurrent, 0) + " / " +
- formatNumber(this.actionTimeToComplete, 0) + ")"
- : action.name,
- display:"inline-block",
- }));
-
- if (isActive) { // Progress bar if its active
- var progress = computedActionTimeCurrent / this.actionTimeToComplete;
- el.appendChild(createElement("p", {
- display:"block",
- innerText:createProgressBarText({progress:progress}),
- }));
- } else { // Start button and set Team Size button
- el.appendChild(createElement("a", {
- innerText:"Start", class: "a-link-button",
- margin:"3px", padding:"3px",
- clickListener:() => {
- this.action.type = ActionTypes.Operation;
- this.action.name = action.name;
- this.startAction(this.action);
- this.updateActionAndSkillsContent();
- return false;
- },
- }));
- el.appendChild(createElement("a", {
- innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button",
- margin:"3px", padding:"3px",
- clickListener:() => {
- var popupId = "bladeburner-operation-set-team-size-popup";
- var txt = createElement("p", {
- innerText:"Enter the amount of team members you would like to take on these " +
- "operations. If you do not have the specified number of team members, " +
- "then as many as possible will be used. Note that team members may " +
- "be lost during operations.",
-
- });
- var input = createElement("input", {
- type:"number", placeholder: "Team size", class: "text-input",
- });
- var setBtn = createElement("a", {
- innerText:"Confirm", class:"a-link-button",
- clickListener:() => {
- var num = Math.round(parseFloat(input.value));
- if (isNaN(num) || num < 0) {
- dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)")
- } else {
- action.teamCount = num;
- this.updateOperationsUIElement(el, action);
- }
- removeElementById(popupId);
- return false;
- },
- });
- var cancelBtn = createElement("a", {
- innerText:"Cancel", class:"a-link-button",
- clickListener:() => {
- removeElementById(popupId);
- return false;
- },
- });
- createPopup(popupId, [txt, input, setBtn, cancelBtn]);
- input.focus();
- },
- }));
- }
-
- // Level and buttons to change level
- var maxLevel = (action.level >= action.maxLevel);
- appendLineBreaks(el, 2);
- el.appendChild(createElement("pre", {
- display:"inline-block",
- innerText:"Level: " + action.level + " / " + action.maxLevel,
- tooltip:action.getSuccessesNeededForNextLevel(BladeburnerConstants.OperationSuccessesPerLevel) + " successes " +
- "needed for next level",
- }));
- el.appendChild(createElement("a", {
- class: maxLevel ? "a-link-button-inactive" : "a-link-button", innerHTML:"↑",
- padding:"2px", margin:"2px",
- tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "",
- display:"inline",
- clickListener:() => {
- ++action.level;
- if (isActive) {this.startAction(this.action);} // Restart Action
- this.updateOperationsUIElement(el, action);
- return false;
- },
- }));
- el.appendChild(createElement("a", {
- class: (action.level <= 1) ? "a-link-button-inactive" : "a-link-button", innerHTML:"↓",
- padding:"2px", margin:"2px",
- tooltip: isActive ? "WARNING: changing the level will restart the Operation" : "",
- display:"inline",
- clickListener:() => {
- --action.level;
- if (isActive) {this.startAction(this.action);} // Restart Action
- this.updateOperationsUIElement(el, action);
- return false;
- },
- }));
-
- // General Info
- var actionTime = action.getActionTime(this);
- appendLineBreaks(el, 2);
- el.appendChild(createElement("pre", {
- display:"inline-block",
- innerHTML:action.desc + "\n\n" +
- `Estimated success chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
- "Time Required: " + convertTimeMsToTimeElapsedString(actionTime*1000) + "\n" +
- "Operations remaining: " + Math.floor(action.count) + "\n" +
- "Successes: " + action.successes + "\n" +
- "Failures: " + action.failures,
- }));
-
- // Autolevel Checkbox
- el.appendChild(createElement("br"));
- var autolevelCheckboxId = "bladeburner-" + action.name + "-autolevel-checkbox";
- el.appendChild(createElement("label", {
- for:autolevelCheckboxId, innerText:"Autolevel: ",color:"white",
- tooltip:"Automatically increase operation level when possible",
- }));
-
- const checkboxInput = createElement("input", {
- type:"checkbox",
- id: autolevelCheckboxId,
- checked: action.autoLevel,
- changeListener: () => {
- action.autoLevel = checkboxInput.checked;
- },
- });
-
- el.appendChild(checkboxInput);
+ ReactDOM.unmountComponentAtNode(el);
+ ReactDOM.render( , el);
}
Bladeburner.prototype.updateBlackOpsUIElement = function(el, action) {
- removeChildrenFromElement(el);
- var isActive = el.classList.contains(ActiveActionCssClass);
- var isCompleted = (this.blackops[action.name] != null);
- var estimatedSuccessChance = action.getSuccessChance(this, {est:true});
- var actionTime = action.getActionTime(this);
- var hasReqdRank = this.rank >= action.reqdRank;
- var computedActionTimeCurrent = Math.min(this.actionTimeCurrent+this.actionTimeOverflow,this.actionTimeToComplete);
-
- // UI for Completed Black Op
- if (isCompleted) {
- el.appendChild(createElement("h2", {
- innerText:action.name + " (COMPLETED)", display:"block",
- }));
- return;
- }
-
- el.appendChild(createElement("h2", { // Header
- innerText:isActive ? action.name + " (IN PROGRESS - " +
- formatNumber(computedActionTimeCurrent, 0) + " / " +
- formatNumber(this.actionTimeToComplete, 0) + ")"
- : action.name,
- display:"inline-block",
- }));
-
- if (isActive) { // Progress bar if its active
- var progress = computedActionTimeCurrent / this.actionTimeToComplete;
- el.appendChild(createElement("p", {
- display:"block",
- innerText:createProgressBarText({progress:progress}),
- }));
- } else {
- el.appendChild(createElement("a", { // Start button
- innerText:"Start", margin:"3px", padding:"3px",
- class:hasReqdRank ? "a-link-button" : "a-link-button-inactive",
- clickListener:() => {
- this.action.type = ActionTypes.BlackOperation;
- this.action.name = action.name;
- this.startAction(this.action);
- this.updateActionAndSkillsContent();
- return false;
- },
- }));
- el.appendChild(createElement("a", { // Set Team Size Button
- innerText:"Set Team Size (Curr Size: " + formatNumber(action.teamCount, 0) + ")", class:"a-link-button",
- margin:"3px", padding:"3px",
- clickListener:() => {
- var popupId = "bladeburner-operation-set-team-size-popup";
- var txt = createElement("p", {
- innerText:"Enter the amount of team members you would like to take on this " +
- "BlackOp. If you do not have the specified number of team members, " +
- "then as many as possible will be used. Note that team members may " +
- "be lost during operations.",
-
- });
- var input = createElement("input", {
- type:"number", placeholder: "Team size", class: "text-input",
- });
- var setBtn = createElement("a", {
- innerText:"Confirm", class:"a-link-button",
- clickListener:() => {
- var num = Math.round(parseFloat(input.value));
- if (isNaN(num) || num < 0) {
- dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)")
- } else {
- action.teamCount = num;
- this.updateBlackOpsUIElement(el, action);
- }
- removeElementById(popupId);
- return false;
- },
- });
- var cancelBtn = createElement("a", {
- innerText:"Cancel", class:"a-link-button",
- clickListener:() => {
- removeElementById(popupId);
- return false;
- },
- });
- createPopup(popupId, [txt, input, setBtn, cancelBtn]);
- input.focus();
- },
- }));
- }
-
- // Info
- appendLineBreaks(el, 2);
- el.appendChild(createElement("p", {
- display:"inline-block",
- innerHTML:" " + action.desc + " ",
- }));
- el.appendChild(createElement("p", {
- display:"block", color:hasReqdRank ? "white" : "red",
- innerHTML:"Required Rank: " + formatNumber(action.reqdRank, 0) + " ",
- }));
- el.appendChild(createElement("p", {
- display:"inline-block",
- innerHTML:`Estimated Success Chance: ${formatNumber(estimatedSuccessChance*100, 1)}% ${action.isStealth?stealthIcon:''}${action.isKill?killIcon:''}\n` +
- "Time Required: " + convertTimeMsToTimeElapsedString(actionTime*1000),
- }))
+ ReactDOM.unmountComponentAtNode(el);
+ ReactDOM.render( , el);
}
Bladeburner.prototype.updateSkillsUIElement = function(el, skill) {
- removeChildrenFromElement(el);
- var skillName = skill.name;
- var currentLevel = 0;
- if (this.skills[skillName] && !isNaN(this.skills[skillName])) {
- currentLevel = this.skills[skillName];
- }
- var pointCost = skill.calculateCost(currentLevel);
-
- const nameDiv = createElement("div");
- ReactDOM.render(React.createElement(CopyableText, {value: skill.name}, null), nameDiv);
- el.appendChild(nameDiv)
-
- const h2 = createElement("h2", { // Header
- display:"inline-block",
- });
- h2.appendChild(nameDiv);
- el.appendChild(h2);
-
- var canLevel = this.skillPoints >= pointCost;
- var maxLvl = skill.maxLvl ? currentLevel >= skill.maxLvl : false;
- el.appendChild(createElement("a", { // Level up button
- innerText:"Level", display:"inline-block",
- class: canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive",
- margin:"3px", padding:"3px",
- clickListener:() => {
- if (this.skillPoints < pointCost) {return;}
- this.skillPoints -= pointCost;
- this.upgradeSkill(skill);
- this.createActionAndSkillsContent();
- return false;
- },
- }));
- appendLineBreaks(el, 2);
- el.appendChild(createElement("p", {
- display:"block",
- innerText:`Level: ${currentLevel}`,
- }));
- if (maxLvl) {
- el.appendChild(createElement("p", {
- color:"red", display:"block",
- innerText:"MAX LEVEL",
- }));
- } else {
- el.appendChild(createElement("p", {
- display:"block",
- innerText:"Skill Points required: " + formatNumber(pointCost, 0),
- }));
- }
- el.appendChild(createElement("p", { // Info/Description
- innerHTML:skill.desc, display:"inline-block",
- }));
+ ReactDOM.unmountComponentAtNode(el);
+ ReactDOM.render( , el);
}
// Bladeburner Console Window
diff --git a/src/Bladeburner/data/ActionTypes.ts b/src/Bladeburner/data/ActionTypes.ts
index f4392313c..a1252b1e4 100644
--- a/src/Bladeburner/data/ActionTypes.ts
+++ b/src/Bladeburner/data/ActionTypes.ts
@@ -1,5 +1,18 @@
// Action Identifier enum
-export const ActionTypes = Object.freeze({
+export const ActionTypes: {
+ [key: string]: number;
+ "Idle": number;
+ "Contract": number;
+ "Operation": number;
+ "BlackOp": number;
+ "BlackOperation": number;
+ "Training": number;
+ "Recruitment": number;
+ "FieldAnalysis": number;
+ "Field Analysis": number;
+ "Diplomacy": number;
+ "Hyperbolic Regeneration Chamber": number;
+} = {
"Idle": 1,
"Contract": 2,
"Operation": 3,
@@ -11,4 +24,4 @@ export const ActionTypes = Object.freeze({
"Field Analysis": 7,
"Diplomacy": 8,
"Hyperbolic Regeneration Chamber": 9,
-});
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/src/Bladeburner/data/Icons.tsx b/src/Bladeburner/data/Icons.tsx
new file mode 100644
index 000000000..296c1d249
--- /dev/null
+++ b/src/Bladeburner/data/Icons.tsx
@@ -0,0 +1,14 @@
+import * as React from "react";
+
+export const stealthIcon =
+
+
+
+
+
+export const killIcon =
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Bladeburner/ui/BlackOpElem.tsx b/src/Bladeburner/ui/BlackOpElem.tsx
new file mode 100644
index 000000000..bcdd65855
--- /dev/null
+++ b/src/Bladeburner/ui/BlackOpElem.tsx
@@ -0,0 +1,110 @@
+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";
+
+interface IProps {
+ bladeburner: any;
+ action: any;
+}
+
+export function BlackOpElem(props: IProps): React.ReactElement {
+ const isCompleted = (props.bladeburner.blackops[props.action.name] != null);
+ if(isCompleted) {
+ return (
+
{props.action.name} (COMPLETED) );
+ }
+
+ const isActive = props.bladeburner.action.type === ActionTypes["BlackOperation"] && props.action.name === props.bladeburner.action.name;
+ const estimatedSuccessChance = props.action.getSuccessChance(props.bladeburner, {est:true});
+ const actionTime = props.action.getActionTime(props.bladeburner);
+ const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
+ const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
+
+ function onStart() {
+ props.bladeburner.action.type = ActionTypes.BlackOperation;
+ props.bladeburner.action.name = props.action.name;
+ props.bladeburner.startAction(props.bladeburner.action);
+ props.bladeburner.updateActionAndSkillsContent();
+ }
+
+ function onTeam() {
+ // TODO(hydroflame): this needs some changes that are in the Gang conversion.
+ // var popupId = "bladeburner-operation-set-team-size-popup";
+ // var txt = createElement("p", {
+ // innerText:"Enter the amount of team members you would like to take on this " +
+ // "BlackOp. If you do not have the specified number of team members, " +
+ // "then as many as possible will be used. Note that team members may " +
+ // "be lost during operations.",
+
+ // });
+ // var input = createElement("input", {
+ // type:"number", placeholder: "Team size", class: "text-input",
+ // });
+ // var setBtn = createElement("a", {
+ // innerText:"Confirm", class:"a-link-button",
+ // clickListener:() => {
+ // var num = Math.round(parseFloat(input.value));
+ // if (isNaN(num) || num < 0) {
+ // dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)")
+ // } else {
+ // action.teamCount = num;
+ // this.updateBlackOpsUIElement(el, action);
+ // }
+ // removeElementById(popupId);
+ // return false;
+ // },
+ // });
+ // var cancelBtn = createElement("a", {
+ // innerText:"Cancel", class:"a-link-button",
+ // clickListener:() => {
+ // removeElementById(popupId);
+ // return false;
+ // },
+ // });
+ // createPopup(popupId, [txt, input, setBtn, cancelBtn]);
+ // input.focus();
+ }
+
+ return (<>
+
+ {isActive ?
+ <>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})> :
+ <>{props.action.name}>
+ }
+
+ {isActive ?
+ {createProgressBarText({progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete})}
:
+ <>
+ Start
+
+ Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
+
+ >}
+
+
+
+
+
+
+ Required Rank: {formatNumber(props.action.reqdRank, 0)}
+
+
+
+ Estimated Success Chance: {formatNumber(estimatedSuccessChance*100, 1)}% {props.action.isStealth?stealthIcon:<>>}{props.action.isKill?killIcon:<>>}
+
+ Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}
+
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/BlackOperationsPage.tsx b/src/Bladeburner/ui/BlackOperationsPage.tsx
deleted file mode 100644
index 7f73397c1..000000000
--- a/src/Bladeburner/ui/BlackOperationsPage.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { BlackOperations } from "../BlackOperations";
-/*
-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]);
-}
-*/
-
-
-
-import * as React from "react";
-
-export function BlackOperationsPage(): React.ReactElement {
- // Put Black Operations in sequence of required rank
- const blackops = [];
- for (const name in BlackOperations) {
- if (BlackOperations.hasOwnProperty(name)) {
- blackops.push(BlackOperations[name]);
- }
- }
- blackops.sort(function(a, b) {
- return (a.reqdRank - b.reqdRank);
- });
-
- 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.
- {blackops.map(() =>
-
,
- )}
-
)
-}
diff --git a/src/Bladeburner/ui/ContractElem.tsx b/src/Bladeburner/ui/ContractElem.tsx
new file mode 100644
index 000000000..9ab48bba1
--- /dev/null
+++ b/src/Bladeburner/ui/ContractElem.tsx
@@ -0,0 +1,137 @@
+import React, { useState } from "react";
+import { ActionTypes } from "../data/ActionTypes";
+import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
+import {
+ formatNumber,
+ convertTimeMsToTimeElapsedString,
+} from "../../../utils/StringHelperFunctions";
+import { stealthIcon, killIcon } from "../data/Icons";
+import { BladeburnerConstants } from "../data/Constants";
+
+interface IProps {
+ bladeburner: any;
+ action: any;
+}
+
+export function ContractElem(props: IProps): React.ReactElement {
+ const setRerender = useState(false)[1];
+ const isActive = props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
+ const estimatedSuccessChance = props.action.getSuccessChance(props.bladeburner, {est:true});
+ const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
+ const maxLevel = (props.action.level >= props.action.maxLevel);
+ const actionTime = props.action.getActionTime(props.bladeburner);
+ const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
+
+ function onStart() {
+ props.bladeburner.action.type = ActionTypes.Contract;
+ props.bladeburner.action.name = props.action.name;
+ props.bladeburner.startAction(props.bladeburner.action);
+ props.bladeburner.updateActionAndSkillsContent();
+ setRerender(old => !old);
+ }
+
+ function increaseLevel() {
+ ++props.action.level;
+ if (isActive) props.bladeburner.startAction(props.bladeburner.action);
+ setRerender(old => !old);
+ }
+
+ function decreaseLevel() {
+ --props.action.level;
+ if (isActive) props.bladeburner.startAction(props.bladeburner.action);
+ setRerender(old => !old);
+ }
+
+ function onAutolevel(event: React.ChangeEvent) {
+ props.action.autoLevel = event.target.checked;
+ setRerender(old => !old);
+ }
+
+ return (<>
+
+ {isActive ?
+ <>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})> :
+ <>{props.action.name}>
+ }
+
+ {isActive ?
+ {createProgressBarText({progress:computedActionTimeCurrent / props.bladeburner.actionTimeToComplete})}
:
+ <>
+
+ Start
+
+ >}
+
+
+
+
+ {props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.ContractSuccessesPerLevel)} successes needed for next level
+
+ Level: {props.action.level} / {props.action.maxLevel}
+
+
+ {isActive && (WARNING: changing the level will restart the Operation )}
+ ↑
+
+
+ {isActive && (WARNING: changing the level will restart the Operation )}
+ ↓
+
+
+
+
+
+
+Estimated success chance: {formatNumber(estimatedSuccessChance*100, 1)}% {props.action.isStealth?stealthIcon:<>>}${props.action.isKill?killIcon:<>>}
+Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}
+Contracts remaining: {Math.floor(props.action.count)}
+Successes: {props.action.successes}
+Failures: {props.action.failures}
+
+
+
+ Autolevel:
+ Automatically increase operation level when possible
+
+
+ >);
+}
+
+/*
+
+// Autolevel Checkbox
+el.appendChild(createElement("br"));
+var autolevelCheckboxId = "bladeburner-" + action.name + "-autolevel-checkbox";
+el.appendChild(createElement("label", {
+ for:autolevelCheckboxId, innerText:"Autolevel: ",color:"white",
+ tooltip:"Automatically increase contract level when possible",
+}));
+
+const checkboxInput = createElement("input", {
+ type:"checkbox",
+ id: autolevelCheckboxId,
+ checked: action.autoLevel,
+ changeListener: () => {
+ action.autoLevel = checkboxInput.checked;
+ },
+});
+
+el.appendChild(checkboxInput);
+
+*/
\ No newline at end of file
diff --git a/src/Bladeburner/ui/GeneralActionElem.tsx b/src/Bladeburner/ui/GeneralActionElem.tsx
new file mode 100644
index 000000000..93b7b1e2d
--- /dev/null
+++ b/src/Bladeburner/ui/GeneralActionElem.tsx
@@ -0,0 +1,49 @@
+import React, { useState } from "react";
+import { ActionTypes } from "../data/ActionTypes";
+import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
+import {
+ formatNumber,
+ convertTimeMsToTimeElapsedString,
+} from "../../../utils/StringHelperFunctions";
+import { stealthIcon, killIcon } from "../data/Icons";
+import { BladeburnerConstants } from "../data/Constants";
+
+interface IProps {
+ bladeburner: any;
+ action: any;
+}
+
+export function GeneralActionElem(props: IProps): React.ReactElement {
+ const setRerender = useState(false)[1];
+ const isActive = props.action.name === props.bladeburner.action.name;
+ const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow, props.bladeburner.actionTimeToComplete);
+
+ function onStart() {
+ props.bladeburner.action.type = ActionTypes[(props.action.name as string)];
+ props.bladeburner.action.name = props.action.name;
+ props.bladeburner.startAction(props.bladeburner.action);
+ setRerender(old => !old);
+ }
+
+ return (<>
+
+ {isActive ?
+ <>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})> :
+ <>{props.action.name}>
+ }
+
+ {isActive ?
+ {createProgressBarText({progress:computedActionTimeCurrent / props.bladeburner.actionTimeToComplete})}
:
+ <>
+
+ Start
+
+ >}
+
+
+
+ >);
+}
\ No newline at end of file
diff --git a/src/Bladeburner/ui/OperationElem.tsx b/src/Bladeburner/ui/OperationElem.tsx
new file mode 100644
index 000000000..1dabeb650
--- /dev/null
+++ b/src/Bladeburner/ui/OperationElem.tsx
@@ -0,0 +1,157 @@
+import React, { useState } from "react";
+import { ActionTypes } from "../data/ActionTypes";
+import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
+import {
+ formatNumber,
+ convertTimeMsToTimeElapsedString,
+} from "../../../utils/StringHelperFunctions";
+import { stealthIcon, killIcon } from "../data/Icons";
+import { BladeburnerConstants } from "../data/Constants";
+
+interface IProps {
+ bladeburner: any;
+ action: any;
+}
+
+export function OperationElem(props: IProps): React.ReactElement {
+ const setRerender = useState(false)[1];
+ const isActive = props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
+ const estimatedSuccessChance = props.action.getSuccessChance(props.bladeburner, {est:true});
+ const computedActionTimeCurrent = Math.min(props.bladeburner.actionTimeCurrent+props.bladeburner.actionTimeOverflow,props.bladeburner.actionTimeToComplete);
+ const maxLevel = (props.action.level >= props.action.maxLevel);
+ const actionTime = props.action.getActionTime(props.bladeburner);
+ const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
+
+ function onStart() {
+ props.bladeburner.action.type = ActionTypes.Operation;
+ props.bladeburner.action.name = props.action.name;
+ props.bladeburner.startAction(props.bladeburner.action);
+ props.bladeburner.updateActionAndSkillsContent();
+ setRerender(old => !old);
+ }
+
+ function onTeam() {
+ // var popupId = "bladeburner-operation-set-team-size-popup";
+ // var txt = createElement("p", {
+ // innerText:"Enter the amount of team members you would like to take on these " +
+ // "operations. If you do not have the specified number of team members, " +
+ // "then as many as possible will be used. Note that team members may " +
+ // "be lost during operations.",
+
+ // });
+ // var input = createElement("input", {
+ // type:"number", placeholder: "Team size", class: "text-input",
+ // });
+ // var setBtn = createElement("a", {
+ // innerText:"Confirm", class:"a-link-button",
+ // clickListener:() => {
+ // var num = Math.round(parseFloat(input.value));
+ // if (isNaN(num) || num < 0) {
+ // dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)")
+ // } else {
+ // action.teamCount = num;
+ // this.updateOperationsUIElement(el, action);
+ // }
+ // removeElementById(popupId);
+ // return false;
+ // },
+ // });
+ // var cancelBtn = createElement("a", {
+ // innerText:"Cancel", class:"a-link-button",
+ // clickListener:() => {
+ // removeElementById(popupId);
+ // return false;
+ // },
+ // });
+ // createPopup(popupId, [txt, input, setBtn, cancelBtn]);
+ // input.focus();
+ }
+
+ function increaseLevel() {
+ ++props.action.level;
+ if (isActive) props.bladeburner.startAction(props.bladeburner.action);
+ setRerender(old => !old);
+ }
+
+ function decreaseLevel() {
+ --props.action.level;
+ if (isActive) props.bladeburner.startAction(props.bladeburner.action);
+ setRerender(old => !old);
+ }
+
+ function onAutolevel(event: React.ChangeEvent) {
+ props.action.autoLevel = event.target.checked;
+ setRerender(old => !old);
+ }
+
+ return (<>
+
+ {isActive ?
+ <>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})> :
+ <>{props.action.name}>
+ }
+
+ {isActive ?
+ {createProgressBarText({progress:computedActionTimeCurrent / props.bladeburner.actionTimeToComplete})}
:
+ <>
+
+ Start
+
+
+ Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
+
+ >}
+
+
+
+
+ {props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.OperationSuccessesPerLevel)} successes needed for next level
+
+ Level: {props.action.level} / {props.action.maxLevel}
+
+
+ {isActive && (WARNING: changing the level will restart the Operation )}
+ ↑
+
+
+ {isActive && (WARNING: changing the level will restart the Operation )}
+ ↓
+
+
+
+
+
+
+Estimated success chance: {formatNumber(estimatedSuccessChance*100, 1)}% {props.action.isStealth?stealthIcon:<>>}{props.action.isKill?killIcon:<>>}
+Time Required: {convertTimeMsToTimeElapsedString(actionTime*1000)}
+Operations remaining: {Math.floor(props.action.count)}
+Successes: {props.action.successes}
+Failures: {props.action.failures}
+
+
+
+ Autolevel:
+ Automatically increase operation level when possible
+
+
+ >);
+}
diff --git a/src/Bladeburner/ui/SkillElem.tsx b/src/Bladeburner/ui/SkillElem.tsx
new file mode 100644
index 000000000..af7ebfbf1
--- /dev/null
+++ b/src/Bladeburner/ui/SkillElem.tsx
@@ -0,0 +1,46 @@
+import * as React from "react";
+import { CopyableText } from "../../ui/React/CopyableText";
+import { formatNumber } from "../../../utils/StringHelperFunctions";
+
+interface IProps {
+ skill: any;
+ bladeburner: any;
+}
+
+export function SkillElem(props: IProps): React.ReactElement {
+ const skillName = props.skill.name;
+ let currentLevel = 0;
+ if (props.bladeburner.skills[skillName] && !isNaN(props.bladeburner.skills[skillName])) {
+ currentLevel = props.bladeburner.skills[skillName];
+ }
+ const pointCost = props.skill.calculateCost(currentLevel);
+
+ const canLevel = props.bladeburner.skillPoints >= pointCost;
+ const maxLvl = props.skill.maxLvl ? currentLevel >= props.skill.maxLvl : false;
+
+ function onClick() {
+ if (props.bladeburner.skillPoints < pointCost) return;
+ props.bladeburner.skillPoints -= pointCost;
+ props.bladeburner.upgradeSkill(props.skill);
+ props.bladeburner.createActionAndSkillsContent();
+ }
+
+ return (<>
+
+
+
+
+ Level
+
+
+
+ Level: {currentLevel}
+ {maxLvl ?
+ MAX LEVEL
:
+ Skill Points required: {formatNumber(pointCost, 0)}
}
+
+ >);
+}
\ No newline at end of file
diff --git a/src/engine.jsx b/src/engine.jsx
index 244dec641..db76bee30 100644
--- a/src/engine.jsx
+++ b/src/engine.jsx
@@ -475,6 +475,7 @@ const Engine = {
Engine.hideAllContent();
routing.navigateTo(Page.Bladeburner);
Player.bladeburner.createContent();
+ MainMenuLinks.Bladeburner.classList.add("active");
} catch(e) {
exceptionAlert(e);
}