Merge branch 'dev' into bugfix/2877-2

This commit is contained in:
hydroflame 2022-03-17 12:05:38 -04:00 committed by GitHub
commit 561219a0ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 669 additions and 418 deletions

@ -137,7 +137,7 @@ module.exports = {
"no-ex-assign": ["off"],
"no-extra-boolean-cast": ["error"],
"no-extra-parens": ["off"],
"no-extra-semi": ["off"],
"no-extra-semi": ["error"],
"no-eval": ["off"],
"no-extend-native": ["off"],
"no-extra-bind": ["error"],
@ -166,12 +166,12 @@ module.exports = {
"no-label-var": ["error"],
"no-labels": ["off"],
"no-lone-blocks": ["error"],
"no-lonely-if": ["off"],
"no-lonely-if": ["error"],
"no-loop-func": ["off"],
"no-magic-numbers": ["off"],
"no-mixed-operators": ["off"],
"no-mixed-requires": ["error"],
"no-mixed-spaces-and-tabs": ["off"],
"no-mixed-spaces-and-tabs": ["error"],
"no-multi-assign": ["off"],
"no-multi-spaces": ["off"],
"no-multi-str": ["error"],
@ -253,7 +253,7 @@ module.exports = {
"no-use-before-define": ["off"],
"no-useless-call": ["off"],
"no-useless-computed-key": ["error"],
"no-useless-concat": ["off"],
"no-useless-concat": ["error"],
"no-useless-constructor": ["error"],
"no-useless-escape": ["off"],
"no-useless-rename": [

89
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -6,10 +6,8 @@ Intelligence is a :ref:`stat <gameplay_stats>` that is unlocked by having
:ref:`Source-File 5 <gameplay_sourcefiles>` (i.e. Destroying BitNode-5).
Intelligence is unique because it is permanent and persistent. It never gets reset
back to 1. However, gaining Intelligence experience is extremely slow. The methods
of gaining Intelligence exp is also hidden. You won't know when you gain
experience and how much. It is a stat that gradually builds up as you continue
to play the game.
back to 1. However, gaining Intelligence experience is extremely slow. It is a stat
that gradually builds up as you continue to play the game.
Intelligence will boost your production for many actions in the game, including:

@ -542,28 +542,6 @@ Then in order to check its logs with 'tail' the same arguments must be used::
$ tail foo.script 10 50000
theme
^^^^^
$ theme [preset] | [#background #text #highlight]
Change the color of the game's user interface
This command can be called with a preset theme. Currently, the supported presets are:
* default
* muted
* solarized
However, you can also specify your own color scheme using hex values.
To do so, you must specify three hex color values for the background
color, the text color, and the highlight color. These hex values must
be preceded by a pound sign (#) and must be either 3 or 6 digits. Example::
$ theme #ffffff #385 #235012
A color picker such as Google's can be used to get your desired hex color values
top
^^^

@ -337,23 +337,21 @@ async function restoreIfNewerExists(window) {
let bestMatch;
if (!steam.data && !disk.data) {
log.info("No data to import");
} else {
} else if (!steam.data) {
// We'll just compare using the lastSave field for now.
if (!steam.data) {
log.debug('Best potential save match: Disk');
bestMatch = disk;
} else if (!disk.data) {
log.debug('Best potential save match: Steam Cloud');
bestMatch = steam;
} else if ((steam.data.lastSave >= disk.data.lastSave)
|| (steam.data.playtime + lowPlaytime > disk.data.playtime)) {
// We want to prioritze steam data if the playtime is very close
log.debug('Best potential save match: Steam Cloud');
bestMatch = steam;
} else {
log.debug('Best potential save match: disk');
bestMatch = disk;
}
log.debug('Best potential save match: Disk');
bestMatch = disk;
} else if (!disk.data) {
log.debug('Best potential save match: Steam Cloud');
bestMatch = steam;
} else if ((steam.data.lastSave >= disk.data.lastSave)
|| (steam.data.playtime + lowPlaytime > disk.data.playtime)) {
// We want to prioritze steam data if the playtime is very close
log.debug('Best potential save match: Steam Cloud');
bestMatch = steam;
} else {
log.debug('Best potential save match: disk');
bestMatch = disk;
}
if (bestMatch) {
if (bestMatch.data.lastSave > currentData.lastSave + 5000) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

49
package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "bitburner",
"version": "1.4.0",
"version": "1.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "bitburner",
"version": "1.4.0",
"version": "1.5.0",
"hasInstallScript": true,
"license": "SEE LICENSE IN license.txt",
"dependencies": {
@ -17,9 +17,12 @@
"@mui/icons-material": "^5.0.3",
"@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1",
"@types/bcrypt": "^5.0.0",
"@types/bcryptjs": "^2.4.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^5.0.0",
"bcryptjs": "^2.4.3",
"better-react-mathjax": "^1.0.3",
"clsx": "^1.1.1",
"date-fns": "^2.25.0",
@ -3977,6 +3980,19 @@
"@babel/types": "^7.3.0"
}
},
"node_modules/@types/bcrypt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz",
"integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/bcryptjs": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
"integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ=="
},
"node_modules/@types/escodegen": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@types/escodegen/-/escodegen-0.0.7.tgz",
@ -4068,8 +4084,7 @@
"node_modules/@types/node": {
"version": "16.10.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.4.tgz",
"integrity": "sha512-EITwVTX5B4nDjXjGeQAfXOrm+Jn+qNjDmyDRtWoD+wZsl/RDPRTFRKivs4Mt74iOFlLOrE5+Kf+p5yjyhm3+cA==",
"dev": true
"integrity": "sha512-EITwVTX5B4nDjXjGeQAfXOrm+Jn+qNjDmyDRtWoD+wZsl/RDPRTFRKivs4Mt74iOFlLOrE5+Kf+p5yjyhm3+cA=="
},
"node_modules/@types/numeral": {
"version": "0.0.25",
@ -5448,6 +5463,11 @@
"tweetnacl": "^0.14.3"
}
},
"node_modules/bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
},
"node_modules/better-react-mathjax": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/better-react-mathjax/-/better-react-mathjax-1.0.3.tgz",
@ -25344,6 +25364,19 @@
"@babel/types": "^7.3.0"
}
},
"@types/bcrypt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz",
"integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==",
"requires": {
"@types/node": "*"
}
},
"@types/bcryptjs": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
"integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ=="
},
"@types/escodegen": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/@types/escodegen/-/escodegen-0.0.7.tgz",
@ -25435,8 +25468,7 @@
"@types/node": {
"version": "16.10.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.4.tgz",
"integrity": "sha512-EITwVTX5B4nDjXjGeQAfXOrm+Jn+qNjDmyDRtWoD+wZsl/RDPRTFRKivs4Mt74iOFlLOrE5+Kf+p5yjyhm3+cA==",
"dev": true
"integrity": "sha512-EITwVTX5B4nDjXjGeQAfXOrm+Jn+qNjDmyDRtWoD+wZsl/RDPRTFRKivs4Mt74iOFlLOrE5+Kf+p5yjyhm3+cA=="
},
"@types/numeral": {
"version": "0.0.25",
@ -26527,6 +26559,11 @@
"tweetnacl": "^0.14.3"
}
},
"bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
},
"better-react-mathjax": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/better-react-mathjax/-/better-react-mathjax-1.0.3.tgz",

@ -17,9 +17,12 @@
"@mui/icons-material": "^5.0.3",
"@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1",
"@types/bcrypt": "^5.0.0",
"@types/bcryptjs": "^2.4.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^5.0.0",
"bcryptjs": "^2.4.3",
"better-react-mathjax": "^1.0.3",
"clsx": "^1.1.1",
"date-fns": "^2.25.0",

@ -481,7 +481,16 @@
"ID": "DEVMENU",
"Name": "Exploit: you're not meant to access this",
"Description": "Open the dev menu."
},
"RAINBOW": {
"ID": "RAINBOW",
"Name": "Exploit: rainbow",
"Description": "Make good use of the rainbow."
},
"TRUE_RECURSION": {
"ID": "TRUE_RECURSION",
"Name": "Exploit: true recursion",
"Description": "Beat BN1 in megabyteburner 2000."
}
}
}

@ -553,7 +553,8 @@ export const achievements: IMap<Achievement> = {
...achievementData["MAX_CACHE"],
Icon: "HASHNETCAP",
Visible: () => hasAccessToSF(Player, 9),
Condition: () => hasHacknetServers(Player) &&
Condition: () =>
hasHacknetServers(Player) &&
Player.hashManager.hashes === Player.hashManager.capacity &&
Player.hashManager.capacity > 0,
},
@ -729,6 +730,18 @@ export const achievements: IMap<Achievement> = {
Secret: true,
Condition: () => Player.exploits.includes(Exploit.YoureNotMeantToAccessThis),
},
RAINBOW: {
...achievementData["RAINBOW"],
Icon: "SF-1",
Secret: true,
Condition: () => Player.exploits.includes(Exploit.INeedARainbow),
},
TRUE_RECURSION: {
...achievementData["TRUE_RECURSION"],
Icon: "SF-1",
Secret: true,
Condition: () => Player.exploits.includes(Exploit.TrueRecursion),
},
};
// Steam has a limit of 100 achievement. So these were planned but commented for now.

@ -1,5 +1,7 @@
import React from "react";
import React, { useEffect } from "react";
import Typography from "@mui/material/Typography";
import { use } from "../../ui/Context";
import { Exploit } from "../../Exploits/Exploit";
const metaBB = "https://bitburner-official.github.io/bitburner-legacy/";
@ -10,6 +12,14 @@ const style = {
} as any;
export function BBCabinetRoot(): React.ReactElement {
const player = use.Player();
useEffect(() => {
window.addEventListener("message", function (this: Window, ev: MessageEvent<boolean>) {
if (ev.isTrusted && ev.origin == "https://bitburner-official.github.io" && ev.data) {
player.giveExploit(Exploit.TrueRecursion);
}
});
});
// prettier-ignore
const joystick =
<>

@ -148,7 +148,7 @@ function initAugmentations(): void {
name: AugmentationNames.HemoRecirculator,
moneyCost: 4.5e7,
repCost: 1e4,
info: "A heart implant that greatly increases the body's ability to effectively use and pump " + "blood.",
info: "A heart implant that greatly increases the body's ability to effectively use and pump blood.",
strength_mult: 1.08,
defense_mult: 1.08,
agility_mult: 1.08,
@ -430,7 +430,7 @@ function initAugmentations(): void {
repCost: 1.125e6,
moneyCost: 4.25e9,
info:
"Graphene is grafted and fused into the skeletal structure, " + "enhancing bone density and tensile strength.",
"Graphene is grafted and fused into the skeletal structure, enhancing bone density and tensile strength.",
strength_mult: 1.7,
defense_mult: 1.7,
});
@ -1085,7 +1085,7 @@ function initAugmentations(): void {
name: AugmentationNames.FocusWire,
repCost: 7.5e4,
moneyCost: 9e8,
info: "A cranial implant that stops procrastination by blocking specific neural pathways " + "in the brain.",
info: "A cranial implant that stops procrastination by blocking specific neural pathways in the brain.",
hacking_exp_mult: 1.05,
strength_exp_mult: 1.05,
defense_exp_mult: 1.05,
@ -1486,7 +1486,7 @@ function initAugmentations(): void {
name: AugmentationNames.SmartSonar,
repCost: 2.25e4,
moneyCost: 7.5e7,
info: "A cochlear implant that helps the player detect and locate enemies " + "using sound propagation.",
info: "A cochlear implant that helps the player detect and locate enemies using sound propagation.",
dexterity_mult: 1.1,
dexterity_exp_mult: 1.15,
crime_money_mult: 1.25,
@ -1703,7 +1703,7 @@ function initAugmentations(): void {
"The left arm of a legendary BitRunner who ascended beyond this world. " +
"It projects a light blue energy shield that protects the exposed inner parts. " +
"Even though it contains no weapons, the advanced tungsten titanium " +
"alloy increases the users strength to unbelievable levels. The augmentation " +
"alloy increases the user's strength to unbelievable levels. The augmentation " +
"gets more powerful over time for seemingly no reason.",
strength_mult: 2.7,
});
@ -2015,7 +2015,7 @@ function initAugmentations(): void {
repCost: 6.25e4,
moneyCost: 2.75e8,
info:
"Cybernetic arms created from plasteel and carbon fibers that completely replace " + "the user's organic arms.",
"Cybernetic arms created from plasteel and carbon fibers that completely replace the user's organic arms.",
strength_mult: 1.3,
dexterity_mult: 1.3,
});

@ -202,8 +202,8 @@ BitNodes["BitNode5"] = new BitNode(
Destroying this BitNode will give you Source-File 5, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants you a special new stat called Intelligence. Intelligence is
unique because it is permanent and persistent (it never gets reset back to 1). However gaining Intelligence
experience is much slower than other stats, and it is also hidden (you won't know when you gain experience and how
much). Higher Intelligence levels will boost your production for many actions in the game. <br />
experience is much slower than other stats. Higher Intelligence levels will boost your production for many actions
in the game. <br />
<br />
In addition, this Source-File will unlock the getBitNodeMultipliers() Netscript function and let you start with
Formulas.exe, and will also raise all of your hacking-related multipliers by:

@ -135,7 +135,7 @@ export class Action implements IAction {
if (this.decays.hasOwnProperty(decay)) {
if (this.decays[decay] > 1) {
throw new Error(
"Invalid decays when constructing " + "Action " + this.name + ". " + "Decay value cannot be greater than 1",
`Invalid decays when constructing Action ${this.name}. Decay value cannot be greater than 1`,
);
}
}

@ -8,7 +8,7 @@ export const Skills: IMap<Skill> = {};
Skills[SkillNames.BladesIntuition] = new Skill({
name: SkillNames.BladesIntuition,
desc:
"Each level of this skill increases your success chance " + "for all Contracts, Operations, and BlackOps by 3%",
"Each level of this skill increases your success chance for all Contracts, Operations, and BlackOps by 3%",
baseCost: 3,
costInc: 2.1,
successChanceAll: 3,
@ -33,14 +33,14 @@ export const Skills: IMap<Skill> = {};
});
Skills[SkillNames.DigitalObserver] = new Skill({
name: SkillNames.DigitalObserver,
desc: "Each level of this skill increases your success chance in " + "all Operations and BlackOps by 4%",
desc: "Each level of this skill increases your success chance in all Operations and BlackOps by 4%",
baseCost: 2,
costInc: 2.1,
successChanceOperation: 4,
});
Skills[SkillNames.Tracer] = new Skill({
name: SkillNames.Tracer,
desc: "Each level of this skill increases your success chance in " + "all Contracts by 4%",
desc: "Each level of this skill increases your success chance in all Contracts by 4%",
baseCost: 2,
costInc: 2.1,
successChanceContract: 4,
@ -67,7 +67,7 @@ export const Skills: IMap<Skill> = {};
});
Skills[SkillNames.EvasiveSystem] = new Skill({
name: SkillNames.EvasiveSystem,
desc: "Each level of this skill increases your effective " + "dexterity and agility for Bladeburner actions by 4%",
desc: "Each level of this skill increases your effective dexterity and agility for Bladeburner actions by 4%",
baseCost: 2,
costInc: 2.1,
effDex: 4,

@ -119,17 +119,17 @@ export function SellMaterial(mat: Material, amt: string, price: string): void {
try {
tempQty = eval(tempQty);
} catch (e) {
throw new Error("Invalid value or expression for sell price field: " + e);
throw new Error("Invalid value or expression for sell quantity field: " + e);
}
if (tempQty == null || isNaN(parseFloat(tempQty)) || parseFloat(tempQty) < 0) {
throw new Error("Invalid value or expression for sell price field");
throw new Error("Invalid value or expression for sell quantity field");
}
mat.sllman[0] = true;
mat.sllman[1] = q; //Use sanitized input
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
throw new Error("Invalid value for sell quantity field! Must be numeric or 'MAX'");
throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'");
} else {
let q = parseFloat(amt);
if (isNaN(q)) {
@ -156,10 +156,10 @@ export function SellProduct(product: Product, city: string, amt: string, price:
try {
temp = eval(temp);
} catch (e) {
throw new Error("Invalid value or expression for sell quantity field: " + e);
throw new Error("Invalid value or expression for sell price field: " + e);
}
if (temp == null || isNaN(parseFloat(temp)) || parseFloat(temp) < 0) {
throw new Error("Invalid value or expression for sell quantity field.");
throw new Error("Invalid value or expression for sell price field.");
}
product.sCost = price; //Use sanitized price
} else {
@ -184,11 +184,11 @@ export function SellProduct(product: Product, city: string, amt: string, price:
try {
temp = eval(temp);
} catch (e) {
throw new Error("Invalid value or expression for sell price field: " + e);
throw new Error("Invalid value or expression for sell quantity field: " + e);
}
if (temp == null || isNaN(parseFloat(temp)) || parseFloat(temp) < 0) {
throw new Error("Invalid value or expression for sell price field");
throw new Error("Invalid value or expression for sell quantity field");
}
if (all) {
for (let i = 0; i < cities.length; ++i) {
@ -201,7 +201,7 @@ export function SellProduct(product: Product, city: string, amt: string, price:
product.sllman[city][1] = qty; //Use sanitized input
}
} else if (isNaN(parseFloat(amt)) || parseFloat(amt) < 0) {
throw new Error("Invalid value for sell quantity field! Must be numeric");
throw new Error("Invalid value for sell quantity field! Must be numeric or 'PROD' or 'MAX'");
} else {
let qty = parseFloat(amt);
if (isNaN(qty)) {
@ -218,8 +218,7 @@ export function SellProduct(product: Product, city: string, amt: string, price:
product.sllman[city][0] = false;
product.sllman[city][1] = "";
}
} else {
if (all) {
} else if (all) {
for (let i = 0; i < cities.length; ++i) {
const tempCity = cities[i];
product.sllman[tempCity][0] = true;
@ -229,7 +228,6 @@ export function SellProduct(product: Product, city: string, amt: string, price:
product.sllman[city][0] = true;
product.sllman[city][1] = qty;
}
}
}
}

@ -227,13 +227,13 @@ export function resetIndustryResearchTrees(): void {
IndustryResearchTrees.Agriculture = getBaseResearchTreeCopy();
IndustryResearchTrees.Fishing = getBaseResearchTreeCopy();
IndustryResearchTrees.Mining = getBaseResearchTreeCopy();
IndustryResearchTrees.Food = getBaseResearchTreeCopy();
IndustryResearchTrees.Tobacco = getBaseResearchTreeCopy();
IndustryResearchTrees.Food = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Tobacco = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Chemical = getBaseResearchTreeCopy();
IndustryResearchTrees.Pharmaceutical = getBaseResearchTreeCopy();
IndustryResearchTrees.Computer = getBaseResearchTreeCopy();
IndustryResearchTrees.Robotics = getBaseResearchTreeCopy();
IndustryResearchTrees.Software = getBaseResearchTreeCopy();
IndustryResearchTrees.Healthcare = getBaseResearchTreeCopy();
IndustryResearchTrees.RealEstate = getBaseResearchTreeCopy();
IndustryResearchTrees.Pharmaceutical = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Computer = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Robotics = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Software = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.Healthcare = getProductIndustryResearchTreeCopy();
IndustryResearchTrees.RealEstate = getProductIndustryResearchTreeCopy();
}

@ -106,7 +106,7 @@ export const researchMetadata: IConstructorParams[] = [
{
name: "JoyWire",
cost: 20e3,
desc: "A brain implant which is installed in employees, increasing their " + "maximum happiness by 10.",
desc: "A brain implant which is installed in employees, increasing their maximum happiness by 10.",
},
{
name: "Market-TA.I",
@ -160,7 +160,7 @@ export const researchMetadata: IConstructorParams[] = [
{
name: "sudo.Assist",
cost: 15e3,
desc: "Develop a virtual assistant AI to handle and manage administrative " + "issues for your corporation.",
desc: "Develop a virtual assistant AI to handle and manage administrative issues for your corporation.",
},
{
name: "uPgrade: Capacity.I",

@ -33,8 +33,7 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
function throwParty(): void {
if (cost === null || isNaN(cost) || cost < 0) {
dialogBoxCreate("Invalid value entered");
} else {
if (!canParty) {
} else if (!canParty) {
dialogBoxCreate("You don't have enough company funds to throw a party!");
} else {
const mult = ThrowParty(corp, props.office, cost);
@ -46,7 +45,6 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
props.rerender();
props.onClose();
}
}
}
function EffectText(): React.ReactElement {

@ -19,6 +19,8 @@ export enum Exploit {
RealityAlteration = "RealityAlteration",
N00dles = "N00dles",
YoureNotMeantToAccessThis = "YoureNotMeantToAccessThis",
TrueRecursion = "TrueRecursion",
INeedARainbow = "INeedARainbow",
// To the players reading this. Yes you're supposed to add EditSaveFile by
// editing your save file, yes you could add them all, no we don't care
// that's not the point.
@ -37,6 +39,8 @@ const names: {
RealityAlteration: "by altering reality to suit your whims.",
N00dles: "by harnessing the power of the n00dles.",
YoureNotMeantToAccessThis: "by accessing the dev menu.",
TrueRecursion: "by truly recursing.",
INeedARainbow: "by using the power of the rainbow.",
};
export function ExploitName(exploit: string): string {

@ -117,8 +117,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
const factionInfo = fac.getInfo();
const hasPrereqs = hasAugmentationPrereqs(aug);
if (!hasPrereqs) {
const txt =
"You must first purchase or install " + aug.prereqs.join(",") + " before you can " + "purchase this one.";
const txt = `You must first purchase or install ${aug.prereqs.join(",")} before you can purchase this one.`;
if (sing) {
return txt;
} else {
@ -166,8 +165,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
if (sing) {
return "You purchased " + aug.name;
} else {
if (!Settings.SuppressBuyAugmentationConfirmation) {
} else if (!Settings.SuppressBuyAugmentationConfirmation) {
dialogBoxCreate(
"You purchased " +
aug.name +
@ -177,7 +175,6 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
"augmentations will now be more expensive.",
);
}
}
} else {
dialogBoxCreate(
"Hmm, something went wrong when trying to purchase an Augmentation. " +

@ -28,7 +28,7 @@ type IProps = {
};
// Info text for all options on the UI
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and " + "faction reputation";
const gangInfo = "Create and manage a gang for this Faction. Gangs will earn you money and faction reputation";
const hackingContractsInfo =
"Complete hacking contracts for your faction. " +
"Your effectiveness, which determines how much " +

@ -1,14 +1,21 @@
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Container from "@mui/material/Container";
import Paper from "@mui/material/Paper";
import TableBody from "@mui/material/TableBody";
import TableRow from "@mui/material/TableRow";
import Typography from "@mui/material/Typography";
import React, { useEffect, useState } from "react";
import {
Box,
Button,
Container,
Paper,
TableBody,
TableRow,
Typography
} from "@mui/material";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Table, TableCell } from "../../ui/React/Table";
import { IRouter } from "../../ui/Router";
import { Faction } from "../Faction";
import { joinFaction } from "../FactionHelpers";
import { Factions } from "../Factions";
@ -51,6 +58,28 @@ export function FactionsRoot(props: IProps): React.ReactElement {
setRerender((x) => !x);
}
const getAugsLeft = (faction: Faction, player: IPlayer): number => {
const isPlayersGang = player.inGang() && player.getGangName() === faction.name;
let augs: string[] = [];
if (isPlayersGang) {
for (const augName of Object.keys(Augmentations)) {
if (
augName === AugmentationNames.NeuroFluxGovernor ||
augName === AugmentationNames.TheRedPill && player.bitNodeN !== 2 ||
Augmentations[augName].isSpecial
) continue;
augs.push(augName)
}
} else {
augs = faction.augmentations.slice();
}
return augs.filter(
(augmentation: string) => !player.hasAugmentation(augmentation)
).length;
}
return (
<Container disableGutters maxWidth="md" sx={{ mx: 0, mb: 10 }}>
<Typography variant="h4">Factions</Typography>
@ -82,11 +111,7 @@ export function FactionsRoot(props: IProps): React.ReactElement {
<TableCell align="right">
<Box ml={1} mb={1}>
<Button sx={{ width: '100%' }} onClick={() => openFactionAugPage(Factions[faction])}>
Augmentations Left: {Factions[faction]
.augmentations
.filter((augmentation: string) =>
!props.player.hasAugmentation(augmentation))
.length}
Augmentations Left: {getAugsLeft(Factions[faction], props.player)}
</Button>
</Box>
</TableCell>

@ -52,21 +52,19 @@ export function SpecialLocation(props: IProps): React.ReactElement {
if (p.inBladeburner()) {
// Enter Bladeburner division
router.toBladeburner();
} else {
} else if (p.strength >= 100 && p.defense >= 100 && p.dexterity >= 100 && p.agility >= 100) {
// Apply for Bladeburner division
if (p.strength >= 100 && p.defense >= 100 && p.dexterity >= 100 && p.agility >= 100) {
p.startBladeburner({ new: true });
dialogBoxCreate("You have been accepted into the Bladeburner division!");
setRerender((old) => !old);
p.startBladeburner({new: true});
dialogBoxCreate("You have been accepted into the Bladeburner division!");
setRerender((old) => !old);
const worldHeader = document.getElementById("world-menu-header");
if (worldHeader instanceof HTMLElement) {
worldHeader.click();
worldHeader.click();
}
} else {
dialogBoxCreate("Rejected! Please apply again when you have 100 of each combat stat (str, def, dex, agi)");
const worldHeader = document.getElementById("world-menu-header");
if (worldHeader instanceof HTMLElement) {
worldHeader.click();
worldHeader.click();
}
} else {
dialogBoxCreate("Rejected! Please apply again when you have 100 of each combat stat (str, def, dex, agi)");
}
}

@ -108,6 +108,7 @@ export const RamCosts: IMap<any> = {
hackAnalyzeSecurity: RamCostConstants.ScriptHackAnalyzeRamCost,
hackAnalyzeChance: RamCostConstants.ScriptHackAnalyzeRamCost,
sleep: 0,
asleep: 0,
share: 2.4,
getSharePower: 0.2,
grow: RamCostConstants.ScriptGrowRamCost,

@ -174,7 +174,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
throw makeRuntimeRejectMsg(
workerScript,
`Invalid scriptArgs argument passed into getRunningScript() from ${callingFnName}(). ` +
`This is probably a bug. Please report to game developer`,
`This is probably a bug. Please report to game developer`,
);
}
@ -425,19 +425,22 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
const helper = {
updateDynamicRam: updateDynamicRam,
makeRuntimeErrorMsg: makeRuntimeErrorMsg,
string: (funcName: string, argName: string, v: any): string => {
string: (funcName: string, argName: string, v: unknown): string => {
if (typeof v === "string") return v;
if (typeof v === "number") return v + ""; // cast to string;
throw makeRuntimeErrorMsg(funcName, `${argName} should be a string`);
},
number: (funcName: string, argName: string, v: any): number => {
if (!isNaN(v)) {
if (typeof v === "number") return v;
if (!isNaN(parseFloat(v))) return parseFloat(v);
number: (funcName: string, argName: string, v: unknown): number => {
if (typeof v === "string") {
const x = parseFloat(v);
if (!isNaN(x)) return x; // otherwise it wasn't even a string representing a number.
} else if (typeof v === "number") {
if (isNaN(v)) throw makeRuntimeErrorMsg(funcName, `${argName} is NaN`);
return v;
}
throw makeRuntimeErrorMsg(funcName, `${argName} should be a number`);
},
boolean: (v: any): boolean => {
boolean: (v: unknown): boolean => {
return !!v; // Just convert it to boolean.
},
getServer: safeGetServer,
@ -467,7 +470,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
const gang = NetscriptGang(Player, workerScript, helper);
const sleeve = NetscriptSleeve(Player, workerScript, helper);
const extra = NetscriptExtra(Player, workerScript);
const extra = NetscriptExtra(Player, workerScript, helper);
const hacknet = NetscriptHacknet(Player, workerScript, helper);
const stanek = NetscriptStanek(Player, workerScript, helper);
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
@ -550,6 +553,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return calculatePercentMoneyHacked(server, Player);
},
hackAnalyzeSecurity: function (threads: any): number {
updateDynamicRam("hackAnalyzeSecurity", getRamCost(Player, "hackAnalyzeSecurity"));
return CONSTANTS.ServerFortifyAmount * threads;
},
hackAnalyzeChance: function (hostname: any): any {
@ -564,6 +568,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return calculateHackingChance(server, Player);
},
sleep: function (time: any): any {
updateDynamicRam("sleep", getRamCost(Player, "sleep"));
if (time === undefined) {
throw makeRuntimeErrorMsg("sleep", "Takes 1 argument.");
}
@ -573,6 +578,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
});
},
asleep: function (time: any): any {
updateDynamicRam("asleep", getRamCost(Player, "asleep"));
if (time === undefined) {
throw makeRuntimeErrorMsg("asleep", "Takes 1 argument.");
}
@ -650,6 +656,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return numCycleForGrowth(server, Number(growth), Player, cores);
},
growthAnalyzeSecurity: function (threads: any): number {
updateDynamicRam("growthAnalyzeSecurity", getRamCost(Player, "growthAnalyzeSecurity"));
return 2 * CONSTANTS.ServerFortifyAmount * threads;
},
weaken: function (hostname: any, { threads: requestedThreads }: any = {}): any {
@ -692,7 +699,8 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
workerScript.log(
"weaken",
() =>
`'${server.hostname}' security level weakened to ${server.hackDifficulty
`'${server.hostname}' security level weakened to ${
server.hackDifficulty
}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`,
);
workerScript.scriptRef.onlineExpGained += expGain;
@ -701,10 +709,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
});
},
weakenAnalyze: function (threads: any, cores: any = 1): number {
updateDynamicRam("weakenAnalyze", getRamCost(Player, "weakenAnalyze"));
const coreBonus = 1 + (cores - 1) / 16;
return CONSTANTS.ServerWeakenAmount * threads * coreBonus * BitNodeMultipliers.ServerWeakenRate;
},
share: function (): Promise<void> {
updateDynamicRam("share", getRamCost(Player, "share"));
workerScript.log("share", () => "Sharing this computer.");
const end = StartSharing(workerScript.scriptRef.threads * calculateIntelligenceBonus(Player.intelligence, 2));
return netscriptDelay(10000, workerScript).finally(function () {
@ -713,21 +723,25 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
});
},
getSharePower: function (): number {
updateDynamicRam("getSharePower", getRamCost(Player, "getSharePower"));
return CalculateShareMult();
},
print: function (...args: any[]): void {
updateDynamicRam("print", getRamCost(Player, "print"));
if (args.length === 0) {
throw makeRuntimeErrorMsg("print", "Takes at least 1 argument.");
}
workerScript.print(argsToString(args));
},
printf: function (format: string, ...args: any[]): void {
updateDynamicRam("printf", getRamCost(Player, "printf"));
if (typeof format !== "string") {
throw makeRuntimeErrorMsg("printf", "First argument must be string for the format.");
}
workerScript.print(vsprintf(format, args));
},
tprint: function (...args: any[]): void {
updateDynamicRam("tprint", getRamCost(Player, "tprint"));
if (args.length === 0) {
throw makeRuntimeErrorMsg("tprint", "Takes at least 1 argument.");
}
@ -751,6 +765,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
Terminal.print(`${workerScript.scriptRef.filename}: ${str}`);
},
tprintf: function (format: any, ...args: any): any {
updateDynamicRam("tprintf", getRamCost(Player, "tprintf"));
if (typeof format !== "string") {
throw makeRuntimeErrorMsg("tprintf", "First argument must be string for the format.");
}
@ -775,9 +790,11 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
Terminal.print(`${str}`);
},
clearLog: function (): any {
updateDynamicRam("clearLog", getRamCost(Player, "clearLog"));
workerScript.scriptRef.clearLog();
},
disableLog: function (fn: any): any {
updateDynamicRam("disableLog", getRamCost(Player, "disableLog"));
if (fn === "ALL") {
for (fn of Object.keys(possibleLogs)) {
workerScript.disableLogs[fn] = true;
@ -791,6 +808,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
}
},
enableLog: function (fn: any): any {
updateDynamicRam("enableLog", getRamCost(Player, "enableLog"));
if (fn === "ALL") {
for (fn of Object.keys(possibleLogs)) {
delete workerScript.disableLogs[fn];
@ -803,12 +821,14 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
workerScript.log("enableLog", () => `Enabled logging for ${fn}`);
},
isLogEnabled: function (fn: any): any {
updateDynamicRam("isLogEnabled", getRamCost(Player, "isLogEnabled"));
if (possibleLogs[fn] === undefined) {
throw makeRuntimeErrorMsg("isLogEnabled", `Invalid argument: ${fn}.`);
}
return !workerScript.disableLogs[fn];
},
getScriptLogs: function (fn: any, hostname: any, ...scriptArgs: any): any {
updateDynamicRam("getScriptLogs", getRamCost(Player, "getScriptLogs"));
const runningScriptObj = getRunningScript(fn, hostname, "getScriptLogs", scriptArgs);
if (runningScriptObj == null) {
workerScript.log("getScriptLogs", () => getCannotFindRunningScriptErrorMessage(fn, hostname, scriptArgs));
@ -818,6 +838,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return runningScriptObj.logs.slice();
},
tail: function (fn: any, hostname: any = workerScript.hostname, ...scriptArgs: any): any {
updateDynamicRam("tail", getRamCost(Player, "tail"));
let runningScriptObj;
if (arguments.length === 0) {
runningScriptObj = workerScript.scriptRef;
@ -1084,6 +1105,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return scriptsRunning;
},
exit: function (): any {
updateDynamicRam("exit", getRamCost(Player, "exit"));
workerScript.running = false; // Prevent workerScript from "finishing execution naturally"
if (killWorkerScript(workerScript)) {
workerScript.log("exit", () => "Exiting...");
@ -1647,7 +1669,10 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
const cost = getPurchaseServerCost(ram);
if (cost === Infinity) {
if (ram > getPurchaseServerMaxRam()) {
workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`);
workerScript.log(
"purchaseServer",
() => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`,
);
} else {
workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must be a positive power of 2`);
}
@ -1769,6 +1794,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return res;
},
writePort: function (port: any, data: any = ""): any {
updateDynamicRam("writePort", getRamCost(Player, "writePort"));
if (typeof data !== "string" && typeof data !== "number") {
throw makeRuntimeErrorMsg(
"writePort",
@ -1857,6 +1883,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
}
},
readPort: function (port: any): any {
updateDynamicRam("readPort", getRamCost(Player, "readPort"));
// Read from port
const iport = helper.getValidPort("readPort", port);
const x = iport.read();
@ -1916,6 +1943,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return 0;
},
clearPort: function (port: any): any {
updateDynamicRam("clearPort", getRamCost(Player, "clearPort"));
// Clear port
const iport = helper.getValidPort("clearPort", port);
return iport.clear();
@ -1964,6 +1992,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return suc;
},
getScriptName: function (): any {
updateDynamicRam("getScriptName", getRamCost(Player, "getScriptName"));
return workerScript.name;
},
getScriptRam: function (scriptname: any, hostname: any = workerScript.hostname): any {
@ -2095,6 +2124,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
}
},
nFormat: function (n: any, format: any): any {
updateDynamicRam("nFormat", getRamCost(Player, "nFormat"));
if (isNaN(n) || isNaN(parseFloat(n)) || typeof format !== "string") {
return "";
}
@ -2102,6 +2132,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return numeralWrapper.format(parseFloat(n), format);
},
tFormat: function (milliseconds: any, milliPrecision: any = false): any {
updateDynamicRam("tFormat", getRamCost(Player, "tFormat"));
return convertTimeMsToTimeElapsedString(milliseconds, milliPrecision);
},
getTimeSinceLastAug: function (): any {
@ -2109,10 +2140,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return Player.playtimeSinceLastAug;
},
alert: function (message: any): void {
updateDynamicRam("alert", getRamCost(Player, "alert"));
message = argsToString([message]);
dialogBoxCreate(message);
},
toast: function (message: any, variant: any = "success", duration: any = 2000): void {
updateDynamicRam("toast", getRamCost(Player, "toast"));
if (!["success", "info", "warning", "error"].includes(variant))
throw new Error(`variant must be one of "success", "info", "warning", or "error"`);
@ -2120,6 +2153,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
SnackbarEvents.emit(message, variant, duration);
},
prompt: function (txt: any): any {
updateDynamicRam("prompt", getRamCost(Player, "prompt"));
if (!isString(txt)) {
txt = JSON.stringify(txt);
}
@ -2132,6 +2166,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
});
},
wget: async function (url: any, target: any, hostname: any = workerScript.hostname): Promise<boolean> {
updateDynamicRam("wget", getRamCost(Player, "wget"));
if (!isScriptFilename(target) && !target.endsWith(".txt")) {
workerScript.log("wget", () => `Invalid target file: '${target}'. Must be a script or text file.`);
return Promise.resolve(false);
@ -2173,7 +2208,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
},
getOwnedSourceFiles: function (): SourceFileLvl[] {
helper.updateDynamicRam("getOwnedSourceFiles", getRamCost(Player, "getOwnedSourceFiles"));
updateDynamicRam("getOwnedSourceFiles", getRamCost(Player, "getOwnedSourceFiles"));
const res: SourceFileLvl[] = [];
for (let i = 0; i < Player.sourceFiles.length; ++i) {
res.push({
@ -2184,7 +2219,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return res;
},
getPlayer: function (): INetscriptPlayer {
helper.updateDynamicRam("getPlayer", getRamCost(Player, "getPlayer"));
updateDynamicRam("getPlayer", getRamCost(Player, "getPlayer"));
const data = {
hacking: Player.hacking,
@ -2273,16 +2308,20 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
jobs: {},
factions: Player.factions.slice(),
tor: Player.hasTorRouter(),
inBladeburner: Player.inBladeburner(),
hasCorporation: Player.hasCorporation(),
};
Object.assign(data.jobs, Player.jobs);
return data;
},
atExit: function (f: any): void {
updateDynamicRam("atExit", getRamCost(Player, "atExit"));
if (typeof f !== "function") {
throw makeRuntimeErrorMsg("atExit", "argument should be function");
}
workerScript.atExit = () => { f(); }; // Wrap the user function to prevent WorkerScript leaking as 'this'
workerScript.atExit = () => {
f();
}; // Wrap the user function to prevent WorkerScript leaking as 'this'
},
mv: function (host: string, source: string, destination: string): void {
updateDynamicRam("mv", getRamCost(Player, "mv"));

@ -1,6 +1,8 @@
import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer";
import { Exploit } from "../Exploits/Exploit";
import * as bcrypt from "bcryptjs";
import { INetscriptHelper } from "./INetscriptHelper";
export interface INetscriptExtra {
heart: {
@ -9,9 +11,10 @@ export interface INetscriptExtra {
exploit(): void;
bypass(doc: Document): void;
alterReality(): void;
rainbow(guess: string): void;
}
export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript): INetscriptExtra {
export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): INetscriptExtra {
return {
heart: {
// Easter egg function
@ -22,17 +25,18 @@ export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript): INe
exploit: function (): void {
player.giveExploit(Exploit.UndocumentedFunctionCall);
},
bypass: function (doc: any): void {
bypass: function (doc: unknown): void {
// reset both fields first
doc.completely_unused_field = undefined;
const d = doc as any;
d.completely_unused_field = undefined;
const real_document: any = document;
real_document.completely_unused_field = undefined;
// set one to true and check that it affected the other.
real_document.completely_unused_field = true;
if (doc.completely_unused_field && workerScript.ramUsage === 1.6) {
if (d.completely_unused_field && workerScript.ramUsage === 1.6) {
player.giveExploit(Exploit.Bypass);
}
doc.completely_unused_field = undefined;
d.completely_unused_field = undefined;
real_document.completely_unused_field = undefined;
},
alterReality: function (): void {
@ -50,5 +54,17 @@ export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript): INe
player.giveExploit(Exploit.RealityAlteration);
}
},
rainbow: function (guess: unknown): void {
async function tryGuess(): Promise<void> {
const verified = await bcrypt.compare(
helper.string("rainbow", "guess", guess),
"$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO",
);
if (verified) {
player.giveExploit(Exploit.INeedARainbow);
}
}
tryGuess();
},
};
}

@ -38,40 +38,6 @@ import {
calculateAscensionPointsGain,
} from "../Gang/formulas/formulas";
export interface INetscriptFormulas {
skills: {
calculateSkill(exp: any, mult?: any): any;
calculateExp(skill: any, mult?: any): any;
};
hacking: {
hackChance(server: any, player: any): any;
hackExp(server: any, player: any): any;
hackPercent(server: any, player: any): any;
growPercent(server: any, threads: any, player: any, cores?: any): any;
hackTime(server: any, player: any): any;
growTime(server: any, player: any): any;
weakenTime(server: any, player: any): any;
};
hacknetNodes: {
moneyGainRate(level: any, ram: any, cores: any, mult?: any): any;
levelUpgradeCost(startingLevel: any, extraLevels?: any, costMult?: any): any;
ramUpgradeCost(startingRam: any, extraLevels?: any, costMult?: any): any;
coreUpgradeCost(startingCore: any, extraCores?: any, costMult?: any): any;
hacknetNodeCost(n: any, mult: any): any;
constants(): any;
};
hacknetServers: {
hashGainRate(level: any, ramUsed: any, maxRam: any, cores: any, mult?: any): any;
levelUpgradeCost(startingLevel: any, extraLevels?: any, costMult?: any): any;
ramUpgradeCost(startingRam: any, extraLevels?: any, costMult?: any): any;
coreUpgradeCost(startingCore: any, extraCores?: any, costMult?: any): any;
cacheUpgradeCost(startingCache: any, extraCache?: any): any;
hashUpgradeCost(upgName: any, level: any): any;
hacknetServerCost(n: any, mult: any): any;
constants(): any;
};
}
export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IFormulas {
const checkFormulasAccess = function (func: string): void {
if (!player.hasProgram(Programs.Formulas.name)) {
@ -80,63 +46,84 @@ export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, h
};
return {
skills: {
calculateSkill: function (exp: any, mult: any = 1): any {
calculateSkill: function (_exp: unknown, _mult: unknown = 1): number {
const exp = helper.number("calculateSkill", "exp", _exp);
const mult = helper.number("calculateSkill", "mult", _mult);
checkFormulasAccess("skills.calculateSkill");
return calculateSkill(exp, mult);
},
calculateExp: function (skill: any, mult: any = 1): any {
calculateExp: function (_skill: unknown, _mult: unknown = 1): number {
const skill = helper.number("calculateExp", "skill", _skill);
const mult = helper.number("calculateExp", "mult", _mult);
checkFormulasAccess("skills.calculateExp");
return calculateExp(skill, mult);
},
},
hacking: {
hackChance: function (server: any, player: any): any {
hackChance: function (server: any, player: any): number {
checkFormulasAccess("hacking.hackChance");
return calculateHackingChance(server, player);
},
hackExp: function (server: any, player: any): any {
hackExp: function (server: any, player: any): number {
checkFormulasAccess("hacking.hackExp");
return calculateHackingExpGain(server, player);
},
hackPercent: function (server: any, player: any): any {
hackPercent: function (server: any, player: any): number {
checkFormulasAccess("hacking.hackPercent");
return calculatePercentMoneyHacked(server, player);
},
growPercent: function (server: any, threads: any, player: any, cores: any = 1): any {
growPercent: function (server: any, _threads: unknown, player: any, _cores: unknown = 1): number {
const threads = helper.number("growPercent", "threads", _threads);
const cores = helper.number("growPercent", "cores", _cores);
checkFormulasAccess("hacking.growPercent");
return calculateServerGrowth(server, threads, player, cores);
},
hackTime: function (server: any, player: any): any {
hackTime: function (server: any, player: any): number {
checkFormulasAccess("hacking.hackTime");
return calculateHackingTime(server, player) * 1000;
},
growTime: function (server: any, player: any): any {
growTime: function (server: any, player: any): number {
checkFormulasAccess("hacking.growTime");
return calculateGrowTime(server, player) * 1000;
},
weakenTime: function (server: any, player: any): any {
weakenTime: function (server: any, player: any): number {
checkFormulasAccess("hacking.weakenTime");
return calculateWeakenTime(server, player) * 1000;
},
},
hacknetNodes: {
moneyGainRate: function (level: any, ram: any, cores: any, mult: any = 1): any {
moneyGainRate: function (_level: unknown, _ram: unknown, _cores: unknown, _mult: unknown = 1): number {
const level = helper.number("moneyGainRate", "level", _level);
const ram = helper.number("moneyGainRate", "ram", _ram);
const cores = helper.number("moneyGainRate", "cores", _cores);
const mult = helper.number("moneyGainRate", "mult", _mult);
checkFormulasAccess("hacknetNodes.moneyGainRate");
return calculateMoneyGainRate(level, ram, cores, mult);
},
levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any {
levelUpgradeCost: function (_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number {
const startingLevel = helper.number("levelUpgradeCost", "startingLevel", _startingLevel);
const extraLevels = helper.number("levelUpgradeCost", "extraLevels", _extraLevels);
const costMult = helper.number("levelUpgradeCost", "costMult", _costMult);
checkFormulasAccess("hacknetNodes.levelUpgradeCost");
return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
},
ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any {
ramUpgradeCost: function (_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number {
const startingRam = helper.number("ramUpgradeCost", "startingRam", _startingRam);
const extraLevels = helper.number("ramUpgradeCost", "extraLevels", _extraLevels);
const costMult = helper.number("ramUpgradeCost", "costMult", _costMult);
checkFormulasAccess("hacknetNodes.ramUpgradeCost");
return calculateRamUpgradeCost(startingRam, extraLevels, costMult);
},
coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any {
coreUpgradeCost: function (_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number {
const startingCore = helper.number("coreUpgradeCost", "startingCore", _startingCore);
const extraCores = helper.number("coreUpgradeCost", "extraCores", _extraCores);
const costMult = helper.number("coreUpgradeCost", "costMult", _costMult);
checkFormulasAccess("hacknetNodes.coreUpgradeCost");
return calculateCoreUpgradeCost(startingCore, extraCores, costMult);
},
hacknetNodeCost: function (n: any, mult: any): any {
hacknetNodeCost: function (_n: unknown, _mult: unknown): number {
const n = helper.number("hacknetNodeCost", "n", _n);
const mult = helper.number("hacknetNodeCost", "mult", _mult);
checkFormulasAccess("hacknetNodes.hacknetNodeCost");
return calculateNodeCost(n, mult);
},
@ -146,27 +133,51 @@ export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, h
},
},
hacknetServers: {
hashGainRate: function (level: any, ramUsed: any, maxRam: any, cores: any, mult: any = 1): any {
hashGainRate: function (
_level: unknown,
_ramUsed: unknown,
_maxRam: unknown,
_cores: unknown,
_mult: unknown = 1,
): number {
const level = helper.number("hashGainRate", "level", _level);
const ramUsed = helper.number("hashGainRate", "ramUsed", _ramUsed);
const maxRam = helper.number("hashGainRate", "maxRam", _maxRam);
const cores = helper.number("hashGainRate", "cores", _cores);
const mult = helper.number("hashGainRate", "mult", _mult);
checkFormulasAccess("hacknetServers.hashGainRate");
return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult);
},
levelUpgradeCost: function (startingLevel: any, extraLevels: any = 1, costMult: any = 1): any {
levelUpgradeCost: function (_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number {
const startingLevel = helper.number("levelUpgradeCost", "startingLevel", _startingLevel);
const extraLevels = helper.number("levelUpgradeCost", "extraLevels", _extraLevels);
const costMult = helper.number("levelUpgradeCost", "costMult", _costMult);
checkFormulasAccess("hacknetServers.levelUpgradeCost");
return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult);
},
ramUpgradeCost: function (startingRam: any, extraLevels: any = 1, costMult: any = 1): any {
ramUpgradeCost: function (_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number {
const startingRam = helper.number("ramUpgradeCost", "startingRam", _startingRam);
const extraLevels = helper.number("ramUpgradeCost", "extraLevels", _extraLevels);
const costMult = helper.number("ramUpgradeCost", "costMult", _costMult);
checkFormulasAccess("hacknetServers.ramUpgradeCost");
return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult);
},
coreUpgradeCost: function (startingCore: any, extraCores: any = 1, costMult: any = 1): any {
coreUpgradeCost: function (_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number {
const startingCore = helper.number("coreUpgradeCost", "startingCore", _startingCore);
const extraCores = helper.number("coreUpgradeCost", "extraCores", _extraCores);
const costMult = helper.number("coreUpgradeCost", "costMult", _costMult);
checkFormulasAccess("hacknetServers.coreUpgradeCost");
return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult);
},
cacheUpgradeCost: function (startingCache: any, extraCache: any = 1): any {
cacheUpgradeCost: function (_startingCache: unknown, _extraCache: unknown = 1): number {
const startingCache = helper.number("cacheUpgradeCost", "startingCache", _startingCache);
const extraCache = helper.number("cacheUpgradeCost", "extraCache", _extraCache);
checkFormulasAccess("hacknetServers.cacheUpgradeCost");
return HScalculateCacheUpgradeCost(startingCache, extraCache);
},
hashUpgradeCost: function (upgName: any, level: any): any {
hashUpgradeCost: function (_upgName: unknown, _level: unknown): number {
const upgName = helper.string("hashUpgradeCost", "upgName", _upgName);
const level = helper.number("hashUpgradeCost", "level", _level);
checkFormulasAccess("hacknetServers.hashUpgradeCost");
const upg = player.hashManager.getUpgrade(upgName);
if (!upg) {
@ -177,7 +188,9 @@ export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, h
}
return upg.getCost(level);
},
hacknetServerCost: function (n: any, mult: any = 1): any {
hacknetServerCost: function (_n: unknown, _mult: unknown = 1): number {
const n = helper.number("hacknetServerCost", "n", _n);
const mult = helper.number("hacknetServerCost", "mult", _mult);
checkFormulasAccess("hacknetServers.hacknetServerCost");
return HScalculateServerCost(n, mult);
},
@ -203,11 +216,13 @@ export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, h
checkFormulasAccess("gang.moneyGain");
return calculateMoneyGain(gang, member, task);
},
ascensionPointsGain: function (exp: any): number {
ascensionPointsGain: function (_exp: unknown): number {
const exp = helper.number("ascensionPointsGain", "exp", _exp);
checkFormulasAccess("gang.ascensionPointsGain");
return calculateAscensionPointsGain(exp);
},
ascensionMultiplier: function (points: any): number {
ascensionMultiplier: function (_points: unknown): number {
const points = helper.number("ascensionMultiplier", "points", _points);
checkFormulasAccess("gang.ascensionMultiplier");
return calculateAscensionMult(points);
},

@ -24,10 +24,7 @@ import { Hacknet as IHacknet, NodeStats } from "../ScriptEditor/NetscriptDefinit
export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IHacknet {
// Utility function to get Hacknet Node object
const getHacknetNode = function (i: any, callingFn = ""): HacknetNode | HacknetServer {
if (isNaN(i)) {
throw helper.makeRuntimeErrorMsg(callingFn, "Invalid index specified for Hacknet Node: " + i);
}
const getHacknetNode = function (i: number, callingFn = ""): HacknetNode | HacknetServer {
if (i < 0 || i >= player.hacknetNodes.length) {
throw helper.makeRuntimeErrorMsg(callingFn, "Index specified for Hacknet Node is out-of-bounds: " + i);
}
@ -72,7 +69,8 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
return getCostOfNextHacknetNode(player);
}
},
getNodeStats: function (i: any): NodeStats {
getNodeStats: function (_i: unknown): NodeStats {
const i = helper.number("getNodeStats", "i", _i);
const node = getHacknetNode(i, "getNodeStats");
const hasUpgraded = hasHacknetServers(player);
const res: any = {
@ -93,19 +91,27 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
return res;
},
upgradeLevel: function (i: any, n: any): boolean {
upgradeLevel: function (_i: unknown, _n: unknown): boolean {
const i = helper.number("upgradeLevel", "i", _i);
const n = helper.number("upgradeLevel", "n", _n);
const node = getHacknetNode(i, "upgradeLevel");
return purchaseLevelUpgrade(player, node, n);
},
upgradeRam: function (i: any, n: any): boolean {
upgradeRam: function (_i: unknown, _n: unknown): boolean {
const i = helper.number("upgradeRam", "i", _i);
const n = helper.number("upgradeRam", "n", _n);
const node = getHacknetNode(i, "upgradeRam");
return purchaseRamUpgrade(player, node, n);
},
upgradeCore: function (i: any, n: any): boolean {
upgradeCore: function (_i: unknown, _n: unknown): boolean {
const i = helper.number("upgradeCore", "i", _i);
const n = helper.number("upgradeCore", "n", _n);
const node = getHacknetNode(i, "upgradeCore");
return purchaseCoreUpgrade(player, node, n);
},
upgradeCache: function (i: any, n: any): boolean {
upgradeCache: function (_i: unknown, _n: unknown): boolean {
const i = helper.number("upgradeCache", "i", _i);
const n = helper.number("upgradeCache", "n", _n);
if (!hasHacknetServers(player)) {
return false;
}
@ -120,19 +126,27 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
}
return res;
},
getLevelUpgradeCost: function (i: any, n: any): number {
getLevelUpgradeCost: function (_i: unknown, _n: unknown): number {
const i = helper.number("getLevelUpgradeCost", "i", _i);
const n = helper.number("getLevelUpgradeCost", "n", _n);
const node = getHacknetNode(i, "upgradeLevel");
return node.calculateLevelUpgradeCost(n, player.hacknet_node_level_cost_mult);
},
getRamUpgradeCost: function (i: any, n: any): number {
getRamUpgradeCost: function (_i: unknown, _n: unknown): number {
const i = helper.number("getRamUpgradeCost", "i", _i);
const n = helper.number("getRamUpgradeCost", "n", _n);
const node = getHacknetNode(i, "upgradeRam");
return node.calculateRamUpgradeCost(n, player.hacknet_node_ram_cost_mult);
},
getCoreUpgradeCost: function (i: any, n: any): number {
getCoreUpgradeCost: function (_i: unknown, _n: unknown): number {
const i = helper.number("getCoreUpgradeCost", "i", _i);
const n = helper.number("getCoreUpgradeCost", "n", _n);
const node = getHacknetNode(i, "upgradeCore");
return node.calculateCoreUpgradeCost(n, player.hacknet_node_core_cost_mult);
},
getCacheUpgradeCost: function (i: any, n: any): number {
getCacheUpgradeCost: function (_i: unknown, _n: unknown): number {
const i = helper.number("getCacheUpgradeCost", "i", _i);
const n = helper.number("getCacheUpgradeCost", "n", _n);
if (!hasHacknetServers(player)) {
return Infinity;
}
@ -155,26 +169,30 @@ export function NetscriptHacknet(player: IPlayer, workerScript: WorkerScript, he
}
return player.hashManager.capacity;
},
hashCost: function (upgName: any): number {
hashCost: function (_upgName: unknown): number {
const upgName = helper.string("hashCost", "upgName", _upgName);
if (!hasHacknetServers(player)) {
return Infinity;
}
return player.hashManager.getUpgradeCost(upgName);
},
spendHashes: function (upgName: any, upgTarget: any): boolean {
spendHashes: function (_upgName: unknown, _upgTarget: unknown): boolean {
const upgName = helper.string("spendHashes", "upgName", _upgName);
const upgTarget = helper.string("spendHashes", "upgTarget", _upgTarget);
if (!hasHacknetServers(player)) {
return false;
}
return purchaseHashUpgrade(player, upgName, upgTarget);
},
getHashUpgrades: function(): string[] {
getHashUpgrades: function (): string[] {
if (!hasHacknetServers(player)) {
return [];
}
return Object.values(HashUpgrades).map((upgrade: HashUpgrade) => upgrade.name);
},
getHashUpgradeLevel: function (upgName: any): number {
getHashUpgradeLevel: function (_upgName: unknown): number {
const upgName = helper.string("getHashUpgradeLevel", "upgName", _upgName);
const level = player.hashManager.upgrades[upgName];
if (level === undefined) {
throw helper.makeRuntimeErrorMsg("hacknet.hashUpgradeLevel", `Invalid Hash Upgrade: ${upgName}`);

@ -3,9 +3,9 @@ import { BaseServer } from "../Server/BaseServer";
export interface INetscriptHelper {
updateDynamicRam(functionName: string, ram: number): void;
makeRuntimeErrorMsg(functionName: string, message: string): void;
string(funcName: string, argName: string, v: any): string;
number(funcName: string, argName: string, v: any): number;
boolean(v: any): boolean;
string(funcName: string, argName: string, v: unknown): string;
number(funcName: string, argName: string, v: unknown): number;
boolean(v: unknown): boolean;
getServer(ip: any, fn: any): BaseServer;
checkSingularityAccess(func: string): void;
hack(hostname: string, manual: boolean): Promise<number>;

@ -375,7 +375,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
});
//Now we have to generate the code that would create the namespace
generatedCode += "var " + namespace + ";\n" + "(function (namespace) {\n";
generatedCode += `var ${namespace};\n(function (namespace) {\n`;
//Add the function declarations
fnDeclarations.forEach((fn: any) => {
@ -390,7 +390,7 @@ function processNetscript1Imports(code: string, workerScript: WorkerScript): any
});
//Finish
generatedCode += "})(" + namespace + " || " + "(" + namespace + " = {}));\n";
generatedCode += `})(${namespace} || (" + namespace + " = {}));\n`;
} else {
//import {...} from script

@ -608,11 +608,9 @@ export function process(this: IPlayer, router: IRouter, numCycles = 1): void {
if (this.workPartTime(numCycles)) {
router.toCity();
}
} else {
if (this.work(numCycles)) {
} else if (this.work(numCycles)) {
router.toCity();
}
}
}
}
@ -1315,9 +1313,7 @@ export function createProgramWork(this: IPlayer, numCycles: number): boolean {
export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string {
const programName = this.createProgramName;
if (cancelled === false) {
dialogBoxCreate(
"You've finished creating " + programName + "!<br>" + "The new program can be found on your home computer.",
);
dialogBoxCreate(`You've finished creating ${programName}!<br>The new program can be found on your home computer.`);
this.getHomeComputer().programs.push(programName);
} else {
@ -2244,8 +2240,7 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
if (!(fulcrumSecretServer instanceof Server)) throw new Error("Fulcrum Secret Technologies should be normal server");
if (fulcrumSecretServer == null) {
console.error("Could not find Fulcrum Secret Technologies Server");
} else {
if (
} else if (
!fulcrumsecrettechonologiesFac.isBanned &&
!fulcrumsecrettechonologiesFac.isMember &&
!fulcrumsecrettechonologiesFac.alreadyInvited &&
@ -2254,7 +2249,6 @@ export function checkForFactionInvitations(this: IPlayer): Faction[] {
) {
invitedFactions.push(fulcrumsecrettechonologiesFac);
}
}
//BitRunners
const bitrunnersFac = Factions["BitRunners"];

@ -34,7 +34,32 @@ export function findSleevePurchasableAugs(sleeve: Sleeve, p: IPlayer): Augmentat
return false;
}
return true;
const validMults = [
"hacking_mult",
"strength_mult",
"defense_mult",
"dexterity_mult",
"agility_mult",
"charisma_mult",
"hacking_exp_mult",
"strength_exp_mult",
"defense_exp_mult",
"dexterity_exp_mult",
"agility_exp_mult",
"charisma_exp_mult",
"company_rep_mult",
"faction_rep_mult",
"crime_money_mult",
"crime_success_mult",
"work_money_mult",
];
for (const mult of Object.keys(aug.mults)) {
if (validMults.includes(mult)) {
return true;
}
}
return false;
}
// If player is in a gang, then we return all augs that the player

@ -1,9 +1,19 @@
import React, { useState, useEffect } from "react";
import { use } from "../../ui/Context";
import { getAvailableCreatePrograms } from "../ProgramHelpers";
import { find } from "lodash";
import { Box, Tooltip, Typography } from "@mui/material";
import Button from "@mui/material/Button";
import {
Box,
Typography,
Button,
Container,
Paper
} from "@mui/material";
import { Check, Lock, Create } from "@mui/icons-material";
import { use } from "../../ui/Context";
import { Settings } from "../../Settings/Settings";
import { Programs } from "../Programs";
export const ProgramsSeen: string[] = [];
@ -15,7 +25,20 @@ export function ProgramsRoot(): React.ReactElement {
setRerender((old) => !old);
}
const programs = getAvailableCreatePrograms(player);
const programs = [...Object.values(Programs)]
.filter(prog => {
const create = prog.create;
if (create === null) return false;
if (prog.name === "b1t_flum3.exe") {
return create.req(player);
}
return true;
})
.sort((a, b) => {
if (player.hasProgram(a.name)) return 1;
if (player.hasProgram(b.name)) return -1;
return (a.create?.level ?? 0) - (b.create?.level ?? 0);
})
useEffect(() => {
programs.forEach((p) => {
@ -29,8 +52,27 @@ export function ProgramsRoot(): React.ReactElement {
return () => clearInterval(id);
}, []);
const getHackingLevelRemaining = (lvl: number): number => {
return Math.ceil(Math.max(lvl - (player.hacking + player.intelligence / 2), 0));
}
const getProgCompletion = (name: string): number => {
const programFile = find(player.getHomeComputer().programs, p => {
return (p.startsWith(name) && p.endsWith("%-INC"));
});
if (!programFile) return -1;
const res = programFile.split("-");
if (res.length != 3) return -1;
const percComplete = Number(res[1].slice(0, -1));
if (isNaN(percComplete) || percComplete < 0 || percComplete >= 100) {
return -1;
}
return percComplete;
}
return (
<>
<Container disableGutters maxWidth="lg" sx={{ mx: 0, mb: 10 }}>
<Typography variant="h4">Create program</Typography>
<Typography>
This page displays any programs that you are able to create. Writing the code for a program takes time, which
@ -38,30 +80,45 @@ export function ProgramsRoot(): React.ReactElement {
time. Your progress will be saved and you can continue later.
</Typography>
<Box sx={{ display: 'grid', width: 'fit-content' }}>
<Box sx={{ display: 'grid', gridTemplateColumns: "repeat(3, 1fr)", my: 1 }}>
{programs.map((program) => {
const create = program.create;
if (create === null) return <></>;
const curCompletion = getProgCompletion(program.name);
return (
<React.Fragment key={program.name}>
<Tooltip title={create.tooltip}>
<Button
sx={{ my: 1 }}
onClick={(event) => {
if (!event.isTrusted) return;
player.startCreateProgramWork(program.name, create.time, create.level);
player.startFocusing();
router.toWork();
}}
>
{program.name}
</Button>
</Tooltip>
</React.Fragment>
<Box component={Paper} sx={{ p: 1, opacity: player.hasProgram(program.name) ? 0.75 : 1 }} key={program.name}>
<Typography variant="h6" sx={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
{player.hasProgram(program.name) && <Check sx={{ mr: 1 }} /> ||
(create.req(player) && <Create sx={{ mr: 1 }} /> || <Lock sx={{ mr: 1 }} />)}
{program.name}
</Typography>
{(!player.hasProgram(program.name) && create.req(player)) && <Button
sx={{ my: 1, width: '100%' }}
onClick={(event) => {
if (!event.isTrusted) return;
player.startCreateProgramWork(program.name, create.time, create.level);
player.startFocusing();
router.toWork();
}}
>
Create program
</Button>}
{(player.hasProgram(program.name) || getHackingLevelRemaining(create.level) === 0) ||
<Typography color={Settings.theme.hack}>
<b>Unlocks in:</b> {getHackingLevelRemaining(create.level)} hacking levels
</Typography>}
{(curCompletion !== -1) &&
<Typography color={Settings.theme.infolight}>
<b>Current completion:</b> {curCompletion}%
</Typography>}
<Typography>
{create.tooltip}
</Typography>
</Box>
);
})}
</Box>
</>
</Container>
);
}

@ -39,7 +39,7 @@ function giveSourceFile(bitNodeNumber: number): void {
if (alreadyOwned && ownedSourceFile) {
if (ownedSourceFile.lvl >= 3 && ownedSourceFile.n !== 12) {
dialogBoxCreate(
"The Source-File for the BitNode you just destroyed, " + sourceFile.name + ", " + "is already at max level!",
`The Source-File for the BitNode you just destroyed, ${sourceFile.name}, is already at max level!`,
);
} else {
++ownedSourceFile.lvl;
@ -75,12 +75,10 @@ function giveSourceFile(bitNodeNumber: number): void {
export function enterBitNode(router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number): void {
if (!flume) {
giveSourceFile(destroyedBitNode);
} else {
if (SourceFileFlags[5] === 0 && newBitNode !== 5) {
} else if (SourceFileFlags[5] === 0 && newBitNode !== 5) {
Player.intelligence = 0;
Player.intelligence_exp = 0;
}
}
if (newBitNode === 5 && Player.intelligence === 0) {
Player.intelligence = 1;
}

@ -94,6 +94,7 @@ interface Player {
factions: string[];
tor: boolean;
hasCorporation: boolean;
inBladeburner: boolean;
}
/**

@ -17,6 +17,7 @@ import { calculateRamUsage, checkInfiniteLoop } from "../../Script/RamCalculatio
import { RamCalculationErrorCode } from "../../Script/RamCalculationErrorCodes";
import { numeralWrapper } from "../../ui/numeralFormat";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import SearchIcon from "@mui/icons-material/Search";
import { NetscriptFunctions } from "../../NetscriptFunctions";
import { WorkerScript } from "../../Netscript/WorkerScript";
@ -42,7 +43,7 @@ import { PromptEvent } from "../../ui/React/PromptManager";
import { Modal } from "../../ui/React/Modal";
import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
import { Tooltip } from "@mui/material";
import { TextField, Tooltip } from "@mui/material";
interface IProps {
// Map of filename -> code
@ -53,7 +54,7 @@ interface IProps {
vim: boolean;
}
// TODO: try to removve global symbols
// TODO: try to remove global symbols
let symbolsLoaded = false;
let symbols: string[] = [];
export function SetupTextEditor(): void {
@ -113,6 +114,8 @@ export function Root(props: IProps): React.ReactElement {
const vimStatusRef = useRef<HTMLElement>(null);
const [vimEditor, setVimEditor] = useState<any>(null);
const [editor, setEditor] = useState<IStandaloneCodeEditor | null>(null);
const [filter, setFilter] = useState("");
const [searchExpanded, setSearchExpanded] = useState(false);
const [ram, setRAM] = useState("RAM: ???");
const [ramEntries, setRamEntries] = useState<string[][]>([["???", ""]]);
@ -232,7 +235,7 @@ export function Root(props: IProps): React.ReactElement {
MonacoVim.VimMode.Vim.mapCommand("gT", "action", "prevTabs", {}, { context: "normal" });
editor.focus();
});
} catch {}
} catch { }
} else if (!options.vim) {
// Whem vim mode is disabled
vimEditor?.dispose();
@ -478,7 +481,7 @@ export function Root(props: IProps): React.ReactElement {
}
try {
infLoop(newCode);
} catch (err) {}
} catch (err) { }
}
function saveScript(scriptToSave: OpenScript): void {
@ -523,7 +526,7 @@ export function Root(props: IProps): React.ReactElement {
const textFile = new TextFile(scriptToSave.fileName, scriptToSave.code);
server.textFiles.push(textFile);
} else {
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)");
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or a text file (.txt)");
return;
}
@ -607,7 +610,7 @@ export function Root(props: IProps): React.ReactElement {
const textFile = new TextFile(currentScript.fileName, currentScript.code);
server.textFiles.push(textFile);
} else {
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)");
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or a text file (.txt)");
return;
}
@ -787,6 +790,16 @@ export function Root(props: IProps): React.ReactElement {
const serverScript = server.scripts.find((s) => s.filename === openScript.fileName);
return serverScript?.code ?? null;
}
function handleFilterChange(event: React.ChangeEvent<HTMLInputElement>): void {
setFilter(event.target.value);
}
function handleExpandSearch(): void {
setFilter("")
setSearchExpanded(!searchExpanded)
}
const filteredOpenScripts = Object.values(openScripts).filter(
(script) => (script.hostname.includes(filter) || script.fileName.includes(filter))
);
// Toolbars are roughly 112px:
// 8px body margin top
@ -797,7 +810,7 @@ export function Root(props: IProps): React.ReactElement {
const editorHeight = dimensions.height - (130 + (options.vim ? 34 : 0));
const tabsMaxWidth = 1640;
const tabMargin = 5;
const tabMaxWidth = openScripts.length ? tabsMaxWidth / openScripts.length - tabMargin : 0;
const tabMaxWidth = filteredOpenScripts.length ? tabsMaxWidth / filteredOpenScripts.length - tabMargin : 0;
const tabIconWidth = 25;
const tabTextWidth = tabMaxWidth - tabIconWidth * 2;
return (
@ -821,23 +834,36 @@ export function Root(props: IProps): React.ReactElement {
overflowX: "scroll",
}}
>
{openScripts.map(({ fileName, hostname }, index) => {
<Tooltip title={"Search Open Scripts"}>
{searchExpanded ?
<TextField
value={filter}
onChange={handleFilterChange}
autoFocus
InputProps={{
startAdornment: <SearchIcon />,
spellCheck: false,
endAdornment: <CloseIcon onClick={handleExpandSearch} />
}}
/> : <Button onClick={handleExpandSearch} ><SearchIcon /></Button>}
</Tooltip>
{filteredOpenScripts.map(({ fileName, hostname }, index) => {
const iconButtonStyle = {
maxWidth: `${tabIconWidth}px`,
minWidth: `${tabIconWidth}px`,
minHeight: "38.5px",
maxHeight: "38.5px",
...(currentScript?.fileName === openScripts[index].fileName
...(currentScript?.fileName === filteredOpenScripts[index].fileName
? {
background: Settings.theme.button,
borderColor: Settings.theme.button,
color: Settings.theme.primary,
}
background: Settings.theme.button,
borderColor: Settings.theme.button,
color: Settings.theme.primary,
}
: {
background: Settings.theme.backgroundsecondary,
borderColor: Settings.theme.backgroundsecondary,
color: Settings.theme.secondary,
}),
background: Settings.theme.backgroundsecondary,
borderColor: Settings.theme.backgroundsecondary,
color: Settings.theme.secondary,
}),
};
const scriptTabText = `${hostname}:~/${fileName} ${dirty(index)}`;
@ -870,18 +896,19 @@ export function Root(props: IProps): React.ReactElement {
}}
style={{
maxWidth: `${tabTextWidth}px`,
minHeight: '38.5px',
overflow: "hidden",
...(currentScript?.fileName === openScripts[index].fileName
...(currentScript?.fileName === filteredOpenScripts[index].fileName
? {
background: Settings.theme.button,
borderColor: Settings.theme.button,
color: Settings.theme.primary,
}
background: Settings.theme.button,
borderColor: Settings.theme.button,
color: Settings.theme.primary,
}
: {
background: Settings.theme.backgroundsecondary,
borderColor: Settings.theme.backgroundsecondary,
color: Settings.theme.secondary,
}),
background: Settings.theme.backgroundsecondary,
borderColor: Settings.theme.backgroundsecondary,
color: Settings.theme.secondary,
}),
}}
>
<span style={{ overflow: "hidden", direction: "rtl", textOverflow: "ellipsis" }}>

@ -78,12 +78,11 @@ export function ipExists(ip: string): boolean {
}
export function createUniqueRandomIp(): string {
const ip = createRandomIp();
// If the Ip already exists, recurse to create a new one
if (ipExists(ip)) {
return createRandomIp();
}
let ip: string;
// Repeat generating ip, until unique one is found
do {
ip = createRandomIp();
} while (ipExists(ip));
return ip;
}

@ -143,11 +143,6 @@ export function SidebarRoot(props: IProps): React.ReactElement {
const augmentationCount = props.player.queuedAugmentations.length;
const invitationsCount = props.player.factionInvitations.filter((f) => !InvitationsSeen.includes(f)).length;
const programCount = getAvailableCreatePrograms(props.player).length - ProgramsSeen.length;
const canCreateProgram =
getAvailableCreatePrograms(props.player).length > 0 ||
props.player.augmentations.length > 0 ||
props.player.queuedAugmentations.length > 0 ||
props.player.sourceFiles.length > 0;
const canOpenFactions =
props.player.factionInvitations.length > 0 ||
@ -439,29 +434,27 @@ export function SidebarRoot(props: IProps): React.ReactElement {
</Typography>
</ListItemText>
</ListItem>
{canCreateProgram && (
<ListItem
button
key={"Create Program"}
className={clsx({
[classes.active]: props.page === Page.CreateProgram,
})}
onClick={clickCreateProgram}
>
<ListItemIcon>
<Badge badgeContent={programCount > 0 ? programCount : undefined} color="error">
<Tooltip title={!open ? "Create Program" : ""}>
<BugReportIcon color={props.page !== Page.CreateProgram ? "secondary" : "primary"} />
</Tooltip>
</Badge>
</ListItemIcon>
<ListItemText>
<Typography color={props.page !== Page.CreateProgram ? "secondary" : "primary"}>
Create Program
</Typography>
</ListItemText>
</ListItem>
)}
<ListItem
button
key={"Create Program"}
className={clsx({
[classes.active]: props.page === Page.CreateProgram,
})}
onClick={clickCreateProgram}
>
<ListItemIcon>
<Badge badgeContent={programCount > 0 ? programCount : undefined} color="error">
<Tooltip title={!open ? "Create Program" : ""}>
<BugReportIcon color={props.page !== Page.CreateProgram ? "secondary" : "primary"} />
</Tooltip>
</Badge>
</ListItemIcon>
<ListItemText>
<Typography color={props.page !== Page.CreateProgram ? "secondary" : "primary"}>
Create Program
</Typography>
</ListItemText>
</ListItem>
{canStaneksGift && (
<ListItem
button

@ -66,10 +66,9 @@ SourceFiles["SourceFile5"] = new SourceFile(
<>
This Source-File grants a special new stat called Intelligence. Intelligence is unique because it is permanent and
persistent (it never gets reset back to 1). However, gaining Intelligence experience is much slower than other
stats, and it is also hidden (you won't know when you gain experience and how much). Higher Intelligence levels
will boost your production for many actions in the game. In addition, this Source-File will unlock the
getBitNodeMultipliers() Netscript function and let you start with Formulas.exe, and will raise all of your
hacking-related multipliers by:
stats. Higher Intelligence levels will boost your production for many actions in the game. In addition, this
Source-File will unlock the getBitNodeMultipliers() Netscript function and let you start with Formulas.exe, and
will raise all of your hacking-related multipliers by:
<br />
<br />
Level 1: 8%

@ -117,7 +117,7 @@ export function buyStock(
const resultTxt =
`Bought ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol} for ${numeralWrapper.formatMoney(
totalPrice,
)}. ` + `Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} in commission fees.`;
)}. Paid ${numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} in commission fees.`;
workerScript.log("stock.buy", () => resultTxt);
} else if (opts.suppressDialog !== true) {
dialogBoxCreate(

@ -165,8 +165,7 @@ function executeOrder(order: Order, refs: IProcessOrderRefs): void {
console.error("Could not find the following Order in Order Book: ");
console.error(order);
} else {
if (isBuy) {
} else if (isBuy) {
dialogBoxCreate(
<>
Failed to execute {order.type} for {stock.symbol} @ <Money money={order.price} /> ({pos}). This is most likely
@ -174,5 +173,4 @@ function executeOrder(order: Order, refs: IProcessOrderRefs): void {
</>,
);
}
}
}

@ -213,13 +213,11 @@ export class Stock {
} else {
this.otlkMag -= changeAmt;
}
} else {
} else if (this.b) {
// Forecast decreases
if (this.b) {
this.otlkMag -= changeAmt;
} else {
this.otlkMag += changeAmt;
}
this.otlkMag -= changeAmt;
} else {
this.otlkMag += changeAmt;
}
this.otlkMag = Math.min(this.otlkMag, 50);

@ -113,11 +113,9 @@ export function StockTicker(props: IProps): React.ReactElement {
if (qty > stock.playerShares) {
return <>You do not have this many shares in the Long position</>;
}
} else {
if (qty > stock.playerShortShares) {
} else if (qty > stock.playerShortShares) {
return <>You do not have this many shares in the Short position</>;
}
}
const cost = getSellTransactionGain(stock, qty, position);
if (cost == null) {

@ -68,13 +68,11 @@ export function ParseCommand(command: string): (string | number | boolean)[] {
}
continue;
}
} else {
if (inQuote === ``) {
} else if (inQuote === ``) {
inQuote = `"`;
} else if (inQuote === `"`) {
inQuote = ``;
}
}
} else if (c === "'") {
// Single quotes, same thing as above
if (!escaped && prevChar === " ") {
@ -88,13 +86,11 @@ export function ParseCommand(command: string): (string | number | boolean)[] {
}
continue;
}
} else {
if (inQuote === ``) {
} else if (inQuote === ``) {
inQuote = `'`;
} else if (inQuote === `'`) {
inQuote = ``;
}
}
} else if (c === " " && inQuote === ``) {
const arg = command.substr(start, i - start);

@ -167,7 +167,7 @@ export function ls(
function postSegments(segments: string[], style?: any, linked?: boolean): void {
const maxLength = Math.max(...segments.map((s) => s.length)) + 1;
const filesPerRow = Math.floor(80 / maxLength);
const filesPerRow = Math.ceil(80 / maxLength);
for (let i = 0; i < segments.length; i++) {
let row = "";
for (let col = 0; col < filesPerRow; col++) {
@ -179,13 +179,11 @@ export function ls(
i--;
if (!style) {
terminal.print(row);
} else {
if (linked) {
} else if (linked) {
terminal.printRaw(<ClickableScriptRow row={row} prefix={prefix} hostname={server.hostname} />);
} else {
terminal.printRaw(<span style={style}>{row}</span>);
}
}
}
}

@ -14,11 +14,9 @@ export function unalias(
if (args.length !== 1) {
terminal.error("Incorrect usage of unalias name. Usage: unalias [alias]");
return;
} else {
if (removeAlias(args[0] + "")) {
} else if (removeAlias(args[0] + "")) {
terminal.print(`Removed alias ${args[0]}`);
} else {
terminal.error(`No such alias exists: ${args[0]}`);
}
}
}

@ -66,28 +66,22 @@ export function tabCompletion(
if (arg === "") {
if (longestStartSubstr === command) {
return allPossibilities;
} else {
if (semiColonIndex === -1) {
} else if (semiColonIndex === -1) {
// No semicolon, so replace the whole command
return longestStartSubstr;
} else {
// Replace only after the last semicolon
return `${oldValue.slice(0, semiColonIndex + 1)} ${longestStartSubstr}`;
}
}
} else {
if (longestStartSubstr === arg) {
} else if (longestStartSubstr === arg) {
// List all possible options
return allPossibilities;
} else {
if (semiColonIndex == -1) {
} else if (semiColonIndex == -1) {
// No semicolon, so replace the whole command
return `${command} ${longestStartSubstr}`;
} else {
// Replace only after the last semicolon
return `${oldValue.slice(0, semiColonIndex + 1)} ${command} ${longestStartSubstr}`;
}
}
}
}
}

@ -640,7 +640,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
"of the path, you may only move to adjacent numbers in the row below.",
"The triangle is represented as a 2D array of numbers:\n\n",
`${triangle}\n\n`,
"Example: If you are given the following triangle:\n\n" + "[\n",
"Example: If you are given the following triangle:\n\n[\n",
"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[2],\n",
"&nbsp;&nbsp;&nbsp;&nbsp;[3,4],\n",
"&nbsp;&nbsp;&nbsp;[6,5,7],\n",

@ -86,6 +86,9 @@ function Intelligence(): React.ReactElement {
<TableCell align="right">
<Typography>{numeralWrapper.formatSkill(player.intelligence)}&nbsp;</Typography>
</TableCell>
<TableCell align="right">
<Typography noWrap>({numeralWrapper.formatExp(player.intelligence_exp)} exp)</Typography>
</TableCell>
</TableRow>
);
}

@ -33,23 +33,34 @@ interface IProps {
}
function Intelligence(): React.ReactElement {
const theme = useTheme();
const player = use.Player();
const classes = useStyles();
if (player.intelligence === 0) return <></>;
const progress = player.calculateSkillProgress(player.intelligence_exp);
return (
<TableRow>
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
<Typography classes={{ root: classes.int }}>Int&nbsp;</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cell }}>
<Typography classes={{ root: classes.int }}>{numeralWrapper.formatSkill(player.intelligence)}</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cell }}>
<Typography id="overview-int-hook" classes={{ root: classes.int }}>
{/*Hook for player scripts*/}
</Typography>
</TableCell>
</TableRow>
<>
<TableRow>
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
<Typography classes={{ root: classes.int }}>Int&nbsp;</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cell }}>
<Typography classes={{ root: classes.int }}>{numeralWrapper.formatSkill(player.intelligence)}</Typography>
</TableCell>
<TableCell align="right" classes={{ root: classes.cell }}>
<Typography id="overview-int-hook" classes={{ root: classes.int }}>
{/*Hook for player scripts*/}
</Typography>
</TableCell>
</TableRow>
<TableRow>
{!Settings.DisableOverviewProgressBars && (
<StatsProgressOverviewCell progress={progress} color={theme.colors.int} />
)}
</TableRow>
</>
);
}

@ -17,8 +17,7 @@ export function validateObject<Type extends Record<string, unknown>, Key extends
if (paramValidator !== undefined) {
if (typeof paramValidator === 'function') {
paramValidator(obj, key);
} else {
if (paramValidator.func !== undefined) {
} else if (paramValidator.func !== undefined) {
paramValidator.func(obj, validator, key);
} else {
if ((typeof obj[key]) !== (typeof paramValidator.default)) {
@ -31,7 +30,6 @@ export function validateObject<Type extends Record<string, unknown>, Key extends
if (obj[key] > paramValidator.max) obj[key] = paramValidator.max as Type[Key];
}
}
}
}
}
}

@ -94,9 +94,9 @@ export function v1APIBreak(): void {
if (s.length === 0) continue;
txt += `// Detected change ${change[0]}, reason: ${change[1]}` + "\n";
txt += `// Detected change ${change[0]}, reason: ${change[1]}\n`;
for (const fl of s) {
txt += `${fl.file}:${fl.line}` + "\n";
txt += `${fl.file}:${fl.line}\n`;
}
}
}

@ -59,7 +59,7 @@ async function main(version, versionNumber, changelog) {
join('\n').replaceAll('`', '\\`');
modifiedConstants = modifiedConstants.
replace(/(^\s*?LatestUpdate:\s`\n)(.*)`,$/ms, `$1${paddedChangelog}\n` + "`,");
replace(/(^\s*?LatestUpdate:\s`\n)(.*)`,$/ms, `$1${paddedChangelog}\n\`,`);
}
await fs.writeFile(appPaths.constants, modifiedConstants);
console.log(`Modified ${appPaths.constants}`);