Merge pull request #3162 from danielyxie/dev

Few fixes
This commit is contained in:
hydroflame 2022-03-17 16:48:47 -04:00 committed by GitHub
commit 7afc7d5c78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
65 changed files with 913 additions and 526 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:

@ -21,23 +21,19 @@ can be used to check how much RAM a server has.
Identifying Servers
^^^^^^^^^^^^^^^^^^^
A server is identified by two properties: its IP address and its hostname.
An IP address is a 32-bit number represented in dot-decimal notation.
For example, "56.1.5.0" and "86.5.1.0" might be two IP addresses
you see in the game. A hostname is a label assigned to a server.
A server is identified by its hostname.
A hostname is a label assigned to a server.
A hostname will usually give you a general idea of what the server
is. For example, the company Nova Medical might have a server with
the hostname "nova-med".
Hostnames and IP addresses are unique. This means that if one
server has the IP address "1.1.1.1" and the hostname
"some-server", then no other server in the game can have that
IP address or that hostname.
Hostnames are unique. This means that if one
server has the the hostname "some-server", then no other server
in the game can have that that hostname.
There are many :ref:`Netscript Functions <netscriptfunctions>`
and :ref:`terminal` commands in the game
that will require you to target a specific server. This is done using
either the IP address or the hostname of the server.
that will require you to target a specific server by hostname.
Player-owned Servers
^^^^^^^^^^^^^^^^^^^^

@ -230,7 +230,7 @@ connect
$ connect [hostname/ip]
Connect to a remote server. The hostname or IP address of the remote server must
Connect to a remote server. The hostname of the remote server must
be given as the argument to this command. Note that only servers that are immediately
adjacent to the current server in the network can be connected to. To see which
servers can be connected to, use the 'scan' command.
@ -326,7 +326,7 @@ Then to kill this script the same arguments would have to be used::
$ kill foo.script 50e3 sigma-cosmetics
If you are killing the script using its PID, then the PID argument must be numeric.
If you are killing the script using its PID, then the PID argument must be numeric.
killall
^^^^^^^
@ -533,28 +533,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,

@ -102,7 +102,7 @@ export class WorkerScript {
scriptRef: RunningScript;
/**
* IP Address on which this script is running
* hostname on which this script is running
*/
hostname: string;

@ -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,17 +2140,20 @@ 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"`);
message = argsToString([message]);
SnackbarEvents.emit(message, variant, duration);
},
prompt: function (txt: any): any {
prompt: function (txt: any, options?: { type?: string; options?: string[] }): any {
updateDynamicRam("prompt", getRamCost(Player, "prompt"));
if (!isString(txt)) {
txt = JSON.stringify(txt);
}
@ -2127,11 +2161,13 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
return new Promise(function (resolve) {
PromptEvent.emit({
txt: txt,
options,
resolve: resolve,
});
});
},
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 +2209,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 +2220,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 +2309,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>;

@ -32,9 +32,9 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
checkStanekAPIAccess("height");
return staneksGift.height();
},
charge: function (arootX: unknown, arootY: unknown): Promise<void> {
const rootX = helper.number("stanek.charge", "rootX", arootX);
const rootY = helper.number("stanek.charge", "rootY", arootY);
charge: function (_rootX: unknown, _rootY: unknown): Promise<void> {
const rootX = helper.number("stanek.charge", "rootX", _rootX);
const rootY = helper.number("stanek.charge", "rootY", _rootY);
helper.updateDynamicRam("charge", getRamCost(player, "stanek", "charge"));
checkStanekAPIAccess("charge");
@ -67,11 +67,11 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
workerScript.log("stanek.clear", () => `Cleared Stanek's Gift.`);
staneksGift.clear();
},
canPlace: function (arootX: unknown, arootY: unknown, arotation: unknown, afragmentId: unknown): boolean {
const rootX = helper.number("stanek.canPlace", "rootX", arootX);
const rootY = helper.number("stanek.canPlace", "rootY", arootY);
const rotation = helper.number("stanek.canPlace", "rotation", arotation);
const fragmentId = helper.number("stanek.canPlace", "fragmentId", afragmentId);
canPlace: function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
const rootX = helper.number("stanek.canPlace", "rootX", _rootX);
const rootY = helper.number("stanek.canPlace", "rootY", _rootY);
const rotation = helper.number("stanek.canPlace", "rotation", _rotation);
const fragmentId = helper.number("stanek.canPlace", "fragmentId", _fragmentId);
helper.updateDynamicRam("canPlace", getRamCost(player, "stanek", "canPlace"));
checkStanekAPIAccess("canPlace");
const fragment = FragmentById(fragmentId);
@ -79,29 +79,29 @@ export function NetscriptStanek(player: IPlayer, workerScript: WorkerScript, hel
const can = staneksGift.canPlace(rootX, rootY, rotation, fragment);
return can;
},
place: function (arootX: unknown, arootY: unknown, arotation: unknown, afragmentId: unknown): boolean {
const rootX = helper.number("stanek.place", "rootX", arootX);
const rootY = helper.number("stanek.place", "rootY", arootY);
const rotation = helper.number("stanek.place", "rotation", arotation);
const fragmentId = helper.number("stanek.place", "fragmentId", afragmentId);
place: function (_rootX: unknown, _rootY: unknown, _rotation: unknown, _fragmentId: unknown): boolean {
const rootX = helper.number("stanek.place", "rootX", _rootX);
const rootY = helper.number("stanek.place", "rootY", _rootY);
const rotation = helper.number("stanek.place", "rotation", _rotation);
const fragmentId = helper.number("stanek.place", "fragmentId", _fragmentId);
helper.updateDynamicRam("place", getRamCost(player, "stanek", "place"));
checkStanekAPIAccess("place");
const fragment = FragmentById(fragmentId);
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${fragmentId}`);
return staneksGift.place(rootX, rootY, rotation, fragment);
},
get: function (arootX: unknown, arootY: unknown): IActiveFragment | undefined {
const rootX = helper.number("stanek.get", "rootX", arootX);
const rootY = helper.number("stanek.get", "rootY", arootY);
get: function (_rootX: unknown, _rootY: unknown): IActiveFragment | undefined {
const rootX = helper.number("stanek.get", "rootX", _rootX);
const rootY = helper.number("stanek.get", "rootY", _rootY);
helper.updateDynamicRam("get", getRamCost(player, "stanek", "get"));
checkStanekAPIAccess("get");
const fragment = staneksGift.findFragment(rootX, rootY);
if (fragment !== undefined) return fragment.copy();
return undefined;
},
remove: function (arootX: unknown, arootY: unknown): boolean {
const rootX = helper.number("stanek.remove", "rootX", arootX);
const rootY = helper.number("stanek.remove", "rootY", arootY);
remove: function (_rootX: unknown, _rootY: unknown): boolean {
const rootX = helper.number("stanek.remove", "rootX", _rootX);
const rootY = helper.number("stanek.remove", "rootY", _rootY);
helper.updateDynamicRam("remove", getRamCost(player, "stanek", "remove"));
checkStanekAPIAccess("remove");
return staneksGift.delete(rootX, rootY);

@ -34,33 +34,37 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return stock;
};
return {
getSymbols: function (): any {
getSymbols: function (): string[] {
helper.updateDynamicRam("getSymbols", getRamCost(player, "stock", "getSymbols"));
checkTixApiAccess("getSymbols");
return Object.values(StockSymbols);
},
getPrice: function (symbol: any): any {
getPrice: function (_symbol: unknown): number {
const symbol = helper.string("getPrice", "symbol", _symbol);
helper.updateDynamicRam("getPrice", getRamCost(player, "stock", "getPrice"));
checkTixApiAccess("getPrice");
const stock = getStockFromSymbol(symbol, "getPrice");
return stock.price;
},
getAskPrice: function (symbol: any): any {
getAskPrice: function (_symbol: unknown): number {
const symbol = helper.string("getAskPrice", "symbol", _symbol);
helper.updateDynamicRam("getAskPrice", getRamCost(player, "stock", "getAskPrice"));
checkTixApiAccess("getAskPrice");
const stock = getStockFromSymbol(symbol, "getAskPrice");
return stock.getAskPrice();
},
getBidPrice: function (symbol: any): any {
getBidPrice: function (_symbol: unknown): number {
const symbol = helper.string("getBidPrice", "symbol", _symbol);
helper.updateDynamicRam("getBidPrice", getRamCost(player, "stock", "getBidPrice"));
checkTixApiAccess("getBidPrice");
const stock = getStockFromSymbol(symbol, "getBidPrice");
return stock.getBidPrice();
},
getPosition: function (symbol: any): any {
getPosition: function (_symbol: unknown): [number, number, number, number] {
const symbol = helper.string("getPosition", "symbol", _symbol);
helper.updateDynamicRam("getPosition", getRamCost(player, "stock", "getPosition"));
checkTixApiAccess("getPosition");
const stock = SymbolToStockMap[symbol];
@ -69,14 +73,18 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
}
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
},
getMaxShares: function (symbol: any): any {
getMaxShares: function (_symbol: unknown): number {
const symbol = helper.string("getMaxShares", "symbol", _symbol);
helper.updateDynamicRam("getMaxShares", getRamCost(player, "stock", "getMaxShares"));
checkTixApiAccess("getMaxShares");
const stock = getStockFromSymbol(symbol, "getMaxShares");
return stock.maxShares;
},
getPurchaseCost: function (symbol: any, shares: any, posType: any): any {
getPurchaseCost: function (_symbol: unknown, _shares: unknown, _posType: unknown): number {
const symbol = helper.string("getPurchaseCost", "symbol", _symbol);
let shares = helper.number("getPurchaseCost", "shares", _shares);
const posType = helper.string("getPurchaseCost", "posType", _posType);
helper.updateDynamicRam("getPurchaseCost", getRamCost(player, "stock", "getPurchaseCost"));
checkTixApiAccess("getPurchaseCost");
const stock = getStockFromSymbol(symbol, "getPurchaseCost");
@ -99,7 +107,10 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return res;
},
getSaleGain: function (symbol: any, shares: any, posType: any): any {
getSaleGain: function (_symbol: unknown, _shares: unknown, _posType: unknown): number {
const symbol = helper.string("getSaleGain", "symbol", _symbol);
let shares = helper.number("getSaleGain", "shares", _shares);
const posType = helper.string("getSaleGain", "posType", _posType);
helper.updateDynamicRam("getSaleGain", getRamCost(player, "stock", "getSaleGain"));
checkTixApiAccess("getSaleGain");
const stock = getStockFromSymbol(symbol, "getSaleGain");
@ -122,14 +133,18 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return res;
},
buy: function (symbol: any, shares: any): any {
buy: function (_symbol: unknown, _shares: unknown): number {
const symbol = helper.string("buy", "symbol", _symbol);
const shares = helper.number("buy", "shares", _shares);
helper.updateDynamicRam("buy", getRamCost(player, "stock", "buy"));
checkTixApiAccess("buy");
const stock = getStockFromSymbol(symbol, "buy");
const res = buyStock(stock, shares, workerScript, {});
return res ? stock.getAskPrice() : 0;
},
sell: function (symbol: any, shares: any): any {
sell: function (_symbol: unknown, _shares: unknown): number {
const symbol = helper.string("sell", "symbol", _symbol);
const shares = helper.number("sell", "shares", _shares);
helper.updateDynamicRam("sell", getRamCost(player, "stock", "sell"));
checkTixApiAccess("sell");
const stock = getStockFromSymbol(symbol, "sell");
@ -137,7 +152,9 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return res ? stock.getBidPrice() : 0;
},
short: function (symbol: any, shares: any): any {
short: function (_symbol: unknown, _shares: unknown): number {
const symbol = helper.string("short", "symbol", _symbol);
const shares = helper.number("short", "shares", _shares);
helper.updateDynamicRam("short", getRamCost(player, "stock", "short"));
checkTixApiAccess("short");
if (player.bitNodeN !== 8) {
@ -153,7 +170,9 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return res ? stock.getBidPrice() : 0;
},
sellShort: function (symbol: any, shares: any): any {
sellShort: function (_symbol: unknown, _shares: unknown): number {
const symbol = helper.string("sellShort", "symbol", _symbol);
const shares = helper.number("sellShort", "shares", _shares);
helper.updateDynamicRam("sellShort", getRamCost(player, "stock", "sellShort"));
checkTixApiAccess("sellShort");
if (player.bitNodeN !== 8) {
@ -169,7 +188,12 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return res ? stock.getAskPrice() : 0;
},
placeOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any {
placeOrder: function (_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean {
const symbol = helper.string("placeOrder", "symbol", _symbol);
const shares = helper.number("placeOrder", "shares", _shares);
const price = helper.number("placeOrder", "price", _price);
const type = helper.string("placeOrder", "type", _type);
const pos = helper.string("placeOrder", "pos", _pos);
helper.updateDynamicRam("placeOrder", getRamCost(player, "stock", "placeOrder"));
checkTixApiAccess("placeOrder");
if (player.bitNodeN !== 8) {
@ -208,7 +232,18 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return placeOrder(stock, shares, price, orderType, orderPos, workerScript);
},
cancelOrder: function (symbol: any, shares: any, price: any, type: any, pos: any): any {
cancelOrder: function (
_symbol: unknown,
_shares: unknown,
_price: unknown,
_type: unknown,
_pos: unknown,
): boolean {
const symbol = helper.string("cancelOrder", "symbol", _symbol);
const shares = helper.number("cancelOrder", "shares", _shares);
const price = helper.number("cancelOrder", "price", _price);
const type = helper.string("cancelOrder", "type", _type);
const pos = helper.string("cancelOrder", "pos", _pos);
helper.updateDynamicRam("cancelOrder", getRamCost(player, "stock", "cancelOrder"));
checkTixApiAccess("cancelOrder");
if (player.bitNodeN !== 8) {
@ -290,7 +325,8 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return orders;
},
getVolatility: function (symbol: any): any {
getVolatility: function (_symbol: unknown): number {
const symbol = helper.string("getVolatility", "symbol", _symbol);
helper.updateDynamicRam("getVolatility", getRamCost(player, "stock", "getVolatility"));
if (!player.has4SDataTixApi) {
throw helper.makeRuntimeErrorMsg("getVolatility", "You don't have 4S Market Data TIX API Access!");
@ -299,7 +335,8 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
return stock.mv / 100; // Convert from percentage to decimal
},
getForecast: function (symbol: any): any {
getForecast: function (_symbol: unknown): number {
const symbol = helper.string("getForecast", "symbol", _symbol);
helper.updateDynamicRam("getForecast", getRamCost(player, "stock", "getForecast"));
if (!player.has4SDataTixApi) {
throw helper.makeRuntimeErrorMsg("getForecast", "You don't have 4S Market Data TIX API Access!");
@ -310,7 +347,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
return forecast / 100; // Convert from percentage to decimal
},
purchase4SMarketData: function () {
purchase4SMarketData: function (): boolean {
helper.updateDynamicRam("purchase4SMarketData", getRamCost(player, "stock", "purchase4SMarketData"));
checkTixApiAccess("purchase4SMarketData");
@ -329,7 +366,7 @@ export function NetscriptStockMarket(player: IPlayer, workerScript: WorkerScript
workerScript.log("stock.purchase4SMarketData", () => "Purchased 4S Market Data");
return true;
},
purchase4SMarketDataTixApi: function () {
purchase4SMarketDataTixApi: function (): boolean {
helper.updateDynamicRam("purchase4SMarketDataTixApi", getRamCost(player, "stock", "purchase4SMarketDataTixApi"));
checkTixApiAccess("purchase4SMarketDataTixApi");

@ -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

@ -363,11 +363,11 @@ export class PlayerObject implements IPlayer {
this.companyName = ""; // Name of Company. Must match a key value in Companies ma;
// Servers
this.currentServer = ""; //IP address of Server currently being accessed through termina;
this.purchasedServers = []; //IP Addresses of purchased server;
this.currentServer = ""; //hostname of Server currently being accessed through termina;
this.purchasedServers = []; //hostnames of purchased server;
// Hacknet Nodes/Servers
this.hacknetNodes = []; // Note= For Hacknet Servers, this array holds the IP addresses of the server;
this.hacknetNodes = []; // Note= For Hacknet Servers, this array holds the hostnames of the server;
this.hashManager = new HashManager();
//Factions
@ -483,11 +483,11 @@ export class PlayerObject implements IPlayer {
// Let's get a hash of some semi-random stuff so we have something unique.
this.identifier = cyrb53(
"I-" +
new Date().getTime() +
navigator.userAgent +
window.innerWidth +
window.innerHeight +
getRandomInt(100, 999),
new Date().getTime() +
navigator.userAgent +
window.innerWidth +
window.innerHeight +
getRandomInt(100, 999),
);
this.init = generalMethods.init;

@ -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;
}
/**
@ -2784,7 +2785,7 @@ export interface Bladeburner {
*
* Note that this is meant to be used for Contracts and Operations.
* This function will return Infinity for actions such as Training and Field Analysis.
* This function will return 1 for BlackOps not yet completed regardless of wether
* This function will return 1 for BlackOps not yet completed regardless of whether
* the player has the required rank to attempt the mission or not.
*
* @param type - Type of action.
@ -2824,7 +2825,7 @@ export interface Bladeburner {
getActionCurrentLevel(type: string, name: string): number;
/**
* Get wether an action is set to autolevel.
* Get whether an action is set to autolevel.
* @remarks
* RAM cost: 4 GB
*
@ -5983,19 +5984,25 @@ export interface NS extends Singularity {
tFormat(milliseconds: number, milliPrecision?: boolean): string;
/**
* Prompt the player with a Yes/No modal.
* Prompt the player with an input modal.
* @remarks
* RAM cost: 0 GB
*
* Prompts the player with a dialog box with two options: Yes and No.
* This function will return true if the player click Yes and false if
* the player clicks No. The scripts execution is halted until the player
* selects one of the options.
* Prompts the player with a dialog box. If `options.type` is undefined or "boolean",
* the player is shown "Yes" and "No" prompts, which return true and false respectively.
* Passing a type of "text" will give the player a text field and a value of "select"
* will show a drop-down field. Choosing type "select" will require an array or object
* to be passed via the `options.choices` property.
* The scripts execution is halted until the player selects one of the options.
*
* @param txt - Text to appear in the prompt dialog box.
* @returns True if the player click Yes and false if the player clicks No.
* @param options - Options to modify the prompt the player is shown.
* @returns True if the player click Yes; false if the player clicks No; or the value entered by the player.
*/
prompt(txt: string): Promise<boolean>;
prompt(
txt: string,
options?: { type?: "boolean" | "text" | "select" | undefined; choices?: string[] },
): Promise<boolean | string>;
/**
* Open up a message box.
@ -6385,12 +6392,12 @@ export interface WarehouseAPI {
*/
buyMaterial(divisionName: string, cityName: string, materialName: string, amt: number): void;
/**
* Set material to bulk buy
* @param divisionName - Name of the division
* @param cityName - Name of the city
* @param materialName - Name of the material
* @param amt - Amount of material to buy
*/
* Set material to bulk buy
* @param divisionName - Name of the division
* @param cityName - Name of the city
* @param materialName - Name of the material
* @param amt - Amount of material to buy
*/
bulkPurchase(divisionName: string, cityName: string, materialName: string, amt: number): void;
/**
* Get warehouse data

@ -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;
}
@ -688,7 +691,7 @@ export function Root(props: IProps): React.ReactElement {
if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex as number].code) {
PromptEvent.emit({
txt: "Do you want to save changes to " + closingScript.fileName + "?",
resolve: (result: boolean) => {
resolve: (result: boolean | string) => {
if (result) {
// Save changes
closingScript.code = savedScriptCode;
@ -745,7 +748,7 @@ export function Root(props: IProps): React.ReactElement {
"Do you want to overwrite the current editor content with the contents of " +
openScript.fileName +
" on the server? This cannot be undone.",
resolve: (result: boolean) => {
resolve: (result: boolean | string) => {
if (result) {
// Save changes
openScript.code = serverScriptCode;
@ -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;
}

@ -76,7 +76,7 @@ export class BaseServer {
// Script files on this Server
scripts: Script[] = [];
// Contains the IP Addresses of all servers that are immediately
// Contains the hostnames of all servers that are immediately
// reachable from this one
serversOnNetwork: string[] = [];

@ -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) {

@ -190,7 +190,7 @@ export const HelpTexts: IMap<string[]> = {
connect: [
"Usage: connect [hostname]",
" ",
"Connect to a remote server. The hostname or IP address of the remote server must be given as the argument ",
"Connect to a remote server. The hostname of the remote server must be given as the argument ",
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
"see which servers can be connected to, use the 'scan' command.",
" ",

@ -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",

@ -56,7 +56,7 @@ export function ServerAccordions(props: IProps): React.ReactElement {
for (const ws of props.workerScripts.values()) {
const server = GetServer(ws.hostname);
if (server == null) {
console.warn(`WorkerScript has invalid IP address: ${ws.hostname}`);
console.warn(`WorkerScript has invalid hostname: ${ws.hostname}`);
continue;
}

@ -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>
</>
);
}

@ -3,54 +3,150 @@ import { EventEmitter } from "../../utils/EventEmitter";
import { Modal } from "./Modal";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import MenuItem from "@mui/material/MenuItem";
export const PromptEvent = new EventEmitter<[Prompt]>();
interface Prompt {
txt: string;
resolve: (result: boolean) => void;
options?: { type?: string; choices?: string[] };
resolve: (result: boolean | string) => void;
}
export function PromptManager(): React.ReactElement {
const [prompt, setPrompt] = useState<Prompt | null>(null);
useEffect(
() =>
PromptEvent.subscribe((p: Prompt) => {
setPrompt(p);
}),
[],
);
useEffect(() => {
return PromptEvent.subscribe((p: Prompt) => {
setPrompt(p);
});
}, []);
if (prompt === null) {
return <></>;
}
function close(): void {
if (prompt === null) return;
prompt.resolve(false);
if (["text", "select"].includes(prompt?.options?.type ?? "")) {
prompt.resolve("");
} else {
prompt.resolve(false);
}
setPrompt(null);
}
function yes(): void {
if (prompt === null) return;
prompt.resolve(true);
const types: { [key: string]: any } = {
text: PromptMenuText,
select: PromptMenuSelect,
};
let PromptContent = PromptMenuBoolean;
if (prompt?.options?.type) PromptContent = types[prompt?.options?.type];
const resolve = (value: boolean | string): void => {
prompt.resolve(value);
setPrompt(null);
}
function no(): void {
if (prompt === null) return;
prompt.resolve(false);
setPrompt(null);
}
};
return (
<Modal open={true} onClose={close}>
<Typography>{prompt.txt}</Typography>
<PromptContent prompt={prompt} resolve={resolve} />
</Modal>
);
}
interface IContentProps {
prompt: Prompt;
resolve: (value: boolean | string) => void;
}
function PromptMenuBoolean({ resolve }: IContentProps): React.ReactElement {
const yes = (): void => resolve(true);
const no = (): void => resolve(false);
return (
<>
{prompt != null && (
<Modal open={true} onClose={close}>
<pre>
<Typography>{prompt.txt}</Typography>
</pre>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', paddingTop: '10px' }}>
<Button style={{ marginRight: 'auto' }} onClick={yes}>Yes</Button>
<Button onClick={no}>No</Button>
</div>
</Modal>
)}
<div style={{ display: "flex", justifyContent: "center", alignItems: "center", paddingTop: "10px" }}>
<Button style={{ marginRight: "auto" }} onClick={yes}>
Yes
</Button>
<Button onClick={no}>No</Button>
</div>
</>
);
}
function PromptMenuText({ resolve }: IContentProps): React.ReactElement {
const [value, setValue] = useState("");
const submit = (): void => resolve(value);
const onInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
setValue(event.target.value);
};
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {
event.stopPropagation();
if (event.key === "Enter") {
event.preventDefault();
submit();
}
};
return (
<>
<div style={{ display: "flex", alignItems: "center", paddingTop: "10px" }}>
<TextField
autoFocus
value={value}
onInput={onInput}
onKeyDown={onKeyDown}
style={{ flex: "1 0 auto" }}
InputProps={{
endAdornment: <Button onClick={submit}>Confirm</Button>,
}}
/>
</div>
</>
);
}
function PromptMenuSelect({ prompt, resolve }: IContentProps): React.ReactElement {
const [value, setValue] = useState("");
const submit = (): void => resolve(value);
const onChange = (event: SelectChangeEvent<string>): void => {
setValue(event.target.value);
};
const getItems = (choices: string[]): React.ReactElement[] => {
const content = [];
for (const i of choices) {
// @ts-ignore
content.push(
<MenuItem key={i} value={i}>
{i}
</MenuItem>,
);
}
return content;
};
return (
<>
<div style={{ display: "flex", alignItems: "center", paddingTop: "10px" }}>
<Select onChange={onChange} value={value} style={{ flex: "1 0 auto" }}>
{getItems(prompt?.options?.choices || [])}
</Select>
<Button onClick={submit} disabled={value === ""}>
Confirm
</Button>
</div>
</>
);
}

@ -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}`);