diff --git a/src/Bladeburner/Bladeburner.tsx b/src/Bladeburner/Bladeburner.tsx index 59ed0432d..430fbca33 100644 --- a/src/Bladeburner/Bladeburner.tsx +++ b/src/Bladeburner/Bladeburner.tsx @@ -17,7 +17,7 @@ import { IAction } from "./IAction"; import { IPlayer } from "../PersonObjects/IPlayer"; import { createTaskTracker, ITaskTracker } from "../PersonObjects/ITaskTracker"; import { IPerson } from "../PersonObjects/IPerson"; -import { IRouter, Page } from "../ui/Router"; +import { IRouter } from "../ui/Router"; import { ConsoleHelpText } from "./data/Help"; import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { getRandomInt } from "../utils/helpers/getRandomInt"; @@ -35,7 +35,6 @@ import { getTimestamp } from "../utils/helpers/getTimestamp"; import { joinFaction } from "../Faction/FactionHelpers"; import { WorkerScript } from "../Netscript/WorkerScript"; import { FactionNames } from "../Faction/data/FactionNames"; -import { BlackOperationNames } from "./data/BlackOperationNames"; import { KEY } from "../utils/helpers/keyCodes"; interface BlackOpsAttempt { @@ -237,13 +236,13 @@ export class Bladeburner implements IBladeburner { } } - upgradeSkill(skill: Skill): void { + upgradeSkill(skill: Skill, count = 1): void { // This does NOT handle deduction of skill points const skillName = skill.name; if (this.skills[skillName]) { - ++this.skills[skillName]; + this.skills[skillName] += count; } else { - this.skills[skillName] = 1; + this.skills[skillName] = count; } if (isNaN(this.skills[skillName]) || this.skills[skillName] < 0) { throw new Error("Level of Skill " + skillName + " is invalid: " + this.skills[skillName]); @@ -2277,7 +2276,7 @@ export class Bladeburner implements IBladeburner { } } - getSkillUpgradeCostNetscriptFn(skillName: string, workerScript: WorkerScript): number { + getSkillUpgradeCostNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): number { if (skillName === "" || !Skills.hasOwnProperty(skillName)) { workerScript.log("bladeburner.getSkillUpgradeCost", () => `Invalid skill: '${skillName}'`); return -1; @@ -2285,13 +2284,13 @@ export class Bladeburner implements IBladeburner { const skill = Skills[skillName]; if (this.skills[skillName] == null) { - return skill.calculateCost(0); + return skill.calculateCost(0, count); } else { - return skill.calculateCost(this.skills[skillName]); + return skill.calculateCost(this.skills[skillName], count); } } - upgradeSkillNetscriptFn(skillName: string, workerScript: WorkerScript): boolean { + upgradeSkillNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): boolean { const errorLogText = `Invalid skill: '${skillName}'`; if (!Skills.hasOwnProperty(skillName)) { workerScript.log("bladeburner.upgradeSkill", () => errorLogText); @@ -2303,10 +2302,10 @@ export class Bladeburner implements IBladeburner { if (this.skills[skillName] && !isNaN(this.skills[skillName])) { currentLevel = this.skills[skillName]; } - const cost = skill.calculateCost(currentLevel); + const cost = skill.calculateCost(currentLevel, count); - if (skill.maxLvl && currentLevel >= skill.maxLvl) { - workerScript.log("bladeburner.upgradeSkill", () => `Skill '${skillName}' is already maxed.`); + if (skill.maxLvl && currentLevel + count > skill.maxLvl) { + workerScript.log("bladeburner.upgradeSkill", () => `Skill '${skillName}' cannot be upgraded ${count} time(s).`); return false; } @@ -2314,13 +2313,13 @@ export class Bladeburner implements IBladeburner { workerScript.log( "bladeburner.upgradeSkill", () => - `You do not have enough skill points to upgrade ${skillName} (You have ${this.skillPoints}, you need ${cost})`, + `You do not have enough skill points to upgrade ${skillName} ${count} time(s). (You have ${this.skillPoints}, you need ${cost})`, ); return false; } this.skillPoints -= cost; - this.upgradeSkill(skill); + this.upgradeSkill(skill, count); workerScript.log("bladeburner.upgradeSkill", () => `'${skillName}' upgraded to level ${this.skills[skillName]}`); return true; } diff --git a/src/Bladeburner/IBladeburner.ts b/src/Bladeburner/IBladeburner.ts index 66f97e0f7..bcd0a3679 100644 --- a/src/Bladeburner/IBladeburner.ts +++ b/src/Bladeburner/IBladeburner.ts @@ -76,8 +76,8 @@ export interface IBladeburner { getActionEstimatedSuccessChanceNetscriptFn(person: IPerson, type: string, name: string): [number, number] | string; getActionCountRemainingNetscriptFn(type: string, name: string, workerScript: WorkerScript): number; getSkillLevelNetscriptFn(skillName: string, workerScript: WorkerScript): number; - getSkillUpgradeCostNetscriptFn(skillName: string, workerScript: WorkerScript): number; - upgradeSkillNetscriptFn(skillName: string, workerScript: WorkerScript): boolean; + getSkillUpgradeCostNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): number; + upgradeSkillNetscriptFn(skillName: string, count: number, workerScript: WorkerScript): boolean; getTeamSizeNetscriptFn(type: string, name: string, workerScript: WorkerScript): number; setTeamSizeNetscriptFn(type: string, name: string, size: number, workerScript: WorkerScript): number; joinBladeburnerFactionNetscriptFn(workerScript: WorkerScript): boolean; diff --git a/src/Bladeburner/Skill.ts b/src/Bladeburner/Skill.ts index 160429ed5..32c8bc8ea 100644 --- a/src/Bladeburner/Skill.ts +++ b/src/Bladeburner/Skill.ts @@ -133,8 +133,37 @@ export class Skill { } } - calculateCost(currentLevel: number): number { - return Math.floor((this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost); + calculateCost(currentLevel: number, count = 1): number { + //Recursive mode does not handle invalid inputs properly, but it should never + //be possible for it to run with them. For the sake of not crashing the game, + const recursiveMode = (currentLevel: number, count: number): number => { + if (count <= 1) { + return Math.floor((this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost); + } else { + const thisUpgrade = Math.floor( + (this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost, + ); + return this.calculateCost(currentLevel + 1, count - 1) + thisUpgrade; + } + }; + + //Count must be a positive integer. + if (count < 0 || count % 1 != 0) { + throw new Error(`${count} is an invalid number of upgrades`); + } + //Use recursive mode if count is small + if (count <= 100) { + return recursiveMode(currentLevel, count); + } + //Use optimized mode if count is large + else { + //unFloored is roughly equivalent to + //(this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost + //being repeated for increasing currentLevel + const preMult = (count * (2 * this.baseCost + this.costInc * (2 * currentLevel + count + 1))) / 2; + const unFloored = preMult * BitNodeMultipliers.BladeburnerSkillCost - count / 2; + return Math.floor(unFloored); + } } getMultiplier(name: string): number { diff --git a/src/Bladeburner/ui/GeneralActionElem.tsx b/src/Bladeburner/ui/GeneralActionElem.tsx index 7cd4b1dcc..b064b298d 100644 --- a/src/Bladeburner/ui/GeneralActionElem.tsx +++ b/src/Bladeburner/ui/GeneralActionElem.tsx @@ -74,7 +74,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement { diff --git a/src/DevMenu/ui/Augmentations.tsx b/src/DevMenu/ui/Augmentations.tsx index 4776264bf..7f129495c 100644 --- a/src/DevMenu/ui/Augmentations.tsx +++ b/src/DevMenu/ui/Augmentations.tsx @@ -22,7 +22,7 @@ export function Augmentations(props: IProps): React.ReactElement { const [augmentation, setAugmentation] = useState("Augmented Targeting I"); function setAugmentationDropdown(event: SelectChangeEvent): void { - setAugmentation(event.target.value ); + setAugmentation(event.target.value); } function queueAug(): void { props.player.queueAugmentation(augmentation); diff --git a/src/DevMenu/ui/CodingContracts.tsx b/src/DevMenu/ui/CodingContracts.tsx index 7ddd96494..3bc166268 100644 --- a/src/DevMenu/ui/CodingContracts.tsx +++ b/src/DevMenu/ui/CodingContracts.tsx @@ -15,7 +15,7 @@ import { CodingContractTypes } from "../../CodingContracts"; export function CodingContracts(): React.ReactElement { const [codingcontract, setCodingcontract] = useState("Find Largest Prime Factor"); function setCodingcontractDropdown(event: SelectChangeEvent): void { - setCodingcontract(event.target.value ); + setCodingcontract(event.target.value); } function specificContract(): void { diff --git a/src/DevMenu/ui/Companies.tsx b/src/DevMenu/ui/Companies.tsx index c1f98dc70..d3d40042a 100644 --- a/src/DevMenu/ui/Companies.tsx +++ b/src/DevMenu/ui/Companies.tsx @@ -18,7 +18,7 @@ const bigNumber = 1e12; export function Companies(): React.ReactElement { const [company, setCompany] = useState(FactionNames.ECorp as string); function setCompanyDropdown(event: SelectChangeEvent): void { - setCompany(event.target.value ); + setCompany(event.target.value); } function resetCompanyRep(): void { AllCompanies[company].playerReputation = 0; diff --git a/src/DevMenu/ui/Factions.tsx b/src/DevMenu/ui/Factions.tsx index 20322822e..6f1453ac1 100644 --- a/src/DevMenu/ui/Factions.tsx +++ b/src/DevMenu/ui/Factions.tsx @@ -29,7 +29,7 @@ export function Factions(props: IProps): React.ReactElement { const [faction, setFaction] = useState(FactionNames.Illuminati as string); function setFactionDropdown(event: SelectChangeEvent): void { - setFaction(event.target.value ); + setFaction(event.target.value); } function receiveInvite(): void { diff --git a/src/DevMenu/ui/Programs.tsx b/src/DevMenu/ui/Programs.tsx index 7b300e81e..830a99b2e 100644 --- a/src/DevMenu/ui/Programs.tsx +++ b/src/DevMenu/ui/Programs.tsx @@ -19,7 +19,7 @@ interface IProps { export function Programs(props: IProps): React.ReactElement { const [program, setProgram] = useState("NUKE.exe"); function setProgramDropdown(event: SelectChangeEvent): void { - setProgram(event.target.value ); + setProgram(event.target.value); } function addProgram(): void { if (!props.player.hasProgram(program)) { diff --git a/src/DevMenu/ui/Servers.tsx b/src/DevMenu/ui/Servers.tsx index b73a935b4..db1f0e53f 100644 --- a/src/DevMenu/ui/Servers.tsx +++ b/src/DevMenu/ui/Servers.tsx @@ -15,7 +15,7 @@ import MenuItem from "@mui/material/MenuItem"; export function Servers(): React.ReactElement { const [server, setServer] = useState("home"); function setServerDropdown(event: SelectChangeEvent): void { - setServer(event.target.value ); + setServer(event.target.value); } function rootServer(): void { const s = GetServer(server); diff --git a/src/Exploits/Unclickable.tsx b/src/Exploits/Unclickable.tsx index fbcfb3c1b..1ab524d9e 100644 --- a/src/Exploits/Unclickable.tsx +++ b/src/Exploits/Unclickable.tsx @@ -8,8 +8,8 @@ export function Unclickable(): React.ReactElement { function unclickable(event: React.MouseEvent): void { if (!event.target || !(event.target instanceof Element)) return; - const display = getComputedStyle(event.target ).display; - const visibility = getComputedStyle(event.target ).visibility; + const display = getComputedStyle(event.target).display; + const visibility = getComputedStyle(event.target).visibility; if (display === "none" && visibility === "hidden" && event.isTrusted) player.giveExploit(Exploit.Unclickable); } diff --git a/src/Hacknet/ui/HacknetNodeElem.tsx b/src/Hacknet/ui/HacknetNodeElem.tsx index b5909027c..c5ce99c10 100644 --- a/src/Hacknet/ui/HacknetNodeElem.tsx +++ b/src/Hacknet/ui/HacknetNodeElem.tsx @@ -54,7 +54,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement { multiplier = getMaxNumberLevelUpgrades(props.player, node, HacknetNodeConstants.MaxLevel); } else { const levelsToMax = HacknetNodeConstants.MaxLevel - node.level; - multiplier = Math.min(levelsToMax, purchaseMult ); + multiplier = Math.min(levelsToMax, purchaseMult); } const increase = @@ -94,7 +94,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement { multiplier = getMaxNumberRamUpgrades(props.player, node, HacknetNodeConstants.MaxRam); } else { const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram)); - multiplier = Math.min(levelsToMax, purchaseMult ); + multiplier = Math.min(levelsToMax, purchaseMult); } const increase = @@ -144,7 +144,7 @@ export function HacknetNodeElem(props: IProps): React.ReactElement { multiplier = getMaxNumberCoreUpgrades(props.player, node, HacknetNodeConstants.MaxCores); } else { const levelsToMax = HacknetNodeConstants.MaxCores - node.cores; - multiplier = Math.min(levelsToMax, purchaseMult ); + multiplier = Math.min(levelsToMax, purchaseMult); } const increase = diff --git a/src/NetscriptFunctions/Bladeburner.ts b/src/NetscriptFunctions/Bladeburner.ts index e96a4efbf..0cc5c47ea 100644 --- a/src/NetscriptFunctions/Bladeburner.ts +++ b/src/NetscriptFunctions/Bladeburner.ts @@ -279,26 +279,28 @@ export function NetscriptBladeburner(player: IPlayer, workerScript: WorkerScript }, getSkillUpgradeCost: (ctx: NetscriptContext) => - (_skillName: unknown): number => { + (_skillName: unknown, _count: unknown = 1): number => { const skillName = ctx.helper.string("skillName", _skillName); + const count = ctx.helper.number("count", _count); checkBladeburnerAccess(ctx); const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript); + return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, count, workerScript); } catch (e: any) { throw ctx.makeRuntimeErrorMsg(e); } }, upgradeSkill: (ctx: NetscriptContext) => - (_skillName: unknown): boolean => { + (_skillName: unknown, _count: unknown = 1): boolean => { const skillName = ctx.helper.string("skillName", _skillName); + const count = ctx.helper.number("count", _count); checkBladeburnerAccess(ctx); const bladeburner = player.bladeburner; if (bladeburner === null) throw new Error("Should not be called without Bladeburner"); try { - return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript); + return bladeburner.upgradeSkillNetscriptFn(skillName, count, workerScript); } catch (e: any) { throw ctx.makeRuntimeErrorMsg(e); } diff --git a/src/NetscriptFunctions/Corporation.ts b/src/NetscriptFunctions/Corporation.ts index 9ed771625..b6abb18e0 100644 --- a/src/NetscriptFunctions/Corporation.ts +++ b/src/NetscriptFunctions/Corporation.ts @@ -257,7 +257,7 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript function getMaterial(divisionName: string, cityName: string, materialName: string): Material { const warehouse = getWarehouse(divisionName, cityName); - const matName = (materialName ).replace(/ /g, ""); + const matName = materialName.replace(/ /g, ""); const material = warehouse.materials[matName]; if (material === undefined) throw new Error(`Invalid material name: '${materialName}'`); return material; @@ -725,9 +725,11 @@ export function NetscriptCorporation(player: IPlayer, workerScript: WorkerScript const employeeName = ctx.helper.string("employeeName", _employeeName); const job = ctx.helper.string("job", _job); const employee = getEmployee(divisionName, cityName, employeeName); - return netscriptDelay(["Training", "Unassigned"].includes(employee.pos) ? 0 : 1000, workerScript).then(function () { - return Promise.resolve(AssignJob(employee, job)); - }); + return netscriptDelay(["Training", "Unassigned"].includes(employee.pos) ? 0 : 1000, workerScript).then( + function () { + return Promise.resolve(AssignJob(employee, job)); + }, + ); }, hireEmployee: (ctx: NetscriptContext) => diff --git a/src/ScriptEditor/NetscriptDefinitions.d.ts b/src/ScriptEditor/NetscriptDefinitions.d.ts index 0177aee7e..bfed1d166 100644 --- a/src/ScriptEditor/NetscriptDefinitions.d.ts +++ b/src/ScriptEditor/NetscriptDefinitions.d.ts @@ -3080,28 +3080,30 @@ export interface Bladeburner { * @remarks * RAM cost: 4 GB * - * This function returns the number of skill points needed to upgrade the specified skill. + * This function returns the number of skill points needed to upgrade the specified skill the specified number of times. * * The function returns -1 if an invalid skill name is passed in. * * @param skillName - Name of skill. Case-sensitive and must be an exact match + * @param count - Number of times to upgrade the skill. Defaults to 1 if not specified. * @returns Number of skill points needed to upgrade the specified skill. */ - getSkillUpgradeCost(name: string): number; + getSkillUpgradeCost(name: string, count?: number): number; /** * Upgrade skill. * @remarks * RAM cost: 4 GB * - * Attempts to upgrade the specified Bladeburner skill. + * Attempts to upgrade the specified Bladeburner skill the specified number of times. * * Returns true if the skill is successfully upgraded, and false otherwise. * * @param skillName - Name of skill to be upgraded. Case-sensitive and must be an exact match + * @param count - Number of times to upgrade the skill. Defaults to 1 if not specified. * @returns true if the skill is successfully upgraded, and false otherwise. */ - upgradeSkill(name: string): boolean; + upgradeSkill(name: string, count?: number): boolean; /** * Get team size. diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index 24780072a..13c2a43d8 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -693,7 +693,7 @@ export function Root(props: IProps): React.ReactElement { if (server === null) throw new Error(`Server '${closingScript.hostname}' should not be null, but it is.`); const serverScriptIndex = server.scripts.findIndex((script) => script.filename === closingScript.fileName); - if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex ].code) { + if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex].code) { PromptEvent.emit({ txt: `Do you want to save changes to ${closingScript.fileName} on ${closingScript.hostname}?`, resolve: (result: boolean | string) => { diff --git a/src/StockMarket/Stock.ts b/src/StockMarket/Stock.ts index f6e27db31..d001e7e50 100644 --- a/src/StockMarket/Stock.ts +++ b/src/StockMarket/Stock.ts @@ -36,7 +36,7 @@ function toNumber(n: number | IMinMaxRange): number { return n; } case "object": { - const range = n ; + const range = n; value = getRandomInt(range.min, range.max); break; } diff --git a/src/StockMarket/StockMarket.tsx b/src/StockMarket/StockMarket.tsx index 208c6e765..96527f0f2 100644 --- a/src/StockMarket/StockMarket.tsx +++ b/src/StockMarket/StockMarket.tsx @@ -68,7 +68,7 @@ export function placeOrder( // Process to see if it should be executed immediately const processOrderRefs = { - stockMarket: StockMarket , + stockMarket: StockMarket, symbolToStockMap: SymbolToStockMap, }; processOrders(stock, order.type, order.pos, processOrderRefs); diff --git a/src/Terminal/commands/mv.ts b/src/Terminal/commands/mv.ts index 2961aa891..f7f04ad94 100644 --- a/src/Terminal/commands/mv.ts +++ b/src/Terminal/commands/mv.ts @@ -84,7 +84,7 @@ export function mv( script.filename = destPath; } else if (srcFile instanceof TextFile) { - const textFile = srcFile ; + const textFile = srcFile; if (!dest.endsWith(".txt")) { terminal.error(`Source and destination files must have the same type`); return; diff --git a/src/TextFile.ts b/src/TextFile.ts index 8ee024795..91d68c374 100644 --- a/src/TextFile.ts +++ b/src/TextFile.ts @@ -121,7 +121,7 @@ export function getTextFile(fn: string, server: BaseServer): TextFile | null { filename = removeLeadingSlash(filename); } - for (const file of server.textFiles ) { + for (const file of server.textFiles) { if (file.fn === filename) { return file; }