fetch latest from upstream

This commit is contained in:
s2ks 2022-07-08 01:35:20 +02:00
commit 063da8aa60
34 changed files with 141 additions and 104 deletions

4
dist/main.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

38
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

@ -39,7 +39,7 @@ There are two methods of obtaining Duplicate Sleeves:
1. Destroy BitNode-10. Each completion give you one additional Duplicate Sleeve 1. Destroy BitNode-10. Each completion give you one additional Duplicate Sleeve
2. Purchase Duplicate Sleeves from :ref:`the faction The Covenant <gameplay_factions>`. 2. Purchase Duplicate Sleeves from :ref:`the faction The Covenant <gameplay_factions>`.
This is only available in BitNodes-10. Sleeves purchased this way are **permanent** (they persist This is only available in BitNode-10. Sleeves purchased this way are **permanent** (they persist
through BitNodes). You can purchase up to 5 Duplicate Sleeves from The Covenant. through BitNodes). You can purchase up to 5 Duplicate Sleeves from The Covenant.
Synchronization Synchronization
@ -86,8 +86,7 @@ switching BitNodes. For example, if a sleeve has a memory of 10, then when you
switch BitNodes its synchronization will initially be set to 10, rather than 1. switch BitNodes its synchronization will initially be set to 10, rather than 1.
Memory can only be increased by purchasing upgrades from The Covenant. Just like Memory can only be increased by purchasing upgrades from The Covenant. Just like
the ability to purchase additional sleeves, this is only available in BitNodes-10 the ability to purchase additional sleeves, this is only available in BitNode-10.
and above, and is only available after defeating BitNode-10 at least once.
Memory is a persistent stat, meaning it never gets reset back to 1. Memory is a persistent stat, meaning it never gets reset back to 1.
The maximum possible value for a sleeve's memory is 100. The maximum possible value for a sleeve's memory is 100.

@ -56,7 +56,7 @@ Here is everything you will KEEP when you install an Augmentation:
* Every Augmentation you have installed * Every Augmentation you have installed
* Scripts on your home computer * Scripts on your home computer
* RAM Upgrades on your home computer * RAM/Core Upgrades on your home computer
* World Stock Exchange account and TIX API Access * World Stock Exchange account and TIX API Access
.. _gameplay_augmentations_purchasingmultiple: .. _gameplay_augmentations_purchasingmultiple:

@ -26,4 +26,16 @@ Harder crimes are typically more profitable, and also give more EXP.
Crime details Crime details
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
TODO Available crimes, and their descriptions, which all begin with "attempt to..."
Shoplift …shoplift from a low-end retailer
Rob store …commit armed robbery on a high-end store
Mug someone …mug a random person on the street
Larceny …rob property from someone's house
Deal Drugs …deal drugs
Bond Forgery …forge corporate bonds
Traffick illegal Arms …smuggle illegal arms into the city
Homicide …murder a random person on the street
Grand theft Auto …commit grand theft auto
Kidnap and Ransom …kidnap and ransom a high-profile-target
Assassinate …assassinate a high-profile target
Heist …pull off the ultimate heist

@ -2,6 +2,9 @@
Factions Factions
======== ========
.. warning:: This page contains spoilers regarding the game's story/plot-line.
Throughout the game you may receive invitations from factions. There are Throughout the game you may receive invitations from factions. There are
many different factions, and each faction has different criteria for many different factions, and each faction has different criteria for
determining its potential members. Joining a faction and furthering determining its potential members. Joining a faction and furthering

@ -8,15 +8,6 @@ on the navigation menu on the left-hand side of the game (you may need to expand
the 'Hacking' header in order to see the 'Terminal' tab). Alternatively, the :ref:`keyboard the 'Hacking' header in order to see the 'Terminal' tab). Alternatively, the :ref:`keyboard
shortcut <shortcuts>` Alt + t can be used to open the Terminal. shortcut <shortcuts>` Alt + t can be used to open the Terminal.
Configuration
-------------
The terminal has a configuration file called .fconf. To edit this file, go to
the terminal and enter::
nano .fconf
.. _terminal_filesystem: .. _terminal_filesystem:
Filesystem (Directories) Filesystem (Directories)

@ -62,7 +62,7 @@ To briefly summarize the information from the links above: Each server has a
security level that affects how difficult it is to hack. Each server also has a security level that affects how difficult it is to hack. Each server also has a
certain amount of money, as well as a maximum amount of money it can hold. Hacking a certain amount of money, as well as a maximum amount of money it can hold. Hacking a
server steals a percentage of that server's money. The :js:func:`hack` Netscript function server steals a percentage of that server's money. The :js:func:`hack` Netscript function
is used to hack server. The :js:func:`grow` Netscript function is used to increase is used to hack a server. The :js:func:`grow` Netscript function is used to increase
the amount of money available on a server. The :js:func:`weaken` Netscript function is the amount of money available on a server. The :js:func:`weaken` Netscript function is
used to decrease a server's security level. used to decrease a server's security level.

@ -21,7 +21,7 @@ To automatically enter commands in the terminal (only works if looking at the te
terminalInput[handler].onChange({target:terminalInput}); terminalInput[handler].onChange({target:terminalInput});
// Simulate an enter press // Simulate an enter press
terminalInput[handler].onKeyDown({keyCode:13,preventDefault:()=>null}); terminalInput[handler].onKeyDown({key:'Enter',preventDefault:()=>null});
To add lines to the terminal (only works if looking at the terminal): To add lines to the terminal (only works if looking at the terminal):
@ -32,4 +32,4 @@ To add lines to the terminal (only works if looking at the terminal):
const list = document.getElementById("generic-react-container").querySelector("ul"); const list = document.getElementById("generic-react-container").querySelector("ul");
// Inject some HTML. // Inject some HTML.
list.insertAdjacentHTML('beforeend',`<li><p color=lime>whatever custom html</p></li>`) list.insertAdjacentHTML('beforeend',`<li><p color=lime>whatever custom html</p></li>`)

@ -15,4 +15,4 @@ scriptKill() Netscript Function
.. code-block:: javascript .. code-block:: javascript
scriptKill("demo.script"); // returns: true scriptKill("demo.script", "home"); // returns: true

@ -23,4 +23,4 @@ spawn() Netscript Function
.. code-block:: javascript .. code-block:: javascript
spawn('foo.script', 10, 'foodnstuff', 90); // "run foo.script 10 foodnstuff 90" in 10 seconds. spawn('foo.script', 10, 'foodnstuff', 90); // "run foo.script foodnstuff 90 -t 10" in 10 seconds.

@ -23,3 +23,14 @@ into a script using::
args.length args.length
**WARNING: Do not try to modify the args array. This will break the game.** **WARNING: Do not try to modify the args array. This will break the game.**
example for accessing arguments in ns2 from terminal execution:
terminal command:
run name_of_script.js -t 10 --tail argument1 argument2
ns2 script:
const args_obj = arguments[0]
const argument1 = (args_obj.server.args[0])
const argument2 = (args_obj.server.args[1])

@ -123,7 +123,7 @@ export const BlackOperations: {
desc: ( desc: (
<> <>
CODE RED SITUATION. Our intelligence tells us that VitaLife has discovered a new android cloning technology. CODE RED SITUATION. Our intelligence tells us that VitaLife has discovered a new android cloning technology.
This technology is supposedly capable of cloning Synthoid, not only physically but also their advanced AI This technology is supposedly capable of cloning Synthoids, not only physically but also their advanced AI
modules. We do not believe that VitaLife is trying to use this technology illegally or maliciously, but if any modules. We do not believe that VitaLife is trying to use this technology illegally or maliciously, but if any
Synthoids were able to infiltrate the corporation and take advantage of this technology then the results would Synthoids were able to infiltrate the corporation and take advantage of this technology then the results would
be catastrophic. be catastrophic.
@ -256,8 +256,8 @@ export const BlackOperations: {
ones from the Uprising. ones from the Uprising.
<br /> <br />
<br /> <br />
{FactionNames.OmniTekIncorporated} has also told us they they believe someone has triggered this malfunction in {FactionNames.OmniTekIncorporated} has also told us they believe someone has triggered this malfunction in a
a large group of MK-VI Synthoids, and that these newly-radicalized Synthoids are now amassing in{" "} large group of MK-VI Synthoids, and that these newly-radicalized Synthoids are now amassing in{" "}
{CityName.Volhaven} to form a terrorist group called Ultron. {CityName.Volhaven} to form a terrorist group called Ultron.
<br /> <br />
<br /> <br />

@ -229,7 +229,7 @@ export const CONSTANTS: {
InfiniteLoopLimit: 2000, InfiniteLoopLimit: 2000,
Donations: 7, Donations: 20,
LatestUpdate: ` LatestUpdate: `
## [draft] v1.7.0 - 2022-04-13 to 2022-05-20 ## [draft] v1.7.0 - 2022-04-13 to 2022-05-20

@ -71,7 +71,7 @@ export const CorporationUpgrades: Record<CorporationUpgradeIndex, CorporationUpg
name: "Wilson Analytics", name: "Wilson Analytics",
desc: desc:
"Purchase data and analysis from Wilson, a marketing research " + "Purchase data and analysis from Wilson, a marketing research " +
"firm. Each level of this upgrades increases the effectiveness of your " + "firm. Each level of this upgrade increases the effectiveness of your " +
"advertising by 0.5% (additive).", "advertising by 0.5% (additive).",
}, },

@ -66,7 +66,7 @@ export function ActiveFragmentSummary(props: IProps): React.ReactElement {
</TableRow> </TableRow>
{summary.map((entry) => { {summary.map((entry) => {
return ( return (
<TableRow> <TableRow key={entry.type}>
<TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}> <TableCell sx={{ borderBottom: "none", p: 0, m: 0 }}>
<Typography> <Typography>
{entry.coordinate.map((coord) => { {entry.coordinate.map((coord) => {

@ -3,7 +3,7 @@ import * as React from "react";
import { ActiveFragment } from "../ActiveFragment"; import { ActiveFragment } from "../ActiveFragment";
import { DummyGift } from "../DummyGift"; import { DummyGift } from "../DummyGift";
import { Grid } from "./Grid"; import { Grid } from "./Grid";
import { calculateGrid, zeros } from "../Helper"; import { zeros } from "../Helper";
interface IProps { interface IProps {
width: number; width: number;
@ -13,7 +13,6 @@ interface IProps {
export function DummyGrid(props: IProps): React.ReactElement { export function DummyGrid(props: IProps): React.ReactElement {
const gift = new DummyGift(props.width, props.height, props.fragments); const gift = new DummyGift(props.width, props.height, props.fragments);
const activeGrid = calculateGrid(gift);
const ghostGrid = zeros([props.width, props.height]); const ghostGrid = zeros([props.width, props.height]);
return ( return (
<Box> <Box>
@ -21,7 +20,6 @@ export function DummyGrid(props: IProps): React.ReactElement {
<Grid <Grid
width={props.width} width={props.width}
height={props.height} height={props.height}
activeGrid={activeGrid}
ghostGrid={ghostGrid} ghostGrid={ghostGrid}
gift={gift} gift={gift}
enter={() => undefined} enter={() => undefined}

@ -1,13 +1,13 @@
import { TableBody, TableRow } from "@mui/material"; import { TableBody, TableRow } from "@mui/material";
import * as React from "react"; import * as React from "react";
import { ActiveFragment } from "../ActiveFragment"; import { ActiveFragment } from "../ActiveFragment";
import { calculateGrid } from "../Helper";
import { IStaneksGift } from "../IStaneksGift"; import { IStaneksGift } from "../IStaneksGift";
import { Cell } from "./Cell"; import { Cell } from "./Cell";
interface IProps { interface IProps {
width: number; width: number;
height: number; height: number;
activeGrid: number[][];
ghostGrid: number[][]; ghostGrid: number[][];
gift: IStaneksGift; gift: IStaneksGift;
enter(i: number, j: number): void; enter(i: number, j: number): void;
@ -32,11 +32,13 @@ function randomColor(fragment: ActiveFragment): string {
} }
export function Grid(props: IProps): React.ReactElement { export function Grid(props: IProps): React.ReactElement {
const activeGrid = calculateGrid(props.gift);
function color(worldX: number, worldY: number): string { function color(worldX: number, worldY: number): string {
if (props.ghostGrid[worldX][worldY] && props.activeGrid[worldX][worldY]) return "red"; if (props.ghostGrid[worldX][worldY] && activeGrid[worldX][worldY]) return "red";
if (props.ghostGrid[worldX][worldY]) return "white"; if (props.ghostGrid[worldX][worldY]) return "white";
if (props.activeGrid[worldX][worldY]) { if (activeGrid[worldX][worldY]) {
const fragment = props.gift.fragmentAt(worldX, worldY); const fragment = props.gift.fragmentAt(worldX, worldY);
if (!fragment) throw new Error("ActiveFragment should not be null"); if (!fragment) throw new Error("ActiveFragment should not be null");
return randomColor(fragment); return randomColor(fragment);

@ -8,7 +8,7 @@ import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Table } from "../../ui/React/Table"; import { Table } from "../../ui/React/Table";
import { Grid } from "./Grid"; import { Grid } from "./Grid";
import { zeros, calculateGrid } from "../Helper"; import { zeros } from "../Helper";
import { ActiveFragmentSummary } from "./ActiveFragmentSummary"; import { ActiveFragmentSummary } from "./ActiveFragmentSummary";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
@ -18,7 +18,6 @@ interface IProps {
} }
export function MainBoard(props: IProps): React.ReactElement { export function MainBoard(props: IProps): React.ReactElement {
const [grid, setGrid] = React.useState(calculateGrid(props.gift));
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()])); const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
const [pos, setPos] = React.useState([0, 0]); const [pos, setPos] = React.useState([0, 0]);
const [rotation, setRotation] = React.useState(0); const [rotation, setRotation] = React.useState(0);
@ -54,12 +53,10 @@ export function MainBoard(props: IProps): React.ReactElement {
if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return; if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return;
props.gift.place(worldX, worldY, rotation, selectedFragment); props.gift.place(worldX, worldY, rotation, selectedFragment);
} }
setGrid(calculateGrid(props.gift));
} }
function clear(): void { function clear(): void {
props.gift.clear(); props.gift.clear();
setGrid(zeros([props.gift.width(), props.gift.height()]));
} }
function updateSelectedFragment(fragment: Fragment): void { function updateSelectedFragment(fragment: Fragment): void {
@ -92,7 +89,6 @@ export function MainBoard(props: IProps): React.ReactElement {
<Grid <Grid
width={props.gift.width()} width={props.gift.width()}
height={props.gift.height()} height={props.gift.height()}
activeGrid={grid}
ghostGrid={ghostGrid} ghostGrid={ghostGrid}
gift={props.gift} gift={props.gift}
enter={(i, j) => moveGhost(i, j, rotation)} enter={(i, j) => moveGhost(i, j, rotation)}

@ -150,7 +150,7 @@ export const FactionInfos: IMap<FactionInfo> = {
[FactionNames.BachmanAssociates]: new FactionInfo({ [FactionNames.BachmanAssociates]: new FactionInfo({
infoText: ( infoText: (
<> <>
Where Law and Business meet - thats where we are. Where Law and Business meet - that's where we are.
<br /> <br />
<br /> <br />
Legal Insight - Business Instinct - Innovative Experience. Legal Insight - Business Instinct - Innovative Experience.

@ -426,7 +426,7 @@ function processAllHacknetServerEarnings(player: IPlayer, numCycles: number): nu
if (upgrade === null) throw new Error("Could not get the hash upgrade"); if (upgrade === null) throw new Error("Could not get the hash upgrade");
if (!upgrade.cost) throw new Error("Upgrade is not properly configured"); if (!upgrade.cost) throw new Error("Upgrade is not properly configured");
const multiplier = Math.floor(wastedHashes / upgrade.cost); const multiplier = wastedHashes / upgrade.cost;
if (multiplier > 0) { if (multiplier > 0) {
player.gainMoney(upgrade.value * multiplier, "hacknet"); player.gainMoney(upgrade.value * multiplier, "hacknet");
} }

@ -244,7 +244,7 @@ export const Literatures: IMap<Literature> = {};
"is that most of the rise is in violent crime such as homicide and assault. According " + "is that most of the rise is in violent crime such as homicide and assault. According " +
"to the study, the city saw a total of 21,406 reported homicides in 2076, which is over " + "to the study, the city saw a total of 21,406 reported homicides in 2076, which is over " +
"a 20% increase compared to 2075.<br><br>" + "a 20% increase compared to 2075.<br><br>" +
"CIA director David Glarow says its too early to know " + "CIA director David Glarow says it's too early to know " +
"whether these figures indicate the beginning of a sustained increase in crime rates, or whether " + "whether these figures indicate the beginning of a sustained increase in crime rates, or whether " +
"the year was just an unfortunate outlier. He states that many intelligence and law enforcement " + "the year was just an unfortunate outlier. He states that many intelligence and law enforcement " +
"agents have noticed an increase in organized crime activites, and believes that these figures may " + "agents have noticed an increase in organized crime activites, and believes that these figures may " +
@ -319,7 +319,7 @@ export const Literatures: IMap<Literature> = {};
"CompanionBot, a robot meant to act as a comforting friend for lonely and grieving people, is eerily human-like " + "CompanionBot, a robot meant to act as a comforting friend for lonely and grieving people, is eerily human-like " +
"in its appearance, speech, mannerisms, and even movement. However its artificial intelligence isn't the same as " + "in its appearance, speech, mannerisms, and even movement. However its artificial intelligence isn't the same as " +
"that of humans. Not yet. It doesn't have sentience or self-awareness or consciousness.<br><br>" + "that of humans. Not yet. It doesn't have sentience or self-awareness or consciousness.<br><br>" +
"Many neuroscientists believe that we won't ever reach the point of creating artificial human intelligence. 'At the end of the " + "Many neuroscientists believe that we won't ever reach the point of creating artificial human intelligence. 'At the end of " +
"the day, AI comes down to 1's and 0's, while the human brain does not. We'll never see AI that is identical to that of " + "the day, AI comes down to 1's and 0's, while the human brain does not. We'll never see AI that is identical to that of " +
"humans.'"; "humans.'";
Literatures[fn] = new Literature(title, fn, txt); Literatures[fn] = new Literature(title, fn, txt);

@ -137,7 +137,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
return ( return (
<> <>
<Typography> <Typography>
<i>A business man is yelling at a clerk. You should come back later.</i> <i>A businessman is yelling at a clerk. You should come back later.</i>
</Typography> </Typography>
</> </>
); );
@ -232,7 +232,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
<> <>
<Typography> <Typography>
<i> <i>
Allison "Mother" Stanek: I see you've taken to my creation. So much so it could hardly be recognized as Allison "Mother" Stanek: I see you've taken to my creation. So much that it could hardly be recognized as
one of my own after your tinkering with it. I see you follow the ways of the Machine God as I do, and your one of my own after your tinkering with it. I see you follow the ways of the Machine God as I do, and your
mastery of the gift clearly demonstrates that. My hopes are climbing by the day for you. mastery of the gift clearly demonstrates that. My hopes are climbing by the day for you.
</i> </i>

@ -164,6 +164,11 @@ export function NetscriptSingularity(player: IPlayer, workerScript: WorkerScript
const augs = getFactionAugmentationsFiltered(player, fac); const augs = getFactionAugmentationsFiltered(player, fac);
if (!player.factions.includes(fac.name)) {
_ctx.log(() => `You can't purchase augmentations from '${facName}' because you aren't a member`);
return false;
}
if (!augs.includes(augName)) { if (!augs.includes(augName)) {
_ctx.log(() => `Faction '${facName}' does not have the '${augName}' augmentation.`); _ctx.log(() => `Faction '${facName}' does not have the '${augName}' augmentation.`);
return false; return false;

@ -1326,10 +1326,16 @@ export function createProgramWork(this: IPlayer, numCycles: number): boolean {
export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string { export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): string {
const programName = this.createProgramName; const programName = this.createProgramName;
let message = "";
if (!cancelled) { if (!cancelled) {
//Complete case //Complete case
this.gainIntelligenceExp((CONSTANTS.IntelligenceProgramBaseExpGain * this.timeWorked) / 1000); this.gainIntelligenceExp((CONSTANTS.IntelligenceProgramBaseExpGain * this.timeWorked) / 1000);
dialogBoxCreate(`You've finished creating ${programName}!<br>The new program can be found on your home computer.`); const lines = [
`You've finished creating ${programName}!`,
"The new program can be found on your home computer.",
];
dialogBoxCreate(lines.join("<br>"));
message = lines.join(" ");
if (!this.getHomeComputer().programs.includes(programName)) { if (!this.getHomeComputer().programs.includes(programName)) {
this.getHomeComputer().programs.push(programName); this.getHomeComputer().programs.push(programName);
@ -1339,12 +1345,12 @@ export function finishCreateProgramWork(this: IPlayer, cancelled: boolean): stri
const perc = (Math.floor((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 10000) / 100).toString(); const perc = (Math.floor((this.timeWorkedCreateProgram / this.timeNeededToCompleteWork) * 10000) / 100).toString();
const incompleteName = programName + "-" + perc + "%-INC"; const incompleteName = programName + "-" + perc + "%-INC";
this.getHomeComputer().programs.push(incompleteName); this.getHomeComputer().programs.push(incompleteName);
message = `Cancelled creating program: ${programName} (${perc}% complete)`;
} }
this.isWorking = false; this.isWorking = false;
this.resetWorkStatus(); this.resetWorkStatus();
return "You've finished creating " + programName + "! The new program can be found on your home computer."; return message;
} }
export function startGraftAugmentationWork(this: IPlayer, augmentationName: string, time: number): void { export function startGraftAugmentationWork(this: IPlayer, augmentationName: string, time: number): void {
@ -1854,7 +1860,11 @@ export function getNextCompanyPosition(
} }
export function quitJob(this: IPlayer, company: string, _sing = false): void { export function quitJob(this: IPlayer, company: string, _sing = false): void {
if (this.isWorking == true && this.workType !== WorkType.Company && this.companyName == company) { if (
this.isWorking === true &&
[WorkType.Company, WorkType.CompanyPartTime].includes(this.workType) &&
this.companyName === company
) {
this.finishWork(true); this.finishWork(true);
} }
delete this.jobs[company]; delete this.jobs[company];

@ -104,7 +104,7 @@ export function FAQModal({ open, onClose }: IProps): React.ReactElement {
<Typography variant="h4">What is Memory?</Typography> <Typography variant="h4">What is Memory?</Typography>
<br /> <br />
<Typography> <Typography>
Sleeve memory dictates what a sleeve's synchronization will be when its reset by switching BitNodes. For Sleeve memory dictates what a sleeve's synchronization will be when it's reset by switching BitNodes. For
example, if a sleeve has a memory of 25, then when you switch BitNodes its synchronization will initially be example, if a sleeve has a memory of 25, then when you switch BitNodes its synchronization will initially be
set to 25, rather than 1. set to 25, rather than 1.
</Typography> </Typography>

@ -580,7 +580,7 @@ function createNewUpdateText(): void {
() => () =>
dialogBoxCreate( dialogBoxCreate(
"New update!<br>" + "New update!<br>" +
"Please report any bugs/issues through the github repository " + "Please report any bugs/issues through the GitHub repository " +
"or the Bitburner subreddit (reddit.com/r/bitburner).<br><br>" + "or the Bitburner subreddit (reddit.com/r/bitburner).<br><br>" +
CONSTANTS.LatestUpdate, CONSTANTS.LatestUpdate,
resets, resets,

@ -4859,6 +4859,7 @@ export interface NS {
* @remarks * @remarks
* RAM cost: 0 GB * RAM cost: 0 GB
* *
* see: https://github.com/alexei/sprintf.js
* @param format - format of the message * @param format - format of the message
* @param msg - Value to be printed. * @param msg - Value to be printed.
*/ */
@ -4950,7 +4951,7 @@ export interface NS {
* @param args - Arguments to identify which scripts to get logs for. * @param args - Arguments to identify which scripts to get logs for.
* @returns Returns an string array, where each line is an element in the array. The most recently logged line is at the end of the array. * @returns Returns an string array, where each line is an element in the array. The most recently logged line is at the end of the array.
*/ */
getScriptLogs(fn?: string, host?: string, ...args: any[]): string[]; getScriptLogs(fn?: string, host?: string, ...args: (string | number | boolean)[]): string[];
/** /**
* Get an array of recently killed scripts across all servers. * Get an array of recently killed scripts across all servers.
@ -5013,7 +5014,7 @@ export interface NS {
* @param host - Optional. Hostname of the script being tailed. Defaults to the server this script is running on. If args are specified, this is not optional. * @param host - Optional. Hostname of the script being tailed. Defaults to the server this script is running on. If args are specified, this is not optional.
* @param args - Arguments for the script being tailed. * @param args - Arguments for the script being tailed.
*/ */
tail(fn?: FilenameOrPID, host?: string, ...args: any[]): void; tail(fn?: FilenameOrPID, host?: string, ...args: (string | number | boolean)[]): void;
/** /**
* Close the tail window of a script. * Close the tail window of a script.
@ -5218,7 +5219,7 @@ export interface NS {
* @param args - Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the second argument numThreads must be filled in with a value. * @param args - Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the second argument numThreads must be filled in with a value.
* @returns Returns the PID of a successfully started script, and 0 otherwise. * @returns Returns the PID of a successfully started script, and 0 otherwise.
*/ */
run(script: string, numThreads?: number, ...args: Array<string | number | boolean>): number; run(script: string, numThreads?: number, ...args: (string | number | boolean)[]): number;
/** /**
* Start another script on any server. * Start another script on any server.
@ -5266,7 +5267,7 @@ export interface NS {
* @param args - Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the third argument numThreads must be filled in with a value. * @param args - Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the third argument numThreads must be filled in with a value.
* @returns Returns the PID of a successfully started script, and 0 otherwise. * @returns Returns the PID of a successfully started script, and 0 otherwise.
*/ */
exec(script: string, host: string, numThreads?: number, ...args: Array<string | number | boolean>): number; exec(script: string, host: string, numThreads?: number, ...args: (string | number | boolean)[]): number;
/** /**
* Terminate current script and start another in 10s. * Terminate current script and start another in 10s.
@ -5296,7 +5297,7 @@ export interface NS {
* @param numThreads - Number of threads to spawn new script with. Will be rounded to nearest integer. * @param numThreads - Number of threads to spawn new script with. Will be rounded to nearest integer.
* @param args - Additional arguments to pass into the new script that is being run. * @param args - Additional arguments to pass into the new script that is being run.
*/ */
spawn(script: string, numThreads?: number, ...args: string[]): void; spawn(script: string, numThreads?: number, ...args: (string | number | boolean)[]): void;
/** /**
* Terminate another script. * Terminate another script.
@ -5366,7 +5367,7 @@ export interface NS {
* ns.kill("foo.script", getHostname(), 1, "foodnstuff"); * ns.kill("foo.script", getHostname(), 1, "foodnstuff");
* ``` * ```
*/ */
kill(script: string, host: string, ...args: string[]): boolean; kill(script: string, host: string, ...args: (string | number | boolean)[]): boolean;
/** /**
* Terminate all scripts on a server. * Terminate all scripts on a server.
@ -5863,7 +5864,7 @@ export interface NS {
* @param args - Arguments to specify/identify which scripts to search for. * @param args - Arguments to specify/identify which scripts to search for.
* @returns True if specified script is running on the target server, and false otherwise. * @returns True if specified script is running on the target server, and false otherwise.
*/ */
isRunning(script: FilenameOrPID, host: string, ...args: string[]): boolean; isRunning(script: FilenameOrPID, host?: string, ...args: (string | number | boolean)[]): boolean;
/** /**
* Get general info about a running script. * Get general info about a running script.
@ -5878,7 +5879,11 @@ export interface NS {
* @param args - Arguments to identify the script * @param args - Arguments to identify the script
* @returns The info about the running script if found, and null otherwise. * @returns The info about the running script if found, and null otherwise.
*/ */
getRunningScript(filename?: FilenameOrPID, hostname?: string, ...args: (string | number)[]): RunningScript | null; getRunningScript(
filename?: FilenameOrPID,
hostname?: string,
...args: (string | number | boolean)[]
): RunningScript | null;
/** /**
* Get cost of purchasing a server. * Get cost of purchasing a server.
@ -6204,13 +6209,13 @@ export interface NS {
* Get the execution time of a hack() call. * Get the execution time of a hack() call.
* @remarks * @remarks
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
*When `hack` completes an amount of money is stolen depending on the player's skills. *
* When `hack` completes an amount of money is stolen depending on the player's skills.
* Returns the amount of time in milliseconds it takes to execute the hack Netscript function on the target server. * Returns the amount of time in milliseconds it takes to execute the hack Netscript function on the target server.
* The function takes in an optional hackLvl parameter that can be specified to see what the hack time would be at different hacking levels.
* The required time is increased by the security level of the target server and decreased by the player's hacking level. * The required time is increased by the security level of the target server and decreased by the player's hacking level.
* *
* @param host - Host of target server. * @param host - Host of target server.
* @returns Returns the amount of time in milliseconds it takes to execute the hack Netscript function. Returns Infinity if called on a Hacknet Server. * @returns Returns the amount of time in milliseconds it takes to execute the hack Netscript function.
*/ */
getHackTime(host: string): number; getHackTime(host: string): number;
@ -6220,11 +6225,10 @@ export interface NS {
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
* *
* Returns the amount of time in milliseconds it takes to execute the grow Netscript function on the target server. * Returns the amount of time in milliseconds it takes to execute the grow Netscript function on the target server.
* The function takes in an optional hackLvl parameter that can be specified to see what the grow time would be at different hacking levels.
* The required time is increased by the security level of the target server and decreased by the player's hacking level. * The required time is increased by the security level of the target server and decreased by the player's hacking level.
* *
* @param host - Host of target server. * @param host - Host of target server.
* @returns Returns the amount of time in milliseconds it takes to execute the grow Netscript function. Returns Infinity if called on a Hacknet Server. * @returns Returns the amount of time in milliseconds it takes to execute the grow Netscript function.
*/ */
getGrowTime(host: string): number; getGrowTime(host: string): number;
@ -6234,11 +6238,10 @@ export interface NS {
* RAM cost: 0.05 GB * RAM cost: 0.05 GB
* *
* Returns the amount of time in milliseconds it takes to execute the weaken Netscript function on the target server. * Returns the amount of time in milliseconds it takes to execute the weaken Netscript function on the target server.
* The function takes in an optional hackLvl parameter that can be specified to see what the weaken time would be at different hacking levels.
* The required time is increased by the security level of the target server and decreased by the player's hacking level. * The required time is increased by the security level of the target server and decreased by the player's hacking level.
* *
* @param host - Host of target server. * @param host - Host of target server.
* @returns Returns the amount of time in milliseconds it takes to execute the weaken Netscript function. Returns Infinity if called on a Hacknet Server. * @returns Returns the amount of time in milliseconds it takes to execute the weaken Netscript function.
*/ */
getWeakenTime(host: string): number; getWeakenTime(host: string): number;
@ -6271,7 +6274,7 @@ export interface NS {
/** /**
* {@inheritDoc NS.(getScriptIncome:1)} * {@inheritDoc NS.(getScriptIncome:1)}
*/ */
getScriptIncome(script: string, host: string, ...args: string[]): number; getScriptIncome(script: string, host: string, ...args: (string | number | boolean)[]): number;
/** /**
* Get the exp gain of a script. * Get the exp gain of a script.
@ -6295,7 +6298,7 @@ export interface NS {
/** /**
* {@inheritDoc NS.(getScriptExpGain:1)} * {@inheritDoc NS.(getScriptExpGain:1)}
*/ */
getScriptExpGain(script: string, host: string, ...args: string[]): number; getScriptExpGain(script: string, host: string, ...args: (string | number | boolean)[]): number;
/** /**
* Returns the amount of time in milliseconds that have passed since you last installed Augmentations. * Returns the amount of time in milliseconds that have passed since you last installed Augmentations.

@ -272,6 +272,8 @@ export function SidebarRoot(props: IProps): React.ReactElement {
// Alt-a - Augmentations // Alt-a - Augmentations
// Alt-u - Tutorial // Alt-u - Tutorial
// Alt-o - Options // Alt-o - Options
// Alt-b - Bladeburner
// Alt-g - Gang
function handleShortcuts(this: Document, event: KeyboardEvent): any { function handleShortcuts(this: Document, event: KeyboardEvent): any {
if (Settings.DisableHotkeys) return; if (Settings.DisableHotkeys) return;
if ((props.player.isWorking && props.player.focus) || props.router.page() === Page.BitVerse) return; if ((props.player.isWorking && props.player.focus) || props.router.page() === Page.BitVerse) return;
@ -315,6 +317,9 @@ export function SidebarRoot(props: IProps): React.ReactElement {
} else if (event.code === KEYCODE.U && event.altKey) { } else if (event.code === KEYCODE.U && event.altKey) {
event.preventDefault(); event.preventDefault();
clickTutorial(); clickTutorial();
} else if (event.code === KEYCODE.O && event.altKey) {
event.preventDefault();
clickOptions();
} else if (event.code === KEYCODE.B && event.altKey && props.player.bladeburner) { } else if (event.code === KEYCODE.B && event.altKey && props.player.bladeburner) {
event.preventDefault(); event.preventDefault();
clickBladeburner(); clickBladeburner();

@ -35,7 +35,7 @@ export const TerminalHelpText: string[] = [
" rm [file] Delete a file from the server", " rm [file] Delete a file from the server",
" run [name] [-t n] [--tail] [args...] Execute a program or script", " run [name] [-t n] [--tail] [args...] Execute a program or script",
" scan Prints all immediately-available network connections", " scan Prints all immediately-available network connections",
" scan-analyze [d] [-a] Prints info for all servers up to <i>d</i> nodes away", " scan-analyze [d] [-a] Prints info for all servers up to d nodes away",
" scp [file ...] [server] Copies a file to a destination server", " scp [file ...] [server] Copies a file to a destination server",
" sudov Shows whether you have root access on this computer", " sudov Shows whether you have root access on this computer",
" tail [script] [args...] Displays dynamic logs for the specified script", " tail [script] [args...] Displays dynamic logs for the specified script",

@ -417,7 +417,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
{ {
desc: (arr: number[][]): string => { desc: (arr: number[][]): string => {
return [ return [
"Given the following array of array of numbers representing a list of", "Given the following array of arrays of numbers representing a list of",
"intervals, merge all overlapping intervals.\n\n", "intervals, merge all overlapping intervals.\n\n",
`[${convert2DArrayToString(arr)}]\n\n`, `[${convert2DArrayToString(arr)}]\n\n`,
"Example:\n\n", "Example:\n\n",
@ -1283,17 +1283,16 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
numTries: 10, numTries: 10,
desc: (n: string): string => { desc: (n: string): string => {
return [ return [
"You are given the following encoded binary String: \n", "You are given the following encoded binary string: \n",
`'${n}' \n`, `'${n}' \n\n`,
"Treat it as an extended Hamming code with 1 'possible' error at a random index.\n", "Treat it as an extended Hamming code with 1 'possible' error at a random index.\n",
"Find the 'possible' wrong bit, fix it and extract the decimal value, which is hidden inside the string.\n\n", "Find the 'possible' wrong bit, fix it and extract the decimal value, which is hidden inside the string.\n\n",
"Note: The length of the binary string is dynamic, but it's encoding/decoding follows Hamming's 'rule'\n", "Note: The length of the binary string is dynamic, but it's encoding/decoding follows Hamming's 'rule'\n",
"Note 2: Index 0 is an 'overall' parity bit. Watch the Hamming code video from 3Blue1Brown for more information\n", "Note 2: Index 0 is an 'overall' parity bit. Watch the Hamming code video from 3Blue1Brown for more information\n",
"Note 3: There's a ~55% chance for an altered Bit. So... MAYBE there is an altered Bit 😉\n", "Note 3: There's a ~55% chance for an altered Bit. So... MAYBE there is an altered Bit 😉\n",
"Note: The endianness of the \ "Note: The endianness of the encoded decimal value is reversed in relation to the endianness of the Hamming code. Where",
encoded decimal value is reversed in relation to the endianness of the Hamming code. Where \ "the Hamming code is expressed as little-endian (LSB at index 0), the decimal value encoded in it is expressed as big-endian",
the Hamming code is expressed as little-endian (LSB at index 0), the decimal value encoded in it is expressed as big-endian \ "(MSB at index 0).\n",
(MSB at index 0)\n",
"Extra note for automation: return the decimal value as a string", "Extra note for automation: return the decimal value as a string",
].join(" "); ].join(" ");
}, },
@ -1550,7 +1549,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
return [ return [
"Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to", "Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to",
"earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk", "earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk",
"begins with a length L, encoded as a single ASCII digit from 1 - 9, followed by the chunk data,", "begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data,",
"which is either:\n\n", "which is either:\n\n",
"1. Exactly L characters, which are to be copied directly into the uncompressed data.\n", "1. Exactly L characters, which are to be copied directly into the uncompressed data.\n",
"2. A reference to an earlier part of the uncompressed data. To do this, the length is followed", "2. A reference to an earlier part of the uncompressed data. To do this, the length is followed",
@ -1585,7 +1584,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
return [ return [
"Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to", "Lempel-Ziv (LZ) compression is a data compression technique which encodes data using references to",
"earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk", "earlier parts of the data. In this variant of LZ, data is encoded in two types of chunk. Each chunk",
"begins with a length L, encoded as a single ASCII digit from 1 - 9, followed by the chunk data,", "begins with a length L, encoded as a single ASCII digit from 1 to 9, followed by the chunk data,",
"which is either:\n\n", "which is either:\n\n",
"1. Exactly L characters, which are to be copied directly into the uncompressed data.\n", "1. Exactly L characters, which are to be copied directly into the uncompressed data.\n",
"2. A reference to an earlier part of the uncompressed data. To do this, the length is followed", "2. A reference to an earlier part of the uncompressed data. To do this, the length is followed",

@ -58,11 +58,12 @@ export function InteractiveTutorialRoot(): React.ReactElement {
const [language, setLanguage] = useState(Language.None); const [language, setLanguage] = useState(Language.None);
const classes = useStyles(); const classes = useStyles();
const tutorialScriptName = { const tutorialScriptExtension = {
[Language.None]: "n00dles.script", [Language.None]: ".script",
[Language.NS1]: "n00dles.script", [Language.NS1]: ".script",
[Language.NS2]: "n00dles.js", [Language.NS2]: ".js",
}[language]; }[language];
const tutorialScriptName = `n00dles${tutorialScriptExtension}`;
const contents: { [number: string]: IContent | undefined } = { const contents: { [number: string]: IContent | undefined } = {
[iTutorialSteps.Start as number]: { [iTutorialSteps.Start as number]: {
@ -377,7 +378,9 @@ export function InteractiveTutorialRoot(): React.ReactElement {
</Typography> </Typography>
<Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano"}</Typography> <Typography classes={{ root: classes.textfield }}>{"[home ~/]> nano"}</Typography>
<Typography>Scripts must end with the .script extension. Let's make a script now by entering </Typography> <Typography>
Scripts must end with the {tutorialScriptExtension} extension. Let's make a script now by entering{" "}
</Typography>
<Typography classes={{ root: classes.textfield }}>{`[home ~/]> nano ${tutorialScriptName}`}</Typography> <Typography classes={{ root: classes.textfield }}>{`[home ~/]> nano ${tutorialScriptName}`}</Typography>
<Typography> <Typography>
@ -457,7 +460,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
<> <>
<Typography> <Typography>
Your script is now running! It will continuously run in the background and will automatically stop if the Your script is now running! It will continuously run in the background and will automatically stop if the
code ever completes (the n00dles.script will never complete because it runs an infinite loop). <br /> code ever completes (the {tutorialScriptName} will never complete because it runs an infinite loop). <br />
<br /> <br />
These scripts can passively earn you income and hacking experience. Your scripts will also earn money and These scripts can passively earn you income and hacking experience. Your scripts will also earn money and
experience while you are offline, although at a slightly slower rate. <br /> experience while you are offline, although at a slightly slower rate. <br />