mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-29 09:27:33 +01:00
commit
4e82293afb
54
dist/vendor.bundle.js
vendored
54
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -28,6 +28,8 @@ grow() Netscript Function
|
||||
server, but there is no required hacking level to run the command. It also
|
||||
raises the security level of the target server by 0.004 per thread.
|
||||
|
||||
Action time is calculated at the start, effect is calculated at the end.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
@ -27,6 +27,8 @@ hack() Netscript Function
|
||||
A successful :doc:`hack<hack>` on a server will raise that server's security
|
||||
level by 0.002.
|
||||
|
||||
Action time is calculated at the start, effect is calculated at the end.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
@ -3,7 +3,9 @@ getAugmentationCost() Netscript Function
|
||||
|
||||
.. js:function:: getAugmentationCost(augName)
|
||||
|
||||
.. warning:: This function is deprecated.
|
||||
.. warning:: This function is deprecated. It still functions, but new
|
||||
scripts should prefer :doc:`getAugmentationPrice<getAugmentationPrice>`
|
||||
and :doc:`getAugmentationRepReq<getAugmentationRepReq>` instead.
|
||||
|
||||
:RAM cost: 5 GB
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
const { app, BrowserWindow, Menu } = require("electron");
|
||||
const { app, BrowserWindow, Menu, globalShortcut, shell } = require("electron");
|
||||
|
||||
Menu.setApplicationMenu(false);
|
||||
function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
@ -12,6 +13,24 @@ function createWindow() {
|
||||
win.maximize();
|
||||
win.loadFile("index.html");
|
||||
win.show();
|
||||
// win.webContents.openDevTools();
|
||||
globalShortcut.register("f5", function () {
|
||||
win.loadFile("index.html");
|
||||
});
|
||||
globalShortcut.register("f8", function () {
|
||||
win.loadFile("index.html", { query: { noScripts: "true" } });
|
||||
});
|
||||
|
||||
win.webContents.on("new-window", function (e, url) {
|
||||
// make sure local urls stay in electron perimeter
|
||||
if ("file://" === url.substr(0, "file://".length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// and open every other protocols on the browser
|
||||
e.preventDefault();
|
||||
shell.openExternal(url);
|
||||
});
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1357
package-lock.json
generated
1357
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -58,7 +58,8 @@
|
||||
"treant-js": "^1.0.1",
|
||||
"unused-webpack-plugin": "^2.4.0",
|
||||
"uuid": "^3.2.1",
|
||||
"w3c-blob": "0.0.1"
|
||||
"w3c-blob": "0.0.1",
|
||||
"webpack-deadcode-plugin": "^0.1.15"
|
||||
},
|
||||
"description": "A cyberpunk-themed incremental game",
|
||||
"devDependencies": {
|
||||
|
15
package.sh
15
package.sh
@ -1,14 +1,19 @@
|
||||
# npm install electron --save-dev
|
||||
# npm install electron-packager --save-dev
|
||||
|
||||
mkdir -p .package/dist || true
|
||||
mkdir -p .package/dist/src/ThirdParty || true
|
||||
mkdir -p .package/src/ThirdParty || true
|
||||
|
||||
cp index.html .package
|
||||
cp electron/* .package
|
||||
cp dist/engine.bundle.js .package/dist
|
||||
cp dist/engineStyle.css .package/dist
|
||||
# The css files
|
||||
cp dist/vendor.css .package/dist
|
||||
cp dist/engineStyle.bundle.js .package/dist
|
||||
cp dist/vendor.bundle.js .package/dist
|
||||
cp main.css .package/main.css
|
||||
|
||||
# The js files.
|
||||
cp dist/vendor.bundle.js .package/dist/vendor.bundle.js
|
||||
cp main.bundle.js .package/main.bundle.js
|
||||
|
||||
cp src/ThirdParty/raphael.min.js .package/src/ThirdParty/raphael.min.js
|
||||
|
||||
npm run package-electron
|
@ -211,7 +211,7 @@ function initAugmentations() {
|
||||
name: AugmentationNames.Targeting3,
|
||||
moneyCost: 1.15e8,
|
||||
repCost: 2.75e4,
|
||||
info: "The latest version of the 'Augmented Targeting' implant adds the ability to " + "lock-on and track threats.",
|
||||
info: "The latest version of the 'Augmented Targeting' implant adds the ability to lock-on and track threats.",
|
||||
prereqs: [AugmentationNames.Targeting2],
|
||||
dexterity_mult: 1.3,
|
||||
});
|
||||
|
@ -16,8 +16,11 @@ class BitNode {
|
||||
// BitNode number
|
||||
number: number;
|
||||
|
||||
constructor(n: number, name: string, desc = "", info: JSX.Element = <></>) {
|
||||
difficulty: 0 | 1 | 2;
|
||||
|
||||
constructor(n: number, difficulty: 0 | 1 | 2, name: string, desc = "", info: JSX.Element = <></>) {
|
||||
this.number = n;
|
||||
this.difficulty = difficulty;
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
this.info = info;
|
||||
@ -28,6 +31,7 @@ export const BitNodes: IMap<BitNode> = {};
|
||||
|
||||
BitNodes["BitNode1"] = new BitNode(
|
||||
1,
|
||||
0,
|
||||
"Source Genesis",
|
||||
"The original BitNode",
|
||||
(
|
||||
@ -54,6 +58,7 @@ BitNodes["BitNode1"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode2"] = new BitNode(
|
||||
2,
|
||||
0,
|
||||
"Rise of the Underworld",
|
||||
"From the shadows, they rose", //Gangs
|
||||
(
|
||||
@ -101,6 +106,7 @@ BitNodes["BitNode2"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode3"] = new BitNode(
|
||||
3,
|
||||
0,
|
||||
"Corporatocracy",
|
||||
"The Price of Civilization",
|
||||
(
|
||||
@ -140,6 +146,7 @@ BitNodes["BitNode3"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode4"] = new BitNode(
|
||||
4,
|
||||
1,
|
||||
"The Singularity",
|
||||
"The Man and the Machine",
|
||||
(
|
||||
@ -164,6 +171,7 @@ BitNodes["BitNode4"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode5"] = new BitNode(
|
||||
5,
|
||||
1,
|
||||
"Artificial Intelligence",
|
||||
"Posthuman",
|
||||
(
|
||||
@ -211,6 +219,7 @@ BitNodes["BitNode5"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode6"] = new BitNode(
|
||||
6,
|
||||
1,
|
||||
"Bladeburners",
|
||||
"Like Tears in Rain",
|
||||
(
|
||||
@ -255,6 +264,7 @@ BitNodes["BitNode6"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode7"] = new BitNode(
|
||||
7,
|
||||
2,
|
||||
"Bladeburners 2079",
|
||||
"More human than humans",
|
||||
(
|
||||
@ -303,6 +313,7 @@ BitNodes["BitNode7"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode8"] = new BitNode(
|
||||
8,
|
||||
2,
|
||||
"Ghost of Wall Street",
|
||||
"Money never sleeps",
|
||||
(
|
||||
@ -347,6 +358,7 @@ BitNodes["BitNode8"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode9"] = new BitNode(
|
||||
9,
|
||||
2,
|
||||
"Hacktocracy",
|
||||
"Hacknet Unleashed",
|
||||
(
|
||||
@ -389,6 +401,7 @@ BitNodes["BitNode9"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode10"] = new BitNode(
|
||||
10,
|
||||
2,
|
||||
"Digital Carbon",
|
||||
"Your body is not who you are",
|
||||
(
|
||||
@ -428,6 +441,7 @@ BitNodes["BitNode10"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode11"] = new BitNode(
|
||||
11,
|
||||
1,
|
||||
"The Big Crash",
|
||||
"Okay. Sell it all.",
|
||||
(
|
||||
@ -492,6 +506,7 @@ BitNodes["BitNode11"] = new BitNode(
|
||||
);
|
||||
BitNodes["BitNode12"] = new BitNode(
|
||||
12,
|
||||
0,
|
||||
"The Recursion",
|
||||
"Repeat.",
|
||||
(
|
||||
@ -507,18 +522,18 @@ BitNodes["BitNode12"] = new BitNode(
|
||||
),
|
||||
);
|
||||
// Books: Frontera, Shiner
|
||||
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
||||
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
|
||||
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
|
||||
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
|
||||
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
|
||||
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
|
||||
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
|
||||
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
|
||||
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
|
||||
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
|
||||
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
|
||||
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
|
||||
BitNodes["BitNode13"] = new BitNode(13, 2, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
|
||||
BitNodes["BitNode14"] = new BitNode(14, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode17"] = new BitNode(17, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode18"] = new BitNode(18, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode19"] = new BitNode(19, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode20"] = new BitNode(20, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode21"] = new BitNode(21, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode22"] = new BitNode(22, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode23"] = new BitNode(23, 2, "", "COMING SOON");
|
||||
BitNodes["BitNode24"] = new BitNode(24, 2, "", "COMING SOON");
|
||||
|
||||
export function initBitNodeMultipliers(p: IPlayer): void {
|
||||
if (p.bitNodeN == null) {
|
||||
|
@ -29,6 +29,9 @@ export function PortalPopup(props: IProps): React.ReactElement {
|
||||
Source-File Level: {props.level} / {maxSourceFileLevel}
|
||||
<br />
|
||||
<br />
|
||||
Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}
|
||||
<br />
|
||||
<br />
|
||||
{bitNode.info}
|
||||
<br />
|
||||
<br />
|
||||
|
@ -3,6 +3,7 @@ import { removePopup } from "../../ui/React/createPopup";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { WorldMap } from "../../ui/React/WorldMap";
|
||||
import { CityName } from "../../Locations/data/CityNames";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -21,7 +22,15 @@ export function TravelPopup(props: IProps): React.ReactElement {
|
||||
Travel to a different city for your Bladeburner activities. This does not cost any money. The city you are in
|
||||
for your Bladeburner duties does not affect your location in the game otherwise.
|
||||
</p>
|
||||
<WorldMap currentCity={props.bladeburner.city as CityName} onTravel={(city: CityName) => travel(city)} />
|
||||
{Settings.DisableASCIIArt ? (
|
||||
Object.values(CityName).map((city: CityName) => (
|
||||
<button key={city} className="std-button" onClick={() => travel(city)}>
|
||||
{city}
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<WorldMap currentCity={props.bladeburner.city as CityName} onTravel={(city: CityName) => travel(city)} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ export class Roulette extends Game<IProps, IState> {
|
||||
if (playerWin && Math.random() > 0.9) {
|
||||
playerWin = false;
|
||||
while (this.state.strategy.match(n)) {
|
||||
n++;
|
||||
n = (n + 1) % 36;
|
||||
}
|
||||
}
|
||||
if (playerWin) {
|
||||
|
1
src/CinematicText.d.ts
vendored
1
src/CinematicText.d.ts
vendored
@ -1 +0,0 @@
|
||||
export declare let cinematicTextFlag: boolean;
|
@ -1,119 +0,0 @@
|
||||
import { Engine } from "./engine";
|
||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
|
||||
import { removeChildrenFromElement } from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { isString } from "../utils/helpers/isString";
|
||||
|
||||
export let cinematicTextFlag = false;
|
||||
|
||||
/**
|
||||
* Print a message using a hacking-style "typing" effect.
|
||||
* Note that this clears the UI so that the text from this is the only thing visible.
|
||||
*
|
||||
* @param lines {string[]} Array of strings to print, where each element is a separate line
|
||||
*/
|
||||
export function writeCinematicText(lines) {
|
||||
cinematicTextFlag = true;
|
||||
|
||||
if (lines.constructor !== Array) {
|
||||
throw new Error("Invalid non-array argument passed into writeCinematicText()");
|
||||
}
|
||||
|
||||
// Reuse the 'Red Pill' content
|
||||
Engine.loadCinematicTextContent();
|
||||
const container = document.getElementById("cinematic-text-container");
|
||||
container.style.width = "75%";
|
||||
if (container == null) {
|
||||
throw new Error("Could not find cinematic-text-container for writeCinematicText()");
|
||||
}
|
||||
removeChildrenFromElement(container);
|
||||
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
if (!isString(lines[i])) {
|
||||
throw new Error("Invalid non-string element in 'lines' argument. writeCinematicText() failed");
|
||||
}
|
||||
}
|
||||
|
||||
return writeCinematicTextRecurse(lines)
|
||||
.then(function () {
|
||||
return cinematicTextEnd(); //Puts the continue button
|
||||
})
|
||||
.catch(function (e) {
|
||||
exceptionAlert(e);
|
||||
});
|
||||
}
|
||||
|
||||
function writeCinematicTextRecurse(lines, lineNumber = 0) {
|
||||
if (lineNumber >= lines.length) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return writeCinematicTextLine(lines[lineNumber]).then(function () {
|
||||
return writeCinematicTextRecurse(lines, lineNumber + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function writeCinematicTextLine(line) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const container = document.getElementById("cinematic-text-container");
|
||||
const pElem = document.createElement("p");
|
||||
container.appendChild(pElem);
|
||||
|
||||
const promise = writeCinematicTextLetter(pElem, line, 0);
|
||||
promise.then(
|
||||
function (res) {
|
||||
resolve(res);
|
||||
},
|
||||
function (e) {
|
||||
reject(e);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function writeCinematicTextLetter(pElem, line, i = 0) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeoutRef(function () {
|
||||
const textToShow = line.substring(0, i);
|
||||
|
||||
if (i >= line.length) {
|
||||
pElem.innerHTML = textToShow;
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
pElem.innerHTML = textToShow + "<span class='typed-cursor'> █ </span>";
|
||||
const promise = writeCinematicTextLetter(pElem, line, i + 1);
|
||||
promise.then(
|
||||
function (res) {
|
||||
resolve(res);
|
||||
},
|
||||
function (e) {
|
||||
reject(e);
|
||||
},
|
||||
);
|
||||
}, 15);
|
||||
});
|
||||
}
|
||||
|
||||
function cinematicTextEnd() {
|
||||
var container = document.getElementById("cinematic-text-container");
|
||||
var mainMenu = document.getElementById("mainmenu-container");
|
||||
container.appendChild(createElement("br"));
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
container.appendChild(
|
||||
createElement("a", {
|
||||
class: "a-link-button",
|
||||
innerText: "Continue...",
|
||||
clickListener: () => {
|
||||
removeChildrenFromElement(container);
|
||||
Engine.loadTerminalContent();
|
||||
mainMenu.style.visibility = "visible";
|
||||
cinematicTextFlag = false;
|
||||
resolve();
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
// Array of all valid states
|
||||
export const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
|
||||
const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
|
||||
|
||||
export class CorporationState {
|
||||
// Number representing what state the Corporation is in. The number
|
||||
|
@ -4,10 +4,12 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
popupId: string;
|
||||
router: IRouter;
|
||||
}
|
||||
|
||||
export function CreateCorporationPopup(props: IProps): React.ReactElement {
|
||||
@ -40,6 +42,7 @@ export function CreateCorporationPopup(props: IProps): React.ReactElement {
|
||||
"and manage your company in the City.",
|
||||
);
|
||||
removePopup(props.popupId);
|
||||
props.router.toCorporation();
|
||||
}
|
||||
|
||||
function seed(): void {
|
||||
@ -55,6 +58,7 @@ export function CreateCorporationPopup(props: IProps): React.ReactElement {
|
||||
"You can visit and manage your company in the City.",
|
||||
);
|
||||
removePopup(props.popupId);
|
||||
props.router.toCorporation();
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -95,7 +95,7 @@ function BulkPurchase(props: IProps): React.ReactElement {
|
||||
style={{ margin: "5px" }}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<button className="std-button">Confirm Bulk Purchase</button>
|
||||
<button className="std-button" onClick={bulkPurchase}>Confirm Bulk Purchase</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ export function ResearchPopup(props: IProps): React.ReactElement {
|
||||
Research(props.industry, allResearch[i]);
|
||||
} catch (err) {
|
||||
dialogBoxCreate(err + "");
|
||||
return;
|
||||
}
|
||||
|
||||
dialogBoxCreate(
|
||||
|
@ -24,35 +24,6 @@ export function checkIfConnectedToDarkweb(): void {
|
||||
}
|
||||
}
|
||||
|
||||
//Handler for dark web commands. The terminal's executeCommand() function will pass
|
||||
//dark web-specific commands into this. It will pass in the raw split command array
|
||||
//rather than the command string
|
||||
export function executeDarkwebTerminalCommand(commandArray: string[]): void {
|
||||
if (commandArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
switch (commandArray[0]) {
|
||||
case "buy": {
|
||||
if (commandArray.length != 2) {
|
||||
Terminal.error("Incorrect number of arguments. Usage: ");
|
||||
Terminal.print("buy -l");
|
||||
Terminal.print("buy [item name]");
|
||||
return;
|
||||
}
|
||||
const arg = commandArray[1];
|
||||
if (arg == "-l" || arg == "-1" || arg == "--list") {
|
||||
listAllDarkwebItems();
|
||||
} else {
|
||||
buyDarkwebItem(arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Terminal.error("Command not found");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function listAllDarkwebItems(): void {
|
||||
for (const key in DarkWebItems) {
|
||||
const item = DarkWebItems[key];
|
||||
|
@ -22,7 +22,7 @@ export function TimeSkip(props: IProps): React.ReactElement {
|
||||
return () => {
|
||||
props.player.lastUpdate -= time;
|
||||
props.engine._lastUpdate -= time;
|
||||
saveObject.saveGame(props.engine.indexedDb);
|
||||
saveObject.saveGame();
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
};
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ interface IServerProps {
|
||||
ip: string;
|
||||
}
|
||||
|
||||
export function ServerAccordion(props: IServerProps): React.ReactElement {
|
||||
function ServerAccordion(props: IServerProps): React.ReactElement {
|
||||
const server = AllServers[props.ip];
|
||||
let totalSize = 0;
|
||||
for (const f of server.scripts) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Player } from "../Player";
|
||||
import { Exploit } from "./Exploit";
|
||||
|
||||
(function () {
|
||||
export function startTampering(): void {
|
||||
const a = 55;
|
||||
setInterval(function () {
|
||||
if (a.toExponential() !== "5.5e+1") {
|
||||
Player.giveExploit(Exploit.PrototypeTampering);
|
||||
}
|
||||
}, 15 * 60 * 1000); // 15 minutes
|
||||
})();
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Player } from "../Player";
|
||||
import { Exploit } from "./Exploit";
|
||||
|
||||
(function () {
|
||||
export function startUnclickable(): void {
|
||||
function clickTheUnclickable(event: MouseEvent): void {
|
||||
if (!event.target || !(event.target instanceof Element)) return;
|
||||
const display = window.getComputedStyle(event.target as Element).display;
|
||||
@ -19,4 +19,4 @@ import { Exploit } from "./Exploit";
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", targetElement);
|
||||
})();
|
||||
}
|
||||
|
@ -1,360 +0,0 @@
|
||||
import { IMap } from "../types";
|
||||
|
||||
/**
|
||||
* Contains the "information" property for all the Factions, which is just a description of each faction
|
||||
*/
|
||||
export class FactionInfo {
|
||||
/**
|
||||
* The multiplier to apply to augmentation base purchase price.
|
||||
*/
|
||||
augmentationPriceMult: number;
|
||||
|
||||
/**
|
||||
* The multiplier to apply to augmentation reputation base requirement.
|
||||
*/
|
||||
augmentationRepRequirementMult: number;
|
||||
|
||||
/**
|
||||
* The names of all other factions considered to be enemies to this faction.
|
||||
*/
|
||||
enemies: string[];
|
||||
|
||||
/**
|
||||
* The descriptive text to show on the faction's page.
|
||||
*/
|
||||
infoText: string;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports field work to earn reputation.
|
||||
*/
|
||||
offerFieldWork: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports hacking missions to earn reputation.
|
||||
*/
|
||||
offerHackingMission: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports hacking work to earn reputation.
|
||||
*/
|
||||
offerHackingWork: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports security work to earn reputation.
|
||||
*/
|
||||
offerSecurityWork: boolean;
|
||||
|
||||
constructor(
|
||||
infoText: string,
|
||||
enemies: string[],
|
||||
offerHackingMission: boolean,
|
||||
offerHackingWork: boolean,
|
||||
offerFieldWork: boolean,
|
||||
offerSecurityWork: boolean,
|
||||
) {
|
||||
this.infoText = infoText;
|
||||
this.enemies = enemies;
|
||||
this.offerHackingMission = offerHackingMission;
|
||||
this.offerHackingWork = offerHackingWork;
|
||||
this.offerFieldWork = offerFieldWork;
|
||||
this.offerSecurityWork = offerSecurityWork;
|
||||
|
||||
// These are always all 1 for now.
|
||||
this.augmentationPriceMult = 1;
|
||||
this.augmentationRepRequirementMult = 1;
|
||||
}
|
||||
|
||||
offersWork(): boolean {
|
||||
return this.offerFieldWork || this.offerHackingMission || this.offerHackingWork || this.offerSecurityWork;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of all factions and associated info to them.
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const FactionInfos: IMap<FactionInfo> = {
|
||||
// Endgame
|
||||
Illuminati: new FactionInfo(
|
||||
"Humanity never changes. No matter how civilized society becomes, it will eventually fall back into chaos. " +
|
||||
"And from this chaos, we are the invisible hand that guides them to order. ",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
Daedalus: new FactionInfo(
|
||||
"Yesterday we obeyed kings and bent our necks to emperors. Today we kneel only to truth.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
"The Covenant": new FactionInfo(
|
||||
"Surrender yourself. Give up your empty individuality to become part of something great, something eternal. " +
|
||||
"Become a slave. Submit your mind, body, and soul. Only then can you set yourself free.<br>" +
|
||||
"<br>" +
|
||||
"Only then can you discover immortality.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
// Megacorporations, each forms its own faction
|
||||
ECorp: new FactionInfo(
|
||||
"ECorp's mission is simple: to connect the world of today with the technology of tomorrow. With our wide " +
|
||||
"range of Internet-related software and commercial hardware, ECorp makes the world's information " +
|
||||
"universally accessible.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
MegaCorp: new FactionInfo(
|
||||
"MegaCorp does what no other dares to do. We imagine. We create. We invent. We create what others have " +
|
||||
"never even dreamed of. Our work fills the world's needs for food, water, power, and transporation on an " +
|
||||
"unprecendented scale, in ways that no other company can.<br>" +
|
||||
"<br>" +
|
||||
"In our labs and factories and on the ground with customers, MegaCorp is ushering in a new era for the world.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Bachman & Associates": new FactionInfo(
|
||||
"Where Law and Business meet - thats where we are.<br>" +
|
||||
"<br>" +
|
||||
"Legal Insight - Business Instinct - Innovative Experience.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Blade Industries": new FactionInfo("Augmentation is Salvation.", [], true, true, true, true),
|
||||
|
||||
NWO: new FactionInfo(
|
||||
"Humans don't truly desire freedom. They want to be observed, understood, and judged. They want to " +
|
||||
"be given purpose and direction in life. That is why they created God. And that is why they created " +
|
||||
"civilization - not because of willingness, but because of a need to be incorporated into higher orders of " +
|
||||
"structure and meaning.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Clarke Incorporated": new FactionInfo("The Power of the Genome - Unlocked.", [], true, true, true, true),
|
||||
|
||||
"OmniTek Incorporated": new FactionInfo(
|
||||
"Simply put, our mission is to design and build robots that make a difference.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Four Sigma": new FactionInfo(
|
||||
"The scientific method is the best way to approach investing. Big strategies backed up with big data. Driven " +
|
||||
"by deep learning and innovative ideas. And improved by iteration. That's Four Sigma.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"KuaiGong International": new FactionInfo("Dream big. Work hard. Make history.", [], true, true, true, true),
|
||||
|
||||
// Other Corporations
|
||||
"Fulcrum Secret Technologies": new FactionInfo(
|
||||
"The human organism has an innate desire to worship. That is why they created gods. If there were no gods, it " +
|
||||
"would be necessary to create them. And now we can.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
|
||||
// Hacker groups
|
||||
BitRunners: new FactionInfo(
|
||||
"Our entire lives are controlled by bits. All of our actions, our thoughts, our personal information. It's " +
|
||||
"all transformed into bits, stored in bits, communicated through bits. It’s impossible for any person to move, " +
|
||||
"to live, to operate at any level without the use of bits. And when a person moves, lives, and operates, they " +
|
||||
"leave behind their bits, mere traces of seemingly meaningless fragments of information. But these bits can be " +
|
||||
"reconstructed. Transformed. Used.<br>" +
|
||||
"<br>" +
|
||||
"Those who run the bits, run the world.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
"The Black Hand": new FactionInfo(
|
||||
"The world, so afraid of strong government, now has no government. Only power - Digital power. Financial " +
|
||||
"power. Technological power. And those at the top rule with an invisible hand. They built a society where the " +
|
||||
"rich get richer, and everyone else suffers.<br>" +
|
||||
"<br>" +
|
||||
"So much pain. So many lives. Their darkness must end.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
NiteSec: new FactionInfo(
|
||||
" __..__ <br>" +
|
||||
" _.nITESECNIt. <br>" +
|
||||
" .-'NITESECNITESEc. <br>" +
|
||||
" .' NITESECNITESECn <br>" +
|
||||
" / NITESECNITESEC; <br>" +
|
||||
" : :NITESECNITESEC; <br>" +
|
||||
" ; $ NITESECNITESECN <br>" +
|
||||
" : _, ,N'ITESECNITESEC <br>" +
|
||||
" : .+^^`, : `NITESECNIT <br>" +
|
||||
" ) /), `-,-=,NITESECNI <br>" +
|
||||
" / ^ ,-;|NITESECN; <br>" +
|
||||
" / _.' '-';NITESECN <br>" +
|
||||
" ( , ,-''`^NITE' <br>" +
|
||||
" )` :`. .' <br>" +
|
||||
" )-- ; `- / <br>" +
|
||||
" ' _.-' : <br>" +
|
||||
" ( _.-' . <br>" +
|
||||
" ------. <br>" +
|
||||
" . <br>" +
|
||||
" _.nIt <br>" +
|
||||
" _.nITESECNi <br>" +
|
||||
" nITESECNIT^' <br>" +
|
||||
" NITE^' ___ <br>" +
|
||||
" / .gP''''Tp. <br>" +
|
||||
" : d' . `b <br>" +
|
||||
" ; d' o `b ; <br>" +
|
||||
" / d; `b| <br>" +
|
||||
" /, $; @ `: <br>" +
|
||||
" /' $$ ; <br>" +
|
||||
" .' $$b o | <br>" +
|
||||
" .' d$$$; : <br>" +
|
||||
" / .d$$$$; , ; <br>" +
|
||||
" d .dNITESEC $ | <br>" +
|
||||
" :bp.__.gNITESEC$$ :$ ; <br>" +
|
||||
" NITESECNITESECNIT $$b : <br>",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// City factions, essentially governments
|
||||
Aevum: new FactionInfo("The Silicon City.", ["Chongqing", "New Tokyo", "Ishima", "Volhaven"], true, true, true, true),
|
||||
Chongqing: new FactionInfo("Serve the People.", ["Sector-12", "Aevum", "Volhaven"], true, true, true, true),
|
||||
Ishima: new FactionInfo(
|
||||
"The East Asian Order of the Future.",
|
||||
["Sector-12", "Aevum", "Volhaven"],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
"New Tokyo": new FactionInfo("Asia's World City.", ["Sector-12", "Aevum", "Volhaven"], true, true, true, true),
|
||||
"Sector-12": new FactionInfo(
|
||||
"The City of the Future.",
|
||||
["Chongqing", "New Tokyo", "Ishima", "Volhaven"],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
Volhaven: new FactionInfo(
|
||||
"Benefit, Honor, and Glory.",
|
||||
["Chongqing", "Sector-12", "New Tokyo", "Aevum", "Ishima"],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
// Criminal Organizations/Gangs
|
||||
"Speakers for the Dead": new FactionInfo(
|
||||
"It is better to reign in Hell than to serve in Heaven.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"The Dark Army": new FactionInfo(
|
||||
"The World doesn't care about right or wrong. It only cares about power.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
"The Syndicate": new FactionInfo("Honor holds you back.", [], true, true, true, true),
|
||||
|
||||
Silhouette: new FactionInfo(
|
||||
"Corporations have filled the void of power left behind by the collapse of Western government. The issue is " +
|
||||
"they've become so big that you don't know who they're working for. And if you're employed at one of these " +
|
||||
"corporations, you don't even know who you're working for.<br>" +
|
||||
"<br>" +
|
||||
"That's terror. Terror, fear, and corruption. All born into the system, all propagated by the system.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
Tetrads: new FactionInfo("Following the mandate of Heaven and carrying out the way.", [], false, false, true, true),
|
||||
|
||||
"Slum Snakes": new FactionInfo("Slum Snakes rule!", [], false, false, true, true),
|
||||
|
||||
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
|
||||
Netburners: new FactionInfo("~~//*>H4CK||3T 8URN3R5**>?>\\~~", [], true, true, false, false),
|
||||
|
||||
"Tian Di Hui": new FactionInfo("Obey Heaven and work righteously.", [], true, true, false, true),
|
||||
|
||||
CyberSec: new FactionInfo(
|
||||
"The Internet is the first thing that was built that we don't fully understand, the largest " +
|
||||
"experiment in anarchy that we have ever had. And as the world becomes increasingly dominated by it, " +
|
||||
"society approaches the brink of total chaos. We serve only to protect society, to protect humanity, to " +
|
||||
"protect the world from imminent collapse.",
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// Special Factions
|
||||
Bladeburners: new FactionInfo(
|
||||
"It's too bad they won't live. But then again, who does?<br><br>Note that for this faction, reputation can " +
|
||||
"only be gained through Bladeburner actions. Completing Bladeburner contracts/operations will increase your " +
|
||||
"reputation.",
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
};
|
433
src/Faction/FactionInfo.tsx
Normal file
433
src/Faction/FactionInfo.tsx
Normal file
@ -0,0 +1,433 @@
|
||||
import React from "react";
|
||||
import { IMap } from "../types";
|
||||
|
||||
/**
|
||||
* Contains the "information" property for all the Factions, which is just a description of each faction
|
||||
*/
|
||||
export class FactionInfo {
|
||||
/**
|
||||
* The multiplier to apply to augmentation base purchase price.
|
||||
*/
|
||||
augmentationPriceMult: number;
|
||||
|
||||
/**
|
||||
* The multiplier to apply to augmentation reputation base requirement.
|
||||
*/
|
||||
augmentationRepRequirementMult: number;
|
||||
|
||||
/**
|
||||
* The names of all other factions considered to be enemies to this faction.
|
||||
*/
|
||||
enemies: string[];
|
||||
|
||||
/**
|
||||
* The descriptive text to show on the faction's page.
|
||||
*/
|
||||
infoText: JSX.Element;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports field work to earn reputation.
|
||||
*/
|
||||
offerFieldWork: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports hacking missions to earn reputation.
|
||||
*/
|
||||
offerHackingMission: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports hacking work to earn reputation.
|
||||
*/
|
||||
offerHackingWork: boolean;
|
||||
|
||||
/**
|
||||
* A flag indicating if the faction supports security work to earn reputation.
|
||||
*/
|
||||
offerSecurityWork: boolean;
|
||||
|
||||
constructor(
|
||||
infoText: JSX.Element,
|
||||
enemies: string[],
|
||||
offerHackingMission: boolean,
|
||||
offerHackingWork: boolean,
|
||||
offerFieldWork: boolean,
|
||||
offerSecurityWork: boolean,
|
||||
) {
|
||||
this.infoText = infoText;
|
||||
this.enemies = enemies;
|
||||
this.offerHackingMission = offerHackingMission;
|
||||
this.offerHackingWork = offerHackingWork;
|
||||
this.offerFieldWork = offerFieldWork;
|
||||
this.offerSecurityWork = offerSecurityWork;
|
||||
|
||||
// These are always all 1 for now.
|
||||
this.augmentationPriceMult = 1;
|
||||
this.augmentationRepRequirementMult = 1;
|
||||
}
|
||||
|
||||
offersWork(): boolean {
|
||||
return this.offerFieldWork || this.offerHackingMission || this.offerHackingWork || this.offerSecurityWork;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of all factions and associated info to them.
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const FactionInfos: IMap<FactionInfo> = {
|
||||
// Endgame
|
||||
Illuminati: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
Humanity never changes. No matter how civilized society becomes, it will eventually fall back into chaos. And
|
||||
from this chaos, we are the invisible hand that guides them to order.{" "}
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
Daedalus: new FactionInfo(
|
||||
<>Yesterday we obeyed kings and bent our necks to emperors. Today we kneel only to truth.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
"The Covenant": new FactionInfo(
|
||||
(
|
||||
<>
|
||||
Surrender yourself. Give up your empty individuality to become part of something great, something eternal.
|
||||
Become a slave. Submit your mind, body, and soul. Only then can you set yourself free.
|
||||
<br />
|
||||
<br />
|
||||
Only then can you discover immortality.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
// Megacorporations, each forms its own faction
|
||||
ECorp: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
ECorp's mission is simple: to connect the world of today with the technology of tomorrow. With our wide range of
|
||||
Internet-related software and commercial hardware, ECorp makes the world's information universally accessible.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
MegaCorp: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
MegaCorp does what no other dares to do. We imagine. We create. We invent. We create what others have never even
|
||||
dreamed of. Our work fills the world's needs for food, water, power, and transporation on an unprecendented
|
||||
scale, in ways that no other company can.
|
||||
<br />
|
||||
<br />
|
||||
In our labs and factories and on the ground with customers, MegaCorp is ushering in a new era for the world.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Bachman & Associates": new FactionInfo(
|
||||
(
|
||||
<>
|
||||
Where Law and Business meet - thats where we are.
|
||||
<br />
|
||||
<br />
|
||||
Legal Insight - Business Instinct - Innovative Experience.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Blade Industries": new FactionInfo(<>Augmentation is Salvation.</>, [], true, true, true, true),
|
||||
|
||||
NWO: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
Humans don't truly desire freedom. They want to be observed, understood, and judged. They want to be given
|
||||
purpose and direction in life. That is why they created God. And that is why they created civilization - not
|
||||
because of willingness, but because of a need to be incorporated into higher orders of structure and meaning.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Clarke Incorporated": new FactionInfo(<>The Power of the Genome - Unlocked.</>, [], true, true, true, true),
|
||||
|
||||
"OmniTek Incorporated": new FactionInfo(
|
||||
<>Simply put, our mission is to design and build robots that make a difference.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Four Sigma": new FactionInfo(
|
||||
(
|
||||
<>
|
||||
The scientific method is the best way to approach investing. Big strategies backed up with big data. Driven by
|
||||
deep learning and innovative ideas. And improved by iteration. That's Four Sigma.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"KuaiGong International": new FactionInfo(<>Dream big. Work hard. Make history.</>, [], true, true, true, true),
|
||||
|
||||
// Other Corporations
|
||||
"Fulcrum Secret Technologies": new FactionInfo(
|
||||
(
|
||||
<>
|
||||
The human organism has an innate desire to worship. That is why they created gods. If there were no gods, it
|
||||
would be necessary to create them. And now we can.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
|
||||
// Hacker groups
|
||||
BitRunners: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
Our entire lives are controlled by bits. All of our actions, our thoughts, our personal information. It's all
|
||||
transformed into bits, stored in bits, communicated through bits. It’s impossible for any person to move, to
|
||||
live, to operate at any level without the use of bits. And when a person moves, lives, and operates, they leave
|
||||
behind their bits, mere traces of seemingly meaningless fragments of information. But these bits can be
|
||||
reconstructed. Transformed. Used.
|
||||
<br />
|
||||
<br />
|
||||
Those who run the bits, run the world.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
"The Black Hand": new FactionInfo(
|
||||
(
|
||||
<>
|
||||
The world, so afraid of strong government, now has no government. Only power - Digital power. Financial power.
|
||||
Technological power. And those at the top rule with an invisible hand. They built a society where the rich get
|
||||
richer, and everyone else suffers.
|
||||
<br />
|
||||
<br />
|
||||
So much pain. So many lives. Their darkness must end.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
// prettier-ignore
|
||||
NiteSec: new FactionInfo(<>
|
||||
{" __..__ "}<br />
|
||||
{" _.nITESECNIt. "}<br />
|
||||
{" .-'NITESECNITESEc. "}<br />
|
||||
{" .' NITESECNITESECn "}<br />
|
||||
{" / NITESECNITESEC; "}<br />
|
||||
{" : :NITESECNITESEC; "}<br />
|
||||
{" ; $ NITESECNITESECN "}<br />
|
||||
{" : _, ,N'ITESECNITESEC "}<br />
|
||||
{" : .+^^`, : `NITESECNIT "}<br />
|
||||
{" ) /), `-,-=,NITESECNI "}<br />
|
||||
{" / ^ ,-;|NITESECN; "}<br />
|
||||
{" / _.' '-';NITESECN "}<br />
|
||||
{" ( , ,-''`^NITE' "}<br />
|
||||
{" )` :`. .' "}<br />
|
||||
{" )-- ; `- / "}<br />
|
||||
{" ' _.-' : "}<br />
|
||||
{" ( _.-' . "}<br />
|
||||
{" ------. "}<br />
|
||||
{" . "}<br />
|
||||
{" _.nIt "}<br />
|
||||
{" _.nITESECNi "}<br />
|
||||
{" nITESECNIT^' "}<br />
|
||||
{" NITE^' ___ "}<br />
|
||||
{" / .gP''''Tp. "}<br />
|
||||
{" : d' . `b "}<br />
|
||||
{" ; d' o `b ; "}<br />
|
||||
{" / d; `b| "}<br />
|
||||
{" /, $; @ `: "}<br />
|
||||
{" /' $$ ; "}<br />
|
||||
{" .' $$b o | "}<br />
|
||||
{" .' d$$$; : "}<br />
|
||||
{" / .d$$$$; , ; "}<br />
|
||||
{" d .dNITESEC $ | "}<br />
|
||||
{" :bp.__.gNITESEC$$ :$ ; "}<br />
|
||||
{" NITESECNITESECNIT $$b : "}<br /></>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// City factions, essentially governments
|
||||
Aevum: new FactionInfo(
|
||||
<>The Silicon City.</>,
|
||||
["Chongqing", "New Tokyo", "Ishima", "Volhaven"],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
Chongqing: new FactionInfo(<>Serve the People.</>, ["Sector-12", "Aevum", "Volhaven"], true, true, true, true),
|
||||
Ishima: new FactionInfo(
|
||||
<>The East Asian Order of the Future.</>,
|
||||
["Sector-12", "Aevum", "Volhaven"],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
"New Tokyo": new FactionInfo(<>Asia's World City.</>, ["Sector-12", "Aevum", "Volhaven"], true, true, true, true),
|
||||
"Sector-12": new FactionInfo(
|
||||
<>The City of the Future.</>,
|
||||
["Chongqing", "New Tokyo", "Ishima", "Volhaven"],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
Volhaven: new FactionInfo(
|
||||
<>Benefit, Honor, and Glory.</>,
|
||||
["Chongqing", "Sector-12", "New Tokyo", "Aevum", "Ishima"],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
// Criminal Organizations/Gangs
|
||||
"Speakers for the Dead": new FactionInfo(
|
||||
<>It is better to reign in Hell than to serve in Heaven.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"The Dark Army": new FactionInfo(
|
||||
<>The World doesn't care about right or wrong. It only cares about power.</>,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
"The Syndicate": new FactionInfo(<>Honor holds you back.</>, [], true, true, true, true),
|
||||
|
||||
Silhouette: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
Corporations have filled the void of power left behind by the collapse of Western government. The issue is
|
||||
they've become so big that you don't know who they're working for. And if you're employed at one of these
|
||||
corporations, you don't even know who you're working for.
|
||||
<br />
|
||||
<br />
|
||||
That's terror. Terror, fear, and corruption. All born into the system, all propagated by the system.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
),
|
||||
|
||||
Tetrads: new FactionInfo(
|
||||
<>Following the mandate of Heaven and carrying out the way.</>,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
|
||||
"Slum Snakes": new FactionInfo(<>Slum Snakes rule!</>, [], false, false, true, true),
|
||||
|
||||
// Earlygame factions - factions the player will prestige with early on that don't belong in other categories.
|
||||
Netburners: new FactionInfo(<>{"~~//*>H4CK||3T 8URN3R5**>?>\\~~"}</>, [], true, true, false, false),
|
||||
|
||||
"Tian Di Hui": new FactionInfo(<>Obey Heaven and work righteously.</>, [], true, true, false, true),
|
||||
|
||||
CyberSec: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
The Internet is the first thing that was built that we don't fully understand, the largest experiment in anarchy
|
||||
that we have ever had. And as the world becomes increasingly dominated by it, society approaches the brink of
|
||||
total chaos. We serve only to protect society, to protect humanity, to protect the world from imminent collapse.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
|
||||
// Special Factions
|
||||
Bladeburners: new FactionInfo(
|
||||
(
|
||||
<>
|
||||
It's too bad they won't live. But then again, who does?
|
||||
<br />
|
||||
<br />
|
||||
Note that for this faction, reputation can only be gained through Bladeburner actions. Completing Bladeburner
|
||||
contracts/operations will increase your reputation.
|
||||
</>
|
||||
),
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
};
|
@ -14,6 +14,11 @@ import { Settings } from "../../Settings/Settings";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import { use } from "../../ui/Context";
|
||||
|
||||
import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TableBody from "@mui/material/TableBody";
|
||||
import Table from "@mui/material/Table";
|
||||
|
||||
type IProps = {
|
||||
faction: Faction;
|
||||
routeToMainPage: () => void;
|
||||
@ -104,8 +109,17 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
(!player.augmentations.some((a) => a.name === aug) && !player.queuedAugmentations.some((a) => a.name === aug)),
|
||||
);
|
||||
|
||||
const purchaseableAugmentation = (aug: string): React.ReactNode => {
|
||||
return <PurchaseableAugmentation augName={aug} faction={props.faction} key={aug} p={player} rerender={rerender} />;
|
||||
const purchaseableAugmentation = (aug: string, owned = false): React.ReactNode => {
|
||||
return (
|
||||
<PurchaseableAugmentation
|
||||
augName={aug}
|
||||
faction={props.faction}
|
||||
key={aug}
|
||||
p={player}
|
||||
rerender={rerender}
|
||||
owned={owned}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const augListElems = purchasable.map((aug) => purchaseableAugmentation(aug));
|
||||
@ -116,42 +130,33 @@ export function AugmentationsPage(props: IProps): React.ReactElement {
|
||||
ownedElem = (
|
||||
<>
|
||||
<br />
|
||||
<h2>Purchased Augmentations</h2>
|
||||
<p>This factions also offers these augmentations but you already own them.</p>
|
||||
{owned.map((aug) => purchaseableAugmentation(aug))}
|
||||
<Typography variant="h4">Purchased Augmentations</Typography>
|
||||
<Typography>This factions also offers these augmentations but you already own them.</Typography>
|
||||
{owned.map((aug) => purchaseableAugmentation(aug, true))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StdButton onClick={props.routeToMainPage} text={"Back"} />
|
||||
<h1>Faction Augmentations</h1>
|
||||
<p>
|
||||
<Button onClick={props.routeToMainPage}>Back</Button>
|
||||
<Typography variant="h4">Faction Augmentations</Typography>
|
||||
<Typography>
|
||||
These are all of the Augmentations that are available to purchase from {props.faction.name}. Augmentations are
|
||||
powerful upgrades that will enhance your abilities.
|
||||
</p>
|
||||
<StdButton onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)} text={"Sort by Cost"} />
|
||||
<StdButton
|
||||
onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}
|
||||
text={"Sort by Reputation"}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}
|
||||
text={"Sort by Default Order"}
|
||||
/>
|
||||
<br />
|
||||
{augListElems}
|
||||
{ownedElem}
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</Typography>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Cost)}>Sort by Cost</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Reputation)}>Sort by Reputation</Button>
|
||||
<Button onClick={() => switchSortOrder(PurchaseAugmentationsOrderSetting.Default)}>Sort by Default Order</Button>
|
||||
<br />
|
||||
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>{augListElems}</TableBody>
|
||||
</Table>
|
||||
|
||||
<Table size="small" padding="none">
|
||||
<TableBody>{ownedElem}</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import { CONSTANTS } from "../../Constants";
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { repFromDonation } from "../formulas/donation";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
@ -18,6 +19,11 @@ import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { MathComponent } from "mathjax-react";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Button from "@mui/material/Button";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
type IProps = {
|
||||
faction: Faction;
|
||||
disabled: boolean;
|
||||
@ -67,36 +73,45 @@ export function DonateOption(props: IProps): React.ReactElement {
|
||||
function Status(): React.ReactElement {
|
||||
if (donateAmt === null) return <></>;
|
||||
if (!canDonate()) {
|
||||
if (props.p.money.lt(donateAmt)) return <p>Insufficient funds</p>;
|
||||
return <p>Invalid donate amount entered!</p>;
|
||||
if (props.p.money.lt(donateAmt)) return <Typography>Insufficient funds</Typography>;
|
||||
return <Typography>Invalid donate amount entered!</Typography>;
|
||||
}
|
||||
return <p>This donation will result in {Reputation(repFromDonation(donateAmt, props.p))} reputation gain</p>;
|
||||
return (
|
||||
<Typography>
|
||||
This donation will result in {Reputation(repFromDonation(donateAmt, props.p))} reputation gain
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"faction-work-div"}>
|
||||
<div className={"faction-work-div-wrapper"}>
|
||||
<input
|
||||
className="text-input"
|
||||
onChange={onChange}
|
||||
placeholder={"Donation amount"}
|
||||
style={inputStyleMarkup}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<StdButton onClick={donate} text={"Donate Money"} disabled={props.disabled || !canDonate()} />
|
||||
<Status />
|
||||
{props.disabled ? (
|
||||
<p>
|
||||
Unlocked at {props.favorToDonate} favor with {props.faction.name}
|
||||
</p>
|
||||
) : (
|
||||
<div className="text">
|
||||
<Paper sx={{ my: 1, p: 1, width: "100%" }}>
|
||||
<Status />
|
||||
{props.disabled ? (
|
||||
<Typography>
|
||||
Unlock donations at {Favor(props.favorToDonate)} favor with {props.faction.name}
|
||||
</Typography>
|
||||
) : (
|
||||
<>
|
||||
<TextField
|
||||
variant="standard"
|
||||
onChange={onChange}
|
||||
placeholder={"Donation amount"}
|
||||
disabled={props.disabled}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<Button onClick={donate} disabled={props.disabled || !canDonate()}>
|
||||
donate
|
||||
</Button>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Typography>
|
||||
<MathComponent
|
||||
tex={String.raw`reputation = \frac{\text{donation amount} \times \text{reputation multiplier}}{10^{${digits}}}`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ import { createPopup } from "../../ui/React/createPopup";
|
||||
import { use } from "../../ui/Context";
|
||||
import { CreateGangPopup } from "./CreateGangPopup";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
type IProps = {
|
||||
faction: Faction;
|
||||
};
|
||||
@ -145,8 +148,11 @@ export function FactionRoot(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="faction-container">
|
||||
<h1>{faction.name}</h1>
|
||||
<>
|
||||
<Button onClick={() => router.toFactions()}>Back</Button>
|
||||
<Typography variant="h4" color="primary">
|
||||
{faction.name}
|
||||
</Typography>
|
||||
<Info faction={faction} factionInfo={factionInfo} />
|
||||
{canAccessGang && <Option buttonText={"Manage Gang"} infoText={gangInfo} onClick={() => manageGang(faction)} />}
|
||||
{!isPlayersGang && factionInfo.offerHackingMission && (
|
||||
@ -186,7 +192,7 @@ export function FactionRoot(props: IProps): React.ReactElement {
|
||||
onClick={sleevePurchases}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -13,85 +13,79 @@ import { Reputation } from "../../ui/React/Reputation";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
import { MathComponent } from "mathjax-react";
|
||||
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
type IProps = {
|
||||
faction: Faction;
|
||||
factionInfo: FactionInfo;
|
||||
};
|
||||
|
||||
type IInnerHTMLMarkup = {
|
||||
__html: string;
|
||||
};
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
noformat: {
|
||||
whiteSpace: "pre-wrap",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const blockStyleMarkup = {
|
||||
display: "block",
|
||||
};
|
||||
export function Info(props: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
|
||||
const infoStyleMarkup = {
|
||||
display: "block",
|
||||
width: "70%",
|
||||
};
|
||||
const favorGain = props.faction.getFavorGain()[0];
|
||||
return (
|
||||
<>
|
||||
<Typography classes={{ root: classes.noformat }}>{props.factionInfo.infoText}</Typography>
|
||||
<Typography>-------------------------</Typography>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography>
|
||||
You will have {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}`}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Typography>Reputation: {Reputation(props.faction.playerReputation)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
export class Info extends React.Component<IProps, any> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
<Typography>-------------------------</Typography>
|
||||
|
||||
this.getFavorGainContent = this.getFavorGainContent.bind(this);
|
||||
this.getReputationContent = this.getReputationContent.bind(this);
|
||||
}
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<>
|
||||
<Typography>
|
||||
Faction favor increases the rate at which you earn reputation for this faction by 1% per favor. Faction
|
||||
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}}`} />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Typography>Faction Favor: {Favor(props.faction.favor)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
getFavorGainContent(): JSX.Element {
|
||||
const favorGain = this.props.faction.getFavorGain()[0];
|
||||
return (
|
||||
<>
|
||||
You will have {Favor(this.props.faction.favor + favorGain)} faction favor after installing an Augmentation.
|
||||
<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}`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
getReputationContent(): JSX.Element {
|
||||
return <>Reputation: {Reputation(this.props.faction.playerReputation)}</>;
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const favorTooltip = (
|
||||
<>
|
||||
Faction favor increases the rate at which you earn reputation for this faction by 1% per favor. Faction 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.
|
||||
<MathComponent tex={String.raw`\large{r = reputation}`} />
|
||||
<MathComponent tex={String.raw`\large{\Delta r = \Delta r \times \frac{100+favor}{100}}`} />
|
||||
</>
|
||||
);
|
||||
|
||||
const infoText: IInnerHTMLMarkup = {
|
||||
__html: this.props.factionInfo.infoText,
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<pre>
|
||||
<i className={"text"} dangerouslySetInnerHTML={infoText}></i>
|
||||
</pre>
|
||||
<p style={blockStyleMarkup}>-------------------------</p>
|
||||
<AutoupdatingParagraph
|
||||
intervalTime={5e3}
|
||||
getContent={this.getReputationContent}
|
||||
getTooltip={this.getFavorGainContent}
|
||||
/>
|
||||
<p style={blockStyleMarkup}>-------------------------</p>
|
||||
<ParagraphWithTooltip content={<>Faction Favor: {Favor(this.props.faction.favor)}</>} tooltip={favorTooltip} />
|
||||
<p style={blockStyleMarkup}>-------------------------</p>
|
||||
<p style={infoStyleMarkup}>
|
||||
Perform work/carry out assignments for your faction to help further its cause! By doing so you will earn
|
||||
reputation for your faction. You will also gain reputation passively over time, although at a very slow rate.
|
||||
Earning reputation will allow you to purchase Augmentations through this faction, which are powerful upgrades
|
||||
that enhance your abilities.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<Typography>-------------------------</Typography>
|
||||
<Typography>
|
||||
Perform work/carry out assignments for your faction to help further its cause! By doing so you will earn
|
||||
reputation for your faction. You will also gain reputation passively over time, although at a very slow rate.
|
||||
Earning reputation will allow you to purchase Augmentations through this faction, which are powerful upgrades
|
||||
that enhance your abilities.
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -7,21 +7,24 @@ import * as React from "react";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
type IProps = {
|
||||
buttonText: string;
|
||||
infoText: string;
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
export class Option extends React.Component<IProps, any> {
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<div className={"faction-work-div"}>
|
||||
<div className={"faction-work-div-wrapper"}>
|
||||
<StdButton onClick={this.props.onClick} text={this.props.buttonText} />
|
||||
<p>{this.props.infoText}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export function Option(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<Box>
|
||||
<Paper sx={{ my: 1, p: 1, width: "100%" }}>
|
||||
<Button onClick={props.onClick}>{props.buttonText}</Button>
|
||||
<Typography>{props.infoText}</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -19,13 +19,60 @@ import { IMap } from "../../types";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import { Augmentation as AugFormat } from "../../ui/React/Augmentation";
|
||||
import Button from "@mui/material/Button";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import { TableCell } from "../../ui/React/Table";
|
||||
import TableRow from "@mui/material/TableRow";
|
||||
|
||||
type IProps = {
|
||||
interface IReqProps {
|
||||
augName: string;
|
||||
p: IPlayer;
|
||||
hasReq: boolean;
|
||||
rep: number;
|
||||
hasRep: boolean;
|
||||
cost: number;
|
||||
hasCost: boolean;
|
||||
}
|
||||
|
||||
function Requirements(props: IReqProps): React.ReactElement {
|
||||
const aug = Augmentations[props.augName];
|
||||
if (!props.hasReq) {
|
||||
return (
|
||||
<TableCell key={1} colSpan={2}>
|
||||
<Typography color="error">
|
||||
Requires{" "}
|
||||
{aug.prereqs.map((aug, i) => (
|
||||
<AugFormat key={i} name={aug} />
|
||||
))}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
);
|
||||
}
|
||||
|
||||
let color = !props.hasRep || !props.hasCost ? "error" : "primary";
|
||||
return (
|
||||
<React.Fragment key="f">
|
||||
<TableCell key={1}>
|
||||
<Typography color={color}>
|
||||
<Money money={props.cost} player={props.p} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell key={2}>
|
||||
<Typography color={color}>Requires {Reputation(props.rep)} faction reputation</Typography>
|
||||
</TableCell>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
augName: string;
|
||||
faction: Faction;
|
||||
p: IPlayer;
|
||||
rerender: () => void;
|
||||
};
|
||||
owned?: boolean;
|
||||
}
|
||||
|
||||
export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
||||
const aug = Augmentations[props.augName];
|
||||
@ -39,22 +86,6 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
||||
return aug.baseRepRequirement * props.faction.getInfo().augmentationRepRequirementMult;
|
||||
}
|
||||
|
||||
function handleClick(): void {
|
||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||
const popupId = "purchase-augmentation-popup";
|
||||
createPopup(popupId, PurchaseAugmentationPopup, {
|
||||
aug: aug,
|
||||
faction: props.faction,
|
||||
player: props.p,
|
||||
rerender: props.rerender,
|
||||
popupId: popupId,
|
||||
});
|
||||
} else {
|
||||
purchaseAugmentation(aug, props.faction);
|
||||
props.rerender();
|
||||
}
|
||||
}
|
||||
|
||||
// Whether the player has the prerequisite Augmentations
|
||||
function hasPrereqs(): boolean {
|
||||
return hasAugmentationPrereqs(aug);
|
||||
@ -94,39 +125,12 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
||||
|
||||
const moneyCost = getMoneyCost();
|
||||
const repCost = getRepCost();
|
||||
const hasReq = hasPrereqs();
|
||||
const hasRep = hasReputation();
|
||||
const hasCost = aug.baseCost !== 0 && props.p.money.gt(aug.baseCost * props.faction.getInfo().augmentationPriceMult);
|
||||
|
||||
// Determine UI properties
|
||||
let disabled = false;
|
||||
let status: JSX.Element = <></>;
|
||||
let color = "";
|
||||
if (!hasPrereqs()) {
|
||||
disabled = true;
|
||||
status = <>LOCKED (Requires {aug.prereqs.map((aug) => AugFormat(aug))} as prerequisite)</>;
|
||||
color = "red";
|
||||
} else if (aug.name !== AugmentationNames.NeuroFluxGovernor && (aug.owned || owned())) {
|
||||
disabled = true;
|
||||
} else if (hasReputation()) {
|
||||
status = (
|
||||
<>
|
||||
UNLOCKED (at {Reputation(repCost)} faction reputation) - <Money money={moneyCost} player={props.p} />
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
disabled = true;
|
||||
status = (
|
||||
<>
|
||||
LOCKED (Requires {Reputation(repCost)} faction reputation - <Money money={moneyCost} player={props.p} />)
|
||||
</>
|
||||
);
|
||||
color = "red";
|
||||
}
|
||||
|
||||
const txtStyle: IMap<string> = {
|
||||
display: "inline-block",
|
||||
};
|
||||
if (color !== "") {
|
||||
txtStyle.color = color;
|
||||
}
|
||||
const color: "error" | "primary" = !hasReq || !hasRep || !hasCost ? "error" : "primary";
|
||||
|
||||
// Determine button txt
|
||||
let btnTxt = aug.name;
|
||||
@ -135,16 +139,16 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
let tooltip = <></>;
|
||||
if (typeof aug.info === "string")
|
||||
if (typeof aug.info === "string") {
|
||||
tooltip = (
|
||||
<>
|
||||
<span dangerouslySetInnerHTML={{ __html: aug.info }} />
|
||||
<span>{aug.info}</span>
|
||||
<br />
|
||||
<br />
|
||||
{aug.stats}
|
||||
</>
|
||||
);
|
||||
else
|
||||
} else
|
||||
tooltip = (
|
||||
<>
|
||||
{aug.info}
|
||||
@ -154,25 +158,60 @@ export function PurchaseableAugmentation(props: IProps): React.ReactElement {
|
||||
</>
|
||||
);
|
||||
|
||||
function handleClick(): void {
|
||||
if (color === "error") return;
|
||||
if (!Settings.SuppressBuyAugmentationConfirmation) {
|
||||
const popupId = "purchase-augmentation-popup";
|
||||
createPopup(popupId, PurchaseAugmentationPopup, {
|
||||
aug: aug,
|
||||
faction: props.faction,
|
||||
player: props.p,
|
||||
rerender: props.rerender,
|
||||
popupId: popupId,
|
||||
});
|
||||
} else {
|
||||
purchaseAugmentation(aug, props.faction);
|
||||
props.rerender();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={aug.name}>
|
||||
<span
|
||||
style={{
|
||||
margin: "4px",
|
||||
padding: "4px",
|
||||
}}
|
||||
>
|
||||
<StdButton
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
}}
|
||||
text={btnTxt}
|
||||
tooltip={tooltip}
|
||||
<TableRow>
|
||||
{!props.owned && (
|
||||
<TableCell key={0}>
|
||||
<Button onClick={handleClick} color={color}>
|
||||
Buy
|
||||
</Button>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell key={1}>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={<Typography>{tooltip}</Typography>}
|
||||
placement="top"
|
||||
disableFocusListener
|
||||
disableTouchListener
|
||||
enterNextDelay={1000}
|
||||
enterDelay={500}
|
||||
leaveDelay={0}
|
||||
leaveTouchDelay={0}
|
||||
>
|
||||
<Typography>{btnTxt}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</TableCell>
|
||||
{!props.owned && (
|
||||
<Requirements
|
||||
key={2}
|
||||
augName={props.augName}
|
||||
p={props.p}
|
||||
cost={moneyCost}
|
||||
rep={repCost}
|
||||
hasReq={hasReq}
|
||||
hasRep={hasRep}
|
||||
hasCost={hasCost}
|
||||
/>
|
||||
<p style={txtStyle}>{status}</p>
|
||||
</span>
|
||||
</li>
|
||||
)}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
@ -218,28 +218,41 @@ export class GangMember {
|
||||
return points.hack > 0 || points.str > 0 || points.def > 0 || points.dex > 0 || points.agi > 0 || points.cha > 0;
|
||||
}
|
||||
|
||||
getAscensionResults(): IMults {
|
||||
getCurrentAscensionMults(): IMults {
|
||||
return {
|
||||
hack: this.calculateAscensionMult(this.hack_asc_points),
|
||||
str: this.calculateAscensionMult(this.str_asc_points),
|
||||
def: this.calculateAscensionMult(this.def_asc_points),
|
||||
dex: this.calculateAscensionMult(this.dex_asc_points),
|
||||
agi: this.calculateAscensionMult(this.agi_asc_points),
|
||||
cha: this.calculateAscensionMult(this.cha_asc_points)
|
||||
}
|
||||
}
|
||||
|
||||
getAscensionMultsAfterAscend(): IMults {
|
||||
const points = this.getGainedAscensionPoints();
|
||||
return {
|
||||
hack:
|
||||
this.calculateAscensionMult(this.hack_asc_points + points.hack) /
|
||||
this.calculateAscensionMult(this.hack_asc_points),
|
||||
str:
|
||||
this.calculateAscensionMult(this.str_asc_points + points.str) /
|
||||
this.calculateAscensionMult(this.str_asc_points),
|
||||
def:
|
||||
this.calculateAscensionMult(this.def_asc_points + points.def) /
|
||||
this.calculateAscensionMult(this.def_asc_points),
|
||||
dex:
|
||||
this.calculateAscensionMult(this.dex_asc_points + points.dex) /
|
||||
this.calculateAscensionMult(this.dex_asc_points),
|
||||
agi:
|
||||
this.calculateAscensionMult(this.agi_asc_points + points.agi) /
|
||||
this.calculateAscensionMult(this.agi_asc_points),
|
||||
cha:
|
||||
this.calculateAscensionMult(this.cha_asc_points + points.cha) /
|
||||
this.calculateAscensionMult(this.cha_asc_points),
|
||||
};
|
||||
hack: this.calculateAscensionMult(this.hack_asc_points + points.hack),
|
||||
str: this.calculateAscensionMult(this.str_asc_points + points.str),
|
||||
def: this.calculateAscensionMult(this.def_asc_points + points.def),
|
||||
dex: this.calculateAscensionMult(this.dex_asc_points + points.dex),
|
||||
agi: this.calculateAscensionMult(this.agi_asc_points + points.agi),
|
||||
cha: this.calculateAscensionMult(this.cha_asc_points + points.cha),
|
||||
}
|
||||
}
|
||||
|
||||
getAscensionResults(): IMults {
|
||||
const postAscend = this.getAscensionMultsAfterAscend();
|
||||
const preAscend = this.getCurrentAscensionMults();
|
||||
|
||||
return {
|
||||
hack: postAscend.hack / preAscend.hack,
|
||||
str: postAscend.str / preAscend.str,
|
||||
def: postAscend.def / preAscend.def,
|
||||
dex: postAscend.dex / preAscend.dex,
|
||||
agi: postAscend.agi / preAscend.agi,
|
||||
cha: postAscend.cha / preAscend.cha,
|
||||
}
|
||||
}
|
||||
|
||||
ascend(): IAscensionResult {
|
||||
|
@ -57,7 +57,9 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
|
||||
const ascendBenefits = props.member.getAscensionResults();
|
||||
// const ascendBenefits = props.member.getAscensionResults();
|
||||
const preAscend = props.member.getCurrentAscensionMults();
|
||||
const postAscend = props.member.getAscensionMultsAfterAscend();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -72,17 +74,17 @@ export function AscensionPopup(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
In return, they will gain the following permanent boost to stat multipliers:
|
||||
<br />
|
||||
Hacking: x{numeralWrapper.format(ascendBenefits.hack, "0.000")}
|
||||
Hacking: x{numeralWrapper.format(preAscend.hack, "0.000")} => x{numeralWrapper.format(postAscend.hack, "0.000")}
|
||||
<br />
|
||||
Strength: x{numeralWrapper.format(ascendBenefits.str, "0.000")}
|
||||
Strength: x{numeralWrapper.format(preAscend.str, "0.000")} => x{numeralWrapper.format(postAscend.str, "0.000")}
|
||||
<br />
|
||||
Defense: x{numeralWrapper.format(ascendBenefits.def, "0.000")}
|
||||
Defense: x{numeralWrapper.format(preAscend.def, "0.000")} => x{numeralWrapper.format(postAscend.def, "0.000")}
|
||||
<br />
|
||||
Dexterity: x{numeralWrapper.format(ascendBenefits.dex, "0.000")}
|
||||
Dexterity: x{numeralWrapper.format(preAscend.dex, "0.000")} => x{numeralWrapper.format(postAscend.dex, "0.000")}
|
||||
<br />
|
||||
Agility: x{numeralWrapper.format(ascendBenefits.agi, "0.000")}
|
||||
Agility: x{numeralWrapper.format(preAscend.agi, "0.000")} => x{numeralWrapper.format(postAscend.agi, "0.000")}
|
||||
<br />
|
||||
Charisma: x{numeralWrapper.format(ascendBenefits.cha, "0.000")}
|
||||
Charisma: x{numeralWrapper.format(preAscend.cha, "0.000")} => x{numeralWrapper.format(postAscend.cha, "0.000")}
|
||||
<br />
|
||||
</pre>
|
||||
<button className="std-button" onClick={confirm}>
|
||||
|
@ -41,6 +41,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
|
||||
createPopup(popupId, CreateCorporationPopup, {
|
||||
player: player,
|
||||
popupId: popupId,
|
||||
router: router,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { makeRuntimeRejectMsg } from "./NetscriptEvaluator";
|
||||
import { ScriptUrl } from "./Script/ScriptUrl";
|
||||
|
||||
// Makes a blob that contains the code of a given script.
|
||||
export function makeScriptBlob(code) {
|
||||
function makeScriptBlob(code) {
|
||||
return new Blob([code], { type: "text/javascript" });
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ function shouldCompile(script, scripts) {
|
||||
* the script parameter.
|
||||
*/
|
||||
// BUG: apparently seen is never consulted. Oops.
|
||||
export function _getScriptUrls(script, scripts, seen) {
|
||||
function _getScriptUrls(script, scripts, seen) {
|
||||
// Inspired by: https://stackoverflow.com/a/43834063/91401
|
||||
/** @type {ScriptUrl[]} */
|
||||
const urlStack = [];
|
||||
|
@ -70,16 +70,20 @@ export function CovenantPurchasesRoot(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<div>
|
||||
<PopupCloseButton popup={PopupId} text={"Close"} />
|
||||
<p>
|
||||
Would you like to purchase an additional Duplicate Sleeve from The Covenant for{" "}
|
||||
<Money money={purchaseCost()} player={props.p} />?
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
These Duplicate Sleeves are permanent (they persist through BitNodes). You can purchase a total of{" "}
|
||||
{MaxSleevesFromCovenant} from The Covenant.
|
||||
</p>
|
||||
<StdButton disabled={purchaseDisabled} onClick={purchaseOnClick} text={"Purchase"} />
|
||||
{props.p.sleevesFromCovenant < MaxSleevesFromCovenant && (
|
||||
<>
|
||||
<p>
|
||||
Would you like to purchase an additional Duplicate Sleeve from The Covenant for{" "}
|
||||
<Money money={purchaseCost()} player={props.p} />?
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
These Duplicate Sleeves are permanent (they persist through BitNodes). You can purchase a total of{" "}
|
||||
{MaxSleevesFromCovenant} from The Covenant.
|
||||
</p>
|
||||
<StdButton disabled={purchaseDisabled} onClick={purchaseOnClick} text={"Purchase"} />
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<p>
|
||||
|
@ -6,6 +6,7 @@ import { removePopup } from "../../../ui/React/createPopup";
|
||||
import { Money } from "../../../ui/React/Money";
|
||||
import { WorldMap } from "../../../ui/React/WorldMap";
|
||||
import { CityName } from "../../../Locations/data/CityNames";
|
||||
import { Settings } from "../../../Settings/Settings";
|
||||
import { dialogBoxCreate } from "../../../../utils/DialogBox";
|
||||
|
||||
interface IProps {
|
||||
@ -34,7 +35,15 @@ export function TravelPopup(props: IProps): React.ReactElement {
|
||||
study. Traveling to a different city costs <Money money={CONSTANTS.TravelCost} player={props.player} />. It will
|
||||
also set your current sleeve task to idle.
|
||||
</p>
|
||||
<WorldMap currentCity={props.sleeve.city} onTravel={(city: CityName) => travel(city)} />
|
||||
{Settings.DisableASCIIArt ? (
|
||||
Object.values(CityName).map((city: CityName) => (
|
||||
<button key={city} className="std-button" onClick={() => travel(city)}>
|
||||
{city}
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<WorldMap currentCity={props.sleeve.city} onTravel={(city: CityName) => travel(city)} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -10,10 +10,6 @@ import { SourceFiles } from "./SourceFile/SourceFiles";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
|
||||
let redPillFlag = false;
|
||||
function hackWorldDaemon(router, flume = false, quick = false) {
|
||||
router.toBitVerse(flume, quick);
|
||||
redPillFlag = true;
|
||||
}
|
||||
|
||||
function giveSourceFile(bitNodeNumber) {
|
||||
var sourceFileKey = "SourceFile" + bitNodeNumber.toString();
|
||||
@ -86,4 +82,4 @@ export function enterBitNode(router, flume, destroyedBitNode, newBitNode) {
|
||||
prestigeSourceFile(flume);
|
||||
}
|
||||
|
||||
export { redPillFlag, hackWorldDaemon };
|
||||
export { redPillFlag };
|
||||
|
8
src/SaveObject.d.ts
vendored
8
src/SaveObject.d.ts
vendored
@ -1,2 +1,6 @@
|
||||
export declare const saveObject: any;
|
||||
export declare function openImportFileHandler(evt: any): void;
|
||||
export declare const saveObject: {
|
||||
getSaveString: () => string;
|
||||
saveGame: () => void;
|
||||
exportGame: () => void;
|
||||
};
|
||||
export declare function loadGame(s: string): boolean;
|
||||
|
@ -22,6 +22,7 @@ import * as ExportBonus from "./ExportBonus";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
|
||||
import { save } from "./db";
|
||||
|
||||
import Decimal from "decimal.js";
|
||||
|
||||
@ -84,33 +85,12 @@ BitburnerSaveObject.prototype.getSaveString = function () {
|
||||
return saveString;
|
||||
};
|
||||
|
||||
BitburnerSaveObject.prototype.saveGame = function (db) {
|
||||
var saveString = this.getSaveString();
|
||||
BitburnerSaveObject.prototype.saveGame = function () {
|
||||
const saveString = this.getSaveString();
|
||||
|
||||
// We'll save to both localstorage and indexedDb
|
||||
var objectStore = db.transaction(["savestring"], "readwrite").objectStore("savestring");
|
||||
var request = objectStore.put(saveString, "save");
|
||||
|
||||
request.onerror = function (e) {
|
||||
console.error("Error saving game to IndexedDB: " + e);
|
||||
};
|
||||
|
||||
try {
|
||||
window.localStorage.setItem("bitburnerSave", saveString);
|
||||
} catch (e) {
|
||||
if (e.code == 22) {
|
||||
createStatusText("Save failed for localStorage! Check console(F12)");
|
||||
console.error(
|
||||
"Failed to save game to localStorage because the size of the save file " +
|
||||
"is too large. However, the game will still be saved to IndexedDb if your browser " +
|
||||
"supports it. If you would like to save to localStorage as well, then " +
|
||||
"consider killing several of your scripts to " +
|
||||
"fix this, or increasing the size of your browsers localStorage",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
createStatusText("Game saved!");
|
||||
save(saveString)
|
||||
.then(() => createStatusText("Game saved!"))
|
||||
.catch((err) => console.error(err));
|
||||
};
|
||||
|
||||
// Makes necessary changes to the loaded/imported data to ensure
|
||||
@ -155,16 +135,10 @@ function evaluateVersionCompatibility(ver) {
|
||||
}
|
||||
|
||||
function loadGame(saveString) {
|
||||
if (saveString === "" || saveString == null || saveString === undefined) {
|
||||
if (!window.localStorage.getItem("bitburnerSave")) {
|
||||
return false;
|
||||
}
|
||||
saveString = decodeURIComponent(escape(atob(window.localStorage.getItem("bitburnerSave"))));
|
||||
} else {
|
||||
saveString = decodeURIComponent(escape(atob(saveString)));
|
||||
}
|
||||
if (!saveString) return false;
|
||||
saveString = decodeURIComponent(escape(atob(saveString)));
|
||||
|
||||
var saveObj = JSON.parse(saveString, Reviver);
|
||||
const saveObj = JSON.parse(saveString, Reviver);
|
||||
|
||||
loadPlayer(saveObj.PlayerSave);
|
||||
loadAllServers(saveObj.AllServersSave);
|
||||
@ -224,19 +198,12 @@ function loadGame(saveString) {
|
||||
} else {
|
||||
Settings.init();
|
||||
}
|
||||
if (saveObj.hasOwnProperty("FconfSettingsSave")) {
|
||||
try {
|
||||
loadFconf(saveObj.FconfSettingsSave);
|
||||
} catch (e) {
|
||||
console.error("ERROR: Failed to parse .fconf Settings.");
|
||||
}
|
||||
}
|
||||
if (saveObj.hasOwnProperty("LastExportBonus")) {
|
||||
try {
|
||||
ExportBonus.setLastExportBonus(JSON.parse(saveObj.LastExportBonus));
|
||||
} catch (err) {
|
||||
ExportBonus.setLastExportBonus(new Date().getTime());
|
||||
console.error("ERROR: Failed to parse .fconf Settings " + err);
|
||||
console.error("ERROR: Failed to parse last export bonus Settings " + err);
|
||||
}
|
||||
}
|
||||
if (saveObj.hasOwnProperty("VersionSave")) {
|
||||
@ -267,173 +234,6 @@ function loadGame(saveString) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function loadImportedGame(saveObj, saveString) {
|
||||
var tempSaveObj = null;
|
||||
var tempPlayer = null;
|
||||
|
||||
// Check to see if the imported save file can be parsed. If any
|
||||
// errors are caught it will fail
|
||||
try {
|
||||
var decodedSaveString = decodeURIComponent(escape(atob(saveString)));
|
||||
tempSaveObj = JSON.parse(decodedSaveString, Reviver);
|
||||
|
||||
tempPlayer = JSON.parse(tempSaveObj.PlayerSave, Reviver);
|
||||
|
||||
// Parse Decimal.js objects
|
||||
tempPlayer.money = new Decimal(tempPlayer.money);
|
||||
|
||||
JSON.parse(tempSaveObj.AllServersSave, Reviver);
|
||||
JSON.parse(tempSaveObj.CompaniesSave, Reviver);
|
||||
JSON.parse(tempSaveObj.FactionsSave, Reviver);
|
||||
JSON.parse(tempSaveObj.SpecialServerIpsSave, Reviver);
|
||||
if (tempSaveObj.hasOwnProperty("AliasesSave")) {
|
||||
try {
|
||||
JSON.parse(tempSaveObj.AliasesSave, Reviver);
|
||||
} catch (e) {
|
||||
console.error(`Parsing Aliases save failed: ${e}`);
|
||||
}
|
||||
}
|
||||
if (tempSaveObj.hasOwnProperty("GlobalAliases")) {
|
||||
try {
|
||||
JSON.parse(tempSaveObj.AliasesSave, Reviver);
|
||||
} catch (e) {
|
||||
console.error(`Parsing Global Aliases save failed: ${e}`);
|
||||
}
|
||||
}
|
||||
if (tempSaveObj.hasOwnProperty("MessagesSave")) {
|
||||
try {
|
||||
JSON.parse(tempSaveObj.MessagesSave, Reviver);
|
||||
} catch (e) {
|
||||
console.error(`Parsing Messages save failed: ${e}`);
|
||||
initMessages();
|
||||
}
|
||||
} else {
|
||||
initMessages();
|
||||
}
|
||||
if (saveObj.hasOwnProperty("StockMarketSave")) {
|
||||
try {
|
||||
JSON.parse(tempSaveObj.StockMarketSave, Reviver);
|
||||
} catch (e) {
|
||||
console.error(`Parsing StockMarket save failed: ${e}`);
|
||||
}
|
||||
}
|
||||
if (saveObj.hasOwnProperty("LastExportBonus")) {
|
||||
try {
|
||||
ExportBonus.setLastExportBonus(JSON.parse(saveObj.LastExportBonus));
|
||||
} catch (err) {
|
||||
ExportBonus.setLastExportBonus(new Date().getTime());
|
||||
console.error("ERROR: Failed to parse .fconf Settings " + err);
|
||||
}
|
||||
}
|
||||
if (tempSaveObj.hasOwnProperty("VersionSave")) {
|
||||
try {
|
||||
var ver = JSON.parse(tempSaveObj.VersionSave, Reviver);
|
||||
evaluateVersionCompatibility(ver);
|
||||
} catch (e) {
|
||||
console.error("Parsing Version save failed: " + e);
|
||||
}
|
||||
}
|
||||
if (tempPlayer.inGang() && tempSaveObj.hasOwnProperty("AllGangsSave")) {
|
||||
try {
|
||||
loadAllGangs(tempSaveObj.AllGangsSave);
|
||||
} catch (e) {
|
||||
console.error(`Failed to parse AllGangsSave: {e}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dialogBoxCreate("Error importing game: " + e.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since the save file is valid, load everything for real
|
||||
saveString = decodeURIComponent(escape(atob(saveString)));
|
||||
saveObj = JSON.parse(saveString, Reviver);
|
||||
|
||||
loadPlayer(saveObj.PlayerSave);
|
||||
loadAllServers(saveObj.AllServersSave);
|
||||
loadCompanies(saveObj.CompaniesSave);
|
||||
loadFactions(saveObj.FactionsSave);
|
||||
loadSpecialServerIps(saveObj.SpecialServerIpsSave);
|
||||
|
||||
if (saveObj.hasOwnProperty("AliasesSave")) {
|
||||
try {
|
||||
loadAliases(saveObj.AliasesSave);
|
||||
} catch (e) {
|
||||
loadAliases("");
|
||||
}
|
||||
} else {
|
||||
loadAliases("");
|
||||
}
|
||||
if (saveObj.hasOwnProperty("GlobalAliasesSave")) {
|
||||
try {
|
||||
loadGlobalAliases(saveObj.GlobalAliasesSave);
|
||||
} catch (e) {
|
||||
loadGlobalAliases("");
|
||||
}
|
||||
} else {
|
||||
loadGlobalAliases("");
|
||||
}
|
||||
if (saveObj.hasOwnProperty("MessagesSave")) {
|
||||
try {
|
||||
loadMessages(saveObj.MessagesSave);
|
||||
} catch (e) {
|
||||
initMessages();
|
||||
}
|
||||
} else {
|
||||
initMessages();
|
||||
}
|
||||
if (saveObj.hasOwnProperty("StockMarketSave")) {
|
||||
try {
|
||||
loadStockMarket(saveObj.StockMarketSave);
|
||||
} catch (e) {
|
||||
loadStockMarket("");
|
||||
}
|
||||
} else {
|
||||
loadStockMarket("");
|
||||
}
|
||||
if (saveObj.hasOwnProperty("SettingsSave")) {
|
||||
try {
|
||||
Settings.load(saveObj.SettingsSave);
|
||||
} catch (e) {
|
||||
Settings.init();
|
||||
}
|
||||
} else {
|
||||
Settings.init();
|
||||
}
|
||||
if (saveObj.hasOwnProperty("FconfSettingsSave")) {
|
||||
try {
|
||||
loadFconf(saveObj.FconfSettingsSave);
|
||||
} catch (e) {
|
||||
console.error("ERROR: Failed to load .fconf settings when importing");
|
||||
}
|
||||
}
|
||||
if (saveObj.hasOwnProperty("VersionSave")) {
|
||||
try {
|
||||
var ver = JSON.parse(saveObj.VersionSave, Reviver);
|
||||
evaluateVersionCompatibility(ver);
|
||||
|
||||
if (ver != CONSTANTS.Version) {
|
||||
createNewUpdateText();
|
||||
}
|
||||
} catch (e) {
|
||||
createNewUpdateText();
|
||||
}
|
||||
} else {
|
||||
createNewUpdateText();
|
||||
}
|
||||
if (Player.inGang() && saveObj.hasOwnProperty("AllGangsSave")) {
|
||||
try {
|
||||
loadAllGangs(saveObj.AllGangsSave);
|
||||
} catch (e) {
|
||||
console.error("ERROR: Failed to parse AllGangsSave: " + e);
|
||||
}
|
||||
}
|
||||
saveObject.saveGame(Engine.indexedDb);
|
||||
location.reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
BitburnerSaveObject.prototype.exportGame = function () {
|
||||
const saveString = this.getSaveString();
|
||||
|
||||
@ -460,31 +260,6 @@ BitburnerSaveObject.prototype.exportGame = function () {
|
||||
}
|
||||
};
|
||||
|
||||
BitburnerSaveObject.prototype.importGame = function () {
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
||||
var fileSelector = clearEventListeners("import-game-file-selector");
|
||||
fileSelector.addEventListener("change", openImportFileHandler, false);
|
||||
$("#import-game-file-selector").click();
|
||||
} else {
|
||||
dialogBoxCreate("ERR: Your browser does not support HTML5 File API. Cannot import.");
|
||||
}
|
||||
};
|
||||
|
||||
BitburnerSaveObject.prototype.deleteGame = function (db) {
|
||||
// Delete from local storage
|
||||
if (window.localStorage.getItem("bitburnerSave")) {
|
||||
window.localStorage.removeItem("bitburnerSave");
|
||||
}
|
||||
|
||||
// Delete from indexedDB
|
||||
var request = db.transaction(["savestring"], "readwrite").objectStore("savestring").delete("save");
|
||||
request.onsuccess = function () {};
|
||||
request.onerror = function (e) {
|
||||
console.error(`Failed to delete save from indexedDb: ${e}`);
|
||||
};
|
||||
createStatusText("Game deleted!");
|
||||
};
|
||||
|
||||
function createNewUpdateText() {
|
||||
dialogBoxCreate(
|
||||
"New update!<br>" +
|
||||
@ -514,19 +289,4 @@ BitburnerSaveObject.fromJSON = function (value) {
|
||||
|
||||
Reviver.constructors.BitburnerSaveObject = BitburnerSaveObject;
|
||||
|
||||
function openImportFileHandler(evt) {
|
||||
var file = evt.target.files[0];
|
||||
if (!file) {
|
||||
dialogBoxCreate("Invalid file selected");
|
||||
return;
|
||||
}
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
var contents = e.target.result;
|
||||
loadImportedGame(saveObject, contents);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
export { saveObject, loadGame, openImportFileHandler };
|
||||
export { saveObject, loadGame };
|
||||
|
@ -3,7 +3,7 @@ export type Position = {
|
||||
column: number;
|
||||
};
|
||||
|
||||
export class PositionTracker {
|
||||
class PositionTracker {
|
||||
positions: Map<string, Position>;
|
||||
|
||||
constructor() {
|
||||
|
@ -5,6 +5,14 @@ import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } fro
|
||||
* Represents the default settings the player could customize.
|
||||
*/
|
||||
interface IDefaultSettings {
|
||||
/**
|
||||
* How many servers per page
|
||||
*/
|
||||
ActiveScriptsServerPageSize: number;
|
||||
/**
|
||||
* How many scripts per page
|
||||
*/
|
||||
ActiveScriptsScriptPageSize: number;
|
||||
/**
|
||||
* How often the game should autosave the player's progress, in seconds.
|
||||
*/
|
||||
@ -101,6 +109,8 @@ interface ISettings extends IDefaultSettings {
|
||||
}
|
||||
|
||||
const defaultSettings: IDefaultSettings = {
|
||||
ActiveScriptsServerPageSize: 10,
|
||||
ActiveScriptsScriptPageSize: 10,
|
||||
AutosaveInterval: 60,
|
||||
CodeInstructionRunTime: 50,
|
||||
DisableASCIIArt: false,
|
||||
@ -123,6 +133,8 @@ const defaultSettings: IDefaultSettings = {
|
||||
*/
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
ActiveScriptsServerPageSize: defaultSettings.ActiveScriptsServerPageSize,
|
||||
ActiveScriptsScriptPageSize: defaultSettings.ActiveScriptsScriptPageSize,
|
||||
AutosaveInterval: defaultSettings.AutosaveInterval,
|
||||
CodeInstructionRunTime: 25,
|
||||
DisableASCIIArt: defaultSettings.DisableASCIIArt,
|
||||
|
@ -51,7 +51,6 @@ import { Settings } from "../../Settings/Settings";
|
||||
import { redPillFlag } from "../../RedPill";
|
||||
|
||||
import { inMission } from "../../Missions";
|
||||
import { cinematicTextFlag } from "../../CinematicText";
|
||||
import { KEY } from "../../../utils/helpers/keyCodes";
|
||||
import { FconfSettings } from "../../Fconf/FconfSettings";
|
||||
|
||||
@ -268,7 +267,7 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
||||
// Alt-o - Options
|
||||
function handleShortcuts(this: Document, event: KeyboardEvent): any {
|
||||
if (Settings.DisableHotkeys) return;
|
||||
if (props.player.isWorking || redPillFlag || inMission || cinematicTextFlag) return;
|
||||
if (props.player.isWorking || redPillFlag || inMission) return;
|
||||
if (event.keyCode == KEY.T && event.altKey) {
|
||||
event.preventDefault();
|
||||
clickTerminal();
|
||||
|
@ -4,43 +4,42 @@ import { IMap } from "../types";
|
||||
export const TerminalHelpText: string[] = [
|
||||
"Type 'help name' to learn more about the command ",
|
||||
"",
|
||||
'alias [-g] [name="value"] Create or display Terminal aliases',
|
||||
"analyze Get information about the current machine ",
|
||||
"backdoor Install a backdoor on the current machine ",
|
||||
"buy [-l/program] Purchase a program through the Dark Web",
|
||||
"cat [file] Display a .msg, .lit, or .txt file",
|
||||
"cd [dir] Change to a new directory",
|
||||
"check [script] [args...] Print a script's logs to Terminal",
|
||||
"clear Clear all text on the terminal ",
|
||||
"cls See 'clear' command ",
|
||||
"connect [ip/hostname] Connects to a remote server",
|
||||
"download [script/text file] Downloads scripts or text files to your computer",
|
||||
"expr [math expression] Evaluate a mathematical expression",
|
||||
"free Check the machine's memory (RAM) usage",
|
||||
"hack Hack the current machine",
|
||||
"help [command] Display this help text, or the help text for a command",
|
||||
"home Connect to home computer",
|
||||
"hostname Displays the hostname of the machine",
|
||||
"ifconfig Displays the IP address of the machine",
|
||||
"kill [script/pid] [args...] Stops the specified script on the current server ",
|
||||
"killall Stops all running scripts on the current machine",
|
||||
"ls [dir] [| grep pattern] Displays all files on the machine",
|
||||
"lscpu Displays the number of CPU cores on the machine",
|
||||
"mem [script] [-t] [n] Displays the amount of RAM required to run the script",
|
||||
"mv [src] [dest] Move/rename a text or script file",
|
||||
"nano [file] Text editor - Open up and edit a script or text file",
|
||||
"ps Display all scripts that are currently running",
|
||||
"rm [file] Delete a file from the server",
|
||||
"run [name] [-t] [n] [args...] Execute a program or script",
|
||||
"scan Prints all immediately-available network connections",
|
||||
"scan-analyze [d] [-a] Prints info for all servers up to <i>d</i> nodes away",
|
||||
"scp [file] [server] Copies a file to a destination server",
|
||||
"sudov Shows whether you have root access on this computer",
|
||||
"tail [script] [args...] Displays dynamic logs for the specified script",
|
||||
"theme [preset] | bg txt hlgt Change the color scheme of the UI",
|
||||
"top Displays all running scripts and their RAM usage",
|
||||
"unalias [alias name] Deletes the specified alias",
|
||||
"wget [url] [target file] Retrieves code/text from a web server",
|
||||
'alias [-g] [name="value"] Create or display Terminal aliases',
|
||||
"analyze Get information about the current machine ",
|
||||
"backdoor Install a backdoor on the current machine ",
|
||||
"buy [-l/program] Purchase a program through the Dark Web",
|
||||
"cat [file] Display a .msg, .lit, or .txt file",
|
||||
"cd [dir] Change to a new directory",
|
||||
"check [script] [args...] Print a script's logs to Terminal",
|
||||
"clear Clear all text on the terminal ",
|
||||
"cls See 'clear' command ",
|
||||
"connect [ip/hostname] Connects to a remote server",
|
||||
"download [script/text file] Downloads scripts or text files to your computer",
|
||||
"expr [math expression] Evaluate a mathematical expression",
|
||||
"free Check the machine's memory (RAM) usage",
|
||||
"hack Hack the current machine",
|
||||
"help [command] Display this help text, or the help text for a command",
|
||||
"home Connect to home computer",
|
||||
"hostname Displays the hostname of the machine",
|
||||
"ifconfig Displays the IP address of the machine",
|
||||
"kill [script/pid] [args...] Stops the specified script on the current server ",
|
||||
"killall Stops all running scripts on the current machine",
|
||||
"ls [dir] [| grep pattern] Displays all files on the machine",
|
||||
"lscpu Displays the number of CPU cores on the machine",
|
||||
"mem [script] [-t] [n] Displays the amount of RAM required to run the script",
|
||||
"mv [src] [dest] Move/rename a text or script file",
|
||||
"nano [file] Text editor - Open up and edit a script or text file",
|
||||
"ps Display all scripts that are currently running",
|
||||
"rm [file] Delete a file from the server",
|
||||
"run [name] [-t n] [--tail] [args...] Execute a program or script",
|
||||
"scan Prints all immediately-available network connections",
|
||||
"scan-analyze [d] [-a] Prints info for all servers up to <i>d</i> nodes away",
|
||||
"scp [file] [server] Copies a file to a destination server",
|
||||
"sudov Shows whether you have root access on this computer",
|
||||
"tail [script] [args...] Displays dynamic logs for the specified script",
|
||||
"top Displays all running scripts and their RAM usage",
|
||||
"unalias [alias name] Deletes the specified alias",
|
||||
"wget [url] [target file] Retrieves code/text from a web server",
|
||||
];
|
||||
|
||||
export const HelpTexts: IMap<string[]> = {
|
||||
@ -268,7 +267,7 @@ export const HelpTexts: IMap<string[]> = {
|
||||
lscpu: ["lscpu", " ", "Prints the number of CPU Cores the current server has"],
|
||||
|
||||
mem: [
|
||||
"mem [script name] [-t] [num threads]",
|
||||
"mem [script name] [-t num_threads]",
|
||||
" ",
|
||||
"Displays the amount of RAM needed to run the specified script with a single thread. The command can also be used to print ",
|
||||
"the amount of RAM needed to run a script with multiple threads using the '-t' flag. If the '-t' flag is specified, then ",
|
||||
@ -374,24 +373,6 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" ",
|
||||
"tail foo.script 10 50000",
|
||||
],
|
||||
theme: [
|
||||
"theme [preset] | [#background #text #highlight]",
|
||||
" ",
|
||||
"Change the color of the game's user interface",
|
||||
" ",
|
||||
"This command can be called with a preset theme. Currently, the supported presets are 'default', 'muted', and 'solarized'. ",
|
||||
"However, you can also specify your own color scheme using hex values. To do so, you must specify three hex color values ",
|
||||
"for the background color, the text color, and the highlight color. These hex values must be preceded by a pound sign (#) and ",
|
||||
"must be either 3 or 6 digits. Example:",
|
||||
" ",
|
||||
"theme #ffffff #385 #235012",
|
||||
" ",
|
||||
"A color picker such as ",
|
||||
"<a href='https://www.google.com/search?q=color+picker&oq=color+picker&aqs=chrome.0.0l6.951j0j1&sourceid=chrome&ie=UTF-8' target='_blank'>Google's</a> ",
|
||||
"can be used to get your desired hex color values",
|
||||
" ",
|
||||
"Themes are not saved, so when the game is closed and then re-opened or reloaded then it will revert back to the default theme.",
|
||||
],
|
||||
top: [
|
||||
"top",
|
||||
" ",
|
||||
|
@ -61,7 +61,6 @@ import { scananalyze } from "./commands/scananalyze";
|
||||
import { scp } from "./commands/scp";
|
||||
import { sudov } from "./commands/sudov";
|
||||
import { tail } from "./commands/tail";
|
||||
import { theme } from "./commands/theme";
|
||||
import { top } from "./commands/top";
|
||||
import { unalias } from "./commands/unalias";
|
||||
import { wget } from "./commands/wget";
|
||||
@ -345,14 +344,10 @@ export class Terminal implements ITerminal {
|
||||
case CodingContractResult.Failure:
|
||||
++contract.tries;
|
||||
if (contract.tries >= contract.getMaxNumTries()) {
|
||||
this.print("Contract <p style='color:red;display:inline'>FAILED</p> - Contract is now self-destructing");
|
||||
this.print("Contract FAILED - Contract is now self-destructing");
|
||||
serv.removeContract(contract);
|
||||
} else {
|
||||
this.print(
|
||||
`Contract <p style='color:red;display:inline'>FAILED</p> - ${
|
||||
contract.getMaxNumTries() - contract.tries
|
||||
} tries remaining`,
|
||||
);
|
||||
this.print(`Contract FAILED - ${contract.getMaxNumTries() - contract.tries} tries remaining`);
|
||||
}
|
||||
break;
|
||||
case CodingContractResult.Cancelled:
|
||||
@ -683,7 +678,6 @@ export class Terminal implements ITerminal {
|
||||
scp: scp,
|
||||
sudov: sudov,
|
||||
tail: tail,
|
||||
theme: theme,
|
||||
top: top,
|
||||
unalias: unalias,
|
||||
wget: wget,
|
||||
|
@ -1,60 +0,0 @@
|
||||
import { ITerminal } from "../ITerminal";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
import { FconfSettings } from "../../Fconf/FconfSettings";
|
||||
|
||||
export function theme(
|
||||
terminal: ITerminal,
|
||||
router: IRouter,
|
||||
player: IPlayer,
|
||||
server: BaseServer,
|
||||
args: (string | number)[],
|
||||
): void {
|
||||
if (args.length !== 1 && args.length !== 3) {
|
||||
terminal.error("Incorrect number of arguments.");
|
||||
terminal.error(
|
||||
"Usage: theme [default|muted|solarized] | #[background color hex] #[text color hex] #[highlight color hex]",
|
||||
);
|
||||
} else if (args.length === 1) {
|
||||
const themeName = args[0];
|
||||
if (themeName == "default") {
|
||||
document.body.style.setProperty("--my-highlight-color", "#ffffff");
|
||||
document.body.style.setProperty("--my-font-color", "#66ff33");
|
||||
document.body.style.setProperty("--my-background-color", "#000000");
|
||||
document.body.style.setProperty("--my-prompt-color", "#f92672");
|
||||
} else if (themeName == "muted") {
|
||||
document.body.style.setProperty("--my-highlight-color", "#ffffff");
|
||||
document.body.style.setProperty("--my-font-color", "#66ff33");
|
||||
document.body.style.setProperty("--my-background-color", "#252527");
|
||||
} else if (themeName == "solarized") {
|
||||
document.body.style.setProperty("--my-highlight-color", "#6c71c4");
|
||||
document.body.style.setProperty("--my-font-color", "#839496");
|
||||
document.body.style.setProperty("--my-background-color", "#002b36");
|
||||
} else {
|
||||
return terminal.error("Theme not found");
|
||||
}
|
||||
FconfSettings.THEME_HIGHLIGHT_COLOR = document.body.style.getPropertyValue("--my-highlight-color");
|
||||
FconfSettings.THEME_FONT_COLOR = document.body.style.getPropertyValue("--my-font-color");
|
||||
FconfSettings.THEME_BACKGROUND_COLOR = document.body.style.getPropertyValue("--my-background-color");
|
||||
FconfSettings.THEME_PROMPT_COLOR = document.body.style.getPropertyValue("--my-prompt-color");
|
||||
} else {
|
||||
const inputBackgroundHex = args[0] + "";
|
||||
const inputTextHex = args[1] + "";
|
||||
const inputHighlightHex = args[2] + "";
|
||||
if (
|
||||
/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(inputBackgroundHex) &&
|
||||
/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(inputTextHex) &&
|
||||
/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(inputHighlightHex)
|
||||
) {
|
||||
document.body.style.setProperty("--my-highlight-color", inputHighlightHex);
|
||||
document.body.style.setProperty("--my-font-color", inputTextHex);
|
||||
document.body.style.setProperty("--my-background-color", inputBackgroundHex);
|
||||
FconfSettings.THEME_HIGHLIGHT_COLOR = document.body.style.getPropertyValue("--my-highlight-color");
|
||||
FconfSettings.THEME_FONT_COLOR = document.body.style.getPropertyValue("--my-font-color");
|
||||
FconfSettings.THEME_BACKGROUND_COLOR = document.body.style.getPropertyValue("--my-background-color");
|
||||
} else {
|
||||
return terminal.error("Invalid Hex Input for theme");
|
||||
}
|
||||
}
|
||||
}
|
86
src/db.tsx
Normal file
86
src/db.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import { Engine } from "./engine";
|
||||
import { createStatusText } from "./ui/createStatusText";
|
||||
|
||||
function getDB(): Promise<IDBObjectStore> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!window.indexedDB) {
|
||||
reject("Indexed DB does not exists");
|
||||
}
|
||||
/**
|
||||
* DB is called bitburnerSave
|
||||
* Object store is called savestring
|
||||
* key for the Object store is called save
|
||||
* Version `1` is important
|
||||
*/
|
||||
const indexedDbRequest: IDBOpenDBRequest = window.indexedDB.open("bitburnerSave", 1);
|
||||
|
||||
// This is called when there's no db to begin with. It's important, don't remove it.
|
||||
indexedDbRequest.onupgradeneeded = function (this: IDBRequest<IDBDatabase>) {
|
||||
const db = this.result;
|
||||
db.createObjectStore("savestring");
|
||||
};
|
||||
|
||||
indexedDbRequest.onerror = function (this: IDBRequest<IDBDatabase>, ev: Event) {
|
||||
reject(`Failed to get IDB ${ev}`);
|
||||
};
|
||||
|
||||
indexedDbRequest.onsuccess = function (this: IDBRequest<IDBDatabase>, ev: Event) {
|
||||
const db = this.result;
|
||||
if (!db) {
|
||||
reject("database loadign result was undefined");
|
||||
return;
|
||||
}
|
||||
resolve(db.transaction(["savestring"], "readwrite").objectStore("savestring"));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
interface ILoadCallback {
|
||||
success: (s: string) => void;
|
||||
error?: () => void;
|
||||
}
|
||||
|
||||
export function load(): Promise<string> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
await getDB()
|
||||
.then((db) => {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const request: IDBRequest<string> = db.get("save");
|
||||
request.onerror = function (this: IDBRequest<string>, ev: Event) {
|
||||
reject("Error in Database request to get savestring: " + ev);
|
||||
};
|
||||
|
||||
request.onsuccess = function (this: IDBRequest<string>) {
|
||||
resolve(this.result);
|
||||
};
|
||||
}).then((saveString) => resolve(saveString));
|
||||
})
|
||||
.catch((r) => reject(r));
|
||||
});
|
||||
}
|
||||
|
||||
interface ISaveCallback {
|
||||
success: () => void;
|
||||
error?: () => void;
|
||||
}
|
||||
|
||||
export function save(saveString: string): Promise<void> {
|
||||
return getDB().then((db) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
// We'll save to both localstorage and indexedDb
|
||||
const request = db.put(saveString, "save");
|
||||
|
||||
request.onerror = function (e) {
|
||||
reject("Error saving game to IndexedDB: " + e);
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteGame(): Promise<void> {
|
||||
return getDB().then((db) => {
|
||||
db.delete("save");
|
||||
});
|
||||
}
|
2
src/engine.d.ts
vendored
2
src/engine.d.ts
vendored
@ -1,3 +1 @@
|
||||
export declare function load(cb: () => void): void;
|
||||
|
||||
export declare const Engine: IEngine;
|
||||
|
@ -44,14 +44,12 @@ import { Reputation } from "./ui/React/Reputation";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
|
||||
import "./Exploits/tampering";
|
||||
import "./Exploits/unclickable";
|
||||
import { startTampering } from "./Exploits/tampering";
|
||||
import { startUnclickable } from "./Exploits/unclickable";
|
||||
|
||||
import React from "react";
|
||||
|
||||
const Engine = {
|
||||
indexedDb: undefined,
|
||||
|
||||
// Time variables (milliseconds unix epoch time)
|
||||
_lastUpdate: new Date().getTime(),
|
||||
|
||||
@ -196,7 +194,7 @@ const Engine = {
|
||||
Engine.Counters.autoSaveCounter = Infinity;
|
||||
} else {
|
||||
Engine.Counters.autoSaveCounter = Settings.AutosaveInterval * 5;
|
||||
saveObject.saveGame(Engine.indexedDb);
|
||||
saveObject.saveGame();
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,6 +246,8 @@ const Engine = {
|
||||
},
|
||||
|
||||
load: function (saveString) {
|
||||
startTampering();
|
||||
startUnclickable();
|
||||
// Load game from save or create new game
|
||||
if (loadGame(saveString)) {
|
||||
initBitNodeMultipliers(Player);
|
||||
@ -420,48 +420,4 @@ const Engine = {
|
||||
},
|
||||
};
|
||||
|
||||
function load(cb) {
|
||||
if (!window.indexedDB) {
|
||||
return Engine.load(null); // Will try to load from localstorage
|
||||
}
|
||||
|
||||
/**
|
||||
* DB is called bitburnerSave
|
||||
* Object store is called savestring
|
||||
* key for the Object store is called save
|
||||
*/
|
||||
indexedDbRequest = window.indexedDB.open("bitburnerSave", 1);
|
||||
|
||||
indexedDbRequest.onerror = function (e) {
|
||||
console.error("Error opening indexedDB: ");
|
||||
console.error(e);
|
||||
Engine.load(null); // Try to load from localstorage
|
||||
cb();
|
||||
};
|
||||
|
||||
indexedDbRequest.onsuccess = function (e) {
|
||||
Engine.indexedDb = e.target.result;
|
||||
var transaction = Engine.indexedDb.transaction(["savestring"]);
|
||||
var objectStore = transaction.objectStore("savestring");
|
||||
var request = objectStore.get("save");
|
||||
request.onerror = function (e) {
|
||||
console.error("Error in Database request to get savestring: " + e);
|
||||
Engine.load(null); // Try to load from localstorage
|
||||
cb();
|
||||
};
|
||||
|
||||
request.onsuccess = function () {
|
||||
Engine.load(request.result);
|
||||
cb();
|
||||
};
|
||||
};
|
||||
|
||||
indexedDbRequest.onupgradeneeded = function (e) {
|
||||
const db = e.target.result;
|
||||
db.createObjectStore("savestring");
|
||||
};
|
||||
}
|
||||
|
||||
var indexedDbRequest;
|
||||
|
||||
export { Engine, load };
|
||||
export { Engine };
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import { TTheme as Theme } from "./ui/React/Theme";
|
||||
import { TTheme as Theme, colors } from "./ui/React/Theme";
|
||||
import { LoadingScreen } from "./ui/LoadingScreen";
|
||||
import "./engineStyle";
|
||||
|
||||
@ -11,3 +11,14 @@ ReactDOM.render(
|
||||
</Theme>,
|
||||
document.getElementById("mainmenu-container"),
|
||||
);
|
||||
|
||||
// setTimeout(() => {
|
||||
// colors.primary = "#fff";
|
||||
// refreshTheme();
|
||||
// ReactDOM.render(
|
||||
// <Theme>
|
||||
// <LoadingScreen />
|
||||
// </Theme>,
|
||||
// document.getElementById("mainmenu-container"),
|
||||
// );
|
||||
// }, 5000);
|
||||
|
@ -4,6 +4,7 @@
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { Money } from "../React/Money";
|
||||
import { MoneyRate } from "../React/MoneyRate";
|
||||
import { use } from "../Context";
|
||||
|
||||
@ -40,18 +41,13 @@ export function ScriptProduction(): React.ReactElement {
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography variant="body2">Total online production of Active scripts:</Typography>
|
||||
<Typography variant="body2">Total production:</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="left" classes={{ root: classes.cell }}>
|
||||
<Typography variant="body2">
|
||||
<MoneyRate money={player.scriptProdSinceLastAug} />
|
||||
<Money money={player.scriptProdSinceLastAug} />
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow style={{ width: "1px" }}>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
<Typography variant="body2">Total online production since last Aug installation:</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="left" classes={{ root: classes.cell }}>
|
||||
<Typography variant="body2">
|
||||
(<MoneyRate money={prodRateSinceLastAug} />)
|
||||
|
@ -4,6 +4,7 @@ import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
|
||||
import List from "@mui/material/List";
|
||||
import TablePagination from "@mui/material/TablePagination";
|
||||
import { TablePaginationActionsAll } from "../React/TablePaginationActionsAll";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
|
||||
interface IProps {
|
||||
workerScripts: WorkerScript[];
|
||||
@ -11,36 +12,30 @@ interface IProps {
|
||||
|
||||
export function ServerAccordionContent(props: IProps): React.ReactElement {
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(Settings.ActiveScriptsScriptPageSize);
|
||||
const handleChangePage = (event: unknown, newPage: number): void => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
Settings.ActiveScriptsScriptPageSize = parseInt(event.target.value, 10);
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
let safePage = page;
|
||||
while (safePage * rowsPerPage + 1 > props.workerScripts.length) {
|
||||
safePage--;
|
||||
}
|
||||
|
||||
if (safePage != page) setPage(safePage);
|
||||
|
||||
return (
|
||||
<>
|
||||
<List dense disablePadding>
|
||||
{props.workerScripts.slice(safePage * rowsPerPage, safePage * rowsPerPage + rowsPerPage).map((ws) => (
|
||||
{props.workerScripts.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((ws) => (
|
||||
<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />
|
||||
))}
|
||||
</List>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[10, 15, 20]}
|
||||
rowsPerPageOptions={[10, 15, 20, 100]}
|
||||
component="div"
|
||||
count={props.workerScripts.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={safePage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActionsAll}
|
||||
|
@ -13,6 +13,7 @@ import { WorkerScript } from "../../Netscript/WorkerScript";
|
||||
import { WorkerScriptStartStopEventEmitter } from "../../Netscript/WorkerScriptStartStopEventEmitter";
|
||||
import { getServer } from "../../Server/ServerHelpers";
|
||||
import { BaseServer } from "../../Server/BaseServer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { TablePaginationActionsAll } from "../React/TablePaginationActionsAll";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
|
||||
@ -33,7 +34,7 @@ type IProps = {
|
||||
export function ServerAccordions(props: IProps): React.ReactElement {
|
||||
const [filter, setFilter] = useState("");
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(Settings.ActiveScriptsServerPageSize);
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number): void => {
|
||||
@ -41,6 +42,7 @@ export function ServerAccordions(props: IProps): React.ReactElement {
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
Settings.ActiveScriptsServerPageSize = parseInt(event.target.value, 10);
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
};
|
||||
@ -74,13 +76,6 @@ export function ServerAccordions(props: IProps): React.ReactElement {
|
||||
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
|
||||
let safePage = page;
|
||||
while (safePage * rowsPerPage + 1 >= filtered.length) {
|
||||
safePage--;
|
||||
}
|
||||
|
||||
if (safePage != page) setPage(safePage);
|
||||
}
|
||||
|
||||
useEffect(() => WorkerScriptStartStopEventEmitter.subscribe(rerender));
|
||||
@ -108,7 +103,7 @@ export function ServerAccordions(props: IProps): React.ReactElement {
|
||||
})}
|
||||
</List>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[10, 15, 20]}
|
||||
rowsPerPageOptions={[10, 15, 20, 100]}
|
||||
component="div"
|
||||
count={filtered.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
|
@ -4,7 +4,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../IEngine";
|
||||
import { ITerminal } from "../Terminal/ITerminal";
|
||||
import { installAugmentations } from "../Augmentation/AugmentationHelpers";
|
||||
import { saveObject, openImportFileHandler } from "../SaveObject";
|
||||
import { saveObject } from "../SaveObject";
|
||||
import { onExport } from "../ExportBonus";
|
||||
import { LocationName } from "../Locations/data/LocationNames";
|
||||
import { Location } from "../Locations/Location";
|
||||
@ -282,7 +282,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
<Context.Router.Provider value={Router}>
|
||||
<Overview>
|
||||
{!ITutorial.isRunning ? (
|
||||
<CharacterOverview save={() => saveObject.saveGame(engine.indexedDb)} />
|
||||
<CharacterOverview save={() => saveObject.saveGame()} />
|
||||
) : (
|
||||
<InteractiveTutorialRoot />
|
||||
)}
|
||||
@ -300,7 +300,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
) : (
|
||||
<Box display="flex" flexDirection="row" width="100%">
|
||||
<SidebarRoot player={player} router={Router} page={page} />
|
||||
<Box className={classes.root} flexGrow={1} display="block" width="100%" px={1} height="100vh">
|
||||
<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 ? (
|
||||
@ -357,10 +357,8 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
) : page === Page.Options ? (
|
||||
<GameOptionsRoot
|
||||
player={player}
|
||||
save={() => saveObject.saveGame(engine.indexedDb)}
|
||||
delete={() => saveObject.deleteGame(engine.indexedDb)}
|
||||
save={() => saveObject.saveGame()}
|
||||
export={() => saveObject.exportGame()}
|
||||
import={openImportFileHandler}
|
||||
forceKill={() => {
|
||||
for (const hostname of Object.keys(AllServers)) {
|
||||
AllServers[hostname].runningScripts = [];
|
||||
|
@ -535,7 +535,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
||||
const content = contents[step];
|
||||
if (content === undefined) throw new Error("error in the tutorial");
|
||||
return (
|
||||
<Paper square sx={{ maxWidth: "35vh", p: 2 }}>
|
||||
<Paper square sx={{ maxWidth: "70vw", p: 2 }}>
|
||||
{content.content}
|
||||
{step !== iTutorialSteps.TutorialPageInfo && (
|
||||
<>
|
||||
|
@ -4,56 +4,13 @@ import Typography from "@mui/material/Typography";
|
||||
import Grid from "@mui/material/Grid";
|
||||
|
||||
import { Terminal } from "../Terminal";
|
||||
import { Engine } from "../engine";
|
||||
import { load } from "../db";
|
||||
import { Player } from "../Player";
|
||||
import { Engine } from "../engine";
|
||||
import { GameRoot } from "./GameRoot";
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
function load(cb: () => void): void {
|
||||
if (!window.indexedDB) {
|
||||
return Engine.load(""); // Will try to load from localstorage
|
||||
}
|
||||
|
||||
/**
|
||||
* DB is called bitburnerSave
|
||||
* Object store is called savestring
|
||||
* key for the Object store is called save
|
||||
*/
|
||||
// Version 1 is important
|
||||
const indexedDbRequest: IDBOpenDBRequest = window.indexedDB.open("bitburnerSave", 1);
|
||||
|
||||
indexedDbRequest.onerror = function (this: IDBRequest<IDBDatabase>, ev: Event) {
|
||||
console.error("Error opening indexedDB: ");
|
||||
console.error(ev);
|
||||
Engine.load(""); // Try to load from localstorage
|
||||
cb();
|
||||
};
|
||||
|
||||
indexedDbRequest.onsuccess = function (this: IDBRequest<IDBDatabase>) {
|
||||
Engine.indexedDb = this.result;
|
||||
const transaction = Engine.indexedDb.transaction(["savestring"]);
|
||||
const objectStore = transaction.objectStore("savestring");
|
||||
const request: IDBRequest<string> = objectStore.get("save");
|
||||
request.onerror = function (this: IDBRequest<string>, ev: Event) {
|
||||
console.error("Error in Database request to get savestring: " + ev);
|
||||
Engine.load(""); // Try to load from localstorage
|
||||
cb();
|
||||
};
|
||||
|
||||
request.onsuccess = function (this: IDBRequest<string>) {
|
||||
Engine.load(this.result);
|
||||
cb();
|
||||
};
|
||||
};
|
||||
|
||||
// This is called when there's no db to begin with. It's important.
|
||||
indexedDbRequest.onupgradeneeded = function (this: IDBRequest<IDBDatabase>) {
|
||||
const db = this.result;
|
||||
db.createObjectStore("savestring");
|
||||
};
|
||||
}
|
||||
|
||||
export function LoadingScreen(): React.ReactElement {
|
||||
const [show, setShow] = useState(false);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
@ -66,9 +23,19 @@ export function LoadingScreen(): React.ReactElement {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
load(() => {
|
||||
setLoaded(true);
|
||||
});
|
||||
async function doLoad() {
|
||||
await load()
|
||||
.then((saveString) => {
|
||||
Engine.load(saveString);
|
||||
setLoaded(true);
|
||||
})
|
||||
.catch((reason) => {
|
||||
console.error(reason);
|
||||
Engine.load("");
|
||||
setLoaded(true);
|
||||
});
|
||||
}
|
||||
doLoad();
|
||||
}, []);
|
||||
|
||||
if (loaded) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
|
||||
export function Augmentation(name: string): JSX.Element {
|
||||
export function Augmentation({ name }: { name: string }): JSX.Element {
|
||||
return (
|
||||
<span className={"samefont"} style={{ color: "white" }}>
|
||||
{name}
|
||||
|
@ -1,7 +1,9 @@
|
||||
// Root React Component for the Corporation UI
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Reputation } from "./Reputation";
|
||||
|
||||
@ -78,35 +80,37 @@ function Work(): React.ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
const useStyles = makeStyles({
|
||||
cellNone: {
|
||||
borderBottom: "none",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
},
|
||||
cell: {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
},
|
||||
hp: {
|
||||
color: colors.hp,
|
||||
},
|
||||
money: {
|
||||
color: colors.money,
|
||||
},
|
||||
hack: {
|
||||
color: colors.hack,
|
||||
},
|
||||
combat: {
|
||||
color: colors.combat,
|
||||
},
|
||||
cha: {
|
||||
color: colors.cha,
|
||||
},
|
||||
int: {
|
||||
color: colors.int,
|
||||
},
|
||||
});
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
cellNone: {
|
||||
borderBottom: "none",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
},
|
||||
cell: {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
},
|
||||
hp: {
|
||||
color: theme.colors.hp,
|
||||
},
|
||||
money: {
|
||||
color: theme.colors.money,
|
||||
},
|
||||
hack: {
|
||||
color: theme.colors.hack,
|
||||
},
|
||||
combat: {
|
||||
color: theme.colors.combat,
|
||||
},
|
||||
cha: {
|
||||
color: theme.colors.cha,
|
||||
},
|
||||
int: {
|
||||
color: theme.colors.int,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export function CharacterOverview({ save }: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
|
@ -28,6 +28,7 @@ import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { ConfirmationModal } from "./ConfirmationModal";
|
||||
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { save, deleteGame } from "../../db";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@ -42,9 +43,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
save: () => void;
|
||||
delete: () => void;
|
||||
export: () => void;
|
||||
import: (evt: any) => void;
|
||||
forceKill: () => void;
|
||||
softReset: () => void;
|
||||
}
|
||||
@ -153,20 +152,38 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
Settings.Locale = event.target.value as string;
|
||||
}
|
||||
|
||||
function importSave(): void {
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
||||
// var fileSelector = clearEventListeners("import-game-file-selector");
|
||||
// fileSelector.addEventListener("change", openImportFileHandler, false);
|
||||
const ii = importInput.current;
|
||||
if (ii === null) throw new Error("import input should not be null");
|
||||
ii.click();
|
||||
} else {
|
||||
dialogBoxCreate("ERR: Your browser does not support HTML5 File API. Cannot import.");
|
||||
}
|
||||
function startImport(): void {
|
||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return;
|
||||
const ii = importInput.current;
|
||||
if (ii === null) throw new Error("import input should not be null");
|
||||
ii.click();
|
||||
}
|
||||
|
||||
function onImport(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
props.import(event);
|
||||
const files = event.target.files;
|
||||
if (files === null) return;
|
||||
const file = files[0];
|
||||
if (!file) {
|
||||
dialogBoxCreate("Invalid file selected");
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (this: FileReader, e: ProgressEvent<FileReader>) {
|
||||
const target = e.target;
|
||||
if (target === null) {
|
||||
console.error("error importing file");
|
||||
return;
|
||||
}
|
||||
const result = target.result;
|
||||
if (typeof result !== "string" || result === null) {
|
||||
console.error("FileReader event was not type string");
|
||||
return;
|
||||
}
|
||||
const contents = result;
|
||||
save(contents).then(() => location.reload());
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -490,7 +507,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={<Typography>import</Typography>}>
|
||||
<Button onClick={importSave}>
|
||||
<Button onClick={startImport}>
|
||||
<UploadIcon color="primary" />
|
||||
Import
|
||||
<input ref={importInput} id="import-game-file-selector" type="file" hidden onChange={onImport} />
|
||||
@ -536,6 +553,9 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box>
|
||||
<Link href="https://github.com/danielyxie/bitburner/issues/new" target="_blank">
|
||||
<Typography>Report bug</Typography>
|
||||
</Link>
|
||||
<Link href="https://bitburner.readthedocs.io/en/latest/changelog.html" target="_blank">
|
||||
<Typography>Changelog</Typography>
|
||||
</Link>
|
||||
@ -554,8 +574,10 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
<FileDiagnosticModal open={diagnosticOpen} onClose={() => setDiagnosticOpen(false)} />
|
||||
<ConfirmationModal
|
||||
onConfirm={() => {
|
||||
props.delete();
|
||||
setDeleteOpen(false);
|
||||
deleteGame()
|
||||
.then(() => location.reload())
|
||||
.catch((r) => console.error(`Could not delete game: ${r}`));
|
||||
}}
|
||||
open={deleteGameOpen}
|
||||
onClose={() => setDeleteOpen(false)}
|
||||
|
@ -5,6 +5,7 @@ import Box from "@mui/material/Box";
|
||||
import Collapse from "@mui/material/Collapse";
|
||||
import Fab from "@mui/material/Fab";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||
import { use } from "../Context";
|
||||
import { Page } from "../Router";
|
||||
|
||||
@ -24,13 +25,19 @@ export function Overview({ children }: IProps): React.ReactElement {
|
||||
const router = use.Router();
|
||||
if (router.page() === Page.BitVerse || router.page() === Page.HackingMission || router.page() === Page.Loading)
|
||||
return <></>;
|
||||
let icon;
|
||||
if (open){
|
||||
icon = <VisibilityOffIcon color="primary" />;
|
||||
} else {
|
||||
icon = <VisibilityIcon color="primary" />;
|
||||
}
|
||||
return (
|
||||
<div style={{ position: "fixed", top: 0, right: 0, zIndex: 1500 }}>
|
||||
<Box display="flex" justifyContent="flex-end" flexDirection={"column"}>
|
||||
<Collapse in={open}>{children}</Collapse>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Fab classes={{ root: classes.nobackground }} onClick={() => setOpen((old) => !old)}>
|
||||
<VisibilityOffIcon color="primary" />
|
||||
{icon}
|
||||
</Fab>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -1,12 +1,31 @@
|
||||
import React from "react";
|
||||
import { createTheme, ThemeProvider, Theme, StyledEngineProvider } from "@mui/material/styles";
|
||||
|
||||
declare module "@mui/styles/defaultTheme" {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface DefaultTheme extends Theme {}
|
||||
declare module "@mui/material/styles" {
|
||||
interface Theme {
|
||||
colors: {
|
||||
hp: React.CSSProperties["color"];
|
||||
money: React.CSSProperties["color"];
|
||||
hack: React.CSSProperties["color"];
|
||||
combat: React.CSSProperties["color"];
|
||||
cha: React.CSSProperties["color"];
|
||||
int: React.CSSProperties["color"];
|
||||
rep: React.CSSProperties["color"];
|
||||
};
|
||||
}
|
||||
interface ThemeOptions {
|
||||
colors: {
|
||||
hp: React.CSSProperties["color"];
|
||||
money: React.CSSProperties["color"];
|
||||
hack: React.CSSProperties["color"];
|
||||
combat: React.CSSProperties["color"];
|
||||
cha: React.CSSProperties["color"];
|
||||
int: React.CSSProperties["color"];
|
||||
rep: React.CSSProperties["color"];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const colors = {
|
||||
export let colors = {
|
||||
primarylight: "#0f0",
|
||||
primary: "#0c0",
|
||||
primarydark: "#090",
|
||||
@ -38,227 +57,242 @@ export const colors = {
|
||||
combat: "#faffdf",
|
||||
cha: "#a671d1",
|
||||
int: "#6495ed",
|
||||
rep: "#faffdf",
|
||||
};
|
||||
|
||||
export const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
light: colors.primarylight,
|
||||
main: colors.primary,
|
||||
dark: colors.primarydark,
|
||||
let theme: Theme;
|
||||
|
||||
function refreshTheme() {
|
||||
theme = createTheme({
|
||||
colors: {
|
||||
hp: "#dd3434",
|
||||
money: "#ffd700",
|
||||
hack: "#adff2f",
|
||||
combat: "#faffdf",
|
||||
cha: "#a671d1",
|
||||
int: "#6495ed",
|
||||
rep: "#faffdf",
|
||||
},
|
||||
secondary: {
|
||||
light: colors.secondarylight,
|
||||
main: colors.secondary,
|
||||
dark: colors.secondarydark,
|
||||
palette: {
|
||||
primary: {
|
||||
light: colors.primarylight,
|
||||
main: colors.primary,
|
||||
dark: colors.primarydark,
|
||||
},
|
||||
secondary: {
|
||||
light: colors.secondarylight,
|
||||
main: colors.secondary,
|
||||
dark: colors.secondarydark,
|
||||
},
|
||||
error: {
|
||||
light: colors.errorlight,
|
||||
main: colors.error,
|
||||
dark: colors.errordark,
|
||||
},
|
||||
info: {
|
||||
light: colors.infolight,
|
||||
main: colors.info,
|
||||
dark: colors.infodark,
|
||||
},
|
||||
warning: {
|
||||
light: colors.warninglight,
|
||||
main: colors.warning,
|
||||
dark: colors.warningdark,
|
||||
},
|
||||
background: {
|
||||
default: colors.black,
|
||||
paper: colors.well,
|
||||
},
|
||||
},
|
||||
error: {
|
||||
light: colors.errorlight,
|
||||
main: colors.error,
|
||||
dark: colors.errordark,
|
||||
typography: {
|
||||
fontFamily: "monospace",
|
||||
button: {
|
||||
textTransform: "none",
|
||||
},
|
||||
},
|
||||
info: {
|
||||
light: colors.infolight,
|
||||
main: colors.info,
|
||||
dark: colors.infodark,
|
||||
},
|
||||
warning: {
|
||||
light: colors.warninglight,
|
||||
main: colors.warning,
|
||||
dark: colors.warningdark,
|
||||
},
|
||||
background: {
|
||||
default: colors.black,
|
||||
paper: colors.well,
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: "monospace",
|
||||
button: {
|
||||
textTransform: "none",
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiInputBase: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.well,
|
||||
color: colors.primary,
|
||||
components: {
|
||||
MuiInputBase: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.well,
|
||||
color: colors.primary,
|
||||
},
|
||||
input: {
|
||||
"&::placeholder": {
|
||||
userSelect: "none",
|
||||
color: colors.primarydark,
|
||||
},
|
||||
},
|
||||
},
|
||||
input: {
|
||||
"&::placeholder": {
|
||||
},
|
||||
|
||||
MuiInput: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.well,
|
||||
borderBottomColor: "#fff",
|
||||
},
|
||||
underline: {
|
||||
"&:hover": {
|
||||
borderBottomColor: colors.primarydark,
|
||||
},
|
||||
"&:before": {
|
||||
borderBottomColor: colors.primary,
|
||||
},
|
||||
"&:after": {
|
||||
borderBottomColor: colors.primarylight,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
MuiInputLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primarydark, // why is this switched?
|
||||
userSelect: "none",
|
||||
color: colors.primarydark,
|
||||
"&:before": {
|
||||
color: colors.primarylight,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: "#333",
|
||||
border: "1px solid " + colors.well,
|
||||
// color: colors.primary,
|
||||
"&:hover": {
|
||||
backgroundColor: colors.black,
|
||||
},
|
||||
|
||||
MuiInput: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.well,
|
||||
borderBottomColor: "#fff",
|
||||
},
|
||||
underline: {
|
||||
"&:hover": {
|
||||
borderBottomColor: colors.primarydark,
|
||||
},
|
||||
"&:before": {
|
||||
borderBottomColor: colors.primary,
|
||||
},
|
||||
"&:after": {
|
||||
borderBottomColor: colors.primarylight,
|
||||
borderRadius: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
MuiInputLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primarydark, // why is this switched?
|
||||
userSelect: "none",
|
||||
"&:before": {
|
||||
color: colors.primarylight,
|
||||
MuiSelect: {
|
||||
styleOverrides: {
|
||||
icon: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: "#333",
|
||||
border: "1px solid " + colors.well,
|
||||
// color: colors.primary,
|
||||
margin: "5px",
|
||||
padding: "3px 5px",
|
||||
"&:hover": {
|
||||
MuiMenu: {
|
||||
styleOverrides: {
|
||||
list: {
|
||||
backgroundColor: colors.well,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenuItem: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAccordionSummary: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: "#111",
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAccordionDetails: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.black,
|
||||
},
|
||||
|
||||
borderRadius: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiSelect: {
|
||||
styleOverrides: {
|
||||
icon: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenu: {
|
||||
styleOverrides: {
|
||||
list: {
|
||||
backgroundColor: colors.well,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenuItem: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAccordionSummary: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: "#111",
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiAccordionDetails: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.black,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTooltip: {
|
||||
styleOverrides: {
|
||||
tooltip: {
|
||||
fontSize: "1em",
|
||||
color: colors.primary,
|
||||
backgroundColor: colors.well,
|
||||
borderRadius: 0,
|
||||
border: "2px solid white",
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiSlider: {
|
||||
styleOverrides: {
|
||||
valueLabel: {
|
||||
color: colors.primary,
|
||||
backgroundColor: colors.well,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDrawer: {
|
||||
styleOverrides: {
|
||||
paper: {
|
||||
"&::-webkit-scrollbar": {
|
||||
// webkit
|
||||
display: "none",
|
||||
MuiIconButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primary,
|
||||
},
|
||||
scrollbarWidth: "none", // firefox
|
||||
backgroundColor: colors.black,
|
||||
},
|
||||
paperAnchorDockedLeft: {
|
||||
borderRight: "1px solid " + colors.welllight,
|
||||
},
|
||||
MuiTooltip: {
|
||||
styleOverrides: {
|
||||
tooltip: {
|
||||
fontSize: "1em",
|
||||
color: colors.primary,
|
||||
backgroundColor: colors.well,
|
||||
borderRadius: 0,
|
||||
border: "2px solid white",
|
||||
maxWidth: "100vh",
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiSlider: {
|
||||
styleOverrides: {
|
||||
valueLabel: {
|
||||
color: colors.primary,
|
||||
backgroundColor: colors.well,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDrawer: {
|
||||
styleOverrides: {
|
||||
paper: {
|
||||
"&::-webkit-scrollbar": {
|
||||
// webkit
|
||||
display: "none",
|
||||
},
|
||||
scrollbarWidth: "none", // firefox
|
||||
backgroundColor: colors.black,
|
||||
},
|
||||
paperAnchorDockedLeft: {
|
||||
borderRight: "1px solid " + colors.welllight,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDivider: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.welllight,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiFormControlLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiSwitch: {
|
||||
styleOverrides: {
|
||||
switchBase: {
|
||||
color: colors.primarydark,
|
||||
},
|
||||
track: {
|
||||
backgroundColor: colors.welllight,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 0,
|
||||
backgroundColor: colors.black,
|
||||
border: "1px solid " + colors.welllight,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTablePagination: {
|
||||
styleOverrides: {
|
||||
select: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiDivider: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundColor: colors.welllight,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiFormControlLabel: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiSwitch: {
|
||||
styleOverrides: {
|
||||
switchBase: {
|
||||
color: colors.primarydark,
|
||||
},
|
||||
track: {
|
||||
backgroundColor: colors.welllight,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 0,
|
||||
backgroundColor: colors.black,
|
||||
border: "1px solid " + colors.welllight,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiTablePagination: {
|
||||
styleOverrides: {
|
||||
select: {
|
||||
color: colors.primary,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
console.log("refreshed");
|
||||
}
|
||||
refreshTheme();
|
||||
|
||||
interface IProps {
|
||||
children: JSX.Element[] | JSX.Element;
|
||||
|
@ -32,11 +32,11 @@ export function WorkInProgressRoot(): React.ReactElement {
|
||||
const faction = Factions[player.currentWorkFactionName];
|
||||
if (player.workType == CONSTANTS.WorkTypeFaction) {
|
||||
function cancel(): void {
|
||||
router.toFaction();
|
||||
router.toFaction(faction);
|
||||
player.finishFactionWork(true);
|
||||
}
|
||||
function unfocus(): void {
|
||||
router.toFaction();
|
||||
router.toFaction(faction);
|
||||
player.stopFocusing();
|
||||
}
|
||||
return (
|
||||
|
@ -11,10 +11,6 @@
|
||||
* This formula ensures that the effects of the statistic that is being processed
|
||||
* has diminishing returns, but never loses its effectiveness as you continue
|
||||
* to raise it.
|
||||
*
|
||||
* There are two implementations of this component. One is simply a function that
|
||||
* can be called with the stat and the exponential/linear factors. The other is a
|
||||
* class where the exponential and linear factors are defined upon construction.
|
||||
*/
|
||||
export function calculateEffectWithFactors(n: number, expFac: number, linearFac: number): number {
|
||||
if (expFac <= 0 || expFac >= 1) {
|
||||
@ -26,20 +22,3 @@ export function calculateEffectWithFactors(n: number, expFac: number, linearFac:
|
||||
|
||||
return Math.pow(n, expFac) + n / linearFac;
|
||||
}
|
||||
|
||||
export class EffectWithFactors {
|
||||
// Exponential factor
|
||||
private expFac: number;
|
||||
|
||||
// Linear Factor
|
||||
private linearFac: number;
|
||||
|
||||
constructor(expFac: number, linearFac: number) {
|
||||
this.expFac = expFac;
|
||||
this.linearFac = linearFac;
|
||||
}
|
||||
|
||||
calculate(n: number): number {
|
||||
return calculateEffectWithFactors(n, this.expFac, this.linearFac);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
|
||||
const UnusedWebpackPlugin = require("unused-webpack-plugin");
|
||||
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
|
||||
const DeadCodePlugin = require("webpack-deadcode-plugin");
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const isDevServer = (env || {}).devServer === true;
|
||||
@ -130,6 +131,10 @@ module.exports = (env, argv) => {
|
||||
module: true,
|
||||
}),
|
||||
isDevelopment && new ReactRefreshWebpackPlugin(),
|
||||
new DeadCodePlugin({
|
||||
patterns: ["src/**/*.(js|jsx|css|ts|tsx)"],
|
||||
exclude: ["**/*.(stories|spec).(js|jsx)"],
|
||||
}),
|
||||
].filter(Boolean),
|
||||
target: "web",
|
||||
entry: entry,
|
||||
|
Loading…
Reference in New Issue
Block a user