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,