mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 14:28:36 +02:00
Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2c7b2504c | ||
|
|
175af0bd28 | ||
|
|
8deb907b89 | ||
|
|
36e3dd73ac | ||
|
|
585e089976 | ||
|
|
7a4a973c06 | ||
|
|
1d8a1d5089 | ||
|
|
7113ee5425 | ||
|
|
38d99ff15e | ||
|
|
b7962ad8ab | ||
|
|
ed308b4fa6 | ||
|
|
574c284321 | ||
|
|
9dc3b22919 | ||
|
|
25afecc0ec | ||
|
|
7f5bc5700e | ||
|
|
da6262c856 | ||
|
|
1288d1c289 | ||
|
|
e478b9a224 | ||
|
|
7ee7a79763 | ||
|
|
519b4fef44 | ||
|
|
e23db93c8b | ||
|
|
1b8205e9d5 | ||
|
|
52111f6e07 | ||
|
|
8ebfcdb089 | ||
|
|
19984a6f22 | ||
|
|
b88361921e | ||
|
|
591ad45154 | ||
|
|
b53c35126e | ||
|
|
35c32e2871 | ||
|
|
e55387df4d | ||
|
|
2414949c2c | ||
|
|
309cd55085 | ||
|
|
8289b23cff | ||
|
|
aef362204d | ||
|
|
bfb9841832 | ||
|
|
6f009679ad | ||
|
|
6a1691fe54 | ||
|
|
bc71b8e18f | ||
|
|
a79d7f9e45 | ||
|
|
478646290e | ||
|
|
5696db2788 | ||
|
|
d4bdb8de2b | ||
|
|
4d3dbf169d | ||
|
|
dc4a85e591 | ||
|
|
de8883ed0f | ||
|
|
3fac471d51 | ||
|
|
0f23c95737 | ||
|
|
f4fcb5cde1 | ||
|
|
e56ed353e5 | ||
|
|
d3b9f32c3f | ||
|
|
384d1c1a2b | ||
|
|
7a1fce6f64 | ||
|
|
eba86e4bf0 | ||
|
|
a3f9a5c21e | ||
|
|
703e7c52ae | ||
|
|
7b993f3550 | ||
|
|
216500ed32 | ||
|
|
dd3975ab1d | ||
|
|
ed59f325ef | ||
|
|
057ccc2a2b | ||
|
|
1ad6f9f310 | ||
|
|
104a97d711 | ||
|
|
be437c83f6 | ||
|
|
7ae309edda | ||
|
|
7ab4ad8174 | ||
|
|
524714601e | ||
|
|
da7f01cca9 | ||
|
|
6c9555ba32 | ||
|
|
6beb6e9f95 | ||
|
|
61274310d6 | ||
|
|
12f9a2b24d | ||
|
|
c467daaf86 | ||
|
|
a923041382 | ||
|
|
ae1ca8f9a6 | ||
|
|
6669c4da6a | ||
|
|
5f1a94a9d3 | ||
|
|
fe87f1f628 | ||
|
|
8553bcb8fc | ||
|
|
75dabd10be | ||
|
|
d8de22a273 | ||
|
|
714c1cc9d6 | ||
|
|
08097aaf09 | ||
|
|
db226ce0b8 | ||
|
|
c5581e92bc | ||
|
|
7c4cd7ef86 | ||
|
|
28a4af0ddc | ||
|
|
0c2a59bb6c | ||
|
|
99f7a4cc7b | ||
|
|
35a34470a2 | ||
|
|
c637d0e4e4 | ||
|
|
803afc5244 | ||
|
|
bbd942ceca | ||
|
|
1e5f7184a2 | ||
|
|
fc8958af83 | ||
|
|
6b9f9ef7fa | ||
|
|
dd4b54406c | ||
|
|
d81358c80f | ||
|
|
6aaeb6b59e | ||
|
|
edf8e24046 | ||
|
|
c0662599b3 | ||
|
|
7ef7b692d0 | ||
|
|
25ac8432fc | ||
|
|
0457081908 | ||
|
|
162310f005 | ||
|
|
c703b71871 | ||
|
|
e9d1ddfaf3 | ||
|
|
4d5401f62e | ||
|
|
de5c1bbf98 | ||
|
|
c93205fec6 | ||
|
|
09c5ec7769 | ||
|
|
33af6685f8 | ||
|
|
d2dd6916b1 | ||
|
|
863ac2c8c0 | ||
|
|
1547581c24 | ||
|
|
1755b7cd7f |
191
.git_blame_ignore_revs
Normal file
191
.git_blame_ignore_revs
Normal file
@@ -0,0 +1,191 @@
|
||||
## What is this?
|
||||
## Some git commits contain mostly prettier/lint changes and aren't as valuable when
|
||||
## using `git blame`. This file contains a list of hashes that identify such commits
|
||||
## in a way that git can use.
|
||||
|
||||
## Tell git to use this file with:
|
||||
## git config --local blame.ignoreRevsFile .git_blame_ignore_revs
|
||||
|
||||
## This file was created by cleaning up and filtering the output of this command line:
|
||||
## git log --grep "[Pp]rettier"
|
||||
|
||||
## All that's really needed here are the actual 40-digit hashes of
|
||||
## the commits. I left the author/date info in as it helped when I
|
||||
## was filtering this list to just formatting changes.
|
||||
|
||||
4c9ca4990450740785a4dc95a75de53aeac724df
|
||||
# Author: omuretsu
|
||||
# Date: Fri Jun 23 10:14:18 2023 -0400
|
||||
|
||||
07b1eefe33b16251e0e67ae3231db2059defb1cc
|
||||
# Author: omuretsu
|
||||
# Date: Tue Feb 7 21:16:18 2023 -0500
|
||||
|
||||
bbe6421b067b0d0b876c42e49dafcbbb6458ef8d
|
||||
# Author: borisflagell
|
||||
# Date: Tue Oct 11 15:33:55 2022 +0300
|
||||
|
||||
ed0a28d29272ba2d5716618b73d231bca1ad79d1
|
||||
# Author: hydroflame
|
||||
# Date: Sun Oct 9 00:22:25 2022 -0400
|
||||
|
||||
a1f90d77ce708efb968d7e7ddb804b9862f2f460
|
||||
# Author: Snarling
|
||||
# Date: Tue Sep 27 19:58:14 2022 -0400
|
||||
|
||||
6be884a9ad42e9920be233302a066dea01766c8e
|
||||
# Author: borisflagell
|
||||
# Date: Sat May 28 01:15:05 2022 +0200
|
||||
|
||||
51ba358464ce5787ef472531e0bdb2b6b239955e
|
||||
# Author: Markus-D-M
|
||||
# Date: Wed May 25 21:27:58 2022 +0200
|
||||
|
||||
9091441389182a261438ec16ea17edcf11ab6f39
|
||||
# Author: borisflagell
|
||||
# Date: Mon May 23 19:43:46 2022 +0200
|
||||
|
||||
5bc1d293ca14f717d967ff4a345fe1ace1ffdd4c
|
||||
# Author: borisflagell
|
||||
# Date: Sun May 22 11:59:14 2022 +0200
|
||||
|
||||
d44d71712f066518321cefed18e145141ea23818
|
||||
# Author: borisflagell
|
||||
# Date: Sun May 22 06:40:32 2022 +0200
|
||||
|
||||
bd2d5396a60f31c1bd9837786fb10586cbcc6eda
|
||||
# Author: phyzical
|
||||
# Date: Thu Apr 28 19:22:37 2022 +0800
|
||||
|
||||
ad4c84be937224297f6745b6df3d433466b047c1
|
||||
# Author: borisflagell
|
||||
# Date: Sat Apr 23 15:18:43 2022 +0200
|
||||
|
||||
d4f8f2d0354832101f48d60f0b3445cc1f43dd23
|
||||
# Author: borisflagell
|
||||
# Date: Sat Apr 23 15:01:24 2022 +0200
|
||||
|
||||
a7045a234353de795494c0b3f378808b4a6d32aa
|
||||
# Author: borisflagell
|
||||
# Date: Thu Apr 21 19:20:16 2022 +0200
|
||||
|
||||
421e7b8c74eb85238ccf45b1a83225a452024058
|
||||
# Author: borisflagell
|
||||
# Date: Thu Apr 21 18:51:04 2022 +0200
|
||||
|
||||
9850b56711b29c254102ad8694b1610b4d5adc81
|
||||
# Author: Snarling
|
||||
# Date: Sun Apr 17 17:23:14 2022 -0400
|
||||
|
||||
567fcf8fb63bc469ae61013cca511a76eb6f9086
|
||||
# Author: violet
|
||||
# Date: Wed Apr 13 14:42:07 2022 -0500
|
||||
|
||||
f6af85a38d8286aec702556f1296565de5a5067f
|
||||
# Author: violet
|
||||
# Date: Sun Apr 10 15:57:34 2022 -0500
|
||||
|
||||
57f04d3911b5a384c379dcfb2d0bcb18b25c37ea
|
||||
# Author: violet
|
||||
# Date: Thu Apr 7 11:45:21 2022 -0500
|
||||
|
||||
720e2112c6766b581e43e1f9e712c47300b22dcd
|
||||
# Author: violet
|
||||
# Date: Mon Mar 21 14:40:17 2022 -0500
|
||||
|
||||
ecffc8655a70e6e3c7bd03ba5723b437d7857dfa
|
||||
Merge: 8506dcfed 00d1d294e
|
||||
# Author: hydroflame
|
||||
# Date: Sun Mar 20 22:21:00 2022 -0400
|
||||
|
||||
901ee92fe97606bd27840900512b0fa760ba428e
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Sun Mar 20 22:02:36 2022 -0400
|
||||
|
||||
b3f9380ebd3feb3b6ef91873a1c6a3a1ddc56654
|
||||
# Author: Billy Vong
|
||||
# Date: Fri Dec 17 12:56:48 2021 -0500
|
||||
|
||||
# I *really* didn't want to include this one because it has a few tiny, actual
|
||||
# code changes, but it reformats tens of thousands of lines of code. Argh.
|
||||
8f13363466c2e825bbfb7e4b66d853c4805eda61
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Mon Oct 11 16:38:50 2021 -0400
|
||||
|
||||
66a2adaeb4b12a48fc8fdafe5e5169919f8d46a5
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Wed Sep 22 12:56:55 2021 -0400
|
||||
|
||||
## Additional changes that should be ignored for blame.
|
||||
|
||||
2e7f164b5f251ce457f0d3b9a42cfa0f08ef200e
|
||||
# Author: tyasuh.taeragan
|
||||
# Date: Fri Oct 22 16:35:05 2021 -0400
|
||||
|
||||
a18bdd6afc77752bded2f0794bf35ad9d6455fa1
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Sat Sep 4 19:09:30 2021 -0400
|
||||
|
||||
# from a search for "lint"; many more of these had significant code changes than
|
||||
# Prettier, so I'm only vetting the largest for inclusion
|
||||
|
||||
48f80f25d6fb90edc292481905814c0d5593b5a9
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Wed Apr 6 19:30:08 2022 -0400
|
||||
|
||||
b0f20c8c8f4859f01cfc79bc3945a8e7272d6592
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Sat Sep 25 01:26:03 2021 -0400
|
||||
|
||||
d745150c45979ab251b51447a0847e28966346a6
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Sat May 1 03:17:31 2021 -0400
|
||||
|
||||
3fad5050961a3259b0158763f48016174a2a690e
|
||||
# Author: Olivier Gagnon
|
||||
# Date: Thu Apr 29 23:52:56 2021 -0400
|
||||
|
||||
#
|
||||
# The commits listed below showed up on a search for "[Pp]rettier", but
|
||||
# weren't just Prettier/lint changes.
|
||||
#
|
||||
|
||||
# 6459b1ab483cf66ae1e6e50518424311c6bbbd8b
|
||||
# Author: omuretsu
|
||||
# Date: Sat Feb 4 07:42:35 2023 -0500
|
||||
|
||||
# 5b4addbb212411aa1de27ba0ee16ebaabedbc6a1
|
||||
# Author: borisflagell
|
||||
# Date: Sun May 22 01:08:53 2022 +0200
|
||||
|
||||
# 705a56f3bdcb859179144cdafab7eeb8832f7eb9
|
||||
# Author: borisflagell
|
||||
# Date: Sat May 7 12:55:56 2022 +0200
|
||||
|
||||
# 4fa65322fe770dc3d79cd0301521d230694a1fc7
|
||||
# Author: borisflagell
|
||||
# Date: Thu Apr 21 18:37:47 2022 +0200
|
||||
|
||||
# 81d1c02fdfe087c3bd0a2a8a2a41c2f82a6fef56
|
||||
# Author: hydroflame
|
||||
# Date: Sun Mar 20 22:50:42 2022 -0400
|
||||
|
||||
# 4a7fcda86f5d3d05638b4a9f3a61e5937dde52c6
|
||||
# Author: hydroflame
|
||||
# Date: Sat Sep 4 19:03:05 2021 -0400
|
||||
|
||||
# 7ee2612c17a90a80e5a0a7793a4c81465e212c0c
|
||||
# Author: Martin Fournier
|
||||
# Date: Sun Jan 9 11:15:09 2022 -0500
|
||||
|
||||
# c59806c87d543c7cc78d1d68af9599d118c0cb2b
|
||||
# Author: Martin Fournier
|
||||
# Date: Fri Dec 17 09:54:32 2021 -0500
|
||||
|
||||
# 306facc0d10104d543974d5cb01b6ff419c9acb2
|
||||
# Author: David Edmondson
|
||||
# Date: Sat Sep 4 22:17:30 2021 -0700
|
||||
|
||||
# 1e42f73e2a1b7ac9489a7287c7be99298f412ac0
|
||||
# Author: David Edmondson
|
||||
# Date: Sat Sep 4 13:18:08 2021 -0700commit 2e7f164b5f251ce457f0d3b9a42cfa0f08ef200e
|
||||
@@ -15,3 +15,5 @@ markdown
|
||||
package.json
|
||||
package.lock.json
|
||||
tsdoc-metadata.json
|
||||
|
||||
.git_blame_ignore_revs
|
||||
|
||||
@@ -44,6 +44,9 @@ async function createWindow(killall) {
|
||||
window.removeMenu();
|
||||
noScripts = killall ? { query: { noScripts: killall } } : {};
|
||||
window.loadFile("index.html", noScripts);
|
||||
window.once("ready-to-show", () => {
|
||||
utils.setZoomFactor(window, utils.getZoomFactor());
|
||||
});
|
||||
window.show();
|
||||
if (debug) window.webContents.openDevTools();
|
||||
|
||||
@@ -60,7 +63,6 @@ async function createWindow(killall) {
|
||||
|
||||
achievements.enableAchievementsInterval(window);
|
||||
utils.attachUnresponsiveAppHandler(window);
|
||||
utils.setZoomFactor(window);
|
||||
|
||||
try {
|
||||
await api.initialize(window);
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
/* eslint-disable no-process-exit */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { app, dialog, BrowserWindow, ipcMain, protocol } = require("electron");
|
||||
|
||||
const log = require("electron-log");
|
||||
log.catchErrors();
|
||||
|
||||
// This handler must be set ASAP to prevent ghost processes.
|
||||
process.on("uncaughtException", function () {
|
||||
// The exception will be logged by electron-log.
|
||||
app.quit();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// This handler must be set ASAP to prevent ghost processes.
|
||||
app.on("window-all-closed", () => {
|
||||
log.info("Quitting the app...");
|
||||
app.quit();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
const greenworks = require("./greenworks");
|
||||
const api = require("./api-server");
|
||||
const gameWindow = require("./gameWindow");
|
||||
@@ -17,15 +34,8 @@ const { fileURLToPath } = require("url");
|
||||
log.transports.file.level = store.get("file-log-level", "info");
|
||||
log.transports.console.level = store.get("console-log-level", "debug");
|
||||
|
||||
log.catchErrors();
|
||||
log.info(`Started app: ${JSON.stringify(process.argv)}`);
|
||||
|
||||
process.on("uncaughtException", function () {
|
||||
// The exception will already have been logged by electron-log
|
||||
app.quit();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// We want to fail gracefully if we cannot connect to Steam
|
||||
try {
|
||||
if (greenworks && greenworks.init()) {
|
||||
@@ -42,13 +52,6 @@ try {
|
||||
|
||||
let isRestoreDisabled = false;
|
||||
|
||||
// This was moved so that startup errors do not lead to ghost processes
|
||||
app.on("window-all-closed", () => {
|
||||
log.info("Quitting the app...");
|
||||
app.quit();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
function setStopProcessHandler(app, window) {
|
||||
const closingWindowHandler = async (e) => {
|
||||
// We need to prevent the default closing event to add custom logic
|
||||
@@ -152,7 +155,7 @@ function setStopProcessHandler(app, window) {
|
||||
log.debug("Saving to Steam Cloud ...");
|
||||
try {
|
||||
const playerId = window.gameInfo.player.identifier;
|
||||
await storage.pushGameSaveToSteamCloud(save, playerId);
|
||||
await storage.pushSaveDataToSteamCloud(save, playerId);
|
||||
log.silly("Saved Game to Steam Cloud");
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
@@ -203,7 +206,7 @@ app.on("ready", async () => {
|
||||
if ((method === "GET" && relativePath.startsWith("dist")) || relativePath.match(/^[a-zA-Z-_]*\.html/)) {
|
||||
return callback(filePath);
|
||||
}
|
||||
log.error("Tried to access a page outside sandbox.");
|
||||
log.error(`Tried to access a page outside the sandbox. Url: ${url}. Method: ${method}.`);
|
||||
callback(path.join(__dirname, "fileError.txt"));
|
||||
});
|
||||
|
||||
|
||||
@@ -103,8 +103,8 @@ function getMenu(window) {
|
||||
enabled: storage.isCloudEnabled(),
|
||||
click: async () => {
|
||||
try {
|
||||
const saveGame = await storage.getSteamCloudSaveString();
|
||||
await storage.pushSaveGameForImport(window, saveGame, false);
|
||||
const saveData = await storage.getSteamCloudSaveData();
|
||||
await storage.pushSaveGameForImport(window, saveData, false);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
utils.writeToast(window, "Could not load from Steam Cloud", "error", 5000);
|
||||
@@ -114,16 +114,6 @@ function getMenu(window) {
|
||||
{
|
||||
type: "separator",
|
||||
},
|
||||
{
|
||||
label: "Compress Disk Saves (.gz)",
|
||||
type: "checkbox",
|
||||
checked: storage.isSaveCompressionEnabled(),
|
||||
click: (menuItem) => {
|
||||
storage.setSaveCompressionConfig(menuItem.checked);
|
||||
utils.writeToast(window, `${menuItem.checked ? "Enabled" : "Disabled"} Save Compression`, "info", 5000);
|
||||
refreshMenu(window);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Auto-Save to Disk",
|
||||
type: "checkbox",
|
||||
@@ -222,7 +212,7 @@ function getMenu(window) {
|
||||
click: () => window.loadFile("index.html"),
|
||||
},
|
||||
{
|
||||
label: "Reload & Kill All Scripts",
|
||||
label: "Reload && Kill All Scripts",
|
||||
click: () => utils.reloadAndKill(window, true),
|
||||
},
|
||||
],
|
||||
|
||||
4
electron/package-lock.json
generated
4
electron/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bitburner",
|
||||
"version": "2.5.2",
|
||||
"version": "2.6.1",
|
||||
"dependencies": {
|
||||
"electron-log": "^4.4.8",
|
||||
"electron-store": "^8.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.1",
|
||||
"description": "A cyberpunk-themed programming incremental game",
|
||||
"main": "main.js",
|
||||
"author": "Daniel Xie, hydroflame, et al.",
|
||||
|
||||
1
electron/saveDataBinaryFormat.d.ts
vendored
Normal file
1
electron/saveDataBinaryFormat.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const isBinaryFormat: (saveData: string | Uint8Array) => boolean;
|
||||
13
electron/saveDataBinaryFormat.js
Normal file
13
electron/saveDataBinaryFormat.js
Normal file
@@ -0,0 +1,13 @@
|
||||
// The 2 magic bytes of the gzip header plus the mandatory compression type of DEFLATE
|
||||
const magicBytes = [0x1f, 0x8b, 0x08];
|
||||
|
||||
function isBinaryFormat(rawData) {
|
||||
for (let i = 0; i < magicBytes.length; ++i) {
|
||||
if (magicBytes[i] !== rawData[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = { isBinaryFormat };
|
||||
@@ -11,6 +11,7 @@ const greenworks = require("./greenworks");
|
||||
const log = require("electron-log");
|
||||
const flatten = require("lodash/flatten");
|
||||
const Store = require("electron-store");
|
||||
const { isBinaryFormat } = require("./saveDataBinaryFormat");
|
||||
const store = new Store();
|
||||
|
||||
// https://stackoverflow.com/a/69418940
|
||||
@@ -86,14 +87,6 @@ function isAutosaveEnabled() {
|
||||
return store.get("autosave-enabled", true);
|
||||
}
|
||||
|
||||
function setSaveCompressionConfig(value) {
|
||||
store.set("save-compression-enabled", value);
|
||||
}
|
||||
|
||||
function isSaveCompressionEnabled() {
|
||||
return store.get("save-compression-enabled", true);
|
||||
}
|
||||
|
||||
function setCloudEnabledConfig(value) {
|
||||
store.set("cloud-enabled", value);
|
||||
}
|
||||
@@ -163,17 +156,22 @@ async function backupSteamDataToDisk(currentPlayerId) {
|
||||
const file = greenworks.getFileNameAndSize(0);
|
||||
const previousPlayerId = file.name.replace(".json.gz", "");
|
||||
if (previousPlayerId !== currentPlayerId) {
|
||||
const backupSave = await getSteamCloudSaveString();
|
||||
const backupSaveData = await getSteamCloudSaveData();
|
||||
const backupFile = path.join(app.getPath("userData"), "/saves/_backups", `${previousPlayerId}.json.gz`);
|
||||
const buffer = Buffer.from(backupSave, "base64").toString("utf8");
|
||||
saveContent = await gzip(buffer);
|
||||
await fs.writeFile(backupFile, saveContent, "utf8");
|
||||
await fs.writeFile(backupFile, backupSaveData, "utf8");
|
||||
log.debug(`Saved backup game to '${backupFile}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function pushGameSaveToSteamCloud(base64save, currentPlayerId) {
|
||||
if (!isCloudEnabled) return Promise.reject("Steam Cloud is not Enabled");
|
||||
/**
|
||||
* The name of save file is `${currentPlayerId}.json.gz`. The content of save file is weird: it's a base64 string of the
|
||||
* binary data of compressed json save string. It's weird because the extension is .json.gz while the content is a
|
||||
* base64 string. Check the comments in the implementation to see why it is like that.
|
||||
*/
|
||||
async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
|
||||
if (!isCloudEnabled()) {
|
||||
return Promise.reject("Steam Cloud is not Enabled");
|
||||
}
|
||||
|
||||
try {
|
||||
backupSteamDataToDisk(currentPlayerId);
|
||||
@@ -183,12 +181,19 @@ async function pushGameSaveToSteamCloud(base64save, currentPlayerId) {
|
||||
|
||||
const steamSaveName = `${currentPlayerId}.json.gz`;
|
||||
|
||||
// Let's decode the base64 string so GZIP is more efficient.
|
||||
const buffer = Buffer.from(base64save, "base64");
|
||||
const compressedBuffer = await gzip(buffer);
|
||||
// We can't use utf8 for some reason, steamworks is unhappy.
|
||||
const content = compressedBuffer.toString("base64");
|
||||
log.debug(`Uncompressed: ${base64save.length} bytes`);
|
||||
/**
|
||||
* When we push save file to Steam Cloud, we use greenworks.saveTextToFile. It seems that this method expects a string
|
||||
* as the file content. That is why saveData is encoded in base64 and pushed to Steam Cloud as a text file.
|
||||
*
|
||||
* Encoding saveData in UTF-8 (with buffer.toString("utf8")) is not the proper way to convert binary data to string.
|
||||
* Quote from buffer's documentation: "If encoding is 'utf8' and a byte sequence in the input is not valid UTF-8, then
|
||||
* each invalid byte is replaced with the replacement character U+FFFD.". The proper way to do it is to use
|
||||
* String.fromCharCode or String.fromCodePoint.
|
||||
*
|
||||
* 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`);
|
||||
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
||||
|
||||
@@ -199,19 +204,22 @@ async function pushGameSaveToSteamCloud(base64save, currentPlayerId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getSteamCloudSaveString() {
|
||||
if (!isCloudEnabled()) return Promise.reject("Steam Cloud is not Enabled");
|
||||
/**
|
||||
* This function processes the save file in Steam Cloud and returns the save data in the binary format.
|
||||
*/
|
||||
async function getSteamCloudSaveData() {
|
||||
if (!isCloudEnabled()) {
|
||||
return Promise.reject("Steam Cloud is not Enabled");
|
||||
}
|
||||
log.debug(`Fetching Save in Steam Cloud`);
|
||||
const cloudString = await getCloudFile();
|
||||
const gzippedBase64Buffer = Buffer.from(cloudString, "base64");
|
||||
const uncompressedBuffer = await gunzip(gzippedBase64Buffer);
|
||||
const content = uncompressedBuffer.toString("base64");
|
||||
log.debug(`Compressed: ${cloudString.length} bytes`);
|
||||
log.debug(`Uncompressed: ${content.length} bytes`);
|
||||
return content;
|
||||
// Decode cloudString to get save data back.
|
||||
const saveData = Buffer.from(cloudString, "base64");
|
||||
log.debug(`SaveData: ${saveData.length} bytes`);
|
||||
return saveData;
|
||||
}
|
||||
|
||||
async function saveGameToDisk(window, saveData) {
|
||||
async function saveGameToDisk(window, electronGameData) {
|
||||
const currentFolder = await getSaveFolder(window);
|
||||
let saveFolderSizeBytes = await getFolderSizeInBytes(currentFolder);
|
||||
const maxFolderSizeBytes = store.get("autosave-quota", 1e8); // 100Mb per playerIndentifier
|
||||
@@ -221,19 +229,12 @@ async function saveGameToDisk(window, saveData) {
|
||||
log.debug(
|
||||
`Remaining: ${remainingSpaceBytes} bytes (${((saveFolderSizeBytes / maxFolderSizeBytes) * 100).toFixed(2)}% used)`,
|
||||
);
|
||||
const shouldCompress = isSaveCompressionEnabled();
|
||||
const fileName = saveData.fileName;
|
||||
const file = path.join(currentFolder, fileName + (shouldCompress ? ".gz" : ""));
|
||||
let saveData = electronGameData.save;
|
||||
const file = path.join(currentFolder, electronGameData.fileName);
|
||||
try {
|
||||
let saveContent = saveData.save;
|
||||
if (shouldCompress) {
|
||||
// Let's decode the base64 string so GZIP is more efficient.
|
||||
const buffer = Buffer.from(saveContent, "base64").toString("utf8");
|
||||
saveContent = await gzip(buffer);
|
||||
}
|
||||
await fs.writeFile(file, saveContent, "utf8");
|
||||
await fs.writeFile(file, saveData, "utf8");
|
||||
log.debug(`Saved Game to '${file}'`);
|
||||
log.debug(`Save Size: ${saveContent.length} bytes`);
|
||||
log.debug(`Save Size: ${saveData.length} bytes`);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
@@ -276,14 +277,14 @@ async function loadLastFromDisk(window) {
|
||||
async function loadFileFromDisk(path) {
|
||||
const buffer = await fs.readFile(path);
|
||||
let content;
|
||||
if (path.endsWith(".gz")) {
|
||||
const uncompressedBuffer = await gunzip(buffer);
|
||||
content = uncompressedBuffer.toString("base64");
|
||||
log.debug(`Uncompressed file content (new size: ${content.length} bytes)`);
|
||||
if (isBinaryFormat(buffer)) {
|
||||
// Save file is in the binary format.
|
||||
content = buffer;
|
||||
} else {
|
||||
// Save file is in the base64 format.
|
||||
content = buffer.toString("utf8");
|
||||
log.debug(`Loaded file with ${content.length} bytes`);
|
||||
}
|
||||
log.debug(`Loaded file with ${content.length} bytes`);
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -319,7 +320,7 @@ async function restoreIfNewerExists(window) {
|
||||
const disk = {};
|
||||
|
||||
try {
|
||||
steam.save = await getSteamCloudSaveString();
|
||||
steam.save = await getSteamCloudSaveData();
|
||||
steam.data = await getSaveInformation(window, steam.save);
|
||||
} catch (error) {
|
||||
log.error("Could not retrieve steam file");
|
||||
@@ -361,12 +362,12 @@ async function restoreIfNewerExists(window) {
|
||||
// We add a few seconds to the currentSave's lastSave to prioritize it
|
||||
log.info("Found newer data than the current's save file");
|
||||
log.silly(bestMatch.data);
|
||||
await pushSaveGameForImport(window, bestMatch.save, true);
|
||||
pushSaveGameForImport(window, bestMatch.save, true);
|
||||
return true;
|
||||
} else if (bestMatch.data.playtime > currentData.playtime && currentData.playtime < lowPlaytime) {
|
||||
log.info("Found older save, but with more playtime, and current less than 15 mins played");
|
||||
log.silly(bestMatch.data);
|
||||
await pushSaveGameForImport(window, bestMatch.save, true);
|
||||
pushSaveGameForImport(window, bestMatch.save, true);
|
||||
return true;
|
||||
} else {
|
||||
log.debug("Current save data is the freshest");
|
||||
@@ -380,8 +381,8 @@ module.exports = {
|
||||
getSaveInformation,
|
||||
restoreIfNewerExists,
|
||||
pushSaveGameForImport,
|
||||
pushGameSaveToSteamCloud,
|
||||
getSteamCloudSaveString,
|
||||
pushSaveDataToSteamCloud,
|
||||
getSteamCloudSaveData,
|
||||
getSteamCloudQuota,
|
||||
deleteCloudFile,
|
||||
saveGameToDisk,
|
||||
@@ -394,6 +395,4 @@ module.exports = {
|
||||
setCloudEnabledConfig,
|
||||
isAutosaveEnabled,
|
||||
setAutosaveConfig,
|
||||
isSaveCompressionEnabled,
|
||||
setSaveCompressionConfig,
|
||||
};
|
||||
|
||||
@@ -7,8 +7,12 @@ const store = new Store();
|
||||
|
||||
function reloadAndKill(window, killScripts) {
|
||||
log.info("Reloading & Killing all scripts...");
|
||||
const zoomFactor = getZoomFactor();
|
||||
window.webContents.forcefullyCrashRenderer();
|
||||
window.loadFile("index.html", killScripts ? { query: { noScripts: true } } : {});
|
||||
window.once("ready-to-show", () => {
|
||||
setZoomFactor(window, zoomFactor);
|
||||
});
|
||||
}
|
||||
|
||||
function promptForReload(window) {
|
||||
|
||||
@@ -9,17 +9,15 @@ Get current action.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getCurrentAction(): BladeburnerCurAction;
|
||||
getCurrentAction(): BladeburnerCurAction | null;
|
||||
```
|
||||
**Returns:**
|
||||
|
||||
[BladeburnerCurAction](./bitburner.bladeburnercuraction.md)
|
||||
[BladeburnerCurAction](./bitburner.bladeburnercuraction.md) \| null
|
||||
|
||||
Object that represents the player’s current Bladeburner action.
|
||||
Object that represents the player’s current Bladeburner action, or null if no action is being performed.
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 1 GB
|
||||
|
||||
Returns an object that represents the player’s current Bladeburner action. If the player is not performing an action, the function will return an object with the ‘type’ property set to “Idle”.
|
||||
|
||||
|
||||
@@ -41,5 +41,6 @@ export interface Corporation extends WarehouseAPI, OfficeAPI
|
||||
| [levelUpgrade(upgradeName)](./bitburner.corporation.levelupgrade.md) | Level an upgrade. |
|
||||
| [nextUpdate()](./bitburner.corporation.nextupdate.md) | Sleep until the next Corporation update has happened. |
|
||||
| [purchaseUnlock(upgradeName)](./bitburner.corporation.purchaseunlock.md) | Unlock an upgrade |
|
||||
| [sellDivision(divisionName)](./bitburner.corporation.selldivision.md) | Sell a division |
|
||||
| [sellShares(amount)](./bitburner.corporation.sellshares.md) | Sell Shares. Transfer shares from the CEO to public traders to receive money in the player's wallet. |
|
||||
|
||||
|
||||
28
markdown/bitburner.corporation.selldivision.md
Normal file
28
markdown/bitburner.corporation.selldivision.md
Normal file
@@ -0,0 +1,28 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Corporation](./bitburner.corporation.md) > [sellDivision](./bitburner.corporation.selldivision.md)
|
||||
|
||||
## Corporation.sellDivision() method
|
||||
|
||||
Sell a division
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
sellDivision(divisionName: string): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| divisionName | string | Name of the division |
|
||||
|
||||
**Returns:**
|
||||
|
||||
void
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 20 GB
|
||||
|
||||
@@ -17,5 +17,7 @@ analysis: {
|
||||
getLiberties(): number[][];
|
||||
|
||||
getControlledEmptyNodes(): string[];
|
||||
|
||||
getStats(): Partial<Record<GoOpponent, SimpleOpponentStats>>;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -15,10 +15,9 @@ cheat: {
|
||||
x: number,
|
||||
y: number,
|
||||
): Promise<{
|
||||
type: "invalid" | "move" | "pass" | "gameOver";
|
||||
x: number;
|
||||
y: number;
|
||||
success: boolean;
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null;
|
||||
y: number | null;
|
||||
}>;
|
||||
playTwoMoves(
|
||||
x1: number,
|
||||
@@ -26,30 +25,27 @@ cheat: {
|
||||
x2: number,
|
||||
y2: number,
|
||||
): Promise<{
|
||||
type: "invalid" | "move" | "pass" | "gameOver";
|
||||
x: number;
|
||||
y: number;
|
||||
success: boolean;
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null;
|
||||
y: number | null;
|
||||
}>;
|
||||
|
||||
repairOfflineNode(
|
||||
x: number,
|
||||
y: number,
|
||||
): Promise<{
|
||||
type: "invalid" | "move" | "pass" | "gameOver";
|
||||
x: number;
|
||||
y: number;
|
||||
success: boolean;
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null;
|
||||
y: number | null;
|
||||
}>;
|
||||
|
||||
destroyNode(
|
||||
x: number,
|
||||
y: number,
|
||||
): Promise<{
|
||||
type: "invalid" | "move" | "pass" | "gameOver";
|
||||
x: number;
|
||||
y: number;
|
||||
success: boolean;
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null;
|
||||
y: number | null;
|
||||
}>;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -8,15 +8,8 @@ Retrieves a simplified version of the board state. "X" represents black pieces,
|
||||
|
||||
For example, a 5x5 board might look like this:
|
||||
|
||||
```
|
||||
[
|
||||
"XX.O.",
|
||||
"X..OO",
|
||||
".XO..",
|
||||
"XXO.#",
|
||||
".XO.#",
|
||||
]
|
||||
```
|
||||
\[<br/> "XX.O.",<br/> "X..OO",<br/> ".XO..",<br/> "XXO.\#",<br/> ".XO.\#",<br/> \]
|
||||
|
||||
Each string represents a vertical column on the board, and each character in the string represents a point.
|
||||
|
||||
Traditional notation for Go is e.g. "B,1" referring to second ("B") column, first rank. This is the equivalent of index \[1\]\[0\].
|
||||
|
||||
19
markdown/bitburner.go.getcurrentplayer.md
Normal file
19
markdown/bitburner.go.getcurrentplayer.md
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Go](./bitburner.go.md) > [getCurrentPlayer](./bitburner.go.getcurrentplayer.md)
|
||||
|
||||
## Go.getCurrentPlayer() method
|
||||
|
||||
Returns the color of the current player, or 'None' if the game is over.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getCurrentPlayer(): "White" | "Black" | "None";
|
||||
```
|
||||
**Returns:**
|
||||
|
||||
"White" \| "Black" \| "None"
|
||||
|
||||
"White" \| "Black" \| "None"
|
||||
|
||||
22
markdown/bitburner.go.getgamestate.md
Normal file
22
markdown/bitburner.go.getgamestate.md
Normal file
@@ -0,0 +1,22 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Go](./bitburner.go.md) > [getGameState](./bitburner.go.getgamestate.md)
|
||||
|
||||
## Go.getGameState() method
|
||||
|
||||
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.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getGameState(): {
|
||||
currentPlayer: "White" | "Black" | "None";
|
||||
whiteScore: number;
|
||||
blackScore: number;
|
||||
previousMove: [number, number] | null;
|
||||
};
|
||||
```
|
||||
**Returns:**
|
||||
|
||||
{ currentPlayer: "White" \| "Black" \| "None"; whiteScore: number; blackScore: number; previousMove: \[number, number\] \| null; }
|
||||
|
||||
@@ -9,9 +9,9 @@ Returns the name of the opponent faction in the current subnet.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getOpponent(): GoOpponent | "No AI" | "????????????";
|
||||
getOpponent(): GoOpponent | "No AI";
|
||||
```
|
||||
**Returns:**
|
||||
|
||||
[GoOpponent](./bitburner.goopponent.md) \| "No AI" \| "????????????"
|
||||
[GoOpponent](./bitburner.goopponent.md) \| "No AI"
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ makeMove(
|
||||
x: number,
|
||||
y: number,
|
||||
): Promise<{
|
||||
type: "invalid" | "move" | "pass" | "gameOver";
|
||||
x: number;
|
||||
y: number;
|
||||
success: boolean;
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null;
|
||||
y: number | null;
|
||||
}>;
|
||||
```
|
||||
|
||||
@@ -29,7 +28,7 @@ makeMove(
|
||||
|
||||
**Returns:**
|
||||
|
||||
Promise<{ type: "invalid" \| "move" \| "pass" \| "gameOver"; x: number; y: number; success: boolean; }>
|
||||
Promise<{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }>
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -16,26 +16,19 @@ export interface Go
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [analysis](./bitburner.go.analysis.md) | | { getValidMoves(): boolean\[\]\[\]; getChains(): (number \| null)\[\]\[\]; getLiberties(): number\[\]\[\]; getControlledEmptyNodes(): string\[\]; } | Tools to analyze the IPvGO subnet. |
|
||||
| [cheat](./bitburner.go.cheat.md) | | { getCheatSuccessChance(): number; removeRouter( x: number, y: number, ): Promise<{ type: "invalid" \| "move" \| "pass" \| "gameOver"; x: number; y: number; success: boolean; }>; playTwoMoves( x1: number, y1: number, x2: number, y2: number, ): Promise<{ type: "invalid" \| "move" \| "pass" \| "gameOver"; x: number; y: number; success: boolean; }>; repairOfflineNode( x: number, y: number, ): Promise<{ type: "invalid" \| "move" \| "pass" \| "gameOver"; x: number; y: number; success: boolean; }>; destroyNode( x: number, y: number, ): Promise<{ type: "invalid" \| "move" \| "pass" \| "gameOver"; x: number; y: number; success: boolean; }>; } | Illicit and dangerous IPvGO tools. Not for the faint of heart. Requires Bitnode 14.2 to use. |
|
||||
| [analysis](./bitburner.go.analysis.md) | | { getValidMoves(): boolean\[\]\[\]; getChains(): (number \| null)\[\]\[\]; getLiberties(): number\[\]\[\]; getControlledEmptyNodes(): string\[\]; getStats(): Partial<Record<[GoOpponent](./bitburner.goopponent.md)<!-- -->, [SimpleOpponentStats](./bitburner.simpleopponentstats.md)<!-- -->>>; } | Tools to analyze the IPvGO subnet. |
|
||||
| [cheat](./bitburner.go.cheat.md) | | { getCheatSuccessChance(): number; removeRouter( x: number, y: number, ): Promise<{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }>; playTwoMoves( x1: number, y1: number, x2: number, y2: number, ): Promise<{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }>; repairOfflineNode( x: number, y: number, ): Promise<{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }>; destroyNode( x: number, y: number, ): Promise<{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }>; } | Illicit and dangerous IPvGO tools. Not for the faint of heart. Requires Bitnode 14.2 to use. |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [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>
|
||||
```
|
||||
[
|
||||
"XX.O.",
|
||||
"X..OO",
|
||||
".XO..",
|
||||
"XXO.#",
|
||||
".XO.#",
|
||||
]
|
||||
```
|
||||
<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> |
|
||||
| [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. |
|
||||
| [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 "Daedalus" or "Illuminati",</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> |
|
||||
|
||||
|
||||
34
markdown/bitburner.go.opponentnextturn.md
Normal file
34
markdown/bitburner.go.opponentnextturn.md
Normal file
@@ -0,0 +1,34 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Go](./bitburner.go.md) > [opponentNextTurn](./bitburner.go.opponentnextturn.md)
|
||||
|
||||
## Go.opponentNextTurn() method
|
||||
|
||||
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.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
opponentNextTurn(logOpponentMove?: boolean): Promise<{
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null;
|
||||
y: number | null;
|
||||
}>;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| logOpponentMove | boolean | _(Optional)_ optional, defaults to true. if false prevents logging opponent move |
|
||||
|
||||
**Returns:**
|
||||
|
||||
Promise<{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }>
|
||||
|
||||
a promise that contains if your last 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
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 0 GB
|
||||
|
||||
@@ -12,15 +12,14 @@ This can also be used if you pick up the game in a state where the opponent need
|
||||
|
||||
```typescript
|
||||
passTurn(): Promise<{
|
||||
type: "invalid" | "move" | "pass" | "gameOver";
|
||||
x: number;
|
||||
y: number;
|
||||
success: boolean;
|
||||
type: "move" | "pass" | "gameOver";
|
||||
x: number | null;
|
||||
y: number | null;
|
||||
}>;
|
||||
```
|
||||
**Returns:**
|
||||
|
||||
Promise<{ type: "invalid" \| "move" \| "pass" \| "gameOver"; x: number; y: number; success: boolean; }>
|
||||
Promise<{ type: "move" \| "pass" \| "gameOver"; x: number \| null; y: number \| null; }>
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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 "Daedalus" or "Illuminati",
|
||||
opponent is "Netburners" or "Slum Snakes" or "The Black Hand" or "Tetrads" or "Daedalus" or "Illuminati" or "????????????",
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
||||
@@ -8,5 +8,12 @@
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
type GoOpponent = "Netburners" | "Slum Snakes" | "The Black Hand" | "Tetrads" | "Daedalus" | "Illuminati";
|
||||
type GoOpponent =
|
||||
| "Netburners"
|
||||
| "Slum Snakes"
|
||||
| "The Black Hand"
|
||||
| "Tetrads"
|
||||
| "Daedalus"
|
||||
| "Illuminati"
|
||||
| "????????????";
|
||||
```
|
||||
|
||||
33
markdown/bitburner.hackingformulas.growamount.md
Normal file
33
markdown/bitburner.hackingformulas.growamount.md
Normal file
@@ -0,0 +1,33 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [HackingFormulas](./bitburner.hackingformulas.md) > [growAmount](./bitburner.hackingformulas.growamount.md)
|
||||
|
||||
## HackingFormulas.growAmount() method
|
||||
|
||||
Calculate the amount of money a grow action will leave a server with. Starting money is server.moneyAvailable. Note that when simulating the effect of [grow](./bitburner.ns.grow.md)<!-- -->, what matters is the state of the server and player when the grow \*finishes\*, not when it is started.
|
||||
|
||||
The growth amount depends both linearly \*and\* exponentially on threads; see [grow](./bitburner.ns.grow.md) for more details.
|
||||
|
||||
The inverse of this function is [formulas.hacking.growThreads](./bitburner.hackingformulas.growthreads.md)<!-- -->, although it rounds up to integer threads.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
growAmount(server: Server, player: Person, threads: number, cores?: number): number;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| server | [Server](./bitburner.server.md) | Server info, typically from [getServer](./bitburner.ns.getserver.md) |
|
||||
| player | [Person](./bitburner.person.md) | Player info, typically from [getPlayer](./bitburner.ns.getplayer.md) |
|
||||
| threads | number | Number of threads to grow with. Can be fractional. |
|
||||
| cores | number | _(Optional)_ Number of cores on the computer that will execute grow. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
number
|
||||
|
||||
The amount of money after the calculated grow.
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
|
||||
## HackingFormulas.growPercent() method
|
||||
|
||||
Calculate the percent a server would grow to. Not exact due to limitations of mathematics. (Ex: 3.0 would grow the server to 300% of its current value.)
|
||||
Calculate the growth multiplier constant for a given server and threads.
|
||||
|
||||
The actual amount of money grown depends both linearly \*and\* exponentially on threads; this is only giving the exponential part that is used for the multiplier. See [grow](./bitburner.ns.grow.md) for more details.
|
||||
|
||||
As a result of the above, this multiplier does \*not\* depend on the amount of money on the server. Changing server.moneyAvailable and server.moneyMax will have no effect.
|
||||
|
||||
For the most common use-cases, you probably want either [formulas.hacking.growThreads](./bitburner.hackingformulas.growthreads.md) or [formulas.hacking.growAmount](./bitburner.hackingformulas.growamount.md) instead.
|
||||
|
||||
**Signature:**
|
||||
|
||||
@@ -17,7 +23,7 @@ growPercent(server: Server, threads: number, player: Person, cores?: number): nu
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| server | [Server](./bitburner.server.md) | Server info, typically from [getServer](./bitburner.ns.getserver.md) |
|
||||
| threads | number | Amount of thread. |
|
||||
| threads | number | Amount of threads. Can be fractional. |
|
||||
| player | [Person](./bitburner.person.md) | Player info, typically from [getPlayer](./bitburner.ns.getplayer.md) |
|
||||
| cores | number | _(Optional)_ Number of cores on the computer that will execute grow. |
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
|
||||
## HackingFormulas.growThreads() method
|
||||
|
||||
Calculate how many threads it will take to grow server to targetMoney. Starting money is server.moneyAvailable.
|
||||
Calculate how many threads it will take to grow server to targetMoney. Starting money is server.moneyAvailable. Note that when simulating the effect of [grow](./bitburner.ns.grow.md)<!-- -->, what matters is the state of the server and player when the grow \*finishes\*, not when it is started.
|
||||
|
||||
The growth amount depends both linearly \*and\* exponentially on threads; see [grow](./bitburner.ns.grow.md) for more details.
|
||||
|
||||
The inverse of this function is [formulas.hacking.growAmount](./bitburner.hackingformulas.growamount.md)<!-- -->, although it can work with fractional threads.
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ interface HackingFormulas
|
||||
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [growPercent(server, threads, player, cores)](./bitburner.hackingformulas.growpercent.md) | Calculate the percent a server would grow to. Not exact due to limitations of mathematics. (Ex: 3.0 would grow the server to 300% of its current value.) |
|
||||
| [growThreads(server, player, targetMoney, cores)](./bitburner.hackingformulas.growthreads.md) | Calculate how many threads it will take to grow server to targetMoney. Starting money is server.moneyAvailable. |
|
||||
| [growAmount(server, player, threads, cores)](./bitburner.hackingformulas.growamount.md) | <p>Calculate the amount of money a grow action will leave a server with. Starting money is server.moneyAvailable. Note that when simulating the effect of [grow](./bitburner.ns.grow.md)<!-- -->, what matters is the state of the server and player when the grow \*finishes\*, not when it is started.</p><p>The growth amount depends both linearly \*and\* exponentially on threads; see [grow](./bitburner.ns.grow.md) for more details.</p><p>The inverse of this function is [formulas.hacking.growThreads](./bitburner.hackingformulas.growthreads.md)<!-- -->, although it rounds up to integer threads.</p> |
|
||||
| [growPercent(server, threads, player, cores)](./bitburner.hackingformulas.growpercent.md) | <p>Calculate the growth multiplier constant for a given server and threads.</p><p>The actual amount of money grown depends both linearly \*and\* exponentially on threads; this is only giving the exponential part that is used for the multiplier. See [grow](./bitburner.ns.grow.md) for more details.</p><p>As a result of the above, this multiplier does \*not\* depend on the amount of money on the server. Changing server.moneyAvailable and server.moneyMax will have no effect.</p><p>For the most common use-cases, you probably want either [formulas.hacking.growThreads](./bitburner.hackingformulas.growthreads.md) or [formulas.hacking.growAmount](./bitburner.hackingformulas.growamount.md) instead.</p> |
|
||||
| [growThreads(server, player, targetMoney, cores)](./bitburner.hackingformulas.growthreads.md) | <p>Calculate how many threads it will take to grow server to targetMoney. Starting money is server.moneyAvailable. Note that when simulating the effect of [grow](./bitburner.ns.grow.md)<!-- -->, what matters is the state of the server and player when the grow \*finishes\*, not when it is started.</p><p>The growth amount depends both linearly \*and\* exponentially on threads; see [grow](./bitburner.ns.grow.md) for more details.</p><p>The inverse of this function is [formulas.hacking.growAmount](./bitburner.hackingformulas.growamount.md)<!-- -->, although it can work with fractional threads.</p> |
|
||||
| [growTime(server, player)](./bitburner.hackingformulas.growtime.md) | Calculate grow time. |
|
||||
| [hackChance(server, player)](./bitburner.hackingformulas.hackchance.md) | Calculate hack chance. (Ex: 0.25 would indicate a 25% chance of success.) |
|
||||
| [hackExp(server, player)](./bitburner.hackingformulas.hackexp.md) | Calculate hack exp for one thread. |
|
||||
|
||||
11
markdown/bitburner.infiltrationlocation.maxclearancelevel.md
Normal file
11
markdown/bitburner.infiltrationlocation.maxclearancelevel.md
Normal file
@@ -0,0 +1,11 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [InfiltrationLocation](./bitburner.infiltrationlocation.md) > [maxClearanceLevel](./bitburner.infiltrationlocation.maxclearancelevel.md)
|
||||
|
||||
## InfiltrationLocation.maxClearanceLevel property
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
maxClearanceLevel: number;
|
||||
```
|
||||
@@ -17,5 +17,7 @@ interface InfiltrationLocation
|
||||
| --- | --- | --- | --- |
|
||||
| [difficulty](./bitburner.infiltrationlocation.difficulty.md) | | number | |
|
||||
| [location](./bitburner.infiltrationlocation.location.md) | | [ILocation](./bitburner.ilocation.md) | |
|
||||
| [maxClearanceLevel](./bitburner.infiltrationlocation.maxclearancelevel.md) | | number | |
|
||||
| [reward](./bitburner.infiltrationlocation.reward.md) | | [InfiltrationReward](./bitburner.infiltrationreward.md) | |
|
||||
| [startingSecurityLevel](./bitburner.infiltrationlocation.startingsecuritylevel.md) | | number | |
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [InfiltrationLocation](./bitburner.infiltrationlocation.md) > [startingSecurityLevel](./bitburner.infiltrationlocation.startingsecuritylevel.md)
|
||||
|
||||
## InfiltrationLocation.startingSecurityLevel property
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
startingSecurityLevel: number;
|
||||
```
|
||||
@@ -162,6 +162,7 @@
|
||||
| [PlayerRequirement](./bitburner.playerrequirement.md) | Structured interface to requirements for joining a faction or company. For fields with numerical value > 0, the player must have at least this value. For fields with numerical value <<!-- -->= 0, the player must have at most this value. For "not", the sub-condition must be failed instead of passed. For "someCondition", at least one sub-condition must be passed. |
|
||||
| [ReactNode](./bitburner.reactnode.md) | <p>A stand-in for the real React.ReactNode. A [ReactElement](./bitburner.reactelement.md) is rendered dynamically with React. number and string are displayed directly. boolean, null, and undefined are ignored and not rendered. An array of ReactNodes will display all members of that array sequentially.</p><p>Use React.createElement to make the ReactElement type, see [creating an element without jsx](https://react.dev/reference/react/createElement#creating-an-element-without-jsx) from the official React documentation.</p> |
|
||||
| [ScriptArg](./bitburner.scriptarg.md) | |
|
||||
| [SimpleOpponentStats](./bitburner.simpleopponentstats.md) | |
|
||||
| [SleeveBladeburnerTask](./bitburner.sleevebladeburnertask.md) | |
|
||||
| [SleeveClassTask](./bitburner.sleeveclasstask.md) | |
|
||||
| [SleeveCompanyTask](./bitburner.sleevecompanytask.md) | |
|
||||
|
||||
@@ -9,7 +9,7 @@ Arguments passed into the script.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
readonly args: (string | number | boolean)[];
|
||||
readonly args: ScriptArg[];
|
||||
```
|
||||
|
||||
## Remarks
|
||||
|
||||
@@ -28,3 +28,5 @@ RAM cost: 0 GB
|
||||
|
||||
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")
|
||||
|
||||
|
||||
@@ -9,12 +9,7 @@ Start another script on any server.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
exec(
|
||||
script: string,
|
||||
hostname: string,
|
||||
threadOrOptions?: number | RunOptions,
|
||||
...args: (string | number | boolean)[]
|
||||
): number;
|
||||
exec(script: string, hostname: string, threadOrOptions?: number | RunOptions, ...args: ScriptArg[]): number;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -24,7 +19,7 @@ exec(
|
||||
| script | string | Filename of script to execute. This file must already exist on the target server. |
|
||||
| hostname | string | Hostname of the <code>target server</code> on which to execute the script. |
|
||||
| threadOrOptions | number \| [RunOptions](./bitburner.runoptions.md) | _(Optional)_ Either an integer number of threads for new script, or a [RunOptions](./bitburner.runoptions.md) object. Threads defaults to 1. |
|
||||
| args | (string \| number \| boolean)\[\] | Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the third argument threadOrOptions must be filled in with a value. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the third argument threadOrOptions must be filled in with a value. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -28,6 +28,12 @@ RAM cost: 0 GB
|
||||
|
||||
Allows Unix-like flag parsing.
|
||||
|
||||
We support 2 forms:
|
||||
|
||||
- Short form: the flag contains only 1 character, e.g. -v.
|
||||
|
||||
- Long form: the flag contains more than 1 character, e.g. --version.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -38,21 +44,24 @@ export async function main(ns) {
|
||||
['server', 'foodnstuff'], // a default string means this flag is a string
|
||||
['exclude', []], // a default array means this flag is a default array of string
|
||||
['help', false], // a default boolean means this flag is a boolean
|
||||
['v', false], // short form
|
||||
]);
|
||||
ns.tprint(data);
|
||||
}
|
||||
|
||||
// [home ~/]> run example.js
|
||||
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":false}
|
||||
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":false,"v":false}
|
||||
// [home ~/]> run example.js --delay 3000
|
||||
// {"_":[],"server":"foodnstuff","exclude":[],"help":false,"delay":3000}
|
||||
// {"_":[],"delay":3000,"server":"foodnstuff","exclude":[],"help":false,"v":false}
|
||||
// [home ~/]> run example.js --delay 3000 --server harakiri-sushi
|
||||
// {"_":[],"exclude":[],"help":false,"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
|
||||
// {"_":["hello","world"],"exclude":[],"help":false,"delay":3000,"server":"harakiri-sushi"}
|
||||
// {"_":["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
|
||||
// {"_":["hello","world"],"help":false,"delay":3000,"server":"harakiri-sushi","exclude":["a","b"]}
|
||||
// [home ~/]> run example.script --help
|
||||
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":true}
|
||||
// {"_":["hello","world"],"delay":3000,"server":"harakiri-sushi","exclude":["a","b"],"help":false,"v":false}
|
||||
// [home ~/]> run example.js --help
|
||||
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":true,"v":false}
|
||||
// [home ~/]> run example.js -v
|
||||
// {"_":[],"delay":0,"server":"foodnstuff","exclude":[],"help":false,"v":true}
|
||||
```
|
||||
|
||||
|
||||
@@ -29,6 +29,6 @@ Returns an object containing the Player’s hacking related multipliers. These m
|
||||
```js
|
||||
const mults = ns.getHackingMultipliers();
|
||||
print(`chance: ${mults.chance}`);
|
||||
print(`growthL ${mults.growth}`);
|
||||
print(`growth: ${mults.growth}`);
|
||||
```
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@ Get general info about a running script.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getRunningScript(
|
||||
filename?: FilenameOrPID,
|
||||
hostname?: string,
|
||||
...args: (string | number | boolean)[]
|
||||
): RunningScript | null;
|
||||
getRunningScript(filename?: FilenameOrPID, hostname?: string, ...args: ScriptArg[]): RunningScript | null;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -22,7 +18,7 @@ getRunningScript(
|
||||
| --- | --- | --- |
|
||||
| filename | [FilenameOrPID](./bitburner.filenameorpid.md) | _(Optional)_ Optional. Filename or PID of the script. |
|
||||
| hostname | string | _(Optional)_ Hostname of target server. Optional, defaults to the server the calling script is running on. |
|
||||
| args | (string \| number \| boolean)\[\] | Arguments to specify/identify the script. Optional, when looking for scripts run without arguments. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments to specify/identify the script. Optional, when looking for scripts run without arguments. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Get the exp gain of a script.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getScriptExpGain(script: string, host: string, ...args: (string | number | boolean)[]): number;
|
||||
getScriptExpGain(script: string, host: string, ...args: ScriptArg[]): number;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,7 +18,7 @@ getScriptExpGain(script: string, host: string, ...args: (string | number | boole
|
||||
| --- | --- | --- |
|
||||
| script | string | Filename of script. |
|
||||
| host | string | Server on which script is running. |
|
||||
| args | (string \| number \| boolean)\[\] | Arguments that the script is running with. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments that the script is running with. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Get the income of a script.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getScriptIncome(script: string, host: string, ...args: (string | number | boolean)[]): number;
|
||||
getScriptIncome(script: string, host: string, ...args: ScriptArg[]): number;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,7 +18,7 @@ getScriptIncome(script: string, host: string, ...args: (string | number | boolea
|
||||
| --- | --- | --- |
|
||||
| script | string | Filename of script. |
|
||||
| host | string | Server on which script is running. |
|
||||
| args | (string \| number \| boolean)\[\] | Arguments that the script is running with. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments that the script is running with. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Get all the logs of a script.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getScriptLogs(fn?: FilenameOrPID, host?: string, ...args: (string | number | boolean)[]): string[];
|
||||
getScriptLogs(fn?: FilenameOrPID, host?: string, ...args: ScriptArg[]): string[];
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,7 +18,7 @@ getScriptLogs(fn?: FilenameOrPID, host?: string, ...args: (string | number | boo
|
||||
| --- | --- | --- |
|
||||
| fn | [FilenameOrPID](./bitburner.filenameorpid.md) | _(Optional)_ Optional. Filename or PID of script to get logs from. |
|
||||
| host | string | _(Optional)_ Optional. Hostname of the server that the script is on. |
|
||||
| args | (string \| number \| boolean)\[\] | Arguments to identify which scripts to get logs for. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments to identify which scripts to get logs for. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -41,7 +41,9 @@ The multiplier scales exponentially with thread count, and its base depends on t
|
||||
|
||||
[growthAnalyze](./bitburner.ns.growthanalyze.md) can be used to determine the number of threads needed for a specified multiplicative portion of server growth.
|
||||
|
||||
To determine the effect of a single grow, obtain access to the Formulas API and use [formulas.hacking.growPercent](./bitburner.hackingformulas.growpercent.md)<!-- -->, or invert [growthAnalyze](./bitburner.ns.growthanalyze.md)<!-- -->.
|
||||
To determine the effect of a single grow, obtain access to the Formulas API and use [formulas.hacking.growPercent](./bitburner.hackingformulas.growamount.md)<!-- -->, or invert [growthAnalyze](./bitburner.ns.growthanalyze.md)<!-- -->.
|
||||
|
||||
To determine how many threads are needed to return a server to max money, obtain access to the Formulas API and use [formulas.hacking.growThreads](./bitburner.hackingformulas.growthreads.md)<!-- -->, or [NS.growthAnalyze()](./bitburner.ns.growthanalyze.md) \*if\* the server will be at the same security in the future.
|
||||
|
||||
Like [hack](./bitburner.ns.hack.md)<!-- -->, `grow` can be called on any hackable server, regardless of where the script is running. Hackable servers are any servers not owned by the player.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Check if a script is running.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
isRunning(script: FilenameOrPID, host?: string, ...args: (string | number | boolean)[]): boolean;
|
||||
isRunning(script: FilenameOrPID, host?: string, ...args: ScriptArg[]): boolean;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,7 +18,7 @@ isRunning(script: FilenameOrPID, host?: string, ...args: (string | number | bool
|
||||
| --- | --- | --- |
|
||||
| script | [FilenameOrPID](./bitburner.filenameorpid.md) | Filename or PID of script to check. This is case-sensitive. |
|
||||
| host | string | _(Optional)_ Hostname of target server. Optional, defaults to the server the calling script is running on. |
|
||||
| args | (string \| number \| boolean)\[\] | Arguments to specify/identify the script. Optional, when looking for scripts run without arguments. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments to specify/identify the script. Optional, when looking for scripts run without arguments. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function main(ns) {
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [args](./bitburner.ns.args.md) | <code>readonly</code> | (string \| number \| boolean)\[\] | Arguments passed into the script. |
|
||||
| [args](./bitburner.ns.args.md) | <code>readonly</code> | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments passed into the script. |
|
||||
| [bladeburner](./bitburner.ns.bladeburner.md) | <code>readonly</code> | [Bladeburner](./bitburner.bladeburner.md) | Namespace for bladeburner functions. Contains spoilers. |
|
||||
| [codingcontract](./bitburner.ns.codingcontract.md) | <code>readonly</code> | [CodingContract](./bitburner.codingcontract.md) | Namespace for codingcontract functions. |
|
||||
| [corporation](./bitburner.ns.corporation.md) | <code>readonly</code> | [Corporation](./bitburner.corporation.md) | Namespace for corporation functions. Contains spoilers. |
|
||||
|
||||
@@ -32,7 +32,9 @@ True if the player clicks “Yes”; false if the player clicks “No”; or the
|
||||
|
||||
RAM cost: 0 GB
|
||||
|
||||
Prompts the player with a dialog box. Here is an explanation of the various options.
|
||||
Prompts the player with a dialog box and returns a promise. If the player cancels this dialog box (press X button or click outside the dialog box), the promise is resolved with a default value (empty string or "false"). If this API is called again while the old dialog box still exists, the old dialog box will be replaced with a new one, and the old promise will be resolved with the default value.
|
||||
|
||||
Here is an explanation of the various options.
|
||||
|
||||
- `options.type` is not provided to the function. If `options.type` is left out and only a string is passed to the function, then the default behavior is to create a boolean dialog box.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Start another script on the current server.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
run(script: string, threadOrOptions?: number | RunOptions, ...args: (string | number | boolean)[]): number;
|
||||
run(script: string, threadOrOptions?: number | RunOptions, ...args: ScriptArg[]): number;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,7 +18,7 @@ run(script: string, threadOrOptions?: number | RunOptions, ...args: (string | nu
|
||||
| --- | --- | --- |
|
||||
| script | string | Filename of script to run. |
|
||||
| threadOrOptions | number \| [RunOptions](./bitburner.runoptions.md) | _(Optional)_ Either an integer number of threads for new script, or a [RunOptions](./bitburner.runoptions.md) object. Threads defaults to 1. |
|
||||
| args | (string \| number \| boolean)\[\] | Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the second argument threadOrOptions must be filled in with a value. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the second argument threadOrOptions must be filled in with a value. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Terminate current script and start another in a defined number of milliseconds.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
spawn(script: string, threadOrOptions?: number | SpawnOptions, ...args: (string | number | boolean)[]): void;
|
||||
spawn(script: string, threadOrOptions?: number | SpawnOptions, ...args: ScriptArg[]): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,7 +18,7 @@ spawn(script: string, threadOrOptions?: number | SpawnOptions, ...args: (string
|
||||
| --- | --- | --- |
|
||||
| script | string | Filename of script to execute. |
|
||||
| threadOrOptions | number \| [SpawnOptions](./bitburner.spawnoptions.md) | _(Optional)_ Either an integer number of threads for new script, or a [SpawnOptions](./bitburner.spawnoptions.md) object. Threads defaults to 1 and spawnDelay defaults to 10,000 ms. |
|
||||
| args | (string \| number \| boolean)\[\] | Additional arguments to pass into the new script that is being run. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Additional arguments to pass into the new script that is being run. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ void
|
||||
|
||||
RAM cost: 0.05 GB
|
||||
|
||||
Runs the SQLInject.exe program on the target server. SQLInject.exe must exist on your home computer.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Open the tail window of a script.
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
tail(fn?: FilenameOrPID, host?: string, ...args: (string | number | boolean)[]): void;
|
||||
tail(fn?: FilenameOrPID, host?: string, ...args: ScriptArg[]): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
@@ -18,7 +18,7 @@ tail(fn?: FilenameOrPID, host?: string, ...args: (string | number | boolean)[]):
|
||||
| --- | --- | --- |
|
||||
| fn | [FilenameOrPID](./bitburner.filenameorpid.md) | _(Optional)_ Optional. Filename or PID of the script being tailed. If omitted, the current script is tailed. |
|
||||
| host | string | _(Optional)_ Optional. Hostname of the script being tailed. Defaults to the server this script is running on. If args are specified, this is not optional. |
|
||||
| args | (string \| number \| boolean)\[\] | Arguments for the script being tailed. |
|
||||
| args | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments for the script being tailed. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@ Script's arguments
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
args: (string | number | boolean)[];
|
||||
args: ScriptArg[];
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ interface ProcessInfo
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [args](./bitburner.processinfo.args.md) | | (string \| number \| boolean)\[\] | Script's arguments |
|
||||
| [args](./bitburner.processinfo.args.md) | | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Script's arguments |
|
||||
| [filename](./bitburner.processinfo.filename.md) | | string | Script name. |
|
||||
| [pid](./bitburner.processinfo.pid.md) | | number | Process ID |
|
||||
| [temporary](./bitburner.processinfo.temporary.md) | | boolean | Whether this process is excluded from saves |
|
||||
|
||||
@@ -9,5 +9,5 @@ Arguments the script was called with
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
args: (string | number | boolean)[];
|
||||
args: ScriptArg[];
|
||||
```
|
||||
|
||||
@@ -15,7 +15,7 @@ interface RunningScript
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [args](./bitburner.runningscript.args.md) | | (string \| number \| boolean)\[\] | Arguments the script was called with |
|
||||
| [args](./bitburner.runningscript.args.md) | | [ScriptArg](./bitburner.scriptarg.md)<!-- -->\[\] | Arguments the script was called with |
|
||||
| [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 |
|
||||
|
||||
20
markdown/bitburner.simpleopponentstats.md
Normal file
20
markdown/bitburner.simpleopponentstats.md
Normal file
@@ -0,0 +1,20 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [SimpleOpponentStats](./bitburner.simpleopponentstats.md)
|
||||
|
||||
## SimpleOpponentStats type
|
||||
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
type SimpleOpponentStats = {
|
||||
wins: number;
|
||||
losses: number;
|
||||
winStreak: number;
|
||||
highestWinStreak: number;
|
||||
favor: number;
|
||||
bonusPercent: number;
|
||||
bonusDescription: string;
|
||||
};
|
||||
```
|
||||
@@ -33,7 +33,7 @@ This function will automatically set you to start working on creating the specif
|
||||
|
||||
This function returns true if you successfully start working on the specified program, and false otherwise.
|
||||
|
||||
Note that creating a program using this function has the same hacking level requirements as it normally would. These level requirements are: \* BruteSSH.exe: 50 \* FTPCrack.exe: 100 \* relaySMTP.exe: 250 \* HTTPWorm.exe: 500 \* SQLInject.exe: 750 \* DeepscanV1.exe: 75 \* DeepscanV2.exe: 400 \* ServerProfiler.exe: 75 \* AutoLink.exe: 25
|
||||
Note that creating a program using this function has the same hacking level requirements as it normally would. These level requirements are:<br/> - BruteSSH.exe: 50<br/> - FTPCrack.exe: 100<br/> - relaySMTP.exe: 250<br/> - HTTPWorm.exe: 500<br/> - SQLInject.exe: 750<br/> - DeepscanV1.exe: 75<br/> - DeepscanV2.exe: 400<br/> - ServerProfiler.exe: 75<br/> - AutoLink.exe: 25
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
32
markdown/bitburner.singularity.getfactionenemies.md
Normal file
32
markdown/bitburner.singularity.getfactionenemies.md
Normal file
@@ -0,0 +1,32 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [Singularity](./bitburner.singularity.md) > [getFactionEnemies](./bitburner.singularity.getfactionenemies.md)
|
||||
|
||||
## Singularity.getFactionEnemies() method
|
||||
|
||||
Get a list of enemies of a faction.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
getFactionEnemies(faction: string): string[];
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| faction | string | Name of faction. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
string\[\]
|
||||
|
||||
Array containing the names of all enemies of the faction.
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 3 GB \* 16/4/1
|
||||
|
||||
Returns an array containing the names (as strings) of all factions that are enemies of the specified faction.
|
||||
|
||||
@@ -48,6 +48,7 @@ This API requires Source-File 4 to use. The RAM cost of all these functions is m
|
||||
| [getCurrentWork()](./bitburner.singularity.getcurrentwork.md) | Get the current work the player is doing. |
|
||||
| [getDarkwebProgramCost(programName)](./bitburner.singularity.getdarkwebprogramcost.md) | Check the price of an exploit on the dark web |
|
||||
| [getDarkwebPrograms()](./bitburner.singularity.getdarkwebprograms.md) | Get a list of programs offered on the dark web. |
|
||||
| [getFactionEnemies(faction)](./bitburner.singularity.getfactionenemies.md) | Get a list of enemies of a faction. |
|
||||
| [getFactionFavor(faction)](./bitburner.singularity.getfactionfavor.md) | Get faction favor. |
|
||||
| [getFactionFavorGain(faction)](./bitburner.singularity.getfactionfavorgain.md) | Get faction favor gain. |
|
||||
| [getFactionInviteRequirements(faction)](./bitburner.singularity.getfactioninviterequirements.md) | List conditions for being invited to a faction. |
|
||||
|
||||
@@ -8,5 +8,10 @@
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
type SleeveInfiltrateTask = { type: "INFILTRATE"; cyclesWorked: number; cyclesNeeded: number };
|
||||
type SleeveInfiltrateTask = {
|
||||
type: "INFILTRATE";
|
||||
cyclesWorked: number;
|
||||
cyclesNeeded: number;
|
||||
nextCompletion: Promise<void>;
|
||||
};
|
||||
```
|
||||
|
||||
508
package-lock.json
generated
508
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bitburner",
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
"dependencies": {
|
||||
@@ -24,12 +24,9 @@
|
||||
"arg": "^5.0.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"better-react-mathjax": "^2.0.3",
|
||||
"buffer": "^6.0.3",
|
||||
"clsx": "^1.2.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"escodegen": "^2.1.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"jquery": "^3.7.1",
|
||||
"js-sha256": "^0.9.0",
|
||||
"jszip": "^3.10.1",
|
||||
"material-ui-color": "^1.2.0",
|
||||
@@ -77,10 +74,8 @@
|
||||
"eslint": "^8.52.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.3.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"http-server": "^14.1.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jsdom": "^20.0.3",
|
||||
@@ -92,13 +87,11 @@
|
||||
"react-refresh": "^0.14.0",
|
||||
"rehype-mathjax": "^4.0.3",
|
||||
"remark-math": "^5.1.1",
|
||||
"source-map": "^0.7.4",
|
||||
"start-server-and-test": "^1.15.4",
|
||||
"style-loader": "^3.3.3",
|
||||
"typescript": "^5.2.2",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
"webpack-dev-server": "^4.15.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -2428,21 +2421,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz",
|
||||
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@hapi/topo": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
|
||||
"integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
|
||||
@@ -4104,27 +4082,6 @@
|
||||
"string-argv": "~0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/address": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
|
||||
"integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sideway/formula": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
|
||||
"integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@sideway/pinpoint": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
|
||||
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
@@ -5577,15 +5534,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"node_modules/asynciterator.prototype": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz",
|
||||
@@ -5631,16 +5579,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||
@@ -5944,6 +5882,7 @@
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -5959,18 +5898,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/batch": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
|
||||
@@ -6153,29 +6080,6 @@
|
||||
"node-int64": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
@@ -6365,15 +6269,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/check-more-types": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
|
||||
"integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
@@ -6757,15 +6652,6 @@
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
},
|
||||
"node_modules/corser": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
|
||||
"integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
@@ -7468,12 +7354,6 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexer": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
|
||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -8276,21 +8156,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/event-stream": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
|
||||
"integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"duplexer": "~0.1.1",
|
||||
"from": "~0",
|
||||
"map-stream": "~0.1.0",
|
||||
"pause-stream": "0.0.11",
|
||||
"split": "0.3",
|
||||
"stream-combiner": "~0.0.4",
|
||||
"through": "~2.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
@@ -8592,31 +8457,6 @@
|
||||
"node": "^10.12.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-loader": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
|
||||
"integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loader-utils": "^2.0.0",
|
||||
"schema-utils": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^4.0.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-saver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
|
||||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
|
||||
},
|
||||
"node_modules/filename-reserved-regex": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
|
||||
@@ -9034,12 +8874,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/from": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
|
||||
"integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
@@ -9931,103 +9765,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz",
|
||||
"integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"basic-auth": "^2.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"corser": "^2.0.1",
|
||||
"he": "^1.2.0",
|
||||
"html-encoding-sniffer": "^3.0.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"mime": "^1.6.0",
|
||||
"minimist": "^1.2.6",
|
||||
"opener": "^1.5.1",
|
||||
"portfinder": "^1.0.28",
|
||||
"secure-compare": "3.0.1",
|
||||
"union": "~0.5.0",
|
||||
"url-join": "^4.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"http-server": "bin/http-server"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/http-server/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/http2-wrapper": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
|
||||
@@ -10092,25 +9829,6 @@
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
@@ -12535,24 +12253,6 @@
|
||||
"integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "17.11.0",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz",
|
||||
"integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@hapi/hoek": "^9.0.0",
|
||||
"@hapi/topo": "^5.0.0",
|
||||
"@sideway/address": "^4.1.3",
|
||||
"@sideway/formula": "^3.0.1",
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jquery": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
|
||||
},
|
||||
"node_modules/js-sha256": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
|
||||
@@ -12863,15 +12563,6 @@
|
||||
"shell-quote": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lazy-ass": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
|
||||
"integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "> 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/leven": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||
@@ -13128,12 +12819,6 @@
|
||||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/map-stream": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
|
||||
"integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/markdown-table": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz",
|
||||
@@ -14147,18 +13832,6 @@
|
||||
"resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz",
|
||||
"integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA=="
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.35.0",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.35.0.tgz",
|
||||
@@ -14568,15 +14241,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/opener": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"opener": "bin/opener-bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
|
||||
@@ -14804,15 +14468,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pause-stream": {
|
||||
"version": "0.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
|
||||
"integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"through": "~2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
@@ -14971,29 +14626,6 @@
|
||||
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
|
||||
"integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA=="
|
||||
},
|
||||
"node_modules/portfinder": {
|
||||
"version": "1.0.32",
|
||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
|
||||
"integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async": "^2.6.4",
|
||||
"debug": "^3.2.7",
|
||||
"mkdirp": "^0.5.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/portfinder/node_modules/debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
@@ -15247,21 +14879,6 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/ps-tree": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
|
||||
"integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"event-stream": "=3.3.4"
|
||||
},
|
||||
"bin": {
|
||||
"ps-tree": "bin/ps-tree.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
@@ -16144,15 +15761,6 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sade": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||
@@ -16252,12 +15860,6 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/secure-compare": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
|
||||
"integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@@ -16724,18 +16326,6 @@
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/split": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
|
||||
"integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"through": "2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
|
||||
@@ -16768,30 +16358,6 @@
|
||||
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/start-server-and-test": {
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.15.4.tgz",
|
||||
"integrity": "sha512-ucQtp5+UCr0m4aHlY+aEV2JSYNTiMZKdSKK/bsIr6AlmwAWDYDnV7uGlWWEtWa7T4XvRI5cPYcPcQgeLqpz+Tg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"arg": "^5.0.2",
|
||||
"bluebird": "3.7.2",
|
||||
"check-more-types": "2.24.0",
|
||||
"debug": "4.3.4",
|
||||
"execa": "5.1.1",
|
||||
"lazy-ass": "1.6.0",
|
||||
"ps-tree": "1.2.0",
|
||||
"wait-on": "7.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"server-test": "src/bin/start.js",
|
||||
"start-server-and-test": "src/bin/start.js",
|
||||
"start-test": "src/bin/start.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
@@ -16801,15 +16367,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-combiner": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
|
||||
"integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"duplexer": "~0.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
@@ -17195,12 +16752,6 @@
|
||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/thunky": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
@@ -17542,18 +17093,6 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/union": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
|
||||
"integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"qs": "^6.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unist-util-find-after": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz",
|
||||
@@ -17697,12 +17236,6 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
@@ -17857,25 +17390,6 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/wait-on": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz",
|
||||
"integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"joi": "^17.7.0",
|
||||
"lodash": "^4.17.21",
|
||||
"minimist": "^1.2.7",
|
||||
"rxjs": "^7.8.0"
|
||||
},
|
||||
"bin": {
|
||||
"wait-on": "bin/wait-on"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/walker": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
|
||||
@@ -18028,9 +17542,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
|
||||
"integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
|
||||
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.10",
|
||||
@@ -18104,9 +17618,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server": {
|
||||
"version": "4.15.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz",
|
||||
"integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==",
|
||||
"version": "4.15.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
|
||||
"integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/bonjour": "^3.5.9",
|
||||
@@ -18137,7 +17651,7 @@
|
||||
"serve-index": "^1.9.1",
|
||||
"sockjs": "^0.3.24",
|
||||
"spdy": "^4.0.2",
|
||||
"webpack-dev-middleware": "^5.3.1",
|
||||
"webpack-dev-middleware": "^5.3.4",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"bin": {
|
||||
|
||||
11
package.json
11
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "bitburner",
|
||||
"license": "SEE LICENSE IN license.txt",
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.1",
|
||||
"main": "electron-main.js",
|
||||
"author": {
|
||||
"name": "Daniel Xie, hydroflame, et al."
|
||||
@@ -24,12 +24,9 @@
|
||||
"arg": "^5.0.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"better-react-mathjax": "^2.0.3",
|
||||
"buffer": "^6.0.3",
|
||||
"clsx": "^1.2.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"escodegen": "^2.1.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"jquery": "^3.7.1",
|
||||
"js-sha256": "^0.9.0",
|
||||
"jszip": "^3.10.1",
|
||||
"material-ui-color": "^1.2.0",
|
||||
@@ -78,10 +75,8 @@
|
||||
"eslint": "^8.52.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.3.0",
|
||||
"html-webpack-plugin": "^5.5.3",
|
||||
"http-server": "^14.1.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jsdom": "^20.0.3",
|
||||
@@ -91,13 +86,11 @@
|
||||
"prettier": "^2.8.8",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-refresh": "^0.14.0",
|
||||
"source-map": "^0.7.4",
|
||||
"start-server-and-test": "^1.15.4",
|
||||
"style-loader": "^3.3.3",
|
||||
"typescript": "^5.2.2",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-dev-server": "^4.15.2",
|
||||
"remark-math": "^5.1.1",
|
||||
"rehype-mathjax": "^4.0.3"
|
||||
},
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import {
|
||||
AugmentationName,
|
||||
BlackOperationName,
|
||||
BladeSkillName,
|
||||
CityName,
|
||||
CompletedProgramName,
|
||||
CorpUnlockName,
|
||||
FactionName,
|
||||
IndustryType,
|
||||
} from "@enums";
|
||||
import { SkillNames } from "../Bladeburner/data/SkillNames";
|
||||
import { Skills } from "../Bladeburner/Skills";
|
||||
import { Skills } from "../Bladeburner/data/Skills";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Exploit } from "../Exploits/Exploit";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
@@ -31,6 +30,7 @@ import { workerScripts } from "../Netscript/WorkerScripts";
|
||||
|
||||
import { getRecordValues } from "../Types/Record";
|
||||
import { ServerConstants } from "../Server/data/Constants";
|
||||
import { blackOpsArray } from "../Bladeburner/data/BlackOperations";
|
||||
|
||||
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
|
||||
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
|
||||
@@ -65,7 +65,7 @@ function bitNodeFinishedState(): boolean {
|
||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||
if (!(wd instanceof Server)) return false;
|
||||
if (wd.backdoorInstalled) return true;
|
||||
return Player.bladeburner !== null && BlackOperationName.OperationDaedalus in Player.bladeburner.blackops;
|
||||
return Player.bladeburner !== null && Player.bladeburner.numBlackOpsComplete >= blackOpsArray.length;
|
||||
}
|
||||
|
||||
function hasAccessToSF(bn: number): boolean {
|
||||
@@ -432,8 +432,7 @@ export const achievements: Record<string, Achievement> = {
|
||||
Icon: "BLADEOVERCLOCK",
|
||||
Visible: () => hasAccessToSF(6),
|
||||
Condition: () =>
|
||||
Player.bladeburner !== null &&
|
||||
Player.bladeburner.skills[SkillNames.Overclock] === Skills[SkillNames.Overclock].maxLvl,
|
||||
Player.bladeburner?.getSkillLevel(BladeSkillName.overclock) === Skills[BladeSkillName.overclock].maxLvl,
|
||||
},
|
||||
BLADEBURNER_UNSPENT_100000: {
|
||||
...achievementData.BLADEBURNER_UNSPENT_100000,
|
||||
|
||||
@@ -141,16 +141,16 @@ function generateStatsDescription(mults: Multipliers, programs?: string[], start
|
||||
// Hacknet: costs are negative
|
||||
if (mults.hacknet_node_money !== 1) desc += `\n+${f(mults.hacknet_node_money - 1)} hacknet production`;
|
||||
if (mults.hacknet_node_purchase_cost !== 1) {
|
||||
desc += `\n-${f(-(mults.hacknet_node_purchase_cost - 1))} hacknet nodes cost`;
|
||||
desc += `\n-${f(-(mults.hacknet_node_purchase_cost - 1))} hacknet purchase cost`;
|
||||
}
|
||||
if (mults.hacknet_node_level_cost !== 1) {
|
||||
desc += `\n-${f(-(mults.hacknet_node_level_cost - 1))} hacknet level upgrade cost`;
|
||||
}
|
||||
if (mults.hacknet_node_ram_cost !== 1) {
|
||||
desc += `\n-${f(-(mults.hacknet_node_ram_cost - 1))} hacknet RAM cost`;
|
||||
desc += `\n-${f(-(mults.hacknet_node_ram_cost - 1))} hacknet RAM upgrade cost`;
|
||||
}
|
||||
if (mults.hacknet_node_core_cost !== 1) {
|
||||
desc += `\n-${f(-(mults.hacknet_node_core_cost - 1))} hacknet core cost`;
|
||||
desc += `\n-${f(-(mults.hacknet_node_core_cost - 1))} hacknet core upgrade cost`;
|
||||
}
|
||||
|
||||
// Bladeburner
|
||||
|
||||
@@ -390,7 +390,7 @@ export const Augmentations: Record<AugmentationName, Augmentation> = (() => {
|
||||
moneyCost: 50e12,
|
||||
info:
|
||||
"Developed by a pioneer in Grafting research, this implant generates pulses of stability which seem to have a nullifying effect versus the Entropy virus.\n\n" +
|
||||
"Note: For unknown reasons, the lowercase 'n' appears to be an integral component to its functionality.",
|
||||
"Note: For unknown reasons, the lowercase 'v' appears to be an integral component to its functionality.",
|
||||
stats: "This Augmentation removes the Entropy virus, and prevents it from affecting you again.",
|
||||
factions: [],
|
||||
},
|
||||
|
||||
@@ -86,9 +86,7 @@ function getRandomBonus(): CircadianBonus {
|
||||
hacknet_node_core_cost: 0.85,
|
||||
hacknet_node_level_cost: 0.85,
|
||||
},
|
||||
description:
|
||||
"Increases the amount of money produced by Hacknet Nodes by 20%.\n" +
|
||||
"Decreases all costs related to Hacknet Node by 15%.",
|
||||
description: "Increases Hacknet production by 20%.\n" + "Decreases all costs related to Hacknet by 15%.",
|
||||
},
|
||||
{
|
||||
bonuses: {
|
||||
|
||||
@@ -90,7 +90,7 @@ export enum AugmentationName {
|
||||
BrachiBlades = "BrachiBlades",
|
||||
BionicArms = "Bionic Arms",
|
||||
SNA = "Social Negotiation Assistant (S.N.A)",
|
||||
CongruityImplant = "nickofolas Congruity Implant",
|
||||
CongruityImplant = "violet Congruity Implant",
|
||||
HydroflameLeftArm = "Hydroflame Left Arm",
|
||||
BigDsBigBrain = "BigD's Big ... Brain",
|
||||
ZOE = "Z.O.Ë.",
|
||||
|
||||
@@ -127,7 +127,7 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||
<Typography>- Money</Typography>
|
||||
<Typography>- Scripts on every computer but your home computer</Typography>
|
||||
<Typography>- Purchased servers</Typography>
|
||||
<Typography>- Hacknet Nodes</Typography>
|
||||
<Typography>- Hacknet</Typography>
|
||||
<Typography>- Faction/Company reputation</Typography>
|
||||
<Typography>- Stocks</Typography>
|
||||
<br />
|
||||
|
||||
@@ -23,14 +23,19 @@ interface IBitNodeModifiedStatsProps {
|
||||
color: string;
|
||||
}
|
||||
|
||||
function customFormatPercent(value: number): string {
|
||||
return formatPercent(value, 2, 100);
|
||||
}
|
||||
|
||||
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)
|
||||
return <Typography color={props.color}>{formatPercent(props.base)}</Typography>;
|
||||
return <Typography color={props.color}>{customFormatPercent(props.base)}</Typography>;
|
||||
|
||||
return (
|
||||
<Typography color={props.color}>
|
||||
<span style={{ opacity: 0.5 }}>{formatPercent(props.base)}</span> {formatPercent(props.base * props.mult)}
|
||||
<span style={{ opacity: 0.5 }}>{customFormatPercent(props.base)}</span>{" "}
|
||||
{customFormatPercent(props.base * props.mult)}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
@@ -192,28 +197,28 @@ export function PlayerMultipliers(): React.ReactElement {
|
||||
];
|
||||
const rightColData: MultiplierListItemData[] = [
|
||||
{
|
||||
mult: "Hacknet Node Production",
|
||||
mult: "Hacknet Production",
|
||||
current: Player.mults.hacknet_node_money,
|
||||
augmented: Player.mults.hacknet_node_money * mults.hacknet_node_money,
|
||||
bnMult: currentNodeMults.HacknetNodeMoney,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node Purchase Cost",
|
||||
mult: "Hacknet Purchase Cost",
|
||||
current: Player.mults.hacknet_node_purchase_cost,
|
||||
augmented: Player.mults.hacknet_node_purchase_cost * mults.hacknet_node_purchase_cost,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node RAM Upgrade Cost",
|
||||
mult: "Hacknet RAM Upgrade Cost",
|
||||
current: Player.mults.hacknet_node_ram_cost,
|
||||
augmented: Player.mults.hacknet_node_ram_cost * mults.hacknet_node_ram_cost,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node Core Purchase Cost",
|
||||
mult: "Hacknet Core Purchase Cost",
|
||||
current: Player.mults.hacknet_node_core_cost,
|
||||
augmented: Player.mults.hacknet_node_core_cost * mults.hacknet_node_core_cost,
|
||||
},
|
||||
{
|
||||
mult: "Hacknet Node Level Upgrade Cost",
|
||||
mult: "Hacknet Level Upgrade Cost",
|
||||
current: Player.mults.hacknet_node_level_cost,
|
||||
augmented: Player.mults.hacknet_node_level_cost * mults.hacknet_node_level_cost,
|
||||
},
|
||||
|
||||
@@ -463,20 +463,20 @@ export function initBitNodes() {
|
||||
its level up to a maximum of 3. This Source-File grants the following benefits:
|
||||
<br />
|
||||
<br />
|
||||
Level 1: 25% increased stat multipliers from Node Power
|
||||
Level 1: 100% increased stat multipliers from Node Power
|
||||
<br />
|
||||
Level 2: Permanently unlocks the go.cheat API
|
||||
<br />
|
||||
Level 3: 25% increased success rate for the go.cheat API
|
||||
Level 3: 25% additive increased success rate for the go.cheat API
|
||||
<br />
|
||||
<br />
|
||||
This Source-File also increases the maximum favor you can gain for each faction from IPvGO by:
|
||||
This Source-File also increases the maximum favor you can gain for each faction from IPvGO to:
|
||||
<br />
|
||||
Level 1: +10
|
||||
Level 1: 80
|
||||
<br />
|
||||
Level 2: +20
|
||||
Level 2: 100
|
||||
<br />
|
||||
Level 3: +40
|
||||
Level 3: 120
|
||||
</>
|
||||
),
|
||||
);
|
||||
|
||||
@@ -43,7 +43,7 @@ export const BitNodeMultipliersDisplay = ({ n, level }: IProps): React.ReactElem
|
||||
const mults = getBitNodeMultipliers(n, level ?? Math.min(Player.sourceFileLvl(n) + 1, maxSfLevel));
|
||||
|
||||
return (
|
||||
<Box sx={{ columnCount: 2, columnGap: 1, mb: -2 }}>
|
||||
<Box sx={{ columnCount: 2, columnGap: 1, mb: n === 1 ? 0 : -2 }}>
|
||||
<GeneralMults n={n} mults={mults} />
|
||||
<SkillMults n={n} mults={mults} />
|
||||
<FactionMults n={n} mults={mults} />
|
||||
|
||||
@@ -241,7 +241,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | <BitNodePortal n={14} level={n(4)} flume={props.flume} destroyedBitNode={destroyed} /> | | O__/ | / \__ | | O | | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | <BitNodePortal n={14} level={n(14)} flume={props.flume} destroyedBitNode={destroyed} /> | | O__/ | / \__ | | O | | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { addOffset } from "../utils/helpers/addOffset";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
import { Bladeburner } from "./Bladeburner";
|
||||
import { Person } from "../PersonObjects/Person";
|
||||
import { calculateIntelligenceBonus } from "../PersonObjects/formulas/intelligence";
|
||||
|
||||
interface ISuccessChanceParams {
|
||||
est: boolean;
|
||||
}
|
||||
|
||||
class StatsMultiplier {
|
||||
[key: string]: number;
|
||||
|
||||
hack = 0;
|
||||
str = 0;
|
||||
def = 0;
|
||||
dex = 0;
|
||||
agi = 0;
|
||||
cha = 0;
|
||||
int = 0;
|
||||
}
|
||||
|
||||
export interface IActionParams {
|
||||
name?: string;
|
||||
level?: number;
|
||||
maxLevel?: number;
|
||||
autoLevel?: boolean;
|
||||
baseDifficulty?: number;
|
||||
difficultyFac?: number;
|
||||
rewardFac?: number;
|
||||
successes?: number;
|
||||
failures?: number;
|
||||
rankGain?: number;
|
||||
rankLoss?: number;
|
||||
hpLoss?: number;
|
||||
hpLost?: number;
|
||||
isStealth?: boolean;
|
||||
isKill?: boolean;
|
||||
count?: number;
|
||||
weights?: StatsMultiplier;
|
||||
decays?: StatsMultiplier;
|
||||
teamCount?: number;
|
||||
}
|
||||
|
||||
export class Action {
|
||||
name = "";
|
||||
|
||||
// Difficulty scales with level. See getDifficulty() method
|
||||
level = 1;
|
||||
maxLevel = 1;
|
||||
autoLevel = true;
|
||||
baseDifficulty = 100;
|
||||
difficultyFac = 1.01;
|
||||
|
||||
// Rank increase/decrease is affected by this exponent
|
||||
rewardFac = 1.02;
|
||||
|
||||
successes = 0;
|
||||
failures = 0;
|
||||
|
||||
// All of these scale with level/difficulty
|
||||
rankGain = 0;
|
||||
rankLoss = 0;
|
||||
hpLoss = 0;
|
||||
hpLost = 0;
|
||||
|
||||
// Action Category. Current categories are stealth and kill
|
||||
isStealth = false;
|
||||
isKill = false;
|
||||
|
||||
/**
|
||||
* Number of this contract remaining, and its growth rate
|
||||
* Growth rate is an integer and the count will increase by that integer every "cycle"
|
||||
*/
|
||||
count: number = getRandomInt(1e3, 25e3);
|
||||
|
||||
// Weighting of each stat in determining action success rate
|
||||
weights: StatsMultiplier = {
|
||||
hack: 1 / 7,
|
||||
str: 1 / 7,
|
||||
def: 1 / 7,
|
||||
dex: 1 / 7,
|
||||
agi: 1 / 7,
|
||||
cha: 1 / 7,
|
||||
int: 1 / 7,
|
||||
};
|
||||
// Diminishing returns of stats (stat ^ decay where 0 <= decay <= 1)
|
||||
decays: StatsMultiplier = {
|
||||
hack: 0.9,
|
||||
str: 0.9,
|
||||
def: 0.9,
|
||||
dex: 0.9,
|
||||
agi: 0.9,
|
||||
cha: 0.9,
|
||||
int: 0.9,
|
||||
};
|
||||
teamCount = 0;
|
||||
|
||||
// Base Class for Contracts, Operations, and BlackOps
|
||||
constructor(params: IActionParams | null = null) {
|
||||
// | null = null
|
||||
if (params && params.name) this.name = params.name;
|
||||
|
||||
if (params && params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
||||
if (params && params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
||||
|
||||
if (params && params.rewardFac) this.rewardFac = params.rewardFac;
|
||||
if (params && params.rankGain) this.rankGain = params.rankGain;
|
||||
if (params && params.rankLoss) this.rankLoss = params.rankLoss;
|
||||
if (params && params.hpLoss) this.hpLoss = params.hpLoss;
|
||||
|
||||
if (params && params.isStealth) this.isStealth = params.isStealth;
|
||||
if (params && params.isKill) this.isKill = params.isKill;
|
||||
|
||||
if (params && params.count) this.count = params.count;
|
||||
|
||||
if (params && params.weights) this.weights = params.weights;
|
||||
if (params && params.decays) this.decays = params.decays;
|
||||
|
||||
// Check to make sure weights are summed properly
|
||||
let sum = 0;
|
||||
for (const weight of Object.keys(this.weights)) {
|
||||
if (Object.hasOwn(this.weights, weight)) {
|
||||
sum += this.weights[weight];
|
||||
}
|
||||
}
|
||||
if (sum - 1 >= 10 * Number.EPSILON) {
|
||||
throw new Error(
|
||||
"Invalid weights when constructing Action " +
|
||||
this.name +
|
||||
". The weights should sum up to 1. They sum up to :" +
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
for (const decay of Object.keys(this.decays)) {
|
||||
if (Object.hasOwn(this.decays, decay)) {
|
||||
if (this.decays[decay] > 1) {
|
||||
throw new Error(`Invalid decays when constructing Action ${this.name}. Decay value cannot be greater than 1`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getDifficulty(): number {
|
||||
const difficulty = this.baseDifficulty * Math.pow(this.difficultyFac, this.level - 1);
|
||||
if (isNaN(difficulty)) {
|
||||
throw new Error("Calculated NaN in Action.getDifficulty()");
|
||||
}
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for success. Should be called when an action has completed
|
||||
* @param inst {Bladeburner} - Bladeburner instance
|
||||
*/
|
||||
attempt(inst: Bladeburner, person: Person): boolean {
|
||||
return Math.random() < this.getSuccessChance(inst, person);
|
||||
}
|
||||
|
||||
// To be implemented by subtypes
|
||||
getActionTimePenalty(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getActionTime(inst: Bladeburner, person: Person): number {
|
||||
const difficulty = this.getDifficulty();
|
||||
let baseTime = difficulty / BladeburnerConstants.DifficultyToTimeFactor;
|
||||
const skillFac = inst.skillMultipliers.actionTime; // Always < 1
|
||||
|
||||
const effAgility = person.skills.agility * inst.skillMultipliers.effAgi;
|
||||
const effDexterity = person.skills.dexterity * inst.skillMultipliers.effDex;
|
||||
const statFac =
|
||||
0.5 *
|
||||
(Math.pow(effAgility, BladeburnerConstants.EffAgiExponentialFactor) +
|
||||
Math.pow(effDexterity, BladeburnerConstants.EffDexExponentialFactor) +
|
||||
effAgility / BladeburnerConstants.EffAgiLinearFactor +
|
||||
effDexterity / BladeburnerConstants.EffDexLinearFactor); // Always > 1
|
||||
|
||||
baseTime = Math.max(1, (baseTime * skillFac) / statFac);
|
||||
|
||||
return Math.ceil(baseTime * this.getActionTimePenalty());
|
||||
}
|
||||
|
||||
// Subtypes of Action implement these differently
|
||||
getTeamSuccessBonus(__inst: Bladeburner): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getActionTypeSkillSuccessBonus(__inst: Bladeburner): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getChaosCompetencePenalty(inst: Bladeburner, params: ISuccessChanceParams): number {
|
||||
const city = inst.getCurrentCity();
|
||||
if (params.est) {
|
||||
return Math.pow(city.popEst / BladeburnerConstants.PopulationThreshold, BladeburnerConstants.PopulationExponent);
|
||||
} else {
|
||||
return Math.pow(city.pop / BladeburnerConstants.PopulationThreshold, BladeburnerConstants.PopulationExponent);
|
||||
}
|
||||
}
|
||||
|
||||
getChaosDifficultyBonus(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
|
||||
const city = inst.getCurrentCity();
|
||||
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||
const mult = Math.pow(diff, 0.5);
|
||||
return mult;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
getEstSuccessChance(inst: Bladeburner, person: Person): [number, number] {
|
||||
function clamp(x: number): number {
|
||||
return Math.max(0, Math.min(x, 1));
|
||||
}
|
||||
const est = this.getSuccessChance(inst, person, { est: true });
|
||||
const real = this.getSuccessChance(inst, person);
|
||||
const diff = Math.abs(real - est);
|
||||
let low = real - diff;
|
||||
let high = real + diff;
|
||||
const city = inst.getCurrentCity();
|
||||
let r = city.pop / city.popEst;
|
||||
if (Number.isNaN(r)) r = 0;
|
||||
if (r < 1) low *= r;
|
||||
else high *= r;
|
||||
return [clamp(low), clamp(high)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inst - Bladeburner Object
|
||||
* @params - options:
|
||||
* est (bool): Get success chance estimate instead of real success chance
|
||||
*/
|
||||
getSuccessChance(inst: Bladeburner, person: Person, params: ISuccessChanceParams = { est: false }): number {
|
||||
if (inst == null) {
|
||||
throw new Error("Invalid Bladeburner instance passed into Action.getSuccessChance");
|
||||
}
|
||||
let difficulty = this.getDifficulty();
|
||||
let competence = 0;
|
||||
for (const stat of Object.keys(this.weights)) {
|
||||
if (Object.hasOwn(this.weights, stat)) {
|
||||
const playerStatLvl = person.queryStatFromString(stat);
|
||||
const key = "eff" + stat.charAt(0).toUpperCase() + stat.slice(1);
|
||||
let effMultiplier = inst.skillMultipliers[key];
|
||||
if (effMultiplier == null) {
|
||||
console.error(`Failed to find Bladeburner Skill multiplier for: ${stat}`);
|
||||
effMultiplier = 1;
|
||||
}
|
||||
competence += this.weights[stat] * Math.pow(effMultiplier * playerStatLvl, this.decays[stat]);
|
||||
}
|
||||
}
|
||||
competence *= calculateIntelligenceBonus(person.skills.intelligence, 0.75);
|
||||
competence *= inst.calculateStaminaPenalty();
|
||||
|
||||
competence *= this.getTeamSuccessBonus(inst);
|
||||
|
||||
competence *= this.getChaosCompetencePenalty(inst, params);
|
||||
difficulty *= this.getChaosDifficultyBonus(inst);
|
||||
|
||||
if (this.name == "Raid" && inst.getCurrentCity().comms <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Factor skill multipliers into success chance
|
||||
competence *= inst.skillMultipliers.successChanceAll;
|
||||
competence *= this.getActionTypeSkillSuccessBonus(inst);
|
||||
if (this.isStealth) {
|
||||
competence *= inst.skillMultipliers.successChanceStealth;
|
||||
}
|
||||
if (this.isKill) {
|
||||
competence *= inst.skillMultipliers.successChanceKill;
|
||||
}
|
||||
|
||||
// Augmentation multiplier
|
||||
competence *= person.mults.bladeburner_success_chance;
|
||||
|
||||
if (isNaN(competence)) {
|
||||
throw new Error("Competence calculated as NaN in Action.getSuccessChance()");
|
||||
}
|
||||
return Math.min(1, competence / difficulty);
|
||||
}
|
||||
|
||||
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number {
|
||||
return Math.ceil(0.5 * this.maxLevel * (2 * baseSuccessesPerLevel + (this.maxLevel - 1)));
|
||||
}
|
||||
|
||||
setMaxLevel(baseSuccessesPerLevel: number): void {
|
||||
if (this.successes >= this.getSuccessesNeededForNextLevel(baseSuccessesPerLevel)) {
|
||||
++this.maxLevel;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return Generic_toJSON("Action", this);
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): Action {
|
||||
return Generic_fromJSON(Action, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.Action = Action;
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
|
||||
interface IParams {
|
||||
name?: string;
|
||||
type?: number;
|
||||
}
|
||||
|
||||
export class ActionIdentifier {
|
||||
name = "";
|
||||
type = -1;
|
||||
|
||||
constructor(params: IParams = {}) {
|
||||
if (params.name) this.name = params.name;
|
||||
if (params.type) this.type = params.type;
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return Generic_toJSON("ActionIdentifier", this);
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): ActionIdentifier {
|
||||
return Generic_fromJSON(ActionIdentifier, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.ActionIdentifier = ActionIdentifier;
|
||||
183
src/Bladeburner/Actions/Action.ts
Normal file
183
src/Bladeburner/Actions/Action.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { Person } from "../../PersonObjects/Person";
|
||||
import type { Availability, SuccessChanceParams } from "../Types";
|
||||
import type { Skills as PersonSkills } from "../../PersonObjects/Skills";
|
||||
|
||||
import { addOffset } from "../../utils/helpers/addOffset";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { calculateIntelligenceBonus } from "../../PersonObjects/formulas/intelligence";
|
||||
import { BladeMultName } from "../Enums";
|
||||
import { getRecordKeys } from "../../Types/Record";
|
||||
|
||||
export interface ActionParams {
|
||||
desc: string;
|
||||
baseDifficulty?: number;
|
||||
rewardFac?: number;
|
||||
rankGain?: number;
|
||||
rankLoss?: number;
|
||||
hpLoss?: number;
|
||||
isStealth?: boolean;
|
||||
isKill?: boolean;
|
||||
weights?: PersonSkills;
|
||||
decays?: PersonSkills;
|
||||
}
|
||||
|
||||
export abstract class ActionClass {
|
||||
desc = "";
|
||||
// For LevelableActions, the base difficulty can be increased based on action level
|
||||
baseDifficulty = 100;
|
||||
|
||||
// All of these scale with level/difficulty
|
||||
rankGain = 0;
|
||||
rankLoss = 0;
|
||||
hpLoss = 0;
|
||||
|
||||
// Action Category. Current categories are stealth and kill
|
||||
isStealth = false;
|
||||
isKill = false;
|
||||
|
||||
// Weighting of each stat in determining action success rate
|
||||
weights: PersonSkills = {
|
||||
hacking: 1 / 7,
|
||||
strength: 1 / 7,
|
||||
defense: 1 / 7,
|
||||
dexterity: 1 / 7,
|
||||
agility: 1 / 7,
|
||||
charisma: 1 / 7,
|
||||
intelligence: 1 / 7,
|
||||
};
|
||||
// Diminishing returns of stats (stat ^ decay where 0 <= decay <= 1)
|
||||
decays: PersonSkills = {
|
||||
hacking: 0.9,
|
||||
strength: 0.9,
|
||||
defense: 0.9,
|
||||
dexterity: 0.9,
|
||||
agility: 0.9,
|
||||
charisma: 0.9,
|
||||
intelligence: 0.9,
|
||||
};
|
||||
|
||||
constructor(params: ActionParams | null = null) {
|
||||
if (!params) return;
|
||||
this.desc = params.desc;
|
||||
if (params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
||||
|
||||
if (params.rankGain) this.rankGain = params.rankGain;
|
||||
if (params.rankLoss) this.rankLoss = params.rankLoss;
|
||||
if (params.hpLoss) this.hpLoss = params.hpLoss;
|
||||
|
||||
if (params.isStealth) this.isStealth = params.isStealth;
|
||||
if (params.isKill) this.isKill = params.isKill;
|
||||
|
||||
if (params.weights) this.weights = params.weights;
|
||||
if (params.decays) this.decays = params.decays;
|
||||
}
|
||||
|
||||
/** Tests for success. Should be called when an action has completed */
|
||||
attempt(bladeburner: Bladeburner, person: Person): boolean {
|
||||
return Math.random() < this.getSuccessChance(bladeburner, person);
|
||||
}
|
||||
|
||||
// All the functions below are overwritten by certain subtypes of action, e.g. BlackOps ignore city stats
|
||||
getPopulationSuccessFactor(bladeburner: Bladeburner, { est }: SuccessChanceParams): number {
|
||||
const city = bladeburner.getCurrentCity();
|
||||
const pop = est ? city.popEst : city.pop;
|
||||
return Math.pow(pop / BladeburnerConstants.PopulationThreshold, BladeburnerConstants.PopulationExponent);
|
||||
}
|
||||
|
||||
getChaosSuccessFactor(bladeburner: Bladeburner): number {
|
||||
const city = bladeburner.getCurrentCity();
|
||||
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||
const mult = Math.pow(diff, 0.5);
|
||||
return mult;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
getActionTime(bladeburner: Bladeburner, person: Person): number {
|
||||
const difficulty = this.getDifficulty();
|
||||
let baseTime = difficulty / BladeburnerConstants.DifficultyToTimeFactor;
|
||||
const skillFac = bladeburner.getSkillMult(BladeMultName.actionTime); // Always < 1
|
||||
|
||||
const effAgility = bladeburner.getEffectiveSkillLevel(person, "agility");
|
||||
const effDexterity = bladeburner.getEffectiveSkillLevel(person, "dexterity");
|
||||
const statFac =
|
||||
0.5 *
|
||||
(Math.pow(effAgility, BladeburnerConstants.EffAgiExponentialFactor) +
|
||||
Math.pow(effDexterity, BladeburnerConstants.EffDexExponentialFactor) +
|
||||
effAgility / BladeburnerConstants.EffAgiLinearFactor +
|
||||
effDexterity / BladeburnerConstants.EffDexLinearFactor); // Always > 1
|
||||
|
||||
baseTime = Math.max(1, (baseTime * skillFac) / statFac);
|
||||
|
||||
return Math.ceil(baseTime * this.getActionTimePenalty());
|
||||
}
|
||||
|
||||
getTeamSuccessBonus(__bladeburner: Bladeburner): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getActionTypeSkillSuccessBonus(__bladeburner: Bladeburner): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getAvailability(__bladeburner: Bladeburner): Availability {
|
||||
return { available: true };
|
||||
}
|
||||
|
||||
getActionTimePenalty(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getDifficulty(): number {
|
||||
return this.baseDifficulty;
|
||||
}
|
||||
|
||||
getSuccessRange(bladeburner: Bladeburner, person: Person): [minChance: number, maxChance: number] {
|
||||
function clamp(x: number): number {
|
||||
return Math.max(0, Math.min(x, 1));
|
||||
}
|
||||
const est = this.getSuccessChance(bladeburner, person, { est: true });
|
||||
const real = this.getSuccessChance(bladeburner, person);
|
||||
const diff = Math.abs(real - est);
|
||||
let low = real - diff;
|
||||
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;
|
||||
return [clamp(low), clamp(high)];
|
||||
}
|
||||
|
||||
getSuccessChance(inst: Bladeburner, person: Person, { est }: SuccessChanceParams = { est: false }): number {
|
||||
let difficulty = this.getDifficulty();
|
||||
let competence = 0;
|
||||
for (const stat of getRecordKeys(person.skills)) {
|
||||
competence += this.weights[stat] * Math.pow(inst.getEffectiveSkillLevel(person, stat), this.decays[stat]);
|
||||
}
|
||||
competence *= calculateIntelligenceBonus(person.skills.intelligence, 0.75);
|
||||
competence *= inst.calculateStaminaPenalty();
|
||||
|
||||
competence *= this.getTeamSuccessBonus(inst);
|
||||
|
||||
competence *= this.getPopulationSuccessFactor(inst, { est });
|
||||
difficulty *= this.getChaosSuccessFactor(inst);
|
||||
|
||||
// Factor skill multipliers into success chance
|
||||
competence *= inst.getSkillMult(BladeMultName.successChanceAll);
|
||||
competence *= this.getActionTypeSkillSuccessBonus(inst);
|
||||
if (this.isStealth) competence *= inst.getSkillMult(BladeMultName.successChanceStealth);
|
||||
if (this.isKill) competence *= inst.getSkillMult(BladeMultName.successChanceKill);
|
||||
|
||||
// Augmentation multiplier
|
||||
competence *= person.mults.bladeburner_success_chance;
|
||||
|
||||
if (isNaN(competence)) {
|
||||
throw new Error("Competence calculated as NaN in Action.getSuccessChance()");
|
||||
}
|
||||
return Math.min(1, competence / difficulty);
|
||||
}
|
||||
}
|
||||
51
src/Bladeburner/Actions/BlackOperation.ts
Normal file
51
src/Bladeburner/Actions/BlackOperation.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { Availability, ActionIdentifier } from "../Types";
|
||||
|
||||
import { BladeActionType, BladeBlackOpName } from "@enums";
|
||||
import { ActionClass, ActionParams } from "./Action";
|
||||
import { operationSkillSuccessBonus, operationTeamSuccessBonus } from "./Operation";
|
||||
|
||||
interface BlackOpParams {
|
||||
name: BladeBlackOpName;
|
||||
reqdRank: number;
|
||||
n: number;
|
||||
}
|
||||
|
||||
export class BlackOperation extends ActionClass {
|
||||
type: BladeActionType.blackOp = BladeActionType.blackOp;
|
||||
name: BladeBlackOpName;
|
||||
n: number;
|
||||
reqdRank: number;
|
||||
teamCount = 0;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
}
|
||||
|
||||
constructor(params: ActionParams & BlackOpParams) {
|
||||
super(params);
|
||||
this.name = params.name;
|
||||
this.reqdRank = params.reqdRank;
|
||||
this.n = params.n;
|
||||
}
|
||||
|
||||
getAvailability(bladeburner: Bladeburner): Availability {
|
||||
if (bladeburner.numBlackOpsComplete < this.n) return { error: "Have not completed the previous Black Operation" };
|
||||
if (bladeburner.numBlackOpsComplete > this.n) return { error: "Already completed" };
|
||||
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 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getChaosSuccessFactor(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
|
||||
return 1;
|
||||
}
|
||||
getTeamSuccessBonus = operationTeamSuccessBonus;
|
||||
getActionTypeSkillSuccessBonus = operationSkillSuccessBonus;
|
||||
}
|
||||
33
src/Bladeburner/Actions/Contract.ts
Normal file
33
src/Bladeburner/Actions/Contract.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { ActionIdentifier } from "../Types";
|
||||
|
||||
import { Generic_fromJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver";
|
||||
import { BladeActionType, BladeContractName, BladeMultName } from "../Enums";
|
||||
import { LevelableActionClass, LevelableActionParams } from "./LevelableAction";
|
||||
|
||||
export class Contract extends LevelableActionClass {
|
||||
type: BladeActionType.contract = BladeActionType.contract;
|
||||
name: BladeContractName = BladeContractName.tracking;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
}
|
||||
|
||||
constructor(params: (LevelableActionParams & { name: BladeContractName }) | null = null) {
|
||||
super(params);
|
||||
if (params) this.name = params.name;
|
||||
}
|
||||
|
||||
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
|
||||
return inst.getSkillMult(BladeMultName.successChanceContract);
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return this.save("Contract");
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): Contract {
|
||||
return Generic_fromJSON(Contract, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.Contract = Contract;
|
||||
35
src/Bladeburner/Actions/GeneralAction.ts
Normal file
35
src/Bladeburner/Actions/GeneralAction.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { Person } from "../../PersonObjects/Person";
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { ActionIdentifier } from "../Types";
|
||||
|
||||
import { BladeActionType, BladeGeneralActionName } from "@enums";
|
||||
import { ActionClass, ActionParams } from "./Action";
|
||||
|
||||
type GeneralActionParams = ActionParams & {
|
||||
name: BladeGeneralActionName;
|
||||
getActionTime: (bladeburner: Bladeburner, person: Person) => number;
|
||||
getSuccessChance?: (bladeburner: Bladeburner, person: Person) => number;
|
||||
};
|
||||
|
||||
export class GeneralAction extends ActionClass {
|
||||
type: BladeActionType.general = BladeActionType.general;
|
||||
name: BladeGeneralActionName;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
}
|
||||
|
||||
constructor(params: GeneralActionParams) {
|
||||
super(params);
|
||||
this.name = params.name;
|
||||
this.getActionTime = params.getActionTime;
|
||||
if (params.getSuccessChance) this.getSuccessChance = params.getSuccessChance;
|
||||
}
|
||||
|
||||
getSuccessChance(__bladeburner: Bladeburner, __person: Person): number {
|
||||
return 1;
|
||||
}
|
||||
getSuccessRange(bladeburner: Bladeburner, person: Person): [minChance: number, maxChance: number] {
|
||||
const chance = this.getSuccessChance(bladeburner, person);
|
||||
return [chance, chance];
|
||||
}
|
||||
}
|
||||
111
src/Bladeburner/Actions/LevelableAction.ts
Normal file
111
src/Bladeburner/Actions/LevelableAction.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { IReviverValue } from "../../utils/JSONReviver";
|
||||
import type { Availability } from "../Types";
|
||||
|
||||
import { ActionClass, ActionParams } from "./Action";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
import { clampInteger } from "../../utils/helpers/clampNumber";
|
||||
|
||||
export type LevelableActionParams = ActionParams & {
|
||||
growthFunction: () => number;
|
||||
difficultyFac?: number;
|
||||
rewardFac?: number;
|
||||
minCount?: number;
|
||||
maxCount?: number;
|
||||
};
|
||||
|
||||
export abstract class LevelableActionClass extends ActionClass {
|
||||
// Static info, not included in save
|
||||
difficultyFac = 1.01;
|
||||
rewardFac = 1.02;
|
||||
growthFunction = () => 0;
|
||||
minCount = 1;
|
||||
maxCount = 150;
|
||||
|
||||
// Dynamic properties included in save
|
||||
count = 0;
|
||||
level = 1;
|
||||
maxLevel = 1;
|
||||
autoLevel = true;
|
||||
successes = 0;
|
||||
failures = 0;
|
||||
|
||||
constructor(params: LevelableActionParams | null = null) {
|
||||
super(params);
|
||||
if (!params) return;
|
||||
if (params.minCount) this.minCount = params.minCount;
|
||||
if (params.maxCount) this.maxCount = params.maxCount;
|
||||
if (params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
||||
if (params.rewardFac) this.rewardFac = params.rewardFac;
|
||||
this.count = getRandomIntInclusive(this.minCount, this.maxCount);
|
||||
this.growthFunction = params.growthFunction;
|
||||
}
|
||||
|
||||
getAvailability(__bladeburner: Bladeburner): Availability {
|
||||
if (this.count < 1) return { error: "Insufficient action count" };
|
||||
return { available: true };
|
||||
}
|
||||
|
||||
setMaxLevel(baseSuccessesPerLevel: number): void {
|
||||
if (this.successes >= this.getSuccessesNeededForNextLevel(baseSuccessesPerLevel)) {
|
||||
++this.maxLevel;
|
||||
}
|
||||
}
|
||||
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number {
|
||||
return Math.ceil(0.5 * this.maxLevel * (2 * baseSuccessesPerLevel + (this.maxLevel - 1)));
|
||||
}
|
||||
|
||||
getDifficulty() {
|
||||
const difficulty = this.baseDifficulty * Math.pow(this.difficultyFac, this.level - 1);
|
||||
if (isNaN(difficulty)) {
|
||||
throw new Error("Calculated NaN in Action.getDifficulty()");
|
||||
}
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
/** Reset a levelable action's tracked stats */
|
||||
reset() {
|
||||
this.count = getRandomIntInclusive(this.minCount, this.maxCount);
|
||||
this.level = 1;
|
||||
this.maxLevel = 1;
|
||||
this.autoLevel = true;
|
||||
this.successes = 0;
|
||||
this.failures = 0;
|
||||
}
|
||||
|
||||
/** These are not loaded the same way as most game objects, to allow better typechecking on load + partially static loading */
|
||||
loadData(loadedObject: LevelableActionClass) {
|
||||
this.maxLevel = clampInteger(loadedObject.maxLevel, 1);
|
||||
this.level = clampInteger(loadedObject.level, 1, this.maxLevel);
|
||||
this.count = clampInteger(loadedObject.count, 0);
|
||||
this.autoLevel = !!loadedObject.autoLevel;
|
||||
this.successes = clampInteger(loadedObject.successes, 0);
|
||||
this.failures = clampInteger(loadedObject.failures, 0);
|
||||
}
|
||||
/** Create a basic object just containing the relevant data for a levelable action */
|
||||
save<T extends LevelableActionClass>(
|
||||
this: T,
|
||||
ctorName: string,
|
||||
...extraParams: (keyof T)[]
|
||||
): IReviverValue<LevelableActionSaveData> {
|
||||
const data = {
|
||||
...Object.fromEntries(extraParams.map((param) => [param, this[param]])),
|
||||
count: this.count,
|
||||
level: this.level,
|
||||
maxLevel: this.maxLevel,
|
||||
autoLevel: this.autoLevel,
|
||||
successes: this.successes,
|
||||
failures: this.failures,
|
||||
};
|
||||
return { ctor: ctorName, data };
|
||||
}
|
||||
}
|
||||
|
||||
export interface LevelableActionSaveData {
|
||||
count: number;
|
||||
level: number;
|
||||
maxLevel: number;
|
||||
autoLevel: boolean;
|
||||
successes: number;
|
||||
failures: number;
|
||||
}
|
||||
84
src/Bladeburner/Actions/Operation.ts
Normal file
84
src/Bladeburner/Actions/Operation.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { Person } from "../../PersonObjects/Person";
|
||||
import type { BlackOperation } from "./BlackOperation";
|
||||
import type { Bladeburner } from "../Bladeburner";
|
||||
import type { Availability, ActionIdentifier, SuccessChanceParams } from "../Types";
|
||||
|
||||
import { BladeActionType, BladeMultName, BladeOperationName } from "@enums";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { ActionClass } from "./Action";
|
||||
import { Generic_fromJSON, IReviverValue, constructorsForReviver } from "../../utils/JSONReviver";
|
||||
import { LevelableActionClass, LevelableActionParams } from "./LevelableAction";
|
||||
import { clampInteger } from "../../utils/helpers/clampNumber";
|
||||
|
||||
export interface OperationParams extends LevelableActionParams {
|
||||
name: BladeOperationName;
|
||||
getAvailability?: (bladeburner: Bladeburner) => Availability;
|
||||
}
|
||||
|
||||
export class Operation extends LevelableActionClass {
|
||||
type: BladeActionType.operation = BladeActionType.operation;
|
||||
name = BladeOperationName.investigation;
|
||||
teamCount = 0;
|
||||
get id(): ActionIdentifier {
|
||||
return { type: this.type, name: this.name };
|
||||
}
|
||||
|
||||
constructor(params: OperationParams | null = null) {
|
||||
super(params);
|
||||
if (!params) return;
|
||||
this.name = params.name;
|
||||
if (params.getAvailability) this.getAvailability = params.getAvailability;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
const city = inst.getCurrentCity();
|
||||
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||
const mult = Math.pow(diff, 0.5);
|
||||
return mult;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
getSuccessChance(inst: Bladeburner, person: Person, params: SuccessChanceParams) {
|
||||
if (this.name == BladeOperationName.raid && inst.getCurrentCity().comms <= 0) return 0;
|
||||
return ActionClass.prototype.getSuccessChance.call(this, inst, person, params);
|
||||
}
|
||||
|
||||
reset() {
|
||||
LevelableActionClass.prototype.reset.call(this);
|
||||
this.teamCount = 0;
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return this.save("Operation", "teamCount");
|
||||
}
|
||||
loadData(loadedObject: Operation): void {
|
||||
this.teamCount = clampInteger(loadedObject.teamCount, 0);
|
||||
LevelableActionClass.prototype.loadData.call(this, loadedObject);
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): Operation {
|
||||
return Generic_fromJSON(Operation, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.Operation = Operation;
|
||||
|
||||
// shared member functions for Operation and BlackOperation
|
||||
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);
|
||||
const teamMultiplier = Math.pow(this.teamCount, 0.05);
|
||||
return teamMultiplier;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
7
src/Bladeburner/Actions/index.ts
Normal file
7
src/Bladeburner/Actions/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// Barrel file for easier importing
|
||||
export { ActionClass } from "./Action";
|
||||
export { BlackOperation } from "./BlackOperation";
|
||||
export { Contract } from "./Contract";
|
||||
export { GeneralAction } from "./GeneralAction";
|
||||
export { Operation } from "./Operation";
|
||||
export { LevelableActionClass } from "./LevelableAction";
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Operation, IOperationParams } from "./Operation";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
|
||||
export class BlackOperation extends Operation {
|
||||
constructor(params: IOperationParams | null = null) {
|
||||
super(params);
|
||||
this.count = 1;
|
||||
}
|
||||
|
||||
// To be implemented by subtypes
|
||||
getActionTimePenalty(): number {
|
||||
return 1.5;
|
||||
}
|
||||
|
||||
getChaosCompetencePenalty(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getChaosDifficultyBonus(/*inst: Bladeburner, params: ISuccessChanceParams*/): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return Generic_toJSON("BlackOperation", this);
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): Operation {
|
||||
return Generic_fromJSON(BlackOperation, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.BlackOperation = BlackOperation;
|
||||
@@ -1,571 +0,0 @@
|
||||
import { BlackOperation } from "./BlackOperation";
|
||||
import { BlackOperationName } from "@enums";
|
||||
|
||||
export const BlackOperations: Record<string, BlackOperation> = {};
|
||||
|
||||
(function () {
|
||||
BlackOperations[BlackOperationName.OperationTyphoon] = new BlackOperation({
|
||||
name: BlackOperationName.OperationTyphoon,
|
||||
baseDifficulty: 2000,
|
||||
reqdRank: 2.5e3,
|
||||
rankGain: 50,
|
||||
rankLoss: 10,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationZero] = new BlackOperation({
|
||||
name: BlackOperationName.OperationZero,
|
||||
baseDifficulty: 2500,
|
||||
reqdRank: 5e3,
|
||||
rankGain: 60,
|
||||
rankLoss: 15,
|
||||
hpLoss: 50,
|
||||
weights: {
|
||||
hack: 0.2,
|
||||
str: 0.15,
|
||||
def: 0.15,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isStealth: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationX] = new BlackOperation({
|
||||
name: BlackOperationName.OperationX,
|
||||
baseDifficulty: 3000,
|
||||
reqdRank: 7.5e3,
|
||||
rankGain: 75,
|
||||
rankLoss: 15,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationTitan] = new BlackOperation({
|
||||
name: BlackOperationName.OperationTitan,
|
||||
baseDifficulty: 4000,
|
||||
reqdRank: 10e3,
|
||||
rankGain: 100,
|
||||
rankLoss: 20,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationAres] = new BlackOperation({
|
||||
name: BlackOperationName.OperationAres,
|
||||
baseDifficulty: 5000,
|
||||
reqdRank: 12.5e3,
|
||||
rankGain: 125,
|
||||
rankLoss: 20,
|
||||
hpLoss: 200,
|
||||
weights: {
|
||||
hack: 0,
|
||||
str: 0.25,
|
||||
def: 0.25,
|
||||
dex: 0.25,
|
||||
agi: 0.25,
|
||||
cha: 0,
|
||||
int: 0,
|
||||
},
|
||||
decays: {
|
||||
hack: 0,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationArchangel] = new BlackOperation({
|
||||
name: BlackOperationName.OperationArchangel,
|
||||
baseDifficulty: 7500,
|
||||
reqdRank: 15e3,
|
||||
rankGain: 200,
|
||||
rankLoss: 20,
|
||||
hpLoss: 25,
|
||||
weights: {
|
||||
hack: 0,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.3,
|
||||
agi: 0.3,
|
||||
cha: 0,
|
||||
int: 0,
|
||||
},
|
||||
decays: {
|
||||
hack: 0,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationJuggernaut] = new BlackOperation({
|
||||
name: BlackOperationName.OperationJuggernaut,
|
||||
baseDifficulty: 10e3,
|
||||
reqdRank: 20e3,
|
||||
rankGain: 300,
|
||||
rankLoss: 40,
|
||||
hpLoss: 300,
|
||||
weights: {
|
||||
hack: 0,
|
||||
str: 0.25,
|
||||
def: 0.25,
|
||||
dex: 0.25,
|
||||
agi: 0.25,
|
||||
cha: 0,
|
||||
int: 0,
|
||||
},
|
||||
decays: {
|
||||
hack: 0,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationRedDragon] = new BlackOperation({
|
||||
name: BlackOperationName.OperationRedDragon,
|
||||
baseDifficulty: 12.5e3,
|
||||
reqdRank: 25e3,
|
||||
rankGain: 500,
|
||||
rankLoss: 50,
|
||||
hpLoss: 500,
|
||||
weights: {
|
||||
hack: 0.05,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.25,
|
||||
agi: 0.25,
|
||||
cha: 0,
|
||||
int: 0.05,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationK] = new BlackOperation({
|
||||
name: BlackOperationName.OperationK,
|
||||
baseDifficulty: 15e3,
|
||||
reqdRank: 30e3,
|
||||
rankGain: 750,
|
||||
rankLoss: 60,
|
||||
hpLoss: 1000,
|
||||
weights: {
|
||||
hack: 0.05,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.25,
|
||||
agi: 0.25,
|
||||
cha: 0,
|
||||
int: 0.05,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationDeckard] = new BlackOperation({
|
||||
name: BlackOperationName.OperationDeckard,
|
||||
baseDifficulty: 20e3,
|
||||
reqdRank: 40e3,
|
||||
rankGain: 1e3,
|
||||
rankLoss: 75,
|
||||
hpLoss: 200,
|
||||
weights: {
|
||||
hack: 0,
|
||||
str: 0.24,
|
||||
def: 0.24,
|
||||
dex: 0.24,
|
||||
agi: 0.24,
|
||||
cha: 0,
|
||||
int: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hack: 0,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationTyrell] = new BlackOperation({
|
||||
name: BlackOperationName.OperationTyrell,
|
||||
baseDifficulty: 25e3,
|
||||
reqdRank: 50e3,
|
||||
rankGain: 1.5e3,
|
||||
rankLoss: 100,
|
||||
hpLoss: 500,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationWallace] = new BlackOperation({
|
||||
name: BlackOperationName.OperationWallace,
|
||||
baseDifficulty: 30e3,
|
||||
reqdRank: 75e3,
|
||||
rankGain: 2e3,
|
||||
rankLoss: 150,
|
||||
hpLoss: 1500,
|
||||
weights: {
|
||||
hack: 0,
|
||||
str: 0.24,
|
||||
def: 0.24,
|
||||
dex: 0.24,
|
||||
agi: 0.24,
|
||||
cha: 0,
|
||||
int: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationShoulderOfOrion] = new BlackOperation({
|
||||
name: BlackOperationName.OperationShoulderOfOrion,
|
||||
baseDifficulty: 35e3,
|
||||
reqdRank: 100e3,
|
||||
rankGain: 2.5e3,
|
||||
rankLoss: 500,
|
||||
hpLoss: 1500,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isStealth: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationHyron] = new BlackOperation({
|
||||
name: BlackOperationName.OperationHyron,
|
||||
baseDifficulty: 40e3,
|
||||
reqdRank: 125e3,
|
||||
rankGain: 3e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 500,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationMorpheus] = new BlackOperation({
|
||||
name: BlackOperationName.OperationMorpheus,
|
||||
baseDifficulty: 45e3,
|
||||
reqdRank: 150e3,
|
||||
rankGain: 4e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hack: 0.05,
|
||||
str: 0.15,
|
||||
def: 0.15,
|
||||
dex: 0.3,
|
||||
agi: 0.3,
|
||||
cha: 0,
|
||||
int: 0.05,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isStealth: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationIonStorm] = new BlackOperation({
|
||||
name: BlackOperationName.OperationIonStorm,
|
||||
baseDifficulty: 50e3,
|
||||
reqdRank: 175e3,
|
||||
rankGain: 5e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 5000,
|
||||
weights: {
|
||||
hack: 0,
|
||||
str: 0.24,
|
||||
def: 0.24,
|
||||
dex: 0.24,
|
||||
agi: 0.24,
|
||||
cha: 0,
|
||||
int: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationAnnihilus] = new BlackOperation({
|
||||
name: BlackOperationName.OperationAnnihilus,
|
||||
baseDifficulty: 55e3,
|
||||
reqdRank: 200e3,
|
||||
rankGain: 7.5e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 10e3,
|
||||
weights: {
|
||||
hack: 0,
|
||||
str: 0.24,
|
||||
def: 0.24,
|
||||
dex: 0.24,
|
||||
agi: 0.24,
|
||||
cha: 0,
|
||||
int: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationUltron] = new BlackOperation({
|
||||
name: BlackOperationName.OperationUltron,
|
||||
baseDifficulty: 60e3,
|
||||
reqdRank: 250e3,
|
||||
rankGain: 10e3,
|
||||
rankLoss: 2e3,
|
||||
hpLoss: 10e3,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationCenturion] = new BlackOperation({
|
||||
name: BlackOperationName.OperationCenturion,
|
||||
baseDifficulty: 70e3,
|
||||
reqdRank: 300e3,
|
||||
rankGain: 15e3,
|
||||
rankLoss: 5e3,
|
||||
hpLoss: 10e3,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationVindictus] = new BlackOperation({
|
||||
name: BlackOperationName.OperationVindictus,
|
||||
baseDifficulty: 75e3,
|
||||
reqdRank: 350e3,
|
||||
rankGain: 20e3,
|
||||
rankLoss: 20e3,
|
||||
hpLoss: 20e3,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
});
|
||||
BlackOperations[BlackOperationName.OperationDaedalus] = new BlackOperation({
|
||||
name: BlackOperationName.OperationDaedalus,
|
||||
baseDifficulty: 80e3,
|
||||
reqdRank: 400e3,
|
||||
rankGain: 40e3,
|
||||
rankLoss: 10e3,
|
||||
hpLoss: 100e3,
|
||||
weights: {
|
||||
hack: 0.1,
|
||||
str: 0.2,
|
||||
def: 0.2,
|
||||
dex: 0.2,
|
||||
agi: 0.2,
|
||||
cha: 0,
|
||||
int: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hack: 0.6,
|
||||
str: 0.8,
|
||||
def: 0.8,
|
||||
dex: 0.8,
|
||||
agi: 0.8,
|
||||
cha: 0,
|
||||
int: 0.75,
|
||||
},
|
||||
});
|
||||
})();
|
||||
1438
src/Bladeburner/Bladeburner.ts
Normal file
1438
src/Bladeburner/Bladeburner.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,161 +1,89 @@
|
||||
import { CityName } from "@enums";
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { getRandomIntInclusive } from "../utils/helpers/getRandomIntInclusive";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
import { addOffset } from "../utils/helpers/addOffset";
|
||||
import { CityName } from "@enums";
|
||||
|
||||
interface IChangePopulationByCountParams {
|
||||
/** How much the estimate should change by. */
|
||||
estChange: number;
|
||||
/** Add offset to estimate (offset by percentage). */
|
||||
estOffset: number;
|
||||
}
|
||||
|
||||
interface IChangePopulationByPercentageParams {
|
||||
nonZero: boolean;
|
||||
changeEstEqually: boolean;
|
||||
}
|
||||
import { clampInteger, clampNumber } from "../utils/helpers/clampNumber";
|
||||
|
||||
export class City {
|
||||
/** Name of the city. */
|
||||
name: CityName;
|
||||
|
||||
/** Population of the city. */
|
||||
pop = 0;
|
||||
|
||||
/** Population estimation of the city. */
|
||||
popEst = 0;
|
||||
|
||||
/** Number of communities in the city. */
|
||||
comms = 0;
|
||||
|
||||
/** Chaos level of the city. */
|
||||
pop = 0; // Population
|
||||
popEst = 0; // Population estimate
|
||||
comms = 0; // Number of communities
|
||||
chaos = 0;
|
||||
|
||||
constructor(name = CityName.Sector12) {
|
||||
this.name = name;
|
||||
|
||||
// Synthoid population and estimate
|
||||
this.pop = getRandomInt(BladeburnerConstants.PopulationThreshold, 1.5 * BladeburnerConstants.PopulationThreshold);
|
||||
this.pop = getRandomIntInclusive(
|
||||
BladeburnerConstants.PopulationThreshold,
|
||||
1.5 * BladeburnerConstants.PopulationThreshold,
|
||||
);
|
||||
this.popEst = this.pop * (Math.random() + 0.5);
|
||||
|
||||
// Number of Synthoid communities population and estimate
|
||||
this.comms = getRandomInt(5, 150);
|
||||
this.comms = getRandomIntInclusive(5, 150);
|
||||
this.chaos = 0;
|
||||
}
|
||||
|
||||
/** p is the percentage, not the multiplier (e.g. pass in p = 5 for 5%) */
|
||||
/** @param {number} p - the percentage change, not the multiplier. e.g. pass in p = 5 for 5% */
|
||||
changeChaosByPercentage(p: number): void {
|
||||
if (isNaN(p)) {
|
||||
throw new Error("NaN passed into City.chaosChaosByPercentage()");
|
||||
}
|
||||
if (p === 0) {
|
||||
return;
|
||||
}
|
||||
this.chaos += this.chaos * (p / 100);
|
||||
if (this.chaos < 0) {
|
||||
this.chaos = 0;
|
||||
}
|
||||
this.chaos = clampNumber(this.chaos * (1 + p / 100), 0);
|
||||
}
|
||||
|
||||
improvePopulationEstimateByCount(n: number): void {
|
||||
if (isNaN(n)) {
|
||||
throw new Error("NaN passed into City.improvePopulationEstimateByCount()");
|
||||
}
|
||||
if (this.popEst < this.pop) {
|
||||
this.popEst += n;
|
||||
if (this.popEst > this.pop) {
|
||||
this.popEst = this.pop;
|
||||
}
|
||||
} else if (this.popEst > this.pop) {
|
||||
this.popEst -= n;
|
||||
if (this.popEst < this.pop) {
|
||||
this.popEst = this.pop;
|
||||
}
|
||||
}
|
||||
n = clampInteger(n, 0);
|
||||
const diff = Math.abs(this.popEst - this.pop);
|
||||
// Chgnge would overshoot actual population -> make estimate accurate
|
||||
if (diff <= n) this.popEst = this.pop;
|
||||
// Otherwise make enstimate closer by n
|
||||
else if (this.popEst < this.pop) this.popEst += n;
|
||||
else this.popEst -= n;
|
||||
}
|
||||
|
||||
/** p is the percentage, not the multiplier (e.g. pass in p = 5 for 5%) */
|
||||
/** @param {number} p - the percentage change, not the multiplier. e.g. pass in p = 5 for 5% */
|
||||
improvePopulationEstimateByPercentage(p: number, skillMult = 1): void {
|
||||
p = p * skillMult;
|
||||
if (isNaN(p)) {
|
||||
throw new Error("NaN passed into City.improvePopulationEstimateByPercentage()");
|
||||
}
|
||||
if (this.popEst < this.pop) {
|
||||
++this.popEst; // In case estimate is 0
|
||||
this.popEst *= 1 + p / 100;
|
||||
if (this.popEst > this.pop) {
|
||||
this.popEst = this.pop;
|
||||
}
|
||||
} else if (this.popEst > this.pop) {
|
||||
this.popEst *= 1 - p / 100;
|
||||
if (this.popEst < this.pop) {
|
||||
this.popEst = this.pop;
|
||||
}
|
||||
}
|
||||
p = clampNumber((p * skillMult) / 100);
|
||||
const diff = Math.abs(this.popEst - this.pop);
|
||||
// Chgnge would overshoot actual population -> make estimate accurate
|
||||
if (diff <= p * this.popEst) this.popEst = this.pop;
|
||||
// Otherwise make enstimate closer by n
|
||||
else if (this.popEst < this.pop) this.popEst = clampNumber(this.popEst * (1 + p));
|
||||
else this.popEst = clampNumber(this.popEst * (1 - p));
|
||||
}
|
||||
|
||||
changePopulationByCount(n: number, params: IChangePopulationByCountParams = { estChange: 0, estOffset: 0 }): void {
|
||||
if (isNaN(n)) {
|
||||
throw new Error("NaN passed into City.changePopulationByCount()");
|
||||
}
|
||||
this.pop += n;
|
||||
/**
|
||||
* @param params.estChange - Number to change the estimate by
|
||||
* @param params.estOffset - Offset percentage to apply to estimate */
|
||||
changePopulationByCount(n: number, params = { estChange: 0, estOffset: 0 }): void {
|
||||
n = clampInteger(n);
|
||||
this.pop = clampInteger(this.pop + n, 0);
|
||||
if (params.estChange && !isNaN(params.estChange)) {
|
||||
this.popEst += params.estChange;
|
||||
}
|
||||
if (params.estOffset) {
|
||||
this.popEst = addOffset(this.popEst, params.estOffset);
|
||||
}
|
||||
this.popEst = Math.max(this.popEst, 0);
|
||||
this.popEst = clampInteger(this.popEst, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @p is the percentage, not the multiplier. e.g. pass in p = 5 for 5%
|
||||
* @params options:
|
||||
* changeEstEqually(bool) - Change the population estimate by an equal amount
|
||||
* nonZero (bool) - Set to true to ensure that population always changes by at least 1
|
||||
*/
|
||||
changePopulationByPercentage(
|
||||
p: number,
|
||||
params: IChangePopulationByPercentageParams = {
|
||||
nonZero: false,
|
||||
changeEstEqually: false,
|
||||
},
|
||||
): number {
|
||||
if (isNaN(p)) {
|
||||
throw new Error("NaN passed into City.changePopulationByPercentage()");
|
||||
}
|
||||
if (p === 0) {
|
||||
return 0;
|
||||
}
|
||||
let change = Math.round(this.pop * (p / 100));
|
||||
* @param {number} p - the percentage change, not the multiplier. e.g. pass in p = 5 for 5%
|
||||
* @param {boolean} params.changeEstEqually - Whether to change the population estimate by an equal amount
|
||||
* @param {boolean} params.nonZero - Whether to ensure that population always changes by at least 1 */
|
||||
changePopulationByPercentage(p: number, params = { nonZero: false, changeEstEqually: false }): number {
|
||||
let change = clampInteger(this.pop * (p / 100));
|
||||
|
||||
// Population always changes by at least 1
|
||||
if (params.nonZero && change === 0) {
|
||||
p > 0 ? (change = 1) : (change = -1);
|
||||
}
|
||||
if (params.nonZero && change === 0) change = p > 0 ? 1 : -1;
|
||||
|
||||
this.pop += change;
|
||||
if (params.changeEstEqually) {
|
||||
this.popEst += change;
|
||||
if (this.popEst < 0) {
|
||||
this.popEst = 0;
|
||||
}
|
||||
}
|
||||
this.pop = clampInteger(this.pop + change, 0);
|
||||
if (params.changeEstEqually) this.popEst = clampInteger(this.popEst + change, 0);
|
||||
return change;
|
||||
}
|
||||
|
||||
changeChaosByCount(n: number): void {
|
||||
if (isNaN(n)) {
|
||||
throw new Error("NaN passed into City.changeChaosByCount()");
|
||||
}
|
||||
if (n === 0) {
|
||||
return;
|
||||
}
|
||||
this.chaos += n;
|
||||
if (this.chaos < 0) {
|
||||
this.chaos = 0;
|
||||
}
|
||||
this.chaos = clampNumber(this.chaos + n, 0);
|
||||
}
|
||||
|
||||
/** Serialize the current object to a JSON save state. */
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Bladeburner } from "./Bladeburner";
|
||||
import { Action, IActionParams } from "./Action";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
|
||||
export class Contract extends Action {
|
||||
constructor(params: IActionParams | null = null) {
|
||||
super(params);
|
||||
}
|
||||
|
||||
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
|
||||
return inst.skillMultipliers.successChanceContract;
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return Generic_toJSON("Contract", this);
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): Contract {
|
||||
return Generic_fromJSON(Contract, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.Contract = Contract;
|
||||
@@ -1,4 +1,31 @@
|
||||
export enum BlackOperationName {
|
||||
export enum BladeActionType {
|
||||
general = "General",
|
||||
contract = "Contracts",
|
||||
operation = "Operations",
|
||||
blackOp = "Black Operations",
|
||||
}
|
||||
export enum BladeGeneralActionName {
|
||||
training = "Training",
|
||||
fieldAnalysis = "Field Analysis",
|
||||
recruitment = "Recruitment",
|
||||
diplomacy = "Diplomacy",
|
||||
hyperbolicRegen = "Hyperbolic Regeneration Chamber",
|
||||
inciteViolence = "Incite Violence",
|
||||
}
|
||||
export enum BladeContractName {
|
||||
tracking = "Tracking",
|
||||
bountyHunter = "Bounty Hunter",
|
||||
retirement = "Retirement",
|
||||
}
|
||||
export enum BladeOperationName {
|
||||
investigation = "Investigation",
|
||||
undercover = "Undercover Operation",
|
||||
sting = "Sting Operation",
|
||||
raid = "Raid",
|
||||
stealthRetirement = "Stealth Retirement Operation",
|
||||
assassination = "Assassination",
|
||||
}
|
||||
export enum BladeBlackOpName {
|
||||
OperationTyphoon = "Operation Typhoon",
|
||||
OperationZero = "Operation Zero",
|
||||
OperationX = "Operation X",
|
||||
@@ -21,3 +48,36 @@ export enum BlackOperationName {
|
||||
OperationVindictus = "Operation Vindictus",
|
||||
OperationDaedalus = "Operation Daedalus",
|
||||
}
|
||||
|
||||
export enum BladeSkillName {
|
||||
bladesIntuition = "Blade's Intuition",
|
||||
cloak = "Cloak",
|
||||
shortCircuit = "Short-Circuit",
|
||||
digitalObserver = "Digital Observer",
|
||||
tracer = "Tracer",
|
||||
overclock = "Overclock",
|
||||
reaper = "Reaper",
|
||||
evasiveSystem = "Evasive System",
|
||||
datamancer = "Datamancer",
|
||||
cybersEdge = "Cyber's Edge",
|
||||
handsOfMidas = "Hands of Midas",
|
||||
hyperdrive = "Hyperdrive",
|
||||
}
|
||||
|
||||
export enum BladeMultName {
|
||||
successChanceAll = "Total Success Chance",
|
||||
successChanceStealth = "Stealth Success Chance",
|
||||
successChanceKill = "Retirement Success Chance",
|
||||
successChanceContract = "Contract Success Chance",
|
||||
successChanceOperation = "Operation Success Chance",
|
||||
successChanceEstimate = "Synthoid Data Estimate",
|
||||
actionTime = "Action Time",
|
||||
effStr = "Effective Strength",
|
||||
effDef = "Effective Defense",
|
||||
effDex = "Effective Dexterity",
|
||||
effAgi = "Effective Agility",
|
||||
effCha = "Effective Charisma",
|
||||
stamina = "Stamina",
|
||||
money = "Contract Money",
|
||||
expGain = "Experience Gain",
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Action } from "./Action";
|
||||
|
||||
export const GeneralActions: Record<string, Action> = {};
|
||||
|
||||
const actionNames: string[] = [
|
||||
"Training",
|
||||
"Field Analysis",
|
||||
"Recruitment",
|
||||
"Diplomacy",
|
||||
"Hyperbolic Regeneration Chamber",
|
||||
"Incite Violence",
|
||||
];
|
||||
|
||||
for (const actionName of actionNames) {
|
||||
GeneralActions[actionName] = new Action({
|
||||
name: actionName,
|
||||
});
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import { Bladeburner } from "./Bladeburner";
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
import { Action, IActionParams } from "./Action";
|
||||
import { Generic_fromJSON, Generic_toJSON, IReviverValue, constructorsForReviver } from "../utils/JSONReviver";
|
||||
|
||||
export interface IOperationParams extends IActionParams {
|
||||
reqdRank?: number;
|
||||
teamCount?: number;
|
||||
}
|
||||
|
||||
export class Operation extends Action {
|
||||
reqdRank = 100;
|
||||
teamCount = 0;
|
||||
|
||||
constructor(params: IOperationParams | null = null) {
|
||||
super(params);
|
||||
if (params && params.reqdRank) this.reqdRank = params.reqdRank;
|
||||
if (params && params.teamCount) this.teamCount = params.teamCount;
|
||||
}
|
||||
|
||||
// For actions that have teams. To be implemented by subtypes.
|
||||
getTeamSuccessBonus(inst: Bladeburner): number {
|
||||
if (this.teamCount && this.teamCount > 0) {
|
||||
this.teamCount = Math.min(this.teamCount, inst.teamSize);
|
||||
const teamMultiplier = Math.pow(this.teamCount, 0.05);
|
||||
return teamMultiplier;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
getActionTypeSkillSuccessBonus(inst: Bladeburner): number {
|
||||
return inst.skillMultipliers.successChanceOperation;
|
||||
}
|
||||
|
||||
getChaosDifficultyBonus(inst: Bladeburner /*, params: ISuccessChanceParams*/): number {
|
||||
const city = inst.getCurrentCity();
|
||||
if (city.chaos > BladeburnerConstants.ChaosThreshold) {
|
||||
const diff = 1 + (city.chaos - BladeburnerConstants.ChaosThreshold);
|
||||
const mult = Math.pow(diff, 0.5);
|
||||
return mult;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
toJSON(): IReviverValue {
|
||||
return Generic_toJSON("Operation", this);
|
||||
}
|
||||
|
||||
static fromJSON(value: IReviverValue): Operation {
|
||||
return Generic_fromJSON(Operation, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
constructorsForReviver.Operation = Operation;
|
||||
@@ -1,161 +1,56 @@
|
||||
import type { BladeMultName, BladeSkillName } from "@enums";
|
||||
|
||||
import { currentNodeMults } from "../BitNode/BitNodeMultipliers";
|
||||
import { Bladeburner } from "./Bladeburner";
|
||||
import { Availability } from "./Types";
|
||||
import { PositiveInteger, PositiveSafeInteger, isPositiveInteger } from "../types";
|
||||
import { PartialRecord, getRecordEntries } from "../Types/Record";
|
||||
|
||||
interface ISkillParams {
|
||||
name: string;
|
||||
interface SkillParams {
|
||||
name: BladeSkillName;
|
||||
desc: string;
|
||||
|
||||
baseCost?: number;
|
||||
costInc?: number;
|
||||
maxLvl?: number;
|
||||
|
||||
successChanceAll?: number;
|
||||
successChanceStealth?: number;
|
||||
successChanceKill?: number;
|
||||
successChanceContract?: number;
|
||||
successChanceOperation?: number;
|
||||
successChanceEstimate?: number;
|
||||
|
||||
actionTime?: number;
|
||||
|
||||
effHack?: number;
|
||||
effStr?: number;
|
||||
effDef?: number;
|
||||
effDex?: number;
|
||||
effAgi?: number;
|
||||
effCha?: number;
|
||||
|
||||
stamina?: number;
|
||||
money?: number;
|
||||
expGain?: number;
|
||||
mults: PartialRecord<BladeMultName, number>;
|
||||
}
|
||||
|
||||
export class Skill {
|
||||
name: string;
|
||||
name: BladeSkillName;
|
||||
desc: string;
|
||||
// Cost is in Skill Points
|
||||
baseCost = 1;
|
||||
baseCost: number;
|
||||
// Additive cost increase per level
|
||||
costInc = 1;
|
||||
maxLvl = 0;
|
||||
costInc: number;
|
||||
maxLvl: number;
|
||||
mults: PartialRecord<BladeMultName, number> = {};
|
||||
|
||||
/**
|
||||
* These benefits are additive. So total multiplier will be level (handled externally) times the
|
||||
* effects below
|
||||
*/
|
||||
successChanceAll = 0;
|
||||
successChanceStealth = 0;
|
||||
successChanceKill = 0;
|
||||
successChanceContract = 0;
|
||||
successChanceOperation = 0;
|
||||
|
||||
/**
|
||||
* This multiplier affects everything that increases synthoid population/community estimate
|
||||
* e.g. Field analysis, Investigation Op, Undercover Op
|
||||
*/
|
||||
successChanceEstimate = 0;
|
||||
actionTime = 0;
|
||||
effHack = 0;
|
||||
effStr = 0;
|
||||
effDef = 0;
|
||||
effDex = 0;
|
||||
effAgi = 0;
|
||||
effCha = 0;
|
||||
stamina = 0;
|
||||
money = 0;
|
||||
expGain = 0;
|
||||
|
||||
constructor(params: ISkillParams = { name: "foo", desc: "foo" }) {
|
||||
if (!params.name) {
|
||||
throw new Error("Failed to initialize Bladeburner Skill. No name was specified in ctor");
|
||||
}
|
||||
if (!params.desc) {
|
||||
throw new Error("Failed to initialize Bladeburner Skills. No desc was specified in ctor");
|
||||
}
|
||||
constructor(params: SkillParams) {
|
||||
this.name = params.name;
|
||||
this.desc = params.desc;
|
||||
this.baseCost = params.baseCost ? params.baseCost : 1;
|
||||
this.costInc = params.costInc ? params.costInc : 1;
|
||||
|
||||
if (params.maxLvl) {
|
||||
this.maxLvl = params.maxLvl;
|
||||
}
|
||||
|
||||
if (params.successChanceAll) {
|
||||
this.successChanceAll = params.successChanceAll;
|
||||
}
|
||||
if (params.successChanceStealth) {
|
||||
this.successChanceStealth = params.successChanceStealth;
|
||||
}
|
||||
if (params.successChanceKill) {
|
||||
this.successChanceKill = params.successChanceKill;
|
||||
}
|
||||
if (params.successChanceContract) {
|
||||
this.successChanceContract = params.successChanceContract;
|
||||
}
|
||||
if (params.successChanceOperation) {
|
||||
this.successChanceOperation = params.successChanceOperation;
|
||||
}
|
||||
|
||||
if (params.successChanceEstimate) {
|
||||
this.successChanceEstimate = params.successChanceEstimate;
|
||||
}
|
||||
|
||||
if (params.actionTime) {
|
||||
this.actionTime = params.actionTime;
|
||||
}
|
||||
if (params.effHack) {
|
||||
this.effHack = params.effHack;
|
||||
}
|
||||
if (params.effStr) {
|
||||
this.effStr = params.effStr;
|
||||
}
|
||||
if (params.effDef) {
|
||||
this.effDef = params.effDef;
|
||||
}
|
||||
if (params.effDex) {
|
||||
this.effDex = params.effDex;
|
||||
}
|
||||
if (params.effAgi) {
|
||||
this.effAgi = params.effAgi;
|
||||
}
|
||||
if (params.effCha) {
|
||||
this.effCha = params.effCha;
|
||||
}
|
||||
|
||||
if (params.stamina) {
|
||||
this.stamina = params.stamina;
|
||||
}
|
||||
if (params.money) {
|
||||
this.money = params.money;
|
||||
}
|
||||
if (params.expGain) {
|
||||
this.expGain = params.expGain;
|
||||
}
|
||||
this.baseCost = params.baseCost ?? 1;
|
||||
this.costInc = params.costInc ?? 1;
|
||||
this.maxLvl = params.maxLvl ?? Number.MAX_SAFE_INTEGER;
|
||||
for (const [multName, mult] of getRecordEntries(params.mults)) this.mults[multName] = mult;
|
||||
}
|
||||
|
||||
calculateCost(currentLevel: number, count = 1): number {
|
||||
//Recursive mode does not handle invalid inputs properly, but it should never
|
||||
//be possible for it to run with them. For the sake of not crashing the game,
|
||||
const recursiveMode = (currentLevel: number, count: number): number => {
|
||||
calculateCost(currentLevel: number, count = 1 as PositiveInteger): number {
|
||||
if (currentLevel + count > this.maxLvl) return Infinity;
|
||||
|
||||
const recursiveMode = (currentLevel: number, count: PositiveSafeInteger): number => {
|
||||
if (count <= 1) {
|
||||
return Math.floor((this.baseCost + currentLevel * this.costInc) * currentNodeMults.BladeburnerSkillCost);
|
||||
} else {
|
||||
const thisUpgrade = Math.floor(
|
||||
(this.baseCost + currentLevel * this.costInc) * currentNodeMults.BladeburnerSkillCost,
|
||||
);
|
||||
return this.calculateCost(currentLevel + 1, count - 1) + thisUpgrade;
|
||||
return this.calculateCost(currentLevel + 1, (count - 1) as PositiveSafeInteger) + thisUpgrade;
|
||||
}
|
||||
};
|
||||
|
||||
//Count must be a positive integer.
|
||||
if (count < 0 || count % 1 != 0) {
|
||||
throw new Error(`${count} is an invalid number of upgrades`);
|
||||
}
|
||||
//Use recursive mode if count is small
|
||||
if (count <= 100) {
|
||||
return recursiveMode(currentLevel, count);
|
||||
}
|
||||
//Use optimized mode if count is large
|
||||
// Use recursive mode if count is small
|
||||
if (count <= 100) return recursiveMode(currentLevel, count as PositiveSafeInteger);
|
||||
// Use optimized mode if count is large
|
||||
else {
|
||||
//unFloored is roughly equivalent to
|
||||
//(this.baseCost + currentLevel * this.costInc) * BitNodeMultipliers.BladeburnerSkillCost
|
||||
@@ -166,26 +61,16 @@ export class Skill {
|
||||
}
|
||||
}
|
||||
|
||||
getMultiplier(name: string): number {
|
||||
if (name === "successChanceAll") return this.successChanceAll;
|
||||
if (name === "successChanceStealth") return this.successChanceStealth;
|
||||
if (name === "successChanceKill") return this.successChanceKill;
|
||||
if (name === "successChanceContract") return this.successChanceContract;
|
||||
if (name === "successChanceOperation") return this.successChanceOperation;
|
||||
if (name === "successChanceEstimate") return this.successChanceEstimate;
|
||||
canUpgrade(bladeburner: Bladeburner, count = 1): Availability<{ cost: number }> {
|
||||
const currentLevel = bladeburner.skills[this.name] ?? 0;
|
||||
if (!isPositiveInteger(count)) return { error: `Invalid upgrade count ${count}` };
|
||||
if (currentLevel + count > this.maxLvl) return { error: `Upgraded level ${currentLevel + count} exceeds max` };
|
||||
const cost = this.calculateCost(currentLevel, count);
|
||||
if (cost > bladeburner.skillPoints) return { error: `Insufficient skill points for upgrade` };
|
||||
return { available: true, cost };
|
||||
}
|
||||
|
||||
if (name === "actionTime") return this.actionTime;
|
||||
|
||||
if (name === "effHack") return this.effHack;
|
||||
if (name === "effStr") return this.effStr;
|
||||
if (name === "effDef") return this.effDef;
|
||||
if (name === "effDex") return this.effDex;
|
||||
if (name === "effAgi") return this.effAgi;
|
||||
if (name === "effCha") return this.effCha;
|
||||
|
||||
if (name === "stamina") return this.stamina;
|
||||
if (name === "money") return this.money;
|
||||
if (name === "expGain") return this.expGain;
|
||||
return 0;
|
||||
getMultiplier(name: BladeMultName): number {
|
||||
return this.mults[name] ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
31
src/Bladeburner/Types.ts
Normal file
31
src/Bladeburner/Types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { BlackOperation, Contract, GeneralAction, Operation } from "./Actions";
|
||||
import type {
|
||||
BladeActionType,
|
||||
BladeBlackOpName,
|
||||
BladeContractName,
|
||||
BladeOperationName,
|
||||
BladeGeneralActionName,
|
||||
} from "@enums";
|
||||
|
||||
export interface SuccessChanceParams {
|
||||
/** Whether the success chance should be based on estimated statistics */
|
||||
est: boolean;
|
||||
}
|
||||
|
||||
type AvailabilitySuccess<T extends object> = { available: true } & T;
|
||||
type AvailabilityFailure = { available?: undefined; error: string };
|
||||
export type Availability<T extends object = object> = AvailabilitySuccess<T> | AvailabilityFailure;
|
||||
|
||||
type AttemptSuccess<T extends object> = { success: true; message?: string } & T;
|
||||
type AttemptFailure = { success?: undefined; message: string };
|
||||
export type Attempt<T extends object = object> = AttemptSuccess<T> | AttemptFailure;
|
||||
|
||||
export type Action = Contract | Operation | BlackOperation | GeneralAction;
|
||||
|
||||
export type ActionIdentifier =
|
||||
| { type: BladeActionType.blackOp; name: BladeBlackOpName }
|
||||
| { type: BladeActionType.contract; name: BladeContractName }
|
||||
| { type: BladeActionType.operation; name: BladeOperationName }
|
||||
| { type: BladeActionType.general; name: BladeGeneralActionName };
|
||||
|
||||
export type LevelableAction = Contract | Operation;
|
||||
@@ -1,29 +0,0 @@
|
||||
// Action Identifier enum
|
||||
export const ActionTypes: {
|
||||
[key: string]: number;
|
||||
Idle: number;
|
||||
Contract: number;
|
||||
Operation: number;
|
||||
BlackOp: number;
|
||||
BlackOperation: number;
|
||||
Training: number;
|
||||
Recruitment: number;
|
||||
FieldAnalysis: number;
|
||||
"Field Analysis": number;
|
||||
Diplomacy: number;
|
||||
"Hyperbolic Regeneration Chamber": number;
|
||||
"Incite Violence": number;
|
||||
} = {
|
||||
Idle: 1,
|
||||
Contract: 2,
|
||||
Operation: 3,
|
||||
BlackOp: 4,
|
||||
BlackOperation: 4,
|
||||
Training: 5,
|
||||
Recruitment: 6,
|
||||
FieldAnalysis: 7,
|
||||
"Field Analysis": 7,
|
||||
Diplomacy: 8,
|
||||
"Hyperbolic Regeneration Chamber": 9,
|
||||
"Incite Violence": 10,
|
||||
};
|
||||
737
src/Bladeburner/data/BlackOperations.ts
Normal file
737
src/Bladeburner/data/BlackOperations.ts
Normal file
@@ -0,0 +1,737 @@
|
||||
import { BlackOperation } from "../Actions/BlackOperation";
|
||||
import { BladeBlackOpName, CityName, FactionName } from "@enums";
|
||||
|
||||
export const BlackOperations: Record<BladeBlackOpName, BlackOperation> = {
|
||||
[BladeBlackOpName.OperationTyphoon]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationTyphoon,
|
||||
n: 0,
|
||||
baseDifficulty: 2000,
|
||||
reqdRank: 2.5e3,
|
||||
rankGain: 50,
|
||||
rankLoss: 10,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"Obadiah Zenyatta is the leader of a RedWater PMC. It has long been known among the intelligence community " +
|
||||
"that Zenyatta, along with the rest of the PMC, is a Synthoid.\n\n" +
|
||||
`The goal of ${BladeBlackOpName.OperationTyphoon} is to find and eliminate Zenyatta and RedWater by any means ` +
|
||||
"necessary. After the task is completed, the actions must be covered up from the general public.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationZero]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationZero,
|
||||
n: 1,
|
||||
baseDifficulty: 2500,
|
||||
reqdRank: 5e3,
|
||||
rankGain: 60,
|
||||
rankLoss: 15,
|
||||
hpLoss: 50,
|
||||
weights: {
|
||||
hacking: 0.2,
|
||||
strength: 0.15,
|
||||
defense: 0.15,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isStealth: true,
|
||||
desc:
|
||||
"AeroCorp is one of the world's largest defense contractors. Its leader, Steve Watataki, is thought to be " +
|
||||
"a supporter of Synthoid rights. He must be removed.\n\n" +
|
||||
`The goal of ${BladeBlackOpName.OperationZero} is to covertly infiltrate AeroCorp and uncover any incriminating ` +
|
||||
"evidence or information against Watataki that will cause him to be removed from his position at AeroCorp. " +
|
||||
"Incriminating evidence can be fabricated as a last resort. Be warned that AeroCorp has some of the most advanced " +
|
||||
"security measures in the world.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationX]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationX,
|
||||
n: 2,
|
||||
baseDifficulty: 3000,
|
||||
reqdRank: 7.5e3,
|
||||
rankGain: 75,
|
||||
rankLoss: 15,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"We have recently discovered an underground publication group called Samizdat. Even though most of their " +
|
||||
"publications are nonsensical conspiracy theories, the average human is gullible enough to believe them. Many of " +
|
||||
"their works discuss Synthoids and pose a threat to society. The publications are spreading rapidly in China and " +
|
||||
"other Eastern countries.\n\n" +
|
||||
"Samizdat has done a good job of keeping hidden and anonymous. However, we've just received intelligence that " +
|
||||
`their base of operations is in ${CityName.Ishima}'s underground sewer systems. Your task is to investigate the ` +
|
||||
"sewer systems, and eliminate Samizdat. They must never publish anything again.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationTitan]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationTitan,
|
||||
n: 3,
|
||||
baseDifficulty: 4000,
|
||||
reqdRank: 10e3,
|
||||
rankGain: 100,
|
||||
rankLoss: 20,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"Several months ago Titan Laboratories' Bioengineering department was infiltrated by Synthoids. As far as we " +
|
||||
"know, Titan Laboratories' management has no knowledge about this. We don't know what the Synthoids are up to, " +
|
||||
"but the research that they could be conducting using Titan Laboratories' vast resources is potentially very " +
|
||||
"dangerous.\n\n" +
|
||||
`Your goal is to enter and destroy the Bioengineering department's facility in ${CityName.Aevum}. The task is not ` +
|
||||
"just to retire the Synthoids there, but also to destroy any information or research at the facility that is " +
|
||||
"relevant to the Synthoids and their goals.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationAres]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationAres,
|
||||
n: 4,
|
||||
baseDifficulty: 5000,
|
||||
reqdRank: 12.5e3,
|
||||
rankGain: 125,
|
||||
rankLoss: 20,
|
||||
hpLoss: 200,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.25,
|
||||
defense: 0.25,
|
||||
dexterity: 0.25,
|
||||
agility: 0.25,
|
||||
charisma: 0,
|
||||
intelligence: 0,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"One of our undercover agents, Agent Carter, has informed us of a massive weapons deal going down in Dubai " +
|
||||
"between rogue Russian militants and a radical Synthoid community. These weapons are next-gen plasma and energy " +
|
||||
"weapons. It is critical for the safety of humanity that this deal does not happen.\n\n" +
|
||||
"Your task is to intercept the deal. Leave no survivors.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationArchangel]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationArchangel,
|
||||
n: 5,
|
||||
baseDifficulty: 7500,
|
||||
reqdRank: 15e3,
|
||||
rankGain: 200,
|
||||
rankLoss: 20,
|
||||
hpLoss: 25,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.3,
|
||||
agility: 0.3,
|
||||
charisma: 0,
|
||||
intelligence: 0,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"Our analysts have discovered that the popular Red Rabbit brothel in Amsterdam is run and 'staffed' by MK-VI " +
|
||||
"Synthoids. Intelligence suggests that the profit from this brothel is used to fund a large black market arms " +
|
||||
"trafficking operation.\n\n" +
|
||||
"The goal of this operation is to take out the leaders that are running the Red Rabbit brothel. Try to limit the " +
|
||||
"number of other casualties, but do what you must to complete the mission.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationJuggernaut]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationJuggernaut,
|
||||
n: 6,
|
||||
baseDifficulty: 10e3,
|
||||
reqdRank: 20e3,
|
||||
rankGain: 300,
|
||||
rankLoss: 40,
|
||||
hpLoss: 300,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.25,
|
||||
defense: 0.25,
|
||||
dexterity: 0.25,
|
||||
agility: 0.25,
|
||||
charisma: 0,
|
||||
intelligence: 0,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"The CIA has just encountered a new security threat. A new criminal group, lead by a shadowy operative who calls " +
|
||||
"himself Juggernaut, has been smuggling drugs and weapons (including suspected bioweapons) into " +
|
||||
`${CityName.Sector12}. We also have reason to believe they tried to break into one of Universal Energy's ` +
|
||||
"facilities in order to cause a city-wide blackout. The CIA suspects that Juggernaut is a heavily-augmented " +
|
||||
"Synthoid, and have thus enlisted our help.\n\n" +
|
||||
"Your mission is to eradicate Juggernaut and his followers.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationRedDragon]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationRedDragon,
|
||||
n: 7,
|
||||
baseDifficulty: 12.5e3,
|
||||
reqdRank: 25e3,
|
||||
rankGain: 500,
|
||||
rankLoss: 50,
|
||||
hpLoss: 500,
|
||||
weights: {
|
||||
hacking: 0.05,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.25,
|
||||
agility: 0.25,
|
||||
charisma: 0,
|
||||
intelligence: 0.05,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
`The ${FactionName.Tetrads} criminal organization is suspected of reverse-engineering the MK-VI Synthoid design. ` +
|
||||
"We believe they altered and possibly improved the design and began manufacturing their own Synthoid models in" +
|
||||
"order to bolster their criminal activities.\n\n" +
|
||||
`Your task is to infiltrate and destroy the ${FactionName.Tetrads}' base of operations in Los Angeles. ` +
|
||||
"Intelligence tells us that their base houses one of their Synthoid manufacturing units.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationK]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationK,
|
||||
n: 8,
|
||||
baseDifficulty: 15e3,
|
||||
reqdRank: 30e3,
|
||||
rankGain: 750,
|
||||
rankLoss: 60,
|
||||
hpLoss: 1000,
|
||||
weights: {
|
||||
hacking: 0.05,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.25,
|
||||
agility: 0.25,
|
||||
charisma: 0,
|
||||
intelligence: 0.05,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"CODE RED SITUATION. Our intelligence tells us that VitaLife has discovered a new android cloning technology. " +
|
||||
"This technology is supposedly capable of cloning Synthoids, not only physically but also their advanced AI " +
|
||||
"modules. We do not believe that VitaLife is trying to use this technology illegally or maliciously, but if any " +
|
||||
"Synthoids were able to infiltrate the corporation and take advantage of this technology then the results would " +
|
||||
"be catastrophic.\n\n" +
|
||||
"We do not have the power or jurisdiction to shut this down through legal or political means, so we must resort " +
|
||||
"to a covert operation. Your goal is to destroy this technology and eliminate anyone who was involved in its " +
|
||||
"creation.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationDeckard]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationDeckard,
|
||||
n: 9,
|
||||
baseDifficulty: 20e3,
|
||||
reqdRank: 40e3,
|
||||
rankGain: 1e3,
|
||||
rankLoss: 75,
|
||||
hpLoss: 200,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.24,
|
||||
defense: 0.24,
|
||||
dexterity: 0.24,
|
||||
agility: 0.24,
|
||||
charisma: 0,
|
||||
intelligence: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"Despite your success in eliminating VitaLife's new android-replicating technology in " +
|
||||
`${BladeBlackOpName.OperationK}, we've discovered that a small group of MK-VI Synthoids were able to make off with ` +
|
||||
"the schematics and design of the technology before the Operation. It is almost a certainty that these Synthoids " +
|
||||
"are some of the rogue MK-VI ones from the Synthoid Uprising.\n\n" +
|
||||
`The goal of ${BladeBlackOpName.OperationDeckard} is to hunt down these Synthoids and retire them. I don't need to ` +
|
||||
"tell you how critical this mission is.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationTyrell]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationTyrell,
|
||||
n: 10,
|
||||
baseDifficulty: 25e3,
|
||||
reqdRank: 50e3,
|
||||
rankGain: 1.5e3,
|
||||
rankLoss: 100,
|
||||
hpLoss: 500,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
`A week ago ${FactionName.BladeIndustries} reported a small break-in at one of their ${CityName.Aevum} ` +
|
||||
`Augmentation storage facilities. We figured out that ${FactionName.TheDarkArmy} was behind the heist, and didn't think ` +
|
||||
"any more of it. However, we've just discovered that several known MK-VI Synthoids were part of that break-in group.\n\n" +
|
||||
"We cannot have Synthoids upgrading their already-enhanced abilities with Augmentations. Your task is to hunt " +
|
||||
`down associated ${FactionName.TheDarkArmy} members and eliminate them.`,
|
||||
}),
|
||||
[BladeBlackOpName.OperationWallace]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationWallace,
|
||||
n: 11,
|
||||
baseDifficulty: 30e3,
|
||||
reqdRank: 75e3,
|
||||
rankGain: 2e3,
|
||||
rankLoss: 150,
|
||||
hpLoss: 1500,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.24,
|
||||
defense: 0.24,
|
||||
dexterity: 0.24,
|
||||
agility: 0.24,
|
||||
charisma: 0,
|
||||
intelligence: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
`Based on information gathered from ${BladeBlackOpName.OperationTyrell}, we've discovered that ` +
|
||||
`${FactionName.TheDarkArmy} was well aware that there were Synthoids amongst their ranks. Even worse, we believe ` +
|
||||
`that ${FactionName.TheDarkArmy} is working together with other criminal organizations such as ` +
|
||||
`${FactionName.TheSyndicate} and that they are planning some sort of large-scale takeover of multiple major ` +
|
||||
`cities, most notably ${CityName.Aevum}. We suspect that Synthoids have infiltrated the ranks of these criminal ` +
|
||||
"factions and are trying to stage another Synthoid uprising.\n\n" +
|
||||
"The best way to deal with this is to prevent it before it even happens. The goal of " +
|
||||
`${BladeBlackOpName.OperationWallace} is to destroy ${FactionName.TheDarkArmy} and Syndicate factions in ` +
|
||||
`${CityName.Aevum} immediately. Leave no survivors.`,
|
||||
}),
|
||||
[BladeBlackOpName.OperationShoulderOfOrion]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationShoulderOfOrion,
|
||||
n: 12,
|
||||
baseDifficulty: 35e3,
|
||||
reqdRank: 100e3,
|
||||
rankGain: 2.5e3,
|
||||
rankLoss: 500,
|
||||
hpLoss: 1500,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isStealth: true,
|
||||
desc:
|
||||
"China's Solaris Space Systems is secretly launching the first manned spacecraft in over a decade using " +
|
||||
"Synthoids. We believe China is trying to establish the first off-world colonies.\n\n" +
|
||||
"The mission is to prevent this launch without instigating an international conflict. When you accept this " +
|
||||
"mission you will be officially disavowed by the NSA and the national government until after you successfully " +
|
||||
"return. In the event of failure, all of the operation's team members must not let themselves be captured alive.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationHyron]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationHyron,
|
||||
n: 13,
|
||||
baseDifficulty: 40e3,
|
||||
reqdRank: 125e3,
|
||||
rankGain: 3e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 500,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
`Our intelligence tells us that ${FactionName.FulcrumSecretTechnologies} is developing a quantum supercomputer ` +
|
||||
"using human brains as core processors. This supercomputer is rumored to be able to store vast amounts of data " +
|
||||
"and perform computations unmatched by any other supercomputer on the planet. But more importantly, the use of " +
|
||||
"organic human brains means that the supercomputer may be able to reason abstractly and become self-aware.\n\n" +
|
||||
"I do not need to remind you why sentient-level AIs pose a serious threat to all of mankind.\n\n" +
|
||||
`The research for this project is being conducted at one of ${FactionName.FulcrumSecretTechnologies} secret ` +
|
||||
`facilities in ${CityName.Aevum}, codenamed 'Alpha Ranch'. Infiltrate the compound, delete and destroy the work, ` +
|
||||
"and then find and kill the project lead.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationMorpheus]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationMorpheus,
|
||||
n: 14,
|
||||
baseDifficulty: 45e3,
|
||||
reqdRank: 150e3,
|
||||
rankGain: 4e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 100,
|
||||
weights: {
|
||||
hacking: 0.05,
|
||||
strength: 0.15,
|
||||
defense: 0.15,
|
||||
dexterity: 0.3,
|
||||
agility: 0.3,
|
||||
charisma: 0,
|
||||
intelligence: 0.05,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isStealth: true,
|
||||
desc:
|
||||
"DreamSense Technologies is an advertising company that uses special technology to transmit their ads into the " +
|
||||
"people's dreams and subconscious. They do this using broadcast transmitter towers. Based on information from our " +
|
||||
`agents and informants in ${CityName.Chongqing}, we have reason to believe that one of the broadcast towers there ` +
|
||||
"has been compromised by Synthoids and is being used to spread pro-Synthoid propaganda.\n\n" +
|
||||
"The mission is to destroy this broadcast tower. Speed and stealth are of the utmost importance for this.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationIonStorm]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationIonStorm,
|
||||
n: 15,
|
||||
baseDifficulty: 50e3,
|
||||
reqdRank: 175e3,
|
||||
rankGain: 5e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 5000,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.24,
|
||||
defense: 0.24,
|
||||
dexterity: 0.24,
|
||||
agility: 0.24,
|
||||
charisma: 0,
|
||||
intelligence: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"Our analysts have uncovered a gathering of MK-VI Synthoids that have taken up residence in the " +
|
||||
`${CityName.Sector12} Slums. We don't know if they are rogue Synthoids from the Uprising, but we do know that they ` +
|
||||
"have been stockpiling weapons, money, and other resources. This makes them dangerous.\n\n" +
|
||||
`This is a full-scale assault operation to find and retire all of these Synthoids in the ${CityName.Sector12} ` +
|
||||
"Slums.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationAnnihilus]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationAnnihilus,
|
||||
n: 16,
|
||||
baseDifficulty: 55e3,
|
||||
reqdRank: 200e3,
|
||||
rankGain: 7.5e3,
|
||||
rankLoss: 1e3,
|
||||
hpLoss: 10e3,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.24,
|
||||
defense: 0.24,
|
||||
dexterity: 0.24,
|
||||
agility: 0.24,
|
||||
charisma: 0,
|
||||
intelligence: 0.04,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
"Our superiors have ordered us to eradicate everything and everyone in an underground facility located in " +
|
||||
`${CityName.Aevum}. They tell us that the facility houses many dangerous Synthoids and belongs to a terrorist ` +
|
||||
`organization called '${FactionName.TheCovenant}'. We have no prior intelligence about this organization, so you ` +
|
||||
"are going in blind.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationUltron]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationUltron,
|
||||
n: 17,
|
||||
baseDifficulty: 60e3,
|
||||
reqdRank: 250e3,
|
||||
rankGain: 10e3,
|
||||
rankLoss: 2e3,
|
||||
hpLoss: 10e3,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
isKill: true,
|
||||
desc:
|
||||
`${FactionName.OmniTekIncorporated}, the original designer and manufacturer of Synthoids, has notified us of a ` +
|
||||
"malfunction in their AI design. This malfunction, when triggered, causes MK-VI Synthoids to become radicalized " +
|
||||
"and seek out the destruction of humanity. They say that this bug affects all MK-VI Synthoids, not just the rogue " +
|
||||
"ones from the Uprising.\n\n" +
|
||||
`${FactionName.OmniTekIncorporated} has also told us they believe someone has triggered this malfunction in a ` +
|
||||
"large group of MK-VI Synthoids, and that these newly-radicalized Synthoids are now amassing in " +
|
||||
`${CityName.Volhaven} to form a terrorist group called Ultron.\n\n` +
|
||||
"Intelligence suggests Ultron is heavily armed and that their members are augmented. We believe Ultron is making " +
|
||||
"moves to take control of and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).\n\n" +
|
||||
"Your task is to find and destroy Ultron.",
|
||||
}),
|
||||
[BladeBlackOpName.OperationCenturion]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationCenturion,
|
||||
n: 18,
|
||||
baseDifficulty: 70e3,
|
||||
reqdRank: 300e3,
|
||||
rankGain: 15e3,
|
||||
rankLoss: 5e3,
|
||||
hpLoss: 10e3,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
desc:
|
||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)\n\n" +
|
||||
"Throughout all of humanity's history, we have relied on technology to survive, conquer, and progress. Its " +
|
||||
"advancement became our primary goal. And at the peak of human civilization technology turned into power. Global, " +
|
||||
"absolute power.\n\n" +
|
||||
"It seems that the universe is not without a sense of irony.\n\n" +
|
||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
||||
}),
|
||||
[BladeBlackOpName.OperationVindictus]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationVindictus,
|
||||
n: 19,
|
||||
baseDifficulty: 75e3,
|
||||
reqdRank: 350e3,
|
||||
rankGain: 20e3,
|
||||
rankLoss: 20e3,
|
||||
hpLoss: 20e3,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
desc:
|
||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)\n\n" +
|
||||
"The bits are all around us. The daemons that hold the Node together can manifest themselves in many different " +
|
||||
"ways.\n\n" +
|
||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
||||
}),
|
||||
[BladeBlackOpName.OperationDaedalus]: new BlackOperation({
|
||||
name: BladeBlackOpName.OperationDaedalus,
|
||||
n: 20,
|
||||
baseDifficulty: 80e3,
|
||||
reqdRank: 400e3,
|
||||
rankGain: 40e3,
|
||||
rankLoss: 10e3,
|
||||
hpLoss: 100e3,
|
||||
weights: {
|
||||
hacking: 0.1,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0.6,
|
||||
strength: 0.8,
|
||||
defense: 0.8,
|
||||
dexterity: 0.8,
|
||||
agility: 0.8,
|
||||
charisma: 0,
|
||||
intelligence: 0.75,
|
||||
},
|
||||
desc: "Yesterday we obeyed kings and bent our neck to emperors. Today we kneel only to truth.",
|
||||
}),
|
||||
};
|
||||
|
||||
/** Array for quick lookup by blackop number */
|
||||
export const blackOpsArray = Object.values(BlackOperations).sort((a, b) => (a.n < b.n ? -1 : 1));
|
||||
// Verify that all "n" properties match the index in the array
|
||||
if (!blackOpsArray.every((blackOp, i) => blackOp.n === i)) {
|
||||
throw new Error("blackOpsArray did not initialize with correct indices");
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
import React from "react";
|
||||
import { BlackOperationName, CityName, FactionName } from "@enums";
|
||||
|
||||
interface IBlackOp {
|
||||
desc: JSX.Element;
|
||||
}
|
||||
|
||||
export const BlackOperations: Record<string, IBlackOp | undefined> = {
|
||||
[BlackOperationName.OperationTyphoon]: {
|
||||
desc: (
|
||||
<>
|
||||
Obadiah Zenyatta is the leader of a RedWater PMC. It has long been known among the intelligence community that
|
||||
Zenyatta, along with the rest of the PMC, is a Synthoid.
|
||||
<br />
|
||||
<br />
|
||||
The goal of {BlackOperationName.OperationTyphoon} is to find and eliminate Zenyatta and RedWater by any means
|
||||
necessary. After the task is completed, the actions must be covered up from the general public.
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
||||
[BlackOperationName.OperationZero]: {
|
||||
desc: (
|
||||
<>
|
||||
AeroCorp is one of the world's largest defense contractors. Its leader, Steve Watataki, is thought to be a
|
||||
supporter of Synthoid rights. He must be removed.
|
||||
<br />
|
||||
<br />
|
||||
The goal of {BlackOperationName.OperationZero} is to covertly infiltrate AeroCorp and uncover any incriminating
|
||||
evidence or information against Watataki that will cause him to be removed from his position at AeroCorp.
|
||||
Incriminating evidence can be fabricated as a last resort. Be warned that AeroCorp has some of the most advanced
|
||||
security measures in the world.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationX]: {
|
||||
desc: (
|
||||
<>
|
||||
We have recently discovered an underground publication group called Samizdat. Even though most of their
|
||||
publications are nonsensical conspiracy theories, the average human is gullible enough to believe them. Many of
|
||||
their works discuss Synthoids and pose a threat to society. The publications are spreading rapidly in China and
|
||||
other Eastern countries.
|
||||
<br />
|
||||
<br />
|
||||
Samizdat has done a good job of keeping hidden and anonymous. However, we've just received intelligence that
|
||||
their base of operations is in {CityName.Ishima}'s underground sewer systems. Your task is to investigate the
|
||||
sewer systems, and eliminate Samizdat. They must never publish anything again.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationTitan]: {
|
||||
desc: (
|
||||
<>
|
||||
Several months ago Titan Laboratories' Bioengineering department was infiltrated by Synthoids. As far as we
|
||||
know, Titan Laboratories' management has no knowledge about this. We don't know what the Synthoids are up to,
|
||||
but the research that they could be conducting using Titan Laboratories' vast resources is potentially very
|
||||
dangerous.
|
||||
<br />
|
||||
<br />
|
||||
Your goal is to enter and destroy the Bioengineering department's facility in {CityName.Aevum}. The task is not
|
||||
just to retire the Synthoids there, but also to destroy any information or research at the facility that is
|
||||
relevant to the Synthoids and their goals.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationAres]: {
|
||||
desc: (
|
||||
<>
|
||||
One of our undercover agents, Agent Carter, has informed us of a massive weapons deal going down in Dubai
|
||||
between rogue Russian militants and a radical Synthoid community. These weapons are next-gen plasma and energy
|
||||
weapons. It is critical for the safety of humanity that this deal does not happen.
|
||||
<br />
|
||||
<br />
|
||||
Your task is to intercept the deal. Leave no survivors.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationArchangel]: {
|
||||
desc: (
|
||||
<>
|
||||
Our analysts have discovered that the popular Red Rabbit brothel in Amsterdam is run and 'staffed' by MK-VI
|
||||
Synthoids. Intelligence suggests that the profit from this brothel is used to fund a large black market arms
|
||||
trafficking operation.
|
||||
<br />
|
||||
<br />
|
||||
The goal of this operation is to take out the leaders that are running the Red Rabbit brothel. Try to limit the
|
||||
number of other casualties, but do what you must to complete the mission.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationJuggernaut]: {
|
||||
desc: (
|
||||
<>
|
||||
The CIA has just encountered a new security threat. A new criminal group, lead by a shadowy operative who calls
|
||||
himself Juggernaut, has been smuggling drugs and weapons (including suspected bioweapons) into{" "}
|
||||
{CityName.Sector12}. We also have reason to believe they tried to break into one of Universal Energy's
|
||||
facilities in order to cause a city-wide blackout. The CIA suspects that Juggernaut is a heavily-augmented
|
||||
Synthoid, and have thus enlisted our help.
|
||||
<br />
|
||||
<br />
|
||||
Your mission is to eradicate Juggernaut and his followers.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationRedDragon]: {
|
||||
desc: (
|
||||
<>
|
||||
The {FactionName.Tetrads} criminal organization is suspected of reverse-engineering the MK-VI Synthoid design.
|
||||
We believe they altered and possibly improved the design and began manufacturing their own Synthoid models in
|
||||
order to bolster their criminal activities.
|
||||
<br />
|
||||
<br />
|
||||
Your task is to infiltrate and destroy the {FactionName.Tetrads}' base of operations in Los Angeles.
|
||||
Intelligence tells us that their base houses one of their Synthoid manufacturing units.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationK]: {
|
||||
desc: (
|
||||
<>
|
||||
CODE RED SITUATION. Our intelligence tells us that VitaLife has discovered a new android cloning technology.
|
||||
This technology is supposedly capable of cloning Synthoids, not only physically but also their advanced AI
|
||||
modules. We do not believe that VitaLife is trying to use this technology illegally or maliciously, but if any
|
||||
Synthoids were able to infiltrate the corporation and take advantage of this technology then the results would
|
||||
be catastrophic.
|
||||
<br />
|
||||
<br />
|
||||
We do not have the power or jurisdiction to shut this down through legal or political means, so we must resort
|
||||
to a covert operation. Your goal is to destroy this technology and eliminate anyone who was involved in its
|
||||
creation.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationDeckard]: {
|
||||
desc: (
|
||||
<>
|
||||
Despite your success in eliminating VitaLife's new android-replicating technology in{" "}
|
||||
{BlackOperationName.OperationK}, we've discovered that a small group of MK-VI Synthoids were able to make off
|
||||
with the schematics and design of the technology before the Operation. It is almost a certainty that these
|
||||
Synthoids are some of the rogue MK-VI ones from the Synthoid Uprising.
|
||||
<br />
|
||||
<br />
|
||||
The goal of {BlackOperationName.OperationDeckard} is to hunt down these Synthoids and retire them. I don't need
|
||||
to tell you how critical this mission is.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationTyrell]: {
|
||||
desc: (
|
||||
<>
|
||||
A week ago {FactionName.BladeIndustries} reported a small break-in at one of their {CityName.Aevum} Augmentation
|
||||
storage facilities. We figured out that {FactionName.TheDarkArmy} was behind the heist, and didn't think any
|
||||
more of it. However, we've just discovered that several known MK-VI Synthoids were part of that break-in group.
|
||||
<br />
|
||||
<br />
|
||||
We cannot have Synthoids upgrading their already-enhanced abilities with Augmentations. Your task is to hunt
|
||||
down associated {FactionName.TheDarkArmy} members and eliminate them.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationWallace]: {
|
||||
desc: (
|
||||
<>
|
||||
Based on information gathered from {BlackOperationName.OperationTyrell}, we've discovered that{" "}
|
||||
{FactionName.TheDarkArmy} was well aware that there were Synthoids amongst their ranks. Even worse, we believe
|
||||
that {FactionName.TheDarkArmy} is working together with other criminal organizations such as{" "}
|
||||
{FactionName.TheSyndicate} and that they are planning some sort of large-scale takeover of multiple major
|
||||
cities, most notably {CityName.Aevum}. We suspect that Synthoids have infiltrated the ranks of these criminal
|
||||
factions and are trying to stage another Synthoid uprising.
|
||||
<br />
|
||||
<br />
|
||||
The best way to deal with this is to prevent it before it even happens. The goal of{" "}
|
||||
{BlackOperationName.OperationWallace} is to destroy {FactionName.TheDarkArmy} and Syndicate factions in{" "}
|
||||
{CityName.Aevum} immediately. Leave no survivors.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationShoulderOfOrion]: {
|
||||
desc: (
|
||||
<>
|
||||
China's Solaris Space Systems is secretly launching the first manned spacecraft in over a decade using
|
||||
Synthoids. We believe China is trying to establish the first off-world colonies.
|
||||
<br />
|
||||
<br />
|
||||
The mission is to prevent this launch without instigating an international conflict. When you accept this
|
||||
mission you will be officially disavowed by the NSA and the national government until after you successfully
|
||||
return. In the event of failure, all of the operation's team members must not let themselves be captured alive.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationHyron]: {
|
||||
desc: (
|
||||
<>
|
||||
Our intelligence tells us that {FactionName.FulcrumSecretTechnologies} is developing a quantum supercomputer
|
||||
using human brains as core processors. This supercomputer is rumored to be able to store vast amounts of data
|
||||
and perform computations unmatched by any other supercomputer on the planet. But more importantly, the use of
|
||||
organic human brains means that the supercomputer may be able to reason abstractly and become self-aware.
|
||||
<br />
|
||||
<br />
|
||||
I do not need to remind you why sentient-level AIs pose a serious threat to all of mankind.
|
||||
<br />
|
||||
<br />
|
||||
The research for this project is being conducted at one of {FactionName.FulcrumSecretTechnologies} secret
|
||||
facilities in {CityName.Aevum}, codenamed 'Alpha Ranch'. Infiltrate the compound, delete and destroy the work,
|
||||
and then find and kill the project lead.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationMorpheus]: {
|
||||
desc: (
|
||||
<>
|
||||
DreamSense Technologies is an advertising company that uses special technology to transmit their ads into the
|
||||
people's dreams and subconscious. They do this using broadcast transmitter towers. Based on information from our
|
||||
agents and informants in {CityName.Chongqing}, we have reason to believe that one of the broadcast towers there
|
||||
has been compromised by Synthoids and is being used to spread pro-Synthoid propaganda.
|
||||
<br />
|
||||
<br />
|
||||
The mission is to destroy this broadcast tower. Speed and stealth are of the utmost importance for this.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationIonStorm]: {
|
||||
desc: (
|
||||
<>
|
||||
Our analysts have uncovered a gathering of MK-VI Synthoids that have taken up residence in the{" "}
|
||||
{CityName.Sector12} Slums. We don't know if they are rogue Synthoids from the Uprising, but we do know that they
|
||||
have been stockpiling weapons, money, and other resources. This makes them dangerous.
|
||||
<br />
|
||||
<br />
|
||||
This is a full-scale assault operation to find and retire all of these Synthoids in the {CityName.Sector12}{" "}
|
||||
Slums.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationAnnihilus]: {
|
||||
desc: (
|
||||
<>
|
||||
Our superiors have ordered us to eradicate everything and everyone in an underground facility located in{" "}
|
||||
{CityName.Aevum}. They tell us that the facility houses many dangerous Synthoids and belongs to a terrorist
|
||||
organization called '{FactionName.TheCovenant}'. We have no prior intelligence about this organization, so you
|
||||
are going in blind.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationUltron]: {
|
||||
desc: (
|
||||
<>
|
||||
{FactionName.OmniTekIncorporated}, the original designer and manufacturer of Synthoids, has notified us of a
|
||||
malfunction in their AI design. This malfunction, when triggered, causes MK-VI Synthoids to become radicalized
|
||||
and seek out the destruction of humanity. They say that this bug affects all MK-VI Synthoids, not just the rogue
|
||||
ones from the Uprising.
|
||||
<br />
|
||||
<br />
|
||||
{FactionName.OmniTekIncorporated} has also told us they believe someone has triggered this malfunction in a
|
||||
large group of MK-VI Synthoids, and that these newly-radicalized Synthoids are now amassing in{" "}
|
||||
{CityName.Volhaven} to form a terrorist group called Ultron.
|
||||
<br />
|
||||
<br />
|
||||
Intelligence suggests Ultron is heavily armed and that their members are augmented. We believe Ultron is making
|
||||
moves to take control of and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).
|
||||
<br />
|
||||
<br />
|
||||
Your task is to find and destroy Ultron.
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationCenturion]: {
|
||||
desc: (
|
||||
<>
|
||||
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||
<br />
|
||||
<br />
|
||||
Throughout all of humanity's history, we have relied on technology to survive, conquer, and progress. Its
|
||||
advancement became our primary goal. And at the peak of human civilization technology turned into power. Global,
|
||||
absolute power.
|
||||
<br />
|
||||
<br />
|
||||
It seems that the universe is not without a sense of irony.
|
||||
<br />
|
||||
<br />
|
||||
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationVindictus]: {
|
||||
desc: (
|
||||
<>
|
||||
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||
<br />
|
||||
<br />
|
||||
The bits are all around us. The daemons that hold the Node together can manifest themselves in many different
|
||||
ways.
|
||||
<br />
|
||||
<br />
|
||||
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||
</>
|
||||
),
|
||||
},
|
||||
[BlackOperationName.OperationDaedalus]: {
|
||||
desc: <> Yesterday we obeyed kings and bent our neck to emperors. Today we kneel only to truth.</>,
|
||||
},
|
||||
};
|
||||
121
src/Bladeburner/data/Contracts.ts
Normal file
121
src/Bladeburner/data/Contracts.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { BladeContractName } from "@enums";
|
||||
import { Contract } from "../Actions/Contract";
|
||||
import { getRandomIntInclusive } from "../../utils/helpers/getRandomIntInclusive";
|
||||
import { assertLoadingType } from "../../utils/TypeAssertion";
|
||||
|
||||
export function createContracts(): Record<BladeContractName, Contract> {
|
||||
return {
|
||||
[BladeContractName.tracking]: new Contract({
|
||||
name: BladeContractName.tracking,
|
||||
desc:
|
||||
"Identify and locate Synthoids. This contract involves reconnaissance and information-gathering ONLY. Do NOT " +
|
||||
"engage. Stealth is of the utmost importance.\n\n" +
|
||||
"Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for whatever " +
|
||||
"city you are currently in.",
|
||||
baseDifficulty: 125,
|
||||
difficultyFac: 1.02,
|
||||
rewardFac: 1.041,
|
||||
rankGain: 0.3,
|
||||
hpLoss: 0.5,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.05,
|
||||
defense: 0.05,
|
||||
dexterity: 0.35,
|
||||
agility: 0.35,
|
||||
charisma: 0.1,
|
||||
intelligence: 0.05,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0,
|
||||
strength: 0.91,
|
||||
defense: 0.91,
|
||||
dexterity: 0.91,
|
||||
agility: 0.91,
|
||||
charisma: 0.9,
|
||||
intelligence: 1,
|
||||
},
|
||||
isStealth: true,
|
||||
growthFunction: () => getRandomIntInclusive(5, 75) / 10,
|
||||
minCount: 25,
|
||||
}),
|
||||
[BladeContractName.bountyHunter]: new Contract({
|
||||
name: BladeContractName.bountyHunter,
|
||||
desc:
|
||||
"Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.\n\n" +
|
||||
"Successfully completing a Bounty Hunter contract will lower the population in your current city, and will also " +
|
||||
"increase its chaos level.",
|
||||
baseDifficulty: 250,
|
||||
difficultyFac: 1.04,
|
||||
rewardFac: 1.085,
|
||||
rankGain: 0.9,
|
||||
hpLoss: 1,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.15,
|
||||
defense: 0.15,
|
||||
dexterity: 0.25,
|
||||
agility: 0.25,
|
||||
charisma: 0.1,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0,
|
||||
strength: 0.91,
|
||||
defense: 0.91,
|
||||
dexterity: 0.91,
|
||||
agility: 0.91,
|
||||
charisma: 0.8,
|
||||
intelligence: 0.9,
|
||||
},
|
||||
isKill: true,
|
||||
growthFunction: () => getRandomIntInclusive(5, 75) / 10,
|
||||
minCount: 5,
|
||||
}),
|
||||
[BladeContractName.retirement]: new Contract({
|
||||
name: BladeContractName.retirement,
|
||||
desc:
|
||||
"Hunt down and retire (kill) rogue Synthoids.\n\n" +
|
||||
"Successfully completing a Retirement contract will lower the population in your current city, and will also " +
|
||||
"increase its chaos level.",
|
||||
baseDifficulty: 200,
|
||||
difficultyFac: 1.03,
|
||||
rewardFac: 1.065,
|
||||
rankGain: 0.6,
|
||||
hpLoss: 1,
|
||||
weights: {
|
||||
hacking: 0,
|
||||
strength: 0.2,
|
||||
defense: 0.2,
|
||||
dexterity: 0.2,
|
||||
agility: 0.2,
|
||||
charisma: 0.1,
|
||||
intelligence: 0.1,
|
||||
},
|
||||
decays: {
|
||||
hacking: 0,
|
||||
strength: 0.91,
|
||||
defense: 0.91,
|
||||
dexterity: 0.91,
|
||||
agility: 0.91,
|
||||
charisma: 0.8,
|
||||
intelligence: 0.9,
|
||||
},
|
||||
isKill: true,
|
||||
growthFunction: () => getRandomIntInclusive(5, 75) / 10,
|
||||
minCount: 5,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function loadContractsData(data: unknown, contracts: Record<BladeContractName, Contract>) {
|
||||
// loading data as "unknown" and typechecking it down is probably not necessary
|
||||
// but this will prevent crashes even with malformed savedata
|
||||
if (!data || typeof data !== "object") return;
|
||||
assertLoadingType<Record<BladeContractName, unknown>>(data);
|
||||
for (const contractName of Object.values(BladeContractName)) {
|
||||
const loadedContract = data[contractName];
|
||||
if (!(loadedContract instanceof Contract)) continue;
|
||||
contracts[contractName].loadData(loadedContract);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
interface IContract {
|
||||
desc: JSX.Element;
|
||||
}
|
||||
|
||||
export const Contracts: Record<string, IContract | undefined> = {
|
||||
Tracking: {
|
||||
desc: (
|
||||
<>
|
||||
Identify and locate Synthoids. This contract involves reconnaissance and information-gathering ONLY. Do NOT
|
||||
engage. Stealth is of the utmost importance.
|
||||
<br />
|
||||
<br />
|
||||
Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for whatever
|
||||
city you are currently in.
|
||||
</>
|
||||
),
|
||||
},
|
||||
"Bounty Hunter": {
|
||||
desc: (
|
||||
<>
|
||||
Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.
|
||||
<br />
|
||||
<br />
|
||||
Successfully completing a Bounty Hunter contract will lower the population in your current city, and will also
|
||||
increase its chaos level.
|
||||
</>
|
||||
),
|
||||
},
|
||||
Retirement: {
|
||||
desc: (
|
||||
<>
|
||||
Hunt down and retire (kill) rogue Synthoids.
|
||||
<br />
|
||||
<br />
|
||||
Successfully completing a Retirement contract will lower the population in your current city, and will also
|
||||
increase its chaos level.
|
||||
</>
|
||||
),
|
||||
},
|
||||
};
|
||||
58
src/Bladeburner/data/GeneralActions.ts
Normal file
58
src/Bladeburner/data/GeneralActions.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { BladeGeneralActionName } from "@enums";
|
||||
import { GeneralAction } from "../Actions/GeneralAction";
|
||||
import { BladeburnerConstants } from "./Constants";
|
||||
|
||||
export const GeneralActions: Record<BladeGeneralActionName, GeneralAction> = {
|
||||
[BladeGeneralActionName.training]: new GeneralAction({
|
||||
name: BladeGeneralActionName.training,
|
||||
getActionTime: () => 30,
|
||||
desc:
|
||||
"Improve your abilities at the Bladeburner unit's specialized training center. Doing this gives experience for " +
|
||||
"all combat stats and also increases your max stamina.",
|
||||
}),
|
||||
[BladeGeneralActionName.fieldAnalysis]: new GeneralAction({
|
||||
name: BladeGeneralActionName.fieldAnalysis,
|
||||
getActionTime: () => 30,
|
||||
desc:
|
||||
"Mine and analyze Synthoid-related data. This improves the Bladeburner unit's intelligence on Synthoid locations " +
|
||||
"and activities. Completing this action will improve the accuracy of your Synthoid population estimated in the " +
|
||||
"current city.\n\n" +
|
||||
"Does NOT require stamina.",
|
||||
}),
|
||||
[BladeGeneralActionName.recruitment]: new GeneralAction({
|
||||
name: BladeGeneralActionName.recruitment,
|
||||
getActionTime: function (bladeburner, person) {
|
||||
const effCharisma = bladeburner.getEffectiveSkillLevel(person, "charisma");
|
||||
const charismaFactor = Math.pow(effCharisma, 0.81) + effCharisma / 90;
|
||||
return Math.max(10, Math.round(BladeburnerConstants.BaseRecruitmentTimeNeeded - charismaFactor));
|
||||
},
|
||||
getSuccessChance: function (bladeburner, person) {
|
||||
return Math.pow(person.skills.charisma, 0.45) / (bladeburner.teamSize - bladeburner.sleeveSize + 1);
|
||||
},
|
||||
desc:
|
||||
"Attempt to recruit members for your Bladeburner team. These members can help you conduct operations.\n\n" +
|
||||
"Does NOT require stamina.",
|
||||
}),
|
||||
[BladeGeneralActionName.diplomacy]: new GeneralAction({
|
||||
name: BladeGeneralActionName.diplomacy,
|
||||
getActionTime: () => 60,
|
||||
desc:
|
||||
"Improve diplomatic relations with the Synthoid population. Completing this action will reduce the Chaos level in " +
|
||||
"your current city.\n\n" +
|
||||
"Does NOT require stamina.",
|
||||
}),
|
||||
[BladeGeneralActionName.hyperbolicRegen]: new GeneralAction({
|
||||
name: BladeGeneralActionName.hyperbolicRegen,
|
||||
getActionTime: () => 60,
|
||||
desc:
|
||||
"Enter cryogenic stasis using the Bladeburner division's hi-tech Regeneration Chamber. This will slowly heal your " +
|
||||
"wounds and slightly increase your stamina.",
|
||||
}),
|
||||
[BladeGeneralActionName.inciteViolence]: new GeneralAction({
|
||||
name: BladeGeneralActionName.inciteViolence,
|
||||
getActionTime: () => 60,
|
||||
desc:
|
||||
"Purposefully stir trouble in the synthoid community in order to gain a political edge. This will generate " +
|
||||
"additional contracts and operations, at the cost of increased Chaos.",
|
||||
}),
|
||||
};
|
||||
@@ -1,97 +0,0 @@
|
||||
import React from "react";
|
||||
import { newWorkStats, WorkStats } from "../../Work/WorkStats";
|
||||
|
||||
interface IGeneral {
|
||||
desc: JSX.Element;
|
||||
exp: WorkStats;
|
||||
}
|
||||
|
||||
export const GeneralActions: Record<string, IGeneral | undefined> = {
|
||||
Training: {
|
||||
desc: (
|
||||
<>
|
||||
Improve your abilities at the Bladeburner unit's specialized training center. Doing this gives experience for
|
||||
all combat stats and also increases your max stamina.
|
||||
</>
|
||||
),
|
||||
exp: newWorkStats({
|
||||
strExp: 30,
|
||||
defExp: 30,
|
||||
dexExp: 30,
|
||||
agiExp: 30,
|
||||
}),
|
||||
},
|
||||
|
||||
"Field Analysis": {
|
||||
desc: (
|
||||
<>
|
||||
Mine and analyze Synthoid-related data. This improves the Bladeburner unit's intelligence on Synthoid locations
|
||||
and activities. Completing this action will improve the accuracy of your Synthoid population estimated in the
|
||||
current city.
|
||||
<br />
|
||||
<br />
|
||||
Does NOT require stamina.
|
||||
</>
|
||||
),
|
||||
exp: newWorkStats({
|
||||
hackExp: 20,
|
||||
chaExp: 20,
|
||||
}),
|
||||
},
|
||||
|
||||
Recruitment: {
|
||||
desc: (
|
||||
<>
|
||||
Attempt to recruit members for your Bladeburner team. These members can help you conduct operations.
|
||||
<br />
|
||||
<br />
|
||||
Does NOT require stamina.
|
||||
</>
|
||||
),
|
||||
exp: newWorkStats({
|
||||
chaExp: 120,
|
||||
}),
|
||||
},
|
||||
|
||||
Diplomacy: {
|
||||
desc: (
|
||||
<>
|
||||
Improve diplomatic relations with the Synthoid population. Completing this action will reduce the Chaos level in
|
||||
your current city.
|
||||
<br />
|
||||
<br />
|
||||
Does NOT require stamina.
|
||||
</>
|
||||
),
|
||||
exp: newWorkStats({
|
||||
chaExp: 120,
|
||||
}),
|
||||
},
|
||||
|
||||
"Hyperbolic Regeneration Chamber": {
|
||||
desc: (
|
||||
<>
|
||||
Enter cryogenic stasis using the Bladeburner division's hi-tech Regeneration Chamber. This will slowly heal your
|
||||
wounds and slightly increase your stamina.
|
||||
<br />
|
||||
<br />
|
||||
</>
|
||||
),
|
||||
exp: newWorkStats(),
|
||||
},
|
||||
"Incite Violence": {
|
||||
desc: (
|
||||
<>
|
||||
Purposefully stir trouble in the synthoid community in order to gain a political edge. This will generate
|
||||
additional contracts and operations, at the cost of increased Chaos.
|
||||
</>
|
||||
),
|
||||
exp: newWorkStats({
|
||||
strExp: 10,
|
||||
defExp: 10,
|
||||
dexExp: 10,
|
||||
agiExp: 10,
|
||||
chaExp: 10,
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
|
||||
export const Growths: {
|
||||
[key: string]: (() => number) | undefined;
|
||||
["Tracking"]: () => number;
|
||||
["Bounty Hunter"]: () => number;
|
||||
["Retirement"]: () => number;
|
||||
["Investigation"]: () => number;
|
||||
["Undercover Operation"]: () => number;
|
||||
["Sting Operation"]: () => number;
|
||||
["Raid"]: () => number;
|
||||
["Stealth Retirement Operation"]: () => number;
|
||||
["Assassination"]: () => number;
|
||||
} = {
|
||||
Tracking: () => getRandomInt(5, 75) / 10,
|
||||
"Bounty Hunter": () => getRandomInt(5, 75) / 10,
|
||||
Retirement: () => getRandomInt(5, 75) / 10,
|
||||
Investigation: () => getRandomInt(10, 40) / 10,
|
||||
"Undercover Operation": () => getRandomInt(10, 40) / 10,
|
||||
"Sting Operation": () => getRandomInt(3, 40) / 10,
|
||||
Raid: () => getRandomInt(2, 40) / 10,
|
||||
"Stealth Retirement Operation": () => getRandomInt(1, 20) / 10,
|
||||
Assassination: () => getRandomInt(1, 20) / 10,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user