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 {
|
||||
background-color: var(--my-background-color);
|
||||
padding: 12px;
|
||||
border: 5px solid var(--my-highlight-color);
|
||||
border: 2px solid $hacker-green;
|
||||
width: 70%;
|
||||
max-height: 80%;
|
||||
overflow-y: auto;
|
||||
z-index: 11; /* Sit on top of the container */
|
||||
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 {
|
||||
@ -89,13 +90,13 @@
|
||||
max-height: 50%;
|
||||
z-index: 10;
|
||||
background-color: var(--my-background-color);
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
border: 2px solid $hacker-green;
|
||||
}
|
||||
|
||||
.log-box-header {
|
||||
z-index: 1300;
|
||||
background-color: #333;
|
||||
border: 1px solid var(--my-highlight-color);
|
||||
border: 2px solid $hacker-green;
|
||||
display: flex;
|
||||
flex: row nowrap;
|
||||
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>
|
||||
<body>
|
||||
<div id="entire-game-container">
|
||||
<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>
|
||||
<div id="root"/>
|
||||
<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>
|
||||
</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 {
|
||||
background-color: var(--my-background-color);
|
||||
padding: 12px;
|
||||
border: 5px solid var(--my-highlight-color);
|
||||
border: 2px solid #adff2f;
|
||||
width: 70%;
|
||||
max-height: 80%;
|
||||
overflow-y: auto;
|
||||
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 {
|
||||
margin: 2px; }
|
||||
@ -2169,12 +2170,12 @@ input[type="checkbox"] {
|
||||
max-height: 50%;
|
||||
z-index: 10;
|
||||
background-color: var(--my-background-color);
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
border: 2px solid #adff2f; }
|
||||
|
||||
.log-box-header {
|
||||
z-index: 1300;
|
||||
background-color: #333;
|
||||
border: 1px solid var(--my-highlight-color);
|
||||
border: 2px solid #adff2f;
|
||||
display: flex;
|
||||
flex: row nowrap;
|
||||
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 { 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 {
|
||||
info: string | JSX.Element;
|
||||
|
@ -11,8 +11,8 @@ import { prestigeAugmentation } from "../Prestige";
|
||||
import { Programs } from "../Programs/Programs";
|
||||
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { clearObject } from "../../utils/helpers/clearObject";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { clearObject } from "../utils/helpers/clearObject";
|
||||
|
||||
import { WHRNG } from "../Casino/RNG";
|
||||
|
||||
@ -2044,6 +2044,28 @@ function initAugmentations(): void {
|
||||
}
|
||||
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
|
||||
const BladeburnersFactionName = "Bladeburners";
|
||||
if (factionExists(BladeburnersFactionName)) {
|
||||
|
@ -41,6 +41,7 @@ export const AugmentationNames: IMap<string> = {
|
||||
CranialSignalProcessorsG4: "Cranial Signal Processors - Gen IV",
|
||||
CranialSignalProcessorsG5: "Cranial Signal Processors - Gen V",
|
||||
NeuronalDensification: "Neuronal Densification",
|
||||
NeuroreceptorManager: "Neuroreceptor Management Implant",
|
||||
NuoptimalInjectorImplant: "Nuoptimal Nootropic Injector Implant",
|
||||
SpeechEnhancement: "Speech Enhancement",
|
||||
FocusWire: "FocusWire",
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Root React component for the Augmentations UI page that display all of your
|
||||
* owned and purchased Augmentations and Source-Files.
|
||||
*/
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { InstalledAugmentations } from "./InstalledAugmentations";
|
||||
import { PlayerMultipliers } from "./PlayerMultipliers";
|
||||
@ -23,10 +23,17 @@ interface IProps {
|
||||
|
||||
export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((o) => !o);
|
||||
}
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 200);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
function doExport(): void {
|
||||
props.exportGameFn();
|
||||
setRerender((o) => !o);
|
||||
rerender();
|
||||
}
|
||||
|
||||
function exportBonusStr(): string {
|
||||
@ -63,13 +70,11 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||
</Typography>
|
||||
<Box mx={2}>
|
||||
<Tooltip title={"'I never asked for this'"}>
|
||||
<Button onClick={props.installAugmentationsFn}>
|
||||
<Typography>Install Augmentations</Typography>
|
||||
</Button>
|
||||
<Button onClick={props.installAugmentationsFn}>Install Augmentations</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={"It's always a good idea to backup/export your save!"}>
|
||||
<Button sx={{ mx: 2 }} onClick={doExport}>
|
||||
<Typography color="error">Backup Save {exportBonusStr()}</Typography>
|
||||
<Button sx={{ mx: 2 }} onClick={doExport} color="error">
|
||||
Backup Save {exportBonusStr()}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<PurchasedAugmentations />
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from "react";
|
||||
import { Player } from "../Player";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { addOffset } from "../../utils/helpers/addOffset";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { addOffset } from "../utils/helpers/addOffset";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
import { IBladeburner } from "./IBladeburner";
|
||||
import { IAction, ISuccessChanceParams } from "./IAction";
|
||||
@ -20,7 +21,6 @@ class StatsMultiplier {
|
||||
|
||||
export interface IActionParams {
|
||||
name?: string;
|
||||
desc?: string;
|
||||
level?: number;
|
||||
maxLevel?: number;
|
||||
autoLevel?: boolean;
|
||||
@ -43,7 +43,6 @@ export interface IActionParams {
|
||||
|
||||
export class Action implements IAction {
|
||||
name = "";
|
||||
desc = "";
|
||||
|
||||
// Difficulty scales with level. See getDifficulty() method
|
||||
level = 1;
|
||||
@ -100,7 +99,6 @@ export class Action implements IAction {
|
||||
constructor(params: IActionParams | null = null) {
|
||||
// | null = null
|
||||
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.difficultyFac) this.difficultyFac = params.difficultyFac;
|
@ -1,5 +1,5 @@
|
||||
import { IActionIdentifier } from "./IActionIdentifier";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
|
||||
interface IParams {
|
||||
name?: string;
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
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 { IActionIdentifier } from "./IActionIdentifier";
|
||||
import { ActionIdentifier } from "./ActionIdentifier";
|
||||
@ -9,7 +10,7 @@ import { BlackOperation } from "./BlackOperation";
|
||||
import { Operation } from "./Operation";
|
||||
import { Contract } from "./Contract";
|
||||
import { GeneralActions } from "./GeneralActions";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||
import { Skills } from "./Skills";
|
||||
import { Skill } from "./Skill";
|
||||
import { City } from "./City";
|
||||
@ -17,21 +18,21 @@ import { IAction } from "./IAction";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IRouter } from "../ui/Router";
|
||||
import { ConsoleHelpText } from "./data/Help";
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { addOffset } from "../../utils/helpers/addOffset";
|
||||
import { addOffset } from "../utils/helpers/addOffset";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { Factions, factionExists } from "../Faction/Factions";
|
||||
import { calculateHospitalizationCost } from "../Hospital/Hospital";
|
||||
import { redPillFlag } from "../RedPill";
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { getTimestamp } from "../../utils/helpers/getTimestamp";
|
||||
import { getTimestamp } from "../utils/helpers/getTimestamp";
|
||||
import { joinFaction } from "../Faction/FactionHelpers";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
|
||||
@ -1586,11 +1587,6 @@ export class Bladeburner implements IBladeburner {
|
||||
create(): void {
|
||||
this.contracts["Tracking"] = new Contract({
|
||||
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,
|
||||
difficultyFac: 1.02,
|
||||
rewardFac: 1.041,
|
||||
@ -1619,10 +1615,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.contracts["Bounty Hunter"] = new Contract({
|
||||
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,
|
||||
difficultyFac: 1.04,
|
||||
rewardFac: 1.085,
|
||||
@ -1651,10 +1643,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.contracts["Retirement"] = new Contract({
|
||||
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,
|
||||
difficultyFac: 1.03,
|
||||
rewardFac: 1.065,
|
||||
@ -1684,12 +1672,6 @@ export class Bladeburner implements IBladeburner {
|
||||
|
||||
this.operations["Investigation"] = new Operation({
|
||||
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,
|
||||
difficultyFac: 1.03,
|
||||
rewardFac: 1.07,
|
||||
@ -1719,11 +1701,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Undercover Operation"] = new 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,
|
||||
difficultyFac: 1.04,
|
||||
rewardFac: 1.09,
|
||||
@ -1754,7 +1731,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Sting Operation"] = new Operation({
|
||||
name: "Sting Operation",
|
||||
desc: "Conduct a sting operation to bait and capture particularly " + "notorious Synthoid criminals.",
|
||||
baseDifficulty: 650,
|
||||
difficultyFac: 1.04,
|
||||
rewardFac: 1.095,
|
||||
@ -1785,10 +1761,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Raid"] = new Operation({
|
||||
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,
|
||||
difficultyFac: 1.045,
|
||||
rewardFac: 1.1,
|
||||
@ -1819,10 +1791,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Stealth Retirement Operation"] = new 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,
|
||||
difficultyFac: 1.05,
|
||||
rewardFac: 1.11,
|
||||
@ -1854,10 +1822,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Assassination"] = new Operation({
|
||||
name: "Assassination",
|
||||
desc:
|
||||
"Assassinate Synthoids that have been identified as " +
|
||||
"important, high-profile social and political leaders " +
|
||||
"in the Synthoid communities.",
|
||||
baseDifficulty: 1500,
|
||||
difficultyFac: 1.06,
|
||||
rewardFac: 1.14,
|
||||
@ -1900,7 +1864,7 @@ export class Bladeburner implements IBladeburner {
|
||||
if (this.action.type !== ActionTypes["Idle"]) {
|
||||
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
|
||||
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;
|
||||
}
|
||||
if (!Settings.SuppressBladeburnerPopup) {
|
@ -1,7 +1,7 @@
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { addOffset } from "../../utils/helpers/addOffset";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { addOffset } from "../utils/helpers/addOffset";
|
||||
|
||||
interface IChangePopulationByCountParams {
|
||||
estChange: number;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IBladeburner } from "./IBladeburner";
|
||||
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 {
|
||||
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 {
|
||||
name: string;
|
||||
desc: string;
|
||||
|
||||
// Difficulty scales with level. See getDifficulty() method
|
||||
level: number;
|
@ -1,7 +1,7 @@
|
||||
import { IBladeburner } from "./IBladeburner";
|
||||
import { BladeburnerConstants } from "./data/Constants";
|
||||
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 {
|
||||
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: {
|
||||
[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 { ContractPage } from "./ContractPage";
|
||||
import { OperationPage } from "./OperationPage";
|
||||
import { BlackOpPage } from "./BlackOpPage";
|
||||
import { SkillPage } from "./SkillPage";
|
||||
import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
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 {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function AllPages(props: IProps): React.ReactElement {
|
||||
const [page, setPage] = useState("General");
|
||||
const setRerender = useState(false)[1];
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||
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>
|
||||
);
|
||||
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||
setValue(tab);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header name={"General"} />
|
||||
<Header name={"Contracts"} />
|
||||
<Header name={"Operations"} />
|
||||
<Header name={"BlackOps"} />
|
||||
<Header name={"Skills"} />
|
||||
<div style={{ display: "block", margin: "4px", padding: "4px" }}>
|
||||
{page === "General" && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "Contracts" && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "Operations" && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "BlackOps" && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "Skills" && <SkillPage bladeburner={props.bladeburner} />}
|
||||
</div>
|
||||
<span className="text">
|
||||
{stealthIcon} = This action requires stealth, {killIcon} = This action involves retirement
|
||||
</span>
|
||||
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
|
||||
<Tab label="General" />
|
||||
<Tab label="Contracts" />
|
||||
<Tab label="Operations" />
|
||||
<Tab label="BlackOps" />
|
||||
<Tab label="Skills" />
|
||||
</Tabs>
|
||||
<Box sx={{ p: 1 }}>
|
||||
{value === 0 && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 1 && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 2 && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 3 && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 4 && <SkillPage bladeburner={props.bladeburner} />}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
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 { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { TeamSizePopup } from "./TeamSizePopup";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { TeamSizeButton } from "./TeamSizeButton";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { BlackOperation } from "../BlackOperation";
|
||||
import { BlackOperations } from "../data/BlackOperations";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
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 {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: BlackOperation;
|
||||
}
|
||||
|
||||
export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
|
||||
if (isCompleted) {
|
||||
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>;
|
||||
@ -26,7 +33,6 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
const isActive =
|
||||
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
|
||||
props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
@ -34,70 +40,54 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes.BlackOperation;
|
||||
props.bladeburner.action.name = 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,
|
||||
});
|
||||
const actionData = BlackOperations[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Typography>
|
||||
{isActive ? (
|
||||
<>
|
||||
<>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
</>
|
||||
) : (
|
||||
<CopyableText value={props.action.name} />
|
||||
)}
|
||||
</h2>
|
||||
{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>
|
||||
<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} />
|
||||
</>
|
||||
)}
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<p style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
||||
<Typography>{actionData.desc}</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<p style={{ display: "block", color: hasReqdRank ? "white" : "red" }}>
|
||||
<Typography color={hasReqdRank ? "primary" : "error"}>
|
||||
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
||||
</p>
|
||||
</Typography>
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
Estimated Success Chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
||||
{props.action.isStealth ? stealthIcon : <></>}
|
||||
{props.action.isKill ? killIcon : <></>}
|
||||
<Typography>
|
||||
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||
<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
</pre>
|
||||
</>
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -35,9 +35,7 @@ export function BlackOpList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{blackops.map((blackop: BlackOperation) => (
|
||||
<li key={blackop.name} className="bladeburner-action">
|
||||
<BlackOpElem bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
||||
</li>
|
||||
<BlackOpElem key={blackop.name} bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { BlackOpList } from "./BlackOpList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,7 +12,7 @@ interface IProps {
|
||||
export function BlackOpPage(props: IProps): React.ReactElement {
|
||||
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
|
||||
successively by completing the one before it.
|
||||
<br />
|
||||
@ -21,7 +22,7 @@ export function BlackOpPage(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank
|
||||
losses.
|
||||
</p>
|
||||
</Typography>
|
||||
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
|
||||
</>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { use } from "../../ui/Context";
|
||||
import { CinematicText } from "../../ui/React/CinematicText";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
|
||||
export function BladeburnerCinematic(): React.ReactElement {
|
||||
const router = use.Router();
|
||||
|
@ -1,42 +1,39 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Stats } from "./Stats";
|
||||
import { Console } from "./Console";
|
||||
import { AllPages } from "./AllPages";
|
||||
|
||||
import { use } from "../../ui/Context";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
export function BladeburnerRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
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;
|
||||
if (bladeburner === null) return <></>;
|
||||
return (
|
||||
<div className="bladeburner-container">
|
||||
<div style={{ height: "60%", display: "block", position: "relative" }}>
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "30%",
|
||||
display: "inline-block",
|
||||
border: "1px solid white",
|
||||
}}
|
||||
>
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Grid container>
|
||||
<Grid item xs={6}>
|
||||
<Stats bladeburner={bladeburner} player={player} router={router} />
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Console bladeburner={bladeburner} player={player} />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: "70%",
|
||||
display: "block",
|
||||
border: "1px solid white",
|
||||
marginTop: "6px",
|
||||
padding: "6px",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<AllPages bladeburner={bladeburner} player={player} />
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -2,18 +2,48 @@ import React, { useState, useRef, useEffect } from "react";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
|
||||
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 {
|
||||
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 {
|
||||
return (
|
||||
<tr>
|
||||
<td className="bladeburner-console-line" style={{ color: "var(--my-font-color)", whiteSpace: "pre-wrap" }}>
|
||||
{props.content}
|
||||
</td>
|
||||
</tr>
|
||||
<ListItem sx={{ p: 0 }}>
|
||||
<Typography>{props.content}</Typography>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
@ -23,15 +53,21 @@ interface IProps {
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setCommand(event.target.value);
|
||||
}
|
||||
|
||||
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
|
||||
|
||||
// TODO: Figure out how to actually make the scrolling work correctly.
|
||||
function scrollToBottom(): void {
|
||||
if (!lastRef.current) return;
|
||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
||||
if (!scrollHook.current) return;
|
||||
scrollHook.current.scrollTop = scrollHook.current.scrollHeight;
|
||||
}
|
||||
|
||||
function rerender(): void {
|
||||
@ -50,13 +86,11 @@ export function Console(props: IProps): React.ReactElement {
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
const command = event.currentTarget.value;
|
||||
event.currentTarget.value = "";
|
||||
if (command.length > 0) {
|
||||
props.bladeburner.postToConsole("> " + command);
|
||||
props.bladeburner.executeConsoleCommands(props.player, command);
|
||||
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
|
||||
rerender();
|
||||
setCommand("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,31 +139,35 @@ export function Console(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={lastRef} className="bladeburner-console-div">
|
||||
<table className="bladeburner-console-table">
|
||||
<tbody>
|
||||
{/*
|
||||
TODO: optimize this.
|
||||
using `i` as a key here isn't great because it'll re-render everything
|
||||
everytime the console reaches max length.
|
||||
*/}
|
||||
<Box height={"60vh"} display={"flex"} alignItems={"stretch"} component={Paper}>
|
||||
<Box>
|
||||
<List sx={{ height: "100%", overflow: "auto" }}>
|
||||
{props.bladeburner.consoleLogs.map((log: any, i: number) => (
|
||||
<Line key={i} content={log} />
|
||||
))}
|
||||
<tr key="input" id="bladeburner-console-input-row" className="bladeburner-console-input-row">
|
||||
<td className="bladeburner-console-input-cell">
|
||||
<pre>{"> "}</pre>
|
||||
<input
|
||||
<TextField
|
||||
classes={{ root: classes.textfield }}
|
||||
autoFocus
|
||||
className="bladeburner-console-input"
|
||||
variant="standard"
|
||||
tabIndex={1}
|
||||
type="text"
|
||||
value={command}
|
||||
onChange={handleCommandChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
InputProps={{
|
||||
// for players to hook in
|
||||
className: classes.input,
|
||||
startAdornment: (
|
||||
<>
|
||||
<Typography>> </Typography>
|
||||
</>
|
||||
),
|
||||
spellCheck: false,
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</List>
|
||||
<div ref={scrollHook}></div>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,114 +1,79 @@
|
||||
import React, { useState } from "react";
|
||||
import { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { Contracts } from "../data/Contracts";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IAction } from "../IAction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
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 {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: IAction;
|
||||
}
|
||||
|
||||
export function ContractElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isActive =
|
||||
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes.Contract;
|
||||
props.bladeburner.action.name = 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);
|
||||
const actionData = Contracts[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Typography>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
</>
|
||||
) : (
|
||||
<CopyableText value={props.action.name} />
|
||||
)}
|
||||
</h2>
|
||||
{isActive ? (
|
||||
<p style={{ display: "block" }}>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
||||
Start
|
||||
</a>
|
||||
<CopyableText value={props.action.name} />
|
||||
<StartButton
|
||||
bladeburner={props.bladeburner}
|
||||
type={ActionTypes.Contract}
|
||||
name={props.action.name}
|
||||
rerender={rerender}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
||||
<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>
|
||||
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
||||
<Typography>
|
||||
{actionData.desc}
|
||||
<br />
|
||||
<br />
|
||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
||||
{props.action.isStealth ? stealthIcon : <></>}
|
||||
{props.action.isKill ? killIcon : <></>}
|
||||
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||
<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
<br />
|
||||
@ -117,13 +82,9 @@ export function ContractElem(props: IProps): React.ReactElement {
|
||||
Successes: {props.action.successes}
|
||||
<br />
|
||||
Failures: {props.action.failures}
|
||||
</pre>
|
||||
</Typography>
|
||||
<br />
|
||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
||||
Autolevel:
|
||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
||||
</label>
|
||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
||||
</>
|
||||
<Autolevel rerender={rerender} action={props.action} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -14,9 +14,7 @@ export function ContractList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{names.map((name: string) => (
|
||||
<li key={name} className="bladeburner-action">
|
||||
<ContractElem bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
||||
</li>
|
||||
<ContractElem key={name} bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { ContractList } from "./ContractList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,14 +12,14 @@ interface IProps {
|
||||
export function ContractPage(props: IProps): React.ReactElement {
|
||||
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
|
||||
to lose HP, which can lead to hospitalization.
|
||||
<br />
|
||||
<br />
|
||||
You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more
|
||||
difficult, but grant more rank, experience, and money.
|
||||
</p>
|
||||
</Typography>
|
||||
<ContractList bladeburner={props.bladeburner} player={props.player} />
|
||||
</>
|
||||
);
|
||||
|
@ -1,19 +1,30 @@
|
||||
import React, { useState } from "react";
|
||||
import { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IAction } from "../IAction";
|
||||
import { GeneralActions } from "../data/GeneralActions";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
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 {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: IAction;
|
||||
}
|
||||
|
||||
export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isActive = props.action.name === props.bladeburner.action.name;
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
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))
|
||||
: -1;
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes[props.action.name as string];
|
||||
props.bladeburner.action.name = props.action.name;
|
||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
const actionData = GeneralActions[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Typography>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
</>
|
||||
) : (
|
||||
<CopyableText value={props.action.name} />
|
||||
)}
|
||||
</h2>
|
||||
{isActive ? (
|
||||
<p style={{ display: "block" }}>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
||||
Start
|
||||
</a>
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<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 />
|
||||
<pre style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }}></pre>
|
||||
<Typography>{actionData.desc}</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
<Typography>
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
{successChance !== -1 && (
|
||||
<>
|
||||
@ -82,7 +93,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
Estimated success chance: {formatNumber(successChance * 100, 1)}%
|
||||
</>
|
||||
)}
|
||||
</pre>
|
||||
</>
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -20,9 +20,7 @@ export function GeneralActionList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{actions.map((action: Action) => (
|
||||
<li key={action.name} className="bladeburner-action">
|
||||
<GeneralActionElem bladeburner={props.bladeburner} action={action} player={props.player} />
|
||||
</li>
|
||||
<GeneralActionElem key={action.name} bladeburner={props.bladeburner} action={action} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { GeneralActionList } from "./GeneralActionList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,10 +12,7 @@ interface IProps {
|
||||
export function GeneralActionPage(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
||||
These are generic actions that will assist you in your Bladeburner duties. They will not affect your Bladeburner
|
||||
rank in any way.
|
||||
</p>
|
||||
<Typography>These are generic actions that will assist you in your Bladeburner duties.</Typography>
|
||||
<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 { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
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 { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
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 Typography from "@mui/material/Typography";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: Operation;
|
||||
}
|
||||
|
||||
export function OperationElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isActive =
|
||||
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes.Operation;
|
||||
props.bladeburner.action.name = 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);
|
||||
const actionData = Operations[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Typography>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
</>
|
||||
) : (
|
||||
<CopyableText value={props.action.name} />
|
||||
)}
|
||||
</h2>
|
||||
{isActive ? (
|
||||
<p style={{ display: "block" }}>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
||||
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>
|
||||
<CopyableText value={props.action.name} />
|
||||
<StartButton
|
||||
bladeburner={props.bladeburner}
|
||||
type={ActionTypes.Operation}
|
||||
name={props.action.name}
|
||||
rerender={rerender}
|
||||
/>
|
||||
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} />
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
||||
<span className="tooltiptext">
|
||||
{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>
|
||||
|
||||
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
||||
<Typography>
|
||||
{actionData.desc}
|
||||
<br />
|
||||
<br />
|
||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
||||
{props.action.isStealth ? stealthIcon : <></>}
|
||||
{props.action.isKill ? killIcon : <></>}
|
||||
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||
<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
<br />
|
||||
@ -131,13 +85,9 @@ export function OperationElem(props: IProps): React.ReactElement {
|
||||
Successes: {props.action.successes}
|
||||
<br />
|
||||
Failures: {props.action.failures}
|
||||
</pre>
|
||||
</Typography>
|
||||
<br />
|
||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
||||
Autolevel:
|
||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
||||
</label>
|
||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
||||
</>
|
||||
<Autolevel rerender={rerender} action={props.action} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -14,9 +14,7 @@ export function OperationList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{names.map((name: string) => (
|
||||
<li key={name} className="bladeburner-action">
|
||||
<OperationElem bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
||||
</li>
|
||||
<OperationElem key={name} bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { OperationList } from "./OperationList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,7 +12,7 @@ interface IProps {
|
||||
export function OperationPage(props: IProps): React.ReactElement {
|
||||
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
|
||||
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.
|
||||
@ -27,7 +28,7 @@ export function OperationPage(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
You can unlock higher-level operations by successfully completing them. Higher-level operations are more
|
||||
difficult, but grant more rank and experience.
|
||||
</p>
|
||||
</Typography>
|
||||
<OperationList bladeburner={props.bladeburner} player={props.player} />
|
||||
</>
|
||||
);
|
||||
|
@ -1,8 +1,15 @@
|
||||
import React from "react";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
import { formatNumber } from "../../../utils/StringHelperFunctions";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
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 {
|
||||
skill: any;
|
||||
bladeburner: IBladeburner;
|
||||
@ -28,26 +35,30 @@ export function SkillElem(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Typography>
|
||||
<CopyableText value={props.skill.name} />
|
||||
</h2>
|
||||
<a
|
||||
onClick={onClick}
|
||||
style={{ display: "inline-block", margin: "3px", padding: "3px" }}
|
||||
className={canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive"}
|
||||
>
|
||||
Level
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
<p style={{ display: "block" }}>Level: {currentLevel}</p>
|
||||
{maxLvl ? (
|
||||
<p style={{ color: "red", display: "block" }}>MAX LEVEL</p>
|
||||
</Typography>
|
||||
{!canLevel || maxLvl ? (
|
||||
<IconButton disabled>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<p style={{ display: "block" }}>Skill Points required: {formatNumber(pointCost, 0)}</p>
|
||||
<IconButton onClick={onClick}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<p style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.skill.desc }} />
|
||||
</>
|
||||
</Box>
|
||||
<br />
|
||||
<br />
|
||||
<Typography>Level: {currentLevel}</Typography>
|
||||
{maxLvl ? (
|
||||
<Typography>MAX LEVEL</Typography>
|
||||
) : (
|
||||
<Typography>Skill Points required: {formatNumber(pointCost, 0)}</Typography>
|
||||
)}
|
||||
<Typography>{props.skill.desc}</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -12,9 +12,7 @@ export function SkillList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{Object.keys(Skills).map((skill: string) => (
|
||||
<li key={skill} className="bladeburner-action">
|
||||
<SkillElem bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
||||
</li>
|
||||
<SkillElem key={skill} bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React, { useState } from "react";
|
||||
import { SkillList } from "./SkillList";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { formatNumber } from "../../../utils/StringHelperFunctions";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
}
|
||||
@ -18,45 +19,48 @@ export function SkillPage(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Typography>
|
||||
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
|
||||
</p>
|
||||
<p>
|
||||
You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
|
||||
</Typography>
|
||||
<Typography>
|
||||
You will gain one skill point every{" "}
|
||||
{BladeburnerConstants.RanksPerSkillPoint * BitNodeMultipliers.BladeburnerSkillCost} ranks.
|
||||
<br />
|
||||
<br />
|
||||
Note that when upgrading a skill, the benefit for that skill is additive. However, the effects of different
|
||||
skills with each other is multiplicative.
|
||||
<br />
|
||||
</p>
|
||||
</Typography>
|
||||
<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"]) && (
|
||||
<p>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</p>
|
||||
<Typography>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</Typography>
|
||||
)}
|
||||
{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"]) && (
|
||||
<p>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</p>
|
||||
<Typography>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</Typography>
|
||||
)}
|
||||
{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"]) && (
|
||||
<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["effHack"]) && <p>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</p>}
|
||||
{valid(mults["effStr"]) && <p>Strength: x{formatNumber(mults["effStr"], 3)}</p>}
|
||||
{valid(mults["effDef"]) && <p>Defense: x{formatNumber(mults["effDef"], 3)}</p>}
|
||||
{valid(mults["effDex"]) && <p>Dexterity: x{formatNumber(mults["effDex"], 3)}</p>}
|
||||
{valid(mults["effAgi"]) && <p>Agility: x{formatNumber(mults["effAgi"], 3)}</p>}
|
||||
{valid(mults["effCha"]) && <p>Charisma: x{formatNumber(mults["effCha"], 3)}</p>}
|
||||
{valid(mults["effInt"]) && <p>Intelligence: x{formatNumber(mults["effInt"], 3)}</p>}
|
||||
{valid(mults["stamina"]) && <p>Stamina: x{formatNumber(mults["stamina"], 3)}</p>}
|
||||
{valid(mults["money"]) && <p>Contract Money: x{formatNumber(mults["money"], 3)}</p>}
|
||||
{valid(mults["expGain"]) && <p>Exp Gain: x{formatNumber(mults["expGain"], 3)}</p>}
|
||||
{valid(mults["actionTime"]) && <Typography>Action Time: x{formatNumber(mults["actionTime"], 3)}</Typography>}
|
||||
{valid(mults["effHack"]) && <Typography>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</Typography>}
|
||||
{valid(mults["effStr"]) && <Typography>Strength: x{formatNumber(mults["effStr"], 3)}</Typography>}
|
||||
{valid(mults["effDef"]) && <Typography>Defense: x{formatNumber(mults["effDef"], 3)}</Typography>}
|
||||
{valid(mults["effDex"]) && <Typography>Dexterity: x{formatNumber(mults["effDex"], 3)}</Typography>}
|
||||
{valid(mults["effAgi"]) && <Typography>Agility: x{formatNumber(mults["effAgi"], 3)}</Typography>}
|
||||
{valid(mults["effCha"]) && <Typography>Charisma: x{formatNumber(mults["effCha"], 3)}</Typography>}
|
||||
{valid(mults["effInt"]) && <Typography>Intelligence: x{formatNumber(mults["effInt"], 3)}</Typography>}
|
||||
{valid(mults["stamina"]) && <Typography>Stamina: x{formatNumber(mults["stamina"], 3)}</Typography>}
|
||||
{valid(mults["money"]) && <Typography>Contract Money: x{formatNumber(mults["money"], 3)}</Typography>}
|
||||
{valid(mults["expGain"]) && <Typography>Exp Gain: x{formatNumber(mults["expGain"], 3)}</Typography>}
|
||||
<br />
|
||||
<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 { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { StatsTable } from "../../ui/React/StatsTable";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { joinFaction } from "../../Faction/FactionHelpers";
|
||||
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 {
|
||||
bladeburner: IBladeburner;
|
||||
@ -21,131 +24,146 @@ interface IProps {
|
||||
}
|
||||
|
||||
export function Stats(props: IProps): React.ReactElement {
|
||||
const [travelOpen, setTravelOpen] = useState(false);
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
const inFaction = props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction;
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||
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 {
|
||||
if (!inFaction) return;
|
||||
const faction = Factions["Bladeburners"];
|
||||
if (faction.isMember) {
|
||||
props.router.toFaction(faction);
|
||||
} else {
|
||||
if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) {
|
||||
if (!faction.isMember) {
|
||||
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 (
|
||||
<>
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Rank: {formatNumber(props.bladeburner.rank, 2)}
|
||||
<span className="tooltiptext">Your rank within the Bladeburner division.</span>
|
||||
</p>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Box display="flex">
|
||||
<Tooltip disableInteractive title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
||||
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
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)}
|
||||
</p>
|
||||
<div className="help-tip" onClick={openStaminaHelp}>
|
||||
?
|
||||
</div>
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p>Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%</p>
|
||||
<Typography>
|
||||
Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%
|
||||
</Typography>
|
||||
<br />
|
||||
<p>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</p>
|
||||
<p>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</p>
|
||||
<Typography>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</Typography>
|
||||
<Typography>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</Typography>
|
||||
<br />
|
||||
<p>Num Times Hospitalized: {props.bladeburner.numHosp}</p>
|
||||
<p>
|
||||
<Typography>Num Times Hospitalized: {props.bladeburner.numHosp}</Typography>
|
||||
<Typography>
|
||||
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
||||
</p>
|
||||
</Typography>
|
||||
<br />
|
||||
<p>Current City: {props.bladeburner.city}</p>
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
<Typography>Current City: {props.bladeburner.city}</Typography>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city. An accurate
|
||||
population count increases success rate estimates.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
||||
<span className="tooltiptext">
|
||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city.
|
||||
</span>
|
||||
</p>
|
||||
<div className="help-tip" onClick={openPopulationHelp}>
|
||||
?
|
||||
</div>
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
||||
<span className="tooltiptext">
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
||||
</span>
|
||||
</p>
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}
|
||||
<span className="tooltiptext">
|
||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a chaos
|
||||
level can make contracts and operations harder.
|
||||
</span>
|
||||
</p>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a
|
||||
chaos level can make contracts and operations harder.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
{(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000 > 15000 && (
|
||||
<>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by
|
||||
browser). Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Bonus time:{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
||||
)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<span className="tooltiptext">
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser).
|
||||
Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||
</span>
|
||||
</p>
|
||||
<p>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</p>
|
||||
</>
|
||||
)}
|
||||
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
||||
<br />
|
||||
<StatsTable
|
||||
rows={[
|
||||
@ -156,17 +174,15 @@ export function Stats(props: IProps): React.ReactElement {
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
<a onClick={openTravel} className="a-link-button" style={{ display: "inline-block" }}>
|
||||
Travel
|
||||
</a>
|
||||
<a onClick={openFaction} className="a-link-button tooltip" style={{ display: "inline-block" }}>
|
||||
<span className="tooltiptext">
|
||||
Apply to the Bladeburner Faction, or go to the faction page if you are already a member
|
||||
</span>
|
||||
<Button onClick={() => setTravelOpen(true)}>Travel</Button>
|
||||
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
||||
<span>
|
||||
<Button disabled={!inFaction} onClick={openFaction}>
|
||||
Faction
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
</>
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
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 { 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 {
|
||||
chance: number[];
|
||||
bladeburner: IBladeburner;
|
||||
action: IAction;
|
||||
}
|
||||
|
||||
export function SuccessChance(props: IProps): React.ReactElement {
|
||||
if (props.chance[0] === props.chance[1]) {
|
||||
return <>{formatNumber(props.chance[0] * 100, 1)}%</>;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
|
||||
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 (
|
||||
<>
|
||||
{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 { removePopup } from "../../ui/React/createPopup";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { Action } from "../Action";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
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>();
|
||||
|
||||
function confirmTeamSize(): void {
|
||||
@ -21,25 +25,32 @@ export function TeamSizePopup(props: IProps): React.ReactElement {
|
||||
} else {
|
||||
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 (
|
||||
<>
|
||||
<p>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>
|
||||
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.
|
||||
</p>
|
||||
<input
|
||||
</Typography>
|
||||
<TextField
|
||||
autoFocus
|
||||
variant="standard"
|
||||
type="number"
|
||||
placeholder="Team size"
|
||||
className="text-input"
|
||||
onChange={(event) => setTeamSize(parseFloat(event.target.value))}
|
||||
value={teamSize}
|
||||
onChange={onTeamSize}
|
||||
/>
|
||||
<a className="a-link-button" onClick={confirmTeamSize}>
|
||||
<Button sx={{ mx: 2 }} onClick={confirmTeamSize}>
|
||||
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 { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
|
||||
const gainLimit = 10e9;
|
||||
|
||||
|
@ -12,7 +12,7 @@ import { SpecialServerNames } from "./Server/SpecialServerIps";
|
||||
import { Server } from "./Server/Server";
|
||||
import { HacknetServer } from "./Hacknet/HacknetServer";
|
||||
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { getRandomInt } from "./utils/helpers/getRandomInt";
|
||||
|
||||
export function generateRandomContract(): void {
|
||||
// First select a random problem type
|
||||
|
@ -2,7 +2,7 @@ import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc
|
||||
|
||||
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 { CodingContractPopup } from "./ui/React/CodingContractPopup";
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { companiesMetadata } from "./data/CompaniesMetadata";
|
||||
import { Company, IConstructorParams } from "./Company";
|
||||
import { IMap } from "../types";
|
||||
import { Reviver } from "../../utils/JSONReviver";
|
||||
import { Reviver } from "../utils/JSONReviver";
|
||||
|
||||
export let Companies: IMap<Company> = {};
|
||||
|
||||
|
@ -4,7 +4,7 @@ import * as posNames from "./data/companypositionnames";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
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 {
|
||||
name: string;
|
||||
|
@ -9,6 +9,7 @@ export const CONSTANTS: {
|
||||
MaxSkillLevel: number;
|
||||
MilliPerCycle: number;
|
||||
CorpFactionRepRequirement: number;
|
||||
BaseFocusBonus: number;
|
||||
BaseCostFor1GBOfRamHome: number;
|
||||
BaseCostFor1GBOfRamServer: number;
|
||||
TravelCost: number;
|
||||
@ -285,6 +286,7 @@ export const CONSTANTS: {
|
||||
GameCyclesPerFiveMinutes: 300000 / 200,
|
||||
|
||||
// Player Work & Action
|
||||
BaseFocusBonus: 0.8,
|
||||
FactionWorkHacking: "Faction Hacking Work",
|
||||
FactionWorkField: "Faction Field Work",
|
||||
FactionWorkSecurity: "Faction Security Work",
|
||||
|
@ -10,9 +10,9 @@ import { showLiterature } from "../Literature/LiteratureHelpers";
|
||||
import { LiteratureNames } from "../Literature/data/LiteratureNames";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../utils/JSONReviver";
|
||||
import { isString } from "../../utils/helpers/isString";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
|
||||
import { isString } from "../utils/helpers/isString";
|
||||
|
||||
// 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
|
||||
const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { CorporationConstants } from "./data/Constants";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { createElement } from "../ui/uiHelpers/createElement";
|
||||
import { EmployeePositions } from "./EmployeePositions";
|
||||
import { ICorporation } from "./ICorporation";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||
import { OfficeSpace } from "./OfficeSpace";
|
||||
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 Decimal from "decimal.js";
|
||||
import { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
|
||||
import { CorporationConstants } from "./data/Constants";
|
||||
import { EmployeePositions } from "./EmployeePositions";
|
||||
import { Material } from "./Material";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { calculateEffectWithFactors } from "../utils/calculateEffectWithFactors";
|
||||
import { OfficeSpace } from "./OfficeSpace";
|
||||
import { Product } from "./Product";
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { isString } from "../../utils/helpers/isString";
|
||||
import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
import { isString } from "../utils/helpers/isString";
|
||||
import { MaterialSizes } from "./MaterialSizes";
|
||||
import { Warehouse } from "./Warehouse";
|
||||
import { ICorporation } from "./ICorporation";
|
||||
import { IIndustry } from "./IIndustry";
|
||||
import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||
|
||||
interface IParams {
|
||||
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";
|
||||
|
||||
interface IConstructorParams {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { EmployeePositions } from "./EmployeePositions";
|
||||
import { CorporationConstants } from "./data/Constants";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { generateRandomString } from "../../utils/StringHelperFunctions";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { generateRandomString } from "../utils/StringHelperFunctions";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { Employee } from "./Employee";
|
||||
import { IIndustry } from "./IIndustry";
|
||||
import { ICorporation } from "./ICorporation";
|
||||
|
@ -6,8 +6,8 @@ import { ProductRatingWeights, IProductRatingWeight } from "./ProductRatingWeigh
|
||||
import { createCityMap } from "../Locations/createCityMap";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
|
||||
interface IConstructorParams {
|
||||
name?: string;
|
||||
|
@ -4,8 +4,8 @@ import { IIndustry } from "./IIndustry";
|
||||
import { MaterialSizes } from "./MaterialSizes";
|
||||
import { IMap } from "../types";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
|
||||
interface IConstructorParams {
|
||||
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 { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { use } from "../../ui/Context";
|
||||
import { useCorporation } from "./Context";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
popupId: string;
|
||||
corp: ICorporation;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player buyback shares
|
||||
// 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);
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
const currentStockPrice = props.corp.sharePrice;
|
||||
const currentStockPrice = corp.sharePrice;
|
||||
const buybackPrice = currentStockPrice * 1.1;
|
||||
|
||||
function buy(): void {
|
||||
if (shares === null) return;
|
||||
const tempStockPrice = props.corp.sharePrice;
|
||||
const tempStockPrice = corp.sharePrice;
|
||||
const buybackPrice = tempStockPrice * 1.1;
|
||||
if (isNaN(shares) || shares <= 0) {
|
||||
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");
|
||||
} else if (shares * buybackPrice > props.player.money) {
|
||||
} else if (shares * buybackPrice > player.money) {
|
||||
dialogBoxCreate(
|
||||
"ERROR: You do not have enough money to purchase this many shares (you need " +
|
||||
numeralWrapper.format(shares * buybackPrice, "$0.000a") +
|
||||
")",
|
||||
);
|
||||
} else {
|
||||
props.corp.numShares += shares;
|
||||
if (isNaN(props.corp.issuedShares)) {
|
||||
console.warn("Corporation issuedShares is NaN: " + props.corp.issuedShares);
|
||||
corp.numShares += shares;
|
||||
if (isNaN(corp.issuedShares)) {
|
||||
console.warn("Corporation issuedShares is NaN: " + corp.issuedShares);
|
||||
console.warn("Converting to number now");
|
||||
const res = props.corp.issuedShares;
|
||||
const res = corp.issuedShares;
|
||||
if (isNaN(res)) {
|
||||
props.corp.issuedShares = 0;
|
||||
corp.issuedShares = 0;
|
||||
} else {
|
||||
props.corp.issuedShares = res;
|
||||
corp.issuedShares = res;
|
||||
}
|
||||
}
|
||||
props.corp.issuedShares -= shares;
|
||||
props.player.loseMoney(shares * buybackPrice);
|
||||
removePopup(props.popupId);
|
||||
corp.issuedShares -= shares;
|
||||
player.loseMoney(shares * buybackPrice);
|
||||
props.onClose();
|
||||
props.rerender();
|
||||
}
|
||||
}
|
||||
@ -62,11 +63,11 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
||||
if (shares === null) return <></>;
|
||||
if (isNaN(shares) || shares <= 0) {
|
||||
return <>ERROR: Invalid value entered for number of shares to buyback</>;
|
||||
} else if (shares > props.corp.issuedShares) {
|
||||
} else if (shares > corp.issuedShares) {
|
||||
return (
|
||||
<>
|
||||
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 {
|
||||
@ -83,7 +84,7 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<p>
|
||||
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.
|
||||
@ -93,7 +94,7 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
<br />
|
||||
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>
|
||||
<CostIndicator />
|
||||
<br />
|
||||
@ -109,6 +110,6 @@ export function BuybackSharesPopup(props: IProps): React.ReactElement {
|
||||
<button onClick={buy} className="a-link-button" style={{ display: "inline-block" }}>
|
||||
Buy shares
|
||||
</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
|
||||
// divisions, see an overview of your corporation, or create a new industry
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { HeaderTab } from "./HeaderTab";
|
||||
import { IIndustry } from "../IIndustry";
|
||||
import { NewIndustryPopup } from "./NewIndustryPopup";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { MainPanel } from "./MainPanel";
|
||||
import { Industries } from "../IndustryData";
|
||||
import { ExpandIndustryTab } from "./ExpandIndustryTab";
|
||||
import { use } from "../../ui/Context";
|
||||
import { Context } from "./Context";
|
||||
import { Overview } from "./Overview";
|
||||
|
||||
interface IExpandButtonProps {
|
||||
corp: ICorporation;
|
||||
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"} />;
|
||||
}
|
||||
import Tabs from "@mui/material/Tabs";
|
||||
import Tab from "@mui/material/Tab";
|
||||
|
||||
export function CorporationRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
@ -46,33 +21,37 @@ export function CorporationRoot(): React.ReactElement {
|
||||
function rerender(): void {
|
||||
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(() => {
|
||||
const id = setInterval(rerender, 1000);
|
||||
const id = setInterval(rerender, 200);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
const canExpand =
|
||||
Object.keys(Industries).filter(
|
||||
(industryType: string) =>
|
||||
corporation.divisions.find((division: IIndustry) => division.type === industryType) === undefined,
|
||||
).length > 0;
|
||||
|
||||
return (
|
||||
<Context.Corporation.Provider value={corporation}>
|
||||
<div className="cmpy-mgmt-container">
|
||||
<div>
|
||||
<HeaderTab
|
||||
current={divisionName === "Overview"}
|
||||
key={"overview"}
|
||||
onClick={() => setDivisionName("Overview")}
|
||||
text={corporation.name}
|
||||
/>
|
||||
{corporation.divisions.map((division: IIndustry) => (
|
||||
<HeaderTab
|
||||
current={division.name === divisionName}
|
||||
key={division.name}
|
||||
onClick={() => setDivisionName(division.name)}
|
||||
text={division.name}
|
||||
/>
|
||||
<Tabs variant="fullWidth" value={divisionName} onChange={handleChange}>
|
||||
<Tab label={corporation.name} value={"Overview"} />
|
||||
{corporation.divisions.map((div) => (
|
||||
<Tab key={div.name} label={div.name} value={div.name} />
|
||||
))}
|
||||
<ExpandButton corp={corporation} setDivisionName={setDivisionName} />
|
||||
</div>
|
||||
<MainPanel rerender={rerender} corp={corporation} divisionName={divisionName} player={player} />
|
||||
{canExpand && <Tab label={"Expand"} value={-1} />}
|
||||
</Tabs>
|
||||
{divisionName === "Overview" && <Overview rerender={rerender} />}
|
||||
{divisionName === -1 && <ExpandIndustryTab setDivisionName={setDivisionName} />}
|
||||
{typeof divisionName === "string" && divisionName !== "Overview" && (
|
||||
<MainPanel rerender={rerender} divisionName={divisionName + ""} />
|
||||
)}
|
||||
</div>
|
||||
</Context.Corporation.Provider>
|
||||
);
|
||||
}
|
||||
|
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 { CorporationConstants } from "../data/Constants";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { NewCity } from "../Actions";
|
||||
import { MoneyCost } from "./MoneyCost";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { Material } from "../Material";
|
||||
|
@ -1,23 +1,25 @@
|
||||
import React from "react";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { useCorporation } from "./Context";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
corp: ICorporation;
|
||||
popupId: string;
|
||||
player: IPlayer;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
// Create a popup that lets the player manage exports
|
||||
export function FindInvestorsPopup(props: IProps): React.ReactElement {
|
||||
const val = props.corp.determineValuation();
|
||||
export function FindInvestorsModal(props: IProps): React.ReactElement {
|
||||
const corp = useCorporation();
|
||||
const val = corp.determineValuation();
|
||||
let percShares = 0;
|
||||
let roundMultiplier = 4;
|
||||
switch (props.corp.fundingRound) {
|
||||
switch (corp.fundingRound) {
|
||||
case 0: //Seed
|
||||
percShares = 0.1;
|
||||
roundMultiplier = 4;
|
||||
@ -41,15 +43,15 @@ export function FindInvestorsPopup(props: IProps): React.ReactElement {
|
||||
const investShares = Math.floor(CorporationConstants.INITIALSHARES * percShares);
|
||||
|
||||
function findInvestors(): void {
|
||||
props.corp.fundingRound++;
|
||||
props.corp.addFunds(funding);
|
||||
props.corp.numShares -= investShares;
|
||||
corp.fundingRound++;
|
||||
corp.addFunds(funding);
|
||||
corp.numShares -= investShares;
|
||||
props.rerender();
|
||||
removePopup(props.popupId);
|
||||
props.onClose();
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>
|
||||
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(investShares, "0.000a")} shares).
|
||||
@ -59,10 +61,8 @@ export function FindInvestorsPopup(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
<br />
|
||||
Hint: Investment firms will offer more money if your corporation is turning a profit
|
||||
</p>
|
||||
<button onClick={findInvestors} className="std-button">
|
||||
Accept
|
||||
</button>
|
||||
</>
|
||||
</Typography>
|
||||
<Button onClick={findInvestors}>Accept</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
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 { OfficeSpace } from "../OfficeSpace";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||
import { formatNumber } from "../../../utils/StringHelperFunctions";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { Employee } from "../Employee";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
|
||||
interface INameEmployeeProps {
|
||||
office: OfficeSpace;
|
||||
|
@ -9,7 +9,7 @@ import { EmployeePositions } from "../EmployeePositions";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
|
||||
import { getSelectText } from "../../../utils/uiHelpers/getSelectData";
|
||||
import { getSelectText } from "../../ui/uiHelpers/getSelectData";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { UpgradeOfficeSizePopup } from "./UpgradeOfficeSizePopup";
|
||||
import { HireEmployeePopup } from "./HireEmployeePopup";
|
||||
|
@ -7,8 +7,8 @@ import { Industries } from "../IndustryData";
|
||||
import { IndustryUpgrades } from "../IndustryUpgrades";
|
||||
import { IIndustry } from "../IIndustry";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { MakeProductPopup } from "./MakeProductPopup";
|
||||
import { ResearchPopup } from "./ResearchPopup";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
|
@ -20,7 +20,7 @@ import { SmartSupplyPopup } from "./SmartSupplyPopup";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
|
||||
import { isString } from "../../../utils/helpers/isString";
|
||||
import { isString } from "../../utils/helpers/isString";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { IIndustry } from "../IIndustry";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
@ -1,29 +1,34 @@
|
||||
import React, { useState } from "react";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
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 {
|
||||
popupId: string;
|
||||
corp: ICorporation;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// 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
|
||||
export function IssueDividendsPopup(props: IProps): React.ReactElement {
|
||||
const [percent, setPercent] = useState<number | null>(null);
|
||||
export function IssueDividendsModal(props: IProps): React.ReactElement {
|
||||
const corp = useCorporation();
|
||||
const [percent, setPercent] = useState(0);
|
||||
|
||||
const canIssue = !isNaN(percent) && percent >= 0 && percent <= CorporationConstants.DividendMaxPercentage * 100;
|
||||
function issueDividends(): void {
|
||||
if (!canIssue) return;
|
||||
if (percent === null) return;
|
||||
try {
|
||||
IssueDividends(props.corp, percent / 100);
|
||||
IssueDividends(corp, percent / 100);
|
||||
} catch (err) {
|
||||
dialogBoxCreate(err + "");
|
||||
}
|
||||
|
||||
removePopup(props.popupId);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
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 {
|
||||
if (event.target.value === "") setPercent(null);
|
||||
else setPercent(parseFloat(event.target.value));
|
||||
if (event.target.value === "") setPercent(0);
|
||||
else {
|
||||
let p = parseFloat(event.target.value);
|
||||
if (p > 50) p = 50;
|
||||
setPercent(p);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>
|
||||
Dividends are a distribution of a portion of the corporation's profits to the shareholders. This includes
|
||||
yourself, as well.
|
||||
<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
|
||||
dividends. Since your corporation starts with 1 billion shares, every shareholder will be paid $0.04 per share
|
||||
per second before taxes.
|
||||
</p>
|
||||
<input
|
||||
autoFocus={true}
|
||||
</Typography>
|
||||
<TextField
|
||||
variant="standard"
|
||||
autoFocus
|
||||
value={percent}
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
className="text-input"
|
||||
placeholder="Dividend %"
|
||||
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
|
||||
</button>
|
||||
</>
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,24 +1,27 @@
|
||||
import React, { useState } from "react";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
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 {
|
||||
corp: ICorporation;
|
||||
shares: number | null;
|
||||
}
|
||||
|
||||
function EffectText(props: IEffectTextProps): React.ReactElement {
|
||||
const corp = useCorporation();
|
||||
if (props.shares === null) return <></>;
|
||||
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
|
||||
const maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
|
||||
const newSharePrice = Math.round(corp.sharePrice * 0.9);
|
||||
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
|
||||
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
||||
let newShares = props.shares;
|
||||
if (isNaN(newShares)) {
|
||||
return <p>Invalid input</p>;
|
||||
return <Typography>Invalid input</Typography>;
|
||||
}
|
||||
|
||||
// Round to nearest ten-millionth
|
||||
@ -26,36 +29,37 @@ function EffectText(props: IEffectTextProps): React.ReactElement {
|
||||
newShares = Math.round(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) {
|
||||
return <p>You cannot issue that many shares</p>;
|
||||
return <Typography>You cannot issue that many shares</Typography>;
|
||||
}
|
||||
|
||||
return (
|
||||
<p>
|
||||
<Typography>
|
||||
Issue ${numeralWrapper.format(newShares, "0.000a")} new shares for{" "}
|
||||
{numeralWrapper.formatMoney(newShares * newSharePrice)}?
|
||||
</p>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
corp: ICorporation;
|
||||
popupId: string;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// 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
|
||||
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 maxNewSharesUnrounded = Math.round(props.corp.totalShares * 0.2);
|
||||
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
|
||||
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
|
||||
|
||||
function issueNewShares(): void {
|
||||
if (shares === null) return;
|
||||
const newSharePrice = Math.round(props.corp.sharePrice * 0.9);
|
||||
const newSharePrice = Math.round(corp.sharePrice * 0.9);
|
||||
let newShares = shares;
|
||||
if (isNaN(newShares)) {
|
||||
dialogBoxCreate("Invalid input for number of new shares");
|
||||
@ -71,8 +75,8 @@ export function IssueNewSharesPopup(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
const profit = newShares * newSharePrice;
|
||||
props.corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
|
||||
props.corp.totalShares += newShares;
|
||||
corp.issueNewSharesCooldown = CorporationConstants.IssueNewSharesCooldown;
|
||||
corp.totalShares += newShares;
|
||||
|
||||
// Determine how many are bought by private investors
|
||||
// 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));
|
||||
privateShares = Math.round(privateShares / 1e6) * 1e6;
|
||||
|
||||
props.corp.issuedShares += newShares - privateShares;
|
||||
props.corp.funds = props.corp.funds.plus(profit);
|
||||
props.corp.immediatelyUpdateSharePrice();
|
||||
|
||||
removePopup(props.popupId);
|
||||
corp.issuedShares += newShares - privateShares;
|
||||
corp.funds = corp.funds.plus(profit);
|
||||
corp.immediatelyUpdateSharePrice();
|
||||
props.onClose();
|
||||
dialogBoxCreate(
|
||||
`Issued ${numeralWrapper.format(newShares, "0.000a")} and raised ` +
|
||||
`${numeralWrapper.formatMoney(profit)}. ${numeralWrapper.format(privateShares, "0.000a")} ` +
|
||||
`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 (
|
||||
<>
|
||||
<p>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>
|
||||
You can issue new equity shares (i.e. stocks) in order to raise capital for your corporation.
|
||||
<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.
|
||||
If they choose to exercise this option, these newly issued shares become private, restricted shares, which means
|
||||
you cannot buy them back.
|
||||
</p>
|
||||
<EffectText corp={props.corp} shares={shares} />
|
||||
<input
|
||||
className="text-input"
|
||||
autoFocus={true}
|
||||
placeholder="# New Shares"
|
||||
style={{ margin: "5px" }}
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<button onClick={issueNewShares} className="std-button" style={{ display: "inline-block" }}>
|
||||
</Typography>
|
||||
<EffectText shares={shares} />
|
||||
<TextField variant="standard" autoFocus placeholder="# New Shares" onChange={onChange} onKeyDown={onKeyDown} />
|
||||
<Button onClick={issueNewShares} sx={{ mx: 1 }}>
|
||||
Issue New Shares
|
||||
</button>
|
||||
</>
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
// React components for the levelable upgrade buttons on the overview panel
|
||||
import React from "react";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { CorporationUpgrade } from "../data/CorporationUpgrades";
|
||||
import { LevelUpgrade } from "../Actions";
|
||||
import { MoneyCost } from "./MoneyCost";
|
||||
import { use } from "../../ui/Context";
|
||||
import { useCorporation } from "./Context";
|
||||
|
||||
interface IProps {
|
||||
upgrade: CorporationUpgrade;
|
||||
corp: ICorporation;
|
||||
player: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function LevelableUpgrade(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const corp = useCorporation();
|
||||
const data = props.upgrade;
|
||||
const level = props.corp.upgrades[data[0]];
|
||||
const level = corp.upgrades[data[0]];
|
||||
|
||||
const baseCost = data[1];
|
||||
const priceMult = data[2];
|
||||
@ -25,14 +25,14 @@ export function LevelableUpgrade(props: IProps): React.ReactElement {
|
||||
|
||||
const text = (
|
||||
<>
|
||||
{data[4]} - <MoneyCost money={cost} corp={props.corp} />
|
||||
{data[4]} - <MoneyCost money={cost} corp={corp} />
|
||||
</>
|
||||
);
|
||||
const tooltip = data[5];
|
||||
function onClick(): void {
|
||||
if (props.corp.funds.lt(cost)) return;
|
||||
if (corp.funds.lt(cost)) return;
|
||||
try {
|
||||
LevelUpgrade(props.corp, props.upgrade);
|
||||
LevelUpgrade(corp, props.upgrade);
|
||||
} catch (err) {
|
||||
dialogBoxCreate(err + "");
|
||||
}
|
||||
|
@ -5,42 +5,28 @@ import React from "react";
|
||||
|
||||
import { CityTabs } from "./CityTabs";
|
||||
import { IIndustry } from "../IIndustry";
|
||||
import { Overview } from "./Overview";
|
||||
import { useCorporation } from "./Context";
|
||||
import { use } from "../../ui/Context";
|
||||
|
||||
import { CityName } from "../../Locations/data/CityNames";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
|
||||
interface IProps {
|
||||
corp: ICorporation;
|
||||
player: IPlayer;
|
||||
divisionName: string;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function MainPanel(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const corp = useCorporation();
|
||||
const division =
|
||||
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
|
||||
|
||||
if (division === undefined) {
|
||||
if (division === undefined) throw new Error("Cannot find division");
|
||||
return (
|
||||
<div id="cmpy-mgmt-panel">
|
||||
<Overview {...props} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div id="cmpy-mgmt-panel">
|
||||
<CityTabs
|
||||
rerender={props.rerender}
|
||||
division={division}
|
||||
corp={props.corp}
|
||||
city={CityName.Sector12}
|
||||
player={props.player}
|
||||
/>
|
||||
<CityTabs rerender={props.rerender} division={division} corp={corp} city={CityName.Sector12} player={player} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { Industries } from "../IndustryData";
|
||||
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
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { LevelableUpgrade } from "./LevelableUpgrade";
|
||||
import { UnlockUpgrade } from "./UnlockUpgrade";
|
||||
import { BribeFactionPopup } from "./BribeFactionPopup";
|
||||
import { SellSharesPopup } from "./SellSharesPopup";
|
||||
import { BuybackSharesPopup } from "./BuybackSharesPopup";
|
||||
import { IssueDividendsPopup } from "./IssueDividendsPopup";
|
||||
import { IssueNewSharesPopup } from "./IssueNewSharesPopup";
|
||||
import { FindInvestorsPopup } from "./FindInvestorsPopup";
|
||||
import { GoPublicPopup } from "./GoPublicPopup";
|
||||
import { BribeFactionModal } from "./BribeFactionModal";
|
||||
import { SellSharesModal } from "./SellSharesModal";
|
||||
import { BuybackSharesModal } from "./BuybackSharesModal";
|
||||
import { IssueDividendsModal } from "./IssueDividendsModal";
|
||||
import { IssueNewSharesModal } from "./IssueNewSharesModal";
|
||||
import { FindInvestorsModal } from "./FindInvestorsModal";
|
||||
import { GoPublicModal } from "./GoPublicModal";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
import { CorporationUnlockUpgrade, CorporationUnlockUpgrades } from "../data/CorporationUnlockUpgrades";
|
||||
@ -16,45 +17,53 @@ import { CorporationUpgrade, CorporationUpgrades } from "../data/CorporationUpgr
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { use } from "../../ui/Context";
|
||||
import { useCorporation } from "./Context";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
corp: ICorporation;
|
||||
player: IPlayer;
|
||||
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();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<Typography>
|
||||
Total Funds: <Money money={corp.funds.toNumber()} />
|
||||
<br />
|
||||
Total Revenue: <Money money={corp.revenue.toNumber()} /> / s<br />
|
||||
Total Expenses: <Money money={corp.expenses.toNumber()} /> / s
|
||||
<br />
|
||||
Total Profits: <Money money={profit} /> / s<br />
|
||||
<DividendsStats corp={corp} profit={profit} />
|
||||
<DividendsStats profit={profit} />
|
||||
Publicly Traded: {corp.public ? "Yes" : "No"}
|
||||
<br />
|
||||
Owned Stock Shares: {numeralWrapper.format(corp.numShares, "0.000a")}
|
||||
<br />
|
||||
Stock Price: {corp.public ? <Money money={corp.sharePrice} /> : "N/A"}
|
||||
<br />
|
||||
</p>
|
||||
<p className="tooltip">
|
||||
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
|
||||
<span className="tooltiptext">
|
||||
</Typography>
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
Outstanding Shares: {numeralWrapper.format(corp.issuedShares, "0.000a")}
|
||||
<br />
|
||||
Private Shares: {numeralWrapper.format(corp.totalShares - corp.issuedShares - corp.numShares, "0.000a")}
|
||||
</span>
|
||||
</p>
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography className="tooltip">
|
||||
Total Stock Shares: {numeralWrapper.format(corp.totalShares, "0.000a")}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
<br />
|
||||
<br />
|
||||
<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="Scientific Research Multiplier: " mult={corp.getScientificResearchMultiplier()} />
|
||||
<br />
|
||||
<BonusTime corp={corp} />
|
||||
<BonusTime />
|
||||
<div>
|
||||
<Button
|
||||
className="a-link-button"
|
||||
display="inline-block"
|
||||
onClick={() => corp.getStarterGuide(player)}
|
||||
text="Getting Started Guide"
|
||||
tooltip={
|
||||
"Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' " +
|
||||
"This is a .lit file that guides you through the beginning of setting up a Corporation and " +
|
||||
"provides some tips/pointers for helping you get started with managing it."
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
Get a copy of and read 'The Complete Handbook for Creating a Successful Corporation.' This is a .lit file
|
||||
that guides you through the beginning of setting up a Corporation and provides some tips/pointers for
|
||||
helping you get started with managing it.
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{corp.public ? (
|
||||
<PublicButtons corp={corp} player={player} rerender={rerender} />
|
||||
) : (
|
||||
<PrivateButtons corp={corp} player={player} rerender={rerender} />
|
||||
)}
|
||||
<BribeButton corp={corp} player={player} />
|
||||
>
|
||||
<Button onClick={() => corp.getStarterGuide(player)}>Getting Started Guide</Button>
|
||||
</Tooltip>
|
||||
{corp.public ? <PublicButtons rerender={rerender} /> : <PrivateButtons rerender={rerender} />}
|
||||
<BribeButton />
|
||||
</div>
|
||||
<br />
|
||||
<Upgrades corp={corp} player={player} rerender={rerender} />
|
||||
<Upgrades rerender={rerender} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface IPrivateButtonsProps {
|
||||
corp: ICorporation;
|
||||
player: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
// Render the buttons for when your Corporation is still private
|
||||
function PrivateButtons({ corp, player, rerender }: IPrivateButtonsProps): React.ReactElement {
|
||||
function openFindInvestorsPopup(): void {
|
||||
const popupId = "cmpy-mgmt-find-investors-popup";
|
||||
createPopup(popupId, FindInvestorsPopup, {
|
||||
rerender,
|
||||
player,
|
||||
popupId,
|
||||
corp: corp,
|
||||
});
|
||||
}
|
||||
|
||||
function openGoPublicPopup(): void {
|
||||
const popupId = "cmpy-mgmt-go-public-popup";
|
||||
createPopup(popupId, GoPublicPopup, {
|
||||
rerender,
|
||||
player,
|
||||
popupId,
|
||||
corp: corp,
|
||||
});
|
||||
}
|
||||
function PrivateButtons({ rerender }: IPrivateButtonsProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const corp = useCorporation();
|
||||
const [findInvestorsopen, setFindInvestorsopen] = useState(false);
|
||||
const [goPublicopen, setGoPublicopen] = useState(false);
|
||||
|
||||
const fundingAvailable = corp.fundingRound < 4;
|
||||
const findInvestorsClassName = fundingAvailable ? "std-button" : "a-link-button-inactive";
|
||||
const findInvestorsTooltip = fundingAvailable
|
||||
? "Search for private investors who will give you startup funding in exchange for equity (stock shares) in your company"
|
||||
: undefined;
|
||||
: "";
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
className={findInvestorsClassName}
|
||||
onClick={openFindInvestorsPopup}
|
||||
text="Find Investors"
|
||||
tooltip={findInvestorsTooltip}
|
||||
display="inline-block"
|
||||
/>
|
||||
<Button
|
||||
className="std-button"
|
||||
onClick={openGoPublicPopup}
|
||||
display="inline-block"
|
||||
text="Go Public"
|
||||
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."
|
||||
<Tooltip disableInteractive title={<Typography>{findInvestorsTooltip}</Typography>}>
|
||||
<Button disabled={!fundingAvailable} onClick={() => setFindInvestorsopen(true)}>
|
||||
Find Investors
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
<Typography>
|
||||
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.
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
>
|
||||
<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 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface IUpgradeProps {
|
||||
corp: ICorporation;
|
||||
player: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
// 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
|
||||
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 (
|
||||
@ -169,29 +156,32 @@ function Upgrades({ corp, player, rerender }: IUpgradeProps): React.ReactElement
|
||||
{Object.values(CorporationUnlockUpgrades)
|
||||
.filter((upgrade: CorporationUnlockUpgrade) => corp.unlockUpgrades[upgrade[0]] === 0)
|
||||
.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>
|
||||
{corp.upgrades
|
||||
.map((level: number, i: number) => CorporationUpgrades[i])
|
||||
.map((upgrade: CorporationUpgrade) => (
|
||||
<LevelableUpgrade rerender={rerender} player={player} corp={corp} upgrade={upgrade} key={upgrade[0]} />
|
||||
<LevelableUpgrade rerender={rerender} upgrade={upgrade} key={upgrade[0]} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface IPublicButtonsProps {
|
||||
corp: ICorporation;
|
||||
player: IPlayer;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
// 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 sellSharesClass = sellSharesOnCd ? "a-link-button-inactive" : "std-button";
|
||||
const sellSharesTooltip = sellSharesOnCd
|
||||
? "Cannot sell shares for " + corp.convertCooldownToString(corp.shareSaleCooldown)
|
||||
: "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.";
|
||||
|
||||
const issueNewSharesOnCd = corp.issueNewSharesCooldown > 0;
|
||||
const issueNewSharesClass = issueNewSharesOnCd ? "a-link-button-inactive" : "std-button";
|
||||
const issueNewSharesTooltip = issueNewSharesOnCd
|
||||
? "Cannot issue new shares for " + corp.convertCooldownToString(corp.issueNewSharesCooldown)
|
||||
: "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 (
|
||||
<>
|
||||
<Button
|
||||
className={sellSharesClass}
|
||||
display="inline-block"
|
||||
onClick={openSellSharesPopup}
|
||||
text="Sell Shares"
|
||||
tooltip={sellSharesTooltip}
|
||||
/>
|
||||
<Button
|
||||
className="std-button"
|
||||
display="inline-block"
|
||||
onClick={openBuybackSharesPopup}
|
||||
text="Buyback shares"
|
||||
tooltip="Buy back shares you that previously issued or sold at market price."
|
||||
/>
|
||||
<Tooltip disableInteractive title={<Typography>{sellSharesTooltip}</Typography>}>
|
||||
<Button disabled={sellSharesOnCd} onClick={() => setSellSharesOpen(true)}>
|
||||
Sell Shares
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<SellSharesModal open={sellSharesOpen} onClose={() => setSellSharesOpen(false)} rerender={rerender} />
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={<Typography>Buy back shares you that previously issued or sold at market price.</Typography>}
|
||||
>
|
||||
<Button onClick={() => setBuybackSharesOpen(true)}>Buyback shares</Button>
|
||||
</Tooltip>
|
||||
<BuybackSharesModal open={buybackSharesOpen} onClose={() => setBuybackSharesOpen(false)} rerender={rerender} />
|
||||
<br />
|
||||
<Button
|
||||
className={issueNewSharesClass}
|
||||
display="inline-block"
|
||||
onClick={openIssueNewSharesPopup}
|
||||
text="Issue New Shares"
|
||||
tooltip={issueNewSharesTooltip}
|
||||
/>
|
||||
<Button
|
||||
className="std-button"
|
||||
display="inline-block"
|
||||
onClick={openIssueDividendsPopup}
|
||||
text="Issue Dividends"
|
||||
tooltip="Manage the dividends that are paid out to shareholders (including yourself)"
|
||||
/>
|
||||
<Tooltip disableInteractive title={<Typography>{issueNewSharesTooltip}</Typography>}>
|
||||
<Button disabled={issueNewSharesOnCd} onClick={() => setIssueNewSharesOpen(true)}>
|
||||
Issue New Shares
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<IssueNewSharesModal open={issueNewSharesOpen} onClose={() => setIssueNewSharesOpen(false)} />
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={<Typography>Manage the dividends that are paid out to shareholders (including yourself)</Typography>}
|
||||
>
|
||||
<Button onClick={() => setIssueDividendsOpen(true)}>Issue Dividends</Button>
|
||||
</Tooltip>
|
||||
<IssueDividendsModal open={issueDividendsOpen} onClose={() => setIssueDividendsOpen(false)} />
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Generic Function for Creating a button
|
||||
interface ICreateButtonProps {
|
||||
text: string;
|
||||
className?: string;
|
||||
display?: string;
|
||||
tooltip?: string;
|
||||
onClick?: (event: React.MouseEvent) => void;
|
||||
function BribeButton(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const corp = useCorporation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const canBribe =
|
||||
corp.determineValuation() >= CorporationConstants.BribeThreshold &&
|
||||
player.factions.filter((f) => Factions[f].getInfo().offersWork()).length > 0;
|
||||
|
||||
function openBribe(): void {
|
||||
if (!canBribe) return;
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
function Button({ className = "std-button", text, display, tooltip, onClick }: ICreateButtonProps): React.ReactElement {
|
||||
const hasTooltip = tooltip != null;
|
||||
if (hasTooltip) className += " tooltip";
|
||||
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 (
|
||||
<Button
|
||||
className={bribeFactionsClass}
|
||||
display="inline-block"
|
||||
onClick={openBribeFactionPopup}
|
||||
text="Bribe Factions"
|
||||
tooltip={
|
||||
<>
|
||||
<Tooltip
|
||||
disableInteractive
|
||||
title={
|
||||
canBribe
|
||||
? "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 {
|
||||
corp: ICorporation;
|
||||
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 <></>;
|
||||
const totalDividends = (corp.dividendPercentage / 100) * profit;
|
||||
const retainedEarnings = profit - totalDividends;
|
||||
@ -361,26 +295,24 @@ interface IMultProps {
|
||||
function Mult({ name, mult }: IMultProps): React.ReactElement {
|
||||
if (mult <= 1) return <></>;
|
||||
return (
|
||||
<p>
|
||||
<Typography>
|
||||
{name}
|
||||
{numeralWrapper.format(mult, "0.000")}
|
||||
<br />
|
||||
</p>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
interface IBonusTimeProps {
|
||||
corp: ICorporation;
|
||||
}
|
||||
// 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;
|
||||
if (storedTime <= 15000) return <></>;
|
||||
return (
|
||||
<p>
|
||||
<Typography>
|
||||
Bonus time: {convertTimeMsToTimeElapsedString(storedTime)}
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { MaterialSizes } from "../MaterialSizes";
|
||||
import { Warehouse } from "../Warehouse";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { IndustryResearchTrees } from "../IndustryData";
|
||||
import { CorporationConstants } from "../data/Constants";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { ICorporation } from "../ICorporation";
|
||||
import { Material } from "../Material";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { Product } from "../Product";
|
||||
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