Compare commits

..

124 Commits

Author SHA1 Message Date
Snarling
633da38301 RELEASE: 2.6.2 (#1454) 2024-07-03 19:14:45 -04:00
catloversg
3c29757827 CORPORATION: Fix wrong product price calculation (#1451) 2024-07-02 15:11:32 -07:00
catloversg
960fe5aa8b BUGFIX: Wrong success chance in ns.bladeburner.getActionEstimatedSuccessChance (#1450) 2024-07-02 15:07:56 -07:00
catloversg
922f0bfcc5 BITVERSE: Fix React warning (#1449) 2024-07-01 13:17:30 -07:00
catloversg
e66a8e319f MISC: Remove debug code (#1448) 2024-07-01 13:16:47 -07:00
catloversg
3fafa23f28 GANG: Fix ns.gang.getRecruitsAvailable (#1442) 2024-06-28 20:18:29 -07:00
catloversg
b1c1fc24a9 GRAFTING: Add new api for checking ongoing grafting (#1435) 2024-06-28 19:59:18 -07:00
Tom Prince
32eb6324fd MISC: Simplify graftingIntBonus calculation. (#1445)
The weight of the intelligence bonus is a multiplier to the percentage increase. So, rather than calculate it with a weight of 3 and then divide by 3, we can just calculate it with a weight of 1.
2024-06-28 19:52:36 -07:00
Sphyxis
61ec7dde80 API: Get Sleeve Success at BB tasks through existing commands (#1428) 2024-06-28 19:49:52 -07:00
David Walker
9c9a69f2e2 NETSCRIPT: Add ramOverride() function (#1346)
This adds a way to dynamically change the static RAM limit of a script,
which is also its current RAM usage. This makes it possible for scripts
to dynamically change their memory footprint, opening up new strategies
beyond current ram-dodging.

Calling functions still permanently increases the *dynamic* memory
limit; RAM-dodging is still the optimal strategy for avoiding RAM costs,
in that sense.

This also adds dynamicRamUsage to the info returned by
`getRunningScript`, to allow introspection on the currently needed ram.
2024-06-28 18:42:20 -07:00
David Walker
1c20a24079 MISC: Make spawn able to have 0 delay (#1333)
This eliminates a hole where spawn was unrelaible, because other scripts
could jump in and steal the RAM. It's not an API break, because 0 used
to be an invalid value.
2024-06-28 18:41:41 -07:00
David Walker
06d742a7f3 BUGFIX: Fix rounding issues due to ramOverride edge cases (#1339)
*All* RAM calculations must take place in units of hundredths-of-a-GB in
order for there not to be issues.

Also adds slightly more verbose logging when the dynamic RAM check
fails.
2024-06-28 17:58:17 -07:00
muesli4brekkies
357cc568e9 TERMINAL: Tweaks and bugfixes to grep (#1431) 2024-06-28 15:13:49 -07:00
catloversg
21e984bda6 DOCUMENTATION: Clarify logging API (#1444) 2024-06-28 02:37:04 -07:00
catloversg
f620ec889c MISC: Update BitNode info and documentation (#1436) 2024-06-28 02:11:50 -07:00
catloversg
f162faf60a INFILTRATION: Improve accuracy of slash game UI (#1422) 2024-06-28 02:09:18 -07:00
catloversg
031b8b9cbb UI: Remember last position of documentation pages (#1434) 2024-06-28 02:08:10 -07:00
Snarling
64933419d6 Changelog update 2024-06-27 12:37:03 -04:00
Snarling
b597746343 TRAVEL: Unify implementation for Player and Sleeves (and some followup for #1365) (#1439) 2024-06-26 20:46:50 -04:00
catloversg
abdf5f52cd INFILTRATION: Handle automated infiltration (#1414) 2024-06-26 11:19:52 -04:00
Snarling
dc057d05fc minor package-lock update
Fixes an audit issue on a dev dependency
2024-06-25 19:30:30 -04:00
catloversg
cef789eb7c ELECTRON: Fix error with symbolic link (#1427) 2024-06-25 19:09:22 -04:00
catloversg
fd8eae5cf5 MISC: Cancel spawned scripts in Bitverse (#1429) 2024-06-24 22:20:08 -07:00
ilkecan
bf8c15332e CORPORATION: Don't check access for getConstants (#1430) 2024-06-24 22:18:57 -07:00
John Gietzen
819f877370 Update codingcontracttypes.ts to clarify '0' (#1433)
Fixes #1432
2024-06-24 22:02:11 -07:00
JamesWilcox-git
67cdd57728 BUGFIX: Hacknet level base cost multiplier fixed (#1412) 2024-06-24 21:40:50 -07:00
catloversg
5a8f0e99af SINGULARITY: Add ns.singularity.getFactionWorkTypes (#1425) 2024-06-24 20:43:36 -07:00
catloversg
0d8cc54c99 INFILTRATION: Fix React warnings (#1423) 2024-06-24 20:37:57 -07:00
catloversg
c0036b03d4 SINGULARITY: Allow being hospitalized while being busy (#1426) 2024-06-24 20:36:03 -07:00
catloversg
48bebeea2b MISC: Remove unused properties of source file (#1424) 2024-06-24 20:34:56 -07:00
Michael Ficocelli
49668f10b2 IPVGO: Fix displayed mult to match the actual bonus of SF 14.1 (#1419) 2024-06-24 20:31:09 -07:00
catloversg
847d45f4f4 GANG: Show equipments when there is not enough money (#1417) 2024-06-21 03:14:48 -07:00
catloversg
a62bdcafef BUGFIX: Fix a bug in ns.singularity.getAugmentationFactions (#1418) 2024-06-21 03:06:57 -07:00
catloversg
337fa4e274 GANG: Fix wrong wanted gain rate (#1415) 2024-06-21 03:05:33 -07:00
catloversg
eff834bfe9 BUGFIX: Fix wrong augmentation price (#1416) 2024-06-21 03:01:57 -07:00
David Walker
99b22a221c BUGFIX: Fix issues and edge-cases with rm (#1404) 2024-06-16 18:27:46 -07:00
catloversg
4382f860db CCT: Clarify empty string solution in UI (#1400) 2024-06-15 16:37:14 -07:00
catloversg
7a39a93fa9 MISC: Remove mention of re-sleeving (#1399) 2024-06-15 16:36:32 -07:00
catloversg
ceb58bc6b3 UI: Show BitNode multipliers in BN 5.1 (#1398) 2024-06-15 16:35:32 -07:00
Jesse Clark
f6de21ea18 UI: Clean up Hacknet UI (#1397) 2024-06-14 00:08:10 -07:00
muesli4brekkies
4936d14639 TERMINAL: Add grep command (#1381) 2024-06-14 00:00:48 -07:00
catloversg
a780880531 MISC: Add ns.enums to AutocompleteData (#1389) 2024-06-13 23:56:27 -07:00
Albert Llop
e9347fca76 DOCS: Fix example signature in IPvGO guide (#1396) 2024-06-13 23:55:46 -07:00
catloversg
417d420793 BLADEBURNER: Fix NaN in getSuccessRange (#1394) 2024-06-13 23:48:50 -07:00
catloversg
a12056a898 BLADEBURNER: Add remaining time for actions (#1391) 2024-06-12 19:21:23 -07:00
G4mingJon4s
bec6e82d7f EDITOR: changed editor tabs to have their own editor mode (#1372) 2024-06-12 19:19:40 -07:00
G4mingJon4s
805ca06922 TERMINAL: Added deleting entire directories using rm (#1378) 2024-06-12 19:17:39 -07:00
catloversg
344054f10d DOCUMENTATION: Clarify maximum number of ports (#1388) 2024-06-12 19:07:05 -07:00
catloversg
39b18e7659 BUGFIX: Fix wrong position name in ns.formulas.work.companyGains (#1393) 2024-06-12 14:34:13 -07:00
catloversg
2198a02152 MISC: Fix support of react-refresh-webpack-plugin (#1392) 2024-06-12 14:31:13 -07:00
catloversg
e76e254c3e BLADEBURNER: Fix wrong tooltip description in ActionLevel (#1384) 2024-06-12 01:24:57 -07:00
G4mingJon4s
ab80ee66c8 EDITOR: re-added vim notifications and register view (#1382) 2024-06-12 01:24:10 -07:00
G4mingJon4s
f25756916a GANG: added effective text to gang UI gains (#1379) 2024-06-12 01:21:27 -07:00
muesli4brekkies
7b3265346d ACCESSIBILITY: Wire cutting infil now prints colours of wire along the wire (#1380) 2024-06-09 16:49:34 -07:00
catloversg
9a2bb16548 MISC: Fix wrong help text of cd command (#1376) 2024-06-09 16:39:56 -07:00
Albert Llop
ba7d45362f bladeburner.getTeamSize: fix signature and documentation (#1383) 2024-06-09 16:39:01 -07:00
Albert Llop
995294a770 BUGFIX: bladeburner.getActionRepGain: mark level param as optional in signature (#1375) 2024-06-09 16:34:53 -07:00
catloversg
a0fc9cc713 UI: Automatically show Bitverse UI if BN is finished (#1358) 2024-06-08 13:54:44 -07:00
catloversg
a354867fc4 MISC: Refactor code of traveling and going to location (#1365) 2024-06-08 13:52:10 -07:00
catloversg
b8f03cb50b BLADEBURNER: Remove unused code (#1368) 2024-06-08 13:51:05 -07:00
catloversg
abcd6c545a BLADEBURNER: Remove obsolete fix (#1367) 2024-06-07 18:04:08 -07:00
catloversg
ebf08d5d1f BLADEBURNER: Improve terminal logging (#1361) 2024-06-07 13:41:42 -07:00
catloversg
8b3206e1c6 BLADEBURNER: Add Stop button and refactor code (#1363) 2024-06-07 13:34:47 -07:00
catloversg
d9efea0fe6 SLEEVES: Clarify augmentation condition (#1369) 2024-06-07 13:24:19 -07:00
catloversg
70383d9085 MISC: Change error message when spawning on an invalid server (#1370) 2024-06-07 13:23:04 -07:00
catloversg
16b9ad21ea MISC: Set copyright line for Electron app (#1371) 2024-06-07 13:21:26 -07:00
catloversg
e782b6fd7c DOCUMENTATION: Split changelog.md (#1374) 2024-06-07 13:20:18 -07:00
catloversg
e64247571d IPVGO: Add missing space characters in API documentation (#1373) 2024-06-07 13:15:46 -07:00
catloversg
7b3cf48453 CORPORATION: Refactor markup multiplier (#1362) 2024-06-06 23:10:16 -07:00
Michael Ficocelli
481938a2fb IPVGO: Balance and improvements for offline bonus time cycles (#1356) 2024-06-05 19:39:22 -07:00
G4mingJon4s
463d4cdb1d EDITOR: useVimEditor uses Material UI (#1332) 2024-06-05 18:30:03 -07:00
Michael Ficocelli
cf48d666f5 IPVGO: Add history, and details to status, to go API (#1348) 2024-06-05 18:24:48 -07:00
catloversg
30a6419b11 MISC: Use camel case for CSS properties (#1353) 2024-06-05 18:18:19 -07:00
catloversg
304a918cc9 BUGFIX: Fix negative elapsed time (#1354) 2024-06-05 18:11:59 -07:00
Michael Ficocelli
eeab6df718 IPVGO: Use tss-react makeStyles on the IPvGO board and UI (#1351) 2024-06-05 21:09:16 -04:00
Michael Ficocelli
fe3e8fb348 IPVGO: Fix crash caused by malformed previous move formatting (#1360) 2024-06-05 17:51:01 -07:00
Michael Ficocelli
d9e8161a64 IPVGO: Fix mismatch in board size options on save loader [save corruption bugfix] (#1355) 2024-06-05 18:27:27 -04:00
Kelenius
653d531d0a Remove sleeve message when quitting job from a script (#1357) 2024-06-05 18:25:54 -04:00
Michael Ficocelli
bd5c502f53 IPVGO: Support bonus cycles from offline time (#1345) 2024-06-04 18:43:29 -07:00
Michael Ficocelli
5f6a5c8785 IPVGO: Fix non-async promise-returning methods to be more consistent (#1327) 2024-06-04 18:37:00 -07:00
catloversg
7321d64383 BUGFIX: Fix manual hack exploit (#1324)
Formula is updated to be still relevant, but not crushing
2024-06-04 18:32:24 -07:00
Caldwell
2316bf5b69 BUGFIX: clamp bitnode mults (#1350) 2024-06-04 18:20:43 -07:00
Caldwell
c42d4143c9 BUGFIX: clamp Hackchance to prevent infinity / infinity (#1349) 2024-06-04 18:19:41 -07:00
Snarling
18ae6ce215 Changelog update 2024-06-04 10:21:44 -04:00
TheAimMan
101914b660 MISC: Add a potential of more than 1 core for initial servers (#963) 2024-06-04 09:50:28 -04:00
Snarling
3afafe4454 SLEEVE: Editorial followup on #1314 (#1347) 2024-06-04 09:49:23 -04:00
Caldwell
cb92643c7e IMPROVEMENT: partial migration @mui/styles to tss-react (#1338) 2024-06-03 12:27:13 -04:00
TheAimMan
e622b9b904 UI: change time elapsed to time remaining for programs and grafting (#1328) 2024-06-02 20:51:21 -07:00
Michael Ficocelli
d9f04203cf IPVGO: Record full history to avoid infinite ko capture loops on larger boards (#1299) 2024-06-02 20:19:26 -07:00
catloversg
2f7950b49c DOCUMENTATION: Clarify server's minimum security level (#1337) 2024-06-02 20:14:18 -07:00
gmcew
1f08724fea CORP: Products sell all at price of 0 (#1330)
Match material behaviour - price of 0 discards stored products
2024-06-02 18:01:03 -07:00
Michael Ficocelli
a28bb4bd99 IPVGO: Support playing manually as white against your scripts using the No AI type board (#1296) 2024-06-02 17:41:31 -07:00
Caldwell
f40d4f8e92 BUGFIX: fix relative imports (#1305)
Relative paths work in imports, at last.
2024-06-02 17:38:01 -07:00
Snarling
76ce2f9955 Hotfix changelog 2024-06-02 12:09:14 -04:00
catloversg
2ee548a140 BUGFIX: Fix corrupted save in Steam cloud (#1341) 2024-06-02 12:04:42 -04:00
Yichi Zhang
54d099e552 TERMINAL: Fix autocomplete for mixed case strings (#1323) 2024-05-29 11:34:46 -07:00
catloversg
bd6585617c UI: Reverse order of Black Operations list (#1322) 2024-05-29 01:31:44 -07:00
catloversg
f439352438 BUGFIX: Fix wrong HP after calling applyEntropy (#1313) 2024-05-28 12:15:12 -07:00
catloversg
c2a56a6150 MISC: Refactor favor code (#1321) 2024-05-28 12:04:16 -07:00
catloversg
b8d3109158 MISC: Change type of location parameter/property (#1316) 2024-05-28 11:49:48 -07:00
Sphyxis
bed66f980f BUGFIX: BB Sleeves cannot be assigned to contract programmatically (#1314) 2024-05-27 18:32:21 -07:00
Yichi Zhang
e674a177d6 BUGFIX: Check that the augmentation is available before installing it on a sleeve. (#1320) 2024-05-27 15:34:19 -07:00
catloversg
53f187fb89 MISC: Fix wrong text in Tutorial and NetscriptDefinitions.d.ts (#1319) 2024-05-27 15:30:32 -07:00
catloversg
ee4471e22c INFILTRATION: Change description of slash game (#1317) 2024-05-27 15:29:10 -07:00
Sphyxis
4b5e0b1f6a BUGFIX: Update GraftingWork.tsx (#1315)
Removed the purposeful structural typo as it was causing bug reports.
2024-05-27 15:22:31 -07:00
Kelenius
bf5c43daa2 UI: Add a stop button to bladeburner (#1312) 2024-05-27 15:02:56 -07:00
gmcew
30cdaa1a7a DOCS: Faction work description update (#1310)
- made it clear security gives hacking exp, and utilises it too
- clarified weighting of stats for hacking/field work.
2024-05-27 14:52:05 -07:00
catloversg
48a7eb364f DOCUMENTATION: Fix wrong examples in NetscriptDefinitions.d.ts (#1309) 2024-05-27 14:42:31 -07:00
catloversg
70521c9156 BUGFIX: Fix hidden debt of exp at level 1 (#1292) 2024-05-27 14:20:41 -07:00
Michael Ficocelli
efd4152eed IPVGO: Fix outdated docs (#1306) 2024-05-23 20:07:45 -04:00
Jesse Clark
23445a917d Give the player starting money and programs as declared in augmentation definitions (#1304) 2024-05-23 01:55:06 -07:00
catloversg
8703da4ab6 BUGFIX: Save position of cursor when switching tabs and unmounting editor (#1297) 2024-05-23 01:51:33 -07:00
gmcew
7f8757b536 SLEEVE: Add funds check for sleeve travel (#1298)
This makes it consistent with player behaviour. This also makes the function description more truthful in that it is now possible to return false.
2024-05-23 01:46:55 -07:00
catloversg
b42f775493 BUGFIX: Fix wrong money gain rate of Hacknet node (#1303) 2024-05-23 01:45:04 -07:00
catloversg
30c04f8152 DOCUMENTATION: Fix wrong examples in NetscriptDefinitions.d.ts (#1295) 2024-05-23 01:42:47 -07:00
catloversg
81a707123e BUGFIX: Engine does not send all faction invitations (#1277) 2024-05-23 01:40:23 -07:00
catloversg
cfa941ce58 DOCUMENTATION: Clarify multipliers of Hacknet server (#1291) 2024-05-23 00:49:06 -07:00
gmcew
c82b2e15a0 MISC: Additional clarity in 'HammingCodes: Encoded Binary to Integer' (#1289)
Should have done this in PR #1244
2024-05-23 00:45:25 -07:00
gmcew
fe14d4fef3 CCT: Minor simplification of 'Shortest Path' solver (#1288)
BFS shouldn't need some checks.
Also allows deletion of a helper file used by this function only.
2024-05-23 00:44:41 -07:00
G4mingJon4s
08eb60d21b EDITOR: Improved infinite loop checking (#1276) 2024-05-23 00:44:15 -07:00
Yichi Zhang
7ed64cbc9c Improve performance of checker for valid math contracts (#1286) 2024-05-23 00:41:52 -07:00
catloversg
819e9f3448 MISC: Embed JetBrainsMono font (#1246) 2024-05-22 21:50:09 -04:00
catloversg
7bb36ec111 MISC: Refactor InvitationModal and AlertManager (#1287) 2024-05-22 21:43:31 -04:00
Snarling
fe7e1c86bc Start 2.6.2 cycle (#1301) 2024-05-22 01:35:35 -04:00
291 changed files with 7826 additions and 5666 deletions

93
dist/OFL.txt vendored Normal file
View File

@@ -0,0 +1,93 @@
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

30
electron/fileError.html Normal file
View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Bitburner</title>
<style>
body {
background-color: black;
color: #0c0;
margin: 0;
}
div {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
h1 {
text-align: center;
}
</style>
</head>
<body>
<div>
<h1>Attempts to access local files outside the normal game environment will be directed to this file.</h1>
</div>
</body>
</html>

View File

@@ -1 +0,0 @@
Attempts to access local files outside the normal game environment will be directed to this file.

View File

@@ -29,6 +29,7 @@ const debounce = require("lodash/debounce");
const Store = require("electron-store");
const store = new Store();
const path = require("path");
const { realpathSync } = require("fs");
const { fileURLToPath } = require("url");
log.transports.file.level = store.get("file-log-level", "info");
@@ -201,13 +202,18 @@ app.on("ready", async () => {
// Intercept file protocol requests and only let valid requests through
protocol.interceptFileProtocol("file", ({ url, method }, callback) => {
const filePath = fileURLToPath(url);
const relativePath = path.relative(__dirname, filePath);
//only provide html files in same directory, or anything in dist
if ((method === "GET" && relativePath.startsWith("dist")) || relativePath.match(/^[a-zA-Z-_]*\.html/)) {
return callback(filePath);
const realPath = realpathSync(filePath);
const relativePath = path.relative(__dirname, realPath);
// Only allow access to files in "dist" folder or html files in the same directory
if (method === "GET" && (relativePath.startsWith("dist") || relativePath.match(/^[a-zA-Z-_]*\.html/))) {
callback(realPath);
return;
}
log.error(`Tried to access a page outside the sandbox. Url: ${url}. Method: ${method}.`);
callback(path.join(__dirname, "fileError.txt"));
log.error(
`Tried to access a page outside the sandbox. Url: ${url}. FilePath: ${filePath}. RealPath: ${realPath}.` +
` __dirname: ${__dirname}. RelativePath: ${relativePath}. Method: ${method}.`,
);
callback(path.join(__dirname, "fileError.html"));
});
log.info("Application is ready!");

View File

@@ -1,12 +1,12 @@
{
"name": "bitburner",
"version": "2.6.1",
"version": "2.6.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bitburner",
"version": "2.6.1",
"version": "2.6.2",
"dependencies": {
"electron-log": "^4.4.8",
"electron-store": "^8.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "bitburner",
"version": "2.6.1",
"version": "2.6.2",
"description": "A cyberpunk-themed programming incremental game",
"main": "main.js",
"author": "Daniel Xie, hydroflame, et al.",

View File

@@ -192,9 +192,9 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
*
* Instead of implementing it, the old code (encoding in base64) is used here for backward compatibility.
*/
const content = saveData.toString("base64");
log.debug(`Uncompressed: ${saveData.length} bytes`);
log.debug(`Compressed: ${content.length} bytes`);
const content = Buffer.from(saveData).toString("base64");
log.debug(`saveData: ${saveData.length} bytes`);
log.debug(`Base64 string of saveData: ${content.length} bytes`);
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
try {

View File

@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [AutocompleteData](./bitburner.autocompletedata.md) &gt; [enums](./bitburner.autocompletedata.enums.md)
## AutocompleteData.enums property
**Signature:**
```typescript
enums: NSEnums;
```

View File

@@ -16,6 +16,7 @@ interface AutocompleteData
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [enums](./bitburner.autocompletedata.enums.md) | | [NSEnums](./bitburner.nsenums.md) | |
| [scripts](./bitburner.autocompletedata.scripts.md) | | string\[\] | |
| [servers](./bitburner.autocompletedata.servers.md) | | string\[\] | |
| [txts](./bitburner.autocompletedata.txts.md) | | string\[\] | |

View File

@@ -9,7 +9,7 @@ Get estimate success chance of an action.
**Signature:**
```typescript
getActionEstimatedSuccessChance(type: string, name: string): [number, number];
getActionEstimatedSuccessChance(type: string, name: string, sleeveNumber?: number): [number, number];
```
## Parameters
@@ -18,6 +18,7 @@ getActionEstimatedSuccessChance(type: string, name: string): [number, number];
| --- | --- | --- |
| type | string | Type of action. |
| name | string | Name of action. Must be an exact match. |
| sleeveNumber | number | _(Optional)_ Optional. Index of the sleeve to retrieve information. |
**Returns:**

View File

@@ -9,7 +9,7 @@ Get the reputation gain of an action.
**Signature:**
```typescript
getActionRepGain(type: string, name: string, level: number): number;
getActionRepGain(type: string, name: string, level?: number): number;
```
## Parameters
@@ -18,7 +18,7 @@ getActionRepGain(type: string, name: string, level: number): number;
| --- | --- | --- |
| type | string | Type of action. |
| name | string | Name of action. Must be an exact match. |
| level | number | Optional number. Action level at which to calculate the gain. Will be the action's current level if not given. |
| level | number | _(Optional)_ Optional number. Action level at which to calculate the gain. Will be the action's current level if not given. |
**Returns:**

View File

@@ -9,15 +9,15 @@ Get team size.
**Signature:**
```typescript
getTeamSize(type: string, name: string): number;
getTeamSize(type?: string, name?: string): number;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| type | string | Type of action. |
| name | string | Name of action. Must be an exact match. |
| type | string | _(Optional)_ Type of action. |
| name | string | _(Optional)_ Name of action. Must be an exact match. |
**Returns:**
@@ -29,7 +29,7 @@ Number of Bladeburner team members that were assigned to the specified action.
RAM cost: 4 GB
Returns the number of Bladeburner team members you have assigned to the specified action.
Returns the number of available Bladeburner team members. You can also pass the type and name of an action to get the number of Bladeburner team members you have assigned to the specified action.
Setting a team is only applicable for Operations and BlackOps. This function will return 0 for other action types.

View File

@@ -24,7 +24,7 @@ You have to be employed in the Bladeburner division and be in BitNode-7 or have
| [getActionCountRemaining(type, name)](./bitburner.bladeburner.getactioncountremaining.md) | Get action count remaining. |
| [getActionCurrentLevel(type, name)](./bitburner.bladeburner.getactioncurrentlevel.md) | Get the current level of an action. |
| [getActionCurrentTime()](./bitburner.bladeburner.getactioncurrenttime.md) | Get the time elapsed on current action. |
| [getActionEstimatedSuccessChance(type, name)](./bitburner.bladeburner.getactionestimatedsuccesschance.md) | Get estimate success chance of an action. |
| [getActionEstimatedSuccessChance(type, name, sleeveNumber)](./bitburner.bladeburner.getactionestimatedsuccesschance.md) | Get estimate success chance of an action. |
| [getActionMaxLevel(type, name)](./bitburner.bladeburner.getactionmaxlevel.md) | Get the maximum level of an action. |
| [getActionRepGain(type, name, level)](./bitburner.bladeburner.getactionrepgain.md) | Get the reputation gain of an action. |
| [getActionSuccesses(type, name)](./bitburner.bladeburner.getactionsuccesses.md) | Get action successes. |

View File

@@ -36,9 +36,11 @@ Attempts to solve the Coding Contract with the provided solution.
```js
const reward = codingcontract.attempt(yourSolution, filename, hostname);
const reward = ns.codingcontract.attempt(yourSolution, filename, hostname);
if (reward) {
ns.tprint(`Contract solved successfully! Reward: ${reward}`)
} else ns.tprint("Failed to solve contract.")
ns.tprint(`Contract solved successfully! Reward: ${reward}`);
} else {
ns.tprint("Failed to solve contract.");
}
```

View File

@@ -33,7 +33,7 @@ The amount of real time spent asleep between updates can vary due to "bonus time
```js
while (true) {
const prevState = await ns.corporation.nextUpdate();
const nextState = ns.corporation.getCorporation().state;
const nextState = ns.corporation.getCorporation().nextState;
ns.print(`Corporation finished with ${prevState}, next will be ${nextState}.`);
// Manage the Corporation
}

View File

@@ -14,9 +14,11 @@ getGameState(): {
whiteScore: number;
blackScore: number;
previousMove: [number, number] | null;
komi: number;
bonusCycles: number;
};
```
**Returns:**
{ currentPlayer: "White" \| "Black" \| "None"; whiteScore: number; blackScore: number; previousMove: \[number, number\] \| null; }
{ currentPlayer: "White" \| "Black" \| "None"; whiteScore: number; blackScore: number; previousMove: \[number, number\] \| null; komi: number; bonusCycles: number; }

View File

@@ -0,0 +1,21 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Go](./bitburner.go.md) &gt; [getMoveHistory](./bitburner.go.getmovehistory.md)
## Go.getMoveHistory() method
Returns all the prior moves in the current game, as an array of simple board states.
For example, a single 5x5 prior move board might look like this:
\[<br/> "XX.O.",<br/> "X..OO",<br/> ".XO..",<br/> "XXO.\#",<br/> ".XO.\#",<br/> \]
**Signature:**
```typescript
getMoveHistory(): string[][];
```
**Returns:**
string\[\]\[\]

View File

@@ -30,7 +30,7 @@ makeMove(
Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;
a promise that contains if your move was valid and successful, the opponent move's x and y coordinates (or pass) in response, or an indication if the game has ended
a promise that contains the opponent move's x and y coordinates (or pass) in response, or an indication if the game has ended
## Remarks

View File

@@ -26,9 +26,10 @@ export interface Go
| [getBoardState()](./bitburner.go.getboardstate.md) | <p>Retrieves a simplified version of the board state. "X" represents black pieces, "O" white, and "." empty points. "\#" are dead nodes that are not part of the subnet. (They are not territory nor open nodes.)</p><p>For example, a 5x5 board might look like this:</p><p>\[<br/> "XX.O.",<br/> "X..OO",<br/> ".XO..",<br/> "XXO.\#",<br/> ".XO.\#",<br/> \]</p><p>Each string represents a vertical column on the board, and each character in the string represents a point.</p><p>Traditional notation for Go is e.g. "B,1" referring to second ("B") column, first rank. This is the equivalent of index \[1\]\[0\].</p><p>Note that the \[0\]\[0\] point is shown on the bottom-left on the visual board (as is traditional), and each string represents a vertical column on the board. In other words, the printed example above can be understood to be rotated 90 degrees clockwise compared to the board UI as shown in the IPvGO subnet tab.</p> |
| [getCurrentPlayer()](./bitburner.go.getcurrentplayer.md) | Returns the color of the current player, or 'None' if the game is over. |
| [getGameState()](./bitburner.go.getgamestate.md) | Gets the status of the current game. Shows the current player, current score, and the previous move coordinates. Previous move coordinates will be \[-1, -1\] for a pass, or if there are no prior moves. |
| [getMoveHistory()](./bitburner.go.getmovehistory.md) | <p>Returns all the prior moves in the current game, as an array of simple board states.</p><p>For example, a single 5x5 prior move board might look like this:</p><p>\[<br/> "XX.O.",<br/> "X..OO",<br/> ".XO..",<br/> "XXO.\#",<br/> ".XO.\#",<br/> \]</p> |
| [getOpponent()](./bitburner.go.getopponent.md) | Returns the name of the opponent faction in the current subnet. |
| [makeMove(x, y)](./bitburner.go.makemove.md) | Make a move on the IPvGO subnet gameboard, and await the opponent's response. x:0 y:0 represents the bottom-left corner of the board in the UI. |
| [opponentNextTurn(logOpponentMove)](./bitburner.go.opponentnextturn.md) | Returns a promise that resolves with the success or failure state of your last move, and the AI's response, if applicable. x:0 y:0 represents the bottom-left corner of the board in the UI. |
| [passTurn()](./bitburner.go.passturn.md) | <p>Pass the player's turn rather than making a move, and await the opponent's response. This ends the game if the opponent passed on the previous turn, or if the opponent passes on their following turn.</p><p>This can also be used if you pick up the game in a state where the opponent needs to play next. For example: if BitBurner was closed while waiting for the opponent to make a move, you may need to call passTurn() to get them to play their move on game start.</p> |
| [resetBoardState(opponent, boardSize)](./bitburner.go.resetboardstate.md) | <p>Gets new IPvGO subnet with the specified size owned by the listed faction, ready for the player to make a move. This will reset your win streak if the current game is not complete and you have already made moves.</p><p>Note that some factions will have a few routers on the subnet at this state.</p><p>opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????",</p> |
| [resetBoardState(opponent, boardSize)](./bitburner.go.resetboardstate.md) | <p>Gets new IPvGO subnet with the specified size owned by the listed faction, ready for the player to make a move. This will reset your win streak if the current game is not complete and you have already made moves.</p><p>Note that some factions will have a few routers on the subnet at this state.</p><p>opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????" or "No AI",</p> |

View File

@@ -21,7 +21,7 @@ passTurn(): Promise<{
Promise&lt;{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }&gt;
a promise that contains if your move was valid and successful, the opponent move's x and y coordinates (or pass) in response, or an indication if the game has ended
a promise that contains the opponent move's x and y coordinates (or pass) in response, or an indication if the game has ended
## Remarks

View File

@@ -8,7 +8,7 @@ Gets new IPvGO subnet with the specified size owned by the listed faction, ready
Note that some factions will have a few routers on the subnet at this state.
opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????",
opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????" or "No AI",
**Signature:**

View File

@@ -4,7 +4,7 @@
## Grafting.getAugmentationGraftTime() method
Retrieves the time required to graft an aug.
Retrieves the time required to graft an aug. Do not use this value to determine when the ongoing grafting finishes. The ongoing grafting is affected by current intelligence level and focus bonus. You should use [waitForOngoingGrafting](./bitburner.grafting.waitforongoinggrafting.md) for that purpose.
**Signature:**
@@ -22,7 +22,7 @@ getAugmentationGraftTime(augName: string): number;
number
The time required, in millis, to graft the named augmentation.
The time required, in milliseconds, to graft the named augmentation.
## Exceptions

View File

@@ -4,7 +4,7 @@
## Grafting.graftAugmentation() method
Begins grafting the named aug. You must be in New Tokyo to use this.
Begins grafting the named aug. You must be in New Tokyo to use this. When you call this API, the current work (grafting or other actions) will be canceled.
**Signature:**

View File

@@ -21,7 +21,8 @@ This API requires Source-File 10 to use.
| Method | Description |
| --- | --- |
| [getAugmentationGraftPrice(augName)](./bitburner.grafting.getaugmentationgraftprice.md) | Retrieve the grafting cost of an aug. |
| [getAugmentationGraftTime(augName)](./bitburner.grafting.getaugmentationgrafttime.md) | Retrieves the time required to graft an aug. |
| [getAugmentationGraftTime(augName)](./bitburner.grafting.getaugmentationgrafttime.md) | Retrieves the time required to graft an aug. Do not use this value to determine when the ongoing grafting finishes. The ongoing grafting is affected by current intelligence level and focus bonus. You should use [waitForOngoingGrafting](./bitburner.grafting.waitforongoinggrafting.md) for that purpose. |
| [getGraftableAugmentations()](./bitburner.grafting.getgraftableaugmentations.md) | Retrieves a list of Augmentations that can be grafted. |
| [graftAugmentation(augName, focus)](./bitburner.grafting.graftaugmentation.md) | Begins grafting the named aug. You must be in New Tokyo to use this. |
| [graftAugmentation(augName, focus)](./bitburner.grafting.graftaugmentation.md) | Begins grafting the named aug. You must be in New Tokyo to use this. When you call this API, the current work (grafting or other actions) will be canceled. |
| [waitForOngoingGrafting()](./bitburner.grafting.waitforongoinggrafting.md) | Wait until the ongoing grafting finishes or is canceled. |

View File

@@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Grafting](./bitburner.grafting.md) &gt; [waitForOngoingGrafting](./bitburner.grafting.waitforongoinggrafting.md)
## Grafting.waitForOngoingGrafting() method
Wait until the ongoing grafting finishes or is canceled.
**Signature:**
```typescript
waitForOngoingGrafting(): Promise<void>;
```
**Returns:**
Promise&lt;void&gt;
A promise that resolves when the current grafting finishes or is canceled. If there is no current work, the promise resolves immediately. If the current work is not a grafting work, the promise rejects immediately.
## Remarks
RAM cost: 1 GB

View File

@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [GraftingTask](./bitburner.graftingtask.md) &gt; [completion](./bitburner.graftingtask.completion.md)
## GraftingTask.completion property
**Signature:**
```typescript
completion: Promise<void>;
```

View File

@@ -21,6 +21,7 @@ An object representing the current grafting status
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [augmentation](./bitburner.graftingtask.augmentation.md) | | string | |
| [completion](./bitburner.graftingtask.completion.md) | | Promise&lt;void&gt; | |
| [cyclesWorked](./bitburner.graftingtask.cyclesworked.md) | | number | |
| [type](./bitburner.graftingtask.type.md) | | "GRAFTING" | |

View File

@@ -9,14 +9,14 @@ Get all infiltrations with difficulty, location and rewards.
**Signature:**
```typescript
getInfiltration(location: string): InfiltrationLocation;
getInfiltration(location: LocationName | `${LocationName}`): InfiltrationLocation;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| location | string | |
| location | [LocationName](./bitburner.locationname.md) \| \`${[LocationName](./bitburner.locationname.md)<!-- -->}\` | |
**Returns:**

View File

@@ -4,7 +4,7 @@
## NS.disableLog() method
Disables logging for the given function.
Disables logging for the given NS function.
**Signature:**
@@ -16,7 +16,7 @@ disableLog(fn: string): void;
| Parameter | Type | Description |
| --- | --- | --- |
| fn | string | Name of function for which to disable logging. |
| fn | string | Name of the NS function for which to disable logging. |
**Returns:**
@@ -30,3 +30,11 @@ Logging can be disabled for all functions by passing `ALL` as the argument.
For specific interfaces, use the form "namespace.functionName". (e.g. "ui.setTheme")
## Example
```js
ns.disableLog("hack"); // Disable logging for `ns.hack()`
```

View File

@@ -4,7 +4,7 @@
## NS.enableLog() method
Enable logging for a certain function.
Enables logging for the given NS function.
**Signature:**
@@ -16,7 +16,7 @@ enableLog(fn: string): void;
| Parameter | Type | Description |
| --- | --- | --- |
| fn | string | Name of function for which to enable logging. |
| fn | string | Name of the NS function for which to enable logging. |
**Returns:**
@@ -26,5 +26,13 @@ void
RAM cost: 0 GB
Re-enables logging for the given function. If `ALL` is passed into this function as an argument, then it will revert the effects of disableLog(`ALL`<!-- -->).
Re-enables logging for the given function. If `ALL` is passed into this function as an argument, it will revert the effect of disableLog("ALL").
## Example
```js
ns.enableLog("hack"); // Enable logging for `ns.hack()`
```

View File

@@ -49,19 +49,19 @@ export async function main(ns) {
ns.tprint(data);
}
// [home ~/]> run example.js
// [home /]> run example.js
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":false,"v":false}
// [home ~/]> run example.js --delay 3000
// [home /]> run example.js --delay 3000
// {"_":[],"delay":3000,"server":"foodnstuff","exclude":[],"help":false,"v":false}
// [home ~/]> run example.js --delay 3000 --server harakiri-sushi
// [home /]> run example.js --delay 3000 --server harakiri-sushi
// {"_":[],"delay":3000,"server":"harakiri-sushi","exclude":[],"help":false,"v":false}
// [home ~/]> run example.js --delay 3000 --server harakiri-sushi hello world
// [home /]> run example.js --delay 3000 --server harakiri-sushi hello world
// {"_":["hello","world"],"delay":3000,"server":"harakiri-sushi","exclude":[],"help":false,"v":false}
// [home ~/]> run example.js --delay 3000 --server harakiri-sushi hello world --exclude a --exclude b
// [home /]> run example.js --delay 3000 --server harakiri-sushi hello world --exclude a --exclude b
// {"_":["hello","world"],"delay":3000,"server":"harakiri-sushi","exclude":["a","b"],"help":false,"v":false}
// [home ~/]> run example.js --help
// [home /]> run example.js --help
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":true,"v":false}
// [home ~/]> run example.js -v
// [home /]> run example.js -v
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":false,"v":true}
```

View File

@@ -28,7 +28,7 @@ Returns an object containing the Players hacking related multipliers. These m
```js
const mults = ns.getHackingMultipliers();
print(`chance: ${mults.chance}`);
print(`growth: ${mults.growth}`);
ns.tprint(`chance: ${mults.chance}`);
ns.tprint(`growth: ${mults.growth}`);
```

View File

@@ -36,6 +36,6 @@ Returns the cost to purchase a server with the specified amount of ram.
```js
const ram = 2 ** 20;
const cost = ns.getPurchasedServerCost(ram);
ns.tprint(`A purchased server with ${ns.formatRam(ram)} costs ${ns.formatMoney(cost)}`);
ns.tprint(`A purchased server with ${ns.formatRam(ram)} costs $${ns.formatNumber(cost)}`);
```

View File

@@ -26,10 +26,10 @@ The most recently killed script is the first element in the array. Note that the
## Example
```ts
```js
let recentScripts = ns.getRecentScripts();
let mostRecent = recentScripts.shift()
let mostRecent = recentScripts.shift();
if (mostRecent)
ns.tprint(mostRecent.logs.join('\n'))
ns.tprint(mostRecent.logs.join('\n'));
```

View File

@@ -26,6 +26,6 @@ RAM cost: 1 GB
const resetInfo = ns.getResetInfo();
const lastAugReset = resetInfo.lastAugReset;
ns.tprint(`The last augmentation reset was: ${new Date(lastAugReset)}`);
ns.tprint(`It has been ${Date.now() - lastAugReset}ms since the last augmentation reset.`);
ns.tprint(`It has been ${Date.now() - lastAugReset} ms since the last augmentation reset.`);
```

View File

@@ -54,6 +54,6 @@ The grow() command requires root access to the target server, but there is no re
```js
let currentMoney = ns.getServerMoneyAvailable("n00dles");
currentMoney *= await ns.grow("foodnstuff");
currentMoney *= await ns.grow("n00dles");
```

View File

@@ -34,12 +34,12 @@ This function returns the decimal number of script threads you need when running
## Example
```ts
```js
// Calculate threadcount of a single hack that would take $100k from n00dles
const hackThreads = hackAnalyzeThreads("n00dles", 1e5);
const hackThreads = ns.hackAnalyzeThreads("n00dles", 1e5);
// Launching a script requires an integer thread count. The below would take less than the targeted $100k.
ns.run("noodleHack.js", Math.floor(hackThreads))
ns.run("noodleHack.js", Math.floor(hackThreads));
```

View File

@@ -4,7 +4,7 @@
## NS.isLogEnabled() method
Checks the status of the logging for the given function.
Checks the status of the logging for the given NS function.
**Signature:**
@@ -22,9 +22,17 @@ isLogEnabled(fn: string): boolean;
boolean
Returns a boolean indicating whether or not logging is enabled for that function (or `ALL`<!-- -->).
Returns a boolean indicating whether or not logging is enabled for that NS function (or `ALL`<!-- -->).
## Remarks
RAM cost: 0 GB
## Example
```js
ns.print(ns.isLogEnabled("hack")); // Check if logging is enabled for `ns.hack()`
```

View File

@@ -63,8 +63,8 @@ export async function main(ns) {
| [clearPort(portNumber)](./bitburner.ns.clearport.md) | Clear data from a port. |
| [closeTail(pid)](./bitburner.ns.closetail.md) | Close the tail window of a script. |
| [deleteServer(host)](./bitburner.ns.deleteserver.md) | Delete a purchased server. |
| [disableLog(fn)](./bitburner.ns.disablelog.md) | Disables logging for the given function. |
| [enableLog(fn)](./bitburner.ns.enablelog.md) | Enable logging for a certain function. |
| [disableLog(fn)](./bitburner.ns.disablelog.md) | Disables logging for the given NS function. |
| [enableLog(fn)](./bitburner.ns.enablelog.md) | Enables logging for the given NS function. |
| [exec(script, hostname, threadOrOptions, args)](./bitburner.ns.exec.md) | Start another script on any server. |
| [exit()](./bitburner.ns.exit.md) | Terminates the current script immediately. |
| [fileExists(filename, host)](./bitburner.ns.fileexists.md) | Check if a file exists. |
@@ -125,7 +125,7 @@ export async function main(ns) {
| [hasRootAccess(host)](./bitburner.ns.hasrootaccess.md) | Check if you have root access on a server. |
| [hasTorRouter()](./bitburner.ns.hastorrouter.md) | Returns whether the player has access to the darkweb. |
| [httpworm(host)](./bitburner.ns.httpworm.md) | Runs HTTPWorm.exe on a server. |
| [isLogEnabled(fn)](./bitburner.ns.islogenabled.md) | Checks the status of the logging for the given function. |
| [isLogEnabled(fn)](./bitburner.ns.islogenabled.md) | Checks the status of the logging for the given NS function. |
| [isRunning(script, host, args)](./bitburner.ns.isrunning.md) | Check if a script is running. |
| [kill(pid)](./bitburner.ns.kill.md) | Terminate the script with the provided PID. |
| [kill(filename, hostname, args)](./bitburner.ns.kill_1.md) | Terminate the script(s) with the provided filename, hostname, and script arguments. |
@@ -143,6 +143,7 @@ export async function main(ns) {
| [prompt(txt, options)](./bitburner.ns.prompt.md) | Prompt the player with an input modal. |
| [ps(host)](./bitburner.ns.ps.md) | List running scripts on a server. |
| [purchaseServer(hostname, ram)](./bitburner.ns.purchaseserver.md) | Purchase a server. |
| [ramOverride(ram)](./bitburner.ns.ramoverride.md) | Change the current static RAM allocation of the script. |
| [read(filename)](./bitburner.ns.read.md) | Read content of a file. |
| [readPort(portNumber)](./bitburner.ns.readport.md) | Read data from a port. |
| [relaysmtp(host)](./bitburner.ns.relaysmtp.md) | Runs relaySMTP.exe on a server. |

View File

@@ -42,7 +42,7 @@ ns.printf("I'm %d seconds old.", age);
ns.printf("My age in binary is %b.", age);
ns.printf("My age in scientific notation is %e.", age);
ns.printf("In %d seconds, I'll be %s.", 6, "Byte");
ns.printf("Am I a nibble? %t", (4 == age));
ns.printf("Am I a nibble? %t", (4 === age));
ns.tail();
```

View File

@@ -35,7 +35,7 @@ Returns an array with general information about all scripts running on the speci
```js
const ps = ns.ps("home");
for (let script of ps) {
for (const script of ps) {
ns.tprint(`${script.filename} ${script.threads}`);
ns.tprint(script.args);
}

View File

@@ -44,7 +44,7 @@ Returns the hostname of the newly purchased server as a string. If the function
// Attempt to purchase 5 servers with 64GB of ram each
const ram = 64;
const prefix = "pserv-";
for (i = 0; i < 5; ++i) {
for (let i = 0; i < 5; ++i) {
ns.purchaseServer(prefix + i, ram);
}
```

View File

@@ -0,0 +1,34 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [NS](./bitburner.ns.md) &gt; [ramOverride](./bitburner.ns.ramoverride.md)
## NS.ramOverride() method
Change the current static RAM allocation of the script.
**Signature:**
```typescript
ramOverride(ram?: number): number;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| ram | number | _(Optional)_ The new RAM limit to set. |
**Returns:**
number
The new static RAM limit, which will be the old one if it wasn't changed. This means you can use no parameters to check the current ram limit.
## Remarks
RAM cost: 0 GB
This acts analagously to the ramOverride parameter in runOptions, but for changing RAM in the current running script. The static RAM allocation (the amount of RAM used by ONE thread) will be adjusted to the given value, if possible. This can fail if the number is less than the current dynamic RAM limit, or if adjusting upward would require more RAM than is available on the server.
RAM usage will be rounded to the nearest hundredth of a GB, which is the granularity of all RAM calculations.

View File

@@ -51,6 +51,6 @@ ns.run("foo.js");
ns.run("foo.js", {threads: 5});
//This next example will run foo.js single-threaded, and will pass the string foodnstuff into the script as an argument:
ns.run("foo.js", 1, 'foodnstuff');
ns.run("foo.js", 1, "foodnstuff");
```

View File

@@ -33,7 +33,7 @@ RAM cost: 0 GB
```js
// This will count from 1 to 10 in your terminal, with one number every 5 seconds
for (var i = 1; i <= 10; i++) {
for (let i = 1; i <= 10; ++i) {
ns.tprint(i);
await ns.sleep(5000);
}

View File

@@ -30,6 +30,8 @@ RAM cost: 2 GB
Terminates the current script, and then after a defined delay it will execute the newly-specified script. The purpose of this function is to execute a new script without being constrained by the RAM usage of the current one. This function can only be used to run scripts on the local server.
The delay specified can be 0; in this case the new script will synchronously replace the old one. (There will not be any opportunity for other scripts to use up the RAM in-between.)
Because this function immediately terminates the script, it does not have a return value.
Running this function with 0 or fewer threads will cause a runtime error.
@@ -39,6 +41,6 @@ Running this function with 0 or fewer threads will cause a runtime error.
```js
//The following example will execute the script foo.js with 10 threads, in 500 milliseconds and the arguments foodnstuff and 90:
ns.spawn('foo.js', {threads: 10, spawnDelay: 500}, 'foodnstuff', 90);
ns.spawn("foo.js", {threads: 10, spawnDelay: 500}, "foodnstuff", 90);
```

View File

@@ -31,3 +31,5 @@ RAM cost: 0 GB
Write data to the given Netscript port.
There is a limit on the maximum number of ports, but you won't reach that limit in normal situations. If you do, it usually means that there is a bug in your script that leaks port data. A port is freed when it does not have any data in its underlying queue. `ns.clearPort` deletes all data on a port. `ns.readPort` reads the first element in the port's queue, then removes it from the queue.

View File

@@ -7,5 +7,5 @@
**Signature:**
```typescript
location: string;
location: LocationName;
```

View File

@@ -20,7 +20,7 @@ interface Player extends Person
| [factions](./bitburner.player.factions.md) | | string\[\] | |
| [jobs](./bitburner.player.jobs.md) | | Partial&lt;Record&lt;[CompanyName](./bitburner.companyname.md)<!-- -->, [JobName](./bitburner.jobname.md)<!-- -->&gt;&gt; | |
| [karma](./bitburner.player.karma.md) | | number | |
| [location](./bitburner.player.location.md) | | string | |
| [location](./bitburner.player.location.md) | | [LocationName](./bitburner.locationname.md) | |
| [money](./bitburner.player.money.md) | | number | |
| [numPeopleKilled](./bitburner.player.numpeoplekilled.md) | | number | |
| [totalPlaytime](./bitburner.player.totalplaytime.md) | | number | |

View File

@@ -0,0 +1,15 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [RunningScript](./bitburner.runningscript.md) &gt; [dynamicRamUsage](./bitburner.runningscript.dynamicramusage.md)
## RunningScript.dynamicRamUsage property
The dynamic RAM usage of (one thread of) this script instance. Does not affect overall RAM consumption (ramUsage is for that), but rather shows how much of the reserved RAM is currently in use via all the ns functions the script has called. Initially 1.6GB, this increases as new functions are called.
Only set for scripts that are still running.
**Signature:**
```typescript
dynamicRamUsage: number | undefined;
```

View File

@@ -16,6 +16,7 @@ interface RunningScript
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [args](./bitburner.runningscript.args.md) | | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments the script was called with |
| [dynamicRamUsage](./bitburner.runningscript.dynamicramusage.md) | | number \| undefined | <p>The dynamic RAM usage of (one thread of) this script instance. Does not affect overall RAM consumption (ramUsage is for that), but rather shows how much of the reserved RAM is currently in use via all the ns functions the script has called. Initially 1.6GB, this increases as new functions are called.</p><p>Only set for scripts that are still running.</p> |
| [filename](./bitburner.runningscript.filename.md) | | string | Filename of the script |
| [logs](./bitburner.runningscript.logs.md) | | string\[\] | Script logs as an array. The newest log entries are at the bottom. Timestamps, if enabled, are placed inside <code>[brackets]</code> at the start of each line. |
| [offlineExpGained](./bitburner.runningscript.offlineexpgained.md) | | number | Total amount of hacking experience earned from this script when offline |
@@ -25,7 +26,7 @@ interface RunningScript
| [onlineMoneyMade](./bitburner.runningscript.onlinemoneymade.md) | | number | Total amount of money made by this script when online |
| [onlineRunningTime](./bitburner.runningscript.onlinerunningtime.md) | | number | Number of seconds that this script has been running online |
| [pid](./bitburner.runningscript.pid.md) | | number | Process ID. Must be an integer |
| [ramUsage](./bitburner.runningscript.ramusage.md) | | number | How much RAM this script uses for ONE thread |
| [ramUsage](./bitburner.runningscript.ramusage.md) | | number | How much RAM this script uses for ONE thread. Also known as "static RAM usage," this value does not change once the script is started, unless you call ns.ramOverride(). |
| [server](./bitburner.runningscript.server.md) | | string | Hostname of the server on which this script runs |
| [tailProperties](./bitburner.runningscript.tailproperties.md) | | [TailProperties](./bitburner.tailproperties.md) \| null | Properties of the tail window, or null if it is not shown |
| [temporary](./bitburner.runningscript.temporary.md) | | boolean | Whether this RunningScript is excluded from saves |

View File

@@ -4,7 +4,7 @@
## RunningScript.ramUsage property
How much RAM this script uses for ONE thread
How much RAM this script uses for ONE thread. Also known as "static RAM usage," this value does not change once the script is started, unless you call ns.ramOverride().
**Signature:**

View File

@@ -16,7 +16,7 @@ interface RunOptions
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [preventDuplicates?](./bitburner.runoptions.preventduplicates.md) | | boolean | _(Optional)_ Should we fail to run if another instance is running with the exact same arguments? This used to be the default behavior, now defaults to false. |
| [ramOverride?](./bitburner.runoptions.ramoverride.md) | | number | <p>_(Optional)_ The RAM allocation to launch each thread of the script with.</p><p>Lowering this will <i>not</i> automatically let you get away with using less RAM: the dynamic RAM check enforces that all [NS](./bitburner.ns.md) functions actually called incur their cost. However, if you know that certain functions that are statically present (and thus included in the static RAM cost) will never be called in a particular circumstance, you can use this to avoid paying for them.</p><p>You can also use this to <i>increase</i> the RAM if the static RAM checker has missed functions that you need to call.</p><p>Must be greater-or-equal to the base RAM cost. Defaults to the statically calculated cost.</p> |
| [ramOverride?](./bitburner.runoptions.ramoverride.md) | | number | <p>_(Optional)_ The RAM allocation to launch each thread of the script with.</p><p>Lowering this will <i>not</i> automatically let you get away with using less RAM: the dynamic RAM check enforces that all [NS](./bitburner.ns.md) functions actually called incur their cost. However, if you know that certain functions that are statically present (and thus included in the static RAM cost) will never be called in a particular circumstance, you can use this to avoid paying for them.</p><p>You can also use this to <i>increase</i> the RAM if the static RAM checker has missed functions that you need to call.</p><p>Must be greater-or-equal to the base RAM cost. Will be rounded to the nearest hundredth-of-a-GB, which is the granularity of all RAM calculations. Defaults to the statically calculated cost.</p> |
| [temporary?](./bitburner.runoptions.temporary.md) | | boolean | _(Optional)_ Whether this script is excluded from saves, defaults to false |
| [threads?](./bitburner.runoptions.threads.md) | | number | _(Optional)_ Number of threads that the script will run with, defaults to 1 |

View File

@@ -10,7 +10,7 @@ Lowering this will <i>not</i> automatically let you get away with using less RAM
You can also use this to <i>increase</i> the RAM if the static RAM checker has missed functions that you need to call.
Must be greater-or-equal to the base RAM cost. Defaults to the statically calculated cost.
Must be greater-or-equal to the base RAM cost. Will be rounded to the nearest hundredth-of-a-GB, which is the granularity of all RAM calculations. Defaults to the statically calculated cost.
**Signature:**

View File

@@ -40,7 +40,7 @@ Note that creating a program using this function has the same hacking level requ
```js
const programName = "BruteSSH.exe";
const success = ns.createProgram(programName);
if (!success) ns.tprint("ERROR: Failed to start working on ${programName}")
const success = ns.singularity.createProgram(programName);
if (!success) ns.tprint(`ERROR: Failed to start working on ${programName}`);
```

View File

@@ -37,7 +37,7 @@ If the program does not exist, an error is thrown.
```js
const programName = "BruteSSH.exe";
const cost = ns.getDarkwebProgramCost(programName);
if (cost > 0) ns.tprint(`${programName} costs ${ns.formatMoney(cost)}`);
const cost = ns.singularity.getDarkwebProgramCost(programName);
if (cost > 0) ns.tprint(`${programName} costs $${ns.formatNumber(cost)}`);
```

View File

@@ -27,7 +27,7 @@ This function allows the player to get a list of programs available for purchase
```js
const programs = ns.getDarkwebPrograms();
ns.tprint(`Available programs are: ${programs.split(", ")}`);
const programs = ns.singularity.getDarkwebPrograms();
ns.tprint(`Available programs are: ${programs}`);
```

View File

@@ -32,7 +32,8 @@ RAM cost: 3 GB \* 16/4/1
```js
ns.singularity.getFactionInviteRequirements("The Syndicate")
ns.singularity.getFactionInviteRequirements("The Syndicate");
[
{ "type": "someCondition", "conditions": [
{ "type": "city", "city": "Aevum" },
@@ -49,7 +50,10 @@ ns.singularity.getFactionInviteRequirements("The Syndicate")
},
{ "type": "money", "money": 10000000 },
{ "type": "skills", "skills": { "hacking": 200 } },
{ "type": "skills", "skills": { "strength": 200, "defense": 200, "dexterity": 200, "agility": 200 } },
{ "type": "skills", "skills": { "strength": 200 } },
{ "type": "skills", "skills": { "defense": 200 } },
{ "type": "skills", "skills": { "dexterity": 200 } },
{ "type": "skills", "skills": { "agility": 200 } },
{ "type": "karma", "karma": -90 }
]
```

View File

@@ -0,0 +1,32 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [bitburner](./bitburner.md) &gt; [Singularity](./bitburner.singularity.md) &gt; [getFactionWorkTypes](./bitburner.singularity.getfactionworktypes.md)
## Singularity.getFactionWorkTypes() method
Get the work types of a faction.
**Signature:**
```typescript
getFactionWorkTypes(faction: string): FactionWorkType[];
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| faction | string | Name of the faction. |
**Returns:**
[FactionWorkType](./bitburner.factionworktype.md)<!-- -->\[\]
The work types of the faction.
## Remarks
RAM cost: 1 GB \* 16/4/1
This function returns an array containing the work types of the specified faction.

View File

@@ -9,14 +9,14 @@ Go to a location.
**Signature:**
```typescript
goToLocation(locationName: string): boolean;
goToLocation(locationName: LocationName | `${LocationName}`): boolean;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| locationName | string | Name of the location. |
| locationName | [LocationName](./bitburner.locationname.md) \| \`${[LocationName](./bitburner.locationname.md)<!-- -->}\` | Name of the location. |
**Returns:**

View File

@@ -53,6 +53,7 @@ This API requires Source-File 4 to use. The RAM cost of all these functions is m
| [getFactionFavorGain(faction)](./bitburner.singularity.getfactionfavorgain.md) | Get faction favor gain. |
| [getFactionInviteRequirements(faction)](./bitburner.singularity.getfactioninviterequirements.md) | List conditions for being invited to a faction. |
| [getFactionRep(faction)](./bitburner.singularity.getfactionrep.md) | Get faction reputation. |
| [getFactionWorkTypes(faction)](./bitburner.singularity.getfactionworktypes.md) | Get the work types of a faction. |
| [getOwnedAugmentations(purchased)](./bitburner.singularity.getownedaugmentations.md) | Get a list of owned augmentation. |
| [getOwnedSourceFiles()](./bitburner.singularity.getownedsourcefiles.md) | Get a list of acquired Source-Files. |
| [getUpgradeHomeCoresCost()](./bitburner.singularity.getupgradehomecorescost.md) | Get the price of upgrading home cores. |

View File

@@ -34,8 +34,8 @@ This function allows you to automatically purchase programs. You MUST have a TOR
```js
const programName = "BruteSSH.exe"
const success = ns.purchaseProgram(programName);
if (!success) ns.tprint("ERROR: Failed to purchase ${programName}")
const programName = "BruteSSH.exe";
const success = ns.singularity.purchaseProgram(programName);
if (!success) ns.tprint(`ERROR: Failed to purchase ${programName}`);
```

View File

@@ -42,6 +42,6 @@ const factionName = "CyberSec";
const workType = "hacking";
let success = ns.singularity.workForFaction(factionName, workType);
if (!success) ns.tprint(`ERROR: Failed to start work for ${factionName} with work type ${workType}.`)
if (!success) ns.tprint(`ERROR: Failed to start work for ${factionName} with work type ${workType}.`);
```

View File

@@ -37,5 +37,5 @@ If you are not in BitNode-10, then you must have Source-File 10 in order to use
| [setToShockRecovery(sleeveNumber)](./bitburner.sleeve.settoshockrecovery.md) | Set a sleeve to shock recovery. |
| [setToSynchronize(sleeveNumber)](./bitburner.sleeve.settosynchronize.md) | Set a sleeve to synchronize. |
| [setToUniversityCourse(sleeveNumber, university, className)](./bitburner.sleeve.settouniversitycourse.md) | Set a sleeve to take a class at a university. |
| [travel(sleeveNumber, city)](./bitburner.sleeve.travel.md) | Make a sleeve travel to another city. |
| [travel(sleeveNumber, city)](./bitburner.sleeve.travel.md) | Make a sleeve travel to another city. The cost for using this function is the same as for a player. |

View File

@@ -34,12 +34,12 @@ Return a boolean indicating whether or not this action was set successfully (fal
## Example
```ts
```js
// Assigns the first sleeve to Homicide.
ns.sleeve.setToCommitCrime(0, "Homicide");
// Assigns the second sleeve to Grand Theft Auto, using enum
const crimes = ns.enums.CrimeType;
ns.sleeve.setToCommitCrime(1, crimes.grandTheftAuto)
ns.sleeve.setToCommitCrime(1, crimes.grandTheftAuto);
```

View File

@@ -4,7 +4,7 @@
## Sleeve.travel() method
Make a sleeve travel to another city.
Make a sleeve travel to another city. The cost for using this function is the same as for a player.
**Signature:**

View File

@@ -16,5 +16,5 @@ interface SpawnOptions extends RunOptions
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [spawnDelay?](./bitburner.spawnoptions.spawndelay.md) | | number | _(Optional)_ Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a positive integer. |
| [spawnDelay?](./bitburner.spawnoptions.spawndelay.md) | | number | _(Optional)_ Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a non-negative integer. If 0, the script will be spawned synchronously. |

View File

@@ -4,7 +4,7 @@
## SpawnOptions.spawnDelay property
Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a positive integer.
Number of milliseconds to delay before spawning script, defaults to 10000 (10s). Must be a non-negative integer. If 0, the script will be spawned synchronously.
**Signature:**

View File

@@ -7,5 +7,5 @@
**Signature:**
```typescript
location: string;
location: LocationName | `${LocationName}`;
```

View File

@@ -22,6 +22,6 @@ An object representing the current study task
| --- | --- | --- | --- |
| [classType](./bitburner.studytask.classtype.md) | | string | |
| [cyclesWorked](./bitburner.studytask.cyclesworked.md) | | number | |
| [location](./bitburner.studytask.location.md) | | string | |
| [location](./bitburner.studytask.location.md) | | [LocationName](./bitburner.locationname.md) \| \`${[LocationName](./bitburner.locationname.md)<!-- -->}\` | |
| [type](./bitburner.studytask.type.md) | | "CLASS" | |

View File

@@ -21,7 +21,7 @@ Object containing information for all the Limit and Stop Orders you have in the
RAM cost: 2.5 GB This is an object containing information for all the Limit and Stop Orders you have in the stock market. For each symbol you have a position in, the returned object will have a key with that symbol's name. The object's properties are each an array of [StockOrderObject](./bitburner.stockorderobject.md) The object has the following structure:
```ts
```js
{
string1: [ // Array of orders for this stock
{
@@ -46,7 +46,7 @@ The “Order type” property can have one of the following four values: "Limit
## Example
```ts
```js
"If you do not have orders in Nova Medical (NVMD), then the returned object will not have a “NVMD” property."
{
ECP: [

View File

@@ -30,7 +30,7 @@ RAM cost: 0 GB
Usage example (NS2)
```ts
```js
const styles = ns.ui.getStyles();
styles.fontFamily = 'Comic Sans Ms';
ns.ui.setStyles(styles);

View File

@@ -30,7 +30,7 @@ RAM cost: 0 GB
Usage example (NS2)
```ts
```js
const theme = ns.ui.getTheme();
theme.primary = '#ff5500';
ns.ui.setTheme(theme);

View File

@@ -7,7 +7,7 @@
**Signature:**
```typescript
gymGains(person: Person, gymType: GymType | `${GymType}`, locationName: string): WorkStats;
gymGains(person: Person, gymType: GymType | `${GymType}`, locationName: LocationName | `${LocationName}`): WorkStats;
```
## Parameters
@@ -16,7 +16,7 @@ gymGains(person: Person, gymType: GymType | `${GymType}`, locationName: string):
| --- | --- | --- |
| person | [Person](./bitburner.person.md) | |
| gymType | [GymType](./bitburner.gymtype.md) \| \`${[GymType](./bitburner.gymtype.md)<!-- -->}\` | |
| locationName | string | |
| locationName | [LocationName](./bitburner.locationname.md) \| \`${[LocationName](./bitburner.locationname.md)<!-- -->}\` | |
**Returns:**

View File

@@ -10,7 +10,7 @@
universityGains(
person: Person,
classType: UniversityClassType | `${UniversityClassType}`,
locationName: string,
locationName: LocationName | `${LocationName}`,
): WorkStats;
```
@@ -20,7 +20,7 @@ universityGains(
| --- | --- | --- |
| person | [Person](./bitburner.person.md) | |
| classType | [UniversityClassType](./bitburner.universityclasstype.md) \| \`${[UniversityClassType](./bitburner.universityclasstype.md)<!-- -->}\` | |
| locationName | string | |
| locationName | [LocationName](./bitburner.locationname.md) \| \`${[LocationName](./bitburner.locationname.md)<!-- -->}\` | |
**Returns:**

130
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "bitburner",
"version": "2.6.1",
"version": "2.6.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bitburner",
"version": "2.6.1",
"version": "2.6.2",
"hasInstallScript": true,
"license": "SEE LICENSE IN license.txt",
"dependencies": {
@@ -43,7 +43,8 @@
"react-resizable": "^3.0.5",
"react-syntax-highlighter": "^15.5.0",
"remark-gfm": "^3.0.1",
"sprintf-js": "^1.1.3"
"sprintf-js": "^1.1.3",
"tss-react": "^4.9.10"
},
"devDependencies": {
"@babel/core": "^7.23.0",
@@ -5945,13 +5946,14 @@
"dev": true
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dev": true,
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@@ -5959,7 +5961,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@@ -5973,6 +5975,7 @@
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
@@ -5982,6 +5985,7 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
@@ -5990,7 +5994,8 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true
"dev": true,
"license": "MIT"
},
"node_modules/bonjour-service": {
"version": "1.1.1",
@@ -6028,12 +6033,13 @@
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -6598,6 +6604,7 @@
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
@@ -6609,10 +6616,11 @@
"dev": true
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
@@ -8232,17 +8240,18 @@
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@@ -8484,10 +8493,11 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -8634,9 +8644,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true,
"funding": [
{
@@ -8644,6 +8654,7 @@
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
@@ -8900,20 +8911,6 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -9810,6 +9807,7 @@
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
@@ -10305,6 +10303,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
@@ -12502,14 +12501,15 @@
}
},
"node_modules/katex": {
"version": "0.16.9",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.9.tgz",
"integrity": "sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==",
"version": "0.16.10",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz",
"integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==",
"dev": true,
"funding": [
"https://opencollective.com/katex",
"https://github.com/sponsors/katex"
],
"license": "MIT",
"dependencies": {
"commander": "^8.3.0"
},
@@ -13126,6 +13126,7 @@
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
@@ -14925,6 +14926,7 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
@@ -14997,10 +14999,11 @@
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@@ -15016,6 +15019,7 @@
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
@@ -16787,6 +16791,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
@@ -16896,6 +16901,30 @@
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/tss-react": {
"version": "4.9.10",
"resolved": "https://registry.npmjs.org/tss-react/-/tss-react-4.9.10.tgz",
"integrity": "sha512-uQj+r8mOKy0tv+/GAIzViVG81w/WeTCOF7tjsDyNjlicnWbxtssYwTvVjWT4lhWh5FSznDRy6RFp0BDdoLbxyg==",
"dependencies": {
"@emotion/cache": "*",
"@emotion/serialize": "*",
"@emotion/utils": "*"
},
"peerDependencies": {
"@emotion/react": "^11.4.1",
"@emotion/server": "^11.4.0",
"@mui/material": "^5.0.0",
"react": "^16.8.0 || ^17.0.2 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/server": {
"optional": true
},
"@mui/material": {
"optional": true
}
}
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -16934,6 +16963,7 @@
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
@@ -17998,9 +18028,9 @@
}
},
"node_modules/ws": {
"version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"dev": true,
"engines": {
"node": ">=10.0.0"

View File

@@ -1,7 +1,7 @@
{
"name": "bitburner",
"license": "SEE LICENSE IN license.txt",
"version": "2.6.1",
"version": "2.6.2",
"main": "electron-main.js",
"author": {
"name": "Daniel Xie, hydroflame, et al."
@@ -43,7 +43,8 @@
"react-resizable": "^3.0.5",
"react-syntax-highlighter": "^15.5.0",
"remark-gfm": "^3.0.1",
"sprintf-js": "^1.1.3"
"sprintf-js": "^1.1.3",
"tss-react": "^4.9.10"
},
"description": "A cyberpunk-themed incremental game",
"devDependencies": {
@@ -86,13 +87,13 @@
"prettier": "^2.8.8",
"raw-loader": "^4.0.2",
"react-refresh": "^0.14.0",
"rehype-mathjax": "^4.0.3",
"remark-math": "^5.1.1",
"style-loader": "^3.3.3",
"typescript": "^5.2.2",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.2",
"remark-math": "^5.1.1",
"rehype-mathjax": "^4.0.3"
"webpack-dev-server": "^4.15.2"
},
"engines": {
"node": ">=14"
@@ -118,7 +119,7 @@
"watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development",
"electron": "bash ./tools/package-electron.sh",
"electron:packager-all": "electron-packager .package bitburner --platform all --arch x64,armv7l,arm64,mips64el --out .build --overwrite --icon .package/icon.png",
"electron:packager-all": "electron-packager .package bitburner --platform all --arch x64,armv7l,arm64,mips64el --out .build --overwrite --icon .package/icon.png --app-copyright \"Copyright (C) 2024 Bitburner\"",
"preversion": "npm install && npm run test",
"version": "sh ./tools/build-release.sh && git add --all",
"postversion": "git push -u origin dev && git push --tags",

View File

@@ -19,7 +19,6 @@ import { HacknetNode } from "../Hacknet/HacknetNode";
import { HacknetServer } from "../Hacknet/HacknetServer";
import { Player } from "@player";
import { GetAllServers, GetServer } from "../Server/AllServers";
import { SpecialServers } from "../Server/data/SpecialServers";
import { Server } from "../Server/Server";
import { Router } from "../ui/GameRoot";
import { Page } from "../ui/Router";
@@ -30,7 +29,7 @@ import { workerScripts } from "../Netscript/WorkerScripts";
import { getRecordValues } from "../Types/Record";
import { ServerConstants } from "../Server/data/Constants";
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
import { isBitNodeFinished } from "../BitNode/BitNodeUtils";
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
@@ -61,15 +60,8 @@ export interface AchievementData {
Description: string;
}
function bitNodeFinishedState(): boolean {
const wd = GetServer(SpecialServers.WorldDaemon);
if (!(wd instanceof Server)) return false;
if (wd.backdoorInstalled) return true;
return Player.bladeburner !== null && Player.bladeburner.numBlackOpsComplete >= blackOpsArray.length;
}
function hasAccessToSF(bn: number): boolean {
return Player.bitNodeN === bn || Player.sourceFileLvl(bn) > 0;
function canAccessBitNodeFeature(bitNode: number): boolean {
return Player.bitNodeN === bitNode || Player.sourceFileLvl(bitNode) > 0;
}
function knowsAboutBitverse(): boolean {
@@ -338,25 +330,25 @@ export const achievements: Record<string, Achievement> = {
GANG: {
...achievementData.GANG,
Icon: "GANG",
Visible: () => hasAccessToSF(2),
Visible: () => canAccessBitNodeFeature(2),
Condition: () => Player.gang !== null,
},
FULL_GANG: {
...achievementData.FULL_GANG,
Icon: "GANGMAX",
Visible: () => hasAccessToSF(2),
Visible: () => canAccessBitNodeFeature(2),
Condition: () => Player.gang !== null && Player.gang.members.length === GangConstants.MaximumGangMembers,
},
GANG_TERRITORY: {
...achievementData.GANG_TERRITORY,
Icon: "GANG100%",
Visible: () => hasAccessToSF(2),
Visible: () => canAccessBitNodeFeature(2),
Condition: () => Player.gang !== null && AllGangs[Player.gang.facName].territory >= 0.999,
},
GANG_MEMBER_POWER: {
...achievementData.GANG_MEMBER_POWER,
Icon: "GANG10000",
Visible: () => hasAccessToSF(2),
Visible: () => canAccessBitNodeFeature(2),
Condition: () =>
Player.gang !== null &&
Player.gang.members.some(
@@ -367,19 +359,19 @@ export const achievements: Record<string, Achievement> = {
CORPORATION: {
...achievementData.CORPORATION,
Icon: "CORP",
Visible: () => hasAccessToSF(3),
Visible: () => canAccessBitNodeFeature(3),
Condition: () => Player.corporation !== null,
},
CORPORATION_BRIBE: {
...achievementData.CORPORATION_BRIBE,
Icon: "CORPLOBBY",
Visible: () => hasAccessToSF(3),
Visible: () => canAccessBitNodeFeature(3),
Condition: () => !!Player.corporation && Player.corporation.unlocks.has(CorpUnlockName.GovernmentPartnership),
},
CORPORATION_PROD_1000: {
...achievementData.CORPORATION_PROD_1000,
Icon: "CORP1000",
Visible: () => hasAccessToSF(3),
Visible: () => canAccessBitNodeFeature(3),
Condition: () => {
if (!Player.corporation) return false;
for (const division of Player.corporation.divisions.values()) {
@@ -391,7 +383,7 @@ export const achievements: Record<string, Achievement> = {
CORPORATION_EMPLOYEE_3000: {
...achievementData.CORPORATION_EMPLOYEE_3000,
Icon: "CORPCITY",
Visible: () => hasAccessToSF(3),
Visible: () => canAccessBitNodeFeature(3),
Condition: (): boolean => {
if (!Player.corporation) return false;
for (const division of Player.corporation.divisions.values()) {
@@ -406,7 +398,7 @@ export const achievements: Record<string, Achievement> = {
Icon: "CORPRE",
Name: "Own the land",
Description: "Expand to the Real Estate division.",
Visible: () => hasAccessToSF(3),
Visible: () => canAccessBitNodeFeature(3),
Condition: () => {
if (!Player.corporation) return false;
for (const division of Player.corporation.divisions.values()) {
@@ -418,26 +410,26 @@ export const achievements: Record<string, Achievement> = {
INTELLIGENCE_255: {
...achievementData.INTELLIGENCE_255,
Icon: "INT255",
Visible: () => hasAccessToSF(5),
Visible: () => canAccessBitNodeFeature(5),
Condition: () => Player.skills.intelligence >= 255,
},
BLADEBURNER_DIVISION: {
...achievementData.BLADEBURNER_DIVISION,
Icon: "BLADE",
Visible: () => hasAccessToSF(6),
Visible: () => canAccessBitNodeFeature(6),
Condition: () => Player.bladeburner !== null,
},
BLADEBURNER_OVERCLOCK: {
...achievementData.BLADEBURNER_OVERCLOCK,
Icon: "BLADEOVERCLOCK",
Visible: () => hasAccessToSF(6),
Visible: () => canAccessBitNodeFeature(6),
Condition: () =>
Player.bladeburner?.getSkillLevel(BladeSkillName.overclock) === Skills[BladeSkillName.overclock].maxLvl,
},
BLADEBURNER_UNSPENT_100000: {
...achievementData.BLADEBURNER_UNSPENT_100000,
Icon: "BLADE100K",
Visible: () => hasAccessToSF(6),
Visible: () => canAccessBitNodeFeature(6),
Condition: () => Player.bladeburner !== null && Player.bladeburner.skillPoints >= 100000,
},
"4S": {
@@ -448,21 +440,21 @@ export const achievements: Record<string, Achievement> = {
FIRST_HACKNET_SERVER: {
...achievementData.FIRST_HACKNET_SERVER,
Icon: "HASHNET",
Visible: () => hasAccessToSF(9),
Visible: () => canAccessBitNodeFeature(9),
Condition: () => hasHacknetServers() && Player.hacknetNodes.length > 0,
AdditionalUnlock: [achievementData.FIRST_HACKNET_NODE.ID],
},
ALL_HACKNET_SERVER: {
...achievementData.ALL_HACKNET_SERVER,
Icon: "HASHNETALL",
Visible: () => hasAccessToSF(9),
Visible: () => canAccessBitNodeFeature(9),
Condition: () => hasHacknetServers() && Player.hacknetNodes.length === HacknetServerConstants.MaxServers,
AdditionalUnlock: [achievementData["30_HACKNET_NODE"].ID],
},
MAX_HACKNET_SERVER: {
...achievementData.MAX_HACKNET_SERVER,
Icon: "HASHNETALL",
Visible: () => hasAccessToSF(9),
Visible: () => canAccessBitNodeFeature(9),
Condition: (): boolean => {
if (!hasHacknetServers()) return false;
for (const h of Player.hacknetNodes) {
@@ -484,14 +476,14 @@ export const achievements: Record<string, Achievement> = {
HACKNET_SERVER_1B: {
...achievementData.HACKNET_SERVER_1B,
Icon: "HASHNETMONEY",
Visible: () => hasAccessToSF(9),
Visible: () => canAccessBitNodeFeature(9),
Condition: () => hasHacknetServers() && Player.moneySourceB.hacknet >= 1e9,
AdditionalUnlock: [achievementData.HACKNET_NODE_10M.ID],
},
MAX_CACHE: {
...achievementData.MAX_CACHE,
Icon: "HASHNETCAP",
Visible: () => hasAccessToSF(9),
Visible: () => canAccessBitNodeFeature(9),
Condition: () =>
hasHacknetServers() &&
Player.hashManager.hashes === Player.hashManager.capacity &&
@@ -500,7 +492,7 @@ export const achievements: Record<string, Achievement> = {
SLEEVE_8: {
...achievementData.SLEEVE_8,
Icon: "SLEEVE8",
Visible: () => hasAccessToSF(10),
Visible: () => canAccessBitNodeFeature(10),
Condition: () => Player.sleeves.length === 8 && Player.sourceFileLvl(10) === 3,
},
INDECISIVE: {
@@ -523,7 +515,7 @@ export const achievements: Record<string, Achievement> = {
...achievementData.FAST_BN,
Icon: "2DAYS",
Visible: knowsAboutBitverse,
Condition: () => bitNodeFinishedState() && Player.playtimeSinceLastBitnode < 1000 * 60 * 60 * 24 * 2,
Condition: () => isBitNodeFinished() && Player.playtimeSinceLastBitnode < 1000 * 60 * 60 * 24 * 2,
},
CHALLENGE_BN1: {
...achievementData.CHALLENGE_BN1,
@@ -531,57 +523,57 @@ export const achievements: Record<string, Achievement> = {
Visible: knowsAboutBitverse,
Condition: () =>
Player.bitNodeN === 1 &&
bitNodeFinishedState() &&
isBitNodeFinished() &&
Player.getHomeComputer().maxRam <= 128 &&
Player.getHomeComputer().cpuCores === 1,
},
CHALLENGE_BN2: {
...achievementData.CHALLENGE_BN2,
Icon: "BN2+",
Visible: () => hasAccessToSF(2),
Condition: () => Player.bitNodeN === 2 && bitNodeFinishedState() && Player.gang === null,
Visible: () => canAccessBitNodeFeature(2),
Condition: () => Player.bitNodeN === 2 && isBitNodeFinished() && Player.gang === null,
},
CHALLENGE_BN3: {
...achievementData.CHALLENGE_BN3,
Icon: "BN3+",
Visible: () => hasAccessToSF(3),
Condition: () => Player.bitNodeN === 3 && bitNodeFinishedState() && Player.corporation === null,
Visible: () => canAccessBitNodeFeature(3),
Condition: () => Player.bitNodeN === 3 && isBitNodeFinished() && Player.corporation === null,
},
CHALLENGE_BN6: {
...achievementData.CHALLENGE_BN6,
Icon: "BN6+",
Visible: () => hasAccessToSF(6),
Condition: () => Player.bitNodeN === 6 && bitNodeFinishedState() && Player.bladeburner === null,
Visible: () => canAccessBitNodeFeature(6),
Condition: () => Player.bitNodeN === 6 && isBitNodeFinished() && Player.bladeburner === null,
},
CHALLENGE_BN7: {
...achievementData.CHALLENGE_BN7,
Icon: "BN7+",
Visible: () => hasAccessToSF(7),
Condition: () => Player.bitNodeN === 7 && bitNodeFinishedState() && Player.bladeburner === null,
Visible: () => canAccessBitNodeFeature(7),
Condition: () => Player.bitNodeN === 7 && isBitNodeFinished() && Player.bladeburner === null,
},
CHALLENGE_BN8: {
...achievementData.CHALLENGE_BN8,
Icon: "BN8+",
Visible: () => hasAccessToSF(8),
Condition: () => Player.bitNodeN === 8 && bitNodeFinishedState() && !Player.has4SData && !Player.has4SDataTixApi,
Visible: () => canAccessBitNodeFeature(8),
Condition: () => Player.bitNodeN === 8 && isBitNodeFinished() && !Player.has4SData && !Player.has4SDataTixApi,
},
CHALLENGE_BN9: {
...achievementData.CHALLENGE_BN9,
Icon: "BN9+",
Visible: () => hasAccessToSF(9),
Visible: () => canAccessBitNodeFeature(9),
Condition: () =>
Player.bitNodeN === 9 &&
bitNodeFinishedState() &&
isBitNodeFinished() &&
Player.moneySourceB.hacknet === 0 &&
Player.moneySourceB.hacknet_expenses === 0,
},
CHALLENGE_BN10: {
...achievementData.CHALLENGE_BN10,
Icon: "BN10+",
Visible: () => hasAccessToSF(10),
Visible: () => canAccessBitNodeFeature(10),
Condition: () =>
Player.bitNodeN === 10 &&
bitNodeFinishedState() &&
isBitNodeFinished() &&
!Player.sleeves.some(
(s) =>
s.augmentations.length > 0 ||
@@ -596,7 +588,7 @@ export const achievements: Record<string, Achievement> = {
CHALLENGE_BN12: {
...achievementData.CHALLENGE_BN12,
Icon: "BN12+",
Visible: () => hasAccessToSF(12),
Visible: () => canAccessBitNodeFeature(12),
Condition: () => Player.sourceFileLvl(12) >= 50,
},
BYPASS: {
@@ -657,10 +649,10 @@ export const achievements: Record<string, Achievement> = {
CHALLENGE_BN13: {
...achievementData.CHALLENGE_BN13,
Icon: "BN13+",
Visible: () => hasAccessToSF(13),
Visible: () => canAccessBitNodeFeature(13),
Condition: () =>
Player.bitNodeN === 13 &&
bitNodeFinishedState() &&
isBitNodeFinished() &&
!Player.augmentations.some((a) => a.name === AugmentationName.StaneksGift1),
},
DEVMENU: {

View File

@@ -1,26 +1,22 @@
import React from "react";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import { Theme } from "@mui/material/styles";
import { AchievementList } from "./AchievementList";
import { achievements } from "./Achievements";
import { Typography } from "@mui/material";
import { Player } from "@player";
import { makeStyles } from "tss-react/mui";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: 50,
padding: theme.spacing(2),
userSelect: "none",
},
}),
);
const useStyles = makeStyles()((theme: Theme) => ({
root: {
width: 50,
padding: theme.spacing(2),
userSelect: "none",
},
}));
export function AchievementsRoot(): JSX.Element {
const classes = useStyles();
const { classes } = useStyles();
return (
<div className={classes.root} style={{ width: "90%" }}>
<Typography variant="h4">Achievements</Typography>

View File

@@ -195,6 +195,12 @@ export class Augmentation {
// The Player/Person classes
mults: Multipliers = defaultMultipliers();
// Amount of money given to the Player when prestiging with this augmentation.
startingMoney: number;
// Array of programs to be given to the player when prestiging with this augmentation.
programs: CompletedProgramName[];
// Factions that offer this aug.
factions: FactionName[] = [];
@@ -219,6 +225,9 @@ export class Augmentation {
if (mult) this.mults[multName] = mult;
}
this.startingMoney = params.startingMoney ?? 0;
this.programs = params.programs ?? [];
if (params.stats === undefined)
this.stats = generateStatsDescription(this.mults, params.programs, params.startingMoney);
else this.stats = params.stats;

View File

@@ -13,11 +13,26 @@ import { Page } from "../ui/Router";
import { mergeMultipliers } from "../PersonObjects/Multipliers";
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
const soaAugmentationNames = [
AugmentationName.BeautyOfAphrodite,
AugmentationName.ChaosOfDionysus,
AugmentationName.FloodOfPoseidon,
AugmentationName.HuntOfArtemis,
AugmentationName.KnowledgeOfApollo,
AugmentationName.MightOfAres,
AugmentationName.TrickeryOfHermes,
AugmentationName.WKSharmonizer,
AugmentationName.WisdomOfAthena,
];
export function getBaseAugmentationPriceMultiplier(): number {
return CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][Player.sourceFileLvl(11)];
}
export function getGenericAugmentationPriceMultiplier(): number {
return Math.pow(getBaseAugmentationPriceMultiplier(), Player.queuedAugmentations.length);
const queuedNonSoAAugmentationList = Player.queuedAugmentations.filter((augmentation) => {
return !soaAugmentationNames.includes(augmentation.name);
});
return Math.pow(getBaseAugmentationPriceMultiplier(), queuedNonSoAAugmentationList.length);
}
export function applyAugmentation(aug: PlayerOwnedAugmentation, reapply = false): void {
@@ -111,7 +126,7 @@ export function getAugCost(aug: Augmentation): AugmentationCosts {
const multiplier = Math.pow(CONSTANTS.NeuroFluxGovernorLevelMult, aug.getLevel());
repCost = aug.baseRepRequirement * multiplier * currentNodeMults.AugmentationRepCost;
moneyCost = aug.baseCost * multiplier * currentNodeMults.AugmentationMoneyCost;
moneyCost *= getBaseAugmentationPriceMultiplier() ** Player.queuedAugmentations.length;
moneyCost *= getGenericAugmentationPriceMultiplier();
break;
}
// SOA Augments use a unique cost method
@@ -124,17 +139,6 @@ export function getAugCost(aug: Augmentation): AugmentationCosts {
case AugmentationName.TrickeryOfHermes:
case AugmentationName.WKSharmonizer:
case AugmentationName.WisdomOfAthena: {
const soaAugmentationNames = [
AugmentationName.BeautyOfAphrodite,
AugmentationName.ChaosOfDionysus,
AugmentationName.FloodOfPoseidon,
AugmentationName.HuntOfArtemis,
AugmentationName.KnowledgeOfApollo,
AugmentationName.MightOfAres,
AugmentationName.TrickeryOfHermes,
AugmentationName.WKSharmonizer,
AugmentationName.WisdomOfAthena,
];
const soaAugCount = soaAugmentationNames.filter((augName) => Player.hasAugmentation(augName)).length;
moneyCost = aug.baseCost * Math.pow(CONSTANTS.SoACostMult, soaAugCount);
repCost = aug.baseRepRequirement * Math.pow(CONSTANTS.SoARepMult, soaAugCount);

View File

@@ -1013,9 +1013,9 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
moneyCost: 1e6,
info:
"Extra-ocular neurons taken from old martial art master. Injecting them gives the user the ability to " +
"predict the enemy's attack before they even know it themselves.",
"predict the enemy's movement.",
stats:
"This augmentation makes the Slash minigame easier by showing you via an indicator when the slash is coming.",
"This augmentation makes the Slash minigame easier by showing you via an indicator when the sentinel is distracted.",
isSpecial: true,
factions: [FactionName.ShadowsOfAnarchy],
},

View File

@@ -28,9 +28,10 @@ function customFormatPercent(value: number): string {
}
function BitNodeModifiedStats(props: IBitNodeModifiedStatsProps): React.ReactElement {
// If player doesn't have SF5 or if the property isn't affected by BitNode mults
if (props.mult === 1 || Player.sourceFileLvl(5) === 0)
// If the player doesn't have access to SF5 feature or if the property isn't affected by BitNode mults
if (props.mult === 1 || (Player.bitNodeN !== 5 && Player.sourceFileLvl(5) === 0)) {
return <Typography color={props.color}>{customFormatPercent(props.base)}</Typography>;
}
return (
<Typography color={props.color}>

View File

@@ -36,23 +36,21 @@ export function initBitNodes() {
"The original BitNode",
(
<>
The first BitNode created by the Enders to imprison the minds of humans. It became the prototype and
testing-grounds for all of the BitNodes that followed.
This is the first BitNode created by the Enders to imprison the minds of humans. It became the prototype and
testing ground for all of the BitNodes that followed.
<br />
<br />
This is the first BitNode that you play through. It has no special modifications or mechanics.
<br />
<br />
Destroying this BitNode will give you Source-File 1, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets the player start with 32GB of RAM on his/her home computer
when entering a new BitNode, and also increases all of the player's multipliers by:
<br />
<br />
Level 1: 16%
<br />
Level 2: 24%
<br />
Level 3: 28%
Destroying this BitNode will give you Source-File 1, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File lets the player start with 32GB of RAM on their home computer
when entering a new BitNode and increases all of the player's multipliers by:
<ul>
<li>Level 1: 16%</li>
<li>Level 2: 24%</li>
<li>Level 3: 28%</li>
</ul>
</>
),
);
@@ -60,36 +58,30 @@ export function initBitNodes() {
2,
0,
"Rise of the Underworld",
"From the shadows, they rose", //Gangs
"From the shadows, they rose",
(
<>
From the shadows, they rose.
<br />
<br />
Organized crime groups quickly filled the void of power left behind from the collapse of Western government in
the 2050s. As society and civilization broke down, people quickly succumbed to the innate human impulse of evil
and savagery. The organized crime factions quickly rose to the top of the modern world.
<br />
<br />
Certain Factions ({FactionName.SlumSnakes}, {FactionName.Tetrads}, {FactionName.TheSyndicate},{" "}
{FactionName.TheDarkArmy}, {FactionName.SpeakersForTheDead}, {FactionName.NiteSec}, {FactionName.TheBlackHand}
) give the player the ability to form and manage their own gang, which can earn the player money and reputation
with the corresponding Faction. Gangs offer more Augmentations than Factions, and in BitNode-2 offer a way to
destroy the BitNode.
Certain factions ({FactionName.SlumSnakes}, {FactionName.Tetrads}, {FactionName.TheSyndicate},{" "}
{FactionName.TheDarkArmy}, {FactionName.SpeakersForTheDead}, {FactionName.NiteSec}, and{" "}
{FactionName.TheBlackHand}) give the player the ability to form and manage their own gang, which can earn the
player money and reputation with the corresponding faction. The gang faction offers more augmentations than
other factions, and in BitNode-2, it offers a way to destroy the BitNode.
<br />
<br />
<br />
Destroying this BitNode will give you Source-File 2, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes once your karma
Destroying this BitNode will give you Source-File 2, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File allows you to form gangs in other BitNodes once your karma
decreases to a certain value. It also increases your crime success rate, crime money, and charisma multipliers
by:
<br />
<br />
Level 1: 24%
<br />
Level 2: 36%
<br />
Level 3: 42%
<ul>
<li>Level 1: 24%</li>
<li>Level 2: 36%</li>
<li>Level 3: 42%</li>
</ul>
</>
),
);
@@ -103,25 +95,24 @@ export function initBitNodes() {
Our greatest illusion is that a healthy society can revolve around a single-minded pursuit of wealth.
<br />
<br />
Sometime in the early 21st century economic and political globalization turned the world into a corporatocracy,
Sometime in the early 21st century, economic and political globalization turned the world into a corporatocracy,
and it never looked back. Now, the privileged elite will happily bankrupt their own countrymen, decimate their
own community, and evict their neighbors from houses in their desperate bid to increase their wealth.
<br />
<br />
In this BitNode you can create and manage your own corporation. Running a successful corporation has the
potential of generating massive profits.
In this BitNode, you can create and manage your own corporation. Running a successful corporation has the
potential to generate massive profits.
<br />
<br />
Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although some
Destroying this BitNode will give you Source-File 3, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although some
BitNodes will disable this mechanic) and level 3 permanently unlocks the full API. This Source-File also
increases your charisma and company salary multipliers by:
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
<ul>
<li>Level 1: 8%</li>
<li>Level 2: 12%</li>
<li>Level 3: 14%</li>
</ul>
</>
),
);
@@ -132,24 +123,23 @@ export function initBitNodes() {
"The Man and the Machine",
(
<>
The Singularity has arrived. The human race is gone, replaced by artificially superintelligent beings that are
more machine than man. <br />
The Singularity has arrived. The human race is gone, replaced by artificially super intelligent beings that are
more machine than man.
<br />
<br />
In this BitNode you will gain access to a new set of Netscript Functions known as Singularity Functions. These
In this BitNode, you will gain access to a new set of Netscript functions known as Singularity functions. These
functions allow you to control most aspects of the game through scripts, including working for
factions/companies, purchasing/installing Augmentations, and creating programs.
factions/companies, purchasing/installing augmentations, and creating programs.
<br />
<br />
Destroying this BitNode will give you Source-File 4, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File lets you access and use the Singularity Functions in other
Destroying this BitNode will give you Source-File 4, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File lets you access and use the Singularity functions in other
BitNodes. Each level of this Source-File reduces the RAM cost of singularity functions:
<br />
Level 1: 16x
<br />
Level 2: 4x
<br />
Level 3: 1x
<ul>
<li>Level 1: 16x</li>
<li>Level 2: 4x</li>
<li>Level 3: 1x</li>
</ul>
</>
),
);
@@ -165,21 +155,20 @@ export function initBitNodes() {
that couldn't be modeled by 1's and 0's. They were wrong.
<br />
<br />
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
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. Higher Intelligence levels will boost your production for many
actions in the game. <br />
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:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
<ul>
<li>Level 1: 8%</li>
<li>Level 2: 12%</li>
<li>Level 3: 14%</li>
</ul>
</>
),
);
@@ -192,26 +181,24 @@ export function initBitNodes() {
<>
In the middle of the 21st century, {FactionName.OmniTekIncorporated} began designing and manufacturing advanced
synthetic androids, or Synthoids for short. They achieved a major technological breakthrough in the sixth
generation of their Synthoid design, called MK-VI, by developing a hyperintelligent AI. Many argue that this was
the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more
generation of their Synthoid design, called MK-VI, by developing a hyper-intelligent AI. Many argue that this
was the first sentient AI ever created. This resulted in Synthoid models that were stronger, faster, and more
intelligent than the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the {FactionName.Bladeburners} Division at the NSA, which provides a
In this BitNode, you will be able to access the {FactionName.Bladeburners} division at the NSA, which provides a
new mechanic for progression.
<br />
<br />
Destroying this BitNode will give you Source-File 6, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the NSA's {FactionName.Bladeburners} Division
in other BitNodes. In addition, this Source-File will raise both the level and experience gain rate of all your
combat stats by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
Destroying this BitNode will give you Source-File 6, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File allows you to access the NSA's {FactionName.Bladeburners}{" "}
division in other BitNodes. In addition, this Source-File will raise both the level and experience gain rate of
all your combat stats by:
<ul>
<li>Level 1: 8%</li>
<li>Level 2: 12%</li>
<li>Level 3: 14%</li>
</ul>
</>
),
);
@@ -225,25 +212,23 @@ export function initBitNodes() {
In the middle of the 21st century, you were doing cutting-edge work at {FactionName.OmniTekIncorporated} as part
of the AI design team for advanced synthetic androids, or Synthoids for short. You helped achieve a major
technological breakthrough in the sixth generation of the company's Synthoid design, called MK-VI, by developing
a hyperintelligent AI. Many argue that this was the first sentient AI ever created. This resulted in Synthoid
a hyper-intelligent AI. Many argue that this was the first sentient AI ever created. This resulted in Synthoid
models that were stronger, faster, and more intelligent than the humans that had created them.
<br />
<br />
In this BitNode you will be able to access the {FactionName.Bladeburners} API, which allows you to access{" "}
In this BitNode, you will be able to access the {FactionName.Bladeburners} API, which allows you to access{" "}
{FactionName.Bladeburners} functionality through Netscript.
<br />
<br />
Destroying this BitNode will give you Source-File 7, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File allows you to access the {FactionName.Bladeburners} Netscript API
in other BitNodes. In addition, this Source-File will increase all of your {FactionName.Bladeburners}{" "}
Destroying this BitNode will give you Source-File 7, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File allows you to access the {FactionName.Bladeburners} Netscript
API in other BitNodes. In addition, this Source-File will increase all of your {FactionName.Bladeburners}{" "}
multipliers by:
<br />
<br />
Level 1: 8%
<br />
Level 2: 12%
<br />
Level 3: 14%
<ul>
<li>Level 1: 8%</li>
<li>Level 2: 12%</li>
<li>Level 3: 14%</li>
</ul>
</>
),
);
@@ -258,33 +243,24 @@ export function initBitNodes() {
<br />
<br />
In this BitNode:
<br />
<br />
You start with $250 million
<br />
You start with a WSE membership and access to the TIX API
<br />
You are able to short stocks and place different types of orders (limit/stop)
<br />
<br />
Destroying this BitNode will give you Source-File 8, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanent access to WSE and TIX API
<br />
Level 2: Ability to short stocks in other BitNodes
<br />
Level 3: Ability to use limit/stop orders in other BitNodes
<br />
<br />
<ul>
<li>You start with $250 million.</li>
<li>You start with a WSE membership and access to the TIX API.</li>
<li>You can short stocks and place different types of orders (limit/stop).</li>
</ul>
Destroying this BitNode will give you Source-File 8, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File grants the following benefits:
<ul>
<li>Level 1: Permanent access to WSE and TIX API</li>
<li>Level 2: Ability to short stocks in other BitNodes</li>
<li>Level 3: Ability to use limit/stop orders in other BitNodes</li>
</ul>
This Source-File also increases your hacking growth multipliers by:
<br />
Level 1: 12%
<br />
Level 2: 18%
<br />
Level 3: 21%
<ul>
<li>Level 1: 12%</li>
<li>Level 2: 18%</li>
<li>Level 3: 21%</li>
</ul>
</>
),
);
@@ -297,36 +273,31 @@ export function initBitNodes() {
<>
When {FactionName.FulcrumSecretTechnologies} released their open-source Linux distro Chapeau, it quickly became
the OS of choice for the underground hacking community. Chapeau became especially notorious for powering the
Hacknet, a global, decentralized network used for nefarious purposes. {FactionName.FulcrumSecretTechnologies}{" "}
quickly abandoned the project and dissociated themselves from it.
Hacknet, which is a global, decentralized network used for nefarious purposes.{" "}
{FactionName.FulcrumSecretTechnologies} quickly abandoned the project and dissociated themselves from it.
<br />
<br />
This BitNode unlocks the Hacknet Server, an upgraded version of the Hacknet Node. Hacknet Servers generate
hashes, which can be spent on a variety of different upgrades.
<br />
<br />
Destroying this BitNode will give you Source-File 9, or if you already have this Source-File it will upgrade its
level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: Permanently unlocks the Hacknet Server in other BitNodes
<br />
Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode
<br />
Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
This BitNode unlocks the Hacknet Server, which is an upgraded version of the Hacknet Node. Hacknet Servers
generate hashes, which can be spent on a variety of different upgrades.
<br />
<br />
Destroying this BitNode will give you Source-File 9, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File grants the following benefits:
<ul>
<li>Level 1: Permanently unlocks the Hacknet Server in other BitNodes</li>
<li>Level 2: You start with 128GB of RAM on your home computer when entering a new BitNode</li>
<li>Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode</li>
</ul>
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
Augmentations)
augmentations)
<br />
<br />
This Source-File also increases hacknet production and reduces hacknet costs by:
<br />
Level 1: 12%
<br />
Level 2: 18%
<br />
Level 3: 21%
<ul>
<li>Level 1: 12%</li>
<li>Level 2: 18%</li>
<li>Level 3: 21%</li>
</ul>
</>
),
);
@@ -343,21 +314,20 @@ export function initBitNodes() {
achieved immortality - at least for those that could afford it.
<br />
<br />
This BitNode unlocks Sleeve and grafting technologies. Sleeve technology allows you to:
<br />
<br />
1. Grafting: Visit VitaLife in New Tokyo to be able to obtain Augmentations without needing to install
<br />
2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks
synchronously.
<br />
<br />
Grafting technology allows you to graft Augmentations, which is an alternative way of installing Augmentations.
<br />
<br />
Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will upgrade
its level up to a maximum of 3. This Source-File unlocks Sleeve technology, and the Grafting API in other
BitNodes. Each level of this Source-File also grants you a Duplicate Sleeve
This BitNode unlocks Sleeve and Grafting technology:
<ul>
<li>
Sleeve: Duplicate your consciousness into Synthoids, allowing you to perform different tasks asynchronously.
You cannot buy Sleeves outside this BitNode.
</li>
<li>
Grafting: Visit VitaLife in New Tokyo to get access to this technology. It allows you to graft
augmentations, which is an alternative way of installing augmentations.
</li>
</ul>
Destroying this BitNode will give you Source-File 10, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File unlocks Sleeve and Grafting API in other BitNodes. Each level
of this Source-File also grants you a Sleeve.
</>
),
);
@@ -369,9 +339,9 @@ export function initBitNodes() {
(
<>
The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around
the world. It was this period of disorder that eventually lead to the governmental reformation of many global
the world. It was this period of disorder that eventually led to the governmental reformation of many global
superpowers, most notably the USA and China. But just as the world was slowly beginning to recover from these
dark times, financial catastrophe hit.
dark times, financial catastrophes hit.
<br />
<br />
In many countries, the high cost of trying to deal with the civil disorder bankrupted the governments. In all of
@@ -380,27 +350,21 @@ export function initBitNodes() {
the world is slowly crumbling in the middle of the biggest economic crisis of all time.
<br />
<br />
Destroying this BitNode will give you Source-File 11, or if you already have this Source-File it will upgrade
Destroying this BitNode will give you Source-File 11, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File makes it so that company favor increases BOTH the player's
salary and reputation gain rate at that company by 1% per favor (rather than just the reputation gain). This
Source-File also increases the player's company salary and reputation gain multipliers by:
<br />
<br />
Level 1: 32%
<br />
Level 2: 48%
<br />
Level 3: 56%
<br />
<br />
It also reduces the price increase for every aug bought by:
<br />
<br />
Level 1: 4%
<br />
Level 2: 6%
<br />
Level 3: 7%
<ul>
<li>Level 1: 32%</li>
<li>Level 2: 48%</li>
<li>Level 3: 56%</li>
</ul>
It also reduces the price increase for every augmentation bought by:
<ul>
<li>Level 1: 4%</li>
<li>Level 2: 6%</li>
<li>Level 3: 7%</li>
</ul>
</>
),
);
@@ -411,13 +375,13 @@ export function initBitNodes() {
"Repeat.",
(
<>
To iterate is human, to recurse divine.
To iterate is human; to recurse, divine.
<br />
<br />
Every time this BitNode is destroyed, it becomes slightly harder. Destroying this BitNode will give you
Source-File 12, or if you already have this Source-File it will upgrade its level. There is no maximum level for
Source-File 12. Each level of Source-File 12 lets you start any BitNodes with NeuroFlux Governor equal to the
level of this source file.
Source-File 12, or if you already have this Source-File, it will upgrade its level. There is no maximum level
for Source-File 12. Each level of Source-File 12 lets you start any BitNodes with NeuroFlux Governor equal to
the level of this source file.
</>
),
);
@@ -428,15 +392,15 @@ export function initBitNodes() {
"1 step back, 2 steps forward",
(
<>
With the invention of Augmentations in the 2040s a religious group known as the{" "}
With the invention of augmentations in the 2040s, a religious group known as the{" "}
{FactionName.ChurchOfTheMachineGod} has rallied far more support than anyone would have hoped.
<br />
<br />
Their leader, Allison "Mother" Stanek is said to have created her own Augmentation whose power goes beyond any
Their leader, Allison "Mother" Stanek is said to have created her own augmentation whose power goes beyond any
other. Find her in {CityName.Chongqing} and gain her trust.
<br />
<br />
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File it will upgrade
Destroying this BitNode will give you Source-File 13, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File lets the {FactionName.ChurchOfTheMachineGod} appear in other
BitNodes.
<br />
@@ -459,24 +423,19 @@ export function initBitNodes() {
networks by controlling the open space in the 'net!
<br />
<br />
Destroying this BitNode will give you Source-File 14, or if you already have this Source-File it will upgrade
Destroying this BitNode will give you Source-File 14, or if you already have this Source-File, it will upgrade
its level up to a maximum of 3. This Source-File grants the following benefits:
<br />
<br />
Level 1: 100% increased stat multipliers from Node Power
<br />
Level 2: Permanently unlocks the go.cheat API
<br />
Level 3: 25% additive increased success rate for the go.cheat API
<br />
<br />
<ul>
<li>Level 1: 100% increased stat multipliers from Node Power</li>
<li>Level 2: Permanently unlocks the go.cheat API</li>
<li>Level 3: 25% additive increased success rate for the go.cheat API</li>
</ul>
This Source-File also increases the maximum favor you can gain for each faction from IPvGO to:
<br />
Level 1: 80
<br />
Level 2: 100
<br />
Level 3: 120
<ul>
<li>Level 1: 80</li>
<li>Level 2: 100</li>
<li>Level 3: 120</li>
</ul>
</>
),
);

View File

@@ -1,5 +1,5 @@
import { PartialRecord, getRecordEntries } from "../Types/Record";
import { clampNumber } from "../utils/helpers/clampNumber";
/**
* Bitnode multipliers influence the difficulty of different aspects of the game.
* Each Bitnode has a different theme/strategy to achieving the end goal, so these multipliers will can help drive the
@@ -173,7 +173,7 @@ export class BitNodeMultipliers {
CorporationDivisions = 1;
constructor(a: PartialRecord<keyof BitNodeMultipliers, number> = {}) {
for (const [key, value] of getRecordEntries(a)) this[key] = value;
for (const [key, value] of getRecordEntries(a)) this[key] = clampNumber(value);
}
}

View File

@@ -0,0 +1,11 @@
import { GetServer } from "../Server/AllServers";
import { Server } from "../Server/Server";
import { SpecialServers } from "../Server/data/SpecialServers";
export function isBitNodeFinished(): boolean {
const wd = GetServer(SpecialServers.WorldDaemon);
if (!(wd instanceof Server)) {
throw new Error("WorldDaemon is not a normal server. This is a bug. Please contact developers.");
}
return wd.backdoorInstalled;
}

View File

@@ -3,8 +3,7 @@ import { BitNodes } from "../BitNode";
import { PortalModal } from "./PortalModal";
import { CinematicText } from "../../ui/React/CinematicText";
import { Player } from "@player";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import { makeStyles } from "tss-react/mui";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import Tooltip from "@mui/material/Tooltip";
@@ -12,33 +11,31 @@ import { Settings } from "../../Settings/Settings";
import Button from "@mui/material/Button";
import { CompletedProgramName } from "@enums";
const useStyles = makeStyles(() =>
createStyles({
portal: {
cursor: "pointer",
fontFamily: "inherit",
fontSize: "1rem",
fontWeight: "bold",
lineHeight: 1,
padding: 0,
"&:hover": {
color: "#fff",
},
const useStyles = makeStyles()(() => ({
portal: {
cursor: "pointer",
fontFamily: "inherit",
fontSize: "1rem",
fontWeight: "bold",
lineHeight: 1,
padding: 0,
"&:hover": {
color: "#fff",
},
level0: {
color: Settings.theme.bnlvl0,
},
level1: {
color: Settings.theme.bnlvl1,
},
level2: {
color: Settings.theme.bnlvl2,
},
level3: {
color: Settings.theme.bnlvl3,
},
}),
);
},
level0: {
color: Settings.theme.bnlvl0,
},
level1: {
color: Settings.theme.bnlvl1,
},
level2: {
color: Settings.theme.bnlvl2,
},
level3: {
color: Settings.theme.bnlvl3,
},
}));
interface IPortalProps {
n: number;
@@ -48,7 +45,7 @@ interface IPortalProps {
}
function BitNodePortal(props: IPortalProps): React.ReactElement {
const [portalOpen, setPortalOpen] = useState(false);
const classes = useStyles();
const { classes } = useStyles();
const bitNode = BitNodes[`BitNode${props.n}`];
if (bitNode == null) {
return <>O</>;

View File

@@ -28,19 +28,18 @@ export function PortalModal(props: IProps): React.ReactElement {
<Typography variant="h4">
BitNode-{props.n}: {bitNode.name}
</Typography>
<Typography variant="h5">{bitNode.desc}</Typography>
<br />
<Typography>
Source-File Level: {props.level} / {maxSourceFileLevel}
</Typography>
<br />
<br />
<Typography> Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}</Typography>
<br />
<br />
<Typography>{bitNode.info}</Typography>
<Typography component="div">{bitNode.info}</Typography>
<BitnodeMultiplierDescription n={props.n} level={newLevel} />
<br />
<br />
<Button
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
autoFocus={true}

View File

@@ -8,6 +8,7 @@ import { BladeburnerConstants } from "../data/Constants";
import { calculateIntelligenceBonus } from "../../PersonObjects/formulas/intelligence";
import { BladeMultName } from "../Enums";
import { getRecordKeys } from "../../Types/Record";
import { clampNumber } from "../../utils/helpers/clampNumber";
export interface ActionParams {
desc: string;
@@ -146,9 +147,16 @@ export abstract class ActionClass {
let high = real + diff;
const city = bladeburner.getCurrentCity();
let r = city.pop / city.popEst;
if (Number.isNaN(r)) r = 0;
if (r < 1) low *= r;
else high *= r;
if (Number.isNaN(r)) {
r = 0;
}
if (r < 1) {
low *= r;
} else {
// We need to "clamp" r with "clampNumber" (not "clamp"), otherwise (high *= r) may be NaN. This happens when the
// action is Raid, popEst=0, and comms=0.
high *= clampNumber(r);
}
return [clamp(low), clamp(high)];
}

View File

@@ -34,18 +34,20 @@ export class BlackOperation extends ActionClass {
if (bladeburner.rank < this.reqdRank) return { error: "Insufficient rank" };
return { available: true };
}
// To be implemented by subtypes
getActionTimePenalty(): number {
return 1.5;
}
getPopulationSuccessFactor(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
getPopulationSuccessFactor(): number {
return 1;
}
getChaosSuccessFactor(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
getChaosSuccessFactor(): number {
return 1;
}
getTeamSuccessBonus = operationTeamSuccessBonus;
getActionTypeSkillSuccessBonus = operationSkillSuccessBonus;
}

View File

@@ -32,6 +32,7 @@ export class Operation extends LevelableActionClass {
// These functions are shared between operations and blackops, so they are defined outside of Operation
getTeamSuccessBonus = operationTeamSuccessBonus;
getActionTypeSkillSuccessBonus = operationSkillSuccessBonus;
getChaosSuccessFactor(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
@@ -45,7 +46,9 @@ export class Operation extends LevelableActionClass {
return 1;
}
getSuccessChance(inst: Bladeburner, person: Person, params: SuccessChanceParams) {
if (this.name == BladeOperationName.raid && inst.getCurrentCity().comms <= 0) return 0;
if (this.name === BladeOperationName.raid && inst.getCurrentCity().comms <= 0) {
return 0;
}
return ActionClass.prototype.getSuccessChance.call(this, inst, person, params);
}
@@ -57,6 +60,7 @@ export class Operation extends LevelableActionClass {
toJSON(): IReviverValue {
return this.save("Operation", "teamCount");
}
loadData(loadedObject: Operation): void {
this.teamCount = clampInteger(loadedObject.teamCount, 0);
LevelableActionClass.prototype.loadData.call(this, loadedObject);
@@ -73,6 +77,7 @@ constructorsForReviver.Operation = Operation;
export const operationSkillSuccessBonus = (inst: Bladeburner) => {
return inst.getSkillMult(BladeMultName.successChanceOperation);
};
export function operationTeamSuccessBonus(this: Operation | BlackOperation, inst: Bladeburner) {
if (this.teamCount && this.teamCount > 0) {
this.teamCount = Math.min(this.teamCount, inst.teamSize);

View File

@@ -17,7 +17,7 @@ import {
} from "@enums";
import { getKeyList } from "../utils/helpers/getKeyList";
import { constructorsForReviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { formatNumberNoSuffix } from "../ui/formatNumber";
import { formatHp, formatNumberNoSuffix, formatSleeveShock } from "../ui/formatNumber";
import { Skills } from "./data/Skills";
import { City } from "./City";
import { Player } from "@player";
@@ -46,6 +46,8 @@ import { clampInteger, clampNumber } from "../utils/helpers/clampNumber";
import { parseCommand } from "../Terminal/Parser";
import { BlackOperations } from "./data/BlackOperations";
import { GeneralActions } from "./data/GeneralActions";
import { PlayerObject } from "../PersonObjects/Player/PlayerObject";
import { Sleeve } from "../PersonObjects/Sleeve/Sleeve";
export const BladeburnerPromise: PromisePair<number> = { promise: null, resolve: null };
@@ -115,7 +117,6 @@ export class Bladeburner {
}
calculateStaminaPenalty(): number {
if (this.stamina === this.maxStamina) return 1;
return Math.min(1, this.stamina / (0.5 * this.maxStamina));
}
@@ -124,7 +125,7 @@ export class Bladeburner {
startAction(actionId: ActionIdentifier | null): Attempt<{ message: string }> {
if (!actionId) {
this.resetAction();
return { success: true, message: "Stopped current bladeburner action" };
return { success: true, message: "Stopped current Bladeburner action" };
}
if (!Player.hasAugmentation(AugmentationName.BladesSimulacrum, true)) Player.finishWork(true);
const action = this.getActionObject(actionId);
@@ -854,6 +855,22 @@ export class Bladeburner {
}
completeAction(person: Person, actionIdent: ActionIdentifier, isPlayer = true): WorkStats {
const currentHp = person.hp.current;
const getExtraLogAfterTakingDamage = (damage: number) => {
let extraLog = "";
if (currentHp <= damage) {
if (person instanceof PlayerObject) {
extraLog += ` ${person.whoAmI()} was hospitalized. Current HP is ${formatHp(person.hp.current)}.`;
} else if (person instanceof Sleeve) {
extraLog += ` ${person.whoAmI()} was shocked. Current shock is ${formatSleeveShock(
person.shock,
)}. Current HP is ${formatHp(person.hp.current)}.`;
}
} else {
extraLog += ` HP reduced from ${formatHp(currentHp)} to ${formatHp(person.hp.current)}.`;
}
return extraLog;
};
let retValue = newWorkStats();
const action = this.getActionObject(actionIdent);
switch (action.type) {
@@ -899,12 +916,12 @@ export class Bladeburner {
this.changeRank(person, gain);
if (isOperation && this.logging.ops) {
this.log(
`${person.whoAmI()}: ${action.name} successfully completed! Gained ${formatBigNumber(gain)} rank`,
`${person.whoAmI()}: ${action.name} successfully completed! Gained ${formatBigNumber(gain)} rank.`,
);
} else if (!isOperation && this.logging.contracts) {
this.log(
`${person.whoAmI()}: ${action.name} contract successfully completed! Gained ` +
`${formatBigNumber(gain)} rank and ${formatMoney(moneyGain)}`,
`${formatBigNumber(gain)} rank and ${formatMoney(moneyGain)}.`,
);
}
}
@@ -930,15 +947,15 @@ export class Bladeburner {
}
let logLossText = "";
if (loss > 0) {
logLossText += "Lost " + formatNumberNoSuffix(loss, 3) + " rank. ";
logLossText += ` Lost ${formatNumberNoSuffix(loss, 3)} rank.`;
}
if (damage > 0) {
logLossText += "Took " + formatNumberNoSuffix(damage, 0) + " damage.";
logLossText += ` Took ${formatNumberNoSuffix(damage, 0)} damage.${getExtraLogAfterTakingDamage(damage)}`;
}
if (isOperation && this.logging.ops) {
this.log(`${person.whoAmI()}: ` + action.name + " failed! " + logLossText);
this.log(`${person.whoAmI()}: ${action.name} failed!${logLossText}`);
} else if (!isOperation && this.logging.contracts) {
this.log(`${person.whoAmI()}: ` + action.name + " contract failed! " + logLossText);
this.log(`${person.whoAmI()}: ${action.name} contract failed!${logLossText}`);
}
isOperation ? this.completeOperation(false) : this.completeContract(false, action);
}
@@ -977,7 +994,9 @@ export class Bladeburner {
teamLossMax = Math.ceil(teamCount / 2);
if (this.logging.blackops) {
this.log(`${person.whoAmI()}: ${action.name} successful! Gained ${formatNumberNoSuffix(rankGain, 1)} rank`);
this.log(
`${person.whoAmI()}: ${action.name} successful! Gained ${formatNumberNoSuffix(rankGain, 1)} rank.`,
);
}
} else {
retValue = this.getActionStats(action, person, false);
@@ -1003,7 +1022,7 @@ export class Bladeburner {
`${person.whoAmI()}: ${action.name} failed! Lost ${formatNumberNoSuffix(
rankLoss,
1,
)} rank and took ${formatNumberNoSuffix(damage, 0)} damage`,
)} rank. Took ${formatNumberNoSuffix(damage, 0)} damage.${getExtraLogAfterTakingDamage(damage)}`,
);
}
}
@@ -1026,7 +1045,7 @@ export class Bladeburner {
this.teamLost += losses;
if (this.logging.blackops) {
this.log(
`${person.whoAmI()}: You lost ${formatNumberNoSuffix(losses, 0)} team members during ${action.name}`,
`${person.whoAmI()}: You lost ${formatNumberNoSuffix(losses, 0)} team members during ${action.name}.`,
);
}
}
@@ -1059,7 +1078,7 @@ export class Bladeburner {
formatExp(agiExpGain) +
" agi exp, " +
formatBigNumber(staminaGain) +
" max stamina",
" max stamina.",
);
}
break;
@@ -1089,7 +1108,7 @@ export class Bladeburner {
`${person.whoAmI()}: ` +
`Field analysis completed. Gained ${formatBigNumber(rankGain)} rank, ` +
`${formatExp(hackingExpGain)} hacking exp, and ` +
`${formatExp(charismaExpGain)} charisma exp`,
`${formatExp(charismaExpGain)} charisma exp.`,
);
}
break;
@@ -1105,7 +1124,7 @@ export class Bladeburner {
`${person.whoAmI()}: ` +
"Successfully recruited a team member! Gained " +
formatExp(expGain) +
" charisma exp",
" charisma exp.",
);
}
} else {
@@ -1116,7 +1135,7 @@ export class Bladeburner {
`${person.whoAmI()}: ` +
"Failed to recruit a team member. Gained " +
formatExp(expGain) +
" charisma exp",
" charisma exp.",
);
}
}
@@ -1132,7 +1151,7 @@ export class Bladeburner {
this.log(
`${person.whoAmI()}: Diplomacy completed. Chaos levels in the current city fell by ${formatPercent(
1 - eff,
)}`,
)}.`,
);
}
break;
@@ -1140,14 +1159,22 @@ export class Bladeburner {
case BladeGeneralActionName.hyperbolicRegen: {
person.regenerateHp(BladeburnerConstants.HrcHpGain);
const currentStamina = this.stamina;
const staminaGain = this.maxStamina * (BladeburnerConstants.HrcStaminaGain / 100);
this.stamina = Math.min(this.maxStamina, this.stamina + staminaGain);
if (this.logging.general) {
this.log(
`${person.whoAmI()}: Rested in Hyperbolic Regeneration Chamber. Restored ${
BladeburnerConstants.HrcHpGain
} HP and gained ${formatStamina(staminaGain)} stamina`,
);
let extraLog = "";
if (Player.hp.current > currentHp) {
extraLog += ` Restored ${formatHp(BladeburnerConstants.HrcHpGain)} HP. Current HP is ${formatHp(
Player.hp.current,
)}.`;
}
if (this.stamina > currentStamina) {
extraLog += ` Restored ${formatStamina(staminaGain)} stamina. Current stamina is ${formatStamina(
this.stamina,
)}.`;
}
this.log(`${person.whoAmI()}: Rested in Hyperbolic Regeneration Chamber.${extraLog}`);
}
break;
}
@@ -1258,13 +1285,16 @@ export class Bladeburner {
calculateMaxStamina(): void {
const baseStamina = Math.pow(this.getEffectiveSkillLevel(Player, "agility"), 0.8);
// Min value of maxStamina is an arbitrarily small positive value. It must not be 0 to avoid NaN stamina penalty.
const maxStamina = clampNumber(
(baseStamina + this.staminaBonus) *
this.getSkillMult(BladeMultName.stamina) *
Player.mults.bladeburner_max_stamina,
0,
1e-9,
);
if (this.maxStamina === maxStamina) return;
if (this.maxStamina === maxStamina) {
return;
}
// If max stamina changed, adjust stamina accordingly
const oldMax = this.maxStamina;
this.maxStamina = maxStamina;
@@ -1431,6 +1461,16 @@ export class Bladeburner {
loadOperationsData(operationsData, bladeburner.operations);
// Regenerate skill multiplier data, which is not included in savedata
bladeburner.updateSkillMultipliers();
// If stamina or maxStamina is invalid, we set both of them to 1 and recalculate them.
if (
!Number.isFinite(bladeburner.stamina) ||
!Number.isFinite(bladeburner.maxStamina) ||
bladeburner.maxStamina === 0
) {
bladeburner.stamina = 1;
bladeburner.maxStamina = 1;
bladeburner.calculateMaxStamina();
}
return bladeburner;
}
}

View File

@@ -0,0 +1,91 @@
import type { Bladeburner } from "../Bladeburner";
import type { Action } from "../Types";
import React from "react";
import { Box, Typography } from "@mui/material";
import { CopyableText } from "../../ui/React/CopyableText";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { StartButton } from "./StartButton";
import { StopButton } from "./StopButton";
import { TeamSizeButton } from "./TeamSizeButton";
import { formatNumberNoSuffix } from "../../ui/formatNumber";
import { BlackOperation, Operation } from "../Actions";
import { BladeburnerConstants } from "../data/Constants";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
interface ActionHeaderProps {
bladeburner: Bladeburner;
action: Action;
rerender: () => void;
}
export function ActionHeader({ bladeburner, action, rerender }: ActionHeaderProps): React.ReactElement {
const isActive = action.name === bladeburner.action?.name;
const computedActionTimeCurrent = Math.min(
bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
bladeburner.actionTimeToComplete,
);
const remainingSeconds = Math.max(
bladeburner.actionTimeToComplete - bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
0,
);
const remainingBonusSeconds = Math.floor(bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond);
/**
* Bladeburner is processed every second. Each time it's processed, we use (up to) 4 bonus seconds and process it as
* if (up to) 5 seconds passed.
* For example, with 20 bonus seconds, we need 5 seconds to use up all those bonus seconds. After 5 seconds, we used
* up 20 bonus seconds and processed Bladeburner as if 25 seconds had passed.
*/
const effectiveBonusSeconds = (remainingBonusSeconds / 4) * 5;
let eta;
if (remainingSeconds <= effectiveBonusSeconds) {
// If we have enough effectiveBonusSeconds, ETA is (remainingSeconds / 5).
eta = Math.floor(remainingSeconds / 5);
} else {
/**
* For example, let's say we start the "Training" action with 20 bonus seconds: remainingSeconds=30;remainingBonusSeconds=20.
* After 5 seconds (remainingBonusSeconds / 4), we processed Bladeburner as if 25 seconds (effectiveBonusSeconds)
* had passed. We still need 5 more seconds (30 - 25 = remainingTime - effectiveBonusSeconds) to complete the action
* at normal speed.
*
* ETA = remainingBonusSeconds / 4 + remainingTime - effectiveBonusSeconds
* = remainingBonusSeconds / 4 + remainingTime - ((remainingBonusSeconds / 4) * 5)
* = remainingTime - remainingBonusSeconds
*/
eta = remainingSeconds - remainingBonusSeconds;
}
const allowTeam = action instanceof Operation || action instanceof BlackOperation;
if (isActive) {
return (
<>
<Box display="flex" flexDirection="row" alignItems="center">
<CopyableText value={action.name} />
<StopButton bladeburner={bladeburner} rerender={rerender} />
</Box>
<Typography>
(IN PROGRESS - {formatNumberNoSuffix(computedActionTimeCurrent, 0)} /{" "}
{formatNumberNoSuffix(bladeburner.actionTimeToComplete, 0)})
</Typography>
<Box display="flex" flexDirection="row" alignItems="center">
<Typography>
{createProgressBarText({
progress: computedActionTimeCurrent / bladeburner.actionTimeToComplete,
})}
</Typography>
<Typography marginLeft="1rem">Remaining time: {convertTimeMsToTimeElapsedString(eta * 1000)}</Typography>
</Box>
</>
);
}
return (
<Box display="flex" flexDirection="row" alignItems="center">
<CopyableText value={action.name} />
<StartButton bladeburner={bladeburner} action={action} rerender={rerender} />
{allowTeam && <TeamSizeButton bladeburner={bladeburner} action={action} />}
</Box>
);
}

View File

@@ -7,6 +7,7 @@ import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { BladeburnerConstants } from "../data/Constants";
import { Contract } from "../Actions";
interface ActionLevelProps {
action: LevelableAction;
@@ -18,6 +19,11 @@ interface ActionLevelProps {
export function ActionLevel({ action, isActive, bladeburner, rerender }: ActionLevelProps): React.ReactElement {
const canIncrease = action.level < action.maxLevel;
const canDecrease = action.level > 1;
const successesNeededForNextLevel = action.getSuccessesNeededForNextLevel(
action instanceof Contract
? BladeburnerConstants.ContractSuccessesPerLevel
: BladeburnerConstants.OperationSuccessesPerLevel,
);
function increaseLevel(): void {
if (!canIncrease) return;
@@ -36,21 +42,7 @@ export function ActionLevel({ action, isActive, bladeburner, rerender }: ActionL
return (
<Box display="flex" flexDirection="row" alignItems="center">
<Box display="flex">
<Tooltip
title={
action.constructor.name === "Contract" ? (
<Typography>
{action.getSuccessesNeededForNextLevel(BladeburnerConstants.ContractSuccessesPerLevel)} successes needed
for next level
</Typography>
) : (
<Typography>
{action.getSuccessesNeededForNextLevel(BladeburnerConstants.OperationSuccessesPerLevel)} successes
needed for next level
</Typography>
)
}
>
<Tooltip title={<Typography>{successesNeededForNextLevel} successes needed for next level</Typography>}>
<Typography>
Level: {action.level} / {action.maxLevel}
</Typography>

View File

@@ -7,72 +7,41 @@ import { Paper, Typography } from "@mui/material";
import { Player } from "@player";
import { formatNumberNoSuffix } from "../../ui/formatNumber";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { TeamSizeButton } from "./TeamSizeButton";
import { CopyableText } from "../../ui/React/CopyableText";
import { SuccessChance } from "./SuccessChance";
import { StartButton } from "./StartButton";
import { useRerender } from "../../ui/React/hooks";
import { ActionHeader } from "./ActionHeader";
interface BlackOpElemProps {
bladeburner: Bladeburner;
blackOp: BlackOperation;
action: BlackOperation;
}
export function BlackOpElem({ bladeburner, blackOp }: BlackOpElemProps): React.ReactElement {
export function BlackOpElem({ bladeburner, action }: BlackOpElemProps): React.ReactElement {
const rerender = useRerender();
const isCompleted = bladeburner.numBlackOpsComplete > blackOp.n;
const isCompleted = bladeburner.numBlackOpsComplete > action.n;
if (isCompleted) {
return (
<Paper sx={{ my: 1, p: 1 }}>
<Typography>{blackOp.name} (COMPLETED)</Typography>
<Typography>{action.name} (COMPLETED)</Typography>
</Paper>
);
}
const isActive = bladeburner.action?.name === blackOp.name;
const actionTime = blackOp.getActionTime(bladeburner, Player);
const hasReqdRank = bladeburner.rank >= blackOp.reqdRank;
const computedActionTimeCurrent = Math.min(
bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
bladeburner.actionTimeToComplete,
);
const actionTime = action.getActionTime(bladeburner, Player);
const hasRequiredRank = bladeburner.rank >= action.reqdRank;
return (
<Paper sx={{ my: 1, p: 1 }}>
{isActive ? (
<>
<CopyableText value={blackOp.name} />
<Typography>
(IN PROGRESS - {formatNumberNoSuffix(computedActionTimeCurrent, 0)} /{" "}
{formatNumberNoSuffix(bladeburner.actionTimeToComplete, 0)})
</Typography>
<Typography>
{createProgressBarText({
progress: computedActionTimeCurrent / bladeburner.actionTimeToComplete,
})}
</Typography>
</>
) : (
<>
<CopyableText value={blackOp.name} />
<StartButton bladeburner={bladeburner} action={blackOp} rerender={rerender} />
<TeamSizeButton action={blackOp} bladeburner={bladeburner} />
</>
)}
<ActionHeader bladeburner={bladeburner} action={action} rerender={rerender}></ActionHeader>
<br />
<Typography whiteSpace={"pre-wrap"}>{action.desc}</Typography>
<br />
<Typography whiteSpace={"pre-wrap"}>{blackOp.desc}</Typography>
<br />
<br />
<Typography color={hasReqdRank ? "primary" : "error"}>
Required Rank: {formatNumberNoSuffix(blackOp.reqdRank, 0)}
<Typography color={hasRequiredRank ? "primary" : "error"}>
Required Rank: {formatNumberNoSuffix(action.reqdRank, 0)}
</Typography>
<br />
<Typography>
<SuccessChance action={blackOp} bladeburner={bladeburner} />
<SuccessChance action={action} bladeburner={bladeburner} />
<br />
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
</Typography>

View File

@@ -1,6 +1,6 @@
import type { Bladeburner } from "../Bladeburner";
import * as React from "react";
import React from "react";
import { Button, Typography } from "@mui/material";
import { FactionName } from "@enums";
import { BlackOpElem } from "./BlackOpElem";
@@ -8,13 +8,25 @@ import { Router } from "../../ui/GameRoot";
import { Page } from "../../ui/Router";
import { CorruptableText } from "../../ui/React/CorruptableText";
import { blackOpsArray } from "../data/BlackOperations";
import { GetServer } from "../../Server/AllServers";
import { SpecialServers } from "../../Server/data/SpecialServers";
import { Server } from "../../Server/Server";
interface BlackOpPageProps {
bladeburner: Bladeburner;
}
function finishBitNode() {
const wd = GetServer(SpecialServers.WorldDaemon);
if (!(wd instanceof Server)) {
throw new Error("WorldDaemon is not a normal server. This is a bug. Please contact developers.");
}
wd.backdoorInstalled = true;
Router.toPage(Page.BitVerse, { flume: false, quick: false });
}
export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactElement {
const blackOps = blackOpsArray.slice(0, bladeburner.numBlackOpsComplete + 1);
const blackOperations = blackOpsArray.slice(0, bladeburner.numBlackOpsComplete + 1).reverse();
return (
<>
@@ -33,13 +45,13 @@ export function BlackOpPage({ bladeburner }: BlackOpPageProps): React.ReactEleme
losses.
</Typography>
{bladeburner.numBlackOpsComplete >= blackOpsArray.length ? (
<Button sx={{ my: 1, p: 1 }} onClick={() => Router.toPage(Page.BitVerse, { flume: false, quick: false })}>
<CorruptableText content="Destroy w0rld_d34mon" spoiler={false}></CorruptableText>
<Button sx={{ my: 1, p: 1 }} onClick={finishBitNode}>
<CorruptableText content="Destroy w0r1d_d43m0n" spoiler={false}></CorruptableText>
</Button>
) : (
<>
{blackOps.map((blackOp) => (
<BlackOpElem key={blackOp.name} bladeburner={bladeburner} blackOp={blackOp} />
{blackOperations.map((blackOperation) => (
<BlackOpElem key={blackOperation.name} bladeburner={bladeburner} action={blackOperation} />
))}
</>
)}

View File

@@ -5,36 +5,33 @@ import { KEY } from "../../utils/helpers/keyCodes";
import { Box, List, ListItem, Paper, TextField, Typography } from "@mui/material";
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import { makeStyles } from "tss-react/mui";
import { useRerender } from "../../ui/React/hooks";
interface ILineProps {
content: React.ReactNode;
}
const useStyles = makeStyles((theme: Theme) =>
createStyles({
textfield: {
margin: theme.spacing(0),
width: "100%",
},
input: {
backgroundColor: theme.colors.backgroundsecondary,
},
nopadding: {
padding: theme.spacing(0),
},
preformatted: {
whiteSpace: "pre-wrap",
margin: theme.spacing(0),
},
list: {
padding: theme.spacing(0),
height: "100%",
},
}),
);
const useStyles = makeStyles()((theme: Theme) => ({
textfield: {
margin: theme.spacing(0),
width: "100%",
},
input: {
backgroundColor: theme.colors.backgroundsecondary,
},
nopadding: {
padding: theme.spacing(0),
},
preformatted: {
whiteSpace: "pre-wrap",
margin: theme.spacing(0),
},
list: {
padding: theme.spacing(0),
height: "100%",
},
}));
function Line(props: ILineProps): React.ReactElement {
return (
@@ -49,7 +46,7 @@ interface IProps {
}
export function Console(props: IProps): React.ReactElement {
const classes = useStyles();
const { classes } = useStyles();
const [command, setCommand] = useState("");
const consoleInput = useRef<HTMLInputElement>(null);
useRerender(1000);

View File

@@ -2,18 +2,15 @@ import type { Bladeburner } from "../Bladeburner";
import type { Contract } from "../Actions/Contract";
import React from "react";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { Player } from "@player";
import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText";
import { ActionLevel } from "./ActionLevel";
import { Autolevel } from "./Autolevel";
import { StartButton } from "./StartButton";
import { formatNumberNoSuffix, formatBigNumber } from "../../ui/formatNumber";
import { formatBigNumber } from "../../ui/formatNumber";
import { Paper, Typography } from "@mui/material";
import { useRerender } from "../../ui/React/hooks";
import { getEnumHelper } from "../../utils/EnumHelper";
import { ActionHeader } from "./ActionHeader";
interface ContractElemProps {
bladeburner: Bladeburner;
@@ -22,41 +19,15 @@ interface ContractElemProps {
export function ContractElem({ bladeburner, action }: ContractElemProps): React.ReactElement {
const rerender = useRerender();
// Temp special return
if (!getEnumHelper("BladeContractName").isMember(action.name)) return <></>;
const isActive = action.name === bladeburner.action?.name;
const computedActionTimeCurrent = Math.min(
bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
bladeburner.actionTimeToComplete,
);
const actionTime = action.getActionTime(bladeburner, Player);
return (
<Paper sx={{ my: 1, p: 1 }}>
{isActive ? (
<>
<CopyableText value={action.name} />
<Typography>
(IN PROGRESS - {formatNumberNoSuffix(computedActionTimeCurrent, 0)} /{" "}
{formatNumberNoSuffix(bladeburner.actionTimeToComplete, 0)})
</Typography>
<Typography>
{createProgressBarText({
progress: computedActionTimeCurrent / bladeburner.actionTimeToComplete,
})}
</Typography>
</>
) : (
<>
<CopyableText value={action.name} />
<StartButton bladeburner={bladeburner} action={action} rerender={rerender} />
</>
)}
<br />
<ActionHeader bladeburner={bladeburner} action={action} rerender={rerender}></ActionHeader>
<br />
<ActionLevel action={action} bladeburner={bladeburner} isActive={isActive} rerender={rerender} />
<br />
<br />
<Typography whiteSpace={"pre-wrap"}>
{action.desc}
<br />

View File

@@ -2,14 +2,12 @@ import type { Bladeburner } from "../Bladeburner";
import type { GeneralAction } from "../Actions/GeneralAction";
import React from "react";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumberNoSuffix } from "../../ui/formatNumber";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { Player } from "@player";
import { CopyableText } from "../../ui/React/CopyableText";
import { StartButton } from "./StartButton";
import { Box, Paper, Typography } from "@mui/material";
import { Paper, Typography } from "@mui/material";
import { useRerender } from "../../ui/React/hooks";
import { ActionHeader } from "./ActionHeader";
interface GeneralActionElemProps {
bladeburner: Bladeburner;
@@ -18,41 +16,16 @@ interface GeneralActionElemProps {
export function GeneralActionElem({ bladeburner, action }: GeneralActionElemProps): React.ReactElement {
const rerender = useRerender();
const isActive = action.name === bladeburner.action?.name;
const computedActionTimeCurrent = Math.min(
bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
bladeburner.actionTimeToComplete,
);
const actionTime = action.getActionTime(bladeburner, Player);
const successChance =
action.name === "Recruitment" ? Math.max(0, Math.min(bladeburner.getRecruitmentSuccessChance(Player), 1)) : -1;
return (
<Paper sx={{ my: 1, p: 1 }}>
{isActive ? (
<>
<CopyableText value={action.name} />
<Typography>
(IN PROGRESS - {formatNumberNoSuffix(computedActionTimeCurrent, 0)} /{" "}
{formatNumberNoSuffix(bladeburner.actionTimeToComplete, 0)})
</Typography>
<Typography>
{createProgressBarText({
progress: computedActionTimeCurrent / bladeburner.actionTimeToComplete,
})}
</Typography>
</>
) : (
<Box display="flex" flexDirection="row" alignItems="center">
<CopyableText value={action.name} />
<StartButton bladeburner={bladeburner} action={action} rerender={rerender} />
</Box>
)}
<br />
<ActionHeader bladeburner={bladeburner} action={action} rerender={rerender}></ActionHeader>
<br />
<Typography>{action.desc}</Typography>
<br />
<br />
<Typography>
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
{successChance !== -1 && (

View File

@@ -5,76 +5,47 @@ import React from "react";
import { Paper, Typography } from "@mui/material";
import { Player } from "@player";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { SuccessChance } from "./SuccessChance";
import { ActionLevel } from "./ActionLevel";
import { Autolevel } from "./Autolevel";
import { StartButton } from "./StartButton";
import { TeamSizeButton } from "./TeamSizeButton";
import { CopyableText } from "../../ui/React/CopyableText";
import { formatNumberNoSuffix, formatBigNumber } from "../../ui/formatNumber";
import { formatBigNumber } from "../../ui/formatNumber";
import { useRerender } from "../../ui/React/hooks";
import { BladeActionType } from "@enums";
import { ActionHeader } from "./ActionHeader";
interface OperationElemProps {
bladeburner: Bladeburner;
operation: Operation;
action: Operation;
}
export function OperationElem({ bladeburner, operation }: OperationElemProps): React.ReactElement {
export function OperationElem({ bladeburner, action }: OperationElemProps): React.ReactElement {
const rerender = useRerender();
const isActive =
bladeburner.action?.type === BladeActionType.operation && operation.name === bladeburner.action?.name;
const computedActionTimeCurrent = Math.min(
bladeburner.actionTimeCurrent + bladeburner.actionTimeOverflow,
bladeburner.actionTimeToComplete,
);
const actionTime = operation.getActionTime(bladeburner, Player);
const isActive = bladeburner.action?.type === BladeActionType.operation && action.name === bladeburner.action?.name;
const actionTime = action.getActionTime(bladeburner, Player);
return (
<Paper sx={{ my: 1, p: 1 }}>
{isActive ? (
<>
<Typography>
<CopyableText value={operation.name} /> (IN PROGRESS - {formatNumberNoSuffix(computedActionTimeCurrent, 0)}{" "}
/ {formatNumberNoSuffix(bladeburner.actionTimeToComplete, 0)})
</Typography>
<Typography>
{createProgressBarText({
progress: computedActionTimeCurrent / bladeburner.actionTimeToComplete,
})}
</Typography>
</>
) : (
<>
<CopyableText value={operation.name} />
<StartButton bladeburner={bladeburner} action={operation} rerender={rerender} />
<TeamSizeButton action={operation} bladeburner={bladeburner} />
</>
)}
<br />
<br />
<ActionLevel action={operation} bladeburner={bladeburner} isActive={isActive} rerender={rerender} />
<ActionHeader bladeburner={bladeburner} action={action} rerender={rerender}></ActionHeader>
<br />
<ActionLevel action={action} bladeburner={bladeburner} isActive={isActive} rerender={rerender} />
<br />
<Typography whiteSpace={"pre-wrap"}>
{operation.desc}
{action.desc}
<br />
<br />
<SuccessChance action={operation} bladeburner={bladeburner} />
<SuccessChance action={action} bladeburner={bladeburner} />
<br />
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
<br />
Operations remaining: {formatBigNumber(Math.floor(operation.count))}
Operations remaining: {formatBigNumber(Math.floor(action.count))}
<br />
Successes: {formatBigNumber(operation.successes)}
Successes: {formatBigNumber(action.successes)}
<br />
Failures: {formatBigNumber(operation.failures)}
Failures: {formatBigNumber(action.failures)}
</Typography>
<br />
<Autolevel rerender={rerender} action={operation} />
<Autolevel rerender={rerender} action={action} />
</Paper>
);
}

Some files were not shown because too many files have changed in this diff Show More