From 0e9d7450c9eb4ce74fca77cc5c08ec07b1bd18d0 Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Fri, 18 Jun 2021 03:38:17 -0400 Subject: [PATCH 01/12] 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); } From 988ca3776482771d83575d15ea879320cc20d6c4 Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Fri, 18 Jun 2021 16:22:12 -0400 Subject: [PATCH 02/12] converting more blade to react/ts --- src/Bladeburner.jsx | 374 ++--------------------- src/Bladeburner/ui/BlackOpList.tsx | 42 +++ src/Bladeburner/ui/BlackOpPage.tsx | 25 ++ src/Bladeburner/ui/ContractList.tsx | 30 ++ src/Bladeburner/ui/ContractPage.tsx | 20 ++ src/Bladeburner/ui/GeneralActionList.tsx | 35 +++ src/Bladeburner/ui/GeneralActionPage.tsx | 16 + src/Bladeburner/ui/OperationList.tsx | 30 ++ src/Bladeburner/ui/OperationPage.tsx | 31 ++ src/Bladeburner/ui/SkillList.tsx | 17 ++ src/Bladeburner/ui/SkillPage.tsx | 66 ++++ src/Bladeburner/ui/Stats.tsx | 78 +++++ src/ui/CharacterInfo.tsx | 4 +- src/ui/React/StatsTable.tsx | 2 +- utils/StringHelperFunctions.ts | 2 +- 15 files changed, 421 insertions(+), 351 deletions(-) create mode 100644 src/Bladeburner/ui/BlackOpList.tsx create mode 100644 src/Bladeburner/ui/BlackOpPage.tsx create mode 100644 src/Bladeburner/ui/ContractList.tsx create mode 100644 src/Bladeburner/ui/ContractPage.tsx create mode 100644 src/Bladeburner/ui/GeneralActionList.tsx create mode 100644 src/Bladeburner/ui/GeneralActionPage.tsx create mode 100644 src/Bladeburner/ui/OperationList.tsx create mode 100644 src/Bladeburner/ui/OperationPage.tsx create mode 100644 src/Bladeburner/ui/SkillList.tsx create mode 100644 src/Bladeburner/ui/SkillPage.tsx create mode 100644 src/Bladeburner/ui/Stats.tsx 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, From 33f0efd49c6c355c0e6a0c05617b862088fdd1ae Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Tue, 10 Aug 2021 11:25:21 -0400 Subject: [PATCH 03/12] converting more blade to react --- src/Bladeburner.jsx | 355 ++--------------------- src/Bladeburner/ui/AllPages.tsx | 46 +++ src/Bladeburner/ui/BlackOpElem.tsx | 5 +- src/Bladeburner/ui/Console.tsx | 43 +++ src/Bladeburner/ui/ContractElem.tsx | 1 - src/Bladeburner/ui/ContractList.tsx | 7 - src/Bladeburner/ui/GeneralActionList.tsx | 7 - src/Bladeburner/ui/OperationElem.tsx | 1 - src/Bladeburner/ui/OperationList.tsx | 7 - src/Bladeburner/ui/SkillElem.tsx | 5 +- src/Bladeburner/ui/SkillList.tsx | 3 +- src/Bladeburner/ui/SkillPage.tsx | 45 +-- src/Bladeburner/ui/Stats.tsx | 109 ++++++- 13 files changed, 234 insertions(+), 400 deletions(-) create mode 100644 src/Bladeburner/ui/AllPages.tsx create mode 100644 src/Bladeburner/ui/Console.tsx diff --git a/src/Bladeburner.jsx b/src/Bladeburner.jsx index 41f4ddafc..4693f99b2 100644 --- a/src/Bladeburner.jsx +++ b/src/Bladeburner.jsx @@ -68,6 +68,8 @@ 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 { AllPages } from "./Bladeburner/ui/AllPages"; +import { Console } from "./Bladeburner/ui/Console"; import { StatsTable } from "./ui/React/StatsTable"; import { CopyableText } from "./ui/React/CopyableText"; @@ -443,9 +445,6 @@ Bladeburner.prototype.process = function() { } } - if (routing.isOn(Page.Bladeburner)) { - this.updateContent(); - } } } @@ -809,10 +808,6 @@ Bladeburner.prototype.completeAction = function() { return hackWorldDaemon(Player.bitNodeN); } - if (routing.isOn(Page.Bladeburner)) { - this.createActionAndSkillsContent(); - } - if (this.logging.blackops) { this.log(action.name + " successful! Gained " + formatNumber(rankGain, 1) + " rank"); } @@ -1228,7 +1223,6 @@ Bladeburner.prototype.initializeDomElementRefs = function() { overviewDiv: null, // Overview of stats that stays fixed on left actionAndSkillsDiv: null, // Panel for different sections (contracts, ops, skills) - currentTab: null, // Contracts, Operations, Black Ops, Skills consoleDiv: null, consoleTable: null, @@ -1236,29 +1230,6 @@ Bladeburner.prototype.initializeDomElementRefs = function() { consoleInputCell: null, // td consoleInputHeader: null, // "> " consoleInput: null, // Actual input element - - // Overview Content - overviewRank: null, - overviewStamina: null, - overviewStaminaHelpTip: null, - overviewGen1: null, // Stamina Penalty, Team, Hospitalized stats, current city - overviewEstPop: null, - overviewEstPopHelpTip: null, - overviewEstComms: null, - overviewChaos: null, - overviewSkillPoints: null, - overviewBonusTime: null, - overviewAugMults: null, - - // Actions and Skills Content - actionsAndSkillsDesc: null, - actionsAndSkillsList: null, // ul element of all UI elements in this panel - generalActions: {}, - contracts: {}, - operations: {}, - blackops: {}, - skills: {}, - skillPointsDisplay: null, }; } @@ -1282,10 +1253,8 @@ Bladeburner.prototype.createContent = function() { border:"1px solid white", margin:"6px", padding:"6px", }); - DomElems.currentTab = "general"; - - this.createOverviewContent(); - this.createActionAndSkillsContent(); + ReactDOM.render(, DomElems.overviewDiv); + ReactDOM.render(, DomElems.actionAndSkillsDiv); // Console DomElems.consoleDiv = createElement("div", { @@ -1297,32 +1266,13 @@ Bladeburner.prototype.createContent = function() { return false; }, }); - DomElems.consoleTable = createElement("table", {class:"bladeburner-console-table"}); - DomElems.consoleInputRow = createElement("tr", {class:"bladeburner-console-input-row", id:"bladeburner-console-input-row"}); - DomElems.consoleInputCell = createElement("td", {class:"bladeburner-console-input-cell"}); - DomElems.consoleInputHeader = createElement("pre", {innerText:"> "}); - DomElems.consoleInput = createElement("input", { - type:"text", class:"bladeburner-console-input", tabIndex:1, - onfocus:() => {DomElems.consoleInput.value = DomElems.consoleInput.value}, - }); - - DomElems.consoleInputCell.appendChild(DomElems.consoleInputHeader); - DomElems.consoleInputCell.appendChild(DomElems.consoleInput); - DomElems.consoleInputRow.appendChild(DomElems.consoleInputCell); - DomElems.consoleTable.appendChild(DomElems.consoleInputRow); - DomElems.consoleDiv.appendChild(DomElems.consoleTable); + ReactDOM.render(, DomElems.consoleDiv); DomElems.overviewConsoleParentDiv.appendChild(DomElems.overviewDiv); DomElems.overviewConsoleParentDiv.appendChild(DomElems.consoleDiv); DomElems.bladeburnerDiv.appendChild(DomElems.overviewConsoleParentDiv); DomElems.bladeburnerDiv.appendChild(DomElems.actionAndSkillsDiv); - - // legend - const legend = createElement("div") - legend.innerHTML = `${stealthIcon}= This action requires stealth, ${killIcon} = This action involves retirement` - DomElems.bladeburnerDiv.appendChild(legend); - document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv); if (this.consoleLogs.length === 0) { @@ -1333,8 +1283,6 @@ Bladeburner.prototype.createContent = function() { this.postToConsole(this.consoleLogs[i], false); } } - - DomElems.consoleInput.focus(); } Bladeburner.prototype.clearContent = function() { @@ -1346,290 +1294,35 @@ Bladeburner.prototype.clearContent = function() { this.initializeDomElementRefs(); } -Bladeburner.prototype.createOverviewContent = function() { - if (DomElems.overviewDiv == null) { - throw new Error("Bladeburner.createOverviewContent() called with DomElems.overviewDiv = null"); - } - - DomElems.overviewRank = createElement("p", { - innerText:"Rank: ", - display:"inline-block", - tooltip:"Your rank within the Bladeburner division", - }); - - DomElems.overviewStamina = createElement("p", { - display:"inline-block", - }); - - DomElems.overviewStaminaHelpTip = createElement("div", { - class:"help-tip", - innerText:"?", - clickListener: () => { - dialogBoxCreate("Performing actions will use up your stamina.

    " + - "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."); - }, - }); - - DomElems.overviewGen1 = createElement("p", { - display:"block", - }); - - DomElems.overviewEstPop = createElement("p", { - innerText:"Est. Synthoid Population: ", - display:"inline-block", - tooltip:"This is your Bladeburner division's estimate of how many Synthoids exist " + - "in your current city.", - }); - - DomElems.overviewEstPopHelpTip = createElement("div", { - innerText:"?", class:"help-tip", - clickListener:() => { - dialogBoxCreate("The success rate of your contracts/operations depends on " + - "the population of Synthoids in your current city. " + - "The success rate that is shown to you is only an estimate, " + - "and it is based on your Synthoid population estimate.

    " + - "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."); - }, - }); - - DomElems.overviewEstComms = createElement("p", { - innerText:"Est. Synthoid Communities: ", - display:"inline-block", - tooltip:"This is your Bladeburner divison's estimate of how many Synthoid " + - "communities exist in your current city.", - }); - - DomElems.overviewChaos = createElement("p", { - innerText:"City Chaos: ", - display:"inline-block", - tooltip:"The city's chaos level due to tensions and conflicts between humans and Synthoids. " + - "Having too high of a chaos level can make contracts and operations harder.", - }); - - DomElems.overviewBonusTime = createElement("p", { - innerText: "Bonus time: ", - display: "inline-block", - tooltip: "You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser). " + - "Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.", - }); - DomElems.overviewSkillPoints = createElement("p", {display:"block"}); - - - DomElems.overviewAugMults = createElement("div", {display:"block"}); - - - DomElems.overviewDiv.appendChild(DomElems.overviewRank); - appendLineBreaks(DomElems.overviewDiv, 1); - DomElems.overviewDiv.appendChild(DomElems.overviewStamina); - DomElems.overviewDiv.appendChild(DomElems.overviewStaminaHelpTip); - DomElems.overviewDiv.appendChild(DomElems.overviewGen1); - DomElems.overviewDiv.appendChild(DomElems.overviewEstPop); - DomElems.overviewDiv.appendChild(DomElems.overviewEstPopHelpTip); - appendLineBreaks(DomElems.overviewDiv, 1); - DomElems.overviewDiv.appendChild(DomElems.overviewEstComms); - appendLineBreaks(DomElems.overviewDiv, 1); - DomElems.overviewDiv.appendChild(DomElems.overviewChaos); - appendLineBreaks(DomElems.overviewDiv, 2); - DomElems.overviewDiv.appendChild(DomElems.overviewBonusTime); - DomElems.overviewDiv.appendChild(DomElems.overviewSkillPoints); - appendLineBreaks(DomElems.overviewDiv, 1); - DomElems.overviewDiv.appendChild(DomElems.overviewAugMults); - - // Travel to new city button - appendLineBreaks(DomElems.overviewDiv, 1); - DomElems.overviewDiv.appendChild(createElement("a", { - innerHTML:"Travel", class:"a-link-button", display:"inline-block", - clickListener:() => { - var popupId = "bladeburner-travel-popup-cancel-btn"; - var popupArguments = []; - popupArguments.push(createElement("a", { // Cancel Button - innerText:"Cancel", class:"a-link-button", - clickListener:() => { - removeElementById(popupId); return false; - }, - })) - popupArguments.push(createElement("p", { // Info Text - innerText:"Travel to a different city for your Bladeburner " + - "activities. This does not cost any money. The city you are " + - "in for your Bladeburner duties does not affect " + - "your location in the game otherwise", - })); - for (var i = 0; i < BladeburnerConstants.CityNames.length; ++i) { - (function(inst, i) { - popupArguments.push(createElement("div", { - /** - * Reusing this css class...it adds a border and makes it - * so that background color changes when you hover - */ - class:"cmpy-mgmt-find-employee-option", - innerText:BladeburnerConstants.CityNames[i], - clickListener:() => { - inst.city = BladeburnerConstants.CityNames[i]; - removeElementById(popupId); - inst.updateOverviewContent(); - return false; - }, - })); - })(this, i); - } - createPopup(popupId, popupArguments); - }, - })); - - // Faction button - const bladeburnersFactionName = "Bladeburners"; - if (factionExists(bladeburnersFactionName)) { - var bladeburnerFac = Factions[bladeburnersFactionName]; - if (!(bladeburnerFac instanceof Faction)) { - throw new Error("Could not properly get Bladeburner Faction object in Bladeburner UI Overview Faction button"); - } - DomElems.overviewDiv.appendChild(createElement("a", { - innerText:"Faction", class:"a-link-button", display:"inline-block", - tooltip:"Apply to the Bladeburner Faction, or go to the faction page if you are already a member", - clickListener:() => { - if (bladeburnerFac.isMember) { - Engine.loadFactionContent(); - displayFactionContent(bladeburnersFactionName); - } else { - if (this.rank >= BladeburnerConstants.RankNeededForFaction) { - joinFaction(bladeburnerFac); - dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction"); - removeChildrenFromElement(DomElems.overviewDiv); - this.createOverviewContent(); - } else { - dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!") - } - } - return false; - }, - })); - } - - DomElems.overviewDiv.appendChild(createElement("br")); - DomElems.overviewDiv.appendChild(createElement("br")); - - this.updateOverviewContent(); -} - -Bladeburner.prototype.createActionAndSkillsContent = function() { - if (DomElems.currentTab == null) {DomElems.currentTab = "general";} - - removeChildrenFromElement(DomElems.actionAndSkillsDiv); - clearObject(DomElems.generalActions); - clearObject(DomElems.contracts); - clearObject(DomElems.operations); - clearObject(DomElems.blackops); - clearObject(DomElems.skills); - - //Navigation buttons - var currTab = DomElems.currentTab.toLowerCase(); - var buttons = ["General", "Contracts", "Operations", "BlackOps", "Skills"]; - for (var i = 0; i < buttons.length; ++i) { - (function(buttons, i, inst, currTab) { - - DomElems.actionAndSkillsDiv.appendChild(createElement("a", { - innerText:buttons[i], - class:currTab === buttons[i].toLowerCase() ? "bladeburner-nav-button-inactive" : "bladeburner-nav-button", - clickListener:() => { - DomElems.currentTab = buttons[i].toLowerCase(); - inst.createActionAndSkillsContent(); - return false; - }, - })); - }) (buttons, i, this, currTab); - } - - // General info/description for each action - DomElems.actionsAndSkillsDesc = createElement("p", { - display:"block", margin:"4px", padding:"4px", - }); - - // List for actions/skills - removeChildrenFromElement(DomElems.actionsAndSkillsList); - DomElems.actionsAndSkillsList = createElement("ul"); - - switch(currTab) { - case "general": - ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc); - ReactDOM.render(, DomElems.actionsAndSkillsDesc); - break; - case "contracts": - ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc); - ReactDOM.render(, DomElems.actionsAndSkillsDesc); - break; - case "operations": - ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc); - ReactDOM.render(, DomElems.actionsAndSkillsDesc); - break; - case "blackops": - ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc); - ReactDOM.render(, DomElems.actionsAndSkillsDesc); - break; - case "skills": - ReactDOM.unmountComponentAtNode(DomElems.actionsAndSkillsDesc); - ReactDOM.render(, DomElems.actionsAndSkillsDesc); - break; - default: - throw new Error("Invalid value for DomElems.currentTab in Bladeburner.createActionAndSkillsContent"); - } - this.updateContent(); - - DomElems.actionAndSkillsDiv.appendChild(DomElems.actionsAndSkillsDesc); - DomElems.actionAndSkillsDiv.appendChild(DomElems.actionsAndSkillsList); -} - -Bladeburner.prototype.updateContent = function() { - this.updateOverviewContent(); -} - -Bladeburner.prototype.updateOverviewContent = function() { - if (!routing.isOn(Page.Bladeburner)) return; - ReactDOM.render(, DomElems.overviewDiv); -} - +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////HYDRO END OF UI////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // Bladeburner Console Window Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) { const MaxConsoleEntries = 100; - if (saveToLogs === true) { + if (saveToLogs) { this.consoleLogs.push(input); if (this.consoleLogs.length > MaxConsoleEntries) { this.consoleLogs.shift(); } } - - if (input == null || DomElems.consoleDiv == null) {return;} - $("#bladeburner-console-input-row").before('' + input + ''); - - if (DomElems.consoleTable.childNodes.length > MaxConsoleEntries) { - DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild); - } - - this.updateConsoleScroll(); } -Bladeburner.prototype.updateConsoleScroll = function() { - DomElems.consoleDiv.scrollTop = DomElems.consoleDiv.scrollHeight; -} Bladeburner.prototype.resetConsoleInput = function() { DomElems.consoleInput.value = ""; @@ -2035,7 +1728,6 @@ Bladeburner.prototype.executeSkillConsoleCommand = function(args) { this.skillPoints -= pointCost; this.upgradeSkill(skill); this.log(skill.name + " upgraded to Level " + this.skills[skillName]); - this.createActionAndSkillsContent(); } else { this.postToConsole("You do not have enough Skill Points to upgrade this. You need " + formatNumber(pointCost, 0)); } @@ -2441,9 +2133,6 @@ Bladeburner.prototype.upgradeSkillNetscriptFn = function(skillName, workerScript this.skillPoints -= cost; this.upgradeSkill(skill); - if (routing.isOn(Page.Bladeburner) && DomElems.currentTab.toLowerCase() === "skills") { - this.createActionAndSkillsContent(); - } workerScript.log("bladeburner.upgradeSkill", `'${skillName}' upgraded to level ${this.skills[skillName]}`); return true; } @@ -2514,10 +2203,6 @@ Bladeburner.prototype.joinBladeburnerFactionNetscriptFn = function(workerScript) } else if (this.rank >= BladeburnerConstants.RankNeededForFaction) { joinFaction(bladeburnerFac); workerScript.log("bladeburner.joinBladeburnerFaction", "Joined Bladeburners faction."); - if (routing.isOn(Page.Bladeburner)) { - removeChildrenFromElement(DomElems.overviewDiv); - this.createOverviewContent(); - } return true; } else { workerScript.log("bladeburner.joinBladeburnerFaction", `You do not have the required rank (${this.rank}/${BladeburnerConstants.RankNeededForFaction}).`); diff --git a/src/Bladeburner/ui/AllPages.tsx b/src/Bladeburner/ui/AllPages.tsx new file mode 100644 index 000000000..deadaf103 --- /dev/null +++ b/src/Bladeburner/ui/AllPages.tsx @@ -0,0 +1,46 @@ +import React, { useState, useEffect } from "react"; +import { GeneralActionPage } from "./GeneralActionPage"; +import { ContractPage } from "./ContractPage"; +import { OperationPage } from "./OperationPage"; +import { BlackOpPage } from "./BlackOpPage"; +import { SkillPage } from "./SkillPage"; +import { stealthIcon, killIcon } from "../data/Icons"; + +interface IProps { + bladeburner: any; +} + +export function AllPages(props: IProps): React.ReactElement { + const [page, setPage] = useState('General'); + const setRerender = useState(false)[1]; + + useEffect(() => { + const id = setInterval(() => setRerender(old => !old), 1000); + return () => clearInterval(id); + }, []); + + function Header(props: {name: string}): React.ReactElement { + return (setPage(props.name)} + className={page !== props.name ? + "bladeburner-nav-button" : + "bladeburner-nav-button-inactive"}> + {props.name} + ); + } + return (<> +
    +
    +
    +
    +
    +
    + {page === 'General' && } + {page === 'Contracts' && } + {page === 'Operations' && } + {page === 'BlackOps' && } + {page === 'Skills' && } +
    + {stealthIcon}= This action requires stealth, {killIcon} = This action involves retirement + ); +} \ No newline at end of file diff --git a/src/Bladeburner/ui/BlackOpElem.tsx b/src/Bladeburner/ui/BlackOpElem.tsx index bcdd65855..3fd3268f9 100644 --- a/src/Bladeburner/ui/BlackOpElem.tsx +++ b/src/Bladeburner/ui/BlackOpElem.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React, { useState } from "react"; import { formatNumber, convertTimeMsToTimeElapsedString, @@ -13,6 +13,7 @@ interface IProps { } export function BlackOpElem(props: IProps): React.ReactElement { + const setRerender = useState(false)[1]; const isCompleted = (props.bladeburner.blackops[props.action.name] != null); if(isCompleted) { return ( @@ -29,7 +30,7 @@ export function BlackOpElem(props: IProps): React.ReactElement { props.bladeburner.action.type = ActionTypes.BlackOperation; props.bladeburner.action.name = props.action.name; props.bladeburner.startAction(props.bladeburner.action); - props.bladeburner.updateActionAndSkillsContent(); + setRerender(old => !old); } function onTeam() { diff --git a/src/Bladeburner/ui/Console.tsx b/src/Bladeburner/ui/Console.tsx new file mode 100644 index 000000000..7c4de7eb8 --- /dev/null +++ b/src/Bladeburner/ui/Console.tsx @@ -0,0 +1,43 @@ +import React, { useState, useRef, useEffect } from "react"; + +interface ILineProps { + content: any; +} + +function Line(props: ILineProps): React.ReactElement { + return ( + {props.content} + ) +} + +interface IProps { + bladeburner: any; +} + +export function Console(props: IProps): React.ReactElement { + const lastRef = useRef(null); + const setRerender = useState(false)[1]; + + useEffect(() => { + if(lastRef.current) + lastRef.current.scrollIntoView({block: "end", inline: "nearest", behavior: "smooth" }); + const id = setInterval(() => setRerender(old => !old), 1000); + return () => clearInterval(id); + }, []); + + return ( + + {/* + TODO: optimize this. + using `i` as a key here isn't great because it'll re-render everything + everytime the console reaches max length. + */} + {props.bladeburner.consoleLogs.map((log: any, i: number) => )} + + + + +
    +
    {"> "}
    +
    ); +} \ No newline at end of file diff --git a/src/Bladeburner/ui/ContractElem.tsx b/src/Bladeburner/ui/ContractElem.tsx index 9ab48bba1..5ffc7d1e9 100644 --- a/src/Bladeburner/ui/ContractElem.tsx +++ b/src/Bladeburner/ui/ContractElem.tsx @@ -26,7 +26,6 @@ export function ContractElem(props: IProps): React.ReactElement { 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); } diff --git a/src/Bladeburner/ui/ContractList.tsx b/src/Bladeburner/ui/ContractList.tsx index 142eb5ce0..dcea7ea5e 100644 --- a/src/Bladeburner/ui/ContractList.tsx +++ b/src/Bladeburner/ui/ContractList.tsx @@ -11,13 +11,6 @@ interface IProps { } 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 (<> diff --git a/src/Bladeburner/ui/GeneralActionList.tsx b/src/Bladeburner/ui/GeneralActionList.tsx index 15f329df9..e459899d4 100644 --- a/src/Bladeburner/ui/GeneralActionList.tsx +++ b/src/Bladeburner/ui/GeneralActionList.tsx @@ -12,13 +12,6 @@ interface IProps { } 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)) { diff --git a/src/Bladeburner/ui/OperationElem.tsx b/src/Bladeburner/ui/OperationElem.tsx index 1dabeb650..133778c8d 100644 --- a/src/Bladeburner/ui/OperationElem.tsx +++ b/src/Bladeburner/ui/OperationElem.tsx @@ -26,7 +26,6 @@ export function OperationElem(props: IProps): React.ReactElement { 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); } diff --git a/src/Bladeburner/ui/OperationList.tsx b/src/Bladeburner/ui/OperationList.tsx index bff9cc3b7..d78d46351 100644 --- a/src/Bladeburner/ui/OperationList.tsx +++ b/src/Bladeburner/ui/OperationList.tsx @@ -11,13 +11,6 @@ interface IProps { } 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 (<> diff --git a/src/Bladeburner/ui/SkillElem.tsx b/src/Bladeburner/ui/SkillElem.tsx index af7ebfbf1..44312e413 100644 --- a/src/Bladeburner/ui/SkillElem.tsx +++ b/src/Bladeburner/ui/SkillElem.tsx @@ -1,10 +1,11 @@ -import * as React from "react"; +import React, { useState } from "react"; import { CopyableText } from "../../ui/React/CopyableText"; import { formatNumber } from "../../../utils/StringHelperFunctions"; interface IProps { skill: any; bladeburner: any; + onUpgrade: () => void; } export function SkillElem(props: IProps): React.ReactElement { @@ -22,7 +23,7 @@ export function SkillElem(props: IProps): React.ReactElement { if (props.bladeburner.skillPoints < pointCost) return; props.bladeburner.skillPoints -= pointCost; props.bladeburner.upgradeSkill(props.skill); - props.bladeburner.createActionAndSkillsContent(); + props.onUpgrade(); } return (<> diff --git a/src/Bladeburner/ui/SkillList.tsx b/src/Bladeburner/ui/SkillList.tsx index 014b6f992..a7fbe2182 100644 --- a/src/Bladeburner/ui/SkillList.tsx +++ b/src/Bladeburner/ui/SkillList.tsx @@ -4,13 +4,14 @@ import { Skills } from "../Skills"; interface IProps { bladeburner: any; + onUpgrade: () => void; } export function SkillList(props: IProps): React.ReactElement { return (<> {Object.keys(Skills).map((skill: string) =>
  • - +
  • )} ); diff --git a/src/Bladeburner/ui/SkillPage.tsx b/src/Bladeburner/ui/SkillPage.tsx index 8a6e7e9bc..c465e95cc 100644 --- a/src/Bladeburner/ui/SkillPage.tsx +++ b/src/Bladeburner/ui/SkillPage.tsx @@ -1,4 +1,4 @@ -import * as React from "react"; +import React, { useState } from "react"; import { SkillList } from "./SkillList"; import { BladeburnerConstants } from "../data/Constants"; import { formatNumber } from "../../../utils/StringHelperFunctions"; @@ -9,6 +9,7 @@ interface IProps { export function SkillPage(props: IProps): React.ReactElement { + const setRerender = useState(false)[1]; const mults = props.bladeburner.skillMultipliers; function valid(mult: any) { @@ -16,7 +17,9 @@ export function SkillPage(props: IProps): React.ReactElement { } return (<> - Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)} +

    + Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)} +

    You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
    @@ -24,27 +27,27 @@ export function SkillPage(props: IProps): React.ReactElement { 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)}
    }
    - + {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)}

    } +
    + setRerender(old => !old)} /> ); } diff --git a/src/Bladeburner/ui/Stats.tsx b/src/Bladeburner/ui/Stats.tsx index 9e072f189..e766d8c11 100644 --- a/src/Bladeburner/ui/Stats.tsx +++ b/src/Bladeburner/ui/Stats.tsx @@ -52,27 +52,104 @@ export function Stats(props: IProps): React.ReactElement { "be logged in the Bladeburner Console."); } - return (

    - Rank: {formatNumber(props.bladeburner.rank, 2)}
    - Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)} + function openTravel() { + // var popupId = "bladeburner-travel-popup-cancel-btn"; + // var popupArguments = []; + // popupArguments.push(createElement("a", { // Cancel Button + // innerText:"Cancel", class:"a-link-button", + // clickListener:() => { + // removeElementById(popupId); return false; + // }, + // })) + // popupArguments.push(createElement("p", { // Info Text + // innerText:"Travel to a different city for your Bladeburner " + + // "activities. This does not cost any money. The city you are " + + // "in for your Bladeburner duties does not affect " + + // "your location in the game otherwise", + // })); + // for (var i = 0; i < BladeburnerConstants.CityNames.length; ++i) { + // (function(inst, i) { + // popupArguments.push(createElement("div", { + // // Reusing this css class...it adds a border and makes it + // // so that background color changes when you hover + // class:"cmpy-mgmt-find-employee-option", + // innerText:BladeburnerConstants.CityNames[i], + // clickListener:() => { + // inst.city = BladeburnerConstants.CityNames[i]; + // removeElementById(popupId); + // inst.updateOverviewContent(); + // return false; + // }, + // })); + // })(this, i); + // } + // createPopup(popupId, popupArguments); + } + + function openFaction() { + // if (bladeburnerFac.isMember) { + // Engine.loadFactionContent(); + // displayFactionContent(bladeburnersFactionName); + // } else { + // if (this.rank >= BladeburnerConstants.RankNeededForFaction) { + // joinFaction(bladeburnerFac); + // dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction"); + // removeChildrenFromElement(DomElems.overviewDiv); + // this.createOverviewContent(); + // } else { + // dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!") + // } + // } + } + + return (<> +

    + Rank: {formatNumber(props.bladeburner.rank, 2)} + Your rank within the Bladeburner division. +


    +

    Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}

    ?

    - Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)} +

    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}

    +

    + Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)} + This is your Bladeburner division's estimate of how many Synthoids exist in your current city. +

    ?

    - 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}
    +

    + Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)} + This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city. +


    +

    + City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)} + The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a chaos level can make contracts and operations harder. +


    +
    +

    + Bonus time: {convertTimeMsToTimeElapsedString(props.bladeburner.storedCycles/BladeburnerConstants.CyclesPerSecond*1000)}
    + + You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser). + Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed. + +

    +

    Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}


    {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 +
    + Travel + + Apply to the Bladeburner Faction, or go to the faction page if you are already a member + Faction + +
    +
    + ); +} From 99d4f17cdbead2f4a522229803ed377295fb8c6e Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Sun, 15 Aug 2021 21:49:08 -0400 Subject: [PATCH 04/12] work on blade to react --- src/Augmentation/Augmentation.tsx | 2 +- src/Bladeburner.jsx | 149 +---------------------- src/Bladeburner/ui/BlackOpElem.tsx | 43 ++----- src/Bladeburner/ui/BlackOpList.tsx | 8 +- src/Bladeburner/ui/Console.tsx | 108 +++++++++++++--- src/Bladeburner/ui/ContractList.tsx | 5 +- src/Bladeburner/ui/GeneralActionList.tsx | 5 +- src/Bladeburner/ui/OperationElem.tsx | 42 ++----- src/Bladeburner/ui/OperationList.tsx | 5 +- src/Bladeburner/ui/Root.tsx | 27 ++++ src/Bladeburner/ui/SkillList.tsx | 5 +- src/Bladeburner/ui/Stats.tsx | 71 ++++------- src/Bladeburner/ui/TeamSizePopup.tsx | 37 ++++++ src/Bladeburner/ui/TravelPopup.tsx | 31 +++++ src/Faction/ui/FactionList.tsx | 6 +- src/Gang/ui/TaskSelector.tsx | 2 +- utils/StringHelperFunctions.ts | 2 +- 17 files changed, 247 insertions(+), 301 deletions(-) create mode 100644 src/Bladeburner/ui/Root.tsx create mode 100644 src/Bladeburner/ui/TeamSizePopup.tsx create mode 100644 src/Bladeburner/ui/TravelPopup.tsx diff --git a/src/Augmentation/Augmentation.tsx b/src/Augmentation/Augmentation.tsx index d64dedc90..eeb6d2cbf 100644 --- a/src/Augmentation/Augmentation.tsx +++ b/src/Augmentation/Augmentation.tsx @@ -55,7 +55,7 @@ interface IConstructorParams { } function generateStatsDescription(mults: IMap, programs?: string[], startingMoney?: number): JSX.Element { - const f = (x: number, decimals: number = 0) => { + const f = (x: number, decimals = 0) => { // look, I don't know how to make a "smart decimals" // todo, make it smarter if(x === 1.0777-1) return "7.77%"; diff --git a/src/Bladeburner.jsx b/src/Bladeburner.jsx index 4693f99b2..8d3b8e158 100644 --- a/src/Bladeburner.jsx +++ b/src/Bladeburner.jsx @@ -52,24 +52,10 @@ 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 { 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 { AllPages } from "./Bladeburner/ui/AllPages"; import { Console } from "./Bladeburner/ui/Console"; +import { Root } from "./Bladeburner/ui/Root"; import { StatsTable } from "./ui/React/StatsTable"; import { CopyableText } from "./ui/React/CopyableText"; @@ -77,73 +63,6 @@ import { Money } from "./ui/React/Money"; import React from "react"; import ReactDOM from "react-dom"; -const stealthIcon = ` ` -const killIcon = `` - -// DOM related variables -const ActiveActionCssClass = "bladeburner-active-action"; - -// Console related stuff -let consoleHistoryIndex = 0; - -// Keypresses for Console -$(document).keydown(function(event) { - if (routing.isOn(Page.Bladeburner)) { - if (!(Player.bladeburner instanceof Bladeburner)) { return; } - let consoleHistory = Player.bladeburner.consoleHistory; - - if (event.keyCode === KEY.ENTER) { - event.preventDefault(); - var command = DomElems.consoleInput.value; - if (command.length > 0) { - Player.bladeburner.postToConsole("> " + command); - Player.bladeburner.resetConsoleInput(); - Player.bladeburner.executeConsoleCommands(command); - } - } - - if (event.keyCode === KEY.UPARROW) { - if (DomElems.consoleInput == null) {return;} - var i = consoleHistoryIndex; - var len = consoleHistory.length; - - if (len === 0) {return;} - if (i < 0 || i > len) { - consoleHistoryIndex = len; - } - - if (i !== 0) { - --consoleHistoryIndex; - } - - var prevCommand = consoleHistory[consoleHistoryIndex]; - DomElems.consoleInput.value = prevCommand; - setTimeoutRef(function(){DomElems.consoleInput.selectionStart = DomElems.consoleInput.selectionEnd = 10000; }, 0); - } - - if (event.keyCode === KEY.DOWNARROW) { - if (DomElems.consoleInput == null) {return;} - var i = consoleHistoryIndex; - var len = consoleHistory.length; - - if (len == 0) {return;} - if (i < 0 || i > len) { - consoleHistoryIndex = len; - } - - // Latest command, put nothing - if (i == len || i == len-1) { - consoleHistoryIndex = len; - DomElems.consoleInput.value = ""; - } else { - ++consoleHistoryIndex; - var prevCommand = consoleHistory[consoleHistoryIndex]; - DomElems.consoleInput.value = prevCommand; - } - } - } -}); - function ActionIdentifier(params={}) { if (params.name) {this.name = params.name;} if (params.type) {this.type = params.type;} @@ -1216,67 +1135,19 @@ let DomElems = {}; Bladeburner.prototype.initializeDomElementRefs = function() { DomElems = { - bladeburnerDiv: null, - - // Main Divs - overviewConsoleParentDiv: null, - - overviewDiv: null, // Overview of stats that stays fixed on left - actionAndSkillsDiv: null, // Panel for different sections (contracts, ops, skills) - - consoleDiv: null, - consoleTable: null, - consoleInputRow: null, // tr - consoleInputCell: null, // td - consoleInputHeader: null, // "> " - consoleInput: null, // Actual input element + bladeburnerDiv: null, }; } Bladeburner.prototype.createContent = function() { - DomElems.bladeburnerDiv = createElement("div", { - id:"bladeburner-container", position:"fixed", class:"generic-menupage-container", - }); + DomElems.bladeburnerDiv = createElement("div"); - // Parent Div for Overview and Console - DomElems.overviewConsoleParentDiv = createElement("div", { - height:"60%", display:"block", position:"relative", - }); - - // Overview and Action/Skill pane - DomElems.overviewDiv = createElement("div", { - width:"30%", display:"inline-block", border:"1px solid white", - }); - - DomElems.actionAndSkillsDiv = createElement("div", { - width:"70%", display:"block", - border:"1px solid white", margin:"6px", padding:"6px", - }); - - ReactDOM.render(, DomElems.overviewDiv); - ReactDOM.render(, DomElems.actionAndSkillsDiv); - - // Console - DomElems.consoleDiv = createElement("div", { - class:"bladeburner-console-div", - clickListener:() => { - if (DomElems.consoleInput instanceof Element) { - DomElems.consoleInput.focus(); - } - return false; - }, - }); - ReactDOM.render(, DomElems.consoleDiv); - - DomElems.overviewConsoleParentDiv.appendChild(DomElems.overviewDiv); - DomElems.overviewConsoleParentDiv.appendChild(DomElems.consoleDiv); - DomElems.bladeburnerDiv.appendChild(DomElems.overviewConsoleParentDiv); - DomElems.bladeburnerDiv.appendChild(DomElems.actionAndSkillsDiv); + ReactDOM.render(, DomElems.bladeburnerDiv); document.getElementById("entire-game-container").appendChild(DomElems.bladeburnerDiv); if (this.consoleLogs.length === 0) { - this.postToConsole("Bladeburner Console BETA"); + this.postToConsole("Bladeburner Console"); this.postToConsole("Type 'help' to see console commands"); } else { for (let i = 0; i < this.consoleLogs.length; ++i) { @@ -1323,16 +1194,7 @@ Bladeburner.prototype.postToConsole = function(input, saveToLogs=true) { } } - -Bladeburner.prototype.resetConsoleInput = function() { - DomElems.consoleInput.value = ""; -} - Bladeburner.prototype.clearConsole = function() { - while (DomElems.consoleTable.childNodes.length > 1) { - DomElems.consoleTable.removeChild(DomElems.consoleTable.firstChild); - } - this.consoleLogs.length = 0; } @@ -1351,7 +1213,6 @@ Bladeburner.prototype.executeConsoleCommands = function(commands) { this.consoleHistory.splice(0, 1); } } - consoleHistoryIndex = this.consoleHistory.length; const arrayOfCommands = commands.split(";"); for (let i = 0; i < arrayOfCommands.length; ++i) { diff --git a/src/Bladeburner/ui/BlackOpElem.tsx b/src/Bladeburner/ui/BlackOpElem.tsx index 3fd3268f9..d2afc6e22 100644 --- a/src/Bladeburner/ui/BlackOpElem.tsx +++ b/src/Bladeburner/ui/BlackOpElem.tsx @@ -6,6 +6,8 @@ import { import { ActionTypes } from "../data/ActionTypes"; import { createProgressBarText } from "../../../utils/helpers/createProgressBarText"; import { stealthIcon, killIcon } from "../data/Icons"; +import { createPopup } from "../../ui/React/createPopup"; +import { TeamSizePopup } from "./TeamSizePopup"; interface IProps { bladeburner: any; @@ -34,41 +36,12 @@ export function BlackOpElem(props: IProps): React.ReactElement { } 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(); + const popupId = "bladeburner-operation-set-team-size-popup"; + createPopup(popupId, TeamSizePopup, { + bladeburner: props.bladeburner, + action: props.action, + popupId: popupId, + }); } return (<> diff --git a/src/Bladeburner/ui/BlackOpList.tsx b/src/Bladeburner/ui/BlackOpList.tsx index eeb0f9907..542eecaab 100644 --- a/src/Bladeburner/ui/BlackOpList.tsx +++ b/src/Bladeburner/ui/BlackOpList.tsx @@ -25,18 +25,16 @@ export function BlackOpList(props: IProps): React.ReactElement { return (a.reqdRank - b.reqdRank); }); - blackops = blackops.filter((blackop: BlackOperation, i: number) => - !(props.bladeburner.blackops[blackops[i].name] == null && + 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) => -
  • + {blackops.map((blackop: BlackOperation) =>
  • -
  • + , )} ); } \ No newline at end of file diff --git a/src/Bladeburner/ui/Console.tsx b/src/Bladeburner/ui/Console.tsx index 7c4de7eb8..971af3c25 100644 --- a/src/Bladeburner/ui/Console.tsx +++ b/src/Bladeburner/ui/Console.tsx @@ -15,29 +15,97 @@ interface IProps { } export function Console(props: IProps): React.ReactElement { - const lastRef = useRef(null); + const lastRef = useRef(null); const setRerender = useState(false)[1]; - useEffect(() => { + const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length); + + // TODO: Figure out how to actually make the scrolling work correctly. + function scrollToBottom() { if(lastRef.current) - lastRef.current.scrollIntoView({block: "end", inline: "nearest", behavior: "smooth" }); - const id = setInterval(() => setRerender(old => !old), 1000); - return () => clearInterval(id); + lastRef.current.scrollTop = lastRef.current.scrollHeight; + } + + function rerender() { + setRerender(old => !old); + } + + useEffect(() => { + const id = setInterval(rerender, 1000); + const id2 = setInterval(scrollToBottom, 100); + return () => { + clearInterval(id); + clearInterval(id2); + }; }, []); - return ( - - {/* - TODO: optimize this. - using `i` as a key here isn't great because it'll re-render everything - everytime the console reaches max length. - */} - {props.bladeburner.consoleLogs.map((log: any, i: number) => )} - - - - -
    -
    {"> "}
    -
    ); + function handleKeyDown(event: React.KeyboardEvent): void { + if (event.keyCode === 13) { + event.preventDefault(); + const command = event.currentTarget.value; + event.currentTarget.value = ""; + if (command.length > 0) { + props.bladeburner.postToConsole("> " + command); + props.bladeburner.executeConsoleCommands(command); + setConsoleHistoryIndex(props.bladeburner.consoleHistory.length); + rerender(); + } + } + + const consoleHistory = props.bladeburner.consoleHistory; + + if (event.keyCode === 38) { // up + let i = consoleHistoryIndex; + const len = consoleHistory.length; + if (len === 0) {return;} + if (i < 0 || i > len) { + setConsoleHistoryIndex(len); + } + + if (i !== 0) { + i = i-1; + } + setConsoleHistoryIndex(i); + const prevCommand = consoleHistory[i]; + event.currentTarget.value = prevCommand; + } + + if (event.keyCode === 40) { + const i = consoleHistoryIndex; + const len = consoleHistory.length; + + if (len == 0) {return;} + if (i < 0 || i > len) { + setConsoleHistoryIndex(len); + } + + // Latest command, put nothing + if (i == len || i == len-1) { + setConsoleHistoryIndex(len); + event.currentTarget.value = ""; + } else { + setConsoleHistoryIndex(consoleHistoryIndex+1); + const prevCommand = consoleHistory[consoleHistoryIndex+1]; + event.currentTarget.value = prevCommand; + } + } + } + + return (
    + + + {/* + TODO: optimize this. + using `i` as a key here isn't great because it'll re-render everything + everytime the console reaches max length. + */} + {props.bladeburner.consoleLogs.map((log: any, i: number) => )} + + + + +
    +
    {"> "}
    +
    +
    ); } \ No newline at end of file diff --git a/src/Bladeburner/ui/ContractList.tsx b/src/Bladeburner/ui/ContractList.tsx index dcea7ea5e..6ba3b53f2 100644 --- a/src/Bladeburner/ui/ContractList.tsx +++ b/src/Bladeburner/ui/ContractList.tsx @@ -14,10 +14,9 @@ export function ContractList(props: IProps): React.ReactElement { const names = Object.keys(props.bladeburner.contracts); const contracts = props.bladeburner.contracts; return (<> - {names.map((name: string) => -
  • + {names.map((name: string) =>
  • -
  • + , )} ); } \ No newline at end of file diff --git a/src/Bladeburner/ui/GeneralActionList.tsx b/src/Bladeburner/ui/GeneralActionList.tsx index e459899d4..a099ae8e6 100644 --- a/src/Bladeburner/ui/GeneralActionList.tsx +++ b/src/Bladeburner/ui/GeneralActionList.tsx @@ -19,10 +19,9 @@ export function GeneralActionList(props: IProps): React.ReactElement { } } return (<> - {actions.map((action: Action) => -
  • + {actions.map((action: Action) =>
  • -
  • + , )} ); } \ No newline at end of file diff --git a/src/Bladeburner/ui/OperationElem.tsx b/src/Bladeburner/ui/OperationElem.tsx index 133778c8d..e4ac63898 100644 --- a/src/Bladeburner/ui/OperationElem.tsx +++ b/src/Bladeburner/ui/OperationElem.tsx @@ -7,6 +7,8 @@ import { } from "../../../utils/StringHelperFunctions"; import { stealthIcon, killIcon } from "../data/Icons"; import { BladeburnerConstants } from "../data/Constants"; +import { createPopup } from "../../ui/React/createPopup"; +import { TeamSizePopup } from "./TeamSizePopup"; interface IProps { bladeburner: any; @@ -30,40 +32,12 @@ export function OperationElem(props: IProps): React.ReactElement { } 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(); + const popupId = "bladeburner-operation-set-team-size-popup"; + createPopup(popupId, TeamSizePopup, { + bladeburner: props.bladeburner, + action: props.action, + popupId: popupId, + }); } function increaseLevel() { diff --git a/src/Bladeburner/ui/OperationList.tsx b/src/Bladeburner/ui/OperationList.tsx index d78d46351..73d07cb16 100644 --- a/src/Bladeburner/ui/OperationList.tsx +++ b/src/Bladeburner/ui/OperationList.tsx @@ -14,10 +14,9 @@ export function OperationList(props: IProps): React.ReactElement { const names = Object.keys(props.bladeburner.operations); const operations = props.bladeburner.operations; return (<> - {names.map((name: string) => -
  • + {names.map((name: string) =>
  • -
  • + , )} ); } \ No newline at end of file diff --git a/src/Bladeburner/ui/Root.tsx b/src/Bladeburner/ui/Root.tsx new file mode 100644 index 000000000..1c1f37106 --- /dev/null +++ b/src/Bladeburner/ui/Root.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { Stats } from "./Stats"; +import { Console } from "./Console"; +import { AllPages } from "./AllPages"; + +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { IEngine } from "../../IEngine"; + +interface IProps { + bladeburner: any; + engine: IEngine; + player: IPlayer; +} + +export function Root(props: IProps): React.ReactElement { + return (
    +
    +
    + +
    + +
    +
    + +
    +
    ); +} \ No newline at end of file diff --git a/src/Bladeburner/ui/SkillList.tsx b/src/Bladeburner/ui/SkillList.tsx index a7fbe2182..ca55033d0 100644 --- a/src/Bladeburner/ui/SkillList.tsx +++ b/src/Bladeburner/ui/SkillList.tsx @@ -9,10 +9,9 @@ interface IProps { export function SkillList(props: IProps): React.ReactElement { return (<> - {Object.keys(Skills).map((skill: string) => -
  • + {Object.keys(Skills).map((skill: string) =>
  • -
  • + , )} ); } \ No newline at end of file diff --git a/src/Bladeburner/ui/Stats.tsx b/src/Bladeburner/ui/Stats.tsx index e766d8c11..6a6899869 100644 --- a/src/Bladeburner/ui/Stats.tsx +++ b/src/Bladeburner/ui/Stats.tsx @@ -5,13 +5,23 @@ import { } from "../../../utils/StringHelperFunctions"; import { BladeburnerConstants } from "../data/Constants"; import { IPlayer } from "../../PersonObjects/IPlayer"; +import { IEngine } from "../../IEngine"; import { Money } from "../../ui/React/Money"; import { StatsTable } from "../../ui/React/StatsTable"; import { numeralWrapper } from "../../ui/numeralFormat"; import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { createPopup } from "../../ui/React/createPopup"; +import { Factions } from "../../Faction/Factions"; +import { + joinFaction, + displayFactionContent, +} from "../../Faction/FactionHelpers"; + +import { TravelPopup } from "./TravelPopup"; interface IProps { bladeburner: any; + engine: IEngine; player: IPlayer; } @@ -53,53 +63,26 @@ export function Stats(props: IProps): React.ReactElement { } function openTravel() { - // var popupId = "bladeburner-travel-popup-cancel-btn"; - // var popupArguments = []; - // popupArguments.push(createElement("a", { // Cancel Button - // innerText:"Cancel", class:"a-link-button", - // clickListener:() => { - // removeElementById(popupId); return false; - // }, - // })) - // popupArguments.push(createElement("p", { // Info Text - // innerText:"Travel to a different city for your Bladeburner " + - // "activities. This does not cost any money. The city you are " + - // "in for your Bladeburner duties does not affect " + - // "your location in the game otherwise", - // })); - // for (var i = 0; i < BladeburnerConstants.CityNames.length; ++i) { - // (function(inst, i) { - // popupArguments.push(createElement("div", { - // // Reusing this css class...it adds a border and makes it - // // so that background color changes when you hover - // class:"cmpy-mgmt-find-employee-option", - // innerText:BladeburnerConstants.CityNames[i], - // clickListener:() => { - // inst.city = BladeburnerConstants.CityNames[i]; - // removeElementById(popupId); - // inst.updateOverviewContent(); - // return false; - // }, - // })); - // })(this, i); - // } - // createPopup(popupId, popupArguments); + const popupId = "bladeburner-travel-popup"; + createPopup(popupId, TravelPopup, { + bladeburner: props.bladeburner, + popupId: popupId, + }); } function openFaction() { - // if (bladeburnerFac.isMember) { - // Engine.loadFactionContent(); - // displayFactionContent(bladeburnersFactionName); - // } else { - // if (this.rank >= BladeburnerConstants.RankNeededForFaction) { - // joinFaction(bladeburnerFac); - // dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction"); - // removeChildrenFromElement(DomElems.overviewDiv); - // this.createOverviewContent(); - // } else { - // dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!") - // } - // } + const faction = Factions["Bladeburners"]; + if (faction.isMember) { + props.engine.loadFactionContent(); + displayFactionContent("Bladeburners"); + } else { + if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) { + joinFaction(faction); + dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction"); + } else { + dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!") + } + } } return (<> diff --git a/src/Bladeburner/ui/TeamSizePopup.tsx b/src/Bladeburner/ui/TeamSizePopup.tsx new file mode 100644 index 000000000..4eb9b07db --- /dev/null +++ b/src/Bladeburner/ui/TeamSizePopup.tsx @@ -0,0 +1,37 @@ +import React, { useState } from "react"; +import { removePopup } from "../../ui/React/createPopup"; +import { BladeburnerConstants } from "../data/Constants"; +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { Action } from "../Action"; + +interface IProps { + bladeburner: any; + action: Action; + popupId: string; +} + +export function TeamSizePopup(props: IProps): React.ReactElement { + const [teamSize, setTeamSize] = useState(); + + function confirmTeamSize(): void { + if(teamSize === undefined) return; + const num = Math.round(teamSize); + if (isNaN(num) || num < 0) { + dialogBoxCreate("Invalid value entered for number of Team Members (must be numeric, positive)") + } else { + props.action.teamCount = num; + } + removePopup(props.popupId); + } + + return (<> +

    + Enter the amount of team members you would like to take on this + Op. 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. +

    + setTeamSize(parseFloat(event.target.value))} /> + Confirm + ); +} \ No newline at end of file diff --git a/src/Bladeburner/ui/TravelPopup.tsx b/src/Bladeburner/ui/TravelPopup.tsx new file mode 100644 index 000000000..3becd7cc4 --- /dev/null +++ b/src/Bladeburner/ui/TravelPopup.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { removePopup } from "../../ui/React/createPopup"; +import { BladeburnerConstants } from "../data/Constants"; + +interface IProps { + bladeburner: any; + popupId: string; +} + +export function TravelPopup(props: IProps): React.ReactElement { + function travel(city: string) { + props.bladeburner.city = city; + removePopup(props.popupId); + } + + return (<> +

    + Travel to a different city for your Bladeburner + activities. This does not cost any money. The city you are + in for your Bladeburner duties does not affect + your location in the game otherwise. +

    + {BladeburnerConstants.CityNames.map(city => + // Reusing this css class...it adds a border and makes it + // so that background color changes when you hover +
    travel(city)}> + {city} +
    )} + ); +} \ No newline at end of file diff --git a/src/Faction/ui/FactionList.tsx b/src/Faction/ui/FactionList.tsx index 3b001a4a3..15a6bda48 100644 --- a/src/Faction/ui/FactionList.tsx +++ b/src/Faction/ui/FactionList.tsx @@ -28,8 +28,7 @@ export function FactionList(props: IProps): React.ReactElement {

    Lists all factions you have joined