diff --git a/src/Constants.ts b/src/Constants.ts
index bc6660f46..b71a1ec8e 100644
--- a/src/Constants.ts
+++ b/src/Constants.ts
@@ -270,6 +270,24 @@ v2.0.0 - 2022-07-19 Work rework
are very common tokens.
* corporation.bribe no longer allows to give shares as bribe.
+ Netscript
+
+ * Add getAugmentationBasePrice
+ * Add getSleeveAugmentationPrice
+ * Add getSleeveAugmentationRepReq
+ * Fix getInfiltrationLocations
+ * Singularity.goToLocation support for non-city-specific locations (@Ansopedian)
+ * All corporation functions are synchronous. Job assignment only works on the following cycle. (@stalefishies)
+ * Add batch functionality to NS spendHashes API (@undeemiss)
+ * Fix #3661 Add missing memory property to Sleeve API (@borisflagell)
+ * FIX#3732 Cannot assign two sleeve on "Take on contracts" regardless of contract type. (@borisflagell)
+
+ Corporation
+
+ * Dividend fixes and exposing dividends info via scripts (@stalefishies)
+ * Add big number format support in some Corporation's modal (@borisflagell)
+ * Fix #3261 Industry overview number formatting (@nickofolas)
+
Multipliers
* The main player object was also plagues with a million fields all called '*_mult'. Representing the different multipliers
@@ -277,6 +295,15 @@ v2.0.0 - 2022-07-19 Work rework
Misc.
+ * #3596 Enhanced terminal command parsing (@RevanProdigalKnight)
+ * Fix #3366 Sleeve UI would sometimes displays the wrong stat while working out. (@borisflagell)
+ * Two new encryption themed contracts - caesar and vigenere (@Markus-D-M)
+ * Fixes #3132 several Sleeve can no longer works concurrently in the same company (@borisflagell)
+ * FIX #3514 Clear recently killed tab on BN end event (@Daniel-Barbera)
+ * HammingCodes description and implementation fixes (@s2ks)
+ * FIX #3794 Sleeve were getting less shocked when hospitalized (was positive, should have detrimental) (@borisflagell)
+ * Fix #3803 Servers can no longer have duplicate IPs (@crimsonhawk47)
+ * Fix #3854 ctrl+c does not clear terminal input (@evil-tim)
* Nerf noodle bar, obviously.
`,
diff --git a/src/SaveObject.tsx b/src/SaveObject.tsx
index df9aa9231..765b5ba55 100755
--- a/src/SaveObject.tsx
+++ b/src/SaveObject.tsx
@@ -35,6 +35,7 @@ import { FactionNames } from "./Faction/data/FactionNames";
import { Faction } from "./Faction/Faction";
import { safetlyCreateUniqueServer } from "./Server/ServerHelpers";
import { SpecialServers } from "./Server/data/SpecialServers";
+import { v2APIBreak } from "./utils/v2APIBreak";
/* SaveObject.js
* Defines the object used to save/load games
@@ -461,6 +462,7 @@ function evaluateVersionCompatibility(ver: string | number): void {
if (create) Player.getHomeComputer().pushProgram(create);
const graft = anyPlayer["graftAugmentationName"];
if (graft) Player.augmentations.push({ name: graft, level: 1 });
+ v2APIBreak();
}
}
}
diff --git a/src/engine.tsx b/src/engine.tsx
index ecb280a7a..3748be96f 100644
--- a/src/engine.tsx
+++ b/src/engine.tsx
@@ -291,7 +291,9 @@ const Engine: {
const offlineHackingIncome = (Player.moneySourceA.hacking / Player.playtimeSinceLastAug) * timeOffline * 0.75;
Player.gainMoney(offlineHackingIncome, "hacking");
// Process offline progress
+
loadAllRunningScripts(Player); // This also takes care of offline production for those scripts
+
if (Player.currentWork !== null) {
Player.focus = true;
Player.processWork(numCyclesOffline);
diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx
index 293038414..281ef5006 100644
--- a/src/ui/GameRoot.tsx
+++ b/src/ui/GameRoot.tsx
@@ -87,6 +87,7 @@ import _wrap from "lodash/wrap";
import _functions from "lodash/functions";
import { Apr1 } from "./Apr1";
import { isFactionWork } from "../Work/FactionWork";
+import { V2Modal } from "../utils/V2Modal";
const htmlLocation = location;
@@ -558,6 +559,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
+
);
diff --git a/src/utils/V2Modal.tsx b/src/utils/V2Modal.tsx
new file mode 100644
index 000000000..f3dc4b6de
--- /dev/null
+++ b/src/utils/V2Modal.tsx
@@ -0,0 +1,35 @@
+import { Button, Typography } from "@mui/material";
+import React, { useState } from "react";
+import { Modal } from "../ui/React/Modal";
+
+let v2ModalOpen = false;
+
+export const openV2Modal = (): void => {
+ v2ModalOpen = true;
+};
+
+export const V2Modal = (): React.ReactElement => {
+ const [open, setOpen] = useState(v2ModalOpen);
+ return (
+ undefined}>
+ Welcome to bitburner v2.0.0!{" "}
+ While this version does not change the game a lot, it does have quite a few API breaks.{" "}
+
+ A file was added to your home computer called V2_0_0_API_BREAK.txt and it is highly recommended you take a look
+ at this file. It explains where most of the API break have occured.
+ {" "}
+
+ You should also take a look at{" "}
+
+ {" "}
+ the migration guide
+ {" "}
+ as well as{" "}
+
+ the changelog
+
+
+
+
+ );
+};
diff --git a/src/utils/v1APIBreak.ts b/src/utils/v1APIBreak.ts
index bdbdffea3..e9f3bde1f 100644
--- a/src/utils/v1APIBreak.ts
+++ b/src/utils/v1APIBreak.ts
@@ -84,11 +84,13 @@ export function AwardNFG(n = 1): void {
}
}
+export interface IFileLine {
+ file: string;
+ line: number;
+ content: string;
+}
+
export function v1APIBreak(): void {
- interface IFileLine {
- file: string;
- line: number;
- }
let txt = "";
for (const server of GetAllServers()) {
for (const change of detect) {
@@ -100,6 +102,7 @@ export function v1APIBreak(): void {
s.push({
file: script.filename,
line: i + 1,
+ content: "",
});
}
}
diff --git a/src/utils/v2APIBreak.ts b/src/utils/v2APIBreak.ts
index 5bcef5dcb..19f36714e 100644
--- a/src/utils/v2APIBreak.ts
+++ b/src/utils/v2APIBreak.ts
@@ -1,4 +1,10 @@
-export const singularity = [
+import { saveObject } from "../SaveObject";
+import { Script } from "../Script/Script";
+import { GetAllServers, GetServer } from "../Server/AllServers";
+import { IFileLine } from "./v1APIBreak";
+import { openV2Modal } from "./V2Modal";
+
+const singularity = [
"applyToCompany",
"b1tflum3",
"checkFactionInvitations",
@@ -52,3 +58,197 @@ export const singularity = [
"workForCompany",
"workForFaction",
];
+
+const getPlayerFields = [
+ "workChaExpGained",
+ "currentWorkFactionName",
+ "workDexExpGained",
+ "workHackExpGained",
+ "createProgramReqLvl",
+ "workStrExpGained",
+ "companyName",
+ "crimeType",
+ "workRepGained",
+ "workChaExpGainRate",
+ "workType",
+ "workStrExpGainRate",
+ "isWorking",
+ "workRepGainRate",
+ "workDefExpGained",
+ "currentWorkFactionDescription",
+ "workHackExpGainRate",
+ "workAgiExpGainRate",
+ "workDexExpGainRate",
+ "workMoneyGained",
+ "workMoneyLossRate",
+ "workMoneyGainRate",
+ "createProgramName",
+ "workDefExpGainRate",
+ "workAgiExpGained",
+ "className",
+];
+
+const mults = [
+ "hacking_chance_mult",
+ "hacking_speed_mult",
+ "hacking_money_mult",
+ "hacking_grow_mult",
+ "hacking_mult",
+ "hacking_exp_mult",
+ "strength_mult",
+ "strength_exp_mult",
+ "defense_mult",
+ "defense_exp_mult",
+ "dexterity_mult",
+ "dexterity_exp_mult",
+ "agility_mult",
+ "agility_exp_mult",
+ "charisma_mult",
+ "charisma_exp_mult",
+ "hacknet_node_money_mult",
+ "hacknet_node_purchase_cost_mult",
+ "hacknet_node_ram_cost_mult",
+ "hacknet_node_core_cost_mult",
+ "hacknet_node_level_cost_mult",
+ "company_rep_mult",
+ "faction_rep_mult",
+ "work_money_mult",
+ "crime_success_mult",
+ "crime_money_mult",
+ "bladeburner_max_stamina_mult",
+ "bladeburner_stamina_gain_mult",
+ "bladeburner_analysis_mult",
+ "bladeburner_success_chance_mult",
+];
+
+interface IRule {
+ match: RegExp;
+ reason: string;
+ offenders: IFileLine[];
+}
+
+export const v2APIBreak = () => {
+ const home = GetServer("home");
+ if (!home) throw new Error("'home' server was not found.");
+ const rules: IRule[] = [
+ {
+ match: /ns\.workForCompany/g,
+ reason: "workForCompany argument companyName is now not-optional.",
+ offenders: [],
+ },
+ {
+ match: /ns\.getScriptExpGain/g,
+ reason: "getScriptExpGain with 0 argument no longer returns the sum of all scripts. Use getTotalScriptExpGain",
+ offenders: [],
+ },
+ {
+ match: /ns\.getScriptExpGain/g,
+ reason: "getScriptIncome with 0 argument no longer returns the sum of all scripts. Use getTotalScriptIncome",
+ offenders: [],
+ },
+ {
+ match: /ns\.scp/g,
+ reason:
+ "scp arguments were switch, it is now scp(files, destination, optionally_source). If you were using 2 argument (not 3) this doesn't affect you.",
+ offenders: [],
+ },
+ {
+ match: /ns\.stock\.buy/g,
+ reason: "buy is a very common word so in order to avoid ram costs it was renamed ns.stock.buyStock",
+ offenders: [],
+ },
+ {
+ match: /ns\.stock\.sell/g,
+ reason: "sell is a very common word so in order to avoid ram costs it was renamed ns.stock.sellStock",
+ offenders: [],
+ },
+ {
+ match: /ns\.corporation\.bribe/g,
+ reason: "bribe no longer allows you to give shares of the corporation, only money",
+ offenders: [],
+ },
+ ];
+
+ for (const fn of singularity) {
+ rules.push({
+ match: new RegExp(`ns.${fn}`, "g"),
+ reason: `ns.${fn} was moved to ns.singularity.${fn}`,
+ offenders: [],
+ });
+ }
+
+ for (const mult of mults) {
+ rules.push({
+ match: new RegExp(mult, "g"),
+ reason: `ns.getPlayer().${mult} was moved to ns.getPlayer().mults.${mult.slice(0, mult.length - 5)}`,
+ offenders: [],
+ });
+ }
+
+ for (const f of getPlayerFields) {
+ rules.push({
+ match: new RegExp(f, "g"),
+ reason: `The work system is completely reworked and ns.getPlayer().${f} no longer exists. This data is likely available inside ns.getPlayer().currentWork`,
+ offenders: [],
+ });
+ }
+
+ for (const script of home.scripts) {
+ processScript(rules, script);
+ }
+
+ home.writeToTextFile("V2_0_0_API_BREAK.txt", formatRules(rules));
+ openV2Modal();
+
+ for (const server of GetAllServers()) {
+ server.runningScripts = [];
+ }
+ saveObject.exportGame();
+};
+
+const formatOffenders = (offenders: IFileLine[]): string => {
+ const files: Record = {};
+ for (const off of offenders) {
+ const current = files[off.file] ?? [];
+ current.push(off);
+ files[off.file] = current;
+ }
+
+ let txt = "";
+ for (const file in files) {
+ txt += "\t" + file + "\n";
+ for (const fileline of files[file]) {
+ txt += `\t\tLine ${fileline.line} ${fileline.content.trim()}\n`;
+ }
+ }
+ return txt;
+};
+
+const formatRules = (rules: IRule[]): string => {
+ let txt =
+ "This file contains the list of potential API break. A pattern was used to look through all your files and note the spots where you might have a problem. Not everything here is broken.";
+ for (const rule of rules) {
+ if (rule.offenders.length === 0) continue;
+ txt += String(rule.match) + "\n";
+ txt += rule.reason + "\n\n";
+ txt += formatOffenders(rule.offenders);
+ txt += "\n\n";
+ }
+ return txt;
+};
+
+const processScript = (rules: IRule[], script: Script) => {
+ const lines = script.code.split("\n");
+ for (let i = 0; i < lines.length; i++) {
+ for (const rule of rules) {
+ const line = lines[i];
+ if (line.match(rule.match)) {
+ rule.offenders.push({
+ file: script.filename,
+ line: i + 1,
+ content: line,
+ });
+ }
+ }
+ }
+};