merge dev

This commit is contained in:
Olivier Gagnon 2021-10-14 02:12:15 -04:00
commit eb846c1318
39 changed files with 16511 additions and 16285 deletions

138
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

@ -0,0 +1,15 @@
alert() Netscript Function
============================
.. js:function:: alert(message)
:RAM cost: 0 GB
:param string message: message to display
Spawns an alert box.
Example:
.. code-block:: javascript
alert("Reached $1b");

@ -0,0 +1,17 @@
toast() Netscript Function
============================
.. js:function:: toast(message[, variant])
:RAM cost: 0 GB
:param string message: message to display
:param success|info|warning|error variant: color of the toast
Spawns a toast (those bottom left notifications).
Example:
.. code-block:: javascript
toast("Reached $1b");
toast("Failed to hack home", "error");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

32056
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -13,10 +13,10 @@
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@monaco-editor/react": "^4.2.2",
"@mui/icons-material": "^5.0.0-rc.1",
"@mui/icons-material": "^5.0.3",
"@mui/lab": "^5.0.0-alpha.46",
"@mui/material": "^5.0.0-rc.1",
"@mui/styles": "^5.0.0-rc.1",
"@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1",
"@types/escodegen": "^0.0.7",
"@types/js-beautify": "^1.13.2",
"@types/numeral": "0.0.25",
@ -30,6 +30,7 @@
"arg": "^5.0.0",
"async": "^2.6.1",
"autosize": "^4.0.2",
"better-react-mathjax": "^1.0.3",
"brace": "^0.11.1",
"codemirror": "^5.58.2",
"decimal.js": "7.2.3",
@ -47,11 +48,11 @@
"loader-utils": "^1.1.0",
"material-ui-color": "^1.2.0",
"mathjax-full": "^3.2.0",
"mathjax-react": "^1.0.6",
"memory-fs": "~0.4.1",
"monaco-editor": "^0.27.0",
"node-sass": "^6.0.1",
"normalize.css": "^8.0.0",
"notistack": "^2.0.2",
"numeral": "2.0.6",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@ -122,7 +123,6 @@
"start-server-and-test": "^1.14.0",
"style-loader": "^0.21.0",
"stylelint": "^9.2.1",
"stylelint-declaration-use-variable": "^1.6.1",
"stylelint-order": "^0.8.1",
"typescript": "^4.2.4",
"uglify-es": "^3.3.9",

@ -735,7 +735,7 @@ function initAugmentations(): void {
const ENMCore = new Augmentation({
name: AugmentationNames.ENMCore,
repCost: 2.5e5,
repCost: 175e3,
moneyCost: 2.5e9,
info:
"The Core library is an implant that upgrades the firmware of the Embedded Netburner Module. " +

@ -132,7 +132,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@ -149,7 +149,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@ -169,7 +169,7 @@ export class Bladeburner implements IBladeburner {
throw new Error("Failed to get BlackOperation object for: " + actionId.name);
}
this.actionTimeToComplete = action.getActionTime(this);
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@ -220,7 +220,7 @@ export class Bladeburner implements IBladeburner {
for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]);
}
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
}
@ -1298,7 +1298,7 @@ export class Bladeburner implements IBladeburner {
action.level = action.maxLevel;
} // Autolevel
this.startAction(player, this.action); // Repeat action
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@ -1387,7 +1387,7 @@ export class Bladeburner implements IBladeburner {
this.log("You lost " + formatNumber(losses, 0) + " team members during " + action.name);
}
}
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
break;
@ -2056,7 +2056,7 @@ export class Bladeburner implements IBladeburner {
this.startAction(player, actionId);
workerScript.log("bladeburner.startAction", `Starting bladeburner action with type '${type}' and name ${name}"`);
return true;
} catch (e) {
} catch (e: any) {
this.resetAction();
workerScript.log("bladeburner.startAction", errorLogText);
return false;

@ -99,7 +99,7 @@ export class Warehouse {
updateSize(corporation: ICorporation, industry: IIndustry): void {
try {
this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier();
} catch (e) {
} catch (e: any) {
exceptionAlert(e);
}
}

@ -1,6 +1,6 @@
import React from "react";
import { IIndustry } from "../IIndustry";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
interface IProps {
division: IIndustry;
@ -19,9 +19,8 @@ export function IndustryProductEquation(props: IProps): React.ReactElement {
}
return (
<MathComponent
display={false}
tex={reqs.join("+") + String.raw`\Rightarrow` + prod.map((p) => String.raw`1\text{ }${p}`).join("+")}
/>
<MathJaxContext>
<MathJax>{"\\(" + reqs.join("+") + `\\Rightarrow` + prod.map((p) => `1\\text{ }${p}`).join("+") + "\\)"}</MathJax>
</MathJaxContext>
);
}

@ -15,7 +15,7 @@ import { Reputation } from "../../ui/React/Reputation";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
@ -98,9 +98,9 @@ export function DonateOption(props: IProps): React.ReactElement {
}}
/>
<Typography>
<MathComponent
tex={String.raw`reputation = \frac{\text{donation amount} \times \text{reputation multiplier}}{10^{${digits}}}`}
/>
<MathJaxContext>
<MathJax>{`\\(reputation = \\frac{\\text{donation amount} \\cdot \\text{reputation multiplier}}{10^{${digits}}}\\)`}</MathJax>
</MathJaxContext>
</Typography>
</>
)}

@ -9,7 +9,7 @@ import { FactionInfo } from "../../Faction/FactionInfo";
import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
@ -57,10 +57,14 @@ export function Info(props: IProps): React.ReactElement {
You will have <Favor favor={props.faction.favor + favorGain} /> faction favor after installing an
Augmentation.
</Typography>
<MathComponent tex={String.raw`\large{r = \text{total faction reputation}}`} />
<MathComponent
tex={String.raw`\large{favor=\left\lfloor\log_{1.02}\left(\frac{r+25000}{25500}\right)\right\rfloor}`}
/>
<MathJaxContext>
<MathJax>{"\\(\\huge{r = \\text{total faction reputation}}\\)"}</MathJax>
</MathJaxContext>
<MathJaxContext>
<MathJax>
{"\\(\\huge{favor=\\left\\lfloor\\log_{1.02}\\left(\\frac{r+25000}{25500}\\right)\\right\\rfloor}\\)"}
</MathJax>
</MathJaxContext>
</>
}
>
@ -81,8 +85,13 @@ export function Info(props: IProps): React.ReactElement {
favor is gained whenever you install an Augmentation. The amount of favor you gain depends on the total
amount of reputation you earned with this faction. Across all resets.
</Typography>
<MathComponent tex={String.raw`\large{r = reputation}`} />
<MathComponent tex={String.raw`\large{\Delta r = \Delta r \times \frac{100+favor}{100}}`} />
<MathJaxContext>
<MathJax>{"\\(\\huge{r = reputation}\\)"}</MathJax>
</MathJaxContext>
<MathJaxContext>
<MathJax>{"\\(\\huge{\\Delta r = \\Delta r \\times \\frac{100+favor}{100}}\\)"}</MathJax>
</MathJaxContext>
</>
}
>

@ -97,7 +97,7 @@ export class Gang {
this.processExperienceGains(cycles);
this.processTerritoryAndPowerGains(cycles);
this.storedCycles -= cycles;
} catch (e) {
} catch (e: any) {
console.error(`Exception caught when processing Gang: ${e}`);
}
}
@ -344,7 +344,7 @@ export class Gang {
workerScript.log("ascend", `Ascended Gang member ${member.name}`);
}
return res;
} catch (e) {
} catch (e: any) {
if (workerScript == null) {
exceptionAlert(e);
}

@ -5,7 +5,7 @@ import Tooltip from "@mui/material/Tooltip";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money";
import { MathComponent } from "mathjax-react";
import { MathJax, MathJaxContext } from "better-react-mathjax";
type IProps = {
p: IPlayer;
@ -19,7 +19,7 @@ export function CoresButton(props: IProps): React.ReactElement {
return <Button>Upgrade 'home' cores - MAX</Button>;
}
const cost = 1e9 * Math.pow(7.5, homeComputer.cpuCores);
const cost = props.p.getUpgradeHomeCoresCost();
function buy(): void {
if (maxCores) return;
@ -30,7 +30,13 @@ export function CoresButton(props: IProps): React.ReactElement {
}
return (
<Tooltip title={<MathComponent tex={String.raw`\large{cost = 10^9 \times 7.5 ^{\text{cores}}}`} />}>
<Tooltip
title={
<MathJaxContext>
<MathJax>{`\\(\\large{cost = 10^9 \\cdot 7.5 ^{\\text{cores}}}\\)`}</MathJax>
</MathJaxContext>
}
>
<span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' cores ({homeComputer.cpuCores} -&gt; {homeComputer.cpuCores + 1}) -&nbsp;

@ -7,8 +7,8 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases";
import { Money } from "../../ui/React/Money";
import { MathComponent } from "mathjax-react";
import { numeralWrapper } from "../../ui/numeralFormat";
import { MathJax, MathJaxContext } from "better-react-mathjax";
type IProps = {
p: IPlayer;
@ -29,7 +29,13 @@ export function RamButton(props: IProps): React.ReactElement {
}
return (
<Tooltip title={<MathComponent tex={String.raw`\large{cost = 3.2 \times 10^3 \times 1.58^{log_2{(ram)}}}`} />}>
<Tooltip
title={
<MathJaxContext>
<MathJax>{`\\(\\large{cost = 3.2 \\cdot 10^3 \\cdot 1.58^{log_2{(ram)}}}\\)`}</MathJax>
</MathJaxContext>
}
>
<span>
<Button disabled={!props.p.canAfford(cost)} onClick={buy}>
Upgrade 'home' RAM ({numeralWrapper.formatRAM(homeComputer.maxRam)} -&gt;&nbsp;

@ -13,12 +13,14 @@ import { Reviver } from "../utils/JSONReviver";
function sendMessage(msg: Message, forced = false): void {
msg.recvd = true;
if (forced || !Settings.SuppressMessages) {
showMessage(msg);
showMessage(msg.filename);
}
addMessageToServer(msg, "home");
}
function showMessage(msg: Message): void {
function showMessage(name: string): void {
const msg = Messages[name];
if (!msg) throw new Error("trying to display unexistent message");
const txt =
"Message received from unknown sender: <br><br>" +
"<i>" +
@ -39,12 +41,11 @@ function addMessageToServer(msg: Message, serverHostname: string): void {
}
for (let i = 0; i < server.messages.length; ++i) {
const other = server.messages[i];
if (typeof other === "string") continue;
if (msg.filename === other.filename) {
if (msg.filename === other) {
return; //Already exists
}
}
server.messages.push(msg);
server.messages.push(msg.filename);
}
//Checks if any of the 'timed' messages should be sent

@ -81,7 +81,6 @@ import { LocationName } from "./Locations/data/LocationNames";
import { Terminal } from "./Terminal";
import { calculateSkill, calculateExp } from "./PersonObjects/formulas/skill";
import { Message } from "./Message/Message";
import { Player } from "./Player";
import { Programs } from "./Programs/Programs";
import { Script } from "./Script/Script";
@ -147,6 +146,8 @@ import { INetscriptSleeve, NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
import { INetscriptExtra, NetscriptExtra } from "./NetscriptFunctions/Extra";
import { INetscriptHacknet, NetscriptHacknet } from "./NetscriptFunctions/Hacknet";
import { INetscriptStanek, NetscriptStanek } from "./NetscriptFunctions/Stanek";
import { dialogBoxCreate } from "./ui/React/DialogBox";
import { SnackbarEvents } from "./ui/React/Snackbar";
const defaultInterpreter = new Interpreter("", () => undefined);
@ -479,15 +480,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
return contract;
};
const checkStanekAPIAccess = function (func: string): void {
if (Player.bitNodeN !== 13 && !SourceFileFlags[13]) {
throw makeRuntimeErrorMsg(
`stanek.${func}`,
"You do not currently have access to the Stanek API. This is either because you are not in BitNode-13 or because you do not have Source-File 13",
);
}
};
const getBladeburnerActionObject = function (func: any, type: any, name: any): any {
const bladeburner = Player.bladeburner;
if (bladeburner === null) throw new Error("Must have joined bladeburner");
@ -1346,7 +1338,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (scriptname.endsWith(".lit")) {
let found = false;
for (let i = 0; i < currServ.messages.length; ++i) {
if (!(currServ.messages[i] instanceof Message) && currServ.messages[i] == scriptname) {
if (currServ.messages[i] == scriptname) {
found = true;
break;
}
@ -1472,20 +1464,11 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
for (let i = 0; i < server.messages.length; i++) {
if (filter) {
const msg = server.messages[i];
if (msg instanceof Message) {
if (msg.filename.includes(filter)) {
allFiles.push(msg.filename);
}
} else if (msg.includes(filter)) {
if (msg.includes(filter)) {
allFiles.push(msg);
}
} else {
const msg = server.messages[i];
if (msg instanceof Message) {
allFiles.push(msg.filename);
} else {
allFiles.push(msg);
}
allFiles.push(server.messages[i]);
}
}
@ -1780,7 +1763,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}
}
for (let i = 0; i < server.messages.length; ++i) {
if (!(server.messages[i] instanceof Message) && filename.toLowerCase() === server.messages[i]) {
if (filename.toLowerCase() === server.messages[i]) {
return true;
}
}
@ -2312,7 +2295,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
// Coerce 'data' to be a string
try {
data = String(data);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("write", `Invalid data (${e}). Data being written must be convertible to a string`);
}
@ -2691,6 +2674,14 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
updateDynamicRam("getTimeSinceLastAug", getRamCost("getTimeSinceLastAug"));
return Player.playtimeSinceLastAug;
},
alert: function (message: any): void {
dialogBoxCreate(message);
},
toast: function (message: any, variant: any = "success"): void {
if (!["success", "info", "warning", "error"].includes(variant))
throw new Error(`variant must be one of "success", "info", "warning", or "error"`);
SnackbarEvents.emit(message, variant);
},
prompt: function (txt: any): any {
if (!isString(txt)) {
txt = JSON.stringify(txt);
@ -3967,7 +3958,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.startActionNetscriptFn(Player, type, name, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.startAction", e);
}
},
@ -3992,7 +3983,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getActionTimeNetscriptFn(Player, type, name, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.getActionTime", e);
}
},
@ -4006,7 +3997,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getActionEstimatedSuccessChanceNetscriptFn(Player, type, name, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.getActionEstimatedSuccessChance", e);
}
},
@ -4030,7 +4021,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getActionCountRemainingNetscriptFn(type, name, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.getActionCountRemaining", e);
}
},
@ -4091,7 +4082,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getSkillLevelNetscriptFn(skillName, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.getSkillLevel", e);
}
},
@ -4102,7 +4093,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getSkillUpgradeCostNetscriptFn(skillName, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.getSkillUpgradeCost", e);
}
},
@ -4113,7 +4104,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.upgradeSkillNetscriptFn(skillName, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.upgradeSkill", e);
}
},
@ -4124,7 +4115,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.getTeamSizeNetscriptFn(type, name, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.getTeamSize", e);
}
},
@ -4135,7 +4126,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
if (bladeburner === null) throw new Error("Should not be called without Bladeburner");
try {
return bladeburner.setTeamSizeNetscriptFn(type, name, size, workerScript);
} catch (e) {
} catch (e: any) {
throw makeRuntimeErrorMsg("bladeburner.setTeamSize", e);
}
},

@ -93,7 +93,7 @@ function startNetscript2Script(workerScript: WorkerScript): Promise<WorkerScript
let result;
try {
result = f(...args);
} catch (e) {
} catch (e: any) {
runningFn = null;
throw e;
}
@ -152,7 +152,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
const importProcessingRes = processNetscript1Imports(code, workerScript);
codeWithImports = importProcessingRes.code;
codeLineOffset = importProcessingRes.lineOffset;
} catch (e) {
} catch (e: any) {
dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + e);
workerScript.env.stopFlag = true;
workerScript.running = false;
@ -252,7 +252,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
let interpreter: any;
try {
interpreter = new Interpreter(codeWithImports, interpreterInitialization, codeLineOffset);
} catch (e) {
} catch (e: any) {
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + e);
workerScript.env.stopFlag = true;
workerScript.running = false;
@ -272,7 +272,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
} else {
resolve(workerScript);
}
} catch (e) {
} catch (e: any) {
e = e.toString();
if (!isScriptErrorMessage(e)) {
e = makeRuntimeRejectMsg(workerScript, e);
@ -284,7 +284,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
try {
runInterpreter();
} catch (e) {
} catch (e: any) {
if (isString(e)) {
workerScript.errorMessage = e;
return reject(workerScript);

@ -191,6 +191,7 @@ export interface IPlayer {
getHomeComputer(): Server;
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition | null;
getUpgradeHomeRamCost(): number;
getUpgradeHomeCoresCost(): number;
gotoLocation(to: LocationName): boolean;
hasAugmentation(aug: string | Augmentation): boolean;
hasCorporation(): boolean;

@ -198,6 +198,7 @@ export class PlayerObject implements IPlayer {
getHomeComputer: () => Server;
getNextCompanyPosition: (company: Company, entryPosType: CompanyPosition) => CompanyPosition | null;
getUpgradeHomeRamCost: () => number;
getUpgradeHomeCoresCost: () => number;
gotoLocation: (to: LocationName) => boolean;
hasAugmentation: (aug: string | Augmentation) => boolean;
hasCorporation: () => boolean;
@ -569,6 +570,7 @@ export class PlayerObject implements IPlayer {
this.getCurrentServer = serverMethods.getCurrentServer;
this.getHomeComputer = serverMethods.getHomeComputer;
this.getUpgradeHomeRamCost = serverMethods.getUpgradeHomeRamCost;
this.getUpgradeHomeCoresCost = serverMethods.getUpgradeHomeCoresCost;
this.createHacknetServer = serverMethods.createHacknetServer;
this.factionWorkType = "";
this.committingCrimeThruSingFn = false;

@ -451,9 +451,8 @@ export function gainIntelligenceExp(this: IPlayer, exp: number): void {
}
if (SourceFileFlags[5] > 0 || this.intelligence > 0) {
this.intelligence_exp += exp;
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
}
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
}
//Given a string expression like "str" or "strength", returns the given stat

@ -40,6 +40,10 @@ export function getUpgradeHomeRamCost(this: IPlayer): number {
return cost;
}
export function getUpgradeHomeCoresCost(this: IPlayer): number {
return 1e9 * Math.pow(7.5, this.getHomeComputer().cpuCores);
}
export function createHacknetServer(this: IPlayer): HacknetServer {
const numOwned = this.hacknetNodes.length;
const name = `hacknet-node-${numOwned}`;

@ -29,5 +29,4 @@ export function loadPlayer(saveString: string): void {
}
Player.exploits = sanitizeExploits(Player.exploits);
console.log(Player.bladeburner);
}

@ -11,7 +11,7 @@ import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
import { staneksGift, loadStaneksGift } from "./CotMG/Helper";
import { GameSavedEvents } from "./ui/React/Snackbar";
import { SnackbarEvents } from "./ui/React/Snackbar";
import * as ExportBonus from "./ExportBonus";
@ -64,7 +64,7 @@ class BitburnerSaveObject {
const saveString = this.getSaveString();
save(saveString)
.then(() => GameSavedEvents.emit())
.then(() => SnackbarEvents.emit("Game Saved!", "info"))
.catch((err) => console.error(err));
}
@ -76,9 +76,10 @@ class BitburnerSaveObject {
const bn = Player.bitNodeN;
const filename = `bitburnerSave_BN${bn}x${SourceFileFlags[bn]}_${epochTime}.json`;
const file = new Blob([saveString], { type: "text/plain" });
if (window.navigator.msSaveOrOpenBlob) {
const navigator = window.navigator as any;
if (navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
const a = document.createElement("a"),
@ -156,6 +157,24 @@ function evaluateVersionCompatibility(ver: string): void {
}
}
}
if (ver < "0.56.1") {
if (anyPlayer.bladeburner === 0) {
anyPlayer.bladeburner = null;
}
if (anyPlayer.gang === 0) {
anyPlayer.gang = null;
}
if (anyPlayer.corporation === 0) {
anyPlayer.corporation = null;
}
// convert all Messages to just filename to save space.
const home = anyPlayer.getHomeComputer();
for (let i = 0; i < home.messages.length; i++) {
if (home.messages[i].filename) {
home.messages[i] = home.messages[i].filename;
}
}
}
}
function loadGame(saveString: string): boolean {

@ -58,9 +58,10 @@ export class Script {
download(): void {
const filename = this.filename + ".js";
const file = new Blob([this.code], { type: "text/plain" });
if (window.navigator.msSaveOrOpenBlob) {
const navigator = window.navigator as any;
if (navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
const a = document.createElement("a"),
@ -112,6 +113,10 @@ export class Script {
this.markUpdated();
}
imports(): string[] {
return [];
}
// Serialize the current object to a JSON save state
toJSON(): any {
return Generic_toJSON("Script", this);

@ -23,6 +23,7 @@ import { Settings } from "../../Settings/Settings";
import { iTutorialNextStep, ITutorial, iTutorialSteps } from "../../InteractiveTutorial";
import { debounce } from "lodash";
import { saveObject } from "../../SaveObject";
import { loadThemes } from "./themes";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
@ -312,6 +313,7 @@ export function Root(props: IProps): React.ReactElement {
});
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, "netscript.d.ts");
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, "netscript.d.ts");
loadThemes(monaco);
}
return (

@ -61,7 +61,7 @@ export class BaseServer {
// For Literature files, this array contains only the filename (string)
// For Messages, it contains the actual Message object
// TODO Separate literature files into its own property
messages: (Message | string)[] = [];
messages: string[] = [];
// Name of company/faction/etc. that this server belongs to.
// Optional, not applicable to all Servers

@ -3,7 +3,6 @@ import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { showMessage } from "../../Message/MessageHelpers";
import { Message } from "../../Message/Message";
import { showLiterature } from "../../Literature/LiteratureHelpers";
export function cat(
@ -29,12 +28,12 @@ export function cat(
for (let i = 0; i < server.messages.length; ++i) {
if (filename.endsWith(".lit") && server.messages[i] === filename) {
const file = server.messages[i];
if (file instanceof Message) throw new Error(".lit file should not be a .msg");
if (file.endsWith(".msg")) throw new Error(".lit file should not be a .msg");
showLiterature(file);
return;
} else if (filename.endsWith(".msg")) {
const file = server.messages[i] as Message;
if (file.filename !== filename) continue;
const file = server.messages[i];
if (file !== filename) continue;
showMessage(file);
return;
}

@ -2,7 +2,6 @@ import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { Message } from "../../Message/Message";
import { getFirstParentDirectory, isValidDirectoryPath, evaluateDirectoryPath } from "../../Terminal/DirectoryHelpers";
export function ls(
@ -103,8 +102,7 @@ export function ls(
for (const script of s.scripts) handleFn(script.filename, allScripts);
for (const txt of s.textFiles) handleFn(txt.fn, allTextFiles);
for (const contract of s.contracts) handleFn(contract.fn, allContracts);
for (const msgOrLit of s.messages)
msgOrLit instanceof Message ? handleFn(msgOrLit.filename, allMessages) : handleFn(msgOrLit, allMessages);
for (const msgOrLit of s.messages) handleFn(msgOrLit, allMessages);
// Sort the files/folders alphabetically then print each
allPrograms.sort();

@ -34,7 +34,7 @@ export function scp(
if (scriptname.endsWith(".lit")) {
let found = false;
for (let i = 0; i < server.messages.length; ++i) {
if (!(server.messages[i] instanceof Message) && server.messages[i] == scriptname) {
if (server.messages[i] == scriptname) {
found = true;
break;
}

@ -71,7 +71,7 @@ export function determineAllPossibilitiesForTabCompletion(
function addAllLitFiles(): void {
for (const file of currServ.messages) {
if (!(file instanceof Message)) {
if (!file.endsWith(".msg")) {
allPos.push(file);
}
}
@ -79,8 +79,8 @@ export function determineAllPossibilitiesForTabCompletion(
function addAllMessages(): void {
for (const file of currServ.messages) {
if (file instanceof Message) {
allPos.push(file.filename);
if (file.endsWith(".msg")) {
allPos.push(file);
}
}
}

@ -35,9 +35,10 @@ export class TextFile {
const filename: string = this.fn;
const file: Blob = new Blob([this.text], { type: "text/plain" });
/* tslint:disable-next-line:strict-boolean-expressions */
if (window.navigator.msSaveOrOpenBlob) {
const navigator = window.navigator as any;
if (navigator.msSaveOrOpenBlob) {
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
navigator.msSaveOrOpenBlob(file, filename);
} else {
// Others
const a: HTMLAnchorElement = document.createElement("a");

@ -67,7 +67,7 @@ import { CharacterOverview } from "./React/CharacterOverview";
import { BladeburnerCinematic } from "../Bladeburner/ui/BladeburnerCinematic";
import { workerScripts } from "../Netscript/WorkerScripts";
import { Unclickable } from "../Exploits/Unclickable";
import { Snackbar } from "./React/Snackbar";
import { Snackbar, SnackbarProvider } from "./React/Snackbar";
import { LogBoxManager } from "./React/LogBoxManager";
import { AlertManager } from "./React/AlertManager";
import { PromptManager } from "./React/PromptManager";
@ -302,102 +302,104 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
) : page === Page.Work ? (
<WorkInProgressRoot />
) : (
<Box display="flex" flexDirection="row" width="100%">
<SidebarRoot player={player} router={Router} page={page} />
<Box className={classes.root} flexGrow={1} display="block" px={1} height="100vh">
{page === Page.Terminal ? (
<TerminalRoot terminal={terminal} router={Router} player={player} />
) : page === Page.Sleeves ? (
<SleeveRoot />
) : page === Page.Stats ? (
<CharacterStats />
) : page === Page.StaneksGift ? (
<StaneksGiftRoot staneksGift={staneksGift} />
) : page === Page.ScriptEditor ? (
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
) : page === Page.ActiveScripts ? (
<ActiveScriptsRoot workerScripts={workerScripts} />
) : page === Page.Hacknet ? (
<HacknetRoot player={player} />
) : page === Page.CreateProgram ? (
<ProgramsRoot />
) : page === Page.Factions ? (
<FactionsRoot player={player} router={Router} />
) : page === Page.Faction ? (
<FactionRoot faction={faction} />
) : page === Page.Milestones ? (
<MilestonesRoot player={player} />
) : page === Page.Tutorial ? (
<TutorialRoot />
) : page === Page.DevMenu ? (
<DevMenuRoot player={player} engine={engine} router={Router} />
) : page === Page.Gang ? (
<GangRoot />
) : page === Page.Corporation ? (
<CorporationRoot />
) : page === Page.Bladeburner ? (
<BladeburnerRoot />
) : page === Page.Resleeves ? (
<ResleeveRoot />
) : page === Page.Travel ? (
<TravelAgencyRoot p={player} router={Router} />
) : page === Page.StockMarket ? (
<StockMarketRoot
buyStockLong={buyStock}
buyStockShort={shortStock}
cancelOrder={cancelOrder}
eventEmitterForReset={eventEmitterForUiReset}
initStockMarket={initStockMarketFnForReact}
p={player}
placeOrder={placeOrder}
sellStockLong={sellStock}
sellStockShort={sellShort}
stockMarket={StockMarket}
/>
) : page === Page.City ? (
<LocationCity />
) : page === Page.Job ? (
<GenericLocation loc={location} />
) : page === Page.Location ? (
<GenericLocation loc={location} />
) : page === Page.Options ? (
<GameOptionsRoot
player={player}
save={() => saveObject.saveGame()}
export={() => saveObject.exportGame()}
forceKill={() => {
for (const server of GetAllServers()) {
server.runningScripts = [];
}
dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page.");
}}
softReset={() => {
dialogBoxCreate("Soft Reset!");
prestigeAugmentation();
Router.toTerminal();
}}
/>
) : page === Page.Augmentations ? (
<AugmentationsRoot
exportGameFn={() => {
saveObject.exportGame();
onExport(player);
}}
installAugmentationsFn={() => {
installAugmentations();
Router.toTerminal();
}}
/>
) : (
<>
<Typography>Cannot load</Typography>
</>
)}
<SnackbarProvider>
<Box display="flex" flexDirection="row" width="100%">
<SidebarRoot player={player} router={Router} page={page} />
<Box className={classes.root} flexGrow={1} display="block" px={1} height="100vh">
{page === Page.Terminal ? (
<TerminalRoot terminal={terminal} router={Router} player={player} />
) : page === Page.Sleeves ? (
<SleeveRoot />
) : page === Page.StaneksGift ? (
<StaneksGiftRoot staneksGift={staneksGift} />
) : page === Page.Stats ? (
<CharacterStats />
) : page === Page.ScriptEditor ? (
<ScriptEditorRoot filename={filename} code={code} player={player} router={Router} />
) : page === Page.ActiveScripts ? (
<ActiveScriptsRoot workerScripts={workerScripts} />
) : page === Page.Hacknet ? (
<HacknetRoot player={player} />
) : page === Page.CreateProgram ? (
<ProgramsRoot />
) : page === Page.Factions ? (
<FactionsRoot player={player} router={Router} />
) : page === Page.Faction ? (
<FactionRoot faction={faction} />
) : page === Page.Milestones ? (
<MilestonesRoot player={player} />
) : page === Page.Tutorial ? (
<TutorialRoot />
) : page === Page.DevMenu ? (
<DevMenuRoot player={player} engine={engine} router={Router} />
) : page === Page.Gang ? (
<GangRoot />
) : page === Page.Corporation ? (
<CorporationRoot />
) : page === Page.Bladeburner ? (
<BladeburnerRoot />
) : page === Page.Resleeves ? (
<ResleeveRoot />
) : page === Page.Travel ? (
<TravelAgencyRoot p={player} router={Router} />
) : page === Page.StockMarket ? (
<StockMarketRoot
buyStockLong={buyStock}
buyStockShort={shortStock}
cancelOrder={cancelOrder}
eventEmitterForReset={eventEmitterForUiReset}
initStockMarket={initStockMarketFnForReact}
p={player}
placeOrder={placeOrder}
sellStockLong={sellStock}
sellStockShort={sellShort}
stockMarket={StockMarket}
/>
) : page === Page.City ? (
<LocationCity />
) : page === Page.Job ? (
<GenericLocation loc={location} />
) : page === Page.Location ? (
<GenericLocation loc={location} />
) : page === Page.Options ? (
<GameOptionsRoot
player={player}
save={() => saveObject.saveGame()}
export={() => saveObject.exportGame()}
forceKill={() => {
for (const server of GetAllServers()) {
server.runningScripts = [];
}
dialogBoxCreate("Forcefully deleted all running scripts. Please save and refresh page.");
}}
softReset={() => {
dialogBoxCreate("Soft Reset!");
prestigeAugmentation();
Router.toTerminal();
}}
/>
) : page === Page.Augmentations ? (
<AugmentationsRoot
exportGameFn={() => {
saveObject.exportGame();
onExport(player);
}}
installAugmentationsFn={() => {
installAugmentations();
Router.toTerminal();
}}
/>
) : (
<>
<Typography>Cannot load</Typography>
</>
)}
</Box>
</Box>
</Box>
<Snackbar />
</SnackbarProvider>
)}
<Unclickable />
<Snackbar />
<LogBoxManager />
<AlertManager />
<PromptManager />

@ -46,6 +46,31 @@ function Intelligence(): React.ReactElement {
);
}
function Bladeburner(): React.ReactElement {
const player = use.Player();
const classes = useStyles();
const bladeburner = player.bladeburner;
if (bladeburner === null) return <></>;
const action = bladeburner.getTypeAndNameFromActionId(bladeburner.action);
if (action.type === "Idle") return <></>;
return (
<>
<TableRow>
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
<Typography>Bladeburner:</Typography>
</TableCell>
</TableRow>
<TableRow>
<TableCell component="th" scope="row" colSpan={2} classes={{ root: classes.cellNone }}>
<Typography>
{action.type}: {action.name}
</Typography>
</TableCell>
</TableRow>
</>
);
}
function Work(): React.ReactElement {
const player = use.Player();
const router = use.Router();
@ -269,6 +294,7 @@ export function CharacterOverview({ save }: IProps): React.ReactElement {
</TableCell>
</TableRow>
<Work />
<Bladeburner />
<TableRow>
<TableCell align="center" colSpan={2} classes={{ root: classes.cellNone }}>

@ -1,29 +1,48 @@
import React, { useState, useEffect } from "react";
import { Snackbar as S } from "@mui/material";
import React, { useEffect } from "react";
import { useSnackbar, SnackbarProvider as SB } from "notistack";
import { EventEmitter } from "../../utils/EventEmitter";
import Typography from "@mui/material/Typography";
import Alert from "@mui/material/Alert";
import Paper from "@mui/material/Paper";
export const GameSavedEvents = new EventEmitter<[]>();
interface IProps {
children: React.ReactNode | React.ReactNode[];
}
export function Snackbar(): React.ReactElement {
const [open, setOpen] = useState(false);
useEffect(() => GameSavedEvents.subscribe(() => setOpen(true)));
export function SnackbarProvider(props: IProps): React.ReactElement {
return (
<S
open={open}
anchorOrigin={{
vertical: "top",
horizontal: "center",
}}
autoHideDuration={2000}
onClose={() => setOpen(false)}
>
<Paper sx={{ p: 2 }}>
<Typography>Game Saved!</Typography>
</Paper>
</S>
<SB dense maxSnack={9} anchorOrigin={{ horizontal: "right", vertical: "bottom" }} autoHideDuration={2000}>
{props.children}
</SB>
);
}
export const SnackbarEvents = new EventEmitter<[string, "success" | "warning" | "error" | "info"]>();
export function Snackbar(): React.ReactElement {
const { enqueueSnackbar } = useSnackbar();
useEffect(() =>
SnackbarEvents.subscribe((s, variant) =>
enqueueSnackbar(<Alert severity={variant}>{s}</Alert>, {
content: (k, m) => <Paper key={k}>{m}</Paper>,
variant: variant,
}),
),
);
return <></>;
// return (
// <S
// open={open}
// anchorOrigin={{
// vertical: "top",
// horizontal: "center",
// }}
// autoHideDuration={2000}
// onClose={() => setOpen(false)}
// >
// <Paper sx={{ p: 2 }}>
// <Typography>Game Saved!</Typography>
// </Paper>
// </S>
// );
}

@ -294,6 +294,27 @@ export function refreshTheme(): void {
},
},
},
MuiAlert: {
styleOverrides: {
root: {
backgroundColor: Settings.theme.black,
borderRadius: 0,
border: "1px solid " + Settings.theme.well,
},
standardSuccess: {
color: Settings.theme.successLight,
},
standardError: {
color: Settings.theme.errorlight,
},
standardWarning: {
color: Settings.theme.warninglight,
},
standardInfo: {
color: Settings.theme.infolight,
},
},
},
},
});
}

@ -1,7 +1,6 @@
/* eslint-disable spaced-comment */
module.exports = {
plugins: [
"stylelint-declaration-use-variable",
"stylelint-order" /*,
"stylelint-scss" */,
],

@ -95,7 +95,7 @@ describe("determineAllPossibilitiesForTabCompletion", function () {
Player.getHomeComputer().writeToTextFile("note.txt", "oh hai mark");
Player.getHomeComputer().writeToScriptFile("/www/script.js", "oh hai mark");
Player.getHomeComputer().contracts.push(new CodingContract("linklist.cct"));
Player.getHomeComputer().messages.push(new Message("asl.msg"));
Player.getHomeComputer().messages.push("asl.msg");
Player.getHomeComputer().messages.push("af.lit");
expect(determineAllPossibilitiesForTabCompletion(Player, "rm ", 0)).equal([
"/www/script.js",
@ -120,7 +120,7 @@ describe("determineAllPossibilitiesForTabCompletion", function () {
it("completes the cat command", () => {
Player.getHomeComputer().writeToTextFile("/www/note.txt", "oh hai mark");
Player.getHomeComputer().messages.push(new Message("asl.msg"));
Player.getHomeComputer().messages.push("asl.msg");
Player.getHomeComputer().messages.push("af.lit");
expect(determineAllPossibilitiesForTabCompletion(Player, "cat ", 0)).equal([
"asl.msg",