From 0e9d7450c9eb4ce74fca77cc5c08ec07b1bd18d0 Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Fri, 18 Jun 2021 03:38:17 -0400 Subject: [PATCH] Converting bladeburner to react --- src/Bladeburner.jsx | 439 +-------------------- src/Bladeburner/data/ActionTypes.ts | 17 +- src/Bladeburner/data/Icons.tsx | 14 + src/Bladeburner/ui/BlackOpElem.tsx | 110 ++++++ src/Bladeburner/ui/BlackOperationsPage.tsx | 62 --- src/Bladeburner/ui/ContractElem.tsx | 137 +++++++ src/Bladeburner/ui/GeneralActionElem.tsx | 49 +++ src/Bladeburner/ui/OperationElem.tsx | 157 ++++++++ src/Bladeburner/ui/SkillElem.tsx | 46 +++ src/engine.jsx | 1 + 10 files changed, 544 insertions(+), 488 deletions(-) create mode 100644 src/Bladeburner/data/Icons.tsx create mode 100644 src/Bladeburner/ui/BlackOpElem.tsx delete mode 100644 src/Bladeburner/ui/BlackOperationsPage.tsx create mode 100644 src/Bladeburner/ui/ContractElem.tsx create mode 100644 src/Bladeburner/ui/GeneralActionElem.tsx create mode 100644 src/Bladeburner/ui/OperationElem.tsx create mode 100644 src/Bladeburner/ui/SkillElem.tsx 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 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} +
+
+ + + ); +} 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); }