mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-22 22:22:26 +01:00
convert blade to mui
This commit is contained in:
parent
498a204c88
commit
86678b6290
dist
index.htmlmain.bundle.jsmain.bundle.js.mapmain.cssmain.css.mapsrc
Bladeburner
Action.tsxBlackOperations.tsBlackOperations.tsxBladeburner.tsxGeneralActions.tsGeneralActions.tsxIAction.tsx
data
ui
ActionLevel.tsxAllPages.tsxAutolevel.tsxBlackOpElem.tsxBlackOpList.tsxBlackOpPage.tsxBladeburnerRoot.tsxConsole.tsxContractElem.tsxContractList.tsxContractPage.tsxGeneralActionElem.tsxGeneralActionList.tsxGeneralActionPage.tsxKillIcon.tsxOperationElem.tsxOperationList.tsxOperationPage.tsxSkillElem.tsxSkillList.tsxSkillPage.tsxStartButton.tsxStats.tsxStealthIcon.tsxSuccessChance.tsxTeamSizeButton.tsxTeamSizeModal.tsxTravelModal.tsxTravelPopup.tsx
Faction/ui
ui/React
56
dist/vendor.bundle.js
vendored
56
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
44
index.html
44
index.html
@ -1,18 +1,18 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8"/>
|
||||||
<title>Bitburner</title>
|
<title>Bitburner</title>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png"/>
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png" />
|
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png"/>
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png" />
|
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png"/>
|
||||||
<link rel="manifest" href="dist/site.webmanifest" />
|
<link rel="manifest" href="dist/site.webmanifest"/>
|
||||||
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000" />
|
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000"/>
|
||||||
<meta name="apple-mobile-web-app-title" content="Bitburner" />
|
<meta name="apple-mobile-web-app-title" content="Bitburner"/>
|
||||||
<meta name="application-name" content="Bitburner" />
|
<meta name="application-name" content="Bitburner"/>
|
||||||
<meta name="msapplication-TileColor" content="#000000" />
|
<meta name="msapplication-TileColor" content="#000000"/>
|
||||||
<meta name="msapplication-config" content="dist/browserconfig.xml" />
|
<meta name="msapplication-config" content="dist/browserconfig.xml"/>
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff"/>
|
||||||
|
|
||||||
<!-- Google Analytics -->
|
<!-- Google Analytics -->
|
||||||
<script>
|
<script>
|
||||||
@ -36,25 +36,9 @@
|
|||||||
ga("send", "pageview");
|
ga("send", "pageview");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<link rel="shortcut icon" href="favicon.ico" />
|
<link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="main.css" rel="stylesheet"></head>
|
||||||
<link href="dist/vendor.css" rel="stylesheet" />
|
|
||||||
<link href="main.css" rel="stylesheet" />
|
|
||||||
</head>
|
|
||||||
<body>
|
<body>
|
||||||
<div id="entire-game-container">
|
<div id="root"/>
|
||||||
<div id="mainmenu-container" style="display: flex; flex-direction: row"></div>
|
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
|
||||||
|
|
||||||
<!-- Status text -->
|
|
||||||
<div id="status-text-container">
|
|
||||||
<p id="status-text"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="modal-portal"></div>
|
|
||||||
<div id="unclickable" style="display: none">Click on this to upgrade your Source-File -1!</div>
|
|
||||||
<script type="text/javascript" src="dist/vendor.bundle.js"></script>
|
|
||||||
<script type="text/javascript" src="main.bundle.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script src="src/ThirdParty/raphael.min.js"></script>
|
<script src="src/ThirdParty/raphael.min.js"></script>
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
9
main.css
9
main.css
@ -2105,13 +2105,14 @@ input[type="checkbox"] {
|
|||||||
.popup-box-content {
|
.popup-box-content {
|
||||||
background-color: var(--my-background-color);
|
background-color: var(--my-background-color);
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: 5px solid var(--my-highlight-color);
|
border: 2px solid #adff2f;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
/* Sit on top of the container */
|
/* Sit on top of the container */
|
||||||
color: var(--my-font-color); }
|
color: var(--my-font-color);
|
||||||
|
box-shadow: 0 3px 5px -1px #090, 0 5px 8px 0 #090, 0 1px 14px 0 #090; }
|
||||||
|
|
||||||
.popup-box-input-div {
|
.popup-box-input-div {
|
||||||
margin: 2px; }
|
margin: 2px; }
|
||||||
@ -2169,12 +2170,12 @@ input[type="checkbox"] {
|
|||||||
max-height: 50%;
|
max-height: 50%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background-color: var(--my-background-color);
|
background-color: var(--my-background-color);
|
||||||
border: 2px solid var(--my-highlight-color); }
|
border: 2px solid #adff2f; }
|
||||||
|
|
||||||
.log-box-header {
|
.log-box-header {
|
||||||
z-index: 1300;
|
z-index: 1300;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
border: 1px solid var(--my-highlight-color);
|
border: 2px solid #adff2f;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: row nowrap;
|
flex: row nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,3 +1,4 @@
|
|||||||
|
import React from "react";
|
||||||
import { Player } from "../Player";
|
import { Player } from "../Player";
|
||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { addOffset } from "../utils/helpers/addOffset";
|
import { addOffset } from "../utils/helpers/addOffset";
|
||||||
@ -20,7 +21,6 @@ class StatsMultiplier {
|
|||||||
|
|
||||||
export interface IActionParams {
|
export interface IActionParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
desc?: string;
|
|
||||||
level?: number;
|
level?: number;
|
||||||
maxLevel?: number;
|
maxLevel?: number;
|
||||||
autoLevel?: boolean;
|
autoLevel?: boolean;
|
||||||
@ -43,7 +43,6 @@ export interface IActionParams {
|
|||||||
|
|
||||||
export class Action implements IAction {
|
export class Action implements IAction {
|
||||||
name = "";
|
name = "";
|
||||||
desc = "";
|
|
||||||
|
|
||||||
// Difficulty scales with level. See getDifficulty() method
|
// Difficulty scales with level. See getDifficulty() method
|
||||||
level = 1;
|
level = 1;
|
||||||
@ -100,7 +99,6 @@ export class Action implements IAction {
|
|||||||
constructor(params: IActionParams | null = null) {
|
constructor(params: IActionParams | null = null) {
|
||||||
// | null = null
|
// | null = null
|
||||||
if (params && params.name) this.name = params.name;
|
if (params && params.name) this.name = params.name;
|
||||||
if (params && params.desc) this.desc = params.desc;
|
|
||||||
|
|
||||||
if (params && params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
if (params && params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
||||||
if (params && params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
if (params && params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
@ -1,763 +0,0 @@
|
|||||||
import { BlackOperation } from "./BlackOperation";
|
|
||||||
import { IMap } from "../types";
|
|
||||||
|
|
||||||
export const BlackOperations: IMap<BlackOperation> = {};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
BlackOperations["Operation Typhoon"] = new BlackOperation({
|
|
||||||
name: "Operation Typhoon",
|
|
||||||
desc:
|
|
||||||
"Obadiah Zenyatta is the leader of a RedWater PMC. It has long " +
|
|
||||||
"been known among the intelligence community that Zenyatta, along " +
|
|
||||||
"with the rest of the PMC, is a Synthoid.<br><br>" +
|
|
||||||
"The goal of Operation Typhoon is to find and eliminate " +
|
|
||||||
"Zenyatta and RedWater by any means necessary. After the task " +
|
|
||||||
"is completed, the actions must be covered up from the general public.",
|
|
||||||
baseDifficulty: 2000,
|
|
||||||
reqdRank: 2.5e3,
|
|
||||||
rankGain: 50,
|
|
||||||
rankLoss: 10,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Zero"] = new BlackOperation({
|
|
||||||
name: "Operation Zero",
|
|
||||||
desc:
|
|
||||||
"AeroCorp is one of the world's largest defense contractors. " +
|
|
||||||
"Its leader, Steve Watataki, is thought to be a supporter of " +
|
|
||||||
"Synthoid rights. He must be removed.<br><br>" +
|
|
||||||
"The goal of Operation Zero is to covertly infiltrate AeroCorp and " +
|
|
||||||
"uncover any incriminating evidence or " +
|
|
||||||
"information against Watataki that will cause him to be removed " +
|
|
||||||
"from his position at AeroCorp. Incriminating evidence can be " +
|
|
||||||
"fabricated as a last resort. Be warned that AeroCorp has some of " +
|
|
||||||
"the most advanced security measures in the world.",
|
|
||||||
baseDifficulty: 2500,
|
|
||||||
reqdRank: 5e3,
|
|
||||||
rankGain: 60,
|
|
||||||
rankLoss: 15,
|
|
||||||
hpLoss: 50,
|
|
||||||
weights: {
|
|
||||||
hack: 0.2,
|
|
||||||
str: 0.15,
|
|
||||||
def: 0.15,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isStealth: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation X"] = new BlackOperation({
|
|
||||||
name: "Operation X",
|
|
||||||
desc:
|
|
||||||
"We have recently discovered an underground publication " +
|
|
||||||
"group called Samizdat. Even though most of their publications " +
|
|
||||||
"are nonsensical conspiracy theories, the average human is " +
|
|
||||||
"gullible enough to believe them. Many of their works discuss " +
|
|
||||||
"Synthoids and pose a threat to society. The publications are spreading " +
|
|
||||||
"rapidly in China and other Eastern countries.<br><br>" +
|
|
||||||
"Samizdat has done a good job of keeping hidden and anonymous. " +
|
|
||||||
"However, we've just received intelligence that their base of " +
|
|
||||||
"operations is in Ishima's underground sewer systems. Your task is to " +
|
|
||||||
"investigate the sewer systems, and eliminate Samizdat. They must " +
|
|
||||||
"never publish anything again.",
|
|
||||||
baseDifficulty: 3000,
|
|
||||||
reqdRank: 7.5e3,
|
|
||||||
rankGain: 75,
|
|
||||||
rankLoss: 15,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Titan"] = new BlackOperation({
|
|
||||||
name: "Operation Titan",
|
|
||||||
desc:
|
|
||||||
"Several months ago Titan Laboratories' Bioengineering department " +
|
|
||||||
"was infiltrated by Synthoids. As far as we know, Titan Laboratories' " +
|
|
||||||
"management has no knowledge about this. We don't know what the " +
|
|
||||||
"Synthoids are up to, but the research that they could " +
|
|
||||||
"be conducting using Titan Laboraties' vast resources is potentially " +
|
|
||||||
"very dangerous.<br><br>" +
|
|
||||||
"Your goal is to enter and destroy the Bioengineering department's " +
|
|
||||||
"facility in Aevum. The task is not just to retire the Synthoids there, but " +
|
|
||||||
"also to destroy any information or research at the facility that " +
|
|
||||||
"is relevant to the Synthoids and their goals.",
|
|
||||||
baseDifficulty: 4000,
|
|
||||||
reqdRank: 10e3,
|
|
||||||
rankGain: 100,
|
|
||||||
rankLoss: 20,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Ares"] = new BlackOperation({
|
|
||||||
name: "Operation Ares",
|
|
||||||
desc:
|
|
||||||
"One of our undercover agents, Agent Carter, has informed us of a " +
|
|
||||||
"massive weapons deal going down in Dubai between rogue Russian " +
|
|
||||||
"militants and a radical Synthoid community. These weapons are next-gen " +
|
|
||||||
"plasma and energy weapons. It is critical for the safety of humanity " +
|
|
||||||
"that this deal does not happen.<br><br>" +
|
|
||||||
"Your task is to intercept the deal. Leave no survivors.",
|
|
||||||
baseDifficulty: 5000,
|
|
||||||
reqdRank: 12.5e3,
|
|
||||||
rankGain: 125,
|
|
||||||
rankLoss: 20,
|
|
||||||
hpLoss: 200,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.25,
|
|
||||||
def: 0.25,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Archangel"] = new BlackOperation({
|
|
||||||
name: "Operation Archangel",
|
|
||||||
desc:
|
|
||||||
"Our analysts have discovered that the popular Red Rabbit brothel in " +
|
|
||||||
"Amsterdam is run and 'staffed' by MK-VI Synthoids. Intelligence " +
|
|
||||||
"suggests that the profit from this brothel is used to fund a large " +
|
|
||||||
"black market arms trafficking operation.<br><br>" +
|
|
||||||
"The goal of this operation is to take out the leaders that are running " +
|
|
||||||
"the Red Rabbit brothel. Try to limit the number of other casualties, " +
|
|
||||||
"but do what you must to complete the mission.",
|
|
||||||
baseDifficulty: 7500,
|
|
||||||
reqdRank: 15e3,
|
|
||||||
rankGain: 200,
|
|
||||||
rankLoss: 20,
|
|
||||||
hpLoss: 25,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.3,
|
|
||||||
agi: 0.3,
|
|
||||||
cha: 0,
|
|
||||||
int: 0,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Juggernaut"] = new BlackOperation({
|
|
||||||
name: "Operation Juggernaut",
|
|
||||||
desc:
|
|
||||||
"The CIA has just encountered a new security threat. A new " +
|
|
||||||
"criminal group, lead by a shadowy operative who calls himself " +
|
|
||||||
"Juggernaut, has been smuggling drugs and weapons (including " +
|
|
||||||
"suspected bioweapons) into Sector-12. We also have reason " +
|
|
||||||
"to believe the tried to break into one of Universal Energy's " +
|
|
||||||
"facilities in order to cause a city-wide blackout. The CIA " +
|
|
||||||
"suspects that Juggernaut is a heavily-augmented Synthoid, and " +
|
|
||||||
"have thus enlisted our help.<br><br>" +
|
|
||||||
"Your mission is to eradicate Juggernaut and his followers.",
|
|
||||||
baseDifficulty: 10e3,
|
|
||||||
reqdRank: 20e3,
|
|
||||||
rankGain: 300,
|
|
||||||
rankLoss: 40,
|
|
||||||
hpLoss: 300,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.25,
|
|
||||||
def: 0.25,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Red Dragon"] = new BlackOperation({
|
|
||||||
name: "Operation Red Dragon",
|
|
||||||
desc:
|
|
||||||
"The Tetrads criminal organization is suspected of " +
|
|
||||||
"reverse-engineering the MK-VI Synthoid design. We believe " +
|
|
||||||
"they altered and possibly improved the design and began " +
|
|
||||||
"manufacturing their own Synthoid models in order to bolster " +
|
|
||||||
"their criminal activities.<br><br>" +
|
|
||||||
"Your task is to infiltrate and destroy the Tetrads' base of operations " +
|
|
||||||
"in Los Angeles. Intelligence tells us that their base houses " +
|
|
||||||
"one of their Synthoid manufacturing units.",
|
|
||||||
baseDifficulty: 12.5e3,
|
|
||||||
reqdRank: 25e3,
|
|
||||||
rankGain: 500,
|
|
||||||
rankLoss: 50,
|
|
||||||
hpLoss: 500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.05,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.05,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation K"] = new BlackOperation({
|
|
||||||
name: "Operation K",
|
|
||||||
desc:
|
|
||||||
"CODE RED SITUATION. Our intelligence tells us that VitaLife " +
|
|
||||||
"has discovered a new android cloning technology. This technology " +
|
|
||||||
"is supposedly capable of cloning Synthoid, not only physically " +
|
|
||||||
"but also their advanced AI modules. We do not believe that " +
|
|
||||||
"VitaLife is trying to use this technology illegally or " +
|
|
||||||
"maliciously, but if any Synthoids were able to infiltrate the " +
|
|
||||||
"corporation and take advantage of this technology then the " +
|
|
||||||
"results would be catastrophic.<br><br>" +
|
|
||||||
"We do not have the power or jurisdiction to shutdown this down " +
|
|
||||||
"through legal or political means, so we must resort to a covert " +
|
|
||||||
"operation. Your goal is to destroy this technology and eliminate " +
|
|
||||||
"anyone who was involved in its creation.",
|
|
||||||
baseDifficulty: 15e3,
|
|
||||||
reqdRank: 30e3,
|
|
||||||
rankGain: 750,
|
|
||||||
rankLoss: 60,
|
|
||||||
hpLoss: 1000,
|
|
||||||
weights: {
|
|
||||||
hack: 0.05,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.05,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Deckard"] = new BlackOperation({
|
|
||||||
name: "Operation Deckard",
|
|
||||||
desc:
|
|
||||||
"Despite your success in eliminating VitaLife's new android-replicating " +
|
|
||||||
"technology in Operation K, we've discovered that a small group of " +
|
|
||||||
"MK-VI Synthoids were able to make off with the schematics and design " +
|
|
||||||
"of the technology before the Operation. It is almost a certainty that " +
|
|
||||||
"these Synthoids are some of the rogue MK-VI ones from the Synthoid Uprising.<br><br>" +
|
|
||||||
"The goal of Operation Deckard is to hunt down these Synthoids and retire " +
|
|
||||||
"them. I don't need to tell you how critical this mission is.",
|
|
||||||
baseDifficulty: 20e3,
|
|
||||||
reqdRank: 40e3,
|
|
||||||
rankGain: 1e3,
|
|
||||||
rankLoss: 75,
|
|
||||||
hpLoss: 200,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Tyrell"] = new BlackOperation({
|
|
||||||
name: "Operation Tyrell",
|
|
||||||
desc:
|
|
||||||
"A week ago Blade Industries reported a small break-in at one " +
|
|
||||||
"of their Aevum Augmentation storage facitilities. We figured out " +
|
|
||||||
"that The Dark Army was behind the heist, and didn't think any more " +
|
|
||||||
"of it. However, we've just discovered that several known MK-VI Synthoids " +
|
|
||||||
"were part of that break-in group.<br><br>" +
|
|
||||||
"We cannot have Synthoids upgrading their already-enhanced abilities " +
|
|
||||||
"with Augmentations. Your task is to hunt down the associated Dark Army " +
|
|
||||||
"members and eliminate them.",
|
|
||||||
baseDifficulty: 25e3,
|
|
||||||
reqdRank: 50e3,
|
|
||||||
rankGain: 1.5e3,
|
|
||||||
rankLoss: 100,
|
|
||||||
hpLoss: 500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Wallace"] = new BlackOperation({
|
|
||||||
name: "Operation Wallace",
|
|
||||||
desc:
|
|
||||||
"Based on information gathered from Operation Tyrell, we've discovered " +
|
|
||||||
"that The Dark Army was well aware that there were Synthoids amongst " +
|
|
||||||
"their ranks. Even worse, we believe that The Dark Army is working " +
|
|
||||||
"together with other criminal organizations such as The Syndicate and " +
|
|
||||||
"that they are planning some sort of large-scale takeover of multiple major " +
|
|
||||||
"cities, most notably Aevum. We suspect that Synthoids have infiltrated " +
|
|
||||||
"the ranks of these criminal factions and are trying to stage another " +
|
|
||||||
"Synthoid uprising.<br><br>" +
|
|
||||||
"The best way to deal with this is to prevent it before it even happens. " +
|
|
||||||
"The goal of Operation Wallace is to destroy the Dark Army and " +
|
|
||||||
"Syndicate factions in Aevum immediately. Leave no survivors.",
|
|
||||||
baseDifficulty: 30e3,
|
|
||||||
reqdRank: 75e3,
|
|
||||||
rankGain: 2e3,
|
|
||||||
rankLoss: 150,
|
|
||||||
hpLoss: 1500,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Shoulder of Orion"] = new BlackOperation({
|
|
||||||
name: "Operation Shoulder of Orion",
|
|
||||||
desc:
|
|
||||||
"China's Solaris Space Systems is secretly launching the first " +
|
|
||||||
"manned spacecraft in over a decade using Synthoids. We believe " +
|
|
||||||
"China is trying to establish the first off-world colonies.<br><br>" +
|
|
||||||
"The mission is to prevent this launch without instigating an " +
|
|
||||||
"international conflict. When you accept this mission you will be " +
|
|
||||||
"officially disavowed by the NSA and the national government until after you " +
|
|
||||||
"successfully return. In the event of failure, all of the operation's " +
|
|
||||||
"team members must not let themselves be captured alive.",
|
|
||||||
baseDifficulty: 35e3,
|
|
||||||
reqdRank: 100e3,
|
|
||||||
rankGain: 2.5e3,
|
|
||||||
rankLoss: 500,
|
|
||||||
hpLoss: 1500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isStealth: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Hyron"] = new BlackOperation({
|
|
||||||
name: "Operation Hyron",
|
|
||||||
desc:
|
|
||||||
"Our intelligence tells us that Fulcrum Technologies is developing " +
|
|
||||||
"a quantum supercomputer using human brains as core " +
|
|
||||||
"processors. This supercomputer " +
|
|
||||||
"is rumored to be able to store vast amounts of data and " +
|
|
||||||
"perform computations unmatched by any other supercomputer on the " +
|
|
||||||
"planet. But more importantly, the use of organic human brains " +
|
|
||||||
"means that the supercomputer may be able to reason abstractly " +
|
|
||||||
"and become self-aware.<br><br>" +
|
|
||||||
"I do not need to remind you why sentient-level AIs pose a serious " +
|
|
||||||
"threat to all of mankind.<br><br>" +
|
|
||||||
"The research for this project is being conducted at one of Fulcrum " +
|
|
||||||
"Technologies secret facilities in Aevum, codenamed 'Alpha Ranch'. " +
|
|
||||||
"Infiltrate the compound, delete and destroy the work, and then find and kill the " +
|
|
||||||
"project lead.",
|
|
||||||
baseDifficulty: 40e3,
|
|
||||||
reqdRank: 125e3,
|
|
||||||
rankGain: 3e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Morpheus"] = new BlackOperation({
|
|
||||||
name: "Operation Morpheus",
|
|
||||||
desc:
|
|
||||||
"DreamSense Technologies is an advertising company that uses " +
|
|
||||||
"special technology to transmit their ads into the peoples " +
|
|
||||||
"dreams and subconcious. They do this using broadcast transmitter " +
|
|
||||||
"towers. Based on information from our agents and informants in " +
|
|
||||||
"Chonqging, we have reason to believe that one of the broadcast " +
|
|
||||||
"towers there has been compromised by Synthoids and is being used " +
|
|
||||||
"to spread pro-Synthoid propaganda.<br><br>" +
|
|
||||||
"The mission is to destroy this broadcast tower. Speed and " +
|
|
||||||
"stealth are of the upmost important for this.",
|
|
||||||
baseDifficulty: 45e3,
|
|
||||||
reqdRank: 150e3,
|
|
||||||
rankGain: 4e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.05,
|
|
||||||
str: 0.15,
|
|
||||||
def: 0.15,
|
|
||||||
dex: 0.3,
|
|
||||||
agi: 0.3,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.05,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isStealth: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Ion Storm"] = new BlackOperation({
|
|
||||||
name: "Operation Ion Storm",
|
|
||||||
desc:
|
|
||||||
"Our analysts have uncovered a gathering of MK-VI Synthoids " +
|
|
||||||
"that have taken up residence in the Sector-12 Slums. We " +
|
|
||||||
"don't know if they are rogue Synthoids from the Uprising, " +
|
|
||||||
"but we do know that they have been stockpiling " +
|
|
||||||
"weapons, money, and other resources. This makes them dangerous.<br><br>" +
|
|
||||||
"This is a full-scale assault operation to find and retire all of these " +
|
|
||||||
"Synthoids in the Sector-12 Slums.",
|
|
||||||
baseDifficulty: 50e3,
|
|
||||||
reqdRank: 175e3,
|
|
||||||
rankGain: 5e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 5000,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Annihilus"] = new BlackOperation({
|
|
||||||
name: "Operation Annihilus",
|
|
||||||
desc:
|
|
||||||
"Our superiors have ordered us to eradicate everything and everyone " +
|
|
||||||
"in an underground facility located in Aevum. They tell us " +
|
|
||||||
"that the facility houses many dangerous Synthoids and " +
|
|
||||||
"belongs to a terrorist organization called " +
|
|
||||||
"'The Covenant'. We have no prior intelligence about this " +
|
|
||||||
"organization, so you are going in blind.",
|
|
||||||
baseDifficulty: 55e3,
|
|
||||||
reqdRank: 200e3,
|
|
||||||
rankGain: 7.5e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 10e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Ultron"] = new BlackOperation({
|
|
||||||
name: "Operation Ultron",
|
|
||||||
desc:
|
|
||||||
"OmniTek Incorporated, the original designer and manufacturer of Synthoids, " +
|
|
||||||
"has notified us of a malfunction in their AI design. This malfunction, " +
|
|
||||||
"when triggered, causes MK-VI Synthoids to become radicalized and seek out " +
|
|
||||||
"the destruction of humanity. They say that this bug affects all MK-VI Synthoids, " +
|
|
||||||
"not just the rogue ones from the Uprising.<br><br>" +
|
|
||||||
"OmniTek has also told us they they believe someone has triggered this " +
|
|
||||||
"malfunction in a large group of MK-VI Synthoids, and that these newly-radicalized Synthoids " +
|
|
||||||
"are now amassing in Volhaven to form a terrorist group called Ultron.<br><br>" +
|
|
||||||
"Intelligence suggests Ultron is heavily armed and that their members are " +
|
|
||||||
"augmented. We believe Ultron is making moves to take control of " +
|
|
||||||
"and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).<br><br>" +
|
|
||||||
"Your task is to find and destroy Ultron.",
|
|
||||||
baseDifficulty: 60e3,
|
|
||||||
reqdRank: 250e3,
|
|
||||||
rankGain: 10e3,
|
|
||||||
rankLoss: 2e3,
|
|
||||||
hpLoss: 10e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Centurion"] = new BlackOperation({
|
|
||||||
name: "Operation Centurion",
|
|
||||||
desc:
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)<br><br>" +
|
|
||||||
"Throughout all of humanity's history, we have relied on " +
|
|
||||||
"technology to survive, conquer, and progress. Its advancement became our primary goal. " +
|
|
||||||
"And at the peak of human civilization technology turned into " +
|
|
||||||
"power. Global, absolute power.<br><br>" +
|
|
||||||
"It seems that the universe is not without a sense of irony.<br><br>" +
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
|
||||||
baseDifficulty: 70e3,
|
|
||||||
reqdRank: 300e3,
|
|
||||||
rankGain: 15e3,
|
|
||||||
rankLoss: 5e3,
|
|
||||||
hpLoss: 10e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Vindictus"] = new BlackOperation({
|
|
||||||
name: "Operation Vindictus",
|
|
||||||
desc:
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)<br><br>" +
|
|
||||||
"The bits are all around us. The daemons that hold the Node " +
|
|
||||||
"together can manifest themselves in many different ways.<br><br>" +
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
|
||||||
baseDifficulty: 75e3,
|
|
||||||
reqdRank: 350e3,
|
|
||||||
rankGain: 20e3,
|
|
||||||
rankLoss: 20e3,
|
|
||||||
hpLoss: 20e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Daedalus"] = new BlackOperation({
|
|
||||||
name: "Operation Daedalus",
|
|
||||||
desc: "Yesterday we obeyed kings and bent our neck to emperors. " + "Today we kneel only to truth.",
|
|
||||||
baseDifficulty: 80e3,
|
|
||||||
reqdRank: 400e3,
|
|
||||||
rankGain: 40e3,
|
|
||||||
rankLoss: 10e3,
|
|
||||||
hpLoss: 100e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
572
src/Bladeburner/BlackOperations.tsx
Normal file
572
src/Bladeburner/BlackOperations.tsx
Normal file
@ -0,0 +1,572 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { BlackOperation } from "./BlackOperation";
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export const BlackOperations: IMap<BlackOperation> = {};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
BlackOperations["Operation Typhoon"] = new BlackOperation({
|
||||||
|
name: "Operation Typhoon",
|
||||||
|
baseDifficulty: 2000,
|
||||||
|
reqdRank: 2.5e3,
|
||||||
|
rankGain: 50,
|
||||||
|
rankLoss: 10,
|
||||||
|
hpLoss: 100,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Zero"] = new BlackOperation({
|
||||||
|
name: "Operation Zero",
|
||||||
|
baseDifficulty: 2500,
|
||||||
|
reqdRank: 5e3,
|
||||||
|
rankGain: 60,
|
||||||
|
rankLoss: 15,
|
||||||
|
hpLoss: 50,
|
||||||
|
weights: {
|
||||||
|
hack: 0.2,
|
||||||
|
str: 0.15,
|
||||||
|
def: 0.15,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isStealth: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation X"] = new BlackOperation({
|
||||||
|
name: "Operation X",
|
||||||
|
baseDifficulty: 3000,
|
||||||
|
reqdRank: 7.5e3,
|
||||||
|
rankGain: 75,
|
||||||
|
rankLoss: 15,
|
||||||
|
hpLoss: 100,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Titan"] = new BlackOperation({
|
||||||
|
name: "Operation Titan",
|
||||||
|
baseDifficulty: 4000,
|
||||||
|
reqdRank: 10e3,
|
||||||
|
rankGain: 100,
|
||||||
|
rankLoss: 20,
|
||||||
|
hpLoss: 100,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Ares"] = new BlackOperation({
|
||||||
|
name: "Operation Ares",
|
||||||
|
baseDifficulty: 5000,
|
||||||
|
reqdRank: 12.5e3,
|
||||||
|
rankGain: 125,
|
||||||
|
rankLoss: 20,
|
||||||
|
hpLoss: 200,
|
||||||
|
weights: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.25,
|
||||||
|
def: 0.25,
|
||||||
|
dex: 0.25,
|
||||||
|
agi: 0.25,
|
||||||
|
cha: 0,
|
||||||
|
int: 0,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Archangel"] = new BlackOperation({
|
||||||
|
name: "Operation Archangel",
|
||||||
|
baseDifficulty: 7500,
|
||||||
|
reqdRank: 15e3,
|
||||||
|
rankGain: 200,
|
||||||
|
rankLoss: 20,
|
||||||
|
hpLoss: 25,
|
||||||
|
weights: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.3,
|
||||||
|
agi: 0.3,
|
||||||
|
cha: 0,
|
||||||
|
int: 0,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Juggernaut"] = new BlackOperation({
|
||||||
|
name: "Operation Juggernaut",
|
||||||
|
baseDifficulty: 10e3,
|
||||||
|
reqdRank: 20e3,
|
||||||
|
rankGain: 300,
|
||||||
|
rankLoss: 40,
|
||||||
|
hpLoss: 300,
|
||||||
|
weights: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.25,
|
||||||
|
def: 0.25,
|
||||||
|
dex: 0.25,
|
||||||
|
agi: 0.25,
|
||||||
|
cha: 0,
|
||||||
|
int: 0,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Red Dragon"] = new BlackOperation({
|
||||||
|
name: "Operation Red Dragon",
|
||||||
|
baseDifficulty: 12.5e3,
|
||||||
|
reqdRank: 25e3,
|
||||||
|
rankGain: 500,
|
||||||
|
rankLoss: 50,
|
||||||
|
hpLoss: 500,
|
||||||
|
weights: {
|
||||||
|
hack: 0.05,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.25,
|
||||||
|
agi: 0.25,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.05,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation K"] = new BlackOperation({
|
||||||
|
name: "Operation K",
|
||||||
|
baseDifficulty: 15e3,
|
||||||
|
reqdRank: 30e3,
|
||||||
|
rankGain: 750,
|
||||||
|
rankLoss: 60,
|
||||||
|
hpLoss: 1000,
|
||||||
|
weights: {
|
||||||
|
hack: 0.05,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.25,
|
||||||
|
agi: 0.25,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.05,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Deckard"] = new BlackOperation({
|
||||||
|
name: "Operation Deckard",
|
||||||
|
baseDifficulty: 20e3,
|
||||||
|
reqdRank: 40e3,
|
||||||
|
rankGain: 1e3,
|
||||||
|
rankLoss: 75,
|
||||||
|
hpLoss: 200,
|
||||||
|
weights: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.24,
|
||||||
|
def: 0.24,
|
||||||
|
dex: 0.24,
|
||||||
|
agi: 0.24,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.04,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Tyrell"] = new BlackOperation({
|
||||||
|
name: "Operation Tyrell",
|
||||||
|
baseDifficulty: 25e3,
|
||||||
|
reqdRank: 50e3,
|
||||||
|
rankGain: 1.5e3,
|
||||||
|
rankLoss: 100,
|
||||||
|
hpLoss: 500,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Wallace"] = new BlackOperation({
|
||||||
|
name: "Operation Wallace",
|
||||||
|
baseDifficulty: 30e3,
|
||||||
|
reqdRank: 75e3,
|
||||||
|
rankGain: 2e3,
|
||||||
|
rankLoss: 150,
|
||||||
|
hpLoss: 1500,
|
||||||
|
weights: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.24,
|
||||||
|
def: 0.24,
|
||||||
|
dex: 0.24,
|
||||||
|
agi: 0.24,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.04,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Shoulder of Orion"] = new BlackOperation({
|
||||||
|
name: "Operation Shoulder of Orion",
|
||||||
|
baseDifficulty: 35e3,
|
||||||
|
reqdRank: 100e3,
|
||||||
|
rankGain: 2.5e3,
|
||||||
|
rankLoss: 500,
|
||||||
|
hpLoss: 1500,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isStealth: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Hyron"] = new BlackOperation({
|
||||||
|
name: "Operation Hyron",
|
||||||
|
baseDifficulty: 40e3,
|
||||||
|
reqdRank: 125e3,
|
||||||
|
rankGain: 3e3,
|
||||||
|
rankLoss: 1e3,
|
||||||
|
hpLoss: 500,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Morpheus"] = new BlackOperation({
|
||||||
|
name: "Operation Morpheus",
|
||||||
|
baseDifficulty: 45e3,
|
||||||
|
reqdRank: 150e3,
|
||||||
|
rankGain: 4e3,
|
||||||
|
rankLoss: 1e3,
|
||||||
|
hpLoss: 100,
|
||||||
|
weights: {
|
||||||
|
hack: 0.05,
|
||||||
|
str: 0.15,
|
||||||
|
def: 0.15,
|
||||||
|
dex: 0.3,
|
||||||
|
agi: 0.3,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.05,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isStealth: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Ion Storm"] = new BlackOperation({
|
||||||
|
name: "Operation Ion Storm",
|
||||||
|
baseDifficulty: 50e3,
|
||||||
|
reqdRank: 175e3,
|
||||||
|
rankGain: 5e3,
|
||||||
|
rankLoss: 1e3,
|
||||||
|
hpLoss: 5000,
|
||||||
|
weights: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.24,
|
||||||
|
def: 0.24,
|
||||||
|
dex: 0.24,
|
||||||
|
agi: 0.24,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.04,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Annihilus"] = new BlackOperation({
|
||||||
|
name: "Operation Annihilus",
|
||||||
|
baseDifficulty: 55e3,
|
||||||
|
reqdRank: 200e3,
|
||||||
|
rankGain: 7.5e3,
|
||||||
|
rankLoss: 1e3,
|
||||||
|
hpLoss: 10e3,
|
||||||
|
weights: {
|
||||||
|
hack: 0,
|
||||||
|
str: 0.24,
|
||||||
|
def: 0.24,
|
||||||
|
dex: 0.24,
|
||||||
|
agi: 0.24,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.04,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Ultron"] = new BlackOperation({
|
||||||
|
name: "Operation Ultron",
|
||||||
|
baseDifficulty: 60e3,
|
||||||
|
reqdRank: 250e3,
|
||||||
|
rankGain: 10e3,
|
||||||
|
rankLoss: 2e3,
|
||||||
|
hpLoss: 10e3,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
isKill: true,
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Centurion"] = new BlackOperation({
|
||||||
|
name: "Operation Centurion",
|
||||||
|
baseDifficulty: 70e3,
|
||||||
|
reqdRank: 300e3,
|
||||||
|
rankGain: 15e3,
|
||||||
|
rankLoss: 5e3,
|
||||||
|
hpLoss: 10e3,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Vindictus"] = new BlackOperation({
|
||||||
|
name: "Operation Vindictus",
|
||||||
|
baseDifficulty: 75e3,
|
||||||
|
reqdRank: 350e3,
|
||||||
|
rankGain: 20e3,
|
||||||
|
rankLoss: 20e3,
|
||||||
|
hpLoss: 20e3,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
BlackOperations["Operation Daedalus"] = new BlackOperation({
|
||||||
|
name: "Operation Daedalus",
|
||||||
|
baseDifficulty: 80e3,
|
||||||
|
reqdRank: 400e3,
|
||||||
|
rankGain: 40e3,
|
||||||
|
rankLoss: 10e3,
|
||||||
|
hpLoss: 100e3,
|
||||||
|
weights: {
|
||||||
|
hack: 0.1,
|
||||||
|
str: 0.2,
|
||||||
|
def: 0.2,
|
||||||
|
dex: 0.2,
|
||||||
|
agi: 0.2,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.1,
|
||||||
|
},
|
||||||
|
decays: {
|
||||||
|
hack: 0.6,
|
||||||
|
str: 0.8,
|
||||||
|
def: 0.8,
|
||||||
|
dex: 0.8,
|
||||||
|
agi: 0.8,
|
||||||
|
cha: 0,
|
||||||
|
int: 0.75,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
@ -1,3 +1,4 @@
|
|||||||
|
import React from "react";
|
||||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
|
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
|
||||||
import { IBladeburner } from "./IBladeburner";
|
import { IBladeburner } from "./IBladeburner";
|
||||||
import { IActionIdentifier } from "./IActionIdentifier";
|
import { IActionIdentifier } from "./IActionIdentifier";
|
||||||
@ -1586,11 +1587,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
create(): void {
|
create(): void {
|
||||||
this.contracts["Tracking"] = new Contract({
|
this.contracts["Tracking"] = new Contract({
|
||||||
name: "Tracking",
|
name: "Tracking",
|
||||||
desc:
|
|
||||||
"Identify and locate Synthoids. This contract involves reconnaissance " +
|
|
||||||
"and information-gathering ONLY. Do NOT engage. Stealth is of the utmost importance.<br><br>" +
|
|
||||||
"Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for " +
|
|
||||||
"whatever city you are currently in.",
|
|
||||||
baseDifficulty: 125,
|
baseDifficulty: 125,
|
||||||
difficultyFac: 1.02,
|
difficultyFac: 1.02,
|
||||||
rewardFac: 1.041,
|
rewardFac: 1.041,
|
||||||
@ -1619,10 +1615,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.contracts["Bounty Hunter"] = new Contract({
|
this.contracts["Bounty Hunter"] = new Contract({
|
||||||
name: "Bounty Hunter",
|
name: "Bounty Hunter",
|
||||||
desc:
|
|
||||||
"Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.<br><br>" +
|
|
||||||
"Successfully completing a Bounty Hunter contract will lower the population in your " +
|
|
||||||
"current city, and will also increase its chaos level.",
|
|
||||||
baseDifficulty: 250,
|
baseDifficulty: 250,
|
||||||
difficultyFac: 1.04,
|
difficultyFac: 1.04,
|
||||||
rewardFac: 1.085,
|
rewardFac: 1.085,
|
||||||
@ -1651,10 +1643,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.contracts["Retirement"] = new Contract({
|
this.contracts["Retirement"] = new Contract({
|
||||||
name: "Retirement",
|
name: "Retirement",
|
||||||
desc:
|
|
||||||
"Hunt down and retire (kill) rogue Synthoids.<br><br>" +
|
|
||||||
"Successfully completing a Retirement contract will lower the population in your current " +
|
|
||||||
"city, and will also increase its chaos level.",
|
|
||||||
baseDifficulty: 200,
|
baseDifficulty: 200,
|
||||||
difficultyFac: 1.03,
|
difficultyFac: 1.03,
|
||||||
rewardFac: 1.065,
|
rewardFac: 1.065,
|
||||||
@ -1684,12 +1672,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
|
|
||||||
this.operations["Investigation"] = new Operation({
|
this.operations["Investigation"] = new Operation({
|
||||||
name: "Investigation",
|
name: "Investigation",
|
||||||
desc:
|
|
||||||
"As a field agent, investigate and identify Synthoid " +
|
|
||||||
"populations, movements, and operations.<br><br>Successful " +
|
|
||||||
"Investigation ops will increase the accuracy of your " +
|
|
||||||
"synthoid data.<br><br>" +
|
|
||||||
"You will NOT lose HP from failed Investigation ops.",
|
|
||||||
baseDifficulty: 400,
|
baseDifficulty: 400,
|
||||||
difficultyFac: 1.03,
|
difficultyFac: 1.03,
|
||||||
rewardFac: 1.07,
|
rewardFac: 1.07,
|
||||||
@ -1719,11 +1701,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Undercover Operation"] = new Operation({
|
this.operations["Undercover Operation"] = new Operation({
|
||||||
name: "Undercover Operation",
|
name: "Undercover Operation",
|
||||||
desc:
|
|
||||||
"Conduct undercover operations to identify hidden " +
|
|
||||||
"and underground Synthoid communities and organizations.<br><br>" +
|
|
||||||
"Successful Undercover ops will increase the accuracy of your synthoid " +
|
|
||||||
"data.",
|
|
||||||
baseDifficulty: 500,
|
baseDifficulty: 500,
|
||||||
difficultyFac: 1.04,
|
difficultyFac: 1.04,
|
||||||
rewardFac: 1.09,
|
rewardFac: 1.09,
|
||||||
@ -1754,7 +1731,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Sting Operation"] = new Operation({
|
this.operations["Sting Operation"] = new Operation({
|
||||||
name: "Sting Operation",
|
name: "Sting Operation",
|
||||||
desc: "Conduct a sting operation to bait and capture particularly " + "notorious Synthoid criminals.",
|
|
||||||
baseDifficulty: 650,
|
baseDifficulty: 650,
|
||||||
difficultyFac: 1.04,
|
difficultyFac: 1.04,
|
||||||
rewardFac: 1.095,
|
rewardFac: 1.095,
|
||||||
@ -1785,10 +1761,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Raid"] = new Operation({
|
this.operations["Raid"] = new Operation({
|
||||||
name: "Raid",
|
name: "Raid",
|
||||||
desc:
|
|
||||||
"Lead an assault on a known Synthoid community. Note that " +
|
|
||||||
"there must be an existing Synthoid community in your current city " +
|
|
||||||
"in order for this Operation to be successful.",
|
|
||||||
baseDifficulty: 800,
|
baseDifficulty: 800,
|
||||||
difficultyFac: 1.045,
|
difficultyFac: 1.045,
|
||||||
rewardFac: 1.1,
|
rewardFac: 1.1,
|
||||||
@ -1819,10 +1791,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Stealth Retirement Operation"] = new Operation({
|
this.operations["Stealth Retirement Operation"] = new Operation({
|
||||||
name: "Stealth Retirement Operation",
|
name: "Stealth Retirement Operation",
|
||||||
desc:
|
|
||||||
"Lead a covert operation to retire Synthoids. The " +
|
|
||||||
"objective is to complete the task without " +
|
|
||||||
"drawing any attention. Stealth and discretion are key.",
|
|
||||||
baseDifficulty: 1000,
|
baseDifficulty: 1000,
|
||||||
difficultyFac: 1.05,
|
difficultyFac: 1.05,
|
||||||
rewardFac: 1.11,
|
rewardFac: 1.11,
|
||||||
@ -1854,10 +1822,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Assassination"] = new Operation({
|
this.operations["Assassination"] = new Operation({
|
||||||
name: "Assassination",
|
name: "Assassination",
|
||||||
desc:
|
|
||||||
"Assassinate Synthoids that have been identified as " +
|
|
||||||
"important, high-profile social and political leaders " +
|
|
||||||
"in the Synthoid communities.",
|
|
||||||
baseDifficulty: 1500,
|
baseDifficulty: 1500,
|
||||||
difficultyFac: 1.06,
|
difficultyFac: 1.06,
|
||||||
rewardFac: 1.14,
|
rewardFac: 1.14,
|
||||||
@ -1900,7 +1864,7 @@ export class Bladeburner implements IBladeburner {
|
|||||||
if (this.action.type !== ActionTypes["Idle"]) {
|
if (this.action.type !== ActionTypes["Idle"]) {
|
||||||
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
|
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
|
||||||
if (this.automateEnabled) {
|
if (this.automateEnabled) {
|
||||||
msg += `<br><br>Your automation was disabled as well. You will have to re-enable it through the Bladeburner console`;
|
msg += `<br /><br />Your automation was disabled as well. You will have to re-enable it through the Bladeburner console`;
|
||||||
this.automateEnabled = false;
|
this.automateEnabled = false;
|
||||||
}
|
}
|
||||||
if (!Settings.SuppressBladeburnerPopup) {
|
if (!Settings.SuppressBladeburnerPopup) {
|
@ -1,54 +0,0 @@
|
|||||||
import { Action } from "./Action";
|
|
||||||
import { IMap } from "../types";
|
|
||||||
|
|
||||||
export const GeneralActions: IMap<Action> = {};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
// General Actions
|
|
||||||
let actionName;
|
|
||||||
actionName = "Training";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Improve your abilities at the Bladeburner unit's specialized training " +
|
|
||||||
"center. Doing this gives experience for all combat stats and also " +
|
|
||||||
"increases your max stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Field Analysis";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Mine and analyze Synthoid-related data. This improves the " +
|
|
||||||
"Bladeburner's unit intelligence on Synthoid locations and " +
|
|
||||||
"activities. Completing this action will improve the accuracy " +
|
|
||||||
"of your Synthoid population estimated in the current city.<br><br>" +
|
|
||||||
"Does NOT require stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Recruitment";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Attempt to recruit members for your Bladeburner team. These members " +
|
|
||||||
"can help you conduct operations.<br><br>" +
|
|
||||||
"Does NOT require stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Diplomacy";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Improve diplomatic relations with the Synthoid population. " +
|
|
||||||
"Completing this action will reduce the Chaos level in your current city.<br><br>" +
|
|
||||||
"Does NOT require stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Hyperbolic Regeneration Chamber";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Enter cryogenic stasis using the Bladeburner division's hi-tech Regeneration Chamber. " +
|
|
||||||
"This will slowly heal your wounds and slightly increase your stamina.<br><br>",
|
|
||||||
});
|
|
||||||
})();
|
|
34
src/Bladeburner/GeneralActions.tsx
Normal file
34
src/Bladeburner/GeneralActions.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Action } from "./Action";
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export const GeneralActions: IMap<Action> = {};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
// General Actions
|
||||||
|
let actionName;
|
||||||
|
actionName = "Training";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Field Analysis";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Recruitment";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Diplomacy";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Hyperbolic Regeneration Chamber";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
})();
|
@ -18,7 +18,6 @@ export interface ISuccessChanceParams {
|
|||||||
|
|
||||||
export interface IAction {
|
export interface IAction {
|
||||||
name: string;
|
name: string;
|
||||||
desc: string;
|
|
||||||
|
|
||||||
// Difficulty scales with level. See getDifficulty() method
|
// Difficulty scales with level. See getDifficulty() method
|
||||||
level: number;
|
level: number;
|
299
src/Bladeburner/data/BlackOperations.tsx
Normal file
299
src/Bladeburner/data/BlackOperations.tsx
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IBlackOp {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlackOperations: {
|
||||||
|
[key: string]: IBlackOp | undefined;
|
||||||
|
} = {
|
||||||
|
"Operation Typhoon": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Obadiah Zenyatta is the leader of a RedWater PMC. It has long been known among the intelligence community that
|
||||||
|
Zenyatta, along with the rest of the PMC, is a Synthoid.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of Operation Typhoon is to find and eliminate Zenyatta and RedWater by any means necessary. After the
|
||||||
|
task is completed, the actions must be covered up from the general public.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
"Operation Zero": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
AeroCorp is one of the world's largest defense contractors. Its leader, Steve Watataki, is thought to be a
|
||||||
|
supporter of Synthoid rights. He must be removed.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of Operation Zero is to covertly infiltrate AeroCorp and uncover any incriminating evidence or
|
||||||
|
information against Watataki that will cause him to be removed from his position at AeroCorp. Incriminating
|
||||||
|
evidence can be fabricated as a last resort. Be warned that AeroCorp has some of the most advanced security
|
||||||
|
measures in the world.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation X": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
We have recently discovered an underground publication group called Samizdat. Even though most of their
|
||||||
|
publications are nonsensical conspiracy theories, the average human is gullible enough to believe them. Many of
|
||||||
|
their works discuss Synthoids and pose a threat to society. The publications are spreading rapidly in China and
|
||||||
|
other Eastern countries.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Samizdat has done a good job of keeping hidden and anonymous. However, we've just received intelligence that
|
||||||
|
their base of operations is in Ishima's underground sewer systems. Your task is to investigate the sewer
|
||||||
|
systems, and eliminate Samizdat. They must never publish anything again.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Titan": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Several months ago Titan Laboratories' Bioengineering department was infiltrated by Synthoids. As far as we
|
||||||
|
know, Titan Laboratories' management has no knowledge about this. We don't know what the Synthoids are up to,
|
||||||
|
but the research that they could be conducting using Titan Laboraties' vast resources is potentially very
|
||||||
|
dangerous.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your goal is to enter and destroy the Bioengineering department's facility in Aevum. The task is not just to
|
||||||
|
retire the Synthoids there, but also to destroy any information or research at the facility that is relevant to
|
||||||
|
the Synthoids and their goals.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Ares": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
One of our undercover agents, Agent Carter, has informed us of a massive weapons deal going down in Dubai
|
||||||
|
between rogue Russian militants and a radical Synthoid community. These weapons are next-gen plasma and energy
|
||||||
|
weapons. It is critical for the safety of humanity that this deal does not happen.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your task is to intercept the deal. Leave no survivors.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Archangel": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our analysts have discovered that the popular Red Rabbit brothel in Amsterdam is run and 'staffed' by MK-VI
|
||||||
|
Synthoids. Intelligence suggests that the profit from this brothel is used to fund a large black market arms
|
||||||
|
trafficking operation.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of this operation is to take out the leaders that are running the Red Rabbit brothel. Try to limit the
|
||||||
|
number of other casualties, but do what you must to complete the mission.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Juggernaut": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
The CIA has just encountered a new security threat. A new criminal group, lead by a shadowy operative who calls
|
||||||
|
himself Juggernaut, has been smuggling drugs and weapons (including suspected bioweapons) into Sector-12. We
|
||||||
|
also have reason to believe the tried to break into one of Universal Energy's facilities in order to cause a
|
||||||
|
city-wide blackout. The CIA suspects that Juggernaut is a heavily-augmented Synthoid, and have thus enlisted our
|
||||||
|
help.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your mission is to eradicate Juggernaut and his followers.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Red Dragon": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
The Tetrads criminal organization is suspected of reverse-engineering the MK-VI Synthoid design. We believe they
|
||||||
|
altered and possibly improved the design and began manufacturing their own Synthoid models in order to bolster
|
||||||
|
their criminal activities.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your task is to infiltrate and destroy the Tetrads' base of operations in Los Angeles. Intelligence tells us
|
||||||
|
that their base houses one of their Synthoid manufacturing units.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation K": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
CODE RED SITUATION. Our intelligence tells us that VitaLife has discovered a new android cloning technology.
|
||||||
|
This technology is supposedly capable of cloning Synthoid, not only physically but also their advanced AI
|
||||||
|
modules. We do not believe that VitaLife is trying to use this technology illegally or maliciously, but if any
|
||||||
|
Synthoids were able to infiltrate the corporation and take advantage of this technology then the results would
|
||||||
|
be catastrophic.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
We do not have the power or jurisdiction to shutdown this down through legal or political means, so we must
|
||||||
|
resort to a covert operation. Your goal is to destroy this technology and eliminate anyone who was involved in
|
||||||
|
its creation.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Deckard": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Despite your success in eliminating VitaLife's new android-replicating technology in Operation K, we've
|
||||||
|
discovered that a small group of MK-VI Synthoids were able to make off with the schematics and design of the
|
||||||
|
technology before the Operation. It is almost a certainty that these Synthoids are some of the rogue MK-VI ones
|
||||||
|
from the Synthoid Uprising.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of Operation Deckard is to hunt down these Synthoids and retire them. I don't need to tell you how
|
||||||
|
critical this mission is.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Tyrell": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
A week ago Blade Industries reported a small break-in at one of their Aevum Augmentation storage facitilities.
|
||||||
|
We figured out that The Dark Army was behind the heist, and didn't think any more of it. However, we've just
|
||||||
|
discovered that several known MK-VI Synthoids were part of that break-in group.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
We cannot have Synthoids upgrading their already-enhanced abilities with Augmentations. Your task is to hunt
|
||||||
|
down the associated Dark Army members and eliminate them.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Wallace": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Based on information gathered from Operation Tyrell, we've discovered that The Dark Army was well aware that
|
||||||
|
there were Synthoids amongst their ranks. Even worse, we believe that The Dark Army is working together with
|
||||||
|
other criminal organizations such as The Syndicate and that they are planning some sort of large-scale takeover
|
||||||
|
of multiple major cities, most notably Aevum. We suspect that Synthoids have infiltrated the ranks of these
|
||||||
|
criminal factions and are trying to stage another Synthoid uprising.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The best way to deal with this is to prevent it before it even happens. The goal of Operation Wallace is to
|
||||||
|
destroy the Dark Army and Syndicate factions in Aevum immediately. Leave no survivors.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Shoulder of Orion": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
China's Solaris Space Systems is secretly launching the first manned spacecraft in over a decade using
|
||||||
|
Synthoids. We believe China is trying to establish the first off-world colonies.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The mission is to prevent this launch without instigating an international conflict. When you accept this
|
||||||
|
mission you will be officially disavowed by the NSA and the national government until after you successfully
|
||||||
|
return. In the event of failure, all of the operation's team members must not let themselves be captured alive.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Hyron": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our intelligence tells us that Fulcrum Technologies is developing a quantum supercomputer using human brains as
|
||||||
|
core processors. This supercomputer is rumored to be able to store vast amounts of data and perform computations
|
||||||
|
unmatched by any other supercomputer on the planet. But more importantly, the use of organic human brains means
|
||||||
|
that the supercomputer may be able to reason abstractly and become self-aware.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
I do not need to remind you why sentient-level AIs pose a serious threat to all of mankind.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The research for this project is being conducted at one of Fulcrum Technologies secret facilities in Aevum,
|
||||||
|
codenamed 'Alpha Ranch'. Infiltrate the compound, delete and destroy the work, and then find and kill the
|
||||||
|
project lead.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Morpheus": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
DreamSense Technologies is an advertising company that uses special technology to transmit their ads into the
|
||||||
|
peoples dreams and subconcious. They do this using broadcast transmitter towers. Based on information from our
|
||||||
|
agents and informants in Chonqging, we have reason to believe that one of the broadcast towers there has been
|
||||||
|
compromised by Synthoids and is being used to spread pro-Synthoid propaganda.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The mission is to destroy this broadcast tower. Speed and stealth are of the upmost important for this.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Ion Storm": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our analysts have uncovered a gathering of MK-VI Synthoids that have taken up residence in the Sector-12 Slums.
|
||||||
|
We don't know if they are rogue Synthoids from the Uprising, but we do know that they have been stockpiling
|
||||||
|
weapons, money, and other resources. This makes them dangerous.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
This is a full-scale assault operation to find and retire all of these Synthoids in the Sector-12 Slums.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Annihilus": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our superiors have ordered us to eradicate everything and everyone in an underground facility located in Aevum.
|
||||||
|
They tell us that the facility houses many dangerous Synthoids and belongs to a terrorist organization called
|
||||||
|
'The Covenant'. We have no prior intelligence about this organization, so you are going in blind.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Ultron": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
OmniTek Incorporated, the original designer and manufacturer of Synthoids, has notified us of a malfunction in
|
||||||
|
their AI design. This malfunction, when triggered, causes MK-VI Synthoids to become radicalized and seek out the
|
||||||
|
destruction of humanity. They say that this bug affects all MK-VI Synthoids, not just the rogue ones from the
|
||||||
|
Uprising.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
OmniTek has also told us they they believe someone has triggered this malfunction in a large group of MK-VI
|
||||||
|
Synthoids, and that these newly-radicalized Synthoids are now amassing in Volhaven to form a terrorist group
|
||||||
|
called Ultron.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Intelligence suggests Ultron is heavily armed and that their members are augmented. We believe Ultron is making
|
||||||
|
moves to take control of and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your task is to find and destroy Ultron.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Centurion": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Throughout all of humanity's history, we have relied on technology to survive, conquer, and progress. Its
|
||||||
|
advancement became our primary goal. And at the peak of human civilization technology turned into power. Global,
|
||||||
|
absolute power.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
It seems that the universe is not without a sense of irony.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Vindictus": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The bits are all around us. The daemons that hold the Node together can manifest themselves in many different
|
||||||
|
ways.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Daedalus": {
|
||||||
|
desc: <> Yesterday we obeyed kings and bent our neck to emperors. Today we kneel only to truth.</>,
|
||||||
|
},
|
||||||
|
};
|
44
src/Bladeburner/data/Contracts.tsx
Normal file
44
src/Bladeburner/data/Contracts.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IContract {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Contracts: {
|
||||||
|
[key: string]: IContract | undefined;
|
||||||
|
} = {
|
||||||
|
Tracking: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Identify and locate Synthoids. This contract involves reconnaissance and information-gathering ONLY. Do NOT
|
||||||
|
engage. Stealth is of the utmost importance.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for whatever
|
||||||
|
city you are currently in.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Bounty Hunter": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successfully completing a Bounty Hunter contract will lower the population in your current city, and will also
|
||||||
|
increase its chaos level.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Retirement: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Hunt down and retire (kill) rogue Synthoids.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successfully completing a Retirement contract will lower the population in your current city, and will also
|
||||||
|
increase its chaos level.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
65
src/Bladeburner/data/GeneralActions.tsx
Normal file
65
src/Bladeburner/data/GeneralActions.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IContract {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GeneralActions: {
|
||||||
|
[key: string]: IContract | undefined;
|
||||||
|
} = {
|
||||||
|
Training: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Improve your abilities at the Bladeburner unit's specialized training center. Doing this gives experience for
|
||||||
|
all combat stats and also increases your max stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
"Field Analysis": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Mine and analyze Synthoid-related data. This improves the Bladeburner's unit intelligence on Synthoid locations
|
||||||
|
and activities. Completing this action will improve the accuracy of your Synthoid population estimated in the
|
||||||
|
current city.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Does NOT require stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
Recruitment: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Attempt to recruit members for your Bladeburner team. These members can help you conduct operations.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Does NOT require stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
Diplomacy: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Improve diplomatic relations with the Synthoid population. Completing this action will reduce the Chaos level in
|
||||||
|
your current city.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Does NOT require stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
"Hyperbolic Regeneration Chamber": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Enter cryogenic stasis using the Bladeburner division's hi-tech Regeneration Chamber. This will slowly heal your
|
||||||
|
wounds and slightly increase your stamina.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
60
src/Bladeburner/data/Operations.tsx
Normal file
60
src/Bladeburner/data/Operations.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IOperation {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Operations: {
|
||||||
|
[key: string]: IOperation | undefined;
|
||||||
|
} = {
|
||||||
|
Investigation: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
As a field agent, investigate and identify Synthoid populations, movements, and operations.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successful Investigation ops will increase the accuracy of your synthoid data.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
You will NOT lose HP from failed Investigation ops.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Undercover Operation": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Conduct undercover operations to identify hidden and underground Synthoid communities and organizations.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successful Undercover ops will increase the accuracy of your synthoid data.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Sting Operation": {
|
||||||
|
desc: <>Conduct a sting operation to bait and capture particularly notorious Synthoid criminals.</>,
|
||||||
|
},
|
||||||
|
Raid: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Lead an assault on a known Synthoid community. Note that there must be an existing Synthoid community in your
|
||||||
|
current city in order for this Operation to be successful.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Stealth Retirement Operation": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Lead a covert operation to retire Synthoids. The objective is to complete the task without drawing any
|
||||||
|
attention. Stealth and discretion are key.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Assassination: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Assassinate Synthoids that have been identified as important, high-profile social and political leaders in the
|
||||||
|
Synthoid communities.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
75
src/Bladeburner/ui/ActionLevel.tsx
Normal file
75
src/Bladeburner/ui/ActionLevel.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { BladeburnerConstants } from "../data/Constants";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
|
||||||
|
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
action: IAction;
|
||||||
|
isActive: boolean;
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ActionLevel({ action, isActive, bladeburner, rerender }: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
|
||||||
|
const canIncrease = action.level < action.maxLevel;
|
||||||
|
const canDecrease = action.level > 1;
|
||||||
|
|
||||||
|
function increaseLevel(): void {
|
||||||
|
if (!canIncrease) return;
|
||||||
|
++action.level;
|
||||||
|
if (isActive) bladeburner.startAction(player, bladeburner.action);
|
||||||
|
rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
function decreaseLevel(): void {
|
||||||
|
if (!canDecrease) return;
|
||||||
|
--action.level;
|
||||||
|
if (isActive) bladeburner.startAction(player, bladeburner.action);
|
||||||
|
rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
|
<Box display="flex">
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<Typography>
|
||||||
|
{action.getSuccessesNeededForNextLevel(BladeburnerConstants.ContractSuccessesPerLevel)} successes needed
|
||||||
|
for next level
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
Level: {action.level} / {action.maxLevel}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<Tooltip
|
||||||
|
disableInteractive
|
||||||
|
title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}
|
||||||
|
>
|
||||||
|
<IconButton disabled={!canIncrease} onClick={increaseLevel}>
|
||||||
|
<ArrowDropUpIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip
|
||||||
|
disableInteractive
|
||||||
|
title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}
|
||||||
|
>
|
||||||
|
<IconButton disabled={!canDecrease} onClick={decreaseLevel}>
|
||||||
|
<ArrowDropDownIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -1,54 +1,45 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React from "react";
|
||||||
import { GeneralActionPage } from "./GeneralActionPage";
|
import { GeneralActionPage } from "./GeneralActionPage";
|
||||||
import { ContractPage } from "./ContractPage";
|
import { ContractPage } from "./ContractPage";
|
||||||
import { OperationPage } from "./OperationPage";
|
import { OperationPage } from "./OperationPage";
|
||||||
import { BlackOpPage } from "./BlackOpPage";
|
import { BlackOpPage } from "./BlackOpPage";
|
||||||
import { SkillPage } from "./SkillPage";
|
import { SkillPage } from "./SkillPage";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
import Tabs from "@mui/material/Tabs";
|
||||||
|
import Tab from "@mui/material/Tab";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AllPages(props: IProps): React.ReactElement {
|
export function AllPages(props: IProps): React.ReactElement {
|
||||||
const [page, setPage] = useState("General");
|
const [value, setValue] = React.useState(0);
|
||||||
const setRerender = useState(false)[1];
|
|
||||||
|
|
||||||
useEffect(() => {
|
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
setValue(tab);
|
||||||
return () => clearInterval(id);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function Header(props: { name: string }): React.ReactElement {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
onClick={() => setPage(props.name)}
|
|
||||||
className={page !== props.name ? "bladeburner-nav-button noselect" : "bladeburner-nav-button-inactive noselect"}
|
|
||||||
>
|
|
||||||
{props.name}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header name={"General"} />
|
<Tabs variant="fullWidth" value={value} onChange={handleChange} aria-label="basic tabs example">
|
||||||
<Header name={"Contracts"} />
|
<Tab label="General" />
|
||||||
<Header name={"Operations"} />
|
<Tab label="Contracts" />
|
||||||
<Header name={"BlackOps"} />
|
<Tab label="Operations" />
|
||||||
<Header name={"Skills"} />
|
<Tab label="BlackOps" />
|
||||||
<div style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Tab label="Skills" />
|
||||||
{page === "General" && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
</Tabs>
|
||||||
{page === "Contracts" && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
<Box sx={{ p: 1 }}>
|
||||||
{page === "Operations" && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
{value === 0 && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
{page === "BlackOps" && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
{value === 1 && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
{page === "Skills" && <SkillPage bladeburner={props.bladeburner} />}
|
{value === 2 && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
</div>
|
{value === 3 && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
<span className="text">
|
{value === 4 && <SkillPage bladeburner={props.bladeburner} />}
|
||||||
{stealthIcon} = This action requires stealth, {killIcon} = This action involves retirement
|
</Box>
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
26
src/Bladeburner/ui/Autolevel.tsx
Normal file
26
src/Bladeburner/ui/Autolevel.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Switch from "@mui/material/Switch";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
action: IAction;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Autolevel(props: IProps): React.ReactElement {
|
||||||
|
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
props.action.autoLevel = event.target.checked;
|
||||||
|
props.rerender();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
|
<Tooltip title={<Typography>Automatically increase operation level when possible</Typography>}>
|
||||||
|
<Typography> Autolevel:</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
<Switch checked={props.action.autoLevel} onChange={onAutolevel} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -2,22 +2,29 @@ import React, { useState } from "react";
|
|||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { ActionTypes } from "../data/ActionTypes";
|
import { ActionTypes } from "../data/ActionTypes";
|
||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
import { TeamSizeButton } from "./TeamSizeButton";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { TeamSizePopup } from "./TeamSizePopup";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { BlackOperation } from "../BlackOperation";
|
||||||
|
import { BlackOperations } from "../data/BlackOperations";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { SuccessChance } from "./SuccessChance";
|
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
import { SuccessChance } from "./SuccessChance";
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: BlackOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BlackOpElem(props: IProps): React.ReactElement {
|
export function BlackOpElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
|
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
|
||||||
if (isCompleted) {
|
if (isCompleted) {
|
||||||
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>;
|
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>;
|
||||||
@ -26,7 +33,6 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
|||||||
const isActive =
|
const isActive =
|
||||||
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
|
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
|
||||||
props.action.name === props.bladeburner.action.name;
|
props.action.name === props.bladeburner.action.name;
|
||||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
@ -34,70 +40,54 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
|||||||
props.bladeburner.actionTimeToComplete,
|
props.bladeburner.actionTimeToComplete,
|
||||||
);
|
);
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = BlackOperations[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes.BlackOperation;
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTeam(): void {
|
|
||||||
const popupId = "bladeburner-operation-set-team-size-popup";
|
|
||||||
createPopup(popupId, TeamSizePopup, {
|
|
||||||
bladeburner: props.bladeburner,
|
|
||||||
action: props.action,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
{isActive ? (
|
{isActive ? (
|
||||||
<>
|
<>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<>
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
|
<p style={{ display: "block" }}>
|
||||||
|
{createProgressBarText({
|
||||||
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<CopyableText value={props.action.name} />
|
<>
|
||||||
|
<CopyableText value={props.action.name} />
|
||||||
|
|
||||||
|
<StartButton
|
||||||
|
bladeburner={props.bladeburner}
|
||||||
|
type={ActionTypes.BlackOperation}
|
||||||
|
name={props.action.name}
|
||||||
|
rerender={rerender}
|
||||||
|
/>
|
||||||
|
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</h2>
|
</Typography>
|
||||||
{isActive ? (
|
|
||||||
<p style={{ display: "block" }}>
|
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<a
|
|
||||||
className={hasReqdRank ? "a-link-button" : "a-link-button-inactive"}
|
|
||||||
style={{ margin: "3px", padding: "3px" }}
|
|
||||||
onClick={onStart}
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
</a>
|
|
||||||
<a onClick={onTeam} style={{ margin: "3px", padding: "3px" }} className="a-link-button">
|
|
||||||
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<p style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
<Typography>{actionData.desc}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<p style={{ display: "block", color: hasReqdRank ? "white" : "red" }}>
|
<Typography color={hasReqdRank ? "primary" : "error"}>
|
||||||
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
||||||
</p>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
Estimated Success Chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||||
{props.action.isStealth ? stealthIcon : <></>}
|
|
||||||
{props.action.isKill ? killIcon : <></>}
|
|
||||||
<br />
|
<br />
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
</pre>
|
</Typography>
|
||||||
</>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,7 @@ export function BlackOpList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{blackops.map((blackop: BlackOperation) => (
|
{blackops.map((blackop: BlackOperation) => (
|
||||||
<li key={blackop.name} className="bladeburner-action">
|
<BlackOpElem key={blackop.name} bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
||||||
<BlackOpElem bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { BlackOpList } from "./BlackOpList";
|
import { BlackOpList } from "./BlackOpList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,7 +12,7 @@ interface IProps {
|
|||||||
export function BlackOpPage(props: IProps): React.ReactElement {
|
export function BlackOpPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>
|
||||||
Black Operations (Black Ops) are special, one-time covert operations. Each Black Op must be unlocked
|
Black Operations (Black Ops) are special, one-time covert operations. Each Black Op must be unlocked
|
||||||
successively by completing the one before it.
|
successively by completing the one before it.
|
||||||
<br />
|
<br />
|
||||||
@ -21,7 +22,7 @@ export function BlackOpPage(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank
|
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank
|
||||||
losses.
|
losses.
|
||||||
</p>
|
</Typography>
|
||||||
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
|
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,42 +1,39 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Stats } from "./Stats";
|
import { Stats } from "./Stats";
|
||||||
import { Console } from "./Console";
|
import { Console } from "./Console";
|
||||||
import { AllPages } from "./AllPages";
|
import { AllPages } from "./AllPages";
|
||||||
|
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
export function BladeburnerRoot(): React.ReactElement {
|
export function BladeburnerRoot(): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
const router = use.Router();
|
const router = use.Router();
|
||||||
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(rerender, 200);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const bladeburner = player.bladeburner;
|
const bladeburner = player.bladeburner;
|
||||||
if (bladeburner === null) return <></>;
|
if (bladeburner === null) return <></>;
|
||||||
return (
|
return (
|
||||||
<div className="bladeburner-container">
|
<Box display="flex" flexDirection="column">
|
||||||
<div style={{ height: "60%", display: "block", position: "relative" }}>
|
<Grid container>
|
||||||
<div
|
<Grid item xs={6}>
|
||||||
style={{
|
|
||||||
height: "100%",
|
|
||||||
width: "30%",
|
|
||||||
display: "inline-block",
|
|
||||||
border: "1px solid white",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stats bladeburner={bladeburner} player={player} router={router} />
|
<Stats bladeburner={bladeburner} player={player} router={router} />
|
||||||
</div>
|
</Grid>
|
||||||
<Console bladeburner={bladeburner} player={player} />
|
<Grid item xs={6}>
|
||||||
</div>
|
<Console bladeburner={bladeburner} player={player} />
|
||||||
<div
|
</Grid>
|
||||||
style={{
|
</Grid>
|
||||||
width: "70%",
|
|
||||||
display: "block",
|
<AllPages bladeburner={bladeburner} player={player} />
|
||||||
border: "1px solid white",
|
</Box>
|
||||||
marginTop: "6px",
|
|
||||||
padding: "6px",
|
|
||||||
position: "relative",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AllPages bladeburner={bladeburner} player={player} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,48 @@ import React, { useState, useRef, useEffect } from "react";
|
|||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import List from "@mui/material/List";
|
||||||
|
import ListItem from "@mui/material/ListItem";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import { Theme } from "@mui/material/styles";
|
||||||
|
import makeStyles from "@mui/styles/makeStyles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
|
||||||
interface ILineProps {
|
interface ILineProps {
|
||||||
content: any;
|
content: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
textfield: {
|
||||||
|
margin: theme.spacing(0),
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
backgroundColor: "#000",
|
||||||
|
},
|
||||||
|
nopadding: {
|
||||||
|
padding: theme.spacing(0),
|
||||||
|
},
|
||||||
|
preformatted: {
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
margin: theme.spacing(0),
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
padding: theme.spacing(0),
|
||||||
|
height: "100%",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
function Line(props: ILineProps): React.ReactElement {
|
function Line(props: ILineProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<ListItem sx={{ p: 0 }}>
|
||||||
<td className="bladeburner-console-line" style={{ color: "var(--my-font-color)", whiteSpace: "pre-wrap" }}>
|
<Typography>{props.content}</Typography>
|
||||||
{props.content}
|
</ListItem>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,15 +53,21 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Console(props: IProps): React.ReactElement {
|
export function Console(props: IProps): React.ReactElement {
|
||||||
const lastRef = useRef<HTMLDivElement>(null);
|
const classes = useStyles();
|
||||||
|
const scrollHook = useRef<HTMLDivElement>(null);
|
||||||
|
const [command, setCommand] = useState("");
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
|
||||||
|
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
setCommand(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
|
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
|
||||||
|
|
||||||
// TODO: Figure out how to actually make the scrolling work correctly.
|
// TODO: Figure out how to actually make the scrolling work correctly.
|
||||||
function scrollToBottom(): void {
|
function scrollToBottom(): void {
|
||||||
if (!lastRef.current) return;
|
if (!scrollHook.current) return;
|
||||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
scrollHook.current.scrollTop = scrollHook.current.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
@ -50,13 +86,11 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const command = event.currentTarget.value;
|
|
||||||
event.currentTarget.value = "";
|
|
||||||
if (command.length > 0) {
|
if (command.length > 0) {
|
||||||
props.bladeburner.postToConsole("> " + command);
|
props.bladeburner.postToConsole("> " + command);
|
||||||
props.bladeburner.executeConsoleCommands(props.player, command);
|
props.bladeburner.executeConsoleCommands(props.player, command);
|
||||||
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
|
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
|
||||||
rerender();
|
setCommand("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,31 +139,35 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={lastRef} className="bladeburner-console-div">
|
<Box height={"60vh"} display={"flex"} alignItems={"stretch"} component={Paper}>
|
||||||
<table className="bladeburner-console-table">
|
<Box>
|
||||||
<tbody>
|
<List sx={{ height: "100%", overflow: "auto" }}>
|
||||||
{/*
|
|
||||||
TODO: optimize this.
|
|
||||||
using `i` as a key here isn't great because it'll re-render everything
|
|
||||||
everytime the console reaches max length.
|
|
||||||
*/}
|
|
||||||
{props.bladeburner.consoleLogs.map((log: any, i: number) => (
|
{props.bladeburner.consoleLogs.map((log: any, i: number) => (
|
||||||
<Line key={i} content={log} />
|
<Line key={i} content={log} />
|
||||||
))}
|
))}
|
||||||
<tr key="input" id="bladeburner-console-input-row" className="bladeburner-console-input-row">
|
<TextField
|
||||||
<td className="bladeburner-console-input-cell">
|
classes={{ root: classes.textfield }}
|
||||||
<pre>{"> "}</pre>
|
autoFocus
|
||||||
<input
|
variant="standard"
|
||||||
autoFocus
|
tabIndex={1}
|
||||||
className="bladeburner-console-input"
|
type="text"
|
||||||
tabIndex={1}
|
value={command}
|
||||||
type="text"
|
onChange={handleCommandChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
InputProps={{
|
||||||
</td>
|
// for players to hook in
|
||||||
</tr>
|
className: classes.input,
|
||||||
</tbody>
|
startAdornment: (
|
||||||
</table>
|
<>
|
||||||
</div>
|
<Typography>> </Typography>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
spellCheck: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</List>
|
||||||
|
<div ref={scrollHook}></div>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,113 +2,78 @@ import React, { useState } from "react";
|
|||||||
import { ActionTypes } from "../data/ActionTypes";
|
import { ActionTypes } from "../data/ActionTypes";
|
||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
import { Contracts } from "../data/Contracts";
|
||||||
import { BladeburnerConstants } from "../data/Constants";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { SuccessChance } from "./SuccessChance";
|
import { SuccessChance } from "./SuccessChance";
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
import { ActionLevel } from "./ActionLevel";
|
||||||
|
import { Autolevel } from "./Autolevel";
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: IAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ContractElem(props: IProps): React.ReactElement {
|
export function ContractElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isActive =
|
const isActive =
|
||||||
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
||||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||||
props.bladeburner.actionTimeToComplete,
|
props.bladeburner.actionTimeToComplete,
|
||||||
);
|
);
|
||||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = Contracts[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes.Contract;
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseLevel(): void {
|
|
||||||
++props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decreaseLevel(): void {
|
|
||||||
--props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
props.action.autoLevel = event.target.checked;
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
{isActive ? (
|
||||||
{isActive ? (
|
<>
|
||||||
<>
|
<Typography>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
</>
|
</Typography>
|
||||||
) : (
|
<Typography>
|
||||||
<CopyableText value={props.action.name} />
|
{createProgressBarText({
|
||||||
)}
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
</h2>
|
})}
|
||||||
{isActive ? (
|
</Typography>
|
||||||
<p style={{ display: "block" }}>
|
</>
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
<CopyableText value={props.action.name} />
|
||||||
Start
|
<StartButton
|
||||||
</a>
|
bladeburner={props.bladeburner}
|
||||||
|
type={ActionTypes.Contract}
|
||||||
|
name={props.action.name}
|
||||||
|
rerender={rerender}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||||
<span className="tooltiptext">
|
|
||||||
{props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.ContractSuccessesPerLevel)} successes needed
|
|
||||||
for next level
|
|
||||||
</span>
|
|
||||||
Level: {props.action.level} / {props.action.maxLevel}
|
|
||||||
</pre>
|
|
||||||
<a
|
|
||||||
onClick={increaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${maxLevel ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↑
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
onClick={decreaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${props.action.level <= 1 ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↓
|
|
||||||
</a>
|
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
{actionData.desc}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||||
{props.action.isStealth ? stealthIcon : <></>}
|
|
||||||
{props.action.isKill ? killIcon : <></>}
|
|
||||||
<br />
|
<br />
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
<br />
|
<br />
|
||||||
@ -117,13 +82,9 @@ export function ContractElem(props: IProps): React.ReactElement {
|
|||||||
Successes: {props.action.successes}
|
Successes: {props.action.successes}
|
||||||
<br />
|
<br />
|
||||||
Failures: {props.action.failures}
|
Failures: {props.action.failures}
|
||||||
</pre>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
<Autolevel rerender={rerender} action={props.action} />
|
||||||
Autolevel:
|
</Paper>
|
||||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
|
||||||
</label>
|
|
||||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ export function ContractList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{names.map((name: string) => (
|
{names.map((name: string) => (
|
||||||
<li key={name} className="bladeburner-action">
|
<ContractElem key={name} bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
||||||
<ContractElem bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { ContractList } from "./ContractList";
|
import { ContractList } from "./ContractList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,14 +12,14 @@ interface IProps {
|
|||||||
export function ContractPage(props: IProps): React.ReactElement {
|
export function ContractPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>
|
||||||
Complete contracts in order to increase your Bladeburner rank and earn money. Failing a contract will cause you
|
Complete contracts in order to increase your Bladeburner rank and earn money. Failing a contract will cause you
|
||||||
to lose HP, which can lead to hospitalization.
|
to lose HP, which can lead to hospitalization.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more
|
You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more
|
||||||
difficult, but grant more rank, experience, and money.
|
difficult, but grant more rank, experience, and money.
|
||||||
</p>
|
</Typography>
|
||||||
<ContractList bladeburner={props.bladeburner} player={props.player} />
|
<ContractList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,17 +3,28 @@ import { ActionTypes } from "../data/ActionTypes";
|
|||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
|
import { GeneralActions } from "../data/GeneralActions";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: IAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GeneralActionElem(props: IProps): React.ReactElement {
|
export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isActive = props.action.name === props.bladeburner.action.name;
|
const isActive = props.action.name === props.bladeburner.action.name;
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||||
@ -37,44 +48,44 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
|||||||
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1))
|
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1))
|
||||||
: -1;
|
: -1;
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = GeneralActions[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes[props.action.name as string];
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
{isActive ? (
|
||||||
{isActive ? (
|
<>
|
||||||
<>
|
<Typography>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
</>
|
</Typography>
|
||||||
) : (
|
<Typography>
|
||||||
<CopyableText value={props.action.name} />
|
{createProgressBarText({
|
||||||
)}
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
</h2>
|
})}
|
||||||
{isActive ? (
|
</Typography>
|
||||||
<p style={{ display: "block" }}>
|
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
|
||||||
Start
|
|
||||||
</a>
|
|
||||||
</>
|
</>
|
||||||
|
) : (
|
||||||
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
|
<Typography>
|
||||||
|
<CopyableText value={props.action.name} />
|
||||||
|
</Typography>
|
||||||
|
<StartButton
|
||||||
|
bladeburner={props.bladeburner}
|
||||||
|
type={ActionTypes[props.action.name as string]}
|
||||||
|
name={props.action.name}
|
||||||
|
rerender={rerender}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }}></pre>
|
<Typography>{actionData.desc}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
{successChance !== -1 && (
|
{successChance !== -1 && (
|
||||||
<>
|
<>
|
||||||
@ -82,7 +93,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
|||||||
Estimated success chance: {formatNumber(successChance * 100, 1)}%
|
Estimated success chance: {formatNumber(successChance * 100, 1)}%
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</pre>
|
</Typography>
|
||||||
</>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,7 @@ export function GeneralActionList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{actions.map((action: Action) => (
|
{actions.map((action: Action) => (
|
||||||
<li key={action.name} className="bladeburner-action">
|
<GeneralActionElem key={action.name} bladeburner={props.bladeburner} action={action} player={props.player} />
|
||||||
<GeneralActionElem bladeburner={props.bladeburner} action={action} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { GeneralActionList } from "./GeneralActionList";
|
import { GeneralActionList } from "./GeneralActionList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,10 +12,7 @@ interface IProps {
|
|||||||
export function GeneralActionPage(props: IProps): React.ReactElement {
|
export function GeneralActionPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>These are generic actions that will assist you in your Bladeburner duties.</Typography>
|
||||||
These are generic actions that will assist you in your Bladeburner duties. They will not affect your Bladeburner
|
|
||||||
rank in any way.
|
|
||||||
</p>
|
|
||||||
<GeneralActionList bladeburner={props.bladeburner} player={props.player} />
|
<GeneralActionList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
13
src/Bladeburner/ui/KillIcon.tsx
Normal file
13
src/Bladeburner/ui/KillIcon.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { killIcon } from "../data/Icons";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
|
||||||
|
export function KillIcon(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<Tooltip disableInteractive title={<Typography>This action involves retirement</Typography>}>
|
||||||
|
{killIcon}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
@ -2,127 +2,81 @@ import React, { useState } from "react";
|
|||||||
import { ActionTypes } from "../data/ActionTypes";
|
import { ActionTypes } from "../data/ActionTypes";
|
||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
|
||||||
import { BladeburnerConstants } from "../data/Constants";
|
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { TeamSizePopup } from "./TeamSizePopup";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { SuccessChance } from "./SuccessChance";
|
import { SuccessChance } from "./SuccessChance";
|
||||||
|
import { ActionLevel } from "./ActionLevel";
|
||||||
|
import { Autolevel } from "./Autolevel";
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
import { TeamSizeButton } from "./TeamSizeButton";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { Operation } from "../Operation";
|
||||||
|
import { Operations } from "../data/Operations";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: Operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OperationElem(props: IProps): React.ReactElement {
|
export function OperationElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isActive =
|
const isActive =
|
||||||
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
||||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||||
props.bladeburner.actionTimeToComplete,
|
props.bladeburner.actionTimeToComplete,
|
||||||
);
|
);
|
||||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = Operations[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes.Operation;
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTeam(): void {
|
|
||||||
const popupId = "bladeburner-operation-set-team-size-popup";
|
|
||||||
createPopup(popupId, TeamSizePopup, {
|
|
||||||
bladeburner: props.bladeburner,
|
|
||||||
action: props.action,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseLevel(): void {
|
|
||||||
++props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decreaseLevel(): void {
|
|
||||||
--props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
props.action.autoLevel = event.target.checked;
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
{isActive ? (
|
||||||
{isActive ? (
|
<>
|
||||||
<>
|
<Typography>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
</>
|
</Typography>
|
||||||
) : (
|
<Typography>
|
||||||
<CopyableText value={props.action.name} />
|
{createProgressBarText({
|
||||||
)}
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
</h2>
|
})}
|
||||||
{isActive ? (
|
</Typography>
|
||||||
<p style={{ display: "block" }}>
|
</>
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
<CopyableText value={props.action.name} />
|
||||||
Start
|
<StartButton
|
||||||
</a>
|
bladeburner={props.bladeburner}
|
||||||
<a onClick={onTeam} style={{ margin: "3px", padding: "3px" }} className="a-link-button">
|
type={ActionTypes.Operation}
|
||||||
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
name={props.action.name}
|
||||||
</a>
|
rerender={rerender}
|
||||||
|
/>
|
||||||
|
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
|
||||||
<span className="tooltiptext">
|
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||||
{props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.OperationSuccessesPerLevel)} successes
|
|
||||||
needed for next level
|
|
||||||
</span>
|
|
||||||
Level: {props.action.level} / {props.action.maxLevel}
|
|
||||||
</pre>
|
|
||||||
<a
|
|
||||||
onClick={increaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${maxLevel ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↑
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
onClick={decreaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${props.action.level <= 1 ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↓
|
|
||||||
</a>
|
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
{actionData.desc}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||||
{props.action.isStealth ? stealthIcon : <></>}
|
|
||||||
{props.action.isKill ? killIcon : <></>}
|
|
||||||
<br />
|
<br />
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
<br />
|
<br />
|
||||||
@ -131,13 +85,9 @@ export function OperationElem(props: IProps): React.ReactElement {
|
|||||||
Successes: {props.action.successes}
|
Successes: {props.action.successes}
|
||||||
<br />
|
<br />
|
||||||
Failures: {props.action.failures}
|
Failures: {props.action.failures}
|
||||||
</pre>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
<Autolevel rerender={rerender} action={props.action} />
|
||||||
Autolevel:
|
</Paper>
|
||||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
|
||||||
</label>
|
|
||||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ export function OperationList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{names.map((name: string) => (
|
{names.map((name: string) => (
|
||||||
<li key={name} className="bladeburner-action">
|
<OperationElem key={name} bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
||||||
<OperationElem bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { OperationList } from "./OperationList";
|
import { OperationList } from "./OperationList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,7 +12,7 @@ interface IProps {
|
|||||||
export function OperationPage(props: IProps): React.ReactElement {
|
export function OperationPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>
|
||||||
Carry out operations for the Bladeburner division. Failing an operation will reduce your Bladeburner rank. It
|
Carry out operations for the Bladeburner division. Failing an operation will reduce your Bladeburner rank. It
|
||||||
will also cause you to lose HP, which can lead to hospitalization. In general, operations are harder and more
|
will also cause you to lose HP, which can lead to hospitalization. In general, operations are harder and more
|
||||||
punishing than contracts, but are also more rewarding.
|
punishing than contracts, but are also more rewarding.
|
||||||
@ -27,7 +28,7 @@ export function OperationPage(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
You can unlock higher-level operations by successfully completing them. Higher-level operations are more
|
You can unlock higher-level operations by successfully completing them. Higher-level operations are more
|
||||||
difficult, but grant more rank and experience.
|
difficult, but grant more rank and experience.
|
||||||
</p>
|
</Typography>
|
||||||
<OperationList bladeburner={props.bladeburner} player={props.player} />
|
<OperationList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,13 @@ import { CopyableText } from "../../ui/React/CopyableText";
|
|||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
skill: any;
|
skill: any;
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -28,26 +35,30 @@ export function SkillElem(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
<CopyableText value={props.skill.name} />
|
<Typography>
|
||||||
</h2>
|
<CopyableText value={props.skill.name} />
|
||||||
<a
|
</Typography>
|
||||||
onClick={onClick}
|
{!canLevel || maxLvl ? (
|
||||||
style={{ display: "inline-block", margin: "3px", padding: "3px" }}
|
<IconButton disabled>
|
||||||
className={canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive"}
|
<CloseIcon />
|
||||||
>
|
</IconButton>
|
||||||
Level
|
) : (
|
||||||
</a>
|
<IconButton onClick={onClick}>
|
||||||
|
<AddIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<p style={{ display: "block" }}>Level: {currentLevel}</p>
|
<Typography>Level: {currentLevel}</Typography>
|
||||||
{maxLvl ? (
|
{maxLvl ? (
|
||||||
<p style={{ color: "red", display: "block" }}>MAX LEVEL</p>
|
<Typography>MAX LEVEL</Typography>
|
||||||
) : (
|
) : (
|
||||||
<p style={{ display: "block" }}>Skill Points required: {formatNumber(pointCost, 0)}</p>
|
<Typography>Skill Points required: {formatNumber(pointCost, 0)}</Typography>
|
||||||
)}
|
)}
|
||||||
<p style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.skill.desc }} />
|
<Typography>{props.skill.desc}</Typography>
|
||||||
</>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,7 @@ export function SkillList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.keys(Skills).map((skill: string) => (
|
{Object.keys(Skills).map((skill: string) => (
|
||||||
<li key={skill} className="bladeburner-action">
|
<SkillElem key={skill} bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
||||||
<SkillElem bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,8 @@ import { SkillList } from "./SkillList";
|
|||||||
import { BladeburnerConstants } from "../data/Constants";
|
import { BladeburnerConstants } from "../data/Constants";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
}
|
}
|
||||||
@ -18,45 +19,48 @@ export function SkillPage(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>
|
<Typography>
|
||||||
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
|
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
|
||||||
</p>
|
</Typography>
|
||||||
<p>
|
<Typography>
|
||||||
You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
|
You will gain one skill point every{" "}
|
||||||
|
{BladeburnerConstants.RanksPerSkillPoint * BitNodeMultipliers.BladeburnerSkillCost} ranks.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Note that when upgrading a skill, the benefit for that skill is additive. However, the effects of different
|
Note that when upgrading a skill, the benefit for that skill is additive. However, the effects of different
|
||||||
skills with each other is multiplicative.
|
skills with each other is multiplicative.
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
{valid(mults["successChanceAll"]) && <p>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</p>}
|
{valid(mults["successChanceAll"]) && (
|
||||||
|
<Typography>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</Typography>
|
||||||
|
)}
|
||||||
{valid(mults["successChanceStealth"]) && (
|
{valid(mults["successChanceStealth"]) && (
|
||||||
<p>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</p>
|
<Typography>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceKill"]) && (
|
{valid(mults["successChanceKill"]) && (
|
||||||
<p>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}</p>
|
<Typography>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceContract"]) && (
|
{valid(mults["successChanceContract"]) && (
|
||||||
<p>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</p>
|
<Typography>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceOperation"]) && (
|
{valid(mults["successChanceOperation"]) && (
|
||||||
<p>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}</p>
|
<Typography>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceEstimate"]) && (
|
{valid(mults["successChanceEstimate"]) && (
|
||||||
<p>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}</p>
|
<Typography>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["actionTime"]) && <p>Action Time: x{formatNumber(mults["actionTime"], 3)}</p>}
|
{valid(mults["actionTime"]) && <Typography>Action Time: x{formatNumber(mults["actionTime"], 3)}</Typography>}
|
||||||
{valid(mults["effHack"]) && <p>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</p>}
|
{valid(mults["effHack"]) && <Typography>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</Typography>}
|
||||||
{valid(mults["effStr"]) && <p>Strength: x{formatNumber(mults["effStr"], 3)}</p>}
|
{valid(mults["effStr"]) && <Typography>Strength: x{formatNumber(mults["effStr"], 3)}</Typography>}
|
||||||
{valid(mults["effDef"]) && <p>Defense: x{formatNumber(mults["effDef"], 3)}</p>}
|
{valid(mults["effDef"]) && <Typography>Defense: x{formatNumber(mults["effDef"], 3)}</Typography>}
|
||||||
{valid(mults["effDex"]) && <p>Dexterity: x{formatNumber(mults["effDex"], 3)}</p>}
|
{valid(mults["effDex"]) && <Typography>Dexterity: x{formatNumber(mults["effDex"], 3)}</Typography>}
|
||||||
{valid(mults["effAgi"]) && <p>Agility: x{formatNumber(mults["effAgi"], 3)}</p>}
|
{valid(mults["effAgi"]) && <Typography>Agility: x{formatNumber(mults["effAgi"], 3)}</Typography>}
|
||||||
{valid(mults["effCha"]) && <p>Charisma: x{formatNumber(mults["effCha"], 3)}</p>}
|
{valid(mults["effCha"]) && <Typography>Charisma: x{formatNumber(mults["effCha"], 3)}</Typography>}
|
||||||
{valid(mults["effInt"]) && <p>Intelligence: x{formatNumber(mults["effInt"], 3)}</p>}
|
{valid(mults["effInt"]) && <Typography>Intelligence: x{formatNumber(mults["effInt"], 3)}</Typography>}
|
||||||
{valid(mults["stamina"]) && <p>Stamina: x{formatNumber(mults["stamina"], 3)}</p>}
|
{valid(mults["stamina"]) && <Typography>Stamina: x{formatNumber(mults["stamina"], 3)}</Typography>}
|
||||||
{valid(mults["money"]) && <p>Contract Money: x{formatNumber(mults["money"], 3)}</p>}
|
{valid(mults["money"]) && <Typography>Contract Money: x{formatNumber(mults["money"], 3)}</Typography>}
|
||||||
{valid(mults["expGain"]) && <p>Exp Gain: x{formatNumber(mults["expGain"], 3)}</p>}
|
{valid(mults["expGain"]) && <Typography>Exp Gain: x{formatNumber(mults["expGain"], 3)}</Typography>}
|
||||||
<br />
|
<br />
|
||||||
<SkillList bladeburner={props.bladeburner} onUpgrade={() => setRerender((old) => !old)} />
|
<SkillList bladeburner={props.bladeburner} onUpgrade={() => setRerender((old) => !old)} />
|
||||||
</>
|
</>
|
||||||
|
44
src/Bladeburner/ui/StartButton.tsx
Normal file
44
src/Bladeburner/ui/StartButton.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { BlackOperation } from "../BlackOperation";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
type: number;
|
||||||
|
name: string;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
export function StartButton(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const action = props.bladeburner.getActionObject({ name: props.name, type: props.type });
|
||||||
|
if (action == null) {
|
||||||
|
throw new Error("Failed to get Operation Object for: " + props.name);
|
||||||
|
}
|
||||||
|
let disabled = false;
|
||||||
|
if (action.count < 1) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
if (props.name === "Raid" && props.bladeburner.getCurrentCity().comms === 0) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action instanceof BlackOperation && props.bladeburner.rank < action.reqdRank) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
function onStart(): void {
|
||||||
|
if (disabled) return;
|
||||||
|
props.bladeburner.action.type = props.type;
|
||||||
|
props.bladeburner.action.name = props.name;
|
||||||
|
props.bladeburner.startAction(player, props.bladeburner.action);
|
||||||
|
props.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button sx={{ mx: 1 }} disabled={disabled} onClick={onStart}>
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
@ -5,14 +5,17 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
|
|||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { StatsTable } from "../../ui/React/StatsTable";
|
import { StatsTable } from "../../ui/React/StatsTable";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { Factions } from "../../Faction/Factions";
|
import { Factions } from "../../Faction/Factions";
|
||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
import { joinFaction } from "../../Faction/FactionHelpers";
|
import { joinFaction } from "../../Faction/FactionHelpers";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
import { TravelPopup } from "./TravelPopup";
|
import { TravelModal } from "./TravelModal";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -21,131 +24,146 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Stats(props: IProps): React.ReactElement {
|
export function Stats(props: IProps): React.ReactElement {
|
||||||
|
const [travelOpen, setTravelOpen] = useState(false);
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
|
||||||
|
const inFaction = props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function openStaminaHelp(): void {
|
|
||||||
dialogBoxCreate(
|
|
||||||
"Performing actions will use up your stamina.<br><br>" +
|
|
||||||
"Your max stamina is determined primarily by your agility stat.<br><br>" +
|
|
||||||
"Your stamina gain rate is determined by both your agility and your " +
|
|
||||||
"max stamina. Higher max stamina leads to a higher gain rate.<br><br>" +
|
|
||||||
"Once your " +
|
|
||||||
"stamina falls below 50% of its max value, it begins to negatively " +
|
|
||||||
"affect the success rate of your contracts/operations. This penalty " +
|
|
||||||
"is shown in the overview panel. If the penalty is 15%, then this means " +
|
|
||||||
"your success rate would be multipled by 85% (100 - 15).<br><br>" +
|
|
||||||
"Your max stamina and stamina gain rate can also be increased by " +
|
|
||||||
"training, or through skills and Augmentation upgrades.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openPopulationHelp(): void {
|
|
||||||
dialogBoxCreate(
|
|
||||||
"The success rate of your contracts/operations depends on " +
|
|
||||||
"the population of Synthoids in your current city. " +
|
|
||||||
"The success rate that is shown to you is only an estimate, " +
|
|
||||||
"and it is based on your Synthoid population estimate.<br><br>" +
|
|
||||||
"Therefore, it is important that this Synthoid population estimate " +
|
|
||||||
"is accurate so that you have a better idea of your " +
|
|
||||||
"success rate for contracts/operations. Certain " +
|
|
||||||
"actions will increase the accuracy of your population " +
|
|
||||||
"estimate.<br><br>" +
|
|
||||||
"The Synthoid populations of cities can change due to your " +
|
|
||||||
"actions or random events. If random events occur, they will " +
|
|
||||||
"be logged in the Bladeburner Console.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openTravel(): void {
|
|
||||||
const popupId = "bladeburner-travel-popup";
|
|
||||||
createPopup(popupId, TravelPopup, {
|
|
||||||
bladeburner: props.bladeburner,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openFaction(): void {
|
function openFaction(): void {
|
||||||
|
if (!inFaction) return;
|
||||||
const faction = Factions["Bladeburners"];
|
const faction = Factions["Bladeburners"];
|
||||||
if (faction.isMember) {
|
if (!faction.isMember) {
|
||||||
props.router.toFaction(faction);
|
joinFaction(faction);
|
||||||
} else {
|
|
||||||
if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) {
|
|
||||||
joinFaction(faction);
|
|
||||||
dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction");
|
|
||||||
} else {
|
|
||||||
dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
props.router.toFaction(faction);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ p: 1 }}>
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
Rank: {formatNumber(props.bladeburner.rank, 2)}
|
<Tooltip disableInteractive title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
||||||
<span className="tooltiptext">Your rank within the Bladeburner division.</span>
|
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
||||||
</p>
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<Box display="flex">
|
||||||
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
<Tooltip
|
||||||
</p>
|
disableInteractive
|
||||||
<div className="help-tip" onClick={openStaminaHelp}>
|
title={
|
||||||
?
|
<Typography>
|
||||||
</div>
|
Performing actions will use up your stamina.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your max stamina is determined primarily by your agility stat.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your stamina gain rate is determined by both your agility and your max stamina. Higher max stamina leads
|
||||||
|
to a higher gain rate.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Once your stamina falls below 50% of its max value, it begins to negatively affect the success rate of
|
||||||
|
your contracts/operations. This penalty is shown in the overview panel. If the penalty is 15%, then this
|
||||||
|
means your success rate would be multipled by 85% (100 - 15).
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your max stamina and stamina gain rate can also be increased by training, or through skills and
|
||||||
|
Augmentation upgrades.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p>Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%</p>
|
<Typography>
|
||||||
|
Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%
|
||||||
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<p>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</p>
|
<Typography>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</Typography>
|
||||||
<p>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</p>
|
<Typography>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<p>Num Times Hospitalized: {props.bladeburner.numHosp}</p>
|
<Typography>Num Times Hospitalized: {props.bladeburner.numHosp}</Typography>
|
||||||
<p>
|
<Typography>
|
||||||
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
||||||
</p>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<p>Current City: {props.bladeburner.city}</p>
|
<Typography>Current City: {props.bladeburner.city}</Typography>
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
<Tooltip
|
||||||
<span className="tooltiptext">
|
disableInteractive
|
||||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city.
|
title={
|
||||||
</span>
|
<Typography>
|
||||||
</p>
|
This is your Bladeburner division's estimate of how many Synthoids exist in your current city. An accurate
|
||||||
<div className="help-tip" onClick={openPopulationHelp}>
|
population count increases success rate estimates.
|
||||||
?
|
</Typography>
|
||||||
</div>
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
<Tooltip
|
||||||
<span className="tooltiptext">
|
disableInteractive
|
||||||
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
title={
|
||||||
</span>
|
<Typography>
|
||||||
</p>
|
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}
|
<Tooltip
|
||||||
<span className="tooltiptext">
|
disableInteractive
|
||||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a chaos
|
title={
|
||||||
level can make contracts and operations harder.
|
<Typography>
|
||||||
</span>
|
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a
|
||||||
</p>
|
chaos level can make contracts and operations harder.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
{(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000 > 15000 && (
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<>
|
||||||
Bonus time:{" "}
|
<Box display="flex">
|
||||||
{convertTimeMsToTimeElapsedString(
|
<Tooltip
|
||||||
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
disableInteractive
|
||||||
)}
|
title={
|
||||||
<br />
|
<Typography>
|
||||||
<span className="tooltiptext">
|
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by
|
||||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser).
|
browser). Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||||
Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
</Typography>
|
||||||
</span>
|
}
|
||||||
</p>
|
>
|
||||||
<p>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</p>
|
<Typography>
|
||||||
|
Bonus time:{" "}
|
||||||
|
{convertTimeMsToTimeElapsedString(
|
||||||
|
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<StatsTable
|
<StatsTable
|
||||||
rows={[
|
rows={[
|
||||||
@ -156,17 +174,15 @@ export function Stats(props: IProps): React.ReactElement {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<a onClick={openTravel} className="a-link-button" style={{ display: "inline-block" }}>
|
<Button onClick={() => setTravelOpen(true)}>Travel</Button>
|
||||||
Travel
|
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
||||||
</a>
|
<span>
|
||||||
<a onClick={openFaction} className="a-link-button tooltip" style={{ display: "inline-block" }}>
|
<Button disabled={!inFaction} onClick={openFaction}>
|
||||||
<span className="tooltiptext">
|
Faction
|
||||||
Apply to the Bladeburner Faction, or go to the faction page if you are already a member
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
Faction
|
</Tooltip>
|
||||||
</a>
|
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
||||||
<br />
|
</Paper>
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
13
src/Bladeburner/ui/StealthIcon.tsx
Normal file
13
src/Bladeburner/ui/StealthIcon.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { stealthIcon } from "../data/Icons";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
|
||||||
|
export function StealthIcon(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<Tooltip disableInteractive title={<Typography>This action involves stealth</Typography>}>
|
||||||
|
{stealthIcon}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
@ -1,18 +1,33 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
|
import { StealthIcon } from "./StealthIcon";
|
||||||
|
import { KillIcon } from "./KillIcon";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
chance: number[];
|
bladeburner: IBladeburner;
|
||||||
|
action: IAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SuccessChance(props: IProps): React.ReactElement {
|
export function SuccessChance(props: IProps): React.ReactElement {
|
||||||
if (props.chance[0] === props.chance[1]) {
|
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||||
return <>{formatNumber(props.chance[0] * 100, 1)}%</>;
|
|
||||||
|
let chance = <></>;
|
||||||
|
if (estimatedSuccessChance[0] === estimatedSuccessChance[1]) {
|
||||||
|
chance = <>{formatNumber(estimatedSuccessChance[0] * 100, 1)}%</>;
|
||||||
|
} else {
|
||||||
|
chance = (
|
||||||
|
<>
|
||||||
|
{formatNumber(estimatedSuccessChance[0] * 100, 1)}% ~ {formatNumber(estimatedSuccessChance[1] * 100, 1)}%
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{formatNumber(props.chance[0] * 100, 1)}% ~ {formatNumber(props.chance[1] * 100, 1)}%
|
Estimated success chance: {chance} {props.action.isStealth ? <StealthIcon /> : <></>}
|
||||||
|
{props.action.isKill ? <KillIcon /> : <></>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
22
src/Bladeburner/ui/TeamSizeButton.tsx
Normal file
22
src/Bladeburner/ui/TeamSizeButton.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Operation } from "../Operation";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { TeamSizeModal } from "./TeamSizeModal";
|
||||||
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
interface IProps {
|
||||||
|
action: Operation;
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
}
|
||||||
|
export function TeamSizeButton(props: IProps): React.ReactElement {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button disabled={props.bladeburner.teamSize === 0} onClick={() => setOpen(true)}>
|
||||||
|
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
||||||
|
</Button>
|
||||||
|
<TeamSizeModal open={open} onClose={() => setOpen(false)} action={props.action} bladeburner={props.bladeburner} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,16 +1,20 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { Action } from "../Action";
|
import { Action } from "../Action";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
action: Action;
|
action: Action;
|
||||||
popupId: string;
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TeamSizePopup(props: IProps): React.ReactElement {
|
export function TeamSizeModal(props: IProps): React.ReactElement {
|
||||||
const [teamSize, setTeamSize] = useState<number | undefined>();
|
const [teamSize, setTeamSize] = useState<number | undefined>();
|
||||||
|
|
||||||
function confirmTeamSize(): void {
|
function confirmTeamSize(): void {
|
||||||
@ -21,25 +25,32 @@ export function TeamSizePopup(props: IProps): React.ReactElement {
|
|||||||
} else {
|
} else {
|
||||||
props.action.teamCount = num;
|
props.action.teamCount = num;
|
||||||
}
|
}
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTeamSize(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
const x = parseFloat(event.target.value);
|
||||||
|
if (x > props.bladeburner.teamSize) setTeamSize(props.bladeburner.teamSize);
|
||||||
|
else setTeamSize(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<p>
|
<Typography>
|
||||||
Enter the amount of team members you would like to take on this Op. If you do not have the specified number of
|
Enter the amount of team members you would like to take on this Op. If you do not have the specified number of
|
||||||
team members, then as many as possible will be used. Note that team members may be lost during operations.
|
team members, then as many as possible will be used. Note that team members may be lost during operations.
|
||||||
</p>
|
</Typography>
|
||||||
<input
|
<TextField
|
||||||
autoFocus
|
autoFocus
|
||||||
|
variant="standard"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="Team size"
|
placeholder="Team size"
|
||||||
className="text-input"
|
value={teamSize}
|
||||||
onChange={(event) => setTeamSize(parseFloat(event.target.value))}
|
onChange={onTeamSize}
|
||||||
/>
|
/>
|
||||||
<a className="a-link-button" onClick={confirmTeamSize}>
|
<Button sx={{ mx: 2 }} onClick={confirmTeamSize}>
|
||||||
Confirm
|
Confirm
|
||||||
</a>
|
</Button>
|
||||||
</>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
41
src/Bladeburner/ui/TravelModal.tsx
Normal file
41
src/Bladeburner/ui/TravelModal.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { WorldMap } from "../../ui/React/WorldMap";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import { CityName } from "../../Locations/data/CityNames";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TravelModal(props: IProps): React.ReactElement {
|
||||||
|
function travel(city: string): void {
|
||||||
|
props.bladeburner.city = city;
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<>
|
||||||
|
<Typography>
|
||||||
|
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.
|
||||||
|
</Typography>
|
||||||
|
{Settings.DisableASCIIArt ? (
|
||||||
|
Object.values(CityName).map((city: CityName) => (
|
||||||
|
<Button key={city} onClick={() => travel(city)}>
|
||||||
|
{city}
|
||||||
|
</Button>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<WorldMap currentCity={props.bladeburner.city as CityName} onTravel={(city: CityName) => travel(city)} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
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;
|
|
||||||
popupId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TravelPopup(props: IProps): React.ReactElement {
|
|
||||||
function travel(city: string): void {
|
|
||||||
props.bladeburner.city = city;
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
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>
|
|
||||||
{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)} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -3,7 +3,7 @@
|
|||||||
* This is the component for displaying a single faction's UI, not the list of all
|
* This is the component for displaying a single faction's UI, not the list of all
|
||||||
* accessible factions
|
* accessible factions
|
||||||
*/
|
*/
|
||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { AugmentationsPage } from "./AugmentationsPage";
|
import { AugmentationsPage } from "./AugmentationsPage";
|
||||||
import { DonateOption } from "./DonateOption";
|
import { DonateOption } from "./DonateOption";
|
||||||
@ -71,6 +71,12 @@ export function FactionRoot(props: IProps): React.ReactElement {
|
|||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
setRerender((old) => !old);
|
setRerender((old) => !old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(rerender, 200);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const faction = props.faction;
|
const faction = props.faction;
|
||||||
|
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
text: string;
|
text: string;
|
||||||
@ -34,10 +35,10 @@ export function CinematicLine(props: IProps): React.ReactElement {
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<pre>
|
<Typography>
|
||||||
{props.text.slice(0, length)}
|
{props.text.slice(0, length)}
|
||||||
{!done && <span>█</span>}
|
{!done && <span>█</span>}
|
||||||
</pre>
|
</Typography>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { CinematicLine } from "./CinematicLine";
|
import { CinematicLine } from "./CinematicLine";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
lines: string[];
|
lines: string[];
|
||||||
@ -24,14 +26,10 @@ export function CinematicText(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{props.lines.slice(0, i).map((line, i) => (
|
{props.lines.slice(0, i).map((line, i) => (
|
||||||
<pre key={i}>{line}</pre>
|
<Typography key={i}>{line}</Typography>
|
||||||
))}
|
))}
|
||||||
{props.lines.length > i && <CinematicLine key={i} text={props.lines[i]} onDone={advance} />}
|
{props.lines.length > i && <CinematicLine key={i} text={props.lines[i]} onDone={advance} />}
|
||||||
{!props.auto && props.onDone && done && (
|
{!props.auto && props.onDone && done && <Button onClick={props.onDone}>Continue ...</Button>}
|
||||||
<button className="std-button" onClick={props.onDone}>
|
|
||||||
Continue ...
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -261,6 +261,16 @@ export function refreshTheme(): void {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MuiTab: {
|
||||||
|
styleOverrides: {
|
||||||
|
textColorPrimary: {
|
||||||
|
color: Settings.theme.secondary,
|
||||||
|
"&.Mui-selected": {
|
||||||
|
color: Settings.theme.primary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user