mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-28 00:47:33 +01:00
commit
af02fe992a
@ -19,12 +19,13 @@
|
|||||||
.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 $hacker-green;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 11; /* Sit on top of the container */
|
z-index: 11; /* 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 {
|
||||||
@ -89,13 +90,13 @@
|
|||||||
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 $hacker-green;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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 $hacker-green;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: row nowrap;
|
flex: row nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
56
dist/vendor.bundle.js
vendored
56
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
12
index.html
12
index.html
@ -38,17 +38,7 @@
|
|||||||
|
|
||||||
<link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="main.css" rel="stylesheet"></head>
|
<link rel="shortcut icon" href="favicon.ico"><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>
|
|
||||||
|
|
||||||
<!-- Status text -->
|
|
||||||
<div id="status-text-container">
|
|
||||||
<p id="status-text"></p>
|
|
||||||
</div>
|
|
||||||
</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 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
@ -8,7 +8,7 @@ import { Factions } from "../Faction/Factions";
|
|||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
import { numeralWrapper } from "../ui/numeralFormat";
|
||||||
import { Money } from "../ui/React/Money";
|
import { Money } from "../ui/React/Money";
|
||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
export interface IConstructorParams {
|
export interface IConstructorParams {
|
||||||
info: string | JSX.Element;
|
info: string | JSX.Element;
|
||||||
|
@ -11,8 +11,8 @@ import { prestigeAugmentation } from "../Prestige";
|
|||||||
import { Programs } from "../Programs/Programs";
|
import { Programs } from "../Programs/Programs";
|
||||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { clearObject } from "../../utils/helpers/clearObject";
|
import { clearObject } from "../utils/helpers/clearObject";
|
||||||
|
|
||||||
import { WHRNG } from "../Casino/RNG";
|
import { WHRNG } from "../Casino/RNG";
|
||||||
|
|
||||||
@ -2044,6 +2044,28 @@ function initAugmentations(): void {
|
|||||||
}
|
}
|
||||||
AddToAugmentations(SNA);
|
AddToAugmentations(SNA);
|
||||||
|
|
||||||
|
const NeuroreceptorManager = new Augmentation({
|
||||||
|
name: AugmentationNames.NeuroreceptorManager,
|
||||||
|
repCost: 0.75e5,
|
||||||
|
moneyCost: 5.5e8,
|
||||||
|
info:
|
||||||
|
"A brain implant carefully assembled around the synapses, which " +
|
||||||
|
"micromanages the activity and levels of various neuroreceptor " +
|
||||||
|
"chemicals and modulates electrical acvitiy to optimize concentration, " +
|
||||||
|
"allowing the user to multitask much more effectively.",
|
||||||
|
stats: (
|
||||||
|
<>
|
||||||
|
This augmentation removes the penalty for not focusing on actions such as working in a job or working for a
|
||||||
|
faction.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
NeuroreceptorManager.addToFactions(["Tian Di Hui"]);
|
||||||
|
if (augmentationExists(AugmentationNames.NeuroreceptorManager)) {
|
||||||
|
delete Augmentations[AugmentationNames.NeuroreceptorManager];
|
||||||
|
}
|
||||||
|
AddToAugmentations(NeuroreceptorManager);
|
||||||
|
|
||||||
// Special Bladeburner Augmentations
|
// Special Bladeburner Augmentations
|
||||||
const BladeburnersFactionName = "Bladeburners";
|
const BladeburnersFactionName = "Bladeburners";
|
||||||
if (factionExists(BladeburnersFactionName)) {
|
if (factionExists(BladeburnersFactionName)) {
|
||||||
|
@ -41,6 +41,7 @@ export const AugmentationNames: IMap<string> = {
|
|||||||
CranialSignalProcessorsG4: "Cranial Signal Processors - Gen IV",
|
CranialSignalProcessorsG4: "Cranial Signal Processors - Gen IV",
|
||||||
CranialSignalProcessorsG5: "Cranial Signal Processors - Gen V",
|
CranialSignalProcessorsG5: "Cranial Signal Processors - Gen V",
|
||||||
NeuronalDensification: "Neuronal Densification",
|
NeuronalDensification: "Neuronal Densification",
|
||||||
|
NeuroreceptorManager: "Neuroreceptor Management Implant",
|
||||||
NuoptimalInjectorImplant: "Nuoptimal Nootropic Injector Implant",
|
NuoptimalInjectorImplant: "Nuoptimal Nootropic Injector Implant",
|
||||||
SpeechEnhancement: "Speech Enhancement",
|
SpeechEnhancement: "Speech Enhancement",
|
||||||
FocusWire: "FocusWire",
|
FocusWire: "FocusWire",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* Root React component for the Augmentations UI page that display all of your
|
* Root React component for the Augmentations UI page that display all of your
|
||||||
* owned and purchased Augmentations and Source-Files.
|
* owned and purchased Augmentations and Source-Files.
|
||||||
*/
|
*/
|
||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { InstalledAugmentations } from "./InstalledAugmentations";
|
import { InstalledAugmentations } from "./InstalledAugmentations";
|
||||||
import { PlayerMultipliers } from "./PlayerMultipliers";
|
import { PlayerMultipliers } from "./PlayerMultipliers";
|
||||||
@ -23,10 +23,17 @@ interface IProps {
|
|||||||
|
|
||||||
export function AugmentationsRoot(props: IProps): React.ReactElement {
|
export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((o) => !o);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(rerender, 200);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
function doExport(): void {
|
function doExport(): void {
|
||||||
props.exportGameFn();
|
props.exportGameFn();
|
||||||
setRerender((o) => !o);
|
rerender();
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportBonusStr(): string {
|
function exportBonusStr(): string {
|
||||||
@ -63,13 +70,11 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Box mx={2}>
|
<Box mx={2}>
|
||||||
<Tooltip title={"'I never asked for this'"}>
|
<Tooltip title={"'I never asked for this'"}>
|
||||||
<Button onClick={props.installAugmentationsFn}>
|
<Button onClick={props.installAugmentationsFn}>Install Augmentations</Button>
|
||||||
<Typography>Install Augmentations</Typography>
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={"It's always a good idea to backup/export your save!"}>
|
<Tooltip title={"It's always a good idea to backup/export your save!"}>
|
||||||
<Button sx={{ mx: 2 }} onClick={doExport}>
|
<Button sx={{ mx: 2 }} onClick={doExport} color="error">
|
||||||
<Typography color="error">Backup Save {exportBonusStr()}</Typography>
|
Backup Save {exportBonusStr()}
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<PurchasedAugmentations />
|
<PurchasedAugmentations />
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
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";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { BladeburnerConstants } from "./data/Constants";
|
import { BladeburnerConstants } from "./data/Constants";
|
||||||
import { IBladeburner } from "./IBladeburner";
|
import { IBladeburner } from "./IBladeburner";
|
||||||
import { IAction, ISuccessChanceParams } from "./IAction";
|
import { IAction, ISuccessChanceParams } from "./IAction";
|
||||||
@ -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,5 +1,5 @@
|
|||||||
import { IActionIdentifier } from "./IActionIdentifier";
|
import { IActionIdentifier } from "./IActionIdentifier";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
interface IParams {
|
interface IParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Operation, IOperationParams } from "./Operation";
|
import { Operation, IOperationParams } from "./Operation";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
export class BlackOperation extends Operation {
|
export class BlackOperation extends Operation {
|
||||||
constructor(params: IOperationParams | null = null) {
|
constructor(params: IOperationParams | null = null) {
|
||||||
|
@ -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,4 +1,5 @@
|
|||||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../utils/JSONReviver";
|
import React from "react";
|
||||||
|
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";
|
||||||
import { ActionIdentifier } from "./ActionIdentifier";
|
import { ActionIdentifier } from "./ActionIdentifier";
|
||||||
@ -9,7 +10,7 @@ import { BlackOperation } from "./BlackOperation";
|
|||||||
import { Operation } from "./Operation";
|
import { Operation } from "./Operation";
|
||||||
import { Contract } from "./Contract";
|
import { Contract } from "./Contract";
|
||||||
import { GeneralActions } from "./GeneralActions";
|
import { GeneralActions } from "./GeneralActions";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||||
import { Skills } from "./Skills";
|
import { Skills } from "./Skills";
|
||||||
import { Skill } from "./Skill";
|
import { Skill } from "./Skill";
|
||||||
import { City } from "./City";
|
import { City } from "./City";
|
||||||
@ -17,21 +18,21 @@ import { IAction } from "./IAction";
|
|||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { IRouter } from "../ui/Router";
|
import { IRouter } from "../ui/Router";
|
||||||
import { ConsoleHelpText } from "./data/Help";
|
import { ConsoleHelpText } from "./data/Help";
|
||||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { BladeburnerConstants } from "./data/Constants";
|
import { BladeburnerConstants } from "./data/Constants";
|
||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
import { numeralWrapper } from "../ui/numeralFormat";
|
||||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||||
import { addOffset } from "../../utils/helpers/addOffset";
|
import { addOffset } from "../utils/helpers/addOffset";
|
||||||
import { Faction } from "../Faction/Faction";
|
import { Faction } from "../Faction/Faction";
|
||||||
import { Factions, factionExists } from "../Faction/Factions";
|
import { Factions, factionExists } from "../Faction/Factions";
|
||||||
import { calculateHospitalizationCost } from "../Hospital/Hospital";
|
import { calculateHospitalizationCost } from "../Hospital/Hospital";
|
||||||
import { redPillFlag } from "../RedPill";
|
import { redPillFlag } from "../RedPill";
|
||||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { Settings } from "../Settings/Settings";
|
import { Settings } from "../Settings/Settings";
|
||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
import { Augmentations } from "../Augmentation/Augmentations";
|
||||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||||
import { getTimestamp } from "../../utils/helpers/getTimestamp";
|
import { getTimestamp } from "../utils/helpers/getTimestamp";
|
||||||
import { joinFaction } from "../Faction/FactionHelpers";
|
import { joinFaction } from "../Faction/FactionHelpers";
|
||||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||||
|
|
||||||
@ -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,7 +1,7 @@
|
|||||||
import { BladeburnerConstants } from "./data/Constants";
|
import { BladeburnerConstants } from "./data/Constants";
|
||||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { addOffset } from "../../utils/helpers/addOffset";
|
import { addOffset } from "../utils/helpers/addOffset";
|
||||||
|
|
||||||
interface IChangePopulationByCountParams {
|
interface IChangePopulationByCountParams {
|
||||||
estChange: number;
|
estChange: number;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { IBladeburner } from "./IBladeburner";
|
import { IBladeburner } from "./IBladeburner";
|
||||||
import { Action, IActionParams } from "./Action";
|
import { Action, IActionParams } from "./Action";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
export class Contract extends Action {
|
export class Contract extends Action {
|
||||||
constructor(params: IActionParams | null = null) {
|
constructor(params: IActionParams | null = null) {
|
||||||
|
@ -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;
|
@ -1,7 +1,7 @@
|
|||||||
import { IBladeburner } from "./IBladeburner";
|
import { IBladeburner } from "./IBladeburner";
|
||||||
import { BladeburnerConstants } from "./data/Constants";
|
import { BladeburnerConstants } from "./data/Constants";
|
||||||
import { Action, IActionParams } from "./Action";
|
import { Action, IActionParams } from "./Action";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
export interface IOperationParams extends IActionParams {
|
export interface IOperationParams extends IActionParams {
|
||||||
reqdRank?: number;
|
reqdRank?: 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 />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||||
|
|
||||||
export const Growths: {
|
export const Growths: {
|
||||||
[key: string]: (() => number) | undefined;
|
[key: string]: (() => number) | undefined;
|
||||||
|
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}>
|
||||||
<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>
|
||||||
|
);
|
||||||
|
}
|
@ -1,23 +1,30 @@
|
|||||||
import React, { useState } from "react";
|
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,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import { CinematicText } from "../../ui/React/CinematicText";
|
import { CinematicText } from "../../ui/React/CinematicText";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
|
||||||
export function BladeburnerCinematic(): React.ReactElement {
|
export function BladeburnerCinematic(): React.ReactElement {
|
||||||
const router = use.Router();
|
const router = use.Router();
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,114 +1,79 @@
|
|||||||
import React, { useState } from "react";
|
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} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,19 +1,30 @@
|
|||||||
import React, { useState } from "react";
|
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 { 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>
|
||||||
|
);
|
||||||
|
}
|
@ -1,128 +1,82 @@
|
|||||||
import React, { useState } from "react";
|
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} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
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>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { SkillList } from "./SkillList";
|
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>
|
||||||
|
);
|
||||||
|
}
|
@ -1,18 +1,21 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { BladeburnerConstants } from "../data/Constants";
|
import { BladeburnerConstants } from "../data/Constants";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
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 "../../../utils/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 "../../../utils/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)} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
|
|
||||||
const gainLimit = 10e9;
|
const gainLimit = 10e9;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import { SpecialServerNames } from "./Server/SpecialServerIps";
|
|||||||
import { Server } from "./Server/Server";
|
import { Server } from "./Server/Server";
|
||||||
import { HacknetServer } from "./Hacknet/HacknetServer";
|
import { HacknetServer } from "./Hacknet/HacknetServer";
|
||||||
|
|
||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
import { getRandomInt } from "./utils/helpers/getRandomInt";
|
||||||
|
|
||||||
export function generateRandomContract(): void {
|
export function generateRandomContract(): void {
|
||||||
// First select a random problem type
|
// First select a random problem type
|
||||||
|
@ -2,7 +2,7 @@ import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc
|
|||||||
|
|
||||||
import { IMap } from "./types";
|
import { IMap } from "./types";
|
||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "./utils/JSONReviver";
|
||||||
import { createPopup, removePopup } from "./ui/React/createPopup";
|
import { createPopup, removePopup } from "./ui/React/createPopup";
|
||||||
import { CodingContractPopup } from "./ui/React/CodingContractPopup";
|
import { CodingContractPopup } from "./ui/React/CodingContractPopup";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { companiesMetadata } from "./data/CompaniesMetadata";
|
import { companiesMetadata } from "./data/CompaniesMetadata";
|
||||||
import { Company, IConstructorParams } from "./Company";
|
import { Company, IConstructorParams } from "./Company";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
import { Reviver } from "../../utils/JSONReviver";
|
import { Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
export let Companies: IMap<Company> = {};
|
export let Companies: IMap<Company> = {};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import * as posNames from "./data/companypositionnames";
|
|||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
export interface IConstructorParams {
|
export interface IConstructorParams {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -9,6 +9,7 @@ export const CONSTANTS: {
|
|||||||
MaxSkillLevel: number;
|
MaxSkillLevel: number;
|
||||||
MilliPerCycle: number;
|
MilliPerCycle: number;
|
||||||
CorpFactionRepRequirement: number;
|
CorpFactionRepRequirement: number;
|
||||||
|
BaseFocusBonus: number;
|
||||||
BaseCostFor1GBOfRamHome: number;
|
BaseCostFor1GBOfRamHome: number;
|
||||||
BaseCostFor1GBOfRamServer: number;
|
BaseCostFor1GBOfRamServer: number;
|
||||||
TravelCost: number;
|
TravelCost: number;
|
||||||
@ -285,6 +286,7 @@ export const CONSTANTS: {
|
|||||||
GameCyclesPerFiveMinutes: 300000 / 200,
|
GameCyclesPerFiveMinutes: 300000 / 200,
|
||||||
|
|
||||||
// Player Work & Action
|
// Player Work & Action
|
||||||
|
BaseFocusBonus: 0.8,
|
||||||
FactionWorkHacking: "Faction Hacking Work",
|
FactionWorkHacking: "Faction Hacking Work",
|
||||||
FactionWorkField: "Faction Field Work",
|
FactionWorkField: "Faction Field Work",
|
||||||
FactionWorkSecurity: "Faction Security Work",
|
FactionWorkSecurity: "Faction Security Work",
|
||||||
|
@ -10,9 +10,9 @@ import { showLiterature } from "../Literature/LiteratureHelpers";
|
|||||||
import { LiteratureNames } from "../Literature/data/LiteratureNames";
|
import { LiteratureNames } from "../Literature/data/LiteratureNames";
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../utils/JSONReviver";
|
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
|
||||||
import { isString } from "../../utils/helpers/isString";
|
import { isString } from "../utils/helpers/isString";
|
||||||
|
|
||||||
// UI Related Imports
|
// UI Related Imports
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
|
|
||||||
// Array of all valid states
|
// Array of all valid states
|
||||||
const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
|
const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { CorporationConstants } from "./data/Constants";
|
import { CorporationConstants } from "./data/Constants";
|
||||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
import { createElement } from "../ui/uiHelpers/createElement";
|
||||||
import { EmployeePositions } from "./EmployeePositions";
|
import { EmployeePositions } from "./EmployeePositions";
|
||||||
import { ICorporation } from "./ICorporation";
|
import { ICorporation } from "./ICorporation";
|
||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
import { numeralWrapper } from "../ui/numeralFormat";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||||
import { OfficeSpace } from "./OfficeSpace";
|
import { OfficeSpace } from "./OfficeSpace";
|
||||||
import { IIndustry } from "./IIndustry";
|
import { IIndustry } from "./IIndustry";
|
||||||
|
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../utils/JSONReviver";
|
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
|
||||||
import { CityName } from "../Locations/data/CityNames";
|
import { CityName } from "../Locations/data/CityNames";
|
||||||
import Decimal from "decimal.js";
|
import Decimal from "decimal.js";
|
||||||
import { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
|
import { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
|
||||||
import { CorporationConstants } from "./data/Constants";
|
import { CorporationConstants } from "./data/Constants";
|
||||||
import { EmployeePositions } from "./EmployeePositions";
|
import { EmployeePositions } from "./EmployeePositions";
|
||||||
import { Material } from "./Material";
|
import { Material } from "./Material";
|
||||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { calculateEffectWithFactors } from "../utils/calculateEffectWithFactors";
|
import { calculateEffectWithFactors } from "../utils/calculateEffectWithFactors";
|
||||||
import { OfficeSpace } from "./OfficeSpace";
|
import { OfficeSpace } from "./OfficeSpace";
|
||||||
import { Product } from "./Product";
|
import { Product } from "./Product";
|
||||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||||
import { isString } from "../../utils/helpers/isString";
|
import { isString } from "../utils/helpers/isString";
|
||||||
import { MaterialSizes } from "./MaterialSizes";
|
import { MaterialSizes } from "./MaterialSizes";
|
||||||
import { Warehouse } from "./Warehouse";
|
import { Warehouse } from "./Warehouse";
|
||||||
import { ICorporation } from "./ICorporation";
|
import { ICorporation } from "./ICorporation";
|
||||||
import { IIndustry } from "./IIndustry";
|
import { IIndustry } from "./IIndustry";
|
||||||
import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades";
|
import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||||
|
|
||||||
interface IParams {
|
interface IParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { Export } from "./Export";
|
import { Export } from "./Export";
|
||||||
|
|
||||||
interface IConstructorParams {
|
interface IConstructorParams {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { EmployeePositions } from "./EmployeePositions";
|
import { EmployeePositions } from "./EmployeePositions";
|
||||||
import { CorporationConstants } from "./data/Constants";
|
import { CorporationConstants } from "./data/Constants";
|
||||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { generateRandomString } from "../../utils/StringHelperFunctions";
|
import { generateRandomString } from "../utils/StringHelperFunctions";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { Employee } from "./Employee";
|
import { Employee } from "./Employee";
|
||||||
import { IIndustry } from "./IIndustry";
|
import { IIndustry } from "./IIndustry";
|
||||||
import { ICorporation } from "./ICorporation";
|
import { ICorporation } from "./ICorporation";
|
||||||
|
@ -6,8 +6,8 @@ import { ProductRatingWeights, IProductRatingWeight } from "./ProductRatingWeigh
|
|||||||
import { createCityMap } from "../Locations/createCityMap";
|
import { createCityMap } from "../Locations/createCityMap";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
|
|
||||||
interface IConstructorParams {
|
interface IConstructorParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -4,8 +4,8 @@ import { IIndustry } from "./IIndustry";
|
|||||||
import { MaterialSizes } from "./MaterialSizes";
|
import { MaterialSizes } from "./MaterialSizes";
|
||||||
import { IMap } from "../types";
|
import { IMap } from "../types";
|
||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
import { numeralWrapper } from "../ui/numeralFormat";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||||
|
|
||||||
interface IConstructorParams {
|
interface IConstructorParams {
|
||||||
corp?: ICorporation;
|
corp?: ICorporation;
|
||||||
|
112
src/Corporation/ui/BribeFactionModal.tsx
Normal file
112
src/Corporation/ui/BribeFactionModal.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Factions } from "../../Faction/Factions";
|
||||||
|
import { CorporationConstants } from "../data/Constants";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import { useCorporation } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BribeFactionModal(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const corp = useCorporation();
|
||||||
|
const [money, setMoney] = useState<number | null>(0);
|
||||||
|
const [stock, setStock] = useState<number | null>(0);
|
||||||
|
const [selectedFaction, setSelectedFaction] = useState(
|
||||||
|
player.factions.length > 0 ? player.factions.filter((faction) => Factions[faction].getInfo().offersWork())[0] : "",
|
||||||
|
);
|
||||||
|
|
||||||
|
function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
setMoney(parseFloat(event.target.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStockChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
setStock(parseFloat(event.target.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeFaction(event: SelectChangeEvent<string>): void {
|
||||||
|
setSelectedFaction(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function repGain(money: number, stock: number): number {
|
||||||
|
return (money + stock * corp.sharePrice) / CorporationConstants.BribeToRepRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRepText(money: number, stock: number): string {
|
||||||
|
if (money === 0 && stock === 0) return "";
|
||||||
|
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
|
||||||
|
return "ERROR: Invalid value(s) entered";
|
||||||
|
} else if (corp.funds.lt(money)) {
|
||||||
|
return "ERROR: You do not have this much money to bribe with";
|
||||||
|
} else if (stock > corp.numShares) {
|
||||||
|
return "ERROR: You do not have this many shares to bribe with";
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
"You will gain " +
|
||||||
|
numeralWrapper.formatReputation(repGain(money, stock)) +
|
||||||
|
" reputation with " +
|
||||||
|
selectedFaction +
|
||||||
|
" with this bribe"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bribe(money: number, stock: number): void {
|
||||||
|
const fac = Factions[selectedFaction];
|
||||||
|
if (fac == null) {
|
||||||
|
dialogBoxCreate("ERROR: You must select a faction to bribe");
|
||||||
|
}
|
||||||
|
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
|
||||||
|
} else if (corp.funds.lt(money)) {
|
||||||
|
} else if (stock > corp.numShares) {
|
||||||
|
} else {
|
||||||
|
const rep = repGain(money, stock);
|
||||||
|
dialogBoxCreate(
|
||||||
|
"You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
|
||||||
|
);
|
||||||
|
fac.playerReputation += rep;
|
||||||
|
corp.funds = corp.funds.minus(money);
|
||||||
|
corp.numShares -= stock;
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<Typography>
|
||||||
|
You can use Corporation funds or stock shares to bribe Faction Leaders in exchange for faction reputation.
|
||||||
|
</Typography>
|
||||||
|
<Box display="flex" alignItems="center">
|
||||||
|
<Typography>Faction:</Typography>
|
||||||
|
<Select variant="standard" value={selectedFaction} onChange={changeFaction}>
|
||||||
|
{player.factions.map((name: string) => {
|
||||||
|
const info = Factions[name].getInfo();
|
||||||
|
if (!info.offersWork()) return;
|
||||||
|
return (
|
||||||
|
<MenuItem key={name} value={name}>
|
||||||
|
{name}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</Box>
|
||||||
|
<Typography>{getRepText(money ? money : 0, stock ? stock : 0)}</Typography>
|
||||||
|
<TextField variant="standard" onChange={onMoneyChange} placeholder="Corporation funds" />
|
||||||
|
<TextField sx={{ mx: 1 }} variant="standard" onChange={onStockChange} placeholder="Stock Shares" />
|
||||||
|
<Button sx={{ mx: 1 }} onClick={() => bribe(money ? money : 0, stock ? stock : 0)}>
|
||||||
|
Bribe
|
||||||
|
</Button>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,109 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { Factions } from "../../Faction/Factions";
|
|
||||||
import { CorporationConstants } from "../data/Constants";
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
|
||||||
import { ICorporation } from "../ICorporation";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
popupId: string;
|
|
||||||
corp: ICorporation;
|
|
||||||
player: IPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BribeFactionPopup(props: IProps): React.ReactElement {
|
|
||||||
const [money, setMoney] = useState<number | null>(0);
|
|
||||||
const [stock, setStock] = useState<number | null>(0);
|
|
||||||
const [selectedFaction, setSelectedFaction] = useState(
|
|
||||||
props.player.factions.length > 0 ? props.player.factions[0] : "",
|
|
||||||
);
|
|
||||||
|
|
||||||
function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
setMoney(parseFloat(event.target.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
function onStockChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
setStock(parseFloat(event.target.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeFaction(event: React.ChangeEvent<HTMLSelectElement>): void {
|
|
||||||
setSelectedFaction(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function repGain(money: number, stock: number): number {
|
|
||||||
return (money + stock * props.corp.sharePrice) / CorporationConstants.BribeToRepRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRepText(money: number, stock: number): string {
|
|
||||||
if (money === 0 && stock === 0) return "";
|
|
||||||
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
|
|
||||||
return "ERROR: Invalid value(s) entered";
|
|
||||||
} else if (props.corp.funds.lt(money)) {
|
|
||||||
return "ERROR: You do not have this much money to bribe with";
|
|
||||||
} else if (stock > props.corp.numShares) {
|
|
||||||
return "ERROR: You do not have this many shares to bribe with";
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
"You will gain " +
|
|
||||||
numeralWrapper.formatReputation(repGain(money, stock)) +
|
|
||||||
" reputation with " +
|
|
||||||
selectedFaction +
|
|
||||||
" with this bribe"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function bribe(money: number, stock: number): void {
|
|
||||||
const fac = Factions[selectedFaction];
|
|
||||||
if (fac == null) {
|
|
||||||
dialogBoxCreate("ERROR: You must select a faction to bribe");
|
|
||||||
}
|
|
||||||
if (isNaN(money) || isNaN(stock) || money < 0 || stock < 0) {
|
|
||||||
} else if (props.corp.funds.lt(money)) {
|
|
||||||
} else if (stock > props.corp.numShares) {
|
|
||||||
} else {
|
|
||||||
const rep = repGain(money, stock);
|
|
||||||
dialogBoxCreate(
|
|
||||||
"You gained " + numeralWrapper.formatReputation(rep) + " reputation with " + fac.name + " by bribing them.",
|
|
||||||
);
|
|
||||||
fac.playerReputation += rep;
|
|
||||||
props.corp.funds = props.corp.funds.minus(money);
|
|
||||||
props.corp.numShares -= stock;
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>You can use Corporation funds or stock shares to bribe Faction Leaders in exchange for faction reputation.</p>
|
|
||||||
<select className="dropdown" style={{ margin: "3px" }} defaultValue={selectedFaction} onChange={changeFaction}>
|
|
||||||
{props.player.factions.map((name: string) => {
|
|
||||||
const info = Factions[name].getInfo();
|
|
||||||
if (!info.offersWork()) return;
|
|
||||||
return (
|
|
||||||
<option key={name} value={name}>
|
|
||||||
{name}
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</select>
|
|
||||||
<p>{getRepText(money ? money : 0, stock ? stock : 0)}</p>
|
|
||||||
<input
|
|
||||||
className="text-input"
|
|
||||||
onChange={onMoneyChange}
|
|
||||||
placeholder="Corporation funds"
|
|
||||||
style={{ margin: "5px" }}
|
|
||||||
/>
|
|
||||||
<input className="text-input" onChange={onStockChange} placeholder="Stock Shares" style={{ margin: "5px" }} />
|
|
||||||
<button
|
|
||||||
className="a-link-button"
|
|
||||||
onClick={() => bribe(money ? money : 0, stock ? stock : 0)}
|
|
||||||
style={{ display: "inline-block" }}
|
|
||||||
>
|
|
||||||
Bribe
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,20 +1,21 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { use } from "../../ui/Context";
|
||||||
|
import { useCorporation } from "./Context";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
player: IPlayer;
|
open: boolean;
|
||||||
popupId: string;
|
onClose: () => void;
|
||||||
corp: ICorporation;
|
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a popup that lets the player buyback shares
|
// Create a popup that lets the player buyback shares
|
||||||
// This is created when the player clicks the "Buyback Shares" button in the overview panel
|
// This is created when the player clicks the "Buyback Shares" button in the overview panel
|
||||||
export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
export function BuybackSharesModal(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const corp = useCorporation();
|
||||||
const [shares, setShares] = useState<number | null>(null);
|
const [shares, setShares] = useState<number | null>(null);
|
||||||
|
|
||||||
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
|
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
@ -22,38 +23,38 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
|||||||
else setShares(Math.round(parseFloat(event.target.value)));
|
else setShares(Math.round(parseFloat(event.target.value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentStockPrice = props.corp.sharePrice;
|
const currentStockPrice = corp.sharePrice;
|
||||||
const buybackPrice = currentStockPrice * 1.1;
|
const buybackPrice = currentStockPrice * 1.1;
|
||||||
|
|
||||||
function buy(): void {
|
function buy(): void {
|
||||||
if (shares === null) return;
|
if (shares === null) return;
|
||||||
const tempStockPrice = props.corp.sharePrice;
|
const tempStockPrice = corp.sharePrice;
|
||||||
const buybackPrice = tempStockPrice * 1.1;
|
const buybackPrice = tempStockPrice * 1.1;
|
||||||
if (isNaN(shares) || shares <= 0) {
|
if (isNaN(shares) || shares <= 0) {
|
||||||
dialogBoxCreate("ERROR: Invalid value for number of shares");
|
dialogBoxCreate("ERROR: Invalid value for number of shares");
|
||||||
} else if (shares > props.corp.issuedShares) {
|
} else if (shares > corp.issuedShares) {
|
||||||
dialogBoxCreate("ERROR: There are not this many oustanding shares to buy back");
|
dialogBoxCreate("ERROR: There are not this many oustanding shares to buy back");
|
||||||
} else if (shares * buybackPrice > props.player.money) {
|
} else if (shares * buybackPrice > player.money) {
|
||||||
dialogBoxCreate(
|
dialogBoxCreate(
|
||||||
"ERROR: You do not have enough money to purchase this many shares (you need " +
|
"ERROR: You do not have enough money to purchase this many shares (you need " +
|
||||||
numeralWrapper.format(shares * buybackPrice, "$0.000a") +
|
numeralWrapper.format(shares * buybackPrice, "$0.000a") +
|
||||||
")",
|
")",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
props.corp.numShares += shares;
|
corp.numShares += shares;
|
||||||
if (isNaN(props.corp.issuedShares)) {
|
if (isNaN(corp.issuedShares)) {
|
||||||
console.warn("Corporation issuedShares is NaN: " + props.corp.issuedShares);
|
console.warn("Corporation issuedShares is NaN: " + corp.issuedShares);
|
||||||
console.warn("Converting to number now");
|
console.warn("Converting to number now");
|
||||||
const res = props.corp.issuedShares;
|
const res = corp.issuedShares;
|
||||||
if (isNaN(res)) {
|
if (isNaN(res)) {
|
||||||
props.corp.issuedShares = 0;
|
corp.issuedShares = 0;
|
||||||
} else {
|
} else {
|
||||||
props.corp.issuedShares = res;
|
corp.issuedShares = res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
props.corp.issuedShares -= shares;
|
corp.issuedShares -= shares;
|
||||||
props.player.loseMoney(shares * buybackPrice);
|
player.loseMoney(shares * buybackPrice);
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
props.rerender();
|
props.rerender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,11 +63,11 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
|||||||
if (shares === null) return <></>;
|
if (shares === null) return <></>;
|
||||||
if (isNaN(shares) || shares <= 0) {
|
if (isNaN(shares) || shares <= 0) {
|
||||||
return <>ERROR: Invalid value entered for number of shares to buyback</>;
|
return <>ERROR: Invalid value entered for number of shares to buyback</>;
|
||||||
} else if (shares > props.corp.issuedShares) {
|
} else if (shares > corp.issuedShares) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
There are not this many shares available to buy back. There are only{" "}
|
There are not this many shares available to buy back. There are only{" "}
|
||||||
{numeralWrapper.formatBigNumber(props.corp.issuedShares)} outstanding shares.
|
{numeralWrapper.formatBigNumber(corp.issuedShares)} outstanding shares.
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -83,7 +84,7 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<p>
|
<p>
|
||||||
Enter the number of outstanding shares you would like to buy back. These shares must be bought at a 10% premium.
|
Enter the number of outstanding shares you would like to buy back. These shares must be bought at a 10% premium.
|
||||||
However, repurchasing shares from the market tends to lead to an increase in stock price.
|
However, repurchasing shares from the market tends to lead to an increase in stock price.
|
||||||
@ -93,7 +94,7 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
The current buyback price of your company's stock is {numeralWrapper.formatMoney(buybackPrice)}. Your company
|
The current buyback price of your company's stock is {numeralWrapper.formatMoney(buybackPrice)}. Your company
|
||||||
currently has {numeralWrapper.formatBigNumber(props.corp.issuedShares)} outstanding stock shares.
|
currently has {numeralWrapper.formatBigNumber(corp.issuedShares)} outstanding stock shares.
|
||||||
</p>
|
</p>
|
||||||
<CostIndicator />
|
<CostIndicator />
|
||||||
<br />
|
<br />
|
||||||
@ -109,6 +110,6 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
|||||||
<button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}>
|
<button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}>
|
||||||
Buy shares
|
Buy shares
|
||||||
</button>
|
</button>
|
||||||
</>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
10
src/Corporation/ui/Context.ts
Normal file
10
src/Corporation/ui/Context.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React, { useContext } from "react";
|
||||||
|
import { ICorporation } from "../ICorporation";
|
||||||
|
|
||||||
|
export const Context: {
|
||||||
|
Corporation: React.Context<ICorporation>;
|
||||||
|
} = {
|
||||||
|
Corporation: React.createContext<ICorporation>({} as ICorporation),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCorporation = () => useContext(Context.Corporation);
|
@ -2,41 +2,16 @@
|
|||||||
// These are the tabs at the top of the UI that let you switch to different
|
// These are the tabs at the top of the UI that let you switch to different
|
||||||
// divisions, see an overview of your corporation, or create a new industry
|
// divisions, see an overview of your corporation, or create a new industry
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { HeaderTab } from "./HeaderTab";
|
|
||||||
import { IIndustry } from "../IIndustry";
|
import { IIndustry } from "../IIndustry";
|
||||||
import { NewIndustryPopup } from "./NewIndustryPopup";
|
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { ICorporation } from "../ICorporation";
|
|
||||||
import { MainPanel } from "./MainPanel";
|
import { MainPanel } from "./MainPanel";
|
||||||
import { Industries } from "../IndustryData";
|
import { Industries } from "../IndustryData";
|
||||||
|
import { ExpandIndustryTab } from "./ExpandIndustryTab";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
|
import { Context } from "./Context";
|
||||||
|
import { Overview } from "./Overview";
|
||||||
|
|
||||||
interface IExpandButtonProps {
|
import Tabs from "@mui/material/Tabs";
|
||||||
corp: ICorporation;
|
import Tab from "@mui/material/Tab";
|
||||||
setDivisionName: (name: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExpandButton(props: IExpandButtonProps): React.ReactElement {
|
|
||||||
const allIndustries = Object.keys(Industries).sort();
|
|
||||||
const possibleIndustries = allIndustries
|
|
||||||
.filter(
|
|
||||||
(industryType: string) =>
|
|
||||||
props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
|
|
||||||
)
|
|
||||||
.sort();
|
|
||||||
if (possibleIndustries.length === 0) return <></>;
|
|
||||||
|
|
||||||
function openNewIndustryPopup(): void {
|
|
||||||
const popupId = "cmpy-mgmt-expand-industry-popup";
|
|
||||||
createPopup(popupId, NewIndustryPopup, {
|
|
||||||
corp: props.corp,
|
|
||||||
setDivisionName: props.setDivisionName,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return <HeaderTab current={false} onClick={openNewIndustryPopup} text={"Expand into new Industry"} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CorporationRoot(): React.ReactElement {
|
export function CorporationRoot(): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
@ -46,33 +21,37 @@ export function CorporationRoot(): React.ReactElement {
|
|||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
setRerender((old) => !old);
|
setRerender((old) => !old);
|
||||||
}
|
}
|
||||||
const [divisionName, setDivisionName] = useState("Overview");
|
const [divisionName, setDivisionName] = useState<string | number>("Overview");
|
||||||
|
function handleChange(event: React.SyntheticEvent, tab: string | number): void {
|
||||||
|
setDivisionName(tab);
|
||||||
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = setInterval(rerender, 1000);
|
const id = setInterval(rerender, 200);
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const canExpand =
|
||||||
|
Object.keys(Industries).filter(
|
||||||
|
(industryType: string) =>
|
||||||
|
corporation.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
|
||||||
|
).length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cmpy-mgmt-container">
|
<Context.Corporation.Provider value={corporation}>
|
||||||
<div>
|
<div className="cmpy-mgmt-container">
|
||||||
<HeaderTab
|
<Tabs variant="fullWidth" value={divisionName} onChange={handleChange}>
|
||||||
current={divisionName === "Overview"}
|
<Tab label={corporation.name} value={"Overview"} />
|
||||||
key={"overview"}
|
{corporation.divisions.map((div) => (
|
||||||
onClick={() => setDivisionName("Overview")}
|
<Tab key={div.name} label={div.name} value={div.name} />
|
||||||
text={corporation.name}
|
))}
|
||||||
/>
|
{canExpand && <Tab label={"Expand"} value={-1} />}
|
||||||
{corporation.divisions.map((division: IIndustry) => (
|
</Tabs>
|
||||||
<HeaderTab
|
{divisionName === "Overview" && <Overview rerender={rerender} />}
|
||||||
current={division.name === divisionName}
|
{divisionName === -1 && <ExpandIndustryTab setDivisionName={setDivisionName} />}
|
||||||
key={division.name}
|
{typeof divisionName === "string" && divisionName !== "Overview" && (
|
||||||
onClick={() => setDivisionName(division.name)}
|
<MainPanel rerender={rerender} divisionName={divisionName + ""} />
|
||||||
text={division.name}
|
)}
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<ExpandButton corp={corporation} setDivisionName={setDivisionName} />
|
|
||||||
</div>
|
</div>
|
||||||
<MainPanel rerender={rerender} corp={corporation} divisionName={divisionName} player={player} />
|
</Context.Corporation.Provider>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
75
src/Corporation/ui/CreateCorporationModal.tsx
Normal file
75
src/Corporation/ui/CreateCorporationModal.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
import { Money } from "../../ui/React/Money";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreateCorporationModal(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const router = use.Router();
|
||||||
|
const canSelfFund = player.canAfford(150e9);
|
||||||
|
if (!player.canAccessCorporation() || player.hasCorporation()) {
|
||||||
|
props.onClose();
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
setName(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selfFund(): void {
|
||||||
|
if (!canSelfFund) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.startCorporation(name);
|
||||||
|
player.loseMoney(150e9);
|
||||||
|
|
||||||
|
props.onClose();
|
||||||
|
router.toCorporation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function seed(): void {
|
||||||
|
if (name == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.startCorporation(name, 500e6);
|
||||||
|
|
||||||
|
props.onClose();
|
||||||
|
router.toCorporation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<Typography>
|
||||||
|
Would you like to start a corporation? This will require $150b for registration and initial funding. This $150b
|
||||||
|
can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million
|
||||||
|
shares
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
If you would like to start one, please enter a name for your corporation below:
|
||||||
|
</Typography>
|
||||||
|
<TextField autoFocus={true} variant="standard" placeholder="Corporation Name" onChange={onChange} value={name} />
|
||||||
|
<Button onClick={seed} disabled={name == ""}>
|
||||||
|
Use seed money
|
||||||
|
</Button>
|
||||||
|
<Button onClick={selfFund} disabled={name == "" || !canSelfFund}>
|
||||||
|
Self-Fund (<Money money={150e9} player={player} />)
|
||||||
|
</Button>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,83 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { Money } from "../../ui/React/Money";
|
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
|
||||||
import { IRouter } from "../../ui/Router";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
player: IPlayer;
|
|
||||||
popupId: string;
|
|
||||||
router: IRouter;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CreateCorporationPopup(props: IProps): React.ReactElement {
|
|
||||||
if (!props.player.canAccessCorporation() || props.player.hasCorporation()) {
|
|
||||||
removePopup(props.popupId);
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
setName(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function selfFund(): void {
|
|
||||||
if (!props.player.canAfford(150e9)) {
|
|
||||||
dialogBoxCreate("You don't have enough money to create a corporation! You need $150b.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name == "") {
|
|
||||||
dialogBoxCreate("Invalid company name!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
props.player.startCorporation(name);
|
|
||||||
props.player.loseMoney(150e9);
|
|
||||||
|
|
||||||
dialogBoxCreate(
|
|
||||||
"Congratulations! You just self-funded your own corporation. You can visit " +
|
|
||||||
"and manage your company in the City.",
|
|
||||||
);
|
|
||||||
removePopup(props.popupId);
|
|
||||||
props.router.toCorporation();
|
|
||||||
}
|
|
||||||
|
|
||||||
function seed(): void {
|
|
||||||
if (name == "") {
|
|
||||||
dialogBoxCreate("Invalid company name!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
props.player.startCorporation(name, 500e6);
|
|
||||||
|
|
||||||
dialogBoxCreate(
|
|
||||||
"Congratulations! You just started your own corporation with government seed money. " +
|
|
||||||
"You can visit and manage your company in the City.",
|
|
||||||
);
|
|
||||||
removePopup(props.popupId);
|
|
||||||
props.router.toCorporation();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
Would you like to start a corporation? This will require $150b for registration and initial funding. This $150b
|
|
||||||
can either be self-funded, or you can obtain the seed money from the government in exchange for 500 million
|
|
||||||
shares
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
If you would like to start one, please enter a name for your corporation below:
|
|
||||||
</p>
|
|
||||||
<input autoFocus={true} className="text-input" placeholder="Corporation Name" onChange={onChange} value={name} />
|
|
||||||
<button className="std-button" onClick={seed} disabled={name == ""}>
|
|
||||||
Use seed money
|
|
||||||
</button>
|
|
||||||
<button className="std-button" onClick={selfFund} disabled={name == "" || !props.player.canAfford(150e9)}>
|
|
||||||
Self-Fund (<Money money={150e9} player={props.player} />)
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
90
src/Corporation/ui/ExpandIndustryTab.tsx
Normal file
90
src/Corporation/ui/ExpandIndustryTab.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { Industries, IndustryDescriptions } from "../IndustryData";
|
||||||
|
import { useCorporation } from "./Context";
|
||||||
|
import { IIndustry } from "../IIndustry";
|
||||||
|
import { NewIndustry } from "../Actions";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
setDivisionName: (name: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExpandIndustryTab(props: IProps): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
|
const allIndustries = Object.keys(Industries).sort();
|
||||||
|
const possibleIndustries = allIndustries
|
||||||
|
.filter(
|
||||||
|
(industryType: string) =>
|
||||||
|
corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
|
||||||
|
)
|
||||||
|
.sort();
|
||||||
|
const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : "");
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
|
||||||
|
function newIndustry(): void {
|
||||||
|
try {
|
||||||
|
NewIndustry(corp, industry, name);
|
||||||
|
} catch (err) {
|
||||||
|
dialogBoxCreate(err + "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set routing to the new division so that the UI automatically switches to it
|
||||||
|
props.setDivisionName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
// [a-zA-Z0-9-_]
|
||||||
|
setName(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
|
if (event.keyCode === 13) newIndustry();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onIndustryChange(event: SelectChangeEvent<string>): void {
|
||||||
|
setIndustry(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const desc = IndustryDescriptions[industry];
|
||||||
|
if (desc === undefined) throw new Error(`Trying to create an industry that doesn't exists: '${industry}'`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography>Create a new division to expand into a new industry:</Typography>
|
||||||
|
<Select variant="standard" value={industry} onChange={onIndustryChange}>
|
||||||
|
{possibleIndustries.map((industry: string) => (
|
||||||
|
<MenuItem key={industry} value={industry}>
|
||||||
|
{industry}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
<Typography>{desc(corp)}</Typography>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Typography>Division name:</Typography>
|
||||||
|
|
||||||
|
<Box display="flex" alignItems="center">
|
||||||
|
<TextField
|
||||||
|
variant="standard"
|
||||||
|
autoFocus={true}
|
||||||
|
value={name}
|
||||||
|
onChange={onNameChange}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
<Button sx={{ mx: 1 }} onClick={newIndustry}>
|
||||||
|
Create Division
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -2,7 +2,7 @@ import React, { useRef } from "react";
|
|||||||
import { IIndustry } from "../IIndustry";
|
import { IIndustry } from "../IIndustry";
|
||||||
import { CorporationConstants } from "../data/Constants";
|
import { CorporationConstants } from "../data/Constants";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { removePopup } from "../../ui/React/createPopup";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { ICorporation } from "../ICorporation";
|
||||||
import { NewCity } from "../Actions";
|
import { NewCity } from "../Actions";
|
||||||
import { MoneyCost } from "./MoneyCost";
|
import { MoneyCost } from "./MoneyCost";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { removePopup } from "../../ui/React/createPopup";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { ICorporation } from "../ICorporation";
|
||||||
import { Material } from "../Material";
|
import { Material } from "../Material";
|
||||||
|
@ -1,23 +1,25 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { CorporationConstants } from "../data/Constants";
|
import { CorporationConstants } from "../data/Constants";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { useCorporation } from "./Context";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
corp: ICorporation;
|
open: boolean;
|
||||||
popupId: string;
|
onClose: () => void;
|
||||||
player: IPlayer;
|
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a popup that lets the player manage exports
|
// Create a popup that lets the player manage exports
|
||||||
export function FindInvestorsPopup(props: IProps): React.ReactElement {
|
export function FindInvestorsModal(props: IProps): React.ReactElement {
|
||||||
const val = props.corp.determineValuation();
|
const corp = useCorporation();
|
||||||
|
const val = corp.determineValuation();
|
||||||
let percShares = 0;
|
let percShares = 0;
|
||||||
let roundMultiplier = 4;
|
let roundMultiplier = 4;
|
||||||
switch (props.corp.fundingRound) {
|
switch (corp.fundingRound) {
|
||||||
case 0: //Seed
|
case 0: //Seed
|
||||||
percShares = 0.1;
|
percShares = 0.1;
|
||||||
roundMultiplier = 4;
|
roundMultiplier = 4;
|
||||||
@ -41,15 +43,15 @@ export function FindInvestorsPopup(props: IProps): React.ReactElement {
|
|||||||
const investShares = Math.floor(CorporationConstants.INITIALSHARES * percShares);
|
const investShares = Math.floor(CorporationConstants.INITIALSHARES * percShares);
|
||||||
|
|
||||||
function findInvestors(): void {
|
function findInvestors(): void {
|
||||||
props.corp.fundingRound++;
|
corp.fundingRound++;
|
||||||
props.corp.addFunds(funding);
|
corp.addFunds(funding);
|
||||||
props.corp.numShares -= investShares;
|
corp.numShares -= investShares;
|
||||||
props.rerender();
|
props.rerender();
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<p>
|
<Typography>
|
||||||
An investment firm has offered you {numeralWrapper.formatMoney(funding)} in funding in exchange for a{" "}
|
An investment firm has offered you {numeralWrapper.formatMoney(funding)} in funding in exchange for a{" "}
|
||||||
{numeralWrapper.format(percShares * 100, "0.000a")}% stake in the company (
|
{numeralWrapper.format(percShares * 100, "0.000a")}% stake in the company (
|
||||||
{numeralWrapper.format(investShares, "0.000a")} shares).
|
{numeralWrapper.format(investShares, "0.000a")} shares).
|
||||||
@ -59,10 +61,8 @@ export function FindInvestorsPopup(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Hint: Investment firms will offer more money if your corporation is turning a profit
|
Hint: Investment firms will offer more money if your corporation is turning a profit
|
||||||
</p>
|
</Typography>
|
||||||
<button onClick={findInvestors} className="std-button">
|
<Button onClick={findInvestors}>Accept</Button>
|
||||||
Accept
|
</Modal>
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
81
src/Corporation/ui/GoPublicModal.tsx
Normal file
81
src/Corporation/ui/GoPublicModal.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { useCorporation } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a popup that lets the player manage exports
|
||||||
|
export function GoPublicModal(props: IProps): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
|
const [shares, setShares] = useState("");
|
||||||
|
const initialSharePrice = corp.determineValuation() / corp.totalShares;
|
||||||
|
|
||||||
|
function goPublic(): void {
|
||||||
|
const numShares = parseFloat(shares);
|
||||||
|
const initialSharePrice = corp.determineValuation() / corp.totalShares;
|
||||||
|
if (isNaN(numShares)) {
|
||||||
|
dialogBoxCreate("Invalid value for number of issued shares");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (numShares > corp.numShares) {
|
||||||
|
dialogBoxCreate("Error: You don't have that many shares to issue!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
corp.public = true;
|
||||||
|
corp.sharePrice = initialSharePrice;
|
||||||
|
corp.issuedShares = numShares;
|
||||||
|
corp.numShares -= numShares;
|
||||||
|
corp.addFunds(numShares * initialSharePrice);
|
||||||
|
props.rerender();
|
||||||
|
dialogBoxCreate(
|
||||||
|
`You took your ${corp.name} public and earned ` +
|
||||||
|
`${numeralWrapper.formatMoney(numShares * initialSharePrice)} in your IPO`,
|
||||||
|
);
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
|
if (event.keyCode === 13) goPublic();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
setShares(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<Typography>
|
||||||
|
Enter the number of shares you would like to issue for your IPO. These shares will be publicly sold and you will
|
||||||
|
no longer own them. Your Corporation will receive {numeralWrapper.formatMoney(initialSharePrice)} per share (the
|
||||||
|
IPO money will be deposited directly into your Corporation's funds).
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
You have a total of {numeralWrapper.format(corp.numShares, "0.000a")} of shares that you can issue.
|
||||||
|
</Typography>
|
||||||
|
<Box display="flex" alignItems="center">
|
||||||
|
<TextField
|
||||||
|
variant="standard"
|
||||||
|
value={shares}
|
||||||
|
onChange={onChange}
|
||||||
|
autoFocus
|
||||||
|
type="number"
|
||||||
|
placeholder="Shares to issue"
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
/>
|
||||||
|
<Button sx={{ mx: 1 }} onClick={goPublic}>
|
||||||
|
Go Public
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,76 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
|
||||||
import { ICorporation } from "../ICorporation";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
corp: ICorporation;
|
|
||||||
popupId: string;
|
|
||||||
player: IPlayer;
|
|
||||||
rerender: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a popup that lets the player manage exports
|
|
||||||
export function GoPublicPopup(props: IProps): React.ReactElement {
|
|
||||||
const [shares, setShares] = useState("");
|
|
||||||
const initialSharePrice = props.corp.determineValuation() / props.corp.totalShares;
|
|
||||||
|
|
||||||
function goPublic(): void {
|
|
||||||
const numShares = parseFloat(shares);
|
|
||||||
const initialSharePrice = props.corp.determineValuation() / props.corp.totalShares;
|
|
||||||
if (isNaN(numShares)) {
|
|
||||||
dialogBoxCreate("Invalid value for number of issued shares");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (numShares > props.corp.numShares) {
|
|
||||||
dialogBoxCreate("Error: You don't have that many shares to issue!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
props.corp.public = true;
|
|
||||||
props.corp.sharePrice = initialSharePrice;
|
|
||||||
props.corp.issuedShares = numShares;
|
|
||||||
props.corp.numShares -= numShares;
|
|
||||||
props.corp.addFunds(numShares * initialSharePrice);
|
|
||||||
props.rerender();
|
|
||||||
dialogBoxCreate(
|
|
||||||
`You took your ${props.corp.name} public and earned ` +
|
|
||||||
`${numeralWrapper.formatMoney(numShares * initialSharePrice)} in your IPO`,
|
|
||||||
);
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
|
||||||
if (event.keyCode === 13) goPublic();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
setShares(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
Enter the number of shares you would like to issue for your IPO. These shares will be publicly sold and you will
|
|
||||||
no longer own them. Your Corporation will receive {numeralWrapper.formatMoney(initialSharePrice)} per share (the
|
|
||||||
IPO money will be deposited directly into your Corporation's funds).
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
You have a total of {numeralWrapper.format(props.corp.numShares, "0.000a")} of shares that you can issue.
|
|
||||||
</p>
|
|
||||||
<input
|
|
||||||
className="text-input"
|
|
||||||
value={shares}
|
|
||||||
onChange={onChange}
|
|
||||||
autoFocus={true}
|
|
||||||
type="number"
|
|
||||||
placeholder="Shares to issue"
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
/>
|
|
||||||
<button className="std-button" onClick={goPublic}>
|
|
||||||
Go Public
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -5,10 +5,10 @@ import { CorporationConstants } from "../data/Constants";
|
|||||||
import { ICorporation } from "../ICorporation";
|
import { ICorporation } from "../ICorporation";
|
||||||
import { OfficeSpace } from "../OfficeSpace";
|
import { OfficeSpace } from "../OfficeSpace";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||||
import { formatNumber } from "../../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { Employee } from "../Employee";
|
import { Employee } from "../Employee";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
|
||||||
interface INameEmployeeProps {
|
interface INameEmployeeProps {
|
||||||
office: OfficeSpace;
|
office: OfficeSpace;
|
||||||
|
@ -9,7 +9,7 @@ import { EmployeePositions } from "../EmployeePositions";
|
|||||||
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
import { getSelectText } from "../../../utils/uiHelpers/getSelectData";
|
import { getSelectText } from "../../ui/uiHelpers/getSelectData";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
import { createPopup } from "../../ui/React/createPopup";
|
||||||
import { UpgradeOfficeSizePopup } from "./UpgradeOfficeSizePopup";
|
import { UpgradeOfficeSizePopup } from "./UpgradeOfficeSizePopup";
|
||||||
import { HireEmployeePopup } from "./HireEmployeePopup";
|
import { HireEmployeePopup } from "./HireEmployeePopup";
|
||||||
|
@ -7,8 +7,8 @@ import { Industries } from "../IndustryData";
|
|||||||
import { IndustryUpgrades } from "../IndustryUpgrades";
|
import { IndustryUpgrades } from "../IndustryUpgrades";
|
||||||
import { IIndustry } from "../IIndustry";
|
import { IIndustry } from "../IIndustry";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { MakeProductPopup } from "./MakeProductPopup";
|
import { MakeProductPopup } from "./MakeProductPopup";
|
||||||
import { ResearchPopup } from "./ResearchPopup";
|
import { ResearchPopup } from "./ResearchPopup";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
import { createPopup } from "../../ui/React/createPopup";
|
||||||
|
@ -20,7 +20,7 @@ import { SmartSupplyPopup } from "./SmartSupplyPopup";
|
|||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
import { createPopup } from "../../ui/React/createPopup";
|
||||||
|
|
||||||
import { isString } from "../../../utils/helpers/isString";
|
import { isString } from "../../utils/helpers/isString";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { ICorporation } from "../ICorporation";
|
||||||
import { IIndustry } from "../IIndustry";
|
import { IIndustry } from "../IIndustry";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
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 "../../../utils/DialogBox";
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { CorporationConstants } from "../data/Constants";
|
import { CorporationConstants } from "../data/Constants";
|
||||||
import { ICorporation } from "../ICorporation";
|
|
||||||
import { IssueDividends } from "../Actions";
|
import { IssueDividends } from "../Actions";
|
||||||
|
import { useCorporation } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
popupId: string;
|
open: boolean;
|
||||||
corp: ICorporation;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a popup that lets the player issue & manage dividends
|
// Create a popup that lets the player issue & manage dividends
|
||||||
// This is created when the player clicks the "Issue Dividends" button in the overview panel
|
// This is created when the player clicks the "Issue Dividends" button in the overview panel
|
||||||
export function IssueDividendsPopup(props: IProps): React.ReactElement {
|
export function IssueDividendsModal(props: IProps): React.ReactElement {
|
||||||
const [percent, setPercent] = useState<number | null>(null);
|
const corp = useCorporation();
|
||||||
|
const [percent, setPercent] = useState(0);
|
||||||
|
|
||||||
|
const canIssue = !isNaN(percent) && percent >= 0 && percent <= CorporationConstants.DividendMaxPercentage * 100;
|
||||||
function issueDividends(): void {
|
function issueDividends(): void {
|
||||||
|
if (!canIssue) return;
|
||||||
if (percent === null) return;
|
if (percent === null) return;
|
||||||
try {
|
try {
|
||||||
IssueDividends(props.corp, percent / 100);
|
IssueDividends(corp, percent / 100);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
dialogBoxCreate(err + "");
|
dialogBoxCreate(err + "");
|
||||||
}
|
}
|
||||||
|
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
@ -31,13 +36,17 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
if (event.target.value === "") setPercent(null);
|
if (event.target.value === "") setPercent(0);
|
||||||
else setPercent(parseFloat(event.target.value));
|
else {
|
||||||
|
let p = parseFloat(event.target.value);
|
||||||
|
if (p > 50) p = 50;
|
||||||
|
setPercent(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<p>
|
<Typography>
|
||||||
Dividends are a distribution of a portion of the corporation's profits to the shareholders. This includes
|
Dividends are a distribution of a portion of the corporation's profits to the shareholders. This includes
|
||||||
yourself, as well.
|
yourself, as well.
|
||||||
<br />
|
<br />
|
||||||
@ -58,19 +67,19 @@ export function IssueDividendsPopup(props: IProps): React.ReactElement {
|
|||||||
That means your corporation will gain $60m / sec in funds and the remaining $40m / sec will be paid as
|
That means your corporation will gain $60m / sec in funds and the remaining $40m / sec will be paid as
|
||||||
dividends. Since your corporation starts with 1 billion shares, every shareholder will be paid $0.04 per share
|
dividends. Since your corporation starts with 1 billion shares, every shareholder will be paid $0.04 per share
|
||||||
per second before taxes.
|
per second before taxes.
|
||||||
</p>
|
</Typography>
|
||||||
<input
|
<TextField
|
||||||
autoFocus={true}
|
variant="standard"
|
||||||
|
autoFocus
|
||||||
|
value={percent}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
className="text-input"
|
|
||||||
placeholder="Dividend %"
|
placeholder="Dividend %"
|
||||||
type="number"
|
type="number"
|
||||||
style={{ margin: "5px" }}
|
|
||||||
/>
|
/>
|
||||||
<button onClick={issueDividends} className="std-button" style={{ display: "inline-block" }}>
|
<Button disabled={!canIssue} sx={{ mx: 1 }} onClick={issueDividends}>
|
||||||
Allocate Dividend Percentage
|
Allocate Dividend Percentage
|
||||||
</button>
|
</Button>
|
||||||
</>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,24 +1,27 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||||
import { CorporationConstants } from "../data/Constants";
|
import { CorporationConstants } from "../data/Constants";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { useCorporation } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
interface IEffectTextProps {
|
interface IEffectTextProps {
|
||||||
corp: ICorporation;
|
|
||||||
shares: number | null;
|
shares: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EffectText(props: IEffectTextProps): React.ReactElement {
|
function EffectText(props: IEffectTextProps): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
if (props.shares === null) return <></>;
|
if (props.shares === null) return <></>;
|
||||||
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
|
const newSharePrice = Math.round(corp.sharePrice * 0.9);
|
||||||
const maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
|
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
|
||||||
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
||||||
let newShares = props.shares;
|
let newShares = props.shares;
|
||||||
if (isNaN(newShares)) {
|
if (isNaN(newShares)) {
|
||||||
return <p>Invalid input</p>;
|
return <Typography>Invalid input</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Round to nearest ten-millionth
|
// Round to nearest ten-millionth
|
||||||
@ -26,36 +29,37 @@ function EffectText(props: IEffectTextProps): React.ReactElement {
|
|||||||
newShares = Math.round(newShares) * 10e6;
|
newShares = Math.round(newShares) * 10e6;
|
||||||
|
|
||||||
if (newShares < 10e6) {
|
if (newShares < 10e6) {
|
||||||
return <p>Must issue at least 10 million new shares</p>;
|
return <Typography>Must issue at least 10 million new shares</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newShares > maxNewShares) {
|
if (newShares > maxNewShares) {
|
||||||
return <p>You cannot issue that many shares</p>;
|
return <Typography>You cannot issue that many shares</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p>
|
<Typography>
|
||||||
Issue ${numeralWrapper.format(newShares, "0.000a")} new shares for{" "}
|
Issue ${numeralWrapper.format(newShares, "0.000a")} new shares for{" "}
|
||||||
{numeralWrapper.formatMoney(newShares * newSharePrice)}?
|
{numeralWrapper.formatMoney(newShares * newSharePrice)}?
|
||||||
</p>
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
corp: ICorporation;
|
open: boolean;
|
||||||
popupId: string;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a popup that lets the player issue new shares
|
// Create a popup that lets the player issue new shares
|
||||||
// This is created when the player clicks the "Issue New Shares" buttons in the overview panel
|
// This is created when the player clicks the "Issue New Shares" buttons in the overview panel
|
||||||
export function IssueNewSharesPopup(props: IProps): React.ReactElement {
|
export function IssueNewSharesModal(props: IProps): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
const [shares, setShares] = useState<number | null>(null);
|
const [shares, setShares] = useState<number | null>(null);
|
||||||
const maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
|
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
|
||||||
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
||||||
|
|
||||||
function issueNewShares(): void {
|
function issueNewShares(): void {
|
||||||
if (shares === null) return;
|
if (shares === null) return;
|
||||||
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
|
const newSharePrice = Math.round(corp.sharePrice * 0.9);
|
||||||
let newShares = shares;
|
let newShares = shares;
|
||||||
if (isNaN(newShares)) {
|
if (isNaN(newShares)) {
|
||||||
dialogBoxCreate("Invalid input for number of new shares");
|
dialogBoxCreate("Invalid input for number of new shares");
|
||||||
@ -71,8 +75,8 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const profit = newShares * newSharePrice;
|
const profit = newShares * newSharePrice;
|
||||||
props.corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
|
corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
|
||||||
props.corp.totalShares += newShares;
|
corp.totalShares += newShares;
|
||||||
|
|
||||||
// Determine how many are bought by private investors
|
// Determine how many are bought by private investors
|
||||||
// Private investors get up to 50% at most
|
// Private investors get up to 50% at most
|
||||||
@ -80,16 +84,15 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
|
|||||||
let privateShares = getRandomInt(0, Math.round(newShares / 2));
|
let privateShares = getRandomInt(0, Math.round(newShares / 2));
|
||||||
privateShares = Math.round(privateShares / 1e6) * 1e6;
|
privateShares = Math.round(privateShares / 1e6) * 1e6;
|
||||||
|
|
||||||
props.corp.issuedShares += newShares - privateShares;
|
corp.issuedShares += newShares - privateShares;
|
||||||
props.corp.funds = props.corp.funds.plus(profit);
|
corp.funds = corp.funds.plus(profit);
|
||||||
props.corp.immediatelyUpdateSharePrice();
|
corp.immediatelyUpdateSharePrice();
|
||||||
|
props.onClose();
|
||||||
removePopup(props.popupId);
|
|
||||||
dialogBoxCreate(
|
dialogBoxCreate(
|
||||||
`Issued ${numeralWrapper.format(newShares, "0.000a")} and raised ` +
|
`Issued ${numeralWrapper.format(newShares, "0.000a")} and raised ` +
|
||||||
`${numeralWrapper.formatMoney(profit)}. ${numeralWrapper.format(privateShares, "0.000a")} ` +
|
`${numeralWrapper.formatMoney(profit)}. ${numeralWrapper.format(privateShares, "0.000a")} ` +
|
||||||
`of these shares were bought by private investors.<br><br>` +
|
`of these shares were bought by private investors.<br><br>` +
|
||||||
`Stock price decreased to ${numeralWrapper.formatMoney(props.corp.sharePrice)}`,
|
`Stock price decreased to ${numeralWrapper.formatMoney(corp.sharePrice)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,8 +106,8 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<p>
|
<Typography>
|
||||||
You can issue new equity shares (i.e. stocks) in order to raise capital for your corporation.
|
You can issue new equity shares (i.e. stocks) in order to raise capital for your corporation.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@ -122,19 +125,12 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
|
|||||||
When you choose to issue new equity, private shareholders have first priority for up to 50% of the new shares.
|
When you choose to issue new equity, private shareholders have first priority for up to 50% of the new shares.
|
||||||
If they choose to exercise this option, these newly issued shares become private, restricted shares, which means
|
If they choose to exercise this option, these newly issued shares become private, restricted shares, which means
|
||||||
you cannot buy them back.
|
you cannot buy them back.
|
||||||
</p>
|
</Typography>
|
||||||
<EffectText corp={props.corp} shares={shares} />
|
<EffectText shares={shares} />
|
||||||
<input
|
<TextField variant="standard" autoFocus placeholder="# New Shares" onChange={onChange} onKeyDown={onKeyDown} />
|
||||||
className="text-input"
|
<Button onClick={issueNewShares} sx={{ mx: 1 }}>
|
||||||
autoFocus={true}
|
|
||||||
placeholder="# New Shares"
|
|
||||||
style={{ margin: "5px" }}
|
|
||||||
onChange={onChange}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
/>
|
|
||||||
<button onClick={issueNewShares} className="std-button" style={{ display: "inline-block" }}>
|
|
||||||
Issue New Shares
|
Issue New Shares
|
||||||
</button>
|
</Button>
|
||||||
</>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,23 +1,23 @@
|
|||||||
// React components for the levelable upgrade buttons on the overview panel
|
// React components for the levelable upgrade buttons on the overview panel
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { ICorporation } from "../ICorporation";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { CorporationUpgrade } from "../data/CorporationUpgrades";
|
import { CorporationUpgrade } from "../data/CorporationUpgrades";
|
||||||
import { LevelUpgrade } from "../Actions";
|
import { LevelUpgrade } from "../Actions";
|
||||||
import { MoneyCost } from "./MoneyCost";
|
import { MoneyCost } from "./MoneyCost";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import { useCorporation } from "./Context";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
upgrade: CorporationUpgrade;
|
upgrade: CorporationUpgrade;
|
||||||
corp: ICorporation;
|
|
||||||
player: IPlayer;
|
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LevelableUpgrade(props: IProps): React.ReactElement {
|
export function LevelableUpgrade(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const corp = useCorporation();
|
||||||
const data = props.upgrade;
|
const data = props.upgrade;
|
||||||
const level = props.corp.upgrades[data[0]];
|
const level = corp.upgrades[data[0]];
|
||||||
|
|
||||||
const baseCost = data[1];
|
const baseCost = data[1];
|
||||||
const priceMult = data[2];
|
const priceMult = data[2];
|
||||||
@ -25,14 +25,14 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
const text = (
|
const text = (
|
||||||
<>
|
<>
|
||||||
{data[4]} - <MoneyCost money={cost} corp={props.corp} />
|
{data[4]} - <MoneyCost money={cost} corp={corp} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
const tooltip = data[5];
|
const tooltip = data[5];
|
||||||
function onClick(): void {
|
function onClick(): void {
|
||||||
if (props.corp.funds.lt(cost)) return;
|
if (corp.funds.lt(cost)) return;
|
||||||
try {
|
try {
|
||||||
LevelUpgrade(props.corp, props.upgrade);
|
LevelUpgrade(corp, props.upgrade);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
dialogBoxCreate(err + "");
|
dialogBoxCreate(err + "");
|
||||||
}
|
}
|
||||||
|
@ -5,42 +5,28 @@ import React from "react";
|
|||||||
|
|
||||||
import { CityTabs } from "./CityTabs";
|
import { CityTabs } from "./CityTabs";
|
||||||
import { IIndustry } from "../IIndustry";
|
import { IIndustry } from "../IIndustry";
|
||||||
import { Overview } from "./Overview";
|
import { useCorporation } from "./Context";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
|
||||||
import { CityName } from "../../Locations/data/CityNames";
|
import { CityName } from "../../Locations/data/CityNames";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { ICorporation } from "../ICorporation";
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
corp: ICorporation;
|
|
||||||
player: IPlayer;
|
|
||||||
divisionName: string;
|
divisionName: string;
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MainPanel(props: IProps): React.ReactElement {
|
export function MainPanel(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const corp = useCorporation();
|
||||||
const division =
|
const division =
|
||||||
props.divisionName !== "Overview"
|
props.divisionName !== "Overview"
|
||||||
? props.corp.divisions.find((division: IIndustry) => division.name === props.divisionName)
|
? corp.divisions.find((division: IIndustry) => division.name === props.divisionName)
|
||||||
: undefined; // use undefined because find returns undefined
|
: undefined; // use undefined because find returns undefined
|
||||||
|
|
||||||
if (division === undefined) {
|
if (division === undefined) throw new Error("Cannot find division");
|
||||||
return (
|
return (
|
||||||
<div id="cmpy-mgmt-panel">
|
<div id="cmpy-mgmt-panel">
|
||||||
<Overview {...props} />
|
<CityTabs rerender={props.rerender} division={division} corp={corp} city={CityName.Sector12} player={player} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div id="cmpy-mgmt-panel">
|
|
||||||
<CityTabs
|
|
||||||
rerender={props.rerender}
|
|
||||||
division={division}
|
|
||||||
corp={props.corp}
|
|
||||||
city={CityName.Sector12}
|
|
||||||
player={props.player}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { removePopup } from "../../ui/React/createPopup";
|
||||||
import { Industries } from "../IndustryData";
|
import { Industries } from "../IndustryData";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { ICorporation } from "../ICorporation";
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { Industries, IndustryDescriptions } from "../IndustryData";
|
|
||||||
import { ICorporation } from "../ICorporation";
|
|
||||||
import { IIndustry } from "../IIndustry";
|
|
||||||
import { NewIndustry } from "../Actions";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
corp: ICorporation;
|
|
||||||
popupId: string;
|
|
||||||
setDivisionName: (name: string) => void;
|
|
||||||
}
|
|
||||||
// Create a popup that lets the player create a new industry.
|
|
||||||
// This is created when the player clicks the "Expand into new Industry" header tab
|
|
||||||
export function NewIndustryPopup(props: IProps): React.ReactElement {
|
|
||||||
const allIndustries = Object.keys(Industries).sort();
|
|
||||||
const possibleIndustries = allIndustries
|
|
||||||
.filter(
|
|
||||||
(industryType: string) =>
|
|
||||||
props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
|
|
||||||
)
|
|
||||||
.sort();
|
|
||||||
const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : "");
|
|
||||||
const [name, setName] = useState("");
|
|
||||||
|
|
||||||
function newIndustry(): void {
|
|
||||||
try {
|
|
||||||
NewIndustry(props.corp, industry, name);
|
|
||||||
} catch (err) {
|
|
||||||
dialogBoxCreate(err + "");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set routing to the new division so that the UI automatically switches to it
|
|
||||||
props.setDivisionName(name);
|
|
||||||
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onNameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
setName(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
|
||||||
if (event.keyCode === 13) newIndustry();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onIndustryChange(event: React.ChangeEvent<HTMLSelectElement>): void {
|
|
||||||
setIndustry(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const desc = IndustryDescriptions[industry];
|
|
||||||
if (desc === undefined) throw new Error(`Trying to create an industry that doesn't exists: '${industry}'`);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>Create a new division to expand into a new industry:</p>
|
|
||||||
<select className="dropdown" defaultValue={industry} onChange={onIndustryChange}>
|
|
||||||
{possibleIndustries.map((industry: string) => (
|
|
||||||
<option key={industry} value={industry}>
|
|
||||||
{industry}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<p>{desc(props.corp)}</p>
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<p>Division name:</p>
|
|
||||||
<input
|
|
||||||
autoFocus={true}
|
|
||||||
value={name}
|
|
||||||
onChange={onNameChange}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
type="text"
|
|
||||||
className="text-input"
|
|
||||||
style={{ display: "block" }}
|
|
||||||
maxLength={30}
|
|
||||||
pattern="[a-zA-Z0-9-_]"
|
|
||||||
/>
|
|
||||||
<span onClick={newIndustry} className="popup-box-button">
|
|
||||||
Create Division
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,14 +1,15 @@
|
|||||||
// React Component for displaying Corporation Overview info
|
// React Component for displaying Corporation Overview info
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { LevelableUpgrade } from "./LevelableUpgrade";
|
import { LevelableUpgrade } from "./LevelableUpgrade";
|
||||||
import { UnlockUpgrade } from "./UnlockUpgrade";
|
import { UnlockUpgrade } from "./UnlockUpgrade";
|
||||||
import { BribeFactionPopup } from "./BribeFactionPopup";
|
import { BribeFactionModal } from "./BribeFactionModal";
|
||||||
import { SellSharesPopup } from "./SellSharesPopup";
|
import { SellSharesModal } from "./SellSharesModal";
|
||||||
import { BuybackSharesPopup } from "./BuybackSharesPopup";
|
import { BuybackSharesModal } from "./BuybackSharesModal";
|
||||||
import { IssueDividendsPopup } from "./IssueDividendsPopup";
|
import { IssueDividendsModal } from "./IssueDividendsModal";
|
||||||
import { IssueNewSharesPopup } from "./IssueNewSharesPopup";
|
import { IssueNewSharesModal } from "./IssueNewSharesModal";
|
||||||
import { FindInvestorsPopup } from "./FindInvestorsPopup";
|
import { FindInvestorsModal } from "./FindInvestorsModal";
|
||||||
import { GoPublicPopup } from "./GoPublicPopup";
|
import { GoPublicModal } from "./GoPublicModal";
|
||||||
|
import { Factions } from "../../Faction/Factions";
|
||||||
|
|
||||||
import { CorporationConstants } from "../data/Constants";
|
import { CorporationConstants } from "../data/Constants";
|
||||||
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
|
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
|
||||||
@ -16,45 +17,53 @@ import { CorporationUpgrade, CorporationUpgrades } from "../data/CorporationUpgr
|
|||||||
|
|
||||||
import { CONSTANTS } from "../../Constants";
|
import { CONSTANTS } from "../../Constants";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { use } from "../../ui/Context";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { useCorporation } from "./Context";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
corp: ICorporation;
|
|
||||||
player: IPlayer;
|
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
export function Overview({ corp, player, rerender }: IProps): React.ReactElement {
|
export function Overview({ rerender }: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const corp = useCorporation();
|
||||||
const profit: number = corp.revenue.minus(corp.expenses).toNumber();
|
const profit: number = corp.revenue.minus(corp.expenses).toNumber();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<Typography>
|
||||||
Total Funds: <Money money={corp.funds.toNumber()} />
|
Total Funds: <Money money={corp.funds.toNumber()} />
|
||||||
<br />
|
<br />
|
||||||
Total Revenue: <Money money={corp.revenue.toNumber()} /> / s<br />
|
Total Revenue: <Money money={corp.revenue.toNumber()} /> / s<br />
|
||||||
Total Expenses: <Money money={corp.expenses.toNumber()} /> / s
|
Total Expenses: <Money money={corp.expenses.toNumber()} /> / s
|
||||||
<br />
|
<br />
|
||||||
Total Profits: <Money money={profit} /> / s<br />
|
Total Profits: <Money money={profit} /> / s<br />
|
||||||
<DividendsStats corp={corp} profit={profit} />
|
<DividendsStats profit={profit} />
|
||||||
Publicly Traded: {corp.public ? "Yes" : "No"}
|
Publicly Traded: {corp.public ? "Yes" : "No"}
|
||||||
<br />
|
<br />
|
||||||
Owned Stock Shares: {numeralWrapper.format(corp.numShares, "0.000a")}
|
Owned Stock Shares: {numeralWrapper.format(corp.numShares, "0.000a")}
|
||||||
<br />
|
<br />
|
||||||
Stock Price: {corp.public ? <Money money={corp.sharePrice} /> : "N/A"}
|
Stock Price: {corp.public ? <Money money={corp.sharePrice} /> : "N/A"}
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</Typography>
|
||||||
<p className="tooltip">
|
<Tooltip
|
||||||
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
|
disableInteractive
|
||||||
<span className="tooltiptext">
|
title={
|
||||||
Outstanding Shares: {numeralWrapper.format(corp.issuedShares, "0.000a")}
|
<Typography>
|
||||||
<br />
|
Outstanding Shares: {numeralWrapper.format(corp.issuedShares, "0.000a")}
|
||||||
Private Shares: {numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a")}
|
<br />
|
||||||
</span>
|
Private Shares: {numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a")}
|
||||||
</p>
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography className="tooltip">
|
||||||
|
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<Mult name="Production Multiplier: " mult={corp.getProductionMultiplier()} />
|
<Mult name="Production Multiplier: " mult={corp.getProductionMultiplier()} />
|
||||||
@ -67,100 +76,78 @@ export function Overview({ corp, player, rerender }: IProps): React.ReactElement
|
|||||||
<Mult name="Sales Multiplier: " mult={corp.getSalesMultiplier()} />
|
<Mult name="Sales Multiplier: " mult={corp.getSalesMultiplier()} />
|
||||||
<Mult name="Scientific Research Multiplier: " mult={corp.getScientificResearchMultiplier()} />
|
<Mult name="Scientific Research Multiplier: " mult={corp.getScientificResearchMultiplier()} />
|
||||||
<br />
|
<br />
|
||||||
<BonusTime corp={corp} />
|
<BonusTime />
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Tooltip
|
||||||
className="a-link-button"
|
disableInteractive
|
||||||
display="inline-block"
|
title={
|
||||||
onClick={() => corp.getStarterGuide(player)}
|
<Typography>
|
||||||
text="Getting Started Guide"
|
Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' This is a .lit file
|
||||||
tooltip={
|
that guides you through the beginning of setting up a Corporation and provides some tips/pointers for
|
||||||
"Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' " +
|
helping you get started with managing it.
|
||||||
"This is a .lit file that guides you through the beginning of setting up a Corporation and " +
|
</Typography>
|
||||||
"provides some tips/pointers for helping you get started with managing it."
|
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
{corp.public ? (
|
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
|
||||||
<PublicButtons corp={corp} player={player} rerender={rerender} />
|
</Tooltip>
|
||||||
) : (
|
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
|
||||||
<PrivateButtons corp={corp} player={player} rerender={rerender} />
|
<BribeButton />
|
||||||
)}
|
|
||||||
<BribeButton corp={corp} player={player} />
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<Upgrades corp={corp} player={player} rerender={rerender} />
|
<Upgrades rerender={rerender} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPrivateButtonsProps {
|
interface IPrivateButtonsProps {
|
||||||
corp: ICorporation;
|
|
||||||
player: IPlayer;
|
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
// Render the buttons for when your Corporation is still private
|
// Render the buttons for when your Corporation is still private
|
||||||
function PrivateButtons({ corp, player, rerender }: IPrivateButtonsProps): React.ReactElement {
|
function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement {
|
||||||
function openFindInvestorsPopup(): void {
|
const player = use.Player();
|
||||||
const popupId = "cmpy-mgmt-find-investors-popup";
|
const corp = useCorporation();
|
||||||
createPopup(popupId, FindInvestorsPopup, {
|
const [findInvestorsopen, setFindInvestorsopen] = useState(false);
|
||||||
rerender,
|
const [goPublicopen, setGoPublicopen] = useState(false);
|
||||||
player,
|
|
||||||
popupId,
|
|
||||||
corp: corp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openGoPublicPopup(): void {
|
|
||||||
const popupId = "cmpy-mgmt-go-public-popup";
|
|
||||||
createPopup(popupId, GoPublicPopup, {
|
|
||||||
rerender,
|
|
||||||
player,
|
|
||||||
popupId,
|
|
||||||
corp: corp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const fundingAvailable = corp.fundingRound < 4;
|
const fundingAvailable = corp.fundingRound < 4;
|
||||||
const findInvestorsClassName = fundingAvailable ? "std-button" : "a-link-button-inactive";
|
|
||||||
const findInvestorsTooltip = fundingAvailable
|
const findInvestorsTooltip = fundingAvailable
|
||||||
? "Search for private investors who will give you startup funding in exchangefor equity (stock shares) in your company"
|
? "Search for private investors who will give you startup funding in exchange for equity (stock shares) in your company"
|
||||||
: undefined;
|
: "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Tooltip disableInteractive title={<Typography>{findInvestorsTooltip}</Typography>}>
|
||||||
className={findInvestorsClassName}
|
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
|
||||||
onClick={openFindInvestorsPopup}
|
Find Investors
|
||||||
text="Find Investors"
|
</Button>
|
||||||
tooltip={findInvestorsTooltip}
|
</Tooltip>
|
||||||
display="inline-block"
|
<Tooltip
|
||||||
/>
|
disableInteractive
|
||||||
<Button
|
title={
|
||||||
className="std-button"
|
<Typography>
|
||||||
onClick={openGoPublicPopup}
|
Become a publicly traded and owned entity. Going public involves issuing shares for an IPO. Once you are a
|
||||||
display="inline-block"
|
public company, your shares will be traded on the stock market.
|
||||||
text="Go Public"
|
</Typography>
|
||||||
tooltip={
|
|
||||||
"Become a publicly traded and owned entity. Going public " +
|
|
||||||
"involves issuing shares for an IPO. Once you are a public " +
|
|
||||||
"company, your shares will be traded on the stock market."
|
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
<Button onClick={() => setGoPublicopen(true)}>Go Public</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<FindInvestorsModal open={findInvestorsopen} onClose={() => setFindInvestorsopen(false)} rerender={rerender} />
|
||||||
|
<GoPublicModal open={goPublicopen} onClose={() => setGoPublicopen(false)} rerender={rerender} />
|
||||||
<br />
|
<br />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUpgradeProps {
|
interface IUpgradeProps {
|
||||||
corp: ICorporation;
|
|
||||||
player: IPlayer;
|
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
// Render the UI for Corporation upgrades
|
// Render the UI for Corporation upgrades
|
||||||
function Upgrades({ corp, player, rerender }: IUpgradeProps): React.ReactElement {
|
function Upgrades({ rerender }: IUpgradeProps): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
// Don't show upgrades
|
// Don't show upgrades
|
||||||
if (corp.divisions.length <= 0) {
|
if (corp.divisions.length <= 0) {
|
||||||
return <h1>Upgrades are unlocked once you create an industry.</h1>;
|
return <Typography variant="h4">Upgrades are unlocked once you create an industry.</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -169,29 +156,32 @@ function Upgrades({ corp, player, rerender }: IUpgradeProps): React.ReactElement
|
|||||||
{Object.values(CorporationUnlockUpgrades)
|
{Object.values(CorporationUnlockUpgrades)
|
||||||
.filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0)
|
.filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0)
|
||||||
.map((upgrade: CorporationUnlockUpgrade) => (
|
.map((upgrade: CorporationUnlockUpgrade) => (
|
||||||
<UnlockUpgrade rerender={rerender} player={player} corp={corp} upgradeData={upgrade} key={upgrade[0]} />
|
<UnlockUpgrade rerender={rerender} upgradeData={upgrade} key={upgrade[0]} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1>
|
<h1 className={"cmpy-mgmt-upgrade-header"}> Upgrades </h1>
|
||||||
{corp.upgrades
|
{corp.upgrades
|
||||||
.map((level: number, i: number) => CorporationUpgrades[i])
|
.map((level: number, i: number) => CorporationUpgrades[i])
|
||||||
.map((upgrade: CorporationUpgrade) => (
|
.map((upgrade: CorporationUpgrade) => (
|
||||||
<LevelableUpgrade rerender={rerender} player={player} corp={corp} upgrade={upgrade} key={upgrade[0]} />
|
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade[0]} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPublicButtonsProps {
|
interface IPublicButtonsProps {
|
||||||
corp: ICorporation;
|
|
||||||
player: IPlayer;
|
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the buttons for when your Corporation has gone public
|
// Render the buttons for when your Corporation has gone public
|
||||||
function PublicButtons({ corp, player, rerender }: IPublicButtonsProps): React.ReactElement {
|
function PublicButtons({ rerender }: IPublicButtonsProps): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
|
const [sellSharesOpen, setSellSharesOpen] = useState(false);
|
||||||
|
const [buybackSharesOpen, setBuybackSharesOpen] = useState(false);
|
||||||
|
const [issueNewSharesOpen, setIssueNewSharesOpen] = useState(false);
|
||||||
|
const [issueDividendsOpen, setIssueDividendsOpen] = useState(false);
|
||||||
|
|
||||||
const sellSharesOnCd = corp.shareSaleCooldown > 0;
|
const sellSharesOnCd = corp.shareSaleCooldown > 0;
|
||||||
const sellSharesClass = sellSharesOnCd ? "a-link-button-inactive" : "std-button";
|
|
||||||
const sellSharesTooltip = sellSharesOnCd
|
const sellSharesTooltip = sellSharesOnCd
|
||||||
? "Cannot sell shares for " + corp.convertCooldownToString(corp.shareSaleCooldown)
|
? "Cannot sell shares for " + corp.convertCooldownToString(corp.shareSaleCooldown)
|
||||||
: "Sell your shares in the company. The money earned from selling your " +
|
: "Sell your shares in the company. The money earned from selling your " +
|
||||||
@ -199,139 +189,83 @@ function PublicButtons({ corp, player, rerender }: IPublicButtonsProps): React.R
|
|||||||
"This is one of the only ways to profit from your business venture.";
|
"This is one of the only ways to profit from your business venture.";
|
||||||
|
|
||||||
const issueNewSharesOnCd = corp.issueNewSharesCooldown > 0;
|
const issueNewSharesOnCd = corp.issueNewSharesCooldown > 0;
|
||||||
const issueNewSharesClass = issueNewSharesOnCd ? "a-link-button-inactive" : "std-button";
|
|
||||||
const issueNewSharesTooltip = issueNewSharesOnCd
|
const issueNewSharesTooltip = issueNewSharesOnCd
|
||||||
? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown)
|
? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown)
|
||||||
: "Issue new equity shares to raise capital.";
|
: "Issue new equity shares to raise capital.";
|
||||||
|
|
||||||
function openSellSharesPopup(): void {
|
|
||||||
const popupId = "cmpy-mgmt-sell-shares-popup";
|
|
||||||
createPopup(popupId, SellSharesPopup, {
|
|
||||||
corp: corp,
|
|
||||||
player,
|
|
||||||
popupId,
|
|
||||||
rerender,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openBuybackSharesPopup(): void {
|
|
||||||
const popupId = "corp-buyback-shares-popup";
|
|
||||||
createPopup(popupId, BuybackSharesPopup, {
|
|
||||||
rerender,
|
|
||||||
player,
|
|
||||||
popupId,
|
|
||||||
corp: corp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openIssueNewSharesPopup(): void {
|
|
||||||
const popupId = "cmpy-mgmt-issue-new-shares-popup";
|
|
||||||
createPopup(popupId, IssueNewSharesPopup, {
|
|
||||||
popupId,
|
|
||||||
corp: corp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openIssueDividendsPopup(): void {
|
|
||||||
const popupId = "cmpy-mgmt-issue-dividends-popup";
|
|
||||||
createPopup(popupId, IssueDividendsPopup, {
|
|
||||||
popupId,
|
|
||||||
corp: corp,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Tooltip disableInteractive title={<Typography>{sellSharesTooltip}</Typography>}>
|
||||||
className={sellSharesClass}
|
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
|
||||||
display="inline-block"
|
Sell Shares
|
||||||
onClick={openSellSharesPopup}
|
</Button>
|
||||||
text="Sell Shares"
|
</Tooltip>
|
||||||
tooltip={sellSharesTooltip}
|
<SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} />
|
||||||
/>
|
<Tooltip
|
||||||
<Button
|
disableInteractive
|
||||||
className="std-button"
|
title={<Typography>Buy back shares you that previously issued or sold at market price.</Typography>}
|
||||||
display="inline-block"
|
>
|
||||||
onClick={openBuybackSharesPopup}
|
<Button onClick={() => setBuybackSharesOpen(true)}>Buyback shares</Button>
|
||||||
text="Buyback shares"
|
</Tooltip>
|
||||||
tooltip="Buy back shares you that previously issued or sold at market price."
|
<BuybackSharesModal open={buybackSharesOpen} onClose={() => setBuybackSharesOpen(false)} rerender={rerender} />
|
||||||
/>
|
|
||||||
<br />
|
<br />
|
||||||
<Button
|
<Tooltip disableInteractive title={<Typography>{issueNewSharesTooltip}</Typography>}>
|
||||||
className={issueNewSharesClass}
|
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
|
||||||
display="inline-block"
|
Issue New Shares
|
||||||
onClick={openIssueNewSharesPopup}
|
</Button>
|
||||||
text="Issue New Shares"
|
</Tooltip>
|
||||||
tooltip={issueNewSharesTooltip}
|
<IssueNewSharesModal open={issueNewSharesOpen} onClose={() => setIssueNewSharesOpen(false)} />
|
||||||
/>
|
<Tooltip
|
||||||
<Button
|
disableInteractive
|
||||||
className="std-button"
|
title={<Typography>Manage the dividends that are paid out to shareholders (including yourself)</Typography>}
|
||||||
display="inline-block"
|
>
|
||||||
onClick={openIssueDividendsPopup}
|
<Button onClick={() => setIssueDividendsOpen(true)}>Issue Dividends</Button>
|
||||||
text="Issue Dividends"
|
</Tooltip>
|
||||||
tooltip="Manage the dividends that are paid out to shareholders (including yourself)"
|
<IssueDividendsModal open={issueDividendsOpen} onClose={() => setIssueDividendsOpen(false)} />
|
||||||
/>
|
|
||||||
<br />
|
<br />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic Function for Creating a button
|
function BribeButton(): React.ReactElement {
|
||||||
interface ICreateButtonProps {
|
const player = use.Player();
|
||||||
text: string;
|
const corp = useCorporation();
|
||||||
className?: string;
|
const [open, setOpen] = useState(false);
|
||||||
display?: string;
|
const canBribe =
|
||||||
tooltip?: string;
|
corp.determineValuation() >= CorporationConstants.BribeThreshold &&
|
||||||
onClick?: (event: React.MouseEvent) => void;
|
player.factions.filter((f) => Factions[f].getInfo().offersWork()).length > 0;
|
||||||
}
|
|
||||||
|
|
||||||
function Button({ className = "std-button", text, display, tooltip, onClick }: ICreateButtonProps): React.ReactElement {
|
function openBribe(): void {
|
||||||
const hasTooltip = tooltip != null;
|
if (!canBribe) return;
|
||||||
if (hasTooltip) className += " tooltip";
|
setOpen(true);
|
||||||
return (
|
|
||||||
<button className={className} onClick={onClick} style={{ display: display ? display : "block" }}>
|
|
||||||
{text}
|
|
||||||
{hasTooltip && <span className={"tooltiptext"}>{tooltip}</span>}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IBribeButtonProps {
|
|
||||||
player: IPlayer;
|
|
||||||
corp: ICorporation;
|
|
||||||
}
|
|
||||||
function BribeButton({ player, corp }: IBribeButtonProps): React.ReactElement {
|
|
||||||
function openBribeFactionPopup(): void {
|
|
||||||
const popupId = "corp-bribe-popup";
|
|
||||||
createPopup(popupId, BribeFactionPopup, {
|
|
||||||
player,
|
|
||||||
popupId,
|
|
||||||
corp: corp,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const canBribe = corp.determineValuation() >= CorporationConstants.BribeThreshold || true;
|
|
||||||
const bribeFactionsClass = canBribe ? "a-link-button" : "a-link-button-inactive";
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<>
|
||||||
className={bribeFactionsClass}
|
<Tooltip
|
||||||
display="inline-block"
|
disableInteractive
|
||||||
onClick={openBribeFactionPopup}
|
title={
|
||||||
text="Bribe Factions"
|
canBribe
|
||||||
tooltip={
|
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
|
||||||
canBribe
|
: "Your Corporation is not powerful enough to bribe Faction leaders"
|
||||||
? "Use your Corporations power and influence to bribe Faction leaders in exchange for reputation"
|
}
|
||||||
: "Your Corporation is not powerful enough to bribe Faction leaders"
|
>
|
||||||
}
|
<span>
|
||||||
/>
|
<Button disabled={!canBribe} onClick={openBribe}>
|
||||||
|
Bribe Factions
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
<BribeFactionModal open={open} onClose={() => setOpen(false)} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IDividendsStatsProps {
|
interface IDividendsStatsProps {
|
||||||
corp: ICorporation;
|
|
||||||
profit: number;
|
profit: number;
|
||||||
}
|
}
|
||||||
function DividendsStats({ corp, profit }: IDividendsStatsProps): React.ReactElement {
|
function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
if (corp.dividendPercentage <= 0 || profit <= 0) return <></>;
|
if (corp.dividendPercentage <= 0 || profit <= 0) return <></>;
|
||||||
const totalDividends = (corp.dividendPercentage / 100) * profit;
|
const totalDividends = (corp.dividendPercentage / 100) * profit;
|
||||||
const retainedEarnings = profit - totalDividends;
|
const retainedEarnings = profit - totalDividends;
|
||||||
@ -361,26 +295,24 @@ interface IMultProps {
|
|||||||
function Mult({ name, mult }: IMultProps): React.ReactElement {
|
function Mult({ name, mult }: IMultProps): React.ReactElement {
|
||||||
if (mult <= 1) return <></>;
|
if (mult <= 1) return <></>;
|
||||||
return (
|
return (
|
||||||
<p>
|
<Typography>
|
||||||
{name}
|
{name}
|
||||||
{numeralWrapper.format(mult, "0.000")}
|
{numeralWrapper.format(mult, "0.000")}
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IBonusTimeProps {
|
|
||||||
corp: ICorporation;
|
|
||||||
}
|
|
||||||
// Returns a string with general information about Corporation
|
// Returns a string with general information about Corporation
|
||||||
function BonusTime({ corp }: IBonusTimeProps): React.ReactElement {
|
function BonusTime(): React.ReactElement {
|
||||||
|
const corp = useCorporation();
|
||||||
const storedTime = corp.storedCycles * CONSTANTS.MilliPerCycle;
|
const storedTime = corp.storedCycles * CONSTANTS.MilliPerCycle;
|
||||||
if (storedTime <= 15000) return <></>;
|
if (storedTime <= 15000) return <></>;
|
||||||
return (
|
return (
|
||||||
<p>
|
<Typography>
|
||||||
Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}
|
Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { removePopup } from "../../ui/React/createPopup";
|
||||||
import { MaterialSizes } from "../MaterialSizes";
|
import { MaterialSizes } from "../MaterialSizes";
|
||||||
import { Warehouse } from "../Warehouse";
|
import { Warehouse } from "../Warehouse";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { removePopup } from "../../ui/React/createPopup";
|
||||||
import { IndustryResearchTrees } from "../IndustryData";
|
import { IndustryResearchTrees } from "../IndustryData";
|
||||||
import { CorporationConstants } from "../data/Constants";
|
import { CorporationConstants } from "../data/Constants";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { removePopup } from "../../ui/React/createPopup";
|
||||||
import { ICorporation } from "../ICorporation";
|
import { ICorporation } from "../ICorporation";
|
||||||
import { Material } from "../Material";
|
import { Material } from "../Material";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { removePopup } from "../../ui/React/createPopup";
|
||||||
import { Product } from "../Product";
|
import { Product } from "../Product";
|
||||||
import { SellProduct } from "../Actions";
|
import { SellProduct } from "../Actions";
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user