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