mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-26 01:23:49 +01:00
sg
This commit is contained in:
commit
7304e5379f
@ -1,61 +0,0 @@
|
|||||||
@mixin animation($property) {
|
|
||||||
-webkit-animation: $property;
|
|
||||||
-moz-animation: $property;
|
|
||||||
-ms-animation: $property;
|
|
||||||
-o-animation: $property;
|
|
||||||
animation: $property;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin borderRadius($property) {
|
|
||||||
-webkit-border-radius: $property;
|
|
||||||
-moz-border-radius: $property;
|
|
||||||
border-radius: $property;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin boxShadow($value) {
|
|
||||||
-webkit-box-shadow: $value;
|
|
||||||
-moz-box-shadow: $value;
|
|
||||||
box-shadow: $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin keyframes($animationName) {
|
|
||||||
@-webkit-keyframes #{$animationName} {
|
|
||||||
$browser: "-webkit-" !global;
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes #{$animationName} {
|
|
||||||
$browser: "-moz-" !global;
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-ms-keyframes #{$animationName} {
|
|
||||||
$browser: "-ms-" !global;
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-o-keyframes #{$animationName} {
|
|
||||||
$browser: "-o-" !global;
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes #{$animationName} {
|
|
||||||
$browser: "" !global;
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin transform($property) {
|
|
||||||
-webkit-transform: $property;
|
|
||||||
-moz-transform: $property;
|
|
||||||
-ms-transform: $property;
|
|
||||||
-o-transform: $property;
|
|
||||||
transform: $property;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin userSelect($value) {
|
|
||||||
-webkit-user-select: $value;
|
|
||||||
-moz-user-select: $value;
|
|
||||||
-ms-user-select: $value;
|
|
||||||
user-select: $value;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
@import "theme";
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-size: $defaultFontSize;
|
|
||||||
font-family: $fontFamily;
|
|
||||||
}
|
|
||||||
|
|
||||||
*,
|
|
||||||
*:before,
|
|
||||||
*:after {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
$fontFamily: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace,
|
|
||||||
"Times New Roman";
|
|
||||||
$defaultFontSize: 16px;
|
|
||||||
|
|
||||||
/* COLORS */
|
|
||||||
$hacker-green: #adff2f;
|
|
||||||
$success-green: #3adb76;
|
|
||||||
$alert-red: #ff2929;
|
|
||||||
$money-gold: #ffd700;
|
|
||||||
$light-yellow: #faffdf;
|
|
||||||
|
|
||||||
/* Attributes */
|
|
||||||
$my-stat-hp-color: #dd3434;
|
|
||||||
$my-stat-money-color: $money-gold;
|
|
||||||
$my-stat-hack-color: $hacker-green;
|
|
||||||
$my-stat-physical: $light-yellow;
|
|
||||||
$my-stat-cha-color: #a671d1;
|
|
||||||
$my-stat-int-color: #6495ed;
|
|
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Styling for the Augmentations UI. This is the page that displays all of the
|
|
||||||
* player's owned and purchased Augmentations and Source-Files. It also allows
|
|
||||||
* the player to install Augmentations
|
|
||||||
*/
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
.augmentations-content {
|
|
||||||
> p {
|
|
||||||
font-size: $defaultFontSize * 0.875;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.augmentations-list {
|
|
||||||
button,
|
|
||||||
div {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
@import "theme";
|
|
||||||
|
|
||||||
.bladeburner-container {
|
|
||||||
a,
|
|
||||||
div,
|
|
||||||
p,
|
|
||||||
pre,
|
|
||||||
td {
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-action {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
margin: 7px;
|
|
||||||
padding: 7px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
|
|
||||||
pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Whatever action is currently active */
|
|
||||||
.bladeburner-active-action {
|
|
||||||
border: 4px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Action & Skills panel navigation button */
|
|
||||||
%bladeburner-nav-button {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 2px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-nav-button {
|
|
||||||
@extend %bladeburner-nav-button;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #3d4044;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-nav-button-inactive {
|
|
||||||
@extend %bladeburner-nav-button;
|
|
||||||
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #555;
|
|
||||||
cursor: default;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checkbox for (de)selecting autoleveling */
|
|
||||||
.bbcheckbox {
|
|
||||||
position: relative;
|
|
||||||
display: inline;
|
|
||||||
label {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background: black;
|
|
||||||
border-width: 1px;
|
|
||||||
border-color: white;
|
|
||||||
border-style: solid;
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
width: 9px;
|
|
||||||
height: 5px;
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
left: 5px;
|
|
||||||
border: 3px solid white;
|
|
||||||
border-top: none;
|
|
||||||
border-right: none;
|
|
||||||
opacity: 0;
|
|
||||||
transform: rotate(-45deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input[type="checkbox"] {
|
|
||||||
margin: 3px;
|
|
||||||
visibility: hidden;
|
|
||||||
&:checked + label:after {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bladeburner Console */
|
|
||||||
.bladeburner-console-div {
|
|
||||||
display: inline-block;
|
|
||||||
width: 40%;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
overflow: auto;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-console-table {
|
|
||||||
height: auto;
|
|
||||||
overflow: auto;
|
|
||||||
table-layout: fixed;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-console-input-row {
|
|
||||||
transition: height 1s;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-console-input-cell {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-console-input {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
border: 0;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
outline: none;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bladeburner-console-line {
|
|
||||||
word-wrap: break-word;
|
|
||||||
hyphens: auto;
|
|
||||||
-webkit-hyphens: auto;
|
|
||||||
-moz-hyphens: auto;
|
|
||||||
}
|
|
112
css/buttons.scss
112
css/buttons.scss
@ -1,112 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
@import "styles";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for all buttons
|
|
||||||
*
|
|
||||||
* Includes <button> elements as well as classes that are used
|
|
||||||
* for formatting buttons
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Remove default <button> styling */
|
|
||||||
button {
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.a-link-button,
|
|
||||||
.std-button {
|
|
||||||
@extend .noselect;
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #555;
|
|
||||||
color: #fff;
|
|
||||||
padding: 3px 5px;
|
|
||||||
margin: 5px;
|
|
||||||
border: 1px solid #333;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
@include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.a-link-button-inactive,
|
|
||||||
.std-button-disabled,
|
|
||||||
.std-button:disabled {
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #333;
|
|
||||||
color: #fff;
|
|
||||||
padding: 3px 5px;
|
|
||||||
margin: 5px;
|
|
||||||
border: 1px solid #333;
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.tooltiptext,
|
|
||||||
.tooltiptexthigh,
|
|
||||||
.tooltiptextleft {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.a-link-button-bought,
|
|
||||||
.std-button-bought {
|
|
||||||
@extend .noselect;
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #0a0;
|
|
||||||
color: #fff;
|
|
||||||
padding: 3px 5px;
|
|
||||||
margin: 5px;
|
|
||||||
border: 1px solid #0a0;
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.tooltiptext,
|
|
||||||
.tooltiptexthigh,
|
|
||||||
.tooltiptextleft {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a button that is meant to be used on accordions (accordion-header and accordion-panel classes)
|
|
||||||
* It has a black background so it does not clash with the default accordion coloring
|
|
||||||
*/
|
|
||||||
.accordion-button {
|
|
||||||
@include borderRadius(12px);
|
|
||||||
@include boxShadow(1px 1px 3px #000);
|
|
||||||
|
|
||||||
color: #aaa;
|
|
||||||
font-size: $defaultFontSize;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 4px;
|
|
||||||
background-color: #000;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:active {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
/* TODO focus selector? */
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
.casino-card {
|
|
||||||
padding: 10px;
|
|
||||||
border: solid 1px #808080;
|
|
||||||
background-color: white;
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 18.5px;
|
|
||||||
text-align: center;
|
|
||||||
margin: 3px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.casino-card .value {
|
|
||||||
font-size: 20px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.casino-card.red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.casino-card.black {
|
|
||||||
color: black;
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for the Character Overview Panel (top-right panel)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#character-overview {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for Corporations
|
|
||||||
* The names/labels refer to "Company Management", which was the old name
|
|
||||||
* for the mechanic before it got changed to avoid confusion with normal
|
|
||||||
* companies
|
|
||||||
*/
|
|
||||||
|
|
||||||
.cmpy-mgmt-container p,
|
|
||||||
.cmpy-mgmt-container a,
|
|
||||||
.cmpy-mgmt-container div,
|
|
||||||
.cmpy-mgmt-container br {
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header tabs */
|
|
||||||
.cmpy-mgmt-header-tab {
|
|
||||||
display: inline-block;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #555;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-header-tab:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-header-tab.current {
|
|
||||||
background-color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Switch between Cities */
|
|
||||||
.cmpy-mgmt-city-tab {
|
|
||||||
display: inline-block;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #555;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-city-tab:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-city-tab.current {
|
|
||||||
background-color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Panels */
|
|
||||||
#cmpy-mgmt-panel {
|
|
||||||
height: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-industry-left-panel,
|
|
||||||
.cmpy-mgmt-industry-right-panel {
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow: visible;
|
|
||||||
top: 10px;
|
|
||||||
width: 45%;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-industry-overview-panel {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-employee-panel {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
display: block;
|
|
||||||
padding: 3px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-warehouse-panel {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hiring new employees */
|
|
||||||
.cmpy-mgmt-find-employee-option {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
margin: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-find-employee-option:hover {
|
|
||||||
background-color: #3d4044;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Warehouse */
|
|
||||||
.cmpy-mgmt-warehouse-material-div {
|
|
||||||
padding: 2px;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-warehouse-product-div {
|
|
||||||
padding: 2px;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Exporting materials/products */
|
|
||||||
.cmpy-mgmt-existing-export {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 25px;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-existing-export:hover {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Corporation Upgrades */
|
|
||||||
.cmpy-mgmt-upgrade-container {
|
|
||||||
border: 1px solid #fff;
|
|
||||||
width: 60%;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-upgrade-header {
|
|
||||||
margin: 6px;
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-upgrade-div {
|
|
||||||
text-align: left;
|
|
||||||
display: inline-block;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 6px;
|
|
||||||
border-radius: 25px;
|
|
||||||
font-size: $defaultFontSize * 0.75;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cmpy-mgmt-upgrade-div:hover {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Industry Upgrades */
|
|
||||||
.industry-purchases-and-upgrades-header {
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advertising */
|
|
||||||
.cmpy-mgmt-advertising-info {
|
|
||||||
font-size: $defaultFontSize * 0.75;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Research */
|
|
||||||
#corporation-research-popup-box-content {
|
|
||||||
overflow-x: auto !important;
|
|
||||||
overflow-y: auto !important;
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
.add-exp-button {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove-exp-button {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.exp-input {
|
|
||||||
margin: 5px 0 5px 0;
|
|
||||||
|
|
||||||
padding: 2px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-center {
|
|
||||||
margin: auto;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.touch-right {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.touch-left {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.touch-sides {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
/* Styling for the game options/settings
|
|
||||||
*
|
|
||||||
* Styling for the actual Game Options popup box can be found in popupboxes.scss
|
|
||||||
* This stylesheet is for everything inside the Game Options pop-up box
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
#game-options-right-panel {
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
width: 46%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: inline-block;
|
|
||||||
width: 46%;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for the Gang mechanic UI (BitNode-2)
|
|
||||||
*/
|
|
||||||
|
|
||||||
.gang-container {
|
|
||||||
p,
|
|
||||||
pre {
|
|
||||||
font-size: $defaultFontSize * 0.9375;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
background-color: black;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#gang-management-subpage > p {
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gang-member-info-div {
|
|
||||||
background-color: #555;
|
|
||||||
display: inline;
|
|
||||||
float: left;
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Showing owned upgrades in the Equipment Box
|
|
||||||
*/
|
|
||||||
|
|
||||||
.gang-owned-upgrades-div {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 6px;
|
|
||||||
width: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gang-owned-upgrade {
|
|
||||||
border: 1px solid white;
|
|
||||||
font-size: 12px;
|
|
||||||
margin: 1px;
|
|
||||||
padding: 1px;
|
|
||||||
}
|
|
3413
css/grid.min.css
vendored
3413
css/grid.min.css
vendored
File diff suppressed because it is too large
Load Diff
@ -1,69 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for the Hacknet Nodes UI Page
|
|
||||||
*/
|
|
||||||
|
|
||||||
.hacknet-general-info {
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hacknet-nodes-container li {
|
|
||||||
float: left;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
&.hacknet-node {
|
|
||||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
|
||||||
@include boxShadow($boxShadowArgs);
|
|
||||||
|
|
||||||
margin: 6px;
|
|
||||||
padding: 7px;
|
|
||||||
width: 35vw;
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#hacknet-nodes-list {
|
|
||||||
list-style: none;
|
|
||||||
width: 82vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hacknet-nodes-money {
|
|
||||||
margin: 10px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hacknet-nodes-money-multipliers-div {
|
|
||||||
display: inline-block;
|
|
||||||
width: 70vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hacknet-nodes-multipliers {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hacknet-nodes-purchase-button {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hacknet-node-container {
|
|
||||||
display: inline-table;
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: table-row;
|
|
||||||
height: 30px;
|
|
||||||
|
|
||||||
p {
|
|
||||||
display: table-cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.upgradable-info {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 4px; /* Don't want the vertical margin/padding, just left & right */
|
|
||||||
padding: 0 4px;
|
|
||||||
width: $defaultFontSize * 4;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
@import "theme";
|
|
||||||
|
|
||||||
.blinking-cursor {
|
|
||||||
font-weight: 100;
|
|
||||||
color: #2e3d48;
|
|
||||||
-webkit-animation: 1s cursorblink step-end infinite;
|
|
||||||
-moz-animation: 1s cursorblink step-end infinite;
|
|
||||||
-ms-animation: 1s cursorblink step-end infinite;
|
|
||||||
-o-animation: 1s cursorblink step-end infinite;
|
|
||||||
animation: 1s cursorblink step-end infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cursorblink {
|
|
||||||
from,
|
|
||||||
to {
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
color: $hacker-green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes cursorblink {
|
|
||||||
from,
|
|
||||||
to {
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
color: $hacker-green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes cursorblink {
|
|
||||||
from,
|
|
||||||
to {
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
color: $hacker-green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-ms-keyframes cursorblink {
|
|
||||||
from,
|
|
||||||
to {
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
color: $hacker-green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-o-keyframes cursorblink {
|
|
||||||
from,
|
|
||||||
to {
|
|
||||||
color: transparent;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
color: $hacker-green;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/* interactivetutorial.css */
|
|
||||||
#interactive-tutorial-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#interactive-tutorial-container {
|
|
||||||
display: none;
|
|
||||||
position: fixed; /* Stay in place */
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
height: 450px;
|
|
||||||
padding: 10px;
|
|
||||||
border: 5px solid #fff;
|
|
||||||
width: 23%;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: #444; /* Fallback color */
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
> strong {
|
|
||||||
background-color: #444;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#interactive-tutorial-text {
|
|
||||||
padding: 4px;
|
|
||||||
margin: 4px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #444;
|
|
||||||
font-size: $defaultFontSize * 0.875;
|
|
||||||
max-height: 350px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#interactive-tutorial-exit,
|
|
||||||
#interactive-tutorial-next,
|
|
||||||
#interactive-tutorial-back {
|
|
||||||
@include borderRadius(12px);
|
|
||||||
@include boxShadow(1px 1px 3px #000);
|
|
||||||
|
|
||||||
color: #aaa;
|
|
||||||
font-size: $defaultFontSize * 1.125;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #000;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#interactive-tutorial-exit {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#interactive-tutorial-back {
|
|
||||||
float: left;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#interactive-tutorial-next {
|
|
||||||
float: right;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-tutorial-command {
|
|
||||||
background-color: #000;
|
|
||||||
color: $hacker-green;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-tutorial-code {
|
|
||||||
background-color: #272822;
|
|
||||||
color: white;
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-tutorial-tab {
|
|
||||||
background-color: #555;
|
|
||||||
color: #e6e6e6;
|
|
||||||
padding: 3px;
|
|
||||||
box-shadow: 0 0 3px #000;
|
|
||||||
}
|
|
111
css/loader.scss
111
css/loader.scss
@ -1,111 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "reset";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
@include keyframes(LOADERSPINNER) {
|
|
||||||
0% {
|
|
||||||
#{$browser}transform: translate(-50%, -50%) rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
#{$browser}transform: translate(-50%, -50%) rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include keyframes(LOADERLABEL) {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
#{$browser}transform: translate(-50%, -50%) scale(1);
|
|
||||||
}
|
|
||||||
5% {
|
|
||||||
opacity: 0.5;
|
|
||||||
#{$browser}transform: translate(-50%, -50%) scale(0.5);
|
|
||||||
}
|
|
||||||
95% {
|
|
||||||
opacity: 0.5;
|
|
||||||
#{$browser}transform: translate(-50%, -50%) scale(0.5);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
#{$browser}transform: translate(-50%, -50%) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderoverlay {
|
|
||||||
$spinnerBoxSize: 200px;
|
|
||||||
$themeColor: #0c0;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #000;
|
|
||||||
color: $themeColor;
|
|
||||||
|
|
||||||
%spinnerBox {
|
|
||||||
border: 20px solid rgba(0, 0, 0, 0);
|
|
||||||
border-top-color: $themeColor;
|
|
||||||
border-bottom-color: $themeColor;
|
|
||||||
border-radius: 1000px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderspinner:before,
|
|
||||||
.loaderspinner:after {
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderspinner {
|
|
||||||
@extend %spinnerBox;
|
|
||||||
@include animation(LOADERSPINNER 5s linear infinite);
|
|
||||||
|
|
||||||
width: $spinnerBoxSize;
|
|
||||||
height: $spinnerBoxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderspinner:before {
|
|
||||||
@extend %spinnerBox;
|
|
||||||
@include animation(LOADERSPINNER 10s linear infinite);
|
|
||||||
|
|
||||||
width: $spinnerBoxSize * 0.8;
|
|
||||||
height: $spinnerBoxSize * 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderspinner:after {
|
|
||||||
@extend %spinnerBox;
|
|
||||||
@include animation(LOADERSPINNER 5s linear infinite);
|
|
||||||
|
|
||||||
width: $spinnerBoxSize * 0.6;
|
|
||||||
height: $spinnerBoxSize * 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loaderlabel {
|
|
||||||
@include animation(LOADERLABEL 5s linear infinite);
|
|
||||||
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: $defaultFontSize * 1.375;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.killAllMessage {
|
|
||||||
position: absolute;
|
|
||||||
top: 95%;
|
|
||||||
left: 50%;
|
|
||||||
-webkit-transform: translateX(-50%);
|
|
||||||
-moz-transform: translateX(-50%);
|
|
||||||
-ms-transform: translateX(-50%);
|
|
||||||
-o-transform: translateX(-50%);
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
.killAllMessageWrapperHidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.killAllMessageWrapperShow {
|
|
||||||
display: block;
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for the main navigation menu on the left-hand-side
|
|
||||||
*/
|
|
||||||
|
|
||||||
.mainmenu {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 10%;
|
|
||||||
position: fixed;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #000;
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default buttons */
|
|
||||||
.mainmenu > li a,
|
|
||||||
.mainmenu > li button {
|
|
||||||
display: block;
|
|
||||||
color: #e6e6e6;
|
|
||||||
background-color: #555;
|
|
||||||
padding: 12px 8px;
|
|
||||||
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainmenu.classic > li a,
|
|
||||||
.mainmenu.classic > li button {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainmenu.compact > li a,
|
|
||||||
.mainmenu.compact > li button {
|
|
||||||
display: block;
|
|
||||||
color: #e6e6e6;
|
|
||||||
background-color: #555;
|
|
||||||
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hovering makes them lighter */
|
|
||||||
.mainmenu > li a:hover,
|
|
||||||
.mainmenu > li a:hover:not(.active),
|
|
||||||
.mainmenu > li a:focus {
|
|
||||||
background-color: #777;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainmenu > li button:hover,
|
|
||||||
.mainmenu > li button:hover:not(.active) {
|
|
||||||
background-color: #777;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Panel headers can become active, and they are "lighter" than the rest */
|
|
||||||
.mainmenu > li a.active,
|
|
||||||
.mainmenu > li button.active {
|
|
||||||
background-color: #777;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainmenu > li a.active:hover,
|
|
||||||
.mainmenu > li button.active:hover {
|
|
||||||
background-color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-header {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#hacking-menu-header-li,
|
|
||||||
#character-menu-header-li,
|
|
||||||
#world-menu-header-li,
|
|
||||||
#help-menu-header-li {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Accordion Outline */
|
|
||||||
.mainmenu-accordion-header,
|
|
||||||
.mainmenu-accordion-header-compact {
|
|
||||||
outline: 2px solid #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainmenu-accordion-header-classic {
|
|
||||||
border: 2px solid #fff;
|
|
||||||
padding: 16px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Plus and minus signs */
|
|
||||||
.mainmenu-accordion-header:after,
|
|
||||||
.mainmenu-accordion-header-compact:after {
|
|
||||||
content: "\02795";
|
|
||||||
float: right;
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 25%;
|
|
||||||
right: 3px;
|
|
||||||
color: transparent;
|
|
||||||
text-shadow: 0 0 0 #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainmenu-accordion-header-classic:after {
|
|
||||||
content: "\02795";
|
|
||||||
float: right;
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
color: #fff;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainmenu-accordion-header.opened,
|
|
||||||
.mainmenu-accordion-header-classic.opened,
|
|
||||||
.mainmenu-accordion-header-compact.opened {
|
|
||||||
background-color: #222 !important;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "\2796";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Slide down transition */
|
|
||||||
.mainmenu-accordion-panel {
|
|
||||||
transition: max-height 0.2s ease-out;
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
|
|
||||||
terminal which has its own page) */
|
|
||||||
|
|
||||||
#generic-react-container {
|
|
||||||
-ms-overflow-style: none; /* for Internet Explorer, Edge */
|
|
||||||
scrollbar-width: none; /* for Firefox */
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#generic-react-container::-webkit-scrollbar {
|
|
||||||
display: none; /* for Chrome, Safari, and Opera */
|
|
||||||
}
|
|
||||||
|
|
||||||
#world-city-name,
|
|
||||||
#world-city-desc {
|
|
||||||
padding: 4px;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#create-program-page-text,
|
|
||||||
#create-program-list {
|
|
||||||
width: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.faction-work-div {
|
|
||||||
width: 70%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.faction-work-div-wrapper {
|
|
||||||
overflow: hidden;
|
|
||||||
border: 2px solid #333;
|
|
||||||
padding: 6px;
|
|
||||||
margin: 6px;
|
|
||||||
width: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.faction-container p,
|
|
||||||
.faction-container pre {
|
|
||||||
padding: 4px 6px;
|
|
||||||
margin: 4px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.faction-container pre {
|
|
||||||
width: 70%;
|
|
||||||
white-space: pre-wrap; /* Since CSS 2.1 */
|
|
||||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
|
||||||
white-space: -pre-wrap; /* Opera 4-6 */
|
|
||||||
white-space: -o-pre-wrap; /* Opera 7 */
|
|
||||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* World */
|
|
||||||
#world-container li {
|
|
||||||
margin: 0 0 15px 0;
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tutorial */
|
|
||||||
#tutorial-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tutorial-text {
|
|
||||||
width: 70%;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tutorial-container a {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dev menu */
|
|
||||||
#dev-menu-container {
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-menu-text {
|
|
||||||
width: 70%;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dev-menu-container a {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Location */
|
|
||||||
#location-container {
|
|
||||||
position: fixed;
|
|
||||||
padding: 6px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#location-container a {
|
|
||||||
display: inline-block;
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#location-slums-description {
|
|
||||||
width: 70%;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#location-return-to-world-button {
|
|
||||||
margin: 10px;
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#location-container > * {
|
|
||||||
margin: 10px 5px 10px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#location-job-reputation,
|
|
||||||
#location-company-favor {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Infiltration */
|
|
||||||
#infiltration-container {
|
|
||||||
position: fixed;
|
|
||||||
margin: 5px;
|
|
||||||
width: 70%;
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
.milestones-container {
|
|
||||||
width: 60%;
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/* css for Missions */
|
|
||||||
|
|
||||||
/* Hacking missions */
|
|
||||||
#mission-container {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
|
||||||
grid-template-rows: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
|
||||||
grid-gap: 2.5%;
|
|
||||||
height: 90%;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-right: 10px;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-node {
|
|
||||||
z-index: 5;
|
|
||||||
background-color: #808080;
|
|
||||||
align-self: center;
|
|
||||||
justify-self: center;
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
p {
|
|
||||||
@include userSelect(none);
|
|
||||||
|
|
||||||
margin-top: 8px;
|
|
||||||
color: #fff;
|
|
||||||
font-size: $defaultFontSize * 0.75;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-player-node {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #00f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-player-node-active {
|
|
||||||
border: 2px solid #fff;
|
|
||||||
background-color: #66f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-enemy-node {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #f00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-cpu-node {
|
|
||||||
@include borderRadius(50%);
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-firewall-node {
|
|
||||||
width: 90%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-database-node {
|
|
||||||
@include transform(skew(20deg));
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 90%;
|
|
||||||
|
|
||||||
p {
|
|
||||||
@include transform(skew(-20deg));
|
|
||||||
@include userSelect(none);
|
|
||||||
|
|
||||||
color: #fff;
|
|
||||||
font-size: $defaultFontSize * 0.75;
|
|
||||||
margin-top: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-transfer-node {
|
|
||||||
@include transform(skew(-20deg));
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 90%;
|
|
||||||
|
|
||||||
p {
|
|
||||||
@include transform(skew(20deg));
|
|
||||||
@include userSelect(none);
|
|
||||||
|
|
||||||
color: #fff;
|
|
||||||
font-size: $defaultFontSize * 0.75;
|
|
||||||
margin-top: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-spam-node,
|
|
||||||
.hack-mission-shield-node {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Non-map related DOM elements */
|
|
||||||
|
|
||||||
/* Element at the top of the Hacking Mission page (intro page, start button, guide buttons, etc.) */
|
|
||||||
.hack-mission-header-element {
|
|
||||||
margin: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hack-mission-action-buttons-container {
|
|
||||||
border: 2px solid #fff;
|
|
||||||
}
|
|
@ -1,246 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
@import "styles";
|
|
||||||
|
|
||||||
/* Pop-up boxes */
|
|
||||||
.popup-box-container {
|
|
||||||
display: none; /* Initially hidden */
|
|
||||||
position: fixed; /* Stay in place */
|
|
||||||
z-index: 1300; /* Sit on top */
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rbga(var(--my-background-color), 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-box-content {
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
padding: 12px;
|
|
||||||
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: 0px 3px 5px -1px #090, 0px 5px 8px 0px #090, 0px 1px 14px 0px #090;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-box-input-div {
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-box-button,
|
|
||||||
.popup-box-button-inactive {
|
|
||||||
color: #aaa;
|
|
||||||
float: right;
|
|
||||||
font-size: $defaultFontSize;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 6px;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popup-box-button:hover,
|
|
||||||
.popup-box-button:focus {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popupbox-button-inactive {
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
#yes-no-text-input-box-input {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
border: 1px solid #fff;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-box-container {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
width: 50%;
|
|
||||||
height: auto;
|
|
||||||
max-height: 50%;
|
|
||||||
top: 40%;
|
|
||||||
left: 50%;
|
|
||||||
margin: -10% 0 0 -25%;
|
|
||||||
overflow: auto;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
border: 5px solid var(--my-highlight-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-box-container {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
background-color: gray;
|
|
||||||
width: 50%;
|
|
||||||
position: fixed;
|
|
||||||
left: 50%;
|
|
||||||
top: 40%;
|
|
||||||
margin: -10% 0 0 -25%;
|
|
||||||
height: auto;
|
|
||||||
max-height: 50%;
|
|
||||||
z-index: 10;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
border: 2px solid $hacker-green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-box-header {
|
|
||||||
z-index: 1300;
|
|
||||||
background-color: #333;
|
|
||||||
border: 2px solid $hacker-green;
|
|
||||||
display: flex;
|
|
||||||
flex: row nowrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-box-log-container {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-box-button {
|
|
||||||
color: #aaa;
|
|
||||||
font-size: $defaultFontSize;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 2px;
|
|
||||||
margin: 6px;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.log-box-button:hover,
|
|
||||||
.log-box-button:focus {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-box-content {
|
|
||||||
z-index: 2;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
p span {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-box-close-button {
|
|
||||||
@include borderRadius(12px);
|
|
||||||
@include boxShadow(1px 1px 3px #000);
|
|
||||||
@extend .noselect;
|
|
||||||
|
|
||||||
float: right;
|
|
||||||
color: #aaa;
|
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#log-box-close {
|
|
||||||
position: fixed;
|
|
||||||
right: 27%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#log-box-kill-script {
|
|
||||||
right: 11%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#log-box-close,
|
|
||||||
#log-box-kill-script {
|
|
||||||
float: right;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-box-close-button:hover,
|
|
||||||
.dialog-box-close-button:focus {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Faction invitation box */
|
|
||||||
#faction-invitation-box-container {
|
|
||||||
transition: opacity 400ms ease-in;
|
|
||||||
}
|
|
||||||
#faction-invitation-box-warning {
|
|
||||||
margin: 4px;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Infiltration-box */
|
|
||||||
#infiltration-box-sell,
|
|
||||||
#infiltration-box-faction {
|
|
||||||
display: block;
|
|
||||||
padding: 8px;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#infiltration-box-content span {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#infiltration-faction-select {
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Game Options */
|
|
||||||
#game-options-container {
|
|
||||||
transition: opacity 400ms ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
#game-options-content {
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
padding: 10px;
|
|
||||||
border: 5px solid var(--my-highlight-color);
|
|
||||||
color: var(--my-font-color);
|
|
||||||
width: 80%;
|
|
||||||
max-height: 80%;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#game-options-left-panel,
|
|
||||||
#game-options-right-panel {
|
|
||||||
display: inline-block;
|
|
||||||
width: 49%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#game-options-close-button {
|
|
||||||
@include borderRadius(12px);
|
|
||||||
@include boxShadow(1px 1px 3px #000);
|
|
||||||
|
|
||||||
color: #aaa;
|
|
||||||
float: right;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 4px;
|
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#game-options-close-button:hover,
|
|
||||||
#game-options-close-button:focus {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#game-options-left-panel fieldset {
|
|
||||||
padding: 2px;
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#import-game-file-selector {
|
|
||||||
display: none;
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
@import "theme";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for the Red Pill screen (the BitNode selection UI)
|
|
||||||
*/
|
|
||||||
#red-pill-container {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bitnode {
|
|
||||||
&.level-0 {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.level-1 {
|
|
||||||
color: yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.level-2 {
|
|
||||||
color: #48d1cc;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.level-3 {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.unimplemented {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* Styling for the Re-Sleeving Page
|
|
||||||
*/
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
.resleeve-elem {
|
|
||||||
border: 1px solid white;
|
|
||||||
margin: 4px;
|
|
||||||
width: 75%;
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.resleeve-panel {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resleeve-aug-selector {
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
|
|
||||||
option {
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Styling for Script Editor (both Ace and CodeMirror)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#script-editor-buttons-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.script-editor-wrapper {
|
|
||||||
height: 110vh;
|
|
||||||
width: 70%;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#script-editor-filename-wrapper {
|
|
||||||
background-color: #555;
|
|
||||||
margin-right: 0;
|
|
||||||
padding-left: 6px;
|
|
||||||
width: 100%;
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#script-editor-filename-tag {
|
|
||||||
display: inline-block;
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
float: center;
|
|
||||||
background-color: #555;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#script-editor-filename {
|
|
||||||
$boxShadowArgs: inset 0 0 8px rgba(0, 0, 0, 0.1), 0 0 16px rgba(0, 0, 0, 0.1);
|
|
||||||
@include boxShadow($boxShadowArgs);
|
|
||||||
|
|
||||||
background-color: #555;
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
color: #fff;
|
|
||||||
display: inline-block;
|
|
||||||
float: center;
|
|
||||||
margin: 4px;
|
|
||||||
padding: 2px;
|
|
||||||
resize: none;
|
|
||||||
width: 60%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#script-editor-status {
|
|
||||||
float: left;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#script-editor-options-panel {
|
|
||||||
position: absolute;
|
|
||||||
right: 9%;
|
|
||||||
bottom: 15%;
|
|
||||||
border: 2px solid #fff;
|
|
||||||
width: 19%;
|
|
||||||
background-color: #444;
|
|
||||||
padding: 2px;
|
|
||||||
overflow: auto;
|
|
||||||
z-index: 1;
|
|
||||||
color: #fff;
|
|
||||||
max-height: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#script-editor-options-panel fieldset {
|
|
||||||
margin-top: 8px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
padding: 2px;
|
|
||||||
font-size: $defaultFontSize * 0.75;
|
|
||||||
|
|
||||||
input {
|
|
||||||
margin: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-options-container {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-options-line {
|
|
||||||
display: flex;
|
|
||||||
flex: row nowrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: start;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* Styling for the Sleeves Management page
|
|
||||||
*/
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
.sleeve-elem {
|
|
||||||
border: 1px solid white;
|
|
||||||
margin: 4px;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sleeves-page-info {
|
|
||||||
display: "block";
|
|
||||||
width: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sleeve-panel {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 2px;
|
|
||||||
|
|
||||||
select {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
@import "theme";
|
|
||||||
|
|
||||||
.stock-market-container {
|
|
||||||
p {
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-size: $defaultFontSize * 0.875;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-market-info-and-purchases {
|
|
||||||
> h2 {
|
|
||||||
display: block;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> p {
|
|
||||||
display: block;
|
|
||||||
margin-left: 10px;
|
|
||||||
width: 70%;
|
|
||||||
}
|
|
||||||
|
|
||||||
> a,
|
|
||||||
> button {
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#stock-market-list {
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
li {
|
|
||||||
button {
|
|
||||||
font-size: $defaultFontSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#stock-market-watchlist-filter {
|
|
||||||
display: block;
|
|
||||||
margin: 5px 5px 5px 10px;
|
|
||||||
padding: 4px;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-market-input {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 4px;
|
|
||||||
margin: 2px;
|
|
||||||
background-color: #000;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-market-price-movement-warning {
|
|
||||||
border: 1px solid white;
|
|
||||||
color: red;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-market-position-text {
|
|
||||||
color: #fff;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: #fff;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-market-order-list {
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 100px;
|
|
||||||
|
|
||||||
li {
|
|
||||||
color: #fff;
|
|
||||||
padding: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-market-order-cancel-btn {
|
|
||||||
background-color: #000;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
margin: 2px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
528
css/styles.scss
528
css/styles.scss
@ -1,528 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
@import "reset";
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--my-font-color: #0c0;
|
|
||||||
--my-background-color: #000;
|
|
||||||
--my-highlight-color: #fff;
|
|
||||||
--my-prompt-color: #f92672;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
-ms-overflow-style: none; /* for Internet Explorer, Edge */
|
|
||||||
scrollbar-width: none; /* for Firefox */
|
|
||||||
}
|
|
||||||
|
|
||||||
body::-webkit-scrollbar {
|
|
||||||
display: none; /* for Chrome, Safari, and Opera */
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
pre,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
.text,
|
|
||||||
td {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: $defaultFontSize * 1.375;
|
|
||||||
color: var(--my-font-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding: 2px;
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
br {
|
|
||||||
@extend .noselect;
|
|
||||||
}
|
|
||||||
|
|
||||||
#entire-game-container {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable border highlight on elements */
|
|
||||||
input:focus,
|
|
||||||
textarea:focus,
|
|
||||||
button:focus,
|
|
||||||
td:focus,
|
|
||||||
tr:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make html links ("a" elements) nice looking buttons with this class */
|
|
||||||
a:link,
|
|
||||||
a:visited {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-input {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #000;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 1px;
|
|
||||||
border-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notification icon (for create program right now only) */
|
|
||||||
#create-program-tab {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#create-program-notification {
|
|
||||||
font-size: $defaultFontSize * 0.625;
|
|
||||||
position: absolute; /* Position the badge within the relatively positioned button */
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#factions-tab {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#factions-notification {
|
|
||||||
font-size: $defaultFontSize * 0.625;
|
|
||||||
position: absolute; /* Position the badge within the relatively positioned button */
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#augmentations-tab {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#augmentations-notification {
|
|
||||||
font-size: $defaultFontSize * 0.625;
|
|
||||||
position: absolute; /* Position the badge within the relatively positioned button */
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-on {
|
|
||||||
background-color: #fa3e3e;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 1px 3px;
|
|
||||||
font-size: $defaultFontSize * 0.625;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-off {
|
|
||||||
background-color: #333;
|
|
||||||
color: #333;
|
|
||||||
border-radius: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification .badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 2px;
|
|
||||||
background: red;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* help tip. Question mark that opens popup with info/details */
|
|
||||||
.help-tip {
|
|
||||||
background-color: black;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 5px;
|
|
||||||
color: #fff;
|
|
||||||
content: "?";
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 3px;
|
|
||||||
padding: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-tip-big {
|
|
||||||
content: "?";
|
|
||||||
padding: 3px;
|
|
||||||
margin-left: 3px;
|
|
||||||
color: #fff;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-tip:hover,
|
|
||||||
.help-tip-big:hover {
|
|
||||||
background-color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-tip:active,
|
|
||||||
.help-tip-big:active {
|
|
||||||
@include boxShadow(inset 0 1px 4px rgba(0, 0, 0, 0.6));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flashing button (Red) */
|
|
||||||
@-webkit-keyframes glowing {
|
|
||||||
0% {
|
|
||||||
background-color: #b20000;
|
|
||||||
-webkit-box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: #f00;
|
|
||||||
-webkit-box-shadow: 0 0 40px #f00;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-color: #b20000;
|
|
||||||
-webkit-box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes glowing {
|
|
||||||
0% {
|
|
||||||
background-color: #b20000;
|
|
||||||
-moz-box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: #f00;
|
|
||||||
-moz-box-shadow: 0 0 40px #f00;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-color: #b20000;
|
|
||||||
-moz-box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-o-keyframes glowing {
|
|
||||||
0% {
|
|
||||||
background-color: #b20000;
|
|
||||||
box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: #f00;
|
|
||||||
box-shadow: 0 0 40px #f00;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-color: #b20000;
|
|
||||||
box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes glowing {
|
|
||||||
0% {
|
|
||||||
background-color: #b20000;
|
|
||||||
box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: #f00;
|
|
||||||
box-shadow: 0 0 40px #f00;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
background-color: #b20000;
|
|
||||||
box-shadow: 0 0 3px #b20000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.flashing-button {
|
|
||||||
-webkit-animation: glowing 1500ms infinite;
|
|
||||||
-moz-animation: glowing 1500ms infinite;
|
|
||||||
-o-animation: glowing 1500ms infinite;
|
|
||||||
animation: glowing 1500ms infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Blinking Cursor */
|
|
||||||
/* ----- blinking cursor animation ----- */
|
|
||||||
.typed-cursor {
|
|
||||||
opacity: 1;
|
|
||||||
-webkit-animation: blink 0.95s infinite;
|
|
||||||
-moz-animation: blink 0.95s infinite;
|
|
||||||
-ms-animation: blink 0.95s infinite;
|
|
||||||
-o-animation: blink 0.95s infinite;
|
|
||||||
animation: blink 0.95s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-keyframes blink {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-webkit-keyframes blink {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-moz-keyframes blink {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-ms-keyframes blink {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-o-keyframes blink {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Status text */
|
|
||||||
@-webkit-keyframes status-text {
|
|
||||||
from {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-text {
|
|
||||||
z-index: 2;
|
|
||||||
-webkit-animation: status-text 3s 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#status-text-container {
|
|
||||||
background-color: transparent;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#status-text {
|
|
||||||
background-color: transparent;
|
|
||||||
bottom: 0;
|
|
||||||
color: #fff;
|
|
||||||
display: none;
|
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
margin-right: 14px;
|
|
||||||
opacity: 0;
|
|
||||||
padding: 4px;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan analyze links from AutoLink */
|
|
||||||
.scan-analyze-link {
|
|
||||||
cursor: pointer;
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Accordion menus (Header with collapsible panel) */
|
|
||||||
.accordion-header {
|
|
||||||
background-color: #444;
|
|
||||||
color: #fff;
|
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
margin: 6px 6px 0 6px;
|
|
||||||
padding: 4px 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 80%;
|
|
||||||
text-align: left;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&.active,
|
|
||||||
&:hover {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "\02795"; /* "plus" sign (+) */
|
|
||||||
font-size: $defaultFontSize * 0.875;
|
|
||||||
float: right;
|
|
||||||
color: transparent;
|
|
||||||
text-shadow: 0 0 0 #fff;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 5px;
|
|
||||||
right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active:after {
|
|
||||||
content: "\2796"; /* "minus" sign (-) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.accordion-panel {
|
|
||||||
margin: 0 6px 6px 6px;
|
|
||||||
padding: 0 6px 6px 6px;
|
|
||||||
width: 75%;
|
|
||||||
margin-left: 5%;
|
|
||||||
display: none;
|
|
||||||
background-color: #555;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: none;
|
|
||||||
|
|
||||||
div,
|
|
||||||
ul,
|
|
||||||
p,
|
|
||||||
ul > li {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* override the global <span> styling */
|
|
||||||
#active-scripts-total-production-active,
|
|
||||||
#active-scripts-total-prod-aug-total,
|
|
||||||
#active-scripts-total-prod-aug-avg {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper Classes */
|
|
||||||
.hacker-green {
|
|
||||||
color: $hacker-green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.money-gold {
|
|
||||||
color: $money-gold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.light-yellow {
|
|
||||||
color: $light-yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unbuyable {
|
|
||||||
color: #66cfbc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.failure {
|
|
||||||
color: $alert-red;
|
|
||||||
text-shadow: 0 0 0 $alert-red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success {
|
|
||||||
color: $success-green;
|
|
||||||
text-shadow: 0 0 0 $success-green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.physical-yellow {
|
|
||||||
color: $my-stat-physical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.charisma-purple {
|
|
||||||
color: $my-stat-cha-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reputation {
|
|
||||||
color: $light-yellow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smallfont {
|
|
||||||
font-size: $defaultFontSize * 0.8125;
|
|
||||||
}
|
|
||||||
|
|
||||||
.samefont {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noscrollbar {
|
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
|
||||||
/* stylelint-disable-next-line property-no-unknown */
|
|
||||||
scrollbar-width: none; /* Firefox https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-width */
|
|
||||||
}
|
|
||||||
|
|
||||||
.noscrollbar::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
filter: invert(1) sepia(1) hue-rotate(41deg) brightness(100%) saturate(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
.optionCheckbox {
|
|
||||||
margin: 5px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.optionRange {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
background: #777;
|
|
||||||
outline: none;
|
|
||||||
opacity: 0.7;
|
|
||||||
height: 10px;
|
|
||||||
-webkit-transition: 0.2s;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
margin: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.optionRange::-webkit-slider-thumb {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background: var(--my-font-color);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.optionRange::-moz-range-thumb {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background: var(--my-font-color);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noselect {
|
|
||||||
-moz-user-select: -moz-none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
@import "theme";
|
|
||||||
|
|
||||||
/* Styling for tooltip-style elements */
|
|
||||||
|
|
||||||
/* Tool tips (when hovering over an element */
|
|
||||||
.tooltip {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.tooltiptext {
|
|
||||||
visibility: hidden;
|
|
||||||
width: 300px;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
padding: 4px;
|
|
||||||
left: 101%;
|
|
||||||
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Positioned to left of element rather than right */
|
|
||||||
.tooltiptextleft {
|
|
||||||
visibility: hidden;
|
|
||||||
width: 300px;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
padding: 4px;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-100%, -100%);
|
|
||||||
|
|
||||||
/* Backwards compatibility */
|
|
||||||
-webkit-transform: translate(-100%, -100%);
|
|
||||||
-moz-transform: translate(-100%, -100%);
|
|
||||||
-o-transform: translate(-100%, -100%);
|
|
||||||
-ms-transform: translate(-100%, -100%);
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tooltip goes below cursor instead of above */
|
|
||||||
.tooltiptextlow {
|
|
||||||
visibility: hidden;
|
|
||||||
width: 300px;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
padding: 4px;
|
|
||||||
left: 101%;
|
|
||||||
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99;
|
|
||||||
bottom: 25%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Same thing as a normal tooltip except its a bit higher */
|
|
||||||
.tooltip .tooltiptexthigh {
|
|
||||||
visibility: hidden;
|
|
||||||
width: 300px;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
padding: 4px;
|
|
||||||
left: 101%;
|
|
||||||
bottom: -25%;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip:hover .tooltiptext,
|
|
||||||
.tooltip:hover .tooltiptexthigh,
|
|
||||||
.tooltip:hover .tooltiptextleft,
|
|
||||||
.tooltip:hover .tooltiptextlow {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy_tooltip {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy_tooltip_copied {
|
|
||||||
color: #fff;
|
|
||||||
transition: color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy_tooltip .copy_tooltip_text {
|
|
||||||
visibility: hidden;
|
|
||||||
font-size: 15px;
|
|
||||||
padding: 5px;
|
|
||||||
background-color: var(--my-background-color);
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top: 120%;
|
|
||||||
left: 5%;
|
|
||||||
opacity: 0;
|
|
||||||
border: 2px solid var(--my-highlight-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy_tooltip .copy_tooltip_text::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
bottom: 100%;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -6px;
|
|
||||||
border-width: 8px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent transparent white transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy_tooltip .copy_tooltip_text_visible {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 0.3s;
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
/* required LIB STYLES */
|
|
||||||
/* .Treant se automatski dodaje na svaki chart conatiner */
|
|
||||||
.Treant {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
.Treant > .node,
|
|
||||||
.Treant > .pseudo {
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
.Treant.Treant-loaded .node,
|
|
||||||
.Treant.Treant-loaded .pseudo {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
.Treant > .pseudo {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.Treant .collapse-switch {
|
|
||||||
width: 3px;
|
|
||||||
height: 3px;
|
|
||||||
display: block;
|
|
||||||
border: 1px solid black;
|
|
||||||
position: absolute;
|
|
||||||
top: 1px;
|
|
||||||
right: 1px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.Treant .collapsed .collapse-switch {
|
|
||||||
background-color: #868dee;
|
|
||||||
}
|
|
||||||
.Treant > .node img {
|
|
||||||
border: none;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.Treant > .node {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px;
|
|
||||||
min-width: 60px;
|
|
||||||
text-align: center;
|
|
||||||
border: 2px solid #e8e8e3;
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Treant > .researched {
|
|
||||||
background-color: #666;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Treant > .locked > div {
|
|
||||||
color: red;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Treant > .node > div {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Treant > .unlocked:hover {
|
|
||||||
background-color: #666;
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
@import "mixins";
|
|
||||||
@import "theme";
|
|
||||||
|
|
||||||
/* Both Work in progress and BitNode stuff */
|
|
||||||
.generic-fullscreen-container {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
width: 99%;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.generic-fullscreen-container-scroll {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#work-in-progress-container {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#work-in-progress-text {
|
|
||||||
color: var(--my-font-color);
|
|
||||||
width: 70%;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.work-button {
|
|
||||||
@include borderRadius(12px);
|
|
||||||
@include boxShadow(1px 1px 3px #000);
|
|
||||||
|
|
||||||
color: #aaa;
|
|
||||||
float: left;
|
|
||||||
font-size: $defaultFontSize * 1.25;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 10px;
|
|
||||||
padding: 5px;
|
|
||||||
border: 3px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.work-button:hover,
|
|
||||||
.work-button:focus {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#cinematic-text-container {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
66
dist/vendor.bundle.js
vendored
66
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -3,6 +3,28 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
v0.55.0 - 2021-09-20 Material UI (hydroflame & community)
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
** Global **
|
||||||
|
|
||||||
|
* The game is now 100% in typescript, react, and Material-UI
|
||||||
|
|
||||||
|
** Misc. **
|
||||||
|
|
||||||
|
* Corporations can no longer bribe special factions
|
||||||
|
* Infiltration can no longer lose focus of the keyboard.
|
||||||
|
* Fix terminal line limit
|
||||||
|
* Added theme editor
|
||||||
|
* Theme applies on game load (@Nolshine)
|
||||||
|
* Sleeves no longer consume all bonus time for some actions
|
||||||
|
* Fix a bug where the autocomlete list would get duplicates
|
||||||
|
* Fix tutorial not scaling properly on small screens
|
||||||
|
* Import should be more consistent
|
||||||
|
* Typo with 'help' command
|
||||||
|
* Fix infinite loop in casino
|
||||||
|
* nerf noodle bar
|
||||||
|
|
||||||
v0.54.0 - 2021-09-20 One big react node (hydroflame & community)
|
v0.54.0 - 2021-09-20 One big react node (hydroflame & community)
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
|
@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.54'
|
version = '0.55'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '0.54.0'
|
release = '0.55.0'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
60
index.html
60
index.html
@ -1,18 +1,18 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8"/>
|
||||||
<title>Bitburner</title>
|
<title>Bitburner</title>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="dist/apple-touch-icon.png"/>
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png" />
|
<link rel="icon" type="image/png" sizes="32x32" href="dist/favicon-32x32.png"/>
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png" />
|
<link rel="icon" type="image/png" sizes="16x16" href="dist/favicon-16x16.png"/>
|
||||||
<link rel="manifest" href="dist/site.webmanifest" />
|
<link rel="manifest" href="dist/site.webmanifest"/>
|
||||||
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000" />
|
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000"/>
|
||||||
<meta name="apple-mobile-web-app-title" content="Bitburner" />
|
<meta name="apple-mobile-web-app-title" content="Bitburner"/>
|
||||||
<meta name="application-name" content="Bitburner" />
|
<meta name="application-name" content="Bitburner"/>
|
||||||
<meta name="msapplication-TileColor" content="#000000" />
|
<meta name="msapplication-TileColor" content="#000000"/>
|
||||||
<meta name="msapplication-config" content="dist/browserconfig.xml" />
|
<meta name="msapplication-config" content="dist/browserconfig.xml"/>
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff"/>
|
||||||
|
|
||||||
<!-- Google Analytics -->
|
<!-- Google Analytics -->
|
||||||
<script>
|
<script>
|
||||||
@ -30,31 +30,27 @@
|
|||||||
m.parentNode.insertBefore(a, m);
|
m.parentNode.insertBefore(a, m);
|
||||||
})(window, document, "script", "https://www.google-analytics.com/analytics.js", "ga");
|
})(window, document, "script", "https://www.google-analytics.com/analytics.js", "ga");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
ga("create", "UA-100157497-1", "auto");
|
ga("create", "UA-100157497-1", "auto");
|
||||||
ga("send", "pageview");
|
ga("send", "pageview");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
-ms-overflow-style: none; /* for Internet Explorer, Edge */
|
||||||
|
scrollbar-width: none; /* for Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
<link rel="shortcut icon" href="favicon.ico" />
|
*::-webkit-scrollbar {
|
||||||
<link href="dist/vendor.css" rel="stylesheet" />
|
display: none; /* for Chrome, Safari, and Opera */
|
||||||
<link href="main.css" rel="stylesheet" />
|
}
|
||||||
</head>
|
</style>
|
||||||
|
<link rel="shortcut icon" href="favicon.ico"></head>
|
||||||
<body>
|
<body>
|
||||||
<div id="entire-game-container">
|
<div id="root"/>
|
||||||
<div id="mainmenu-container" style="display: flex; flex-direction: row"></div>
|
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
|
||||||
|
|
||||||
<!-- Status text -->
|
|
||||||
<div id="status-text-container">
|
|
||||||
<p id="status-text"></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="modal-portal"></div>
|
|
||||||
<div id="unclickable" style="display: none">Click on this to upgrade your Source-File -1!</div>
|
|
||||||
<script type="text/javascript" src="dist/vendor.bundle.js"></script>
|
|
||||||
<script type="text/javascript" src="main.bundle.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script src="src/ThirdParty/raphael.min.js"></script>
|
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
9
main.css
9
main.css
@ -2105,13 +2105,14 @@ input[type="checkbox"] {
|
|||||||
.popup-box-content {
|
.popup-box-content {
|
||||||
background-color: var(--my-background-color);
|
background-color: var(--my-background-color);
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: 5px solid var(--my-highlight-color);
|
border: 2px solid #adff2f;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
/* Sit on top of the container */
|
/* Sit on top of the container */
|
||||||
color: var(--my-font-color); }
|
color: var(--my-font-color);
|
||||||
|
box-shadow: 0 3px 5px -1px #090, 0 5px 8px 0 #090, 0 1px 14px 0 #090; }
|
||||||
|
|
||||||
.popup-box-input-div {
|
.popup-box-input-div {
|
||||||
margin: 2px; }
|
margin: 2px; }
|
||||||
@ -2169,12 +2170,12 @@ input[type="checkbox"] {
|
|||||||
max-height: 50%;
|
max-height: 50%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background-color: var(--my-background-color);
|
background-color: var(--my-background-color);
|
||||||
border: 2px solid var(--my-highlight-color); }
|
border: 2px solid #adff2f; }
|
||||||
|
|
||||||
.log-box-header {
|
.log-box-header {
|
||||||
z-index: 1300;
|
z-index: 1300;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
border: 1px solid var(--my-highlight-color);
|
border: 2px solid #adff2f;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: row nowrap;
|
flex: row nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
File diff suppressed because one or more lines are too long
@ -148,6 +148,7 @@
|
|||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"start": "http-server -p 8000",
|
"start": "http-server -p 8000",
|
||||||
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
|
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
|
||||||
|
"start:dev-fast": "webpack-dev-server --progress --env.devServer --mode development --fast true",
|
||||||
"start:container": "webpack-dev-server --progress --env.devServer --mode development --env.runInContainer",
|
"start:container": "webpack-dev-server --progress --env.devServer --mode development --env.runInContainer",
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
"build:dev": "webpack --mode development",
|
"build:dev": "webpack --mode development",
|
||||||
|
@ -2044,6 +2044,28 @@ function initAugmentations(): void {
|
|||||||
}
|
}
|
||||||
AddToAugmentations(SNA);
|
AddToAugmentations(SNA);
|
||||||
|
|
||||||
|
const NeuroreceptorManager = new Augmentation({
|
||||||
|
name: AugmentationNames.NeuroreceptorManager,
|
||||||
|
repCost: 0.75e5,
|
||||||
|
moneyCost: 5.5e8,
|
||||||
|
info:
|
||||||
|
"A brain implant carefully assembled around the synapses, which " +
|
||||||
|
"micromanages the activity and levels of various neuroreceptor " +
|
||||||
|
"chemicals and modulates electrical acvitiy to optimize concentration, " +
|
||||||
|
"allowing the user to multitask much more effectively.",
|
||||||
|
stats: (
|
||||||
|
<>
|
||||||
|
This augmentation removes the penalty for not focusing on actions such as working in a job or working for a
|
||||||
|
faction.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
NeuroreceptorManager.addToFactions(["Tian Di Hui"]);
|
||||||
|
if (augmentationExists(AugmentationNames.NeuroreceptorManager)) {
|
||||||
|
delete Augmentations[AugmentationNames.NeuroreceptorManager];
|
||||||
|
}
|
||||||
|
AddToAugmentations(NeuroreceptorManager);
|
||||||
|
|
||||||
// Special Bladeburner Augmentations
|
// Special Bladeburner Augmentations
|
||||||
const BladeburnersFactionName = "Bladeburners";
|
const BladeburnersFactionName = "Bladeburners";
|
||||||
if (factionExists(BladeburnersFactionName)) {
|
if (factionExists(BladeburnersFactionName)) {
|
||||||
@ -2384,6 +2406,7 @@ function initAugmentations(): void {
|
|||||||
hacknet_node_core_cost_mult: 1.1,
|
hacknet_node_core_cost_mult: 1.1,
|
||||||
hacknet_node_level_cost_mult: 1.1,
|
hacknet_node_level_cost_mult: 1.1,
|
||||||
work_money_mult: 0.9,
|
work_money_mult: 0.9,
|
||||||
|
stats: null,
|
||||||
});
|
});
|
||||||
StaneksGift1.addToFactions([ChurchOfTheMachineGodFactionName]);
|
StaneksGift1.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
resetAugmentation(StaneksGift1);
|
resetAugmentation(StaneksGift1);
|
||||||
@ -2423,6 +2446,7 @@ function initAugmentations(): void {
|
|||||||
hacknet_node_core_cost_mult: 1.05 / 1.1,
|
hacknet_node_core_cost_mult: 1.05 / 1.1,
|
||||||
hacknet_node_level_cost_mult: 1.05 / 1.1,
|
hacknet_node_level_cost_mult: 1.05 / 1.1,
|
||||||
work_money_mult: 0.95 / 0.9,
|
work_money_mult: 0.95 / 0.9,
|
||||||
|
stats: null,
|
||||||
});
|
});
|
||||||
StaneksGift2.addToFactions([ChurchOfTheMachineGodFactionName]);
|
StaneksGift2.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
resetAugmentation(StaneksGift2);
|
resetAugmentation(StaneksGift2);
|
||||||
@ -2462,6 +2486,7 @@ function initAugmentations(): void {
|
|||||||
hacknet_node_core_cost_mult: 1 / 1.05,
|
hacknet_node_core_cost_mult: 1 / 1.05,
|
||||||
hacknet_node_level_cost_mult: 1 / 1.05,
|
hacknet_node_level_cost_mult: 1 / 1.05,
|
||||||
work_money_mult: 1 / 0.95,
|
work_money_mult: 1 / 0.95,
|
||||||
|
stats: null,
|
||||||
});
|
});
|
||||||
StaneksGift3.addToFactions([ChurchOfTheMachineGodFactionName]);
|
StaneksGift3.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
resetAugmentation(StaneksGift3);
|
resetAugmentation(StaneksGift3);
|
||||||
@ -2475,6 +2500,7 @@ function initAugmentations(): void {
|
|||||||
"(hydro notes: Finishes the BN, eventually)",
|
"(hydro notes: Finishes the BN, eventually)",
|
||||||
prereqs: [AugmentationNames.StaneksGift3],
|
prereqs: [AugmentationNames.StaneksGift3],
|
||||||
isSpecial: true,
|
isSpecial: true,
|
||||||
|
stats: null,
|
||||||
});
|
});
|
||||||
StaneksGiftAscension4.addToFactions([ChurchOfTheMachineGodFactionName]);
|
StaneksGiftAscension4.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||||
resetAugmentation(StaneksGiftAscension4);
|
resetAugmentation(StaneksGiftAscension4);
|
||||||
|
@ -41,6 +41,7 @@ export const AugmentationNames: IMap<string> = {
|
|||||||
CranialSignalProcessorsG4: "Cranial Signal Processors - Gen IV",
|
CranialSignalProcessorsG4: "Cranial Signal Processors - Gen IV",
|
||||||
CranialSignalProcessorsG5: "Cranial Signal Processors - Gen V",
|
CranialSignalProcessorsG5: "Cranial Signal Processors - Gen V",
|
||||||
NeuronalDensification: "Neuronal Densification",
|
NeuronalDensification: "Neuronal Densification",
|
||||||
|
NeuroreceptorManager: "Neuroreceptor Management Implant",
|
||||||
NuoptimalInjectorImplant: "Nuoptimal Nootropic Injector Implant",
|
NuoptimalInjectorImplant: "Nuoptimal Nootropic Injector Implant",
|
||||||
SpeechEnhancement: "Speech Enhancement",
|
SpeechEnhancement: "Speech Enhancement",
|
||||||
FocusWire: "FocusWire",
|
FocusWire: "FocusWire",
|
||||||
|
@ -10,6 +10,7 @@ import { PurchasedAugmentations } from "./PurchasedAugmentations";
|
|||||||
import { SourceFiles } from "./SourceFiles";
|
import { SourceFiles } from "./SourceFiles";
|
||||||
|
|
||||||
import { canGetBonus } from "../../ExportBonus";
|
import { canGetBonus } from "../../ExportBonus";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
@ -22,6 +23,7 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function AugmentationsRoot(props: IProps): React.ReactElement {
|
export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
setRerender((o) => !o);
|
setRerender((o) => !o);
|
||||||
@ -69,10 +71,14 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
|||||||
Purchased Augmentations
|
Purchased Augmentations
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box mx={2}>
|
<Box mx={2}>
|
||||||
<Tooltip title={"'I never asked for this'"}>
|
<Tooltip title={<Typography>'I never asked for this'</Typography>}>
|
||||||
<Button onClick={props.installAugmentationsFn}>Install Augmentations</Button>
|
<span>
|
||||||
|
<Button disabled={player.queuedAugmentations.length === 0} onClick={props.installAugmentationsFn}>
|
||||||
|
Install Augmentations
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={"It's always a good idea to backup/export your save!"}>
|
<Tooltip title={<Typography>It's always a good idea to backup/export your save!</Typography>}>
|
||||||
<Button sx={{ mx: 2 }} onClick={doExport} color="error">
|
<Button sx={{ mx: 2 }} onClick={doExport} color="error">
|
||||||
Backup Save {exportBonusStr()}
|
Backup Save {exportBonusStr()}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -396,6 +396,15 @@ BitNodes["BitNode9"] = new BitNode(
|
|||||||
<br />
|
<br />
|
||||||
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
|
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
|
||||||
Augmentations)
|
Augmentations)
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
This Source-File also increases your hacknet multipliers by:
|
||||||
|
<br />
|
||||||
|
Level 1: 8%
|
||||||
|
<br />
|
||||||
|
Level 2: 12%
|
||||||
|
<br />
|
||||||
|
Level 3: 14%
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
33
src/BitNode/ui/BitFlumeModal.tsx
Normal file
33
src/BitNode/ui/BitFlumeModal.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import { EventEmitter } from "../../utils/EventEmitter";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
export const BitFlumeEvent = new EventEmitter<[]>();
|
||||||
|
|
||||||
|
export function BitFlumeModal(): React.ReactElement {
|
||||||
|
const router = use.Router();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
function flume(): void {
|
||||||
|
router.toBitVerse(true, false);
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => BitFlumeEvent.subscribe(() => setOpen(true)), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={open} onClose={() => setOpen(false)}>
|
||||||
|
<Typography>
|
||||||
|
WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode and select a new one.
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<Button onClick={flume}>Travel to the BitVerse</Button>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { IRouter } from "../../ui/Router";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
player: IPlayer;
|
|
||||||
router: IRouter;
|
|
||||||
popupId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BitFlumePopup(props: IProps): React.ReactElement {
|
|
||||||
function flume(): void {
|
|
||||||
props.router.toBitVerse(true, false);
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
WARNING: USING THIS PROGRAM WILL CAUSE YOU TO LOSE ALL OF YOUR PROGRESS ON THE CURRENT BITNODE.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
Do you want to travel to the BitNode Nexus? This allows you to reset the current BitNode and select a new one.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<button className="std-button" onClick={flume}>
|
|
||||||
Travel to the BitVerse
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -3,10 +3,42 @@ import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
|||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
import { BitNodes } from "../BitNode";
|
import { BitNodes } from "../BitNode";
|
||||||
import { enterBitNode, setRedPillFlag } from "../../RedPill";
|
import { enterBitNode, setRedPillFlag } from "../../RedPill";
|
||||||
import { PortalPopup } from "./PortalPopup";
|
import { PortalModal } from "./PortalModal";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { CinematicText } from "../../ui/React/CinematicText";
|
import { CinematicText } from "../../ui/React/CinematicText";
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
|
import makeStyles from "@mui/styles/makeStyles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(() =>
|
||||||
|
createStyles({
|
||||||
|
level0: {
|
||||||
|
color: "red",
|
||||||
|
"&:hover": {
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
level1: {
|
||||||
|
color: "yellow",
|
||||||
|
"&:hover": {
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
level2: {
|
||||||
|
color: "#48d1cc",
|
||||||
|
"&:hover": {
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
level3: {
|
||||||
|
color: "blue",
|
||||||
|
"&:hover": {
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
interface IPortalProps {
|
interface IPortalProps {
|
||||||
n: number;
|
n: number;
|
||||||
@ -16,51 +48,53 @@ interface IPortalProps {
|
|||||||
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
|
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
|
||||||
}
|
}
|
||||||
function BitNodePortal(props: IPortalProps): React.ReactElement {
|
function BitNodePortal(props: IPortalProps): React.ReactElement {
|
||||||
const router = use.Router();
|
const [portalOpen, setPortalOpen] = useState(false);
|
||||||
|
const classes = useStyles();
|
||||||
const bitNode = BitNodes[`BitNode${props.n}`];
|
const bitNode = BitNodes[`BitNode${props.n}`];
|
||||||
if (bitNode == null) {
|
if (bitNode == null) {
|
||||||
return <>O</>;
|
return <>O</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cssClass;
|
let cssClass = classes.level0;
|
||||||
if (props.n === 12 && props.level >= 2) {
|
if (props.n === 12 && props.level >= 2) {
|
||||||
// Repeating BitNode
|
// Repeating BitNode
|
||||||
cssClass = "level-2";
|
cssClass = classes.level2;
|
||||||
} else {
|
} else if (props.level === 1) {
|
||||||
cssClass = `level-${props.level}`;
|
cssClass = classes.level1;
|
||||||
|
} else if (props.level === 3) {
|
||||||
|
cssClass = classes.level3;
|
||||||
}
|
}
|
||||||
|
if (props.level === 2) {
|
||||||
function openPortalPopup(): void {
|
cssClass = classes.level2;
|
||||||
const popupId = "bitverse-portal-popup";
|
|
||||||
createPopup(popupId, PortalPopup, {
|
|
||||||
n: props.n,
|
|
||||||
level: props.level,
|
|
||||||
enter: props.enter,
|
|
||||||
router: router,
|
|
||||||
destroyedBitNode: props.destroyedBitNode,
|
|
||||||
flume: props.flume,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<>
|
||||||
className={`bitnode ${cssClass} tooltip`}
|
<Tooltip
|
||||||
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
|
title={
|
||||||
onClick={openPortalPopup}
|
<Typography>
|
||||||
>
|
<strong>
|
||||||
<strong>O</strong>
|
BitNode-{bitNode.number.toString()}: {bitNode.name}
|
||||||
<span className="tooltiptext">
|
</strong>
|
||||||
<strong>
|
<br />
|
||||||
BitNode-{bitNode.number.toString()}
|
{bitNode.desc}
|
||||||
<br />
|
</Typography>
|
||||||
{bitNode.name}
|
}
|
||||||
</strong>
|
>
|
||||||
<br />
|
<span onClick={() => setPortalOpen(true)} className={cssClass}>
|
||||||
{bitNode.desc}
|
<b>O</b>
|
||||||
<br />
|
</span>
|
||||||
</span>
|
</Tooltip>
|
||||||
</button>
|
<PortalModal
|
||||||
|
open={portalOpen}
|
||||||
|
onClose={() => setPortalOpen(false)}
|
||||||
|
n={props.n}
|
||||||
|
level={props.level}
|
||||||
|
enter={props.enter}
|
||||||
|
destroyedBitNode={props.destroyedBitNode}
|
||||||
|
flume={props.flume}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,32 +149,32 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
<div className="noselect">
|
<>
|
||||||
<pre> O </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
|
||||||
<pre> | O O | O O | </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
|
||||||
<pre> O | | / __| \ | | O </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
|
||||||
<pre> O | O | | O / | O | | O | O </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
|
||||||
<pre> | | | | |_/ |/ | \_ \_| | | | | </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
|
||||||
<pre> O | | | O | | O__/ | / \__ | | O | | | O </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | O | | O__/ | / \__ | | O | | | O </Typography>
|
||||||
<pre> | | | | | | | / /| O / \| | | | | | | </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
|
||||||
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
|
||||||
<pre>| | | |O / | | O / | O O | | \ O| | | |</pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
|
||||||
<pre>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
|
||||||
<pre> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
|
||||||
<pre> | | |_/ | | \| / | \_| | | </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
|
||||||
<pre> \| / \| | / / \ |/ </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
|
||||||
<pre> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | <BitNodePortal n={10} level={nextSourceFileFlags[10]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | / | <BitNodePortal n={11} level={nextSourceFileFlags[11]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | </Typography>
|
||||||
<pre> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={9} level={nextSourceFileFlags[9]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | | | | | | <BitNodePortal n={12} level={nextSourceFileFlags[12]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
|
||||||
<pre> | | | / / \ \ | | | </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
|
||||||
<pre> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| | / <BitNodePortal n={7} level={nextSourceFileFlags[7]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> / \ <BitNodePortal n={8} level={nextSourceFileFlags[8]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \ | |/ </Typography>
|
||||||
<pre> \ | / / | | \ \ | / </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
|
||||||
<pre> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \JUMP <BitNodePortal n={5} level={nextSourceFileFlags[5]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />3R | | | | | | R3<BitNodePortal n={6} level={nextSourceFileFlags[6]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> PMUJ/ / </Typography>
|
||||||
<pre> \|| | | | | | | | | ||/ </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
|
||||||
<pre> \| \_ | | | | | | _/ |/ </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
|
||||||
<pre> \ \| / \ / \ |/ / </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
|
||||||
<pre> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> <BitNodePortal n={1} level={nextSourceFileFlags[1]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> |/ <BitNodePortal n={2} level={nextSourceFileFlags[2]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> | | <BitNodePortal n={3} level={nextSourceFileFlags[3]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \| <BitNodePortal n={4} level={nextSourceFileFlags[4]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> </Typography>
|
||||||
<pre> | | | | | | | | </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
|
||||||
<pre> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </pre>
|
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@ -168,7 +202,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
|||||||
"> ",
|
"> ",
|
||||||
"> (Enter a new BitNode using the image above)",
|
"> (Enter a new BitNode using the image above)",
|
||||||
]} />
|
]} />
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
|
@ -2,18 +2,23 @@ import React from "react";
|
|||||||
|
|
||||||
import { BitNodes } from "../BitNode";
|
import { BitNodes } from "../BitNode";
|
||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
import { use } from "../../ui/Context";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
n: number;
|
n: number;
|
||||||
level: number;
|
level: number;
|
||||||
destroyedBitNode: number;
|
destroyedBitNode: number;
|
||||||
flume: boolean;
|
flume: boolean;
|
||||||
router: IRouter;
|
|
||||||
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
|
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
|
||||||
popupId: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PortalPopup(props: IProps): React.ReactElement {
|
export function PortalModal(props: IProps): React.ReactElement {
|
||||||
|
const router = use.Router();
|
||||||
const bitNodeKey = "BitNode" + props.n;
|
const bitNodeKey = "BitNode" + props.n;
|
||||||
const bitNode = BitNodes[bitNodeKey];
|
const bitNode = BitNodes[bitNodeKey];
|
||||||
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
|
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
|
||||||
@ -21,29 +26,30 @@ export function PortalPopup(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3);
|
const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3);
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<h1>
|
<Typography variant="h4">
|
||||||
BitNode-{props.n}: {bitNode.name}
|
BitNode-{props.n}: {bitNode.name}
|
||||||
</h1>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
Source-File Level: {props.level} / {maxSourceFileLevel}
|
<Typography>
|
||||||
|
Source-File Level: {props.level} / {maxSourceFileLevel}
|
||||||
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}
|
<Typography> Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
{bitNode.info}
|
<Typography>{bitNode.info}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<button
|
<Button
|
||||||
className="std-button"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.enter(props.router, props.flume, props.destroyedBitNode, props.n);
|
props.enter(router, props.flume, props.destroyedBitNode, props.n);
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Enter BN{props.n}.{newLevel}
|
Enter BN{props.n}.{newLevel}
|
||||||
</button>
|
</Button>
|
||||||
</>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -20,7 +20,6 @@ class StatsMultiplier {
|
|||||||
|
|
||||||
export interface IActionParams {
|
export interface IActionParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
desc?: string;
|
|
||||||
level?: number;
|
level?: number;
|
||||||
maxLevel?: number;
|
maxLevel?: number;
|
||||||
autoLevel?: boolean;
|
autoLevel?: boolean;
|
||||||
@ -43,7 +42,6 @@ export interface IActionParams {
|
|||||||
|
|
||||||
export class Action implements IAction {
|
export class Action implements IAction {
|
||||||
name = "";
|
name = "";
|
||||||
desc = "";
|
|
||||||
|
|
||||||
// Difficulty scales with level. See getDifficulty() method
|
// Difficulty scales with level. See getDifficulty() method
|
||||||
level = 1;
|
level = 1;
|
||||||
@ -100,7 +98,6 @@ export class Action implements IAction {
|
|||||||
constructor(params: IActionParams | null = null) {
|
constructor(params: IActionParams | null = null) {
|
||||||
// | null = null
|
// | null = null
|
||||||
if (params && params.name) this.name = params.name;
|
if (params && params.name) this.name = params.name;
|
||||||
if (params && params.desc) this.desc = params.desc;
|
|
||||||
|
|
||||||
if (params && params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
if (params && params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
||||||
if (params && params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
if (params && params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
@ -1,763 +0,0 @@
|
|||||||
import { BlackOperation } from "./BlackOperation";
|
|
||||||
import { IMap } from "../types";
|
|
||||||
|
|
||||||
export const BlackOperations: IMap<BlackOperation> = {};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
BlackOperations["Operation Typhoon"] = new BlackOperation({
|
|
||||||
name: "Operation Typhoon",
|
|
||||||
desc:
|
|
||||||
"Obadiah Zenyatta is the leader of a RedWater PMC. It has long " +
|
|
||||||
"been known among the intelligence community that Zenyatta, along " +
|
|
||||||
"with the rest of the PMC, is a Synthoid.<br><br>" +
|
|
||||||
"The goal of Operation Typhoon is to find and eliminate " +
|
|
||||||
"Zenyatta and RedWater by any means necessary. After the task " +
|
|
||||||
"is completed, the actions must be covered up from the general public.",
|
|
||||||
baseDifficulty: 2000,
|
|
||||||
reqdRank: 2.5e3,
|
|
||||||
rankGain: 50,
|
|
||||||
rankLoss: 10,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Zero"] = new BlackOperation({
|
|
||||||
name: "Operation Zero",
|
|
||||||
desc:
|
|
||||||
"AeroCorp is one of the world's largest defense contractors. " +
|
|
||||||
"Its leader, Steve Watataki, is thought to be a supporter of " +
|
|
||||||
"Synthoid rights. He must be removed.<br><br>" +
|
|
||||||
"The goal of Operation Zero is to covertly infiltrate AeroCorp and " +
|
|
||||||
"uncover any incriminating evidence or " +
|
|
||||||
"information against Watataki that will cause him to be removed " +
|
|
||||||
"from his position at AeroCorp. Incriminating evidence can be " +
|
|
||||||
"fabricated as a last resort. Be warned that AeroCorp has some of " +
|
|
||||||
"the most advanced security measures in the world.",
|
|
||||||
baseDifficulty: 2500,
|
|
||||||
reqdRank: 5e3,
|
|
||||||
rankGain: 60,
|
|
||||||
rankLoss: 15,
|
|
||||||
hpLoss: 50,
|
|
||||||
weights: {
|
|
||||||
hack: 0.2,
|
|
||||||
str: 0.15,
|
|
||||||
def: 0.15,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isStealth: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation X"] = new BlackOperation({
|
|
||||||
name: "Operation X",
|
|
||||||
desc:
|
|
||||||
"We have recently discovered an underground publication " +
|
|
||||||
"group called Samizdat. Even though most of their publications " +
|
|
||||||
"are nonsensical conspiracy theories, the average human is " +
|
|
||||||
"gullible enough to believe them. Many of their works discuss " +
|
|
||||||
"Synthoids and pose a threat to society. The publications are spreading " +
|
|
||||||
"rapidly in China and other Eastern countries.<br><br>" +
|
|
||||||
"Samizdat has done a good job of keeping hidden and anonymous. " +
|
|
||||||
"However, we've just received intelligence that their base of " +
|
|
||||||
"operations is in Ishima's underground sewer systems. Your task is to " +
|
|
||||||
"investigate the sewer systems, and eliminate Samizdat. They must " +
|
|
||||||
"never publish anything again.",
|
|
||||||
baseDifficulty: 3000,
|
|
||||||
reqdRank: 7.5e3,
|
|
||||||
rankGain: 75,
|
|
||||||
rankLoss: 15,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Titan"] = new BlackOperation({
|
|
||||||
name: "Operation Titan",
|
|
||||||
desc:
|
|
||||||
"Several months ago Titan Laboratories' Bioengineering department " +
|
|
||||||
"was infiltrated by Synthoids. As far as we know, Titan Laboratories' " +
|
|
||||||
"management has no knowledge about this. We don't know what the " +
|
|
||||||
"Synthoids are up to, but the research that they could " +
|
|
||||||
"be conducting using Titan Laboraties' vast resources is potentially " +
|
|
||||||
"very dangerous.<br><br>" +
|
|
||||||
"Your goal is to enter and destroy the Bioengineering department's " +
|
|
||||||
"facility in Aevum. The task is not just to retire the Synthoids there, but " +
|
|
||||||
"also to destroy any information or research at the facility that " +
|
|
||||||
"is relevant to the Synthoids and their goals.",
|
|
||||||
baseDifficulty: 4000,
|
|
||||||
reqdRank: 10e3,
|
|
||||||
rankGain: 100,
|
|
||||||
rankLoss: 20,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Ares"] = new BlackOperation({
|
|
||||||
name: "Operation Ares",
|
|
||||||
desc:
|
|
||||||
"One of our undercover agents, Agent Carter, has informed us of a " +
|
|
||||||
"massive weapons deal going down in Dubai between rogue Russian " +
|
|
||||||
"militants and a radical Synthoid community. These weapons are next-gen " +
|
|
||||||
"plasma and energy weapons. It is critical for the safety of humanity " +
|
|
||||||
"that this deal does not happen.<br><br>" +
|
|
||||||
"Your task is to intercept the deal. Leave no survivors.",
|
|
||||||
baseDifficulty: 5000,
|
|
||||||
reqdRank: 12.5e3,
|
|
||||||
rankGain: 125,
|
|
||||||
rankLoss: 20,
|
|
||||||
hpLoss: 200,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.25,
|
|
||||||
def: 0.25,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Archangel"] = new BlackOperation({
|
|
||||||
name: "Operation Archangel",
|
|
||||||
desc:
|
|
||||||
"Our analysts have discovered that the popular Red Rabbit brothel in " +
|
|
||||||
"Amsterdam is run and 'staffed' by MK-VI Synthoids. Intelligence " +
|
|
||||||
"suggests that the profit from this brothel is used to fund a large " +
|
|
||||||
"black market arms trafficking operation.<br><br>" +
|
|
||||||
"The goal of this operation is to take out the leaders that are running " +
|
|
||||||
"the Red Rabbit brothel. Try to limit the number of other casualties, " +
|
|
||||||
"but do what you must to complete the mission.",
|
|
||||||
baseDifficulty: 7500,
|
|
||||||
reqdRank: 15e3,
|
|
||||||
rankGain: 200,
|
|
||||||
rankLoss: 20,
|
|
||||||
hpLoss: 25,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.3,
|
|
||||||
agi: 0.3,
|
|
||||||
cha: 0,
|
|
||||||
int: 0,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Juggernaut"] = new BlackOperation({
|
|
||||||
name: "Operation Juggernaut",
|
|
||||||
desc:
|
|
||||||
"The CIA has just encountered a new security threat. A new " +
|
|
||||||
"criminal group, lead by a shadowy operative who calls himself " +
|
|
||||||
"Juggernaut, has been smuggling drugs and weapons (including " +
|
|
||||||
"suspected bioweapons) into Sector-12. We also have reason " +
|
|
||||||
"to believe the tried to break into one of Universal Energy's " +
|
|
||||||
"facilities in order to cause a city-wide blackout. The CIA " +
|
|
||||||
"suspects that Juggernaut is a heavily-augmented Synthoid, and " +
|
|
||||||
"have thus enlisted our help.<br><br>" +
|
|
||||||
"Your mission is to eradicate Juggernaut and his followers.",
|
|
||||||
baseDifficulty: 10e3,
|
|
||||||
reqdRank: 20e3,
|
|
||||||
rankGain: 300,
|
|
||||||
rankLoss: 40,
|
|
||||||
hpLoss: 300,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.25,
|
|
||||||
def: 0.25,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Red Dragon"] = new BlackOperation({
|
|
||||||
name: "Operation Red Dragon",
|
|
||||||
desc:
|
|
||||||
"The Tetrads criminal organization is suspected of " +
|
|
||||||
"reverse-engineering the MK-VI Synthoid design. We believe " +
|
|
||||||
"they altered and possibly improved the design and began " +
|
|
||||||
"manufacturing their own Synthoid models in order to bolster " +
|
|
||||||
"their criminal activities.<br><br>" +
|
|
||||||
"Your task is to infiltrate and destroy the Tetrads' base of operations " +
|
|
||||||
"in Los Angeles. Intelligence tells us that their base houses " +
|
|
||||||
"one of their Synthoid manufacturing units.",
|
|
||||||
baseDifficulty: 12.5e3,
|
|
||||||
reqdRank: 25e3,
|
|
||||||
rankGain: 500,
|
|
||||||
rankLoss: 50,
|
|
||||||
hpLoss: 500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.05,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.05,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation K"] = new BlackOperation({
|
|
||||||
name: "Operation K",
|
|
||||||
desc:
|
|
||||||
"CODE RED SITUATION. Our intelligence tells us that VitaLife " +
|
|
||||||
"has discovered a new android cloning technology. This technology " +
|
|
||||||
"is supposedly capable of cloning Synthoid, not only physically " +
|
|
||||||
"but also their advanced AI modules. We do not believe that " +
|
|
||||||
"VitaLife is trying to use this technology illegally or " +
|
|
||||||
"maliciously, but if any Synthoids were able to infiltrate the " +
|
|
||||||
"corporation and take advantage of this technology then the " +
|
|
||||||
"results would be catastrophic.<br><br>" +
|
|
||||||
"We do not have the power or jurisdiction to shutdown this down " +
|
|
||||||
"through legal or political means, so we must resort to a covert " +
|
|
||||||
"operation. Your goal is to destroy this technology and eliminate " +
|
|
||||||
"anyone who was involved in its creation.",
|
|
||||||
baseDifficulty: 15e3,
|
|
||||||
reqdRank: 30e3,
|
|
||||||
rankGain: 750,
|
|
||||||
rankLoss: 60,
|
|
||||||
hpLoss: 1000,
|
|
||||||
weights: {
|
|
||||||
hack: 0.05,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.25,
|
|
||||||
agi: 0.25,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.05,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Deckard"] = new BlackOperation({
|
|
||||||
name: "Operation Deckard",
|
|
||||||
desc:
|
|
||||||
"Despite your success in eliminating VitaLife's new android-replicating " +
|
|
||||||
"technology in Operation K, we've discovered that a small group of " +
|
|
||||||
"MK-VI Synthoids were able to make off with the schematics and design " +
|
|
||||||
"of the technology before the Operation. It is almost a certainty that " +
|
|
||||||
"these Synthoids are some of the rogue MK-VI ones from the Synthoid Uprising.<br><br>" +
|
|
||||||
"The goal of Operation Deckard is to hunt down these Synthoids and retire " +
|
|
||||||
"them. I don't need to tell you how critical this mission is.",
|
|
||||||
baseDifficulty: 20e3,
|
|
||||||
reqdRank: 40e3,
|
|
||||||
rankGain: 1e3,
|
|
||||||
rankLoss: 75,
|
|
||||||
hpLoss: 200,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Tyrell"] = new BlackOperation({
|
|
||||||
name: "Operation Tyrell",
|
|
||||||
desc:
|
|
||||||
"A week ago Blade Industries reported a small break-in at one " +
|
|
||||||
"of their Aevum Augmentation storage facitilities. We figured out " +
|
|
||||||
"that The Dark Army was behind the heist, and didn't think any more " +
|
|
||||||
"of it. However, we've just discovered that several known MK-VI Synthoids " +
|
|
||||||
"were part of that break-in group.<br><br>" +
|
|
||||||
"We cannot have Synthoids upgrading their already-enhanced abilities " +
|
|
||||||
"with Augmentations. Your task is to hunt down the associated Dark Army " +
|
|
||||||
"members and eliminate them.",
|
|
||||||
baseDifficulty: 25e3,
|
|
||||||
reqdRank: 50e3,
|
|
||||||
rankGain: 1.5e3,
|
|
||||||
rankLoss: 100,
|
|
||||||
hpLoss: 500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Wallace"] = new BlackOperation({
|
|
||||||
name: "Operation Wallace",
|
|
||||||
desc:
|
|
||||||
"Based on information gathered from Operation Tyrell, we've discovered " +
|
|
||||||
"that The Dark Army was well aware that there were Synthoids amongst " +
|
|
||||||
"their ranks. Even worse, we believe that The Dark Army is working " +
|
|
||||||
"together with other criminal organizations such as The Syndicate and " +
|
|
||||||
"that they are planning some sort of large-scale takeover of multiple major " +
|
|
||||||
"cities, most notably Aevum. We suspect that Synthoids have infiltrated " +
|
|
||||||
"the ranks of these criminal factions and are trying to stage another " +
|
|
||||||
"Synthoid uprising.<br><br>" +
|
|
||||||
"The best way to deal with this is to prevent it before it even happens. " +
|
|
||||||
"The goal of Operation Wallace is to destroy the Dark Army and " +
|
|
||||||
"Syndicate factions in Aevum immediately. Leave no survivors.",
|
|
||||||
baseDifficulty: 30e3,
|
|
||||||
reqdRank: 75e3,
|
|
||||||
rankGain: 2e3,
|
|
||||||
rankLoss: 150,
|
|
||||||
hpLoss: 1500,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Shoulder of Orion"] = new BlackOperation({
|
|
||||||
name: "Operation Shoulder of Orion",
|
|
||||||
desc:
|
|
||||||
"China's Solaris Space Systems is secretly launching the first " +
|
|
||||||
"manned spacecraft in over a decade using Synthoids. We believe " +
|
|
||||||
"China is trying to establish the first off-world colonies.<br><br>" +
|
|
||||||
"The mission is to prevent this launch without instigating an " +
|
|
||||||
"international conflict. When you accept this mission you will be " +
|
|
||||||
"officially disavowed by the NSA and the national government until after you " +
|
|
||||||
"successfully return. In the event of failure, all of the operation's " +
|
|
||||||
"team members must not let themselves be captured alive.",
|
|
||||||
baseDifficulty: 35e3,
|
|
||||||
reqdRank: 100e3,
|
|
||||||
rankGain: 2.5e3,
|
|
||||||
rankLoss: 500,
|
|
||||||
hpLoss: 1500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isStealth: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Hyron"] = new BlackOperation({
|
|
||||||
name: "Operation Hyron",
|
|
||||||
desc:
|
|
||||||
"Our intelligence tells us that Fulcrum Technologies is developing " +
|
|
||||||
"a quantum supercomputer using human brains as core " +
|
|
||||||
"processors. This supercomputer " +
|
|
||||||
"is rumored to be able to store vast amounts of data and " +
|
|
||||||
"perform computations unmatched by any other supercomputer on the " +
|
|
||||||
"planet. But more importantly, the use of organic human brains " +
|
|
||||||
"means that the supercomputer may be able to reason abstractly " +
|
|
||||||
"and become self-aware.<br><br>" +
|
|
||||||
"I do not need to remind you why sentient-level AIs pose a serious " +
|
|
||||||
"threat to all of mankind.<br><br>" +
|
|
||||||
"The research for this project is being conducted at one of Fulcrum " +
|
|
||||||
"Technologies secret facilities in Aevum, codenamed 'Alpha Ranch'. " +
|
|
||||||
"Infiltrate the compound, delete and destroy the work, and then find and kill the " +
|
|
||||||
"project lead.",
|
|
||||||
baseDifficulty: 40e3,
|
|
||||||
reqdRank: 125e3,
|
|
||||||
rankGain: 3e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 500,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Morpheus"] = new BlackOperation({
|
|
||||||
name: "Operation Morpheus",
|
|
||||||
desc:
|
|
||||||
"DreamSense Technologies is an advertising company that uses " +
|
|
||||||
"special technology to transmit their ads into the peoples " +
|
|
||||||
"dreams and subconcious. They do this using broadcast transmitter " +
|
|
||||||
"towers. Based on information from our agents and informants in " +
|
|
||||||
"Chonqging, we have reason to believe that one of the broadcast " +
|
|
||||||
"towers there has been compromised by Synthoids and is being used " +
|
|
||||||
"to spread pro-Synthoid propaganda.<br><br>" +
|
|
||||||
"The mission is to destroy this broadcast tower. Speed and " +
|
|
||||||
"stealth are of the upmost important for this.",
|
|
||||||
baseDifficulty: 45e3,
|
|
||||||
reqdRank: 150e3,
|
|
||||||
rankGain: 4e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 100,
|
|
||||||
weights: {
|
|
||||||
hack: 0.05,
|
|
||||||
str: 0.15,
|
|
||||||
def: 0.15,
|
|
||||||
dex: 0.3,
|
|
||||||
agi: 0.3,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.05,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isStealth: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Ion Storm"] = new BlackOperation({
|
|
||||||
name: "Operation Ion Storm",
|
|
||||||
desc:
|
|
||||||
"Our analysts have uncovered a gathering of MK-VI Synthoids " +
|
|
||||||
"that have taken up residence in the Sector-12 Slums. We " +
|
|
||||||
"don't know if they are rogue Synthoids from the Uprising, " +
|
|
||||||
"but we do know that they have been stockpiling " +
|
|
||||||
"weapons, money, and other resources. This makes them dangerous.<br><br>" +
|
|
||||||
"This is a full-scale assault operation to find and retire all of these " +
|
|
||||||
"Synthoids in the Sector-12 Slums.",
|
|
||||||
baseDifficulty: 50e3,
|
|
||||||
reqdRank: 175e3,
|
|
||||||
rankGain: 5e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 5000,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Annihilus"] = new BlackOperation({
|
|
||||||
name: "Operation Annihilus",
|
|
||||||
desc:
|
|
||||||
"Our superiors have ordered us to eradicate everything and everyone " +
|
|
||||||
"in an underground facility located in Aevum. They tell us " +
|
|
||||||
"that the facility houses many dangerous Synthoids and " +
|
|
||||||
"belongs to a terrorist organization called " +
|
|
||||||
"'The Covenant'. We have no prior intelligence about this " +
|
|
||||||
"organization, so you are going in blind.",
|
|
||||||
baseDifficulty: 55e3,
|
|
||||||
reqdRank: 200e3,
|
|
||||||
rankGain: 7.5e3,
|
|
||||||
rankLoss: 1e3,
|
|
||||||
hpLoss: 10e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0,
|
|
||||||
str: 0.24,
|
|
||||||
def: 0.24,
|
|
||||||
dex: 0.24,
|
|
||||||
agi: 0.24,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.04,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Ultron"] = new BlackOperation({
|
|
||||||
name: "Operation Ultron",
|
|
||||||
desc:
|
|
||||||
"OmniTek Incorporated, the original designer and manufacturer of Synthoids, " +
|
|
||||||
"has notified us of a malfunction in their AI design. This malfunction, " +
|
|
||||||
"when triggered, causes MK-VI Synthoids to become radicalized and seek out " +
|
|
||||||
"the destruction of humanity. They say that this bug affects all MK-VI Synthoids, " +
|
|
||||||
"not just the rogue ones from the Uprising.<br><br>" +
|
|
||||||
"OmniTek has also told us they they believe someone has triggered this " +
|
|
||||||
"malfunction in a large group of MK-VI Synthoids, and that these newly-radicalized Synthoids " +
|
|
||||||
"are now amassing in Volhaven to form a terrorist group called Ultron.<br><br>" +
|
|
||||||
"Intelligence suggests Ultron is heavily armed and that their members are " +
|
|
||||||
"augmented. We believe Ultron is making moves to take control of " +
|
|
||||||
"and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).<br><br>" +
|
|
||||||
"Your task is to find and destroy Ultron.",
|
|
||||||
baseDifficulty: 60e3,
|
|
||||||
reqdRank: 250e3,
|
|
||||||
rankGain: 10e3,
|
|
||||||
rankLoss: 2e3,
|
|
||||||
hpLoss: 10e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
isKill: true,
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Centurion"] = new BlackOperation({
|
|
||||||
name: "Operation Centurion",
|
|
||||||
desc:
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)<br><br>" +
|
|
||||||
"Throughout all of humanity's history, we have relied on " +
|
|
||||||
"technology to survive, conquer, and progress. Its advancement became our primary goal. " +
|
|
||||||
"And at the peak of human civilization technology turned into " +
|
|
||||||
"power. Global, absolute power.<br><br>" +
|
|
||||||
"It seems that the universe is not without a sense of irony.<br><br>" +
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
|
||||||
baseDifficulty: 70e3,
|
|
||||||
reqdRank: 300e3,
|
|
||||||
rankGain: 15e3,
|
|
||||||
rankLoss: 5e3,
|
|
||||||
hpLoss: 10e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Vindictus"] = new BlackOperation({
|
|
||||||
name: "Operation Vindictus",
|
|
||||||
desc:
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)<br><br>" +
|
|
||||||
"The bits are all around us. The daemons that hold the Node " +
|
|
||||||
"together can manifest themselves in many different ways.<br><br>" +
|
|
||||||
"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)",
|
|
||||||
baseDifficulty: 75e3,
|
|
||||||
reqdRank: 350e3,
|
|
||||||
rankGain: 20e3,
|
|
||||||
rankLoss: 20e3,
|
|
||||||
hpLoss: 20e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
BlackOperations["Operation Daedalus"] = new BlackOperation({
|
|
||||||
name: "Operation Daedalus",
|
|
||||||
desc: "Yesterday we obeyed kings and bent our neck to emperors. " + "Today we kneel only to truth.",
|
|
||||||
baseDifficulty: 80e3,
|
|
||||||
reqdRank: 400e3,
|
|
||||||
rankGain: 40e3,
|
|
||||||
rankLoss: 10e3,
|
|
||||||
hpLoss: 100e3,
|
|
||||||
weights: {
|
|
||||||
hack: 0.1,
|
|
||||||
str: 0.2,
|
|
||||||
def: 0.2,
|
|
||||||
dex: 0.2,
|
|
||||||
agi: 0.2,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.1,
|
|
||||||
},
|
|
||||||
decays: {
|
|
||||||
hack: 0.6,
|
|
||||||
str: 0.8,
|
|
||||||
def: 0.8,
|
|
||||||
dex: 0.8,
|
|
||||||
agi: 0.8,
|
|
||||||
cha: 0,
|
|
||||||
int: 0.75,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
571
src/Bladeburner/BlackOperations.tsx
Normal file
571
src/Bladeburner/BlackOperations.tsx
Normal file
@ -0,0 +1,571 @@
|
|||||||
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
@ -1586,11 +1586,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
create(): void {
|
create(): void {
|
||||||
this.contracts["Tracking"] = new Contract({
|
this.contracts["Tracking"] = new Contract({
|
||||||
name: "Tracking",
|
name: "Tracking",
|
||||||
desc:
|
|
||||||
"Identify and locate Synthoids. This contract involves reconnaissance " +
|
|
||||||
"and information-gathering ONLY. Do NOT engage. Stealth is of the utmost importance.<br><br>" +
|
|
||||||
"Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for " +
|
|
||||||
"whatever city you are currently in.",
|
|
||||||
baseDifficulty: 125,
|
baseDifficulty: 125,
|
||||||
difficultyFac: 1.02,
|
difficultyFac: 1.02,
|
||||||
rewardFac: 1.041,
|
rewardFac: 1.041,
|
||||||
@ -1619,10 +1614,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.contracts["Bounty Hunter"] = new Contract({
|
this.contracts["Bounty Hunter"] = new Contract({
|
||||||
name: "Bounty Hunter",
|
name: "Bounty Hunter",
|
||||||
desc:
|
|
||||||
"Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.<br><br>" +
|
|
||||||
"Successfully completing a Bounty Hunter contract will lower the population in your " +
|
|
||||||
"current city, and will also increase its chaos level.",
|
|
||||||
baseDifficulty: 250,
|
baseDifficulty: 250,
|
||||||
difficultyFac: 1.04,
|
difficultyFac: 1.04,
|
||||||
rewardFac: 1.085,
|
rewardFac: 1.085,
|
||||||
@ -1651,10 +1642,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.contracts["Retirement"] = new Contract({
|
this.contracts["Retirement"] = new Contract({
|
||||||
name: "Retirement",
|
name: "Retirement",
|
||||||
desc:
|
|
||||||
"Hunt down and retire (kill) rogue Synthoids.<br><br>" +
|
|
||||||
"Successfully completing a Retirement contract will lower the population in your current " +
|
|
||||||
"city, and will also increase its chaos level.",
|
|
||||||
baseDifficulty: 200,
|
baseDifficulty: 200,
|
||||||
difficultyFac: 1.03,
|
difficultyFac: 1.03,
|
||||||
rewardFac: 1.065,
|
rewardFac: 1.065,
|
||||||
@ -1684,12 +1671,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
|
|
||||||
this.operations["Investigation"] = new Operation({
|
this.operations["Investigation"] = new Operation({
|
||||||
name: "Investigation",
|
name: "Investigation",
|
||||||
desc:
|
|
||||||
"As a field agent, investigate and identify Synthoid " +
|
|
||||||
"populations, movements, and operations.<br><br>Successful " +
|
|
||||||
"Investigation ops will increase the accuracy of your " +
|
|
||||||
"synthoid data.<br><br>" +
|
|
||||||
"You will NOT lose HP from failed Investigation ops.",
|
|
||||||
baseDifficulty: 400,
|
baseDifficulty: 400,
|
||||||
difficultyFac: 1.03,
|
difficultyFac: 1.03,
|
||||||
rewardFac: 1.07,
|
rewardFac: 1.07,
|
||||||
@ -1719,11 +1700,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Undercover Operation"] = new Operation({
|
this.operations["Undercover Operation"] = new Operation({
|
||||||
name: "Undercover Operation",
|
name: "Undercover Operation",
|
||||||
desc:
|
|
||||||
"Conduct undercover operations to identify hidden " +
|
|
||||||
"and underground Synthoid communities and organizations.<br><br>" +
|
|
||||||
"Successful Undercover ops will increase the accuracy of your synthoid " +
|
|
||||||
"data.",
|
|
||||||
baseDifficulty: 500,
|
baseDifficulty: 500,
|
||||||
difficultyFac: 1.04,
|
difficultyFac: 1.04,
|
||||||
rewardFac: 1.09,
|
rewardFac: 1.09,
|
||||||
@ -1754,7 +1730,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Sting Operation"] = new Operation({
|
this.operations["Sting Operation"] = new Operation({
|
||||||
name: "Sting Operation",
|
name: "Sting Operation",
|
||||||
desc: "Conduct a sting operation to bait and capture particularly " + "notorious Synthoid criminals.",
|
|
||||||
baseDifficulty: 650,
|
baseDifficulty: 650,
|
||||||
difficultyFac: 1.04,
|
difficultyFac: 1.04,
|
||||||
rewardFac: 1.095,
|
rewardFac: 1.095,
|
||||||
@ -1785,10 +1760,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Raid"] = new Operation({
|
this.operations["Raid"] = new Operation({
|
||||||
name: "Raid",
|
name: "Raid",
|
||||||
desc:
|
|
||||||
"Lead an assault on a known Synthoid community. Note that " +
|
|
||||||
"there must be an existing Synthoid community in your current city " +
|
|
||||||
"in order for this Operation to be successful.",
|
|
||||||
baseDifficulty: 800,
|
baseDifficulty: 800,
|
||||||
difficultyFac: 1.045,
|
difficultyFac: 1.045,
|
||||||
rewardFac: 1.1,
|
rewardFac: 1.1,
|
||||||
@ -1819,10 +1790,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Stealth Retirement Operation"] = new Operation({
|
this.operations["Stealth Retirement Operation"] = new Operation({
|
||||||
name: "Stealth Retirement Operation",
|
name: "Stealth Retirement Operation",
|
||||||
desc:
|
|
||||||
"Lead a covert operation to retire Synthoids. The " +
|
|
||||||
"objective is to complete the task without " +
|
|
||||||
"drawing any attention. Stealth and discretion are key.",
|
|
||||||
baseDifficulty: 1000,
|
baseDifficulty: 1000,
|
||||||
difficultyFac: 1.05,
|
difficultyFac: 1.05,
|
||||||
rewardFac: 1.11,
|
rewardFac: 1.11,
|
||||||
@ -1854,10 +1821,6 @@ export class Bladeburner implements IBladeburner {
|
|||||||
});
|
});
|
||||||
this.operations["Assassination"] = new Operation({
|
this.operations["Assassination"] = new Operation({
|
||||||
name: "Assassination",
|
name: "Assassination",
|
||||||
desc:
|
|
||||||
"Assassinate Synthoids that have been identified as " +
|
|
||||||
"important, high-profile social and political leaders " +
|
|
||||||
"in the Synthoid communities.",
|
|
||||||
baseDifficulty: 1500,
|
baseDifficulty: 1500,
|
||||||
difficultyFac: 1.06,
|
difficultyFac: 1.06,
|
||||||
rewardFac: 1.14,
|
rewardFac: 1.14,
|
||||||
@ -1900,7 +1863,7 @@ export class Bladeburner implements IBladeburner {
|
|||||||
if (this.action.type !== ActionTypes["Idle"]) {
|
if (this.action.type !== ActionTypes["Idle"]) {
|
||||||
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
|
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
|
||||||
if (this.automateEnabled) {
|
if (this.automateEnabled) {
|
||||||
msg += `<br><br>Your automation was disabled as well. You will have to re-enable it through the Bladeburner console`;
|
msg += `<br /><br />Your automation was disabled as well. You will have to re-enable it through the Bladeburner console`;
|
||||||
this.automateEnabled = false;
|
this.automateEnabled = false;
|
||||||
}
|
}
|
||||||
if (!Settings.SuppressBladeburnerPopup) {
|
if (!Settings.SuppressBladeburnerPopup) {
|
@ -1,54 +0,0 @@
|
|||||||
import { Action } from "./Action";
|
|
||||||
import { IMap } from "../types";
|
|
||||||
|
|
||||||
export const GeneralActions: IMap<Action> = {};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
// General Actions
|
|
||||||
let actionName;
|
|
||||||
actionName = "Training";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Improve your abilities at the Bladeburner unit's specialized training " +
|
|
||||||
"center. Doing this gives experience for all combat stats and also " +
|
|
||||||
"increases your max stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Field Analysis";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Mine and analyze Synthoid-related data. This improves the " +
|
|
||||||
"Bladeburner's unit intelligence on Synthoid locations and " +
|
|
||||||
"activities. Completing this action will improve the accuracy " +
|
|
||||||
"of your Synthoid population estimated in the current city.<br><br>" +
|
|
||||||
"Does NOT require stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Recruitment";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Attempt to recruit members for your Bladeburner team. These members " +
|
|
||||||
"can help you conduct operations.<br><br>" +
|
|
||||||
"Does NOT require stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Diplomacy";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Improve diplomatic relations with the Synthoid population. " +
|
|
||||||
"Completing this action will reduce the Chaos level in your current city.<br><br>" +
|
|
||||||
"Does NOT require stamina.",
|
|
||||||
});
|
|
||||||
|
|
||||||
actionName = "Hyperbolic Regeneration Chamber";
|
|
||||||
GeneralActions[actionName] = new Action({
|
|
||||||
name: actionName,
|
|
||||||
desc:
|
|
||||||
"Enter cryogenic stasis using the Bladeburner division's hi-tech Regeneration Chamber. " +
|
|
||||||
"This will slowly heal your wounds and slightly increase your stamina.<br><br>",
|
|
||||||
});
|
|
||||||
})();
|
|
33
src/Bladeburner/GeneralActions.tsx
Normal file
33
src/Bladeburner/GeneralActions.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Action } from "./Action";
|
||||||
|
import { IMap } from "../types";
|
||||||
|
|
||||||
|
export const GeneralActions: IMap<Action> = {};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
// General Actions
|
||||||
|
let actionName;
|
||||||
|
actionName = "Training";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Field Analysis";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Recruitment";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Diplomacy";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
|
||||||
|
actionName = "Hyperbolic Regeneration Chamber";
|
||||||
|
GeneralActions[actionName] = new Action({
|
||||||
|
name: actionName,
|
||||||
|
});
|
||||||
|
})();
|
@ -18,7 +18,6 @@ export interface ISuccessChanceParams {
|
|||||||
|
|
||||||
export interface IAction {
|
export interface IAction {
|
||||||
name: string;
|
name: string;
|
||||||
desc: string;
|
|
||||||
|
|
||||||
// Difficulty scales with level. See getDifficulty() method
|
// Difficulty scales with level. See getDifficulty() method
|
||||||
level: number;
|
level: number;
|
299
src/Bladeburner/data/BlackOperations.tsx
Normal file
299
src/Bladeburner/data/BlackOperations.tsx
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IBlackOp {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlackOperations: {
|
||||||
|
[key: string]: IBlackOp | undefined;
|
||||||
|
} = {
|
||||||
|
"Operation Typhoon": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Obadiah Zenyatta is the leader of a RedWater PMC. It has long been known among the intelligence community that
|
||||||
|
Zenyatta, along with the rest of the PMC, is a Synthoid.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of Operation Typhoon is to find and eliminate Zenyatta and RedWater by any means necessary. After the
|
||||||
|
task is completed, the actions must be covered up from the general public.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
"Operation Zero": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
AeroCorp is one of the world's largest defense contractors. Its leader, Steve Watataki, is thought to be a
|
||||||
|
supporter of Synthoid rights. He must be removed.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of Operation Zero is to covertly infiltrate AeroCorp and uncover any incriminating evidence or
|
||||||
|
information against Watataki that will cause him to be removed from his position at AeroCorp. Incriminating
|
||||||
|
evidence can be fabricated as a last resort. Be warned that AeroCorp has some of the most advanced security
|
||||||
|
measures in the world.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation X": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
We have recently discovered an underground publication group called Samizdat. Even though most of their
|
||||||
|
publications are nonsensical conspiracy theories, the average human is gullible enough to believe them. Many of
|
||||||
|
their works discuss Synthoids and pose a threat to society. The publications are spreading rapidly in China and
|
||||||
|
other Eastern countries.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Samizdat has done a good job of keeping hidden and anonymous. However, we've just received intelligence that
|
||||||
|
their base of operations is in Ishima's underground sewer systems. Your task is to investigate the sewer
|
||||||
|
systems, and eliminate Samizdat. They must never publish anything again.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Titan": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Several months ago Titan Laboratories' Bioengineering department was infiltrated by Synthoids. As far as we
|
||||||
|
know, Titan Laboratories' management has no knowledge about this. We don't know what the Synthoids are up to,
|
||||||
|
but the research that they could be conducting using Titan Laboraties' vast resources is potentially very
|
||||||
|
dangerous.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your goal is to enter and destroy the Bioengineering department's facility in Aevum. The task is not just to
|
||||||
|
retire the Synthoids there, but also to destroy any information or research at the facility that is relevant to
|
||||||
|
the Synthoids and their goals.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Ares": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
One of our undercover agents, Agent Carter, has informed us of a massive weapons deal going down in Dubai
|
||||||
|
between rogue Russian militants and a radical Synthoid community. These weapons are next-gen plasma and energy
|
||||||
|
weapons. It is critical for the safety of humanity that this deal does not happen.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your task is to intercept the deal. Leave no survivors.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Archangel": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our analysts have discovered that the popular Red Rabbit brothel in Amsterdam is run and 'staffed' by MK-VI
|
||||||
|
Synthoids. Intelligence suggests that the profit from this brothel is used to fund a large black market arms
|
||||||
|
trafficking operation.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of this operation is to take out the leaders that are running the Red Rabbit brothel. Try to limit the
|
||||||
|
number of other casualties, but do what you must to complete the mission.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Juggernaut": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
The CIA has just encountered a new security threat. A new criminal group, lead by a shadowy operative who calls
|
||||||
|
himself Juggernaut, has been smuggling drugs and weapons (including suspected bioweapons) into Sector-12. We
|
||||||
|
also have reason to believe the tried to break into one of Universal Energy's facilities in order to cause a
|
||||||
|
city-wide blackout. The CIA suspects that Juggernaut is a heavily-augmented Synthoid, and have thus enlisted our
|
||||||
|
help.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your mission is to eradicate Juggernaut and his followers.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Red Dragon": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
The Tetrads criminal organization is suspected of reverse-engineering the MK-VI Synthoid design. We believe they
|
||||||
|
altered and possibly improved the design and began manufacturing their own Synthoid models in order to bolster
|
||||||
|
their criminal activities.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your task is to infiltrate and destroy the Tetrads' base of operations in Los Angeles. Intelligence tells us
|
||||||
|
that their base houses one of their Synthoid manufacturing units.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation K": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
CODE RED SITUATION. Our intelligence tells us that VitaLife has discovered a new android cloning technology.
|
||||||
|
This technology is supposedly capable of cloning Synthoid, not only physically but also their advanced AI
|
||||||
|
modules. We do not believe that VitaLife is trying to use this technology illegally or maliciously, but if any
|
||||||
|
Synthoids were able to infiltrate the corporation and take advantage of this technology then the results would
|
||||||
|
be catastrophic.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
We do not have the power or jurisdiction to shutdown this down through legal or political means, so we must
|
||||||
|
resort to a covert operation. Your goal is to destroy this technology and eliminate anyone who was involved in
|
||||||
|
its creation.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Deckard": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Despite your success in eliminating VitaLife's new android-replicating technology in Operation K, we've
|
||||||
|
discovered that a small group of MK-VI Synthoids were able to make off with the schematics and design of the
|
||||||
|
technology before the Operation. It is almost a certainty that these Synthoids are some of the rogue MK-VI ones
|
||||||
|
from the Synthoid Uprising.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The goal of Operation Deckard is to hunt down these Synthoids and retire them. I don't need to tell you how
|
||||||
|
critical this mission is.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Tyrell": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
A week ago Blade Industries reported a small break-in at one of their Aevum Augmentation storage facitilities.
|
||||||
|
We figured out that The Dark Army was behind the heist, and didn't think any more of it. However, we've just
|
||||||
|
discovered that several known MK-VI Synthoids were part of that break-in group.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
We cannot have Synthoids upgrading their already-enhanced abilities with Augmentations. Your task is to hunt
|
||||||
|
down the associated Dark Army members and eliminate them.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Wallace": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Based on information gathered from Operation Tyrell, we've discovered that The Dark Army was well aware that
|
||||||
|
there were Synthoids amongst their ranks. Even worse, we believe that The Dark Army is working together with
|
||||||
|
other criminal organizations such as The Syndicate and that they are planning some sort of large-scale takeover
|
||||||
|
of multiple major cities, most notably Aevum. We suspect that Synthoids have infiltrated the ranks of these
|
||||||
|
criminal factions and are trying to stage another Synthoid uprising.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The best way to deal with this is to prevent it before it even happens. The goal of Operation Wallace is to
|
||||||
|
destroy the Dark Army and Syndicate factions in Aevum immediately. Leave no survivors.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Shoulder of Orion": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
China's Solaris Space Systems is secretly launching the first manned spacecraft in over a decade using
|
||||||
|
Synthoids. We believe China is trying to establish the first off-world colonies.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The mission is to prevent this launch without instigating an international conflict. When you accept this
|
||||||
|
mission you will be officially disavowed by the NSA and the national government until after you successfully
|
||||||
|
return. In the event of failure, all of the operation's team members must not let themselves be captured alive.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Hyron": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our intelligence tells us that Fulcrum Technologies is developing a quantum supercomputer using human brains as
|
||||||
|
core processors. This supercomputer is rumored to be able to store vast amounts of data and perform computations
|
||||||
|
unmatched by any other supercomputer on the planet. But more importantly, the use of organic human brains means
|
||||||
|
that the supercomputer may be able to reason abstractly and become self-aware.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
I do not need to remind you why sentient-level AIs pose a serious threat to all of mankind.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The research for this project is being conducted at one of Fulcrum Technologies secret facilities in Aevum,
|
||||||
|
codenamed 'Alpha Ranch'. Infiltrate the compound, delete and destroy the work, and then find and kill the
|
||||||
|
project lead.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Morpheus": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
DreamSense Technologies is an advertising company that uses special technology to transmit their ads into the
|
||||||
|
peoples dreams and subconcious. They do this using broadcast transmitter towers. Based on information from our
|
||||||
|
agents and informants in Chonqging, we have reason to believe that one of the broadcast towers there has been
|
||||||
|
compromised by Synthoids and is being used to spread pro-Synthoid propaganda.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The mission is to destroy this broadcast tower. Speed and stealth are of the upmost important for this.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Ion Storm": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our analysts have uncovered a gathering of MK-VI Synthoids that have taken up residence in the Sector-12 Slums.
|
||||||
|
We don't know if they are rogue Synthoids from the Uprising, but we do know that they have been stockpiling
|
||||||
|
weapons, money, and other resources. This makes them dangerous.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
This is a full-scale assault operation to find and retire all of these Synthoids in the Sector-12 Slums.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Annihilus": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Our superiors have ordered us to eradicate everything and everyone in an underground facility located in Aevum.
|
||||||
|
They tell us that the facility houses many dangerous Synthoids and belongs to a terrorist organization called
|
||||||
|
'The Covenant'. We have no prior intelligence about this organization, so you are going in blind.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Ultron": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
OmniTek Incorporated, the original designer and manufacturer of Synthoids, has notified us of a malfunction in
|
||||||
|
their AI design. This malfunction, when triggered, causes MK-VI Synthoids to become radicalized and seek out the
|
||||||
|
destruction of humanity. They say that this bug affects all MK-VI Synthoids, not just the rogue ones from the
|
||||||
|
Uprising.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
OmniTek has also told us they they believe someone has triggered this malfunction in a large group of MK-VI
|
||||||
|
Synthoids, and that these newly-radicalized Synthoids are now amassing in Volhaven to form a terrorist group
|
||||||
|
called Ultron.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Intelligence suggests Ultron is heavily armed and that their members are augmented. We believe Ultron is making
|
||||||
|
moves to take control of and weaponize DeltaOne's Tactical High-Energy Satellite Laser Array (THESLA).
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your task is to find and destroy Ultron.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Centurion": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Throughout all of humanity's history, we have relied on technology to survive, conquer, and progress. Its
|
||||||
|
advancement became our primary goal. And at the peak of human civilization technology turned into power. Global,
|
||||||
|
absolute power.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
It seems that the universe is not without a sense of irony.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Vindictus": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The bits are all around us. The daemons that hold the Node together can manifest themselves in many different
|
||||||
|
ways.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{"D)@#)($M)C0293c40($*)@#D0JUMP3Rm0C<*@#)*$)#02c94830c(#$*D)"}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Operation Daedalus": {
|
||||||
|
desc: <> Yesterday we obeyed kings and bent our neck to emperors. Today we kneel only to truth.</>,
|
||||||
|
},
|
||||||
|
};
|
44
src/Bladeburner/data/Contracts.tsx
Normal file
44
src/Bladeburner/data/Contracts.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IContract {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Contracts: {
|
||||||
|
[key: string]: IContract | undefined;
|
||||||
|
} = {
|
||||||
|
Tracking: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Identify and locate Synthoids. This contract involves reconnaissance and information-gathering ONLY. Do NOT
|
||||||
|
engage. Stealth is of the utmost importance.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for whatever
|
||||||
|
city you are currently in.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Bounty Hunter": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successfully completing a Bounty Hunter contract will lower the population in your current city, and will also
|
||||||
|
increase its chaos level.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Retirement: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Hunt down and retire (kill) rogue Synthoids.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successfully completing a Retirement contract will lower the population in your current city, and will also
|
||||||
|
increase its chaos level.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
65
src/Bladeburner/data/GeneralActions.tsx
Normal file
65
src/Bladeburner/data/GeneralActions.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IContract {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GeneralActions: {
|
||||||
|
[key: string]: IContract | undefined;
|
||||||
|
} = {
|
||||||
|
Training: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Improve your abilities at the Bladeburner unit's specialized training center. Doing this gives experience for
|
||||||
|
all combat stats and also increases your max stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
"Field Analysis": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Mine and analyze Synthoid-related data. This improves the Bladeburner's unit intelligence on Synthoid locations
|
||||||
|
and activities. Completing this action will improve the accuracy of your Synthoid population estimated in the
|
||||||
|
current city.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Does NOT require stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
Recruitment: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Attempt to recruit members for your Bladeburner team. These members can help you conduct operations.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Does NOT require stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
Diplomacy: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Improve diplomatic relations with the Synthoid population. Completing this action will reduce the Chaos level in
|
||||||
|
your current city.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Does NOT require stamina.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
"Hyperbolic Regeneration Chamber": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Enter cryogenic stasis using the Bladeburner division's hi-tech Regeneration Chamber. This will slowly heal your
|
||||||
|
wounds and slightly increase your stamina.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
60
src/Bladeburner/data/Operations.tsx
Normal file
60
src/Bladeburner/data/Operations.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface IOperation {
|
||||||
|
desc: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Operations: {
|
||||||
|
[key: string]: IOperation | undefined;
|
||||||
|
} = {
|
||||||
|
Investigation: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
As a field agent, investigate and identify Synthoid populations, movements, and operations.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successful Investigation ops will increase the accuracy of your synthoid data.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
You will NOT lose HP from failed Investigation ops.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Undercover Operation": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Conduct undercover operations to identify hidden and underground Synthoid communities and organizations.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Successful Undercover ops will increase the accuracy of your synthoid data.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Sting Operation": {
|
||||||
|
desc: <>Conduct a sting operation to bait and capture particularly notorious Synthoid criminals.</>,
|
||||||
|
},
|
||||||
|
Raid: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Lead an assault on a known Synthoid community. Note that there must be an existing Synthoid community in your
|
||||||
|
current city in order for this Operation to be successful.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"Stealth Retirement Operation": {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Lead a covert operation to retire Synthoids. The objective is to complete the task without drawing any
|
||||||
|
attention. Stealth and discretion are key.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Assassination: {
|
||||||
|
desc: (
|
||||||
|
<>
|
||||||
|
Assassinate Synthoids that have been identified as important, high-profile social and political leaders in the
|
||||||
|
Synthoid communities.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
69
src/Bladeburner/ui/ActionLevel.tsx
Normal file
69
src/Bladeburner/ui/ActionLevel.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
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 title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}>
|
||||||
|
<IconButton disabled={!canIncrease} onClick={increaseLevel}>
|
||||||
|
<ArrowDropUpIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={isActive ? <Typography>WARNING: changing the level will restart the Operation</Typography> : ""}>
|
||||||
|
<IconButton disabled={!canDecrease} onClick={decreaseLevel}>
|
||||||
|
<ArrowDropDownIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -1,54 +1,44 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React from "react";
|
||||||
import { GeneralActionPage } from "./GeneralActionPage";
|
import { GeneralActionPage } from "./GeneralActionPage";
|
||||||
import { ContractPage } from "./ContractPage";
|
import { ContractPage } from "./ContractPage";
|
||||||
import { OperationPage } from "./OperationPage";
|
import { OperationPage } from "./OperationPage";
|
||||||
import { BlackOpPage } from "./BlackOpPage";
|
import { BlackOpPage } from "./BlackOpPage";
|
||||||
import { SkillPage } from "./SkillPage";
|
import { SkillPage } from "./SkillPage";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
import Tabs from "@mui/material/Tabs";
|
||||||
|
import Tab from "@mui/material/Tab";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AllPages(props: IProps): React.ReactElement {
|
export function AllPages(props: IProps): React.ReactElement {
|
||||||
const [page, setPage] = useState("General");
|
const [value, setValue] = React.useState(0);
|
||||||
const setRerender = useState(false)[1];
|
|
||||||
|
|
||||||
useEffect(() => {
|
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
setValue(tab);
|
||||||
return () => clearInterval(id);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function Header(props: { name: string }): React.ReactElement {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
onClick={() => setPage(props.name)}
|
|
||||||
className={page !== props.name ? "bladeburner-nav-button noselect" : "bladeburner-nav-button-inactive noselect"}
|
|
||||||
>
|
|
||||||
{props.name}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header name={"General"} />
|
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
|
||||||
<Header name={"Contracts"} />
|
<Tab label="General" />
|
||||||
<Header name={"Operations"} />
|
<Tab label="Contracts" />
|
||||||
<Header name={"BlackOps"} />
|
<Tab label="Operations" />
|
||||||
<Header name={"Skills"} />
|
<Tab label="BlackOps" />
|
||||||
<div style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Tab label="Skills" />
|
||||||
{page === "General" && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
</Tabs>
|
||||||
{page === "Contracts" && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
<Box sx={{ p: 1 }}>
|
||||||
{page === "Operations" && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
{value === 0 && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
{page === "BlackOps" && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
{value === 1 && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
{page === "Skills" && <SkillPage bladeburner={props.bladeburner} />}
|
{value === 2 && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
</div>
|
{value === 3 && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||||
<span className="text">
|
{value === 4 && <SkillPage bladeburner={props.bladeburner} />}
|
||||||
{stealthIcon} = This action requires stealth, {killIcon} = This action involves retirement
|
</Box>
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
26
src/Bladeburner/ui/Autolevel.tsx
Normal file
26
src/Bladeburner/ui/Autolevel.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Switch from "@mui/material/Switch";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
action: IAction;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Autolevel(props: IProps): React.ReactElement {
|
||||||
|
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
props.action.autoLevel = event.target.checked;
|
||||||
|
props.rerender();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
|
<Tooltip title={<Typography>Automatically increase operation level when possible</Typography>}>
|
||||||
|
<Typography> Autolevel:</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
<Switch checked={props.action.autoLevel} onChange={onAutolevel} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
@ -2,22 +2,29 @@ import React, { useState } from "react";
|
|||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { ActionTypes } from "../data/ActionTypes";
|
import { ActionTypes } from "../data/ActionTypes";
|
||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
import { TeamSizeButton } from "./TeamSizeButton";
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { TeamSizePopup } from "./TeamSizePopup";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { BlackOperation } from "../BlackOperation";
|
||||||
|
import { BlackOperations } from "../data/BlackOperations";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { SuccessChance } from "./SuccessChance";
|
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
import { SuccessChance } from "./SuccessChance";
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: BlackOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BlackOpElem(props: IProps): React.ReactElement {
|
export function BlackOpElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
|
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
|
||||||
if (isCompleted) {
|
if (isCompleted) {
|
||||||
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>;
|
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>;
|
||||||
@ -26,7 +33,6 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
|||||||
const isActive =
|
const isActive =
|
||||||
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
|
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
|
||||||
props.action.name === props.bladeburner.action.name;
|
props.action.name === props.bladeburner.action.name;
|
||||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
@ -34,70 +40,54 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
|||||||
props.bladeburner.actionTimeToComplete,
|
props.bladeburner.actionTimeToComplete,
|
||||||
);
|
);
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = BlackOperations[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes.BlackOperation;
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTeam(): void {
|
|
||||||
const popupId = "bladeburner-operation-set-team-size-popup";
|
|
||||||
createPopup(popupId, TeamSizePopup, {
|
|
||||||
bladeburner: props.bladeburner,
|
|
||||||
action: props.action,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
{isActive ? (
|
{isActive ? (
|
||||||
<>
|
<>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<>
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
|
<p style={{ display: "block" }}>
|
||||||
|
{createProgressBarText({
|
||||||
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<CopyableText value={props.action.name} />
|
<>
|
||||||
|
<CopyableText value={props.action.name} />
|
||||||
|
|
||||||
|
<StartButton
|
||||||
|
bladeburner={props.bladeburner}
|
||||||
|
type={ActionTypes.BlackOperation}
|
||||||
|
name={props.action.name}
|
||||||
|
rerender={rerender}
|
||||||
|
/>
|
||||||
|
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</h2>
|
</Typography>
|
||||||
{isActive ? (
|
|
||||||
<p style={{ display: "block" }}>
|
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<a
|
|
||||||
className={hasReqdRank ? "a-link-button" : "a-link-button-inactive"}
|
|
||||||
style={{ margin: "3px", padding: "3px" }}
|
|
||||||
onClick={onStart}
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
</a>
|
|
||||||
<a onClick={onTeam} style={{ margin: "3px", padding: "3px" }} className="a-link-button">
|
|
||||||
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<p style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
<Typography>{actionData.desc}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<p style={{ display: "block", color: hasReqdRank ? "white" : "red" }}>
|
<Typography color={hasReqdRank ? "primary" : "error"}>
|
||||||
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
||||||
</p>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
Estimated Success Chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||||
{props.action.isStealth ? stealthIcon : <></>}
|
|
||||||
{props.action.isKill ? killIcon : <></>}
|
|
||||||
<br />
|
<br />
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
</pre>
|
</Typography>
|
||||||
</>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,7 @@ export function BlackOpList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{blackops.map((blackop: BlackOperation) => (
|
{blackops.map((blackop: BlackOperation) => (
|
||||||
<li key={blackop.name} className="bladeburner-action">
|
<BlackOpElem key={blackop.name} bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
||||||
<BlackOpElem bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { BlackOpList } from "./BlackOpList";
|
import { BlackOpList } from "./BlackOpList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,7 +12,7 @@ interface IProps {
|
|||||||
export function BlackOpPage(props: IProps): React.ReactElement {
|
export function BlackOpPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>
|
||||||
Black Operations (Black Ops) are special, one-time covert operations. Each Black Op must be unlocked
|
Black Operations (Black Ops) are special, one-time covert operations. Each Black Op must be unlocked
|
||||||
successively by completing the one before it.
|
successively by completing the one before it.
|
||||||
<br />
|
<br />
|
||||||
@ -21,7 +22,7 @@ export function BlackOpPage(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank
|
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank
|
||||||
losses.
|
losses.
|
||||||
</p>
|
</Typography>
|
||||||
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
|
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,42 +1,39 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Stats } from "./Stats";
|
import { Stats } from "./Stats";
|
||||||
import { Console } from "./Console";
|
import { Console } from "./Console";
|
||||||
import { AllPages } from "./AllPages";
|
import { AllPages } from "./AllPages";
|
||||||
|
|
||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
export function BladeburnerRoot(): React.ReactElement {
|
export function BladeburnerRoot(): React.ReactElement {
|
||||||
const player = use.Player();
|
const player = use.Player();
|
||||||
const router = use.Router();
|
const router = use.Router();
|
||||||
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(rerender, 200);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const bladeburner = player.bladeburner;
|
const bladeburner = player.bladeburner;
|
||||||
if (bladeburner === null) return <></>;
|
if (bladeburner === null) return <></>;
|
||||||
return (
|
return (
|
||||||
<div className="bladeburner-container">
|
<Box display="flex" flexDirection="column">
|
||||||
<div style={{ height: "60%", display: "block", position: "relative" }}>
|
<Grid container>
|
||||||
<div
|
<Grid item xs={6}>
|
||||||
style={{
|
|
||||||
height: "100%",
|
|
||||||
width: "30%",
|
|
||||||
display: "inline-block",
|
|
||||||
border: "1px solid white",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Stats bladeburner={bladeburner} player={player} router={router} />
|
<Stats bladeburner={bladeburner} player={player} router={router} />
|
||||||
</div>
|
</Grid>
|
||||||
<Console bladeburner={bladeburner} player={player} />
|
<Grid item xs={6}>
|
||||||
</div>
|
<Console bladeburner={bladeburner} player={player} />
|
||||||
<div
|
</Grid>
|
||||||
style={{
|
</Grid>
|
||||||
width: "70%",
|
|
||||||
display: "block",
|
<AllPages bladeburner={bladeburner} player={player} />
|
||||||
border: "1px solid white",
|
</Box>
|
||||||
marginTop: "6px",
|
|
||||||
padding: "6px",
|
|
||||||
position: "relative",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AllPages bladeburner={bladeburner} player={player} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,48 @@ import React, { useState, useRef, useEffect } from "react";
|
|||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import List from "@mui/material/List";
|
||||||
|
import ListItem from "@mui/material/ListItem";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import { Theme } from "@mui/material/styles";
|
||||||
|
import makeStyles from "@mui/styles/makeStyles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
|
||||||
interface ILineProps {
|
interface ILineProps {
|
||||||
content: any;
|
content: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
textfield: {
|
||||||
|
margin: theme.spacing(0),
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
backgroundColor: "#000",
|
||||||
|
},
|
||||||
|
nopadding: {
|
||||||
|
padding: theme.spacing(0),
|
||||||
|
},
|
||||||
|
preformatted: {
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
margin: theme.spacing(0),
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
padding: theme.spacing(0),
|
||||||
|
height: "100%",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
function Line(props: ILineProps): React.ReactElement {
|
function Line(props: ILineProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<tr>
|
<ListItem sx={{ p: 0 }}>
|
||||||
<td className="bladeburner-console-line" style={{ color: "var(--my-font-color)", whiteSpace: "pre-wrap" }}>
|
<Typography>{props.content}</Typography>
|
||||||
{props.content}
|
</ListItem>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,15 +53,21 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Console(props: IProps): React.ReactElement {
|
export function Console(props: IProps): React.ReactElement {
|
||||||
const lastRef = useRef<HTMLDivElement>(null);
|
const classes = useStyles();
|
||||||
|
const scrollHook = useRef<HTMLDivElement>(null);
|
||||||
|
const [command, setCommand] = useState("");
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
|
||||||
|
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
setCommand(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
|
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
|
||||||
|
|
||||||
// TODO: Figure out how to actually make the scrolling work correctly.
|
// TODO: Figure out how to actually make the scrolling work correctly.
|
||||||
function scrollToBottom(): void {
|
function scrollToBottom(): void {
|
||||||
if (!lastRef.current) return;
|
if (!scrollHook.current) return;
|
||||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
scrollHook.current.scrollTop = scrollHook.current.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function rerender(): void {
|
function rerender(): void {
|
||||||
@ -50,13 +86,11 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const command = event.currentTarget.value;
|
|
||||||
event.currentTarget.value = "";
|
|
||||||
if (command.length > 0) {
|
if (command.length > 0) {
|
||||||
props.bladeburner.postToConsole("> " + command);
|
props.bladeburner.postToConsole("> " + command);
|
||||||
props.bladeburner.executeConsoleCommands(props.player, command);
|
props.bladeburner.executeConsoleCommands(props.player, command);
|
||||||
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
|
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
|
||||||
rerender();
|
setCommand("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,31 +139,34 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={lastRef} className="bladeburner-console-div">
|
<Box height={"60vh"} display={"flex"} alignItems={"stretch"} component={Paper}>
|
||||||
<table className="bladeburner-console-table">
|
<Box>
|
||||||
<tbody>
|
<List sx={{ height: "100%", overflow: "auto" }}>
|
||||||
{/*
|
|
||||||
TODO: optimize this.
|
|
||||||
using `i` as a key here isn't great because it'll re-render everything
|
|
||||||
everytime the console reaches max length.
|
|
||||||
*/}
|
|
||||||
{props.bladeburner.consoleLogs.map((log: any, i: number) => (
|
{props.bladeburner.consoleLogs.map((log: any, i: number) => (
|
||||||
<Line key={i} content={log} />
|
<Line key={i} content={log} />
|
||||||
))}
|
))}
|
||||||
<tr key="input" id="bladeburner-console-input-row" className="bladeburner-console-input-row">
|
<TextField
|
||||||
<td className="bladeburner-console-input-cell">
|
classes={{ root: classes.textfield }}
|
||||||
<pre>{"> "}</pre>
|
autoFocus
|
||||||
<input
|
tabIndex={1}
|
||||||
autoFocus
|
type="text"
|
||||||
className="bladeburner-console-input"
|
value={command}
|
||||||
tabIndex={1}
|
onChange={handleCommandChange}
|
||||||
type="text"
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
InputProps={{
|
||||||
/>
|
// for players to hook in
|
||||||
</td>
|
className: classes.input,
|
||||||
</tr>
|
startAdornment: (
|
||||||
</tbody>
|
<>
|
||||||
</table>
|
<Typography>> </Typography>
|
||||||
</div>
|
</>
|
||||||
|
),
|
||||||
|
spellCheck: false,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</List>
|
||||||
|
<div ref={scrollHook}></div>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,113 +2,78 @@ import React, { useState } from "react";
|
|||||||
import { ActionTypes } from "../data/ActionTypes";
|
import { ActionTypes } from "../data/ActionTypes";
|
||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
import { Contracts } from "../data/Contracts";
|
||||||
import { BladeburnerConstants } from "../data/Constants";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { SuccessChance } from "./SuccessChance";
|
import { SuccessChance } from "./SuccessChance";
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
import { ActionLevel } from "./ActionLevel";
|
||||||
|
import { Autolevel } from "./Autolevel";
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: IAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ContractElem(props: IProps): React.ReactElement {
|
export function ContractElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isActive =
|
const isActive =
|
||||||
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
||||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||||
props.bladeburner.actionTimeToComplete,
|
props.bladeburner.actionTimeToComplete,
|
||||||
);
|
);
|
||||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = Contracts[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes.Contract;
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseLevel(): void {
|
|
||||||
++props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decreaseLevel(): void {
|
|
||||||
--props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
props.action.autoLevel = event.target.checked;
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
{isActive ? (
|
||||||
{isActive ? (
|
<>
|
||||||
<>
|
<Typography>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
</>
|
</Typography>
|
||||||
) : (
|
<Typography>
|
||||||
<CopyableText value={props.action.name} />
|
{createProgressBarText({
|
||||||
)}
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
</h2>
|
})}
|
||||||
{isActive ? (
|
</Typography>
|
||||||
<p style={{ display: "block" }}>
|
</>
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
<CopyableText value={props.action.name} />
|
||||||
Start
|
<StartButton
|
||||||
</a>
|
bladeburner={props.bladeburner}
|
||||||
|
type={ActionTypes.Contract}
|
||||||
|
name={props.action.name}
|
||||||
|
rerender={rerender}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||||
<span className="tooltiptext">
|
|
||||||
{props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.ContractSuccessesPerLevel)} successes needed
|
|
||||||
for next level
|
|
||||||
</span>
|
|
||||||
Level: {props.action.level} / {props.action.maxLevel}
|
|
||||||
</pre>
|
|
||||||
<a
|
|
||||||
onClick={increaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${maxLevel ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↑
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
onClick={decreaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${props.action.level <= 1 ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↓
|
|
||||||
</a>
|
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
{actionData.desc}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||||
{props.action.isStealth ? stealthIcon : <></>}
|
|
||||||
{props.action.isKill ? killIcon : <></>}
|
|
||||||
<br />
|
<br />
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
<br />
|
<br />
|
||||||
@ -117,13 +82,9 @@ export function ContractElem(props: IProps): React.ReactElement {
|
|||||||
Successes: {props.action.successes}
|
Successes: {props.action.successes}
|
||||||
<br />
|
<br />
|
||||||
Failures: {props.action.failures}
|
Failures: {props.action.failures}
|
||||||
</pre>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
<Autolevel rerender={rerender} action={props.action} />
|
||||||
Autolevel:
|
</Paper>
|
||||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
|
||||||
</label>
|
|
||||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ export function ContractList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{names.map((name: string) => (
|
{names.map((name: string) => (
|
||||||
<li key={name} className="bladeburner-action">
|
<ContractElem key={name} bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
||||||
<ContractElem bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { ContractList } from "./ContractList";
|
import { ContractList } from "./ContractList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,14 +12,14 @@ interface IProps {
|
|||||||
export function ContractPage(props: IProps): React.ReactElement {
|
export function ContractPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>
|
||||||
Complete contracts in order to increase your Bladeburner rank and earn money. Failing a contract will cause you
|
Complete contracts in order to increase your Bladeburner rank and earn money. Failing a contract will cause you
|
||||||
to lose HP, which can lead to hospitalization.
|
to lose HP, which can lead to hospitalization.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more
|
You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more
|
||||||
difficult, but grant more rank, experience, and money.
|
difficult, but grant more rank, experience, and money.
|
||||||
</p>
|
</Typography>
|
||||||
<ContractList bladeburner={props.bladeburner} player={props.player} />
|
<ContractList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,17 +3,28 @@ import { ActionTypes } from "../data/ActionTypes";
|
|||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
|
import { GeneralActions } from "../data/GeneralActions";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: IAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GeneralActionElem(props: IProps): React.ReactElement {
|
export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isActive = props.action.name === props.bladeburner.action.name;
|
const isActive = props.action.name === props.bladeburner.action.name;
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||||
@ -37,44 +48,44 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
|||||||
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1))
|
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1))
|
||||||
: -1;
|
: -1;
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = GeneralActions[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes[props.action.name as string];
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
{isActive ? (
|
||||||
{isActive ? (
|
<>
|
||||||
<>
|
<Typography>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
</>
|
</Typography>
|
||||||
) : (
|
<Typography>
|
||||||
<CopyableText value={props.action.name} />
|
{createProgressBarText({
|
||||||
)}
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
</h2>
|
})}
|
||||||
{isActive ? (
|
</Typography>
|
||||||
<p style={{ display: "block" }}>
|
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
|
||||||
Start
|
|
||||||
</a>
|
|
||||||
</>
|
</>
|
||||||
|
) : (
|
||||||
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
|
<Typography>
|
||||||
|
<CopyableText value={props.action.name} />
|
||||||
|
</Typography>
|
||||||
|
<StartButton
|
||||||
|
bladeburner={props.bladeburner}
|
||||||
|
type={ActionTypes[props.action.name as string]}
|
||||||
|
name={props.action.name}
|
||||||
|
rerender={rerender}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }}></pre>
|
<Typography>{actionData.desc}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
{successChance !== -1 && (
|
{successChance !== -1 && (
|
||||||
<>
|
<>
|
||||||
@ -82,7 +93,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
|||||||
Estimated success chance: {formatNumber(successChance * 100, 1)}%
|
Estimated success chance: {formatNumber(successChance * 100, 1)}%
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</pre>
|
</Typography>
|
||||||
</>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,7 @@ export function GeneralActionList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{actions.map((action: Action) => (
|
{actions.map((action: Action) => (
|
||||||
<li key={action.name} className="bladeburner-action">
|
<GeneralActionElem key={action.name} bladeburner={props.bladeburner} action={action} player={props.player} />
|
||||||
<GeneralActionElem bladeburner={props.bladeburner} action={action} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { GeneralActionList } from "./GeneralActionList";
|
import { GeneralActionList } from "./GeneralActionList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,10 +12,7 @@ interface IProps {
|
|||||||
export function GeneralActionPage(props: IProps): React.ReactElement {
|
export function GeneralActionPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>These are generic actions that will assist you in your Bladeburner duties.</Typography>
|
||||||
These are generic actions that will assist you in your Bladeburner duties. They will not affect your Bladeburner
|
|
||||||
rank in any way.
|
|
||||||
</p>
|
|
||||||
<GeneralActionList bladeburner={props.bladeburner} player={props.player} />
|
<GeneralActionList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
9
src/Bladeburner/ui/KillIcon.tsx
Normal file
9
src/Bladeburner/ui/KillIcon.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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 title={<Typography>This action involves retirement</Typography>}>{killIcon}</Tooltip>;
|
||||||
|
}
|
@ -2,127 +2,81 @@ import React, { useState } from "react";
|
|||||||
import { ActionTypes } from "../data/ActionTypes";
|
import { ActionTypes } from "../data/ActionTypes";
|
||||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { stealthIcon, killIcon } from "../data/Icons";
|
|
||||||
import { BladeburnerConstants } from "../data/Constants";
|
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { TeamSizePopup } from "./TeamSizePopup";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { SuccessChance } from "./SuccessChance";
|
import { SuccessChance } from "./SuccessChance";
|
||||||
|
import { ActionLevel } from "./ActionLevel";
|
||||||
|
import { Autolevel } from "./Autolevel";
|
||||||
|
import { StartButton } from "./StartButton";
|
||||||
|
import { TeamSizeButton } from "./TeamSizeButton";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { Operation } from "../Operation";
|
||||||
|
import { Operations } from "../data/Operations";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import { CopyableText } from "../../ui/React/CopyableText";
|
import { CopyableText } from "../../ui/React/CopyableText";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
player: IPlayer;
|
player: IPlayer;
|
||||||
action: any;
|
action: Operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OperationElem(props: IProps): React.ReactElement {
|
export function OperationElem(props: IProps): React.ReactElement {
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
function rerender(): void {
|
||||||
|
setRerender((old) => !old);
|
||||||
|
}
|
||||||
const isActive =
|
const isActive =
|
||||||
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
||||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
|
||||||
const computedActionTimeCurrent = Math.min(
|
const computedActionTimeCurrent = Math.min(
|
||||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||||
props.bladeburner.actionTimeToComplete,
|
props.bladeburner.actionTimeToComplete,
|
||||||
);
|
);
|
||||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
|
||||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
|
||||||
|
|
||||||
function onStart(): void {
|
const actionData = Operations[props.action.name];
|
||||||
props.bladeburner.action.type = ActionTypes.Operation;
|
if (actionData === undefined) {
|
||||||
props.bladeburner.action.name = props.action.name;
|
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTeam(): void {
|
|
||||||
const popupId = "bladeburner-operation-set-team-size-popup";
|
|
||||||
createPopup(popupId, TeamSizePopup, {
|
|
||||||
bladeburner: props.bladeburner,
|
|
||||||
action: props.action,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseLevel(): void {
|
|
||||||
++props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function decreaseLevel(): void {
|
|
||||||
--props.action.level;
|
|
||||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
props.action.autoLevel = event.target.checked;
|
|
||||||
setRerender((old) => !old);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
{isActive ? (
|
||||||
{isActive ? (
|
<>
|
||||||
<>
|
<Typography>
|
||||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||||
</>
|
</Typography>
|
||||||
) : (
|
<Typography>
|
||||||
<CopyableText value={props.action.name} />
|
{createProgressBarText({
|
||||||
)}
|
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||||
</h2>
|
})}
|
||||||
{isActive ? (
|
</Typography>
|
||||||
<p style={{ display: "block" }}>
|
</>
|
||||||
{createProgressBarText({
|
|
||||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
<CopyableText value={props.action.name} />
|
||||||
Start
|
<StartButton
|
||||||
</a>
|
bladeburner={props.bladeburner}
|
||||||
<a onClick={onTeam} style={{ margin: "3px", padding: "3px" }} className="a-link-button">
|
type={ActionTypes.Operation}
|
||||||
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
name={props.action.name}
|
||||||
</a>
|
rerender={rerender}
|
||||||
|
/>
|
||||||
|
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
|
||||||
<span className="tooltiptext">
|
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||||
{props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.OperationSuccessesPerLevel)} successes
|
|
||||||
needed for next level
|
|
||||||
</span>
|
|
||||||
Level: {props.action.level} / {props.action.maxLevel}
|
|
||||||
</pre>
|
|
||||||
<a
|
|
||||||
onClick={increaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${maxLevel ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↑
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
onClick={decreaseLevel}
|
|
||||||
style={{ padding: "2px", margin: "2px" }}
|
|
||||||
className={`tooltip ${props.action.level <= 1 ? "a-link-button-inactive" : "a-link-button"}`}
|
|
||||||
>
|
|
||||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↓
|
|
||||||
</a>
|
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<pre style={{ display: "inline-block" }}>
|
<Typography>
|
||||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
{actionData.desc}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||||
{props.action.isStealth ? stealthIcon : <></>}
|
|
||||||
{props.action.isKill ? killIcon : <></>}
|
|
||||||
<br />
|
<br />
|
||||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||||
<br />
|
<br />
|
||||||
@ -131,13 +85,9 @@ export function OperationElem(props: IProps): React.ReactElement {
|
|||||||
Successes: {props.action.successes}
|
Successes: {props.action.successes}
|
||||||
<br />
|
<br />
|
||||||
Failures: {props.action.failures}
|
Failures: {props.action.failures}
|
||||||
</pre>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
<Autolevel rerender={rerender} action={props.action} />
|
||||||
Autolevel:
|
</Paper>
|
||||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
|
||||||
</label>
|
|
||||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,7 @@ export function OperationList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{names.map((name: string) => (
|
{names.map((name: string) => (
|
||||||
<li key={name} className="bladeburner-action">
|
<OperationElem key={name} bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
||||||
<OperationElem bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import { OperationList } from "./OperationList";
|
import { OperationList } from "./OperationList";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -11,7 +12,7 @@ interface IProps {
|
|||||||
export function OperationPage(props: IProps): React.ReactElement {
|
export function OperationPage(props: IProps): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
<Typography>
|
||||||
Carry out operations for the Bladeburner division. Failing an operation will reduce your Bladeburner rank. It
|
Carry out operations for the Bladeburner division. Failing an operation will reduce your Bladeburner rank. It
|
||||||
will also cause you to lose HP, which can lead to hospitalization. In general, operations are harder and more
|
will also cause you to lose HP, which can lead to hospitalization. In general, operations are harder and more
|
||||||
punishing than contracts, but are also more rewarding.
|
punishing than contracts, but are also more rewarding.
|
||||||
@ -27,7 +28,7 @@ export function OperationPage(props: IProps): React.ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
You can unlock higher-level operations by successfully completing them. Higher-level operations are more
|
You can unlock higher-level operations by successfully completing them. Higher-level operations are more
|
||||||
difficult, but grant more rank and experience.
|
difficult, but grant more rank and experience.
|
||||||
</p>
|
</Typography>
|
||||||
<OperationList bladeburner={props.bladeburner} player={props.player} />
|
<OperationList bladeburner={props.bladeburner} player={props.player} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,13 @@ import { CopyableText } from "../../ui/React/CopyableText";
|
|||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
skill: any;
|
skill: any;
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -28,26 +35,26 @@ export function SkillElem(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ my: 1, p: 1 }}>
|
||||||
<h2 style={{ display: "inline-block" }}>
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
<CopyableText value={props.skill.name} />
|
<CopyableText variant="h6" color="primary" value={props.skill.name} />
|
||||||
</h2>
|
{!canLevel || maxLvl ? (
|
||||||
<a
|
<IconButton disabled>
|
||||||
onClick={onClick}
|
<CloseIcon />
|
||||||
style={{ display: "inline-block", margin: "3px", padding: "3px" }}
|
</IconButton>
|
||||||
className={canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive"}
|
) : (
|
||||||
>
|
<IconButton onClick={onClick}>
|
||||||
Level
|
<AddIcon />
|
||||||
</a>
|
</IconButton>
|
||||||
<br />
|
)}
|
||||||
<br />
|
</Box>
|
||||||
<p style={{ display: "block" }}>Level: {currentLevel}</p>
|
<Typography>Level: {currentLevel}</Typography>
|
||||||
{maxLvl ? (
|
{maxLvl ? (
|
||||||
<p style={{ color: "red", display: "block" }}>MAX LEVEL</p>
|
<Typography>MAX LEVEL</Typography>
|
||||||
) : (
|
) : (
|
||||||
<p style={{ display: "block" }}>Skill Points required: {formatNumber(pointCost, 0)}</p>
|
<Typography>Skill Points required: {formatNumber(pointCost, 0)}</Typography>
|
||||||
)}
|
)}
|
||||||
<p style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.skill.desc }} />
|
<Typography>{props.skill.desc}</Typography>
|
||||||
</>
|
</Paper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,7 @@ export function SkillList(props: IProps): React.ReactElement {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.keys(Skills).map((skill: string) => (
|
{Object.keys(Skills).map((skill: string) => (
|
||||||
<li key={skill} className="bladeburner-action">
|
<SkillElem key={skill} bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
||||||
<SkillElem bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,8 @@ import { SkillList } from "./SkillList";
|
|||||||
import { BladeburnerConstants } from "../data/Constants";
|
import { BladeburnerConstants } from "../data/Constants";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
}
|
}
|
||||||
@ -18,46 +19,45 @@ export function SkillPage(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>
|
<Typography>
|
||||||
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
|
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
|
||||||
</p>
|
</Typography>
|
||||||
<p>
|
<Typography>
|
||||||
You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
|
You will gain one skill point every{" "}
|
||||||
<br />
|
{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
|
Note that when upgrading a skill, the benefit for that skill is additive. However, the effects of different
|
||||||
skills with each other is multiplicative.
|
skills with each other is multiplicative.
|
||||||
<br />
|
</Typography>
|
||||||
</p>
|
{valid(mults["successChanceAll"]) && (
|
||||||
<br />
|
<Typography>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</Typography>
|
||||||
{valid(mults["successChanceAll"]) && <p>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</p>}
|
)}
|
||||||
{valid(mults["successChanceStealth"]) && (
|
{valid(mults["successChanceStealth"]) && (
|
||||||
<p>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</p>
|
<Typography>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceKill"]) && (
|
{valid(mults["successChanceKill"]) && (
|
||||||
<p>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}</p>
|
<Typography>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceContract"]) && (
|
{valid(mults["successChanceContract"]) && (
|
||||||
<p>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</p>
|
<Typography>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceOperation"]) && (
|
{valid(mults["successChanceOperation"]) && (
|
||||||
<p>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}</p>
|
<Typography>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["successChanceEstimate"]) && (
|
{valid(mults["successChanceEstimate"]) && (
|
||||||
<p>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}</p>
|
<Typography>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}</Typography>
|
||||||
)}
|
)}
|
||||||
{valid(mults["actionTime"]) && <p>Action Time: x{formatNumber(mults["actionTime"], 3)}</p>}
|
{valid(mults["actionTime"]) && <Typography>Action Time: x{formatNumber(mults["actionTime"], 3)}</Typography>}
|
||||||
{valid(mults["effHack"]) && <p>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</p>}
|
{valid(mults["effHack"]) && <Typography>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</Typography>}
|
||||||
{valid(mults["effStr"]) && <p>Strength: x{formatNumber(mults["effStr"], 3)}</p>}
|
{valid(mults["effStr"]) && <Typography>Strength: x{formatNumber(mults["effStr"], 3)}</Typography>}
|
||||||
{valid(mults["effDef"]) && <p>Defense: x{formatNumber(mults["effDef"], 3)}</p>}
|
{valid(mults["effDef"]) && <Typography>Defense: x{formatNumber(mults["effDef"], 3)}</Typography>}
|
||||||
{valid(mults["effDex"]) && <p>Dexterity: x{formatNumber(mults["effDex"], 3)}</p>}
|
{valid(mults["effDex"]) && <Typography>Dexterity: x{formatNumber(mults["effDex"], 3)}</Typography>}
|
||||||
{valid(mults["effAgi"]) && <p>Agility: x{formatNumber(mults["effAgi"], 3)}</p>}
|
{valid(mults["effAgi"]) && <Typography>Agility: x{formatNumber(mults["effAgi"], 3)}</Typography>}
|
||||||
{valid(mults["effCha"]) && <p>Charisma: x{formatNumber(mults["effCha"], 3)}</p>}
|
{valid(mults["effCha"]) && <Typography>Charisma: x{formatNumber(mults["effCha"], 3)}</Typography>}
|
||||||
{valid(mults["effInt"]) && <p>Intelligence: x{formatNumber(mults["effInt"], 3)}</p>}
|
{valid(mults["effInt"]) && <Typography>Intelligence: x{formatNumber(mults["effInt"], 3)}</Typography>}
|
||||||
{valid(mults["stamina"]) && <p>Stamina: x{formatNumber(mults["stamina"], 3)}</p>}
|
{valid(mults["stamina"]) && <Typography>Stamina: x{formatNumber(mults["stamina"], 3)}</Typography>}
|
||||||
{valid(mults["money"]) && <p>Contract Money: x{formatNumber(mults["money"], 3)}</p>}
|
{valid(mults["money"]) && <Typography>Contract Money: x{formatNumber(mults["money"], 3)}</Typography>}
|
||||||
{valid(mults["expGain"]) && <p>Exp Gain: x{formatNumber(mults["expGain"], 3)}</p>}
|
{valid(mults["expGain"]) && <Typography>Exp Gain: x{formatNumber(mults["expGain"], 3)}</Typography>}
|
||||||
<br />
|
|
||||||
<SkillList bladeburner={props.bladeburner} onUpgrade={() => setRerender((old) => !old)} />
|
<SkillList bladeburner={props.bladeburner} onUpgrade={() => setRerender((old) => !old)} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
44
src/Bladeburner/ui/StartButton.tsx
Normal file
44
src/Bladeburner/ui/StartButton.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { BlackOperation } from "../BlackOperation";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
type: number;
|
||||||
|
name: string;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
export function StartButton(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
const action = props.bladeburner.getActionObject({ name: props.name, type: props.type });
|
||||||
|
if (action == null) {
|
||||||
|
throw new Error("Failed to get Operation Object for: " + props.name);
|
||||||
|
}
|
||||||
|
let disabled = false;
|
||||||
|
if (action.count < 1) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
if (props.name === "Raid" && props.bladeburner.getCurrentCity().comms === 0) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action instanceof BlackOperation && props.bladeburner.rank < action.reqdRank) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
|
function onStart(): void {
|
||||||
|
if (disabled) return;
|
||||||
|
props.bladeburner.action.type = props.type;
|
||||||
|
props.bladeburner.action.name = props.name;
|
||||||
|
props.bladeburner.startAction(player, props.bladeburner.action);
|
||||||
|
props.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button sx={{ mx: 1 }} disabled={disabled} onClick={onStart}>
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
@ -5,14 +5,17 @@ import { IPlayer } from "../../PersonObjects/IPlayer";
|
|||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { StatsTable } from "../../ui/React/StatsTable";
|
import { StatsTable } from "../../ui/React/StatsTable";
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
|
||||||
import { createPopup } from "../../ui/React/createPopup";
|
|
||||||
import { Factions } from "../../Faction/Factions";
|
import { Factions } from "../../Faction/Factions";
|
||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
import { joinFaction } from "../../Faction/FactionHelpers";
|
import { joinFaction } from "../../Faction/FactionHelpers";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
import { TravelPopup } from "./TravelPopup";
|
import { TravelModal } from "./TravelModal";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
@ -21,131 +24,141 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Stats(props: IProps): React.ReactElement {
|
export function Stats(props: IProps): React.ReactElement {
|
||||||
|
const [travelOpen, setTravelOpen] = useState(false);
|
||||||
const setRerender = useState(false)[1];
|
const setRerender = useState(false)[1];
|
||||||
|
|
||||||
|
const inFaction = props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function openStaminaHelp(): void {
|
|
||||||
dialogBoxCreate(
|
|
||||||
"Performing actions will use up your stamina.<br><br>" +
|
|
||||||
"Your max stamina is determined primarily by your agility stat.<br><br>" +
|
|
||||||
"Your stamina gain rate is determined by both your agility and your " +
|
|
||||||
"max stamina. Higher max stamina leads to a higher gain rate.<br><br>" +
|
|
||||||
"Once your " +
|
|
||||||
"stamina falls below 50% of its max value, it begins to negatively " +
|
|
||||||
"affect the success rate of your contracts/operations. This penalty " +
|
|
||||||
"is shown in the overview panel. If the penalty is 15%, then this means " +
|
|
||||||
"your success rate would be multipled by 85% (100 - 15).<br><br>" +
|
|
||||||
"Your max stamina and stamina gain rate can also be increased by " +
|
|
||||||
"training, or through skills and Augmentation upgrades.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openPopulationHelp(): void {
|
|
||||||
dialogBoxCreate(
|
|
||||||
"The success rate of your contracts/operations depends on " +
|
|
||||||
"the population of Synthoids in your current city. " +
|
|
||||||
"The success rate that is shown to you is only an estimate, " +
|
|
||||||
"and it is based on your Synthoid population estimate.<br><br>" +
|
|
||||||
"Therefore, it is important that this Synthoid population estimate " +
|
|
||||||
"is accurate so that you have a better idea of your " +
|
|
||||||
"success rate for contracts/operations. Certain " +
|
|
||||||
"actions will increase the accuracy of your population " +
|
|
||||||
"estimate.<br><br>" +
|
|
||||||
"The Synthoid populations of cities can change due to your " +
|
|
||||||
"actions or random events. If random events occur, they will " +
|
|
||||||
"be logged in the Bladeburner Console.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openTravel(): void {
|
|
||||||
const popupId = "bladeburner-travel-popup";
|
|
||||||
createPopup(popupId, TravelPopup, {
|
|
||||||
bladeburner: props.bladeburner,
|
|
||||||
popupId: popupId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function openFaction(): void {
|
function openFaction(): void {
|
||||||
|
if (!inFaction) return;
|
||||||
const faction = Factions["Bladeburners"];
|
const faction = Factions["Bladeburners"];
|
||||||
if (faction.isMember) {
|
if (!faction.isMember) {
|
||||||
props.router.toFaction(faction);
|
joinFaction(faction);
|
||||||
} else {
|
|
||||||
if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) {
|
|
||||||
joinFaction(faction);
|
|
||||||
dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction");
|
|
||||||
} else {
|
|
||||||
dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
props.router.toFaction(faction);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Paper sx={{ p: 1 }}>
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
Rank: {formatNumber(props.bladeburner.rank, 2)}
|
<Tooltip title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
||||||
<span className="tooltiptext">Your rank within the Bladeburner division.</span>
|
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
||||||
</p>
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<Box display="flex">
|
||||||
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
<Tooltip
|
||||||
</p>
|
title={
|
||||||
<div className="help-tip" onClick={openStaminaHelp}>
|
<Typography>
|
||||||
?
|
Performing actions will use up your stamina.
|
||||||
</div>
|
<br />
|
||||||
|
<br />
|
||||||
|
Your max stamina is determined primarily by your agility stat.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your stamina gain rate is determined by both your agility and your max stamina. Higher max stamina leads
|
||||||
|
to a higher gain rate.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Once your stamina falls below 50% of its max value, it begins to negatively affect the success rate of
|
||||||
|
your contracts/operations. This penalty is shown in the overview panel. If the penalty is 15%, then this
|
||||||
|
means your success rate would be multipled by 85% (100 - 15).
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Your max stamina and stamina gain rate can also be increased by training, or through skills and
|
||||||
|
Augmentation upgrades.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p>Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%</p>
|
<Typography>
|
||||||
|
Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%
|
||||||
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<p>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</p>
|
<Typography>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</Typography>
|
||||||
<p>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</p>
|
<Typography>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<p>Num Times Hospitalized: {props.bladeburner.numHosp}</p>
|
<Typography>Num Times Hospitalized: {props.bladeburner.numHosp}</Typography>
|
||||||
<p>
|
<Typography>
|
||||||
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
||||||
</p>
|
</Typography>
|
||||||
<br />
|
<br />
|
||||||
<p>Current City: {props.bladeburner.city}</p>
|
<Typography>Current City: {props.bladeburner.city}</Typography>
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
<Tooltip
|
||||||
<span className="tooltiptext">
|
title={
|
||||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city.
|
<Typography>
|
||||||
</span>
|
This is your Bladeburner division's estimate of how many Synthoids exist in your current city. An accurate
|
||||||
</p>
|
population count increases success rate estimates.
|
||||||
<div className="help-tip" onClick={openPopulationHelp}>
|
</Typography>
|
||||||
?
|
}
|
||||||
</div>
|
>
|
||||||
|
<Typography>
|
||||||
|
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
<Tooltip
|
||||||
<span className="tooltiptext">
|
title={
|
||||||
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
<Typography>
|
||||||
</span>
|
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
||||||
</p>
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>
|
||||||
|
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<Box display="flex">
|
||||||
City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}
|
<Tooltip
|
||||||
<span className="tooltiptext">
|
title={
|
||||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a chaos
|
<Typography>
|
||||||
level can make contracts and operations harder.
|
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a
|
||||||
</span>
|
chaos level can make contracts and operations harder.
|
||||||
</p>
|
</Typography>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography>City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
{(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000 > 15000 && (
|
||||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
<>
|
||||||
Bonus time:{" "}
|
<Box display="flex">
|
||||||
{convertTimeMsToTimeElapsedString(
|
<Tooltip
|
||||||
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
title={
|
||||||
)}
|
<Typography>
|
||||||
<br />
|
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by
|
||||||
<span className="tooltiptext">
|
browser). Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser).
|
</Typography>
|
||||||
Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
}
|
||||||
</span>
|
>
|
||||||
</p>
|
<Typography>
|
||||||
<p>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</p>
|
Bonus time:{" "}
|
||||||
|
{convertTimeMsToTimeElapsedString(
|
||||||
|
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
||||||
<br />
|
<br />
|
||||||
<StatsTable
|
<StatsTable
|
||||||
rows={[
|
rows={[
|
||||||
@ -156,17 +169,15 @@ export function Stats(props: IProps): React.ReactElement {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<a onClick={openTravel} className="a-link-button" style={{ display: "inline-block" }}>
|
<Button onClick={() => setTravelOpen(true)}>Travel</Button>
|
||||||
Travel
|
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
||||||
</a>
|
<span>
|
||||||
<a onClick={openFaction} className="a-link-button tooltip" style={{ display: "inline-block" }}>
|
<Button disabled={!inFaction} onClick={openFaction}>
|
||||||
<span className="tooltiptext">
|
Faction
|
||||||
Apply to the Bladeburner Faction, or go to the faction page if you are already a member
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
Faction
|
</Tooltip>
|
||||||
</a>
|
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
||||||
<br />
|
</Paper>
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
9
src/Bladeburner/ui/StealthIcon.tsx
Normal file
9
src/Bladeburner/ui/StealthIcon.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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 title={<Typography>This action involves stealth</Typography>}>{stealthIcon}</Tooltip>;
|
||||||
|
}
|
@ -1,18 +1,33 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
|
import { StealthIcon } from "./StealthIcon";
|
||||||
|
import { KillIcon } from "./KillIcon";
|
||||||
|
import { IAction } from "../IAction";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
chance: number[];
|
bladeburner: IBladeburner;
|
||||||
|
action: IAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SuccessChance(props: IProps): React.ReactElement {
|
export function SuccessChance(props: IProps): React.ReactElement {
|
||||||
if (props.chance[0] === props.chance[1]) {
|
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||||
return <>{formatNumber(props.chance[0] * 100, 1)}%</>;
|
|
||||||
|
let chance = <></>;
|
||||||
|
if (estimatedSuccessChance[0] === estimatedSuccessChance[1]) {
|
||||||
|
chance = <>{formatNumber(estimatedSuccessChance[0] * 100, 1)}%</>;
|
||||||
|
} else {
|
||||||
|
chance = (
|
||||||
|
<>
|
||||||
|
{formatNumber(estimatedSuccessChance[0] * 100, 1)}% ~ {formatNumber(estimatedSuccessChance[1] * 100, 1)}%
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{formatNumber(props.chance[0] * 100, 1)}% ~ {formatNumber(props.chance[1] * 100, 1)}%
|
Estimated success chance: {chance} {props.action.isStealth ? <StealthIcon /> : <></>}
|
||||||
|
{props.action.isKill ? <KillIcon /> : <></>}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
22
src/Bladeburner/ui/TeamSizeButton.tsx
Normal file
22
src/Bladeburner/ui/TeamSizeButton.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Operation } from "../Operation";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { TeamSizeModal } from "./TeamSizeModal";
|
||||||
|
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
interface IProps {
|
||||||
|
action: Operation;
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
}
|
||||||
|
export function TeamSizeButton(props: IProps): React.ReactElement {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button disabled={props.bladeburner.teamSize === 0} onClick={() => setOpen(true)}>
|
||||||
|
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
||||||
|
</Button>
|
||||||
|
<TeamSizeModal open={open} onClose={() => setOpen(false)} action={props.action} bladeburner={props.bladeburner} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,16 +1,20 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
import { Action } from "../Action";
|
import { Action } from "../Action";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
bladeburner: IBladeburner;
|
bladeburner: IBladeburner;
|
||||||
action: Action;
|
action: Action;
|
||||||
popupId: string;
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TeamSizePopup(props: IProps): React.ReactElement {
|
export function TeamSizeModal(props: IProps): React.ReactElement {
|
||||||
const [teamSize, setTeamSize] = useState<number | undefined>();
|
const [teamSize, setTeamSize] = useState<number | undefined>();
|
||||||
|
|
||||||
function confirmTeamSize(): void {
|
function confirmTeamSize(): void {
|
||||||
@ -21,25 +25,25 @@ export function TeamSizePopup(props: IProps): React.ReactElement {
|
|||||||
} else {
|
} else {
|
||||||
props.action.teamCount = num;
|
props.action.teamCount = num;
|
||||||
}
|
}
|
||||||
removePopup(props.popupId);
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTeamSize(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
const x = parseFloat(event.target.value);
|
||||||
|
if (x > props.bladeburner.teamSize) setTeamSize(props.bladeburner.teamSize);
|
||||||
|
else setTeamSize(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
<p>
|
<Typography>
|
||||||
Enter the amount of team members you would like to take on this Op. If you do not have the specified number of
|
Enter the amount of team members you would like to take on this Op. If you do not have the specified number of
|
||||||
team members, then as many as possible will be used. Note that team members may be lost during operations.
|
team members, then as many as possible will be used. Note that team members may be lost during operations.
|
||||||
</p>
|
</Typography>
|
||||||
<input
|
<TextField autoFocus type="number" placeholder="Team size" value={teamSize} onChange={onTeamSize} />
|
||||||
autoFocus
|
<Button sx={{ mx: 2 }} onClick={confirmTeamSize}>
|
||||||
type="number"
|
|
||||||
placeholder="Team size"
|
|
||||||
className="text-input"
|
|
||||||
onChange={(event) => setTeamSize(parseFloat(event.target.value))}
|
|
||||||
/>
|
|
||||||
<a className="a-link-button" onClick={confirmTeamSize}>
|
|
||||||
Confirm
|
Confirm
|
||||||
</a>
|
</Button>
|
||||||
</>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
41
src/Bladeburner/ui/TravelModal.tsx
Normal file
41
src/Bladeburner/ui/TravelModal.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { WorldMap } from "../../ui/React/WorldMap";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import { CityName } from "../../Locations/data/CityNames";
|
||||||
|
import { Settings } from "../../Settings/Settings";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
bladeburner: IBladeburner;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TravelModal(props: IProps): React.ReactElement {
|
||||||
|
function travel(city: string): void {
|
||||||
|
props.bladeburner.city = city;
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<>
|
||||||
|
<Typography>
|
||||||
|
Travel to a different city for your Bladeburner activities. This does not cost any money. The city you are in
|
||||||
|
for your Bladeburner duties does not affect your location in the game otherwise.
|
||||||
|
</Typography>
|
||||||
|
{Settings.DisableASCIIArt ? (
|
||||||
|
Object.values(CityName).map((city: CityName) => (
|
||||||
|
<Button key={city} onClick={() => travel(city)}>
|
||||||
|
{city}
|
||||||
|
</Button>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<WorldMap currentCity={props.bladeburner.city as CityName} onTravel={(city: CityName) => travel(city)} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
import { IBladeburner } from "../IBladeburner";
|
|
||||||
import { WorldMap } from "../../ui/React/WorldMap";
|
|
||||||
import { CityName } from "../../Locations/data/CityNames";
|
|
||||||
import { Settings } from "../../Settings/Settings";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
bladeburner: IBladeburner;
|
|
||||||
popupId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TravelPopup(props: IProps): React.ReactElement {
|
|
||||||
function travel(city: string): void {
|
|
||||||
props.bladeburner.city = city;
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
Travel to a different city for your Bladeburner activities. This does not cost any money. The city you are in
|
|
||||||
for your Bladeburner duties does not affect your location in the game otherwise.
|
|
||||||
</p>
|
|
||||||
{Settings.DisableASCIIArt ? (
|
|
||||||
Object.values(CityName).map((city: CityName) => (
|
|
||||||
<button key={city} className="std-button" onClick={() => travel(city)}>
|
|
||||||
{city}
|
|
||||||
</button>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<WorldMap currentCity={props.bladeburner.city as CityName} onTravel={(city: CityName) => travel(city)} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -7,9 +7,11 @@ import { Deck } from "./CardDeck/Deck";
|
|||||||
import { Hand } from "./CardDeck/Hand";
|
import { Hand } from "./CardDeck/Hand";
|
||||||
import { InputAdornment } from "@mui/material";
|
import { InputAdornment } from "@mui/material";
|
||||||
import { ReactCard } from "./CardDeck/ReactCard";
|
import { ReactCard } from "./CardDeck/ReactCard";
|
||||||
import { MuiTextField } from "../ui/React/MuiTextField";
|
import Button from "@mui/material/Button";
|
||||||
import { MuiButton } from "../ui/React/MuiButton";
|
import Paper from "@mui/material/Paper";
|
||||||
import { MuiPaper } from "../ui/React/MuiPaper";
|
import Box from "@mui/material/Box";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
|
||||||
const MAX_BET = 100e6;
|
const MAX_BET = 100e6;
|
||||||
|
|
||||||
@ -305,10 +307,10 @@ export class Blackjack extends Game<Props, State> {
|
|||||||
const dealerHandValues = this.getHandDisplayValues(dealerHand);
|
const dealerHandValues = this.getHandDisplayValues(dealerHand);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{/* Wager input */}
|
{/* Wager input */}
|
||||||
<div>
|
<Box>
|
||||||
<MuiTextField
|
<TextField
|
||||||
value={betInput}
|
value={betInput}
|
||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
@ -322,93 +324,88 @@ export class Blackjack extends Game<Props, State> {
|
|||||||
error={wagerInvalid}
|
error={wagerInvalid}
|
||||||
helperText={wagerInvalid ? wagerInvalidHelperText : ""}
|
helperText={wagerInvalid ? wagerInvalidHelperText : ""}
|
||||||
type="number"
|
type="number"
|
||||||
variant="filled"
|
|
||||||
style={{
|
style={{
|
||||||
width: "200px",
|
width: "200px",
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: <InputAdornment position="start">$</InputAdornment>,
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<Typography>$</Typography>
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p>
|
<Typography>
|
||||||
{"Total earnings this session: "}
|
{"Total earnings this session: "}
|
||||||
<Money money={gains} />
|
<Money money={gains} />
|
||||||
</p>
|
</Typography>
|
||||||
</div>
|
</Box>
|
||||||
|
|
||||||
{/* Buttons */}
|
{/* Buttons */}
|
||||||
{!gameInProgress ? (
|
{!gameInProgress ? (
|
||||||
<div>
|
<Button onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
|
||||||
<MuiButton onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
|
Start
|
||||||
Start
|
</Button>
|
||||||
</MuiButton>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<>
|
||||||
<MuiButton onClick={this.playerHit}>Hit</MuiButton>
|
<Button onClick={this.playerHit}>Hit</Button>
|
||||||
<MuiButton color="secondary" onClick={this.playerStay}>
|
<Button color="secondary" onClick={this.playerStay}>
|
||||||
Stay
|
Stay
|
||||||
</MuiButton>
|
</Button>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Main game part. Displays both if the game is in progress OR if there's a result so you can see
|
{/* Main game part. Displays both if the game is in progress OR if there's a result so you can see
|
||||||
* the cards that led to that result. */}
|
* the cards that led to that result. */}
|
||||||
{(gameInProgress || result !== Result.Pending) && (
|
{(gameInProgress || result !== Result.Pending) && (
|
||||||
<div>
|
<>
|
||||||
<MuiPaper variant="outlined" elevation={2}>
|
<Box display="flex">
|
||||||
<pre>Player</pre>
|
<Paper elevation={2}>
|
||||||
{playerHand.cards.map((card, i) => (
|
<pre>Player</pre>
|
||||||
<ReactCard card={card} key={i} />
|
{playerHand.cards.map((card, i) => (
|
||||||
))}
|
<ReactCard card={card} key={i} />
|
||||||
|
))}
|
||||||
|
|
||||||
<pre>Value(s): </pre>
|
<pre>Value(s): </pre>
|
||||||
{playerHandValues.map((value, i) => (
|
{playerHandValues.map((value, i) => (
|
||||||
<pre key={i}>{value}</pre>
|
<pre key={i}>{value}</pre>
|
||||||
))}
|
))}
|
||||||
</MuiPaper>
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<MuiPaper variant="outlined" elevation={2}>
|
<Box display="flex">
|
||||||
<pre>Dealer</pre>
|
<Paper elevation={2}>
|
||||||
{dealerHand.cards.map((card, i) => (
|
<pre>Dealer</pre>
|
||||||
// Hide every card except the first while game is in progress
|
{dealerHand.cards.map((card, i) => (
|
||||||
<ReactCard card={card} hidden={gameInProgress && i !== 0} key={i} />
|
// Hide every card except the first while game is in progress
|
||||||
))}
|
<ReactCard card={card} hidden={gameInProgress && i !== 0} key={i} />
|
||||||
|
))}
|
||||||
|
|
||||||
{!gameInProgress && (
|
{!gameInProgress && (
|
||||||
<>
|
<>
|
||||||
<pre>Value(s): </pre>
|
<pre>Value(s): </pre>
|
||||||
{dealerHandValues.map((value, i) => (
|
{dealerHandValues.map((value, i) => (
|
||||||
<pre key={i}>{value}</pre>
|
<pre key={i}>{value}</pre>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</MuiPaper>
|
</Paper>
|
||||||
</div>
|
</Box>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Results from previous round */}
|
{/* Results from previous round */}
|
||||||
{result !== Result.Pending && (
|
{result !== Result.Pending && (
|
||||||
<p>
|
<Typography>
|
||||||
{result}
|
{result}
|
||||||
{this.isPlayerWinResult(result) && (
|
{this.isPlayerWinResult(result) && <Money money={this.state.bet} />}
|
||||||
<>
|
{result === Result.DealerWon && <Money money={this.state.bet} />}
|
||||||
{" You gained "}
|
</Typography>
|
||||||
<Money money={this.state.bet} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{result === Result.DealerWon && (
|
|
||||||
<>
|
|
||||||
{" You lost "}
|
|
||||||
<Money money={this.state.bet} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,44 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { Card, Suit } from "./Card";
|
import { Card, Suit } from "./Card";
|
||||||
|
|
||||||
|
import makeStyles from "@mui/styles/makeStyles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
card: Card;
|
card: Card;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useStyles = makeStyles(() =>
|
||||||
|
createStyles({
|
||||||
|
card: {
|
||||||
|
padding: "10px",
|
||||||
|
border: "solid 1px #808080",
|
||||||
|
backgroundColor: "white",
|
||||||
|
display: "inline-block",
|
||||||
|
borderRadius: "10px",
|
||||||
|
fontSize: "18.5px",
|
||||||
|
textAlign: "center",
|
||||||
|
margin: "3px",
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
red: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
|
||||||
|
black: {
|
||||||
|
color: "black",
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
fontSize: "20px",
|
||||||
|
fontFamily: "sans-serif",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const ReactCard: FC<Props> = ({ card, hidden }) => {
|
export const ReactCard: FC<Props> = ({ card, hidden }) => {
|
||||||
|
const classes = useStyles();
|
||||||
let suit: React.ReactNode;
|
let suit: React.ReactNode;
|
||||||
switch (card.suit) {
|
switch (card.suit) {
|
||||||
case Suit.Clubs:
|
case Suit.Clubs:
|
||||||
@ -25,11 +57,11 @@ export const ReactCard: FC<Props> = ({ card, hidden }) => {
|
|||||||
throw new Error(`MissingCaseException: ${card.suit}`);
|
throw new Error(`MissingCaseException: ${card.suit}`);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={`casino-card ${card.isRedSuit() ? "red" : "black"}`}>
|
<Paper className={`${classes.card} ${card.isRedSuit() ? classes.red : classes.black}`}>
|
||||||
<>
|
<>
|
||||||
<div className="value">{hidden ? " - " : card.formatValue()}</div>
|
<span className={classes.value}>{hidden ? " - " : card.formatValue()}</span>
|
||||||
<div className={`suit`}>{hidden ? " - " : suit}</div>
|
<span>{hidden ? " - " : suit}</span>
|
||||||
</>
|
</>
|
||||||
</div>
|
</Paper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,44 +3,32 @@
|
|||||||
*
|
*
|
||||||
* This subcomponent renders all of the buttons for training at the gym
|
* This subcomponent renders all of the buttons for training at the gym
|
||||||
*/
|
*/
|
||||||
import * as React from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { StdButton } from "../ui/React/StdButton";
|
|
||||||
import { BadRNG } from "./RNG";
|
import { BadRNG } from "./RNG";
|
||||||
import { Game } from "./Game";
|
import { win, reachedLimit } from "./Game";
|
||||||
import { trusted } from "./utils";
|
import { trusted } from "./utils";
|
||||||
|
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
p: IPlayer;
|
p: IPlayer;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IState = {
|
|
||||||
investment: number;
|
|
||||||
result: any;
|
|
||||||
status: string;
|
|
||||||
playLock: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const minPlay = 0;
|
const minPlay = 0;
|
||||||
const maxPlay = 10e3;
|
const maxPlay = 10e3;
|
||||||
|
|
||||||
export class CoinFlip extends Game<IProps, IState> {
|
export function CoinFlip(props: IProps): React.ReactElement {
|
||||||
constructor(props: IProps) {
|
const [investment, setInvestment] = useState(1000);
|
||||||
super(props);
|
const [result, setResult] = useState(<span> </span>);
|
||||||
|
const [status, setStatus] = useState("");
|
||||||
|
const [playLock, setPlayLock] = useState(false);
|
||||||
|
|
||||||
this.state = {
|
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
investment: 1000,
|
|
||||||
result: <span> </span>,
|
|
||||||
status: "",
|
|
||||||
playLock: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.play = this.play.bind(this);
|
|
||||||
this.updateInvestment = this.updateInvestment.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
|
|
||||||
let investment: number = parseInt(e.currentTarget.value);
|
let investment: number = parseInt(e.currentTarget.value);
|
||||||
if (isNaN(investment)) {
|
if (isNaN(investment)) {
|
||||||
investment = minPlay;
|
investment = minPlay;
|
||||||
@ -51,11 +39,11 @@ export class CoinFlip extends Game<IProps, IState> {
|
|||||||
if (investment < minPlay) {
|
if (investment < minPlay) {
|
||||||
investment = minPlay;
|
investment = minPlay;
|
||||||
}
|
}
|
||||||
this.setState({ investment: investment });
|
setInvestment(investment);
|
||||||
}
|
}
|
||||||
|
|
||||||
play(guess: string): void {
|
function play(guess: string): void {
|
||||||
if (this.reachedLimit(this.props.p)) return;
|
if (reachedLimit(props.p)) return;
|
||||||
const v = BadRNG.random();
|
const v = BadRNG.random();
|
||||||
let letter: string;
|
let letter: string;
|
||||||
if (v < 0.5) {
|
if (v < 0.5) {
|
||||||
@ -64,39 +52,48 @@ export class CoinFlip extends Game<IProps, IState> {
|
|||||||
letter = "T";
|
letter = "T";
|
||||||
}
|
}
|
||||||
const correct: boolean = guess === letter;
|
const correct: boolean = guess === letter;
|
||||||
this.setState({
|
|
||||||
result: <span className={correct ? "text" : "failure"}>{letter}</span>,
|
setResult(
|
||||||
status: correct ? " win!" : "lose!",
|
<Box display="flex">
|
||||||
playLock: true,
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }} color={correct ? "primary" : "error"}>
|
||||||
});
|
{letter}
|
||||||
setTimeout(() => this.setState({ playLock: false }), 250);
|
</Typography>
|
||||||
|
</Box>,
|
||||||
|
);
|
||||||
|
setStatus(correct ? " win!" : "lose!");
|
||||||
|
setPlayLock(true);
|
||||||
|
|
||||||
|
setTimeout(() => setPlayLock(false), 250);
|
||||||
if (correct) {
|
if (correct) {
|
||||||
this.win(this.props.p, this.state.investment);
|
win(props.p, investment);
|
||||||
} else {
|
} else {
|
||||||
this.win(this.props.p, -this.state.investment);
|
win(props.p, -investment);
|
||||||
}
|
}
|
||||||
if (this.reachedLimit(this.props.p)) return;
|
if (reachedLimit(props.p)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): React.ReactNode {
|
return (
|
||||||
return (
|
<>
|
||||||
<>
|
<Typography>Result:</Typography> {result}
|
||||||
<pre>{`+———————+`}</pre>
|
<Box display="flex" alignItems="center">
|
||||||
<pre>{`| | | |`}</pre>
|
<TextField
|
||||||
<pre>
|
type="number"
|
||||||
{`| | `}
|
onChange={updateInvestment}
|
||||||
{this.state.result}
|
InputProps={{
|
||||||
{` | |`}
|
endAdornment: (
|
||||||
</pre>
|
<>
|
||||||
<pre>{`| | | |`}</pre>
|
<Button onClick={trusted(() => play("H"))} disabled={playLock}>
|
||||||
<pre>{`+———————+`}</pre>
|
Head!
|
||||||
<span className="text">Play for: </span>
|
</Button>
|
||||||
<input type="number" className="text-input" onChange={this.updateInvestment} value={this.state.investment} />
|
<Button onClick={trusted(() => play("T"))} disabled={playLock}>
|
||||||
<br />
|
Tail!
|
||||||
<StdButton onClick={trusted(() => this.play("H"))} text={"Head!"} disabled={this.state.playLock} />
|
</Button>
|
||||||
<StdButton onClick={trusted(() => this.play("T"))} text={"Tail!"} disabled={this.state.playLock} />
|
</>
|
||||||
<h1>{this.state.status}</h1>
|
),
|
||||||
</>
|
}}
|
||||||
);
|
/>
|
||||||
}
|
</Box>
|
||||||
|
<Typography variant="h3">{status}</Typography>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,19 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
|
|||||||
|
|
||||||
const gainLimit = 10e9;
|
const gainLimit = 10e9;
|
||||||
|
|
||||||
|
export function win(p: IPlayer, n: number): void {
|
||||||
|
p.gainMoney(n);
|
||||||
|
p.recordMoneySource(n, "casino");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reachedLimit(p: IPlayer): boolean {
|
||||||
|
const reached = p.getCasinoWinnings() > gainLimit;
|
||||||
|
if (reached) {
|
||||||
|
dialogBoxCreate(<>Alright cheater get out of here. You're not allowed here anymore.</>);
|
||||||
|
}
|
||||||
|
return reached;
|
||||||
|
}
|
||||||
|
|
||||||
export class Game<T, U> extends React.Component<T, U> {
|
export class Game<T, U> extends React.Component<T, U> {
|
||||||
win(p: IPlayer, n: number): void {
|
win(p: IPlayer, n: number): void {
|
||||||
p.gainMoney(n);
|
p.gainMoney(n);
|
||||||
|
@ -1,25 +1,18 @@
|
|||||||
import * as React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { StdButton } from "../ui/React/StdButton";
|
|
||||||
import { Money } from "../ui/React/Money";
|
import { Money } from "../ui/React/Money";
|
||||||
import { Game } from "./Game";
|
import { win, reachedLimit } from "./Game";
|
||||||
import { WHRNG } from "./RNG";
|
import { WHRNG } from "./RNG";
|
||||||
import { trusted } from "./utils";
|
import { trusted } from "./utils";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
p: IPlayer;
|
p: IPlayer;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IState = {
|
|
||||||
investment: number;
|
|
||||||
canPlay: boolean;
|
|
||||||
status: string | JSX.Element;
|
|
||||||
n: number;
|
|
||||||
lock: boolean;
|
|
||||||
strategy: Strategy;
|
|
||||||
};
|
|
||||||
|
|
||||||
const minPlay = 0;
|
const minPlay = 0;
|
||||||
const maxPlay = 1e7;
|
const maxPlay = 1e7;
|
||||||
|
|
||||||
@ -118,48 +111,32 @@ function Single(s: number): Strategy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Roulette extends Game<IProps, IState> {
|
export function Roulette(props: IProps): React.ReactElement {
|
||||||
interval = -1;
|
const [rng] = useState(new WHRNG(new Date().getTime()));
|
||||||
rng: WHRNG;
|
const [investment, setInvestment] = useState(1000);
|
||||||
|
const [canPlay, setCanPlay] = useState(true);
|
||||||
|
const [status, setStatus] = useState<string | JSX.Element>("waiting");
|
||||||
|
const [n, setN] = useState(0);
|
||||||
|
const [lock, setLock] = useState(true);
|
||||||
|
const [strategy, setStrategy] = useState<Strategy>({
|
||||||
|
payout: 0,
|
||||||
|
match: (): boolean => {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
constructor(props: IProps) {
|
useEffect(() => {
|
||||||
super(props);
|
const i = window.setInterval(step, 50);
|
||||||
|
return () => clearInterval(i);
|
||||||
|
});
|
||||||
|
|
||||||
this.rng = new WHRNG(new Date().getTime());
|
function step(): void {
|
||||||
this.state = {
|
if (!lock) {
|
||||||
investment: 1000,
|
setN(Math.floor(Math.random() * 37));
|
||||||
canPlay: true,
|
|
||||||
status: "waiting",
|
|
||||||
n: 0,
|
|
||||||
lock: true,
|
|
||||||
strategy: {
|
|
||||||
payout: 0,
|
|
||||||
match: (): boolean => {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.step = this.step.bind(this);
|
|
||||||
this.currentNumber = this.currentNumber.bind(this);
|
|
||||||
this.updateInvestment = this.updateInvestment.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount(): void {
|
|
||||||
this.interval = window.setInterval(this.step, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
step(): void {
|
|
||||||
if (!this.state.lock) {
|
|
||||||
this.setState({ n: Math.floor(Math.random() * 37) });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(): void {
|
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
clearInterval(this.interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
|
|
||||||
let investment: number = parseInt(e.currentTarget.value);
|
let investment: number = parseInt(e.currentTarget.value);
|
||||||
if (isNaN(investment)) {
|
if (isNaN(investment)) {
|
||||||
investment = minPlay;
|
investment = minPlay;
|
||||||
@ -170,265 +147,312 @@ export class Roulette extends Game<IProps, IState> {
|
|||||||
if (investment < minPlay) {
|
if (investment < minPlay) {
|
||||||
investment = minPlay;
|
investment = minPlay;
|
||||||
}
|
}
|
||||||
this.setState({ investment: investment });
|
setInvestment(investment);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentNumber(): string {
|
function currentNumber(): string {
|
||||||
if (this.state.n === 0) return "0";
|
if (n === 0) return "0";
|
||||||
const color = isRed(this.state.n) ? "R" : "B";
|
const color = isRed(n) ? "R" : "B";
|
||||||
return `${this.state.n}${color}`;
|
return `${n}${color}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
play(s: Strategy): void {
|
function play(s: Strategy): void {
|
||||||
if (this.reachedLimit(this.props.p)) return;
|
if (reachedLimit(props.p)) return;
|
||||||
this.setState({
|
|
||||||
canPlay: false,
|
setCanPlay(false);
|
||||||
lock: false,
|
setLock(false);
|
||||||
status: "playing",
|
setStatus("playing");
|
||||||
strategy: s,
|
setStrategy(s);
|
||||||
});
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let n = Math.floor(this.rng.random() * 37);
|
let n = Math.floor(rng.random() * 37);
|
||||||
let status = <></>;
|
let status = <></>;
|
||||||
let gain = 0;
|
let gain = 0;
|
||||||
let playerWin = this.state.strategy.match(n);
|
let playerWin = strategy.match(n);
|
||||||
// oh yeah, the house straight up cheats. Try finding the seed now!
|
// oh yeah, the house straight up cheats. Try finding the seed now!
|
||||||
if (playerWin && Math.random() > 0.9) {
|
if (playerWin && Math.random() > 0.9) {
|
||||||
playerWin = false;
|
playerWin = false;
|
||||||
while (this.state.strategy.match(n)) {
|
while (strategy.match(n)) {
|
||||||
n = (n + 1) % 36;
|
n = (n + 1) % 36;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (playerWin) {
|
if (playerWin) {
|
||||||
gain = this.state.investment * this.state.strategy.payout;
|
gain = investment * strategy.payout;
|
||||||
status = (
|
status = (
|
||||||
<>
|
<>
|
||||||
won <Money money={gain} />
|
won <Money money={gain} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
gain = -this.state.investment;
|
gain = -investment;
|
||||||
status = (
|
status = (
|
||||||
<>
|
<>
|
||||||
lost <Money money={-gain} />
|
lost <Money money={-gain} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.win(this.props.p, gain);
|
win(props.p, gain);
|
||||||
this.setState({
|
|
||||||
canPlay: true,
|
setCanPlay(true);
|
||||||
lock: true,
|
setLock(true);
|
||||||
status: status,
|
setStatus(status);
|
||||||
n: n,
|
setN(n);
|
||||||
});
|
|
||||||
this.reachedLimit(this.props.p);
|
reachedLimit(props.p);
|
||||||
}, 1600);
|
}, 1600);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): React.ReactNode {
|
return (
|
||||||
return (
|
<>
|
||||||
<>
|
<Typography variant="h4">{currentNumber()}</Typography>
|
||||||
<h1>{this.currentNumber()}</h1>
|
<TextField type="number" onChange={updateInvestment} placeholder={"Amount to play"} disabled={!canPlay} />
|
||||||
<input
|
<Typography variant="h4">{status}</Typography>
|
||||||
type="number"
|
<table>
|
||||||
className="text-input"
|
<tbody>
|
||||||
onChange={this.updateInvestment}
|
<tr>
|
||||||
placeholder={"Amount to play"}
|
<td>
|
||||||
value={this.state.investment}
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(3)))}>
|
||||||
disabled={!this.state.canPlay}
|
3
|
||||||
/>
|
</Button>
|
||||||
<h1>{this.state.status}</h1>
|
</td>
|
||||||
<table>
|
<td>
|
||||||
<tbody>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(6)))}>
|
||||||
<tr>
|
6
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"3"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(3)))} />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(9)))}>
|
||||||
<StdButton text={"6"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(6)))} />
|
9
|
||||||
</td>
|
</Button>
|
||||||
<td>
|
</td>
|
||||||
<StdButton text={"9"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(9)))} />
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(12)))}>
|
||||||
<td>
|
12
|
||||||
<StdButton text={"12"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(12)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<StdButton text={"15"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(15)))} />
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(15)))}>
|
||||||
</td>
|
15
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"18"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(18)))} />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(18)))}>
|
||||||
<StdButton text={"21"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(21)))} />
|
18
|
||||||
</td>
|
</Button>
|
||||||
<td>
|
</td>
|
||||||
<StdButton text={"24"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(24)))} />
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(21)))}>
|
||||||
<td>
|
21
|
||||||
<StdButton text={"27"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(27)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<StdButton text={"30"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(30)))} />
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(24)))}>
|
||||||
</td>
|
24
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"33"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(33)))} />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(27)))}>
|
||||||
<StdButton text={"36"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(36)))} />
|
27
|
||||||
</td>
|
</Button>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
<td>
|
||||||
<td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(30)))}>
|
||||||
<StdButton text={"2"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(2)))} />
|
30
|
||||||
</td>
|
</Button>
|
||||||
<td>
|
</td>
|
||||||
<StdButton text={"5"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(5)))} />
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(33)))}>
|
||||||
<td>
|
33
|
||||||
<StdButton text={"8"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(8)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<StdButton text={"11"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(11)))} />
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(36)))}>
|
||||||
</td>
|
36
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"14"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(14)))} />
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<StdButton text={"17"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(17)))} />
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(2)))}>
|
||||||
<td>
|
2
|
||||||
<StdButton text={"20"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(20)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<StdButton text={"23"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(23)))} />
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(5)))}>
|
||||||
</td>
|
5
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"26"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(26)))} />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(8)))}>
|
||||||
<StdButton text={"29"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(29)))} />
|
8
|
||||||
</td>
|
</Button>
|
||||||
<td>
|
</td>
|
||||||
<StdButton text={"32"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(32)))} />
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(11)))}>
|
||||||
<td>
|
11
|
||||||
<StdButton text={"35"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(35)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(14)))}>
|
||||||
<td>
|
14
|
||||||
<StdButton text={"1"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(1)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<StdButton text={"4"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(4)))} />
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(17)))}>
|
||||||
</td>
|
17
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"7"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(7)))} />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(20)))}>
|
||||||
<StdButton text={"10"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(10)))} />
|
20
|
||||||
</td>
|
</Button>
|
||||||
<td>
|
</td>
|
||||||
<StdButton text={"13"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(13)))} />
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(23)))}>
|
||||||
<td>
|
23
|
||||||
<StdButton text={"16"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(16)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<StdButton text={"19"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(19)))} />
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(26)))}>
|
||||||
</td>
|
26
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"22"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(22)))} />
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(29)))}>
|
||||||
<StdButton text={"25"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(25)))} />
|
29
|
||||||
</td>
|
</Button>
|
||||||
<td>
|
</td>
|
||||||
<StdButton text={"28"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(28)))} />
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(32)))}>
|
||||||
<td>
|
32
|
||||||
<StdButton text={"31"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(31)))} />
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<StdButton text={"34"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(34)))} />
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(35)))}>
|
||||||
</td>
|
35
|
||||||
</tr>
|
</Button>
|
||||||
<tr>
|
</td>
|
||||||
<td colSpan={4}>
|
</tr>
|
||||||
<StdButton
|
<tr>
|
||||||
text={"1 to 12"}
|
<td>
|
||||||
disabled={!this.state.canPlay}
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(1)))}>
|
||||||
onClick={trusted(() => this.play(strategies.Third1))}
|
1
|
||||||
/>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td colSpan={4}>
|
<td>
|
||||||
<StdButton
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(4)))}>
|
||||||
text={"13 to 24"}
|
4
|
||||||
disabled={!this.state.canPlay}
|
</Button>
|
||||||
onClick={trusted(() => this.play(strategies.Third2))}
|
</td>
|
||||||
/>
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(7)))}>
|
||||||
<td colSpan={4}>
|
7
|
||||||
<StdButton
|
</Button>
|
||||||
text={"25 to 36"}
|
</td>
|
||||||
disabled={!this.state.canPlay}
|
<td>
|
||||||
onClick={trusted(() => this.play(strategies.Third3))}
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(10)))}>
|
||||||
/>
|
10
|
||||||
</td>
|
</Button>
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
<td>
|
||||||
<td colSpan={2}>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(13)))}>
|
||||||
<StdButton
|
13
|
||||||
text={"Red"}
|
</Button>
|
||||||
disabled={!this.state.canPlay}
|
</td>
|
||||||
onClick={trusted(() => this.play(strategies.Red))}
|
<td>
|
||||||
/>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(16)))}>
|
||||||
</td>
|
16
|
||||||
<td colSpan={2}>
|
</Button>
|
||||||
<StdButton
|
</td>
|
||||||
text={"Black"}
|
<td>
|
||||||
disabled={!this.state.canPlay}
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(19)))}>
|
||||||
onClick={trusted(() => this.play(strategies.Black))}
|
19
|
||||||
/>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
<td colSpan={2}>
|
<td>
|
||||||
<StdButton
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(22)))}>
|
||||||
text={"Odd"}
|
22
|
||||||
disabled={!this.state.canPlay}
|
</Button>
|
||||||
onClick={trusted(() => this.play(strategies.Odd))}
|
</td>
|
||||||
/>
|
<td>
|
||||||
</td>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(25)))}>
|
||||||
<td colSpan={2}>
|
25
|
||||||
<StdButton
|
</Button>
|
||||||
text={"Even"}
|
</td>
|
||||||
disabled={!this.state.canPlay}
|
<td>
|
||||||
onClick={trusted(() => this.play(strategies.Even))}
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(28)))}>
|
||||||
/>
|
28
|
||||||
</td>
|
</Button>
|
||||||
<td colSpan={2}>
|
</td>
|
||||||
<StdButton
|
<td>
|
||||||
text={"High"}
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(31)))}>
|
||||||
disabled={!this.state.canPlay}
|
31
|
||||||
onClick={trusted(() => this.play(strategies.High))}
|
</Button>
|
||||||
/>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td colSpan={2}>
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(34)))}>
|
||||||
<StdButton
|
34
|
||||||
text={"Low"}
|
</Button>
|
||||||
disabled={!this.state.canPlay}
|
</td>
|
||||||
onClick={trusted(() => this.play(strategies.Low))}
|
</tr>
|
||||||
/>
|
<tr>
|
||||||
</td>
|
<td colSpan={4}>
|
||||||
</tr>
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third1))}>
|
||||||
<tr>
|
1 to 12
|
||||||
<td>
|
</Button>
|
||||||
<StdButton text={"0"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(0)))} />
|
</td>
|
||||||
</td>
|
<td colSpan={4}>
|
||||||
</tr>
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third2))}>
|
||||||
</tbody>
|
13 to 24
|
||||||
</table>
|
</Button>
|
||||||
</>
|
</td>
|
||||||
);
|
<td colSpan={4}>
|
||||||
}
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third3))}>
|
||||||
|
25 to 36
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Red))}>
|
||||||
|
Red
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Black))}>
|
||||||
|
Black
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Odd))}>
|
||||||
|
Odd
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Even))}>
|
||||||
|
Even
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.High))}>
|
||||||
|
High
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
<td colSpan={2}>
|
||||||
|
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Low))}>
|
||||||
|
Low
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Button disabled={!canPlay} onClick={trusted(() => play(Single(0)))}>
|
||||||
|
0
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,18 @@
|
|||||||
import * as React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||||
import { StdButton } from "../ui/React/StdButton";
|
|
||||||
import { Money } from "../ui/React/Money";
|
import { Money } from "../ui/React/Money";
|
||||||
import { WHRNG } from "./RNG";
|
import { WHRNG } from "./RNG";
|
||||||
import { Game } from "./Game";
|
import { win, reachedLimit } from "./Game";
|
||||||
import { trusted } from "./utils";
|
import { trusted } from "./utils";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import TextField from "@mui/material/TextField";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
p: IPlayer;
|
p: IPlayer;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IState = {
|
|
||||||
index: number[];
|
|
||||||
locks: number[];
|
|
||||||
investment: number;
|
|
||||||
canPlay: boolean;
|
|
||||||
status: string | JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
// statically shuffled array of symbols.
|
// statically shuffled array of symbols.
|
||||||
const symbols = [
|
const symbols = [
|
||||||
"D",
|
"D",
|
||||||
@ -147,104 +141,76 @@ const payLines = [
|
|||||||
const minPlay = 0;
|
const minPlay = 0;
|
||||||
const maxPlay = 1e6;
|
const maxPlay = 1e6;
|
||||||
|
|
||||||
export class SlotMachine extends Game<IProps, IState> {
|
export function SlotMachine(props: IProps): React.ReactElement {
|
||||||
rng: WHRNG;
|
const [rng] = useState(new WHRNG(props.p.totalPlaytime));
|
||||||
interval = -1;
|
const [index, setIndex] = useState<number[]>([0, 0, 0, 0, 0]);
|
||||||
|
const [locks, setLocks] = useState<number[]>([0, 0, 0, 0, 0]);
|
||||||
|
const [investment, setInvestment] = useState(1000);
|
||||||
|
const [canPlay, setCanPlay] = useState(true);
|
||||||
|
const [status, setStatus] = useState<string | JSX.Element>("waiting");
|
||||||
|
|
||||||
constructor(props: IProps) {
|
useEffect(() => {
|
||||||
super(props);
|
const i = window.setInterval(step, 50);
|
||||||
this.rng = new WHRNG(this.props.p.totalPlaytime);
|
return () => clearInterval(i);
|
||||||
|
});
|
||||||
|
|
||||||
this.state = {
|
function step(): void {
|
||||||
index: [0, 0, 0, 0, 0],
|
|
||||||
investment: 1000,
|
|
||||||
locks: [0, 0, 0, 0, 0],
|
|
||||||
canPlay: true,
|
|
||||||
status: "waiting",
|
|
||||||
};
|
|
||||||
|
|
||||||
this.play = this.play.bind(this);
|
|
||||||
this.lock = this.lock.bind(this);
|
|
||||||
this.unlock = this.unlock.bind(this);
|
|
||||||
this.step = this.step.bind(this);
|
|
||||||
this.checkWinnings = this.checkWinnings.bind(this);
|
|
||||||
this.getTable = this.getTable.bind(this);
|
|
||||||
this.updateInvestment = this.updateInvestment.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount(): void {
|
|
||||||
this.interval = window.setInterval(this.step, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
step(): void {
|
|
||||||
let stoppedOne = false;
|
let stoppedOne = false;
|
||||||
const index = this.state.index.slice();
|
const copy = index.slice();
|
||||||
for (const i in index) {
|
for (const i in copy) {
|
||||||
if (index[i] === this.state.locks[i] && !stoppedOne) continue;
|
if (copy[i] === locks[i] && !stoppedOne) continue;
|
||||||
index[i] = (index[i] + 1) % symbols.length;
|
copy[i] = (copy[i] + 1) % symbols.length;
|
||||||
stoppedOne = true;
|
stoppedOne = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ index: index });
|
setIndex(copy);
|
||||||
|
|
||||||
if (stoppedOne && index.every((e, i) => e === this.state.locks[i])) {
|
if (stoppedOne && copy.every((e, i) => e === locks[i])) {
|
||||||
this.checkWinnings();
|
checkWinnings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(): void {
|
function getTable(): string[][] {
|
||||||
clearInterval(this.interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTable(): string[][] {
|
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
symbols[(this.state.index[0] + symbols.length - 1) % symbols.length],
|
symbols[(index[0] + symbols.length - 1) % symbols.length],
|
||||||
symbols[(this.state.index[1] + symbols.length - 1) % symbols.length],
|
symbols[(index[1] + symbols.length - 1) % symbols.length],
|
||||||
symbols[(this.state.index[2] + symbols.length - 1) % symbols.length],
|
symbols[(index[2] + symbols.length - 1) % symbols.length],
|
||||||
symbols[(this.state.index[3] + symbols.length - 1) % symbols.length],
|
symbols[(index[3] + symbols.length - 1) % symbols.length],
|
||||||
symbols[(this.state.index[4] + symbols.length - 1) % symbols.length],
|
symbols[(index[4] + symbols.length - 1) % symbols.length],
|
||||||
],
|
],
|
||||||
|
[symbols[index[0]], symbols[index[1]], symbols[index[2]], symbols[index[3]], symbols[index[4]]],
|
||||||
[
|
[
|
||||||
symbols[this.state.index[0]],
|
symbols[(index[0] + 1) % symbols.length],
|
||||||
symbols[this.state.index[1]],
|
symbols[(index[1] + 1) % symbols.length],
|
||||||
symbols[this.state.index[2]],
|
symbols[(index[2] + 1) % symbols.length],
|
||||||
symbols[this.state.index[3]],
|
symbols[(index[3] + 1) % symbols.length],
|
||||||
symbols[this.state.index[4]],
|
symbols[(index[4] + 1) % symbols.length],
|
||||||
],
|
|
||||||
[
|
|
||||||
symbols[(this.state.index[0] + 1) % symbols.length],
|
|
||||||
symbols[(this.state.index[1] + 1) % symbols.length],
|
|
||||||
symbols[(this.state.index[2] + 1) % symbols.length],
|
|
||||||
symbols[(this.state.index[3] + 1) % symbols.length],
|
|
||||||
symbols[(this.state.index[4] + 1) % symbols.length],
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
play(): void {
|
function play(): void {
|
||||||
if (this.reachedLimit(this.props.p)) return;
|
if (reachedLimit(props.p)) return;
|
||||||
this.setState({ status: "playing" });
|
setStatus("playing");
|
||||||
this.win(this.props.p, -this.state.investment);
|
win(props.p, -investment);
|
||||||
if (!this.state.canPlay) return;
|
if (!canPlay) return;
|
||||||
this.unlock();
|
unlock();
|
||||||
setTimeout(this.lock, this.rng.random() * 2000 + 1000);
|
setTimeout(lock, rng.random() * 2000 + 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock(): void {
|
function lock(): void {
|
||||||
this.setState({
|
setLocks([
|
||||||
locks: [
|
Math.floor(rng.random() * symbols.length),
|
||||||
Math.floor(this.rng.random() * symbols.length),
|
Math.floor(rng.random() * symbols.length),
|
||||||
Math.floor(this.rng.random() * symbols.length),
|
Math.floor(rng.random() * symbols.length),
|
||||||
Math.floor(this.rng.random() * symbols.length),
|
Math.floor(rng.random() * symbols.length),
|
||||||
Math.floor(this.rng.random() * symbols.length),
|
Math.floor(rng.random() * symbols.length),
|
||||||
Math.floor(this.rng.random() * symbols.length),
|
]);
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkWinnings(): void {
|
function checkWinnings(): void {
|
||||||
const t = this.getTable();
|
const t = getTable();
|
||||||
const getPaylineData = function (payline: number[][]): string[] {
|
const getPaylineData = function (payline: number[][]): string[] {
|
||||||
const data = [];
|
const data = [];
|
||||||
for (const point of payline) {
|
for (const point of payline) {
|
||||||
@ -263,35 +229,31 @@ export class SlotMachine extends Game<IProps, IState> {
|
|||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
let gains = -this.state.investment;
|
let gains = -investment;
|
||||||
for (const payline of payLines) {
|
for (const payline of payLines) {
|
||||||
const data = getPaylineData(payline);
|
const data = getPaylineData(payline);
|
||||||
const count = countSequence(data);
|
const count = countSequence(data);
|
||||||
if (count < 3) continue;
|
if (count < 3) continue;
|
||||||
const payout = getPayout(data[0], count - 3);
|
const payout = getPayout(data[0], count - 3);
|
||||||
gains += this.state.investment * payout;
|
gains += investment * payout;
|
||||||
this.win(this.props.p, this.state.investment * payout);
|
win(props.p, investment * payout);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
setStatus(
|
||||||
status: (
|
<>
|
||||||
<>
|
{gains > 0 ? "gained" : "lost"} <Money money={Math.abs(gains)} />
|
||||||
{gains > 0 ? "gained" : "lost"} <Money money={Math.abs(gains)} />
|
</>,
|
||||||
</>
|
);
|
||||||
),
|
setCanPlay(true);
|
||||||
canPlay: true,
|
if (reachedLimit(props.p)) return;
|
||||||
});
|
|
||||||
if (this.reachedLimit(this.props.p)) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock(): void {
|
function unlock(): void {
|
||||||
this.setState({
|
setLocks([-1, -1, -1, -1, -1]);
|
||||||
locks: [-1, -1, -1, -1, -1],
|
setCanPlay(false);
|
||||||
canPlay: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
|
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
let investment: number = parseInt(e.currentTarget.value);
|
let investment: number = parseInt(e.currentTarget.value);
|
||||||
if (isNaN(investment)) {
|
if (isNaN(investment)) {
|
||||||
investment = minPlay;
|
investment = minPlay;
|
||||||
@ -302,53 +264,49 @@ export class SlotMachine extends Game<IProps, IState> {
|
|||||||
if (investment < minPlay) {
|
if (investment < minPlay) {
|
||||||
investment = minPlay;
|
investment = minPlay;
|
||||||
}
|
}
|
||||||
this.setState({ investment: investment });
|
setInvestment(investment);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): React.ReactNode {
|
const t = getTable();
|
||||||
const t = this.getTable();
|
// prettier-ignore
|
||||||
// prettier-ignore
|
return (
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
<pre>+———————————————————————+</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>+———————————————————————+</Typography>
|
||||||
<pre>| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |</Typography>
|
||||||
<pre>| | | | | | | |</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | | | | | | |</Typography>
|
||||||
<pre>| | {symbols[this.state.index[0]]} | {symbols[this.state.index[1]]} | {symbols[this.state.index[2]]} | {symbols[this.state.index[3]]} | {symbols[this.state.index[4]]} | |</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {symbols[index[0]]} | {symbols[index[1]]} | {symbols[index[2]]} | {symbols[index[3]]} | {symbols[index[4]]} | |</Typography>
|
||||||
<pre>| | | | | | | |</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | | | | | | |</Typography>
|
||||||
<pre>| | {symbols[(this.state.index[0]+1)%symbols.length]} | {symbols[(this.state.index[1]+1)%symbols.length]} | {symbols[(this.state.index[2]+1)%symbols.length]} | {symbols[(this.state.index[3]+1)%symbols.length]} | {symbols[(this.state.index[4]+1)%symbols.length]} | |</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {symbols[(index[0]+1)%symbols.length]} | {symbols[(index[1]+1)%symbols.length]} | {symbols[(index[2]+1)%symbols.length]} | {symbols[(index[3]+1)%symbols.length]} | {symbols[(index[4]+1)%symbols.length]} | |</Typography>
|
||||||
<pre>+———————————————————————+</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>+———————————————————————+</Typography>
|
||||||
<input
|
<TextField
|
||||||
type="number"
|
type="number"
|
||||||
className="text-input"
|
onChange={updateInvestment}
|
||||||
onChange={this.updateInvestment}
|
|
||||||
placeholder={"Amount to play"}
|
placeholder={"Amount to play"}
|
||||||
value={this.state.investment}
|
disabled={!canPlay}
|
||||||
disabled={!this.state.canPlay}
|
InputProps={{endAdornment:(<Button
|
||||||
|
onClick={trusted(play)}
|
||||||
|
disabled={!canPlay}
|
||||||
|
>Spin!</Button>)}}
|
||||||
/>
|
/>
|
||||||
<StdButton
|
|
||||||
onClick={trusted(this.play)}
|
<Typography variant="h4">{status}</Typography>
|
||||||
text={"Spin!"}
|
<Typography>Pay lines</Typography>
|
||||||
disabled={!this.state.canPlay}
|
|
||||||
/>
|
|
||||||
<h1>{this.state.status}</h1>
|
|
||||||
<h2>Pay lines</h2>
|
|
||||||
|
|
||||||
<pre>----- ····· ·····</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>----- ····· ·····</Typography>
|
||||||
<pre>····· ----- ·····</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ----- ·····</Typography>
|
||||||
<pre>····· ····· -----</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ····· -----</Typography>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<pre>··^·· \···/ \···/</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>··^·· \···/ \···/</Typography>
|
||||||
<pre>·/·\· ·\·/· ·---·</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>·/·\· ·\·/· ·---·</Typography>
|
||||||
<pre>/···\ ··v·· ·····</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>/···\ ··v·· ·····</Typography>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<pre>····· ·---· ·····</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ·---· ·····</Typography>
|
||||||
<pre>·---· /···\ \···/</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>·---· /···\ \···/</Typography>
|
||||||
<pre>/···\ ····· ·---·</pre>
|
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>/···\ ····· ·---·</Typography>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://felgo.com/doc/how-to-make-a-slot-game-tutorial/
|
// https://felgo.com/doc/how-to-make-a-slot-game-tutorial/
|
||||||
|
@ -3,8 +3,7 @@ import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc
|
|||||||
import { IMap } from "./types";
|
import { IMap } from "./types";
|
||||||
|
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "./utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "./utils/JSONReviver";
|
||||||
import { createPopup, removePopup } from "./ui/React/createPopup";
|
import { CodingContractEvent } from "./ui/React/CodingContractModal";
|
||||||
import { CodingContractPopup } from "./ui/React/CodingContractPopup";
|
|
||||||
|
|
||||||
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
|
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
|
||||||
|
|
||||||
@ -166,29 +165,21 @@ export class CodingContract {
|
|||||||
* Creates a popup to prompt the player to solve the problem
|
* Creates a popup to prompt the player to solve the problem
|
||||||
*/
|
*/
|
||||||
async prompt(): Promise<CodingContractResult> {
|
async prompt(): Promise<CodingContractResult> {
|
||||||
const popupId = `coding-contract-prompt-popup-${this.fn}`;
|
|
||||||
return new Promise<CodingContractResult>((resolve) => {
|
return new Promise<CodingContractResult>((resolve) => {
|
||||||
createPopup(
|
const props = {
|
||||||
popupId,
|
c: this,
|
||||||
CodingContractPopup,
|
onClose: () => {
|
||||||
{
|
resolve(CodingContractResult.Cancelled);
|
||||||
c: this,
|
|
||||||
popupId: popupId,
|
|
||||||
onClose: () => {
|
|
||||||
resolve(CodingContractResult.Cancelled);
|
|
||||||
removePopup(popupId);
|
|
||||||
},
|
|
||||||
onAttempt: (val: string) => {
|
|
||||||
if (this.isSolution(val)) {
|
|
||||||
resolve(CodingContractResult.Success);
|
|
||||||
} else {
|
|
||||||
resolve(CodingContractResult.Failure);
|
|
||||||
}
|
|
||||||
removePopup(popupId);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
() => resolve(CodingContractResult.Cancelled),
|
onAttempt: (val: string) => {
|
||||||
);
|
if (this.isSolution(val)) {
|
||||||
|
resolve(CodingContractResult.Success);
|
||||||
|
} else {
|
||||||
|
resolve(CodingContractResult.Failure);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
CodingContractEvent.emit(props);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
32
src/Company/ui/QuitJobModal.tsx
Normal file
32
src/Company/ui/QuitJobModal.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Company } from "../Company";
|
||||||
|
import { use } from "../../ui/Context";
|
||||||
|
import { Modal } from "../../ui/React/Modal";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
locName: string;
|
||||||
|
company: Company;
|
||||||
|
onQuit: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function QuitJobModal(props: IProps): React.ReactElement {
|
||||||
|
const player = use.Player();
|
||||||
|
function quit(): void {
|
||||||
|
player.quitJob(props.locName);
|
||||||
|
props.onQuit();
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={props.open} onClose={props.onClose}>
|
||||||
|
<Typography> Would you like to quit your job at {props.company.name}?</Typography>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<Button onClick={quit}>Quit</Button>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Company } from "../Company";
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
|
||||||
import { removePopup } from "../../ui/React/createPopup";
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
locName: string;
|
|
||||||
company: Company;
|
|
||||||
player: IPlayer;
|
|
||||||
onQuit: () => void;
|
|
||||||
popupId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function QuitJobPopup(props: IProps): React.ReactElement {
|
|
||||||
function quit(): void {
|
|
||||||
props.player.quitJob(props.locName);
|
|
||||||
props.onQuit();
|
|
||||||
removePopup(props.popupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
Would you like to quit your job at {props.company.name}?
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<button autoFocus={true} className="std-button" onClick={quit}>
|
|
||||||
Quit
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
119
src/Constants.ts
119
src/Constants.ts
@ -9,6 +9,7 @@ export const CONSTANTS: {
|
|||||||
MaxSkillLevel: number;
|
MaxSkillLevel: number;
|
||||||
MilliPerCycle: number;
|
MilliPerCycle: number;
|
||||||
CorpFactionRepRequirement: number;
|
CorpFactionRepRequirement: number;
|
||||||
|
BaseFocusBonus: number;
|
||||||
BaseCostFor1GBOfRamHome: number;
|
BaseCostFor1GBOfRamHome: number;
|
||||||
BaseCostFor1GBOfRamServer: number;
|
BaseCostFor1GBOfRamServer: number;
|
||||||
TravelCost: number;
|
TravelCost: number;
|
||||||
@ -46,14 +47,6 @@ export const CONSTANTS: {
|
|||||||
IntelligenceTerminalHackBaseExpGain: number;
|
IntelligenceTerminalHackBaseExpGain: number;
|
||||||
IntelligenceSingFnBaseExpGain: number;
|
IntelligenceSingFnBaseExpGain: number;
|
||||||
IntelligenceClassBaseExpGain: number;
|
IntelligenceClassBaseExpGain: number;
|
||||||
IntelligenceHackingMissionBaseExpGain: number;
|
|
||||||
HackingMissionRepToDiffConversion: number;
|
|
||||||
HackingMissionRepToRewardConversion: number;
|
|
||||||
HackingMissionSpamTimeIncrease: number;
|
|
||||||
HackingMissionTransferAttackIncrease: number;
|
|
||||||
HackingMissionMiscDefenseIncrease: number;
|
|
||||||
HackingMissionDifficultyToHacking: number;
|
|
||||||
HackingMissionHowToPlay: string;
|
|
||||||
MillisecondsPer20Hours: number;
|
MillisecondsPer20Hours: number;
|
||||||
GameCyclesPer20Hours: number;
|
GameCyclesPer20Hours: number;
|
||||||
MillisecondsPer10Hours: number;
|
MillisecondsPer10Hours: number;
|
||||||
@ -121,7 +114,7 @@ export const CONSTANTS: {
|
|||||||
TotalNumBitNodes: number;
|
TotalNumBitNodes: number;
|
||||||
LatestUpdate: string;
|
LatestUpdate: string;
|
||||||
} = {
|
} = {
|
||||||
Version: "0.54.0",
|
Version: "0.55.0",
|
||||||
|
|
||||||
// Speed (in ms) at which the main loop is updated
|
// Speed (in ms) at which the main loop is updated
|
||||||
_idleSpeed: 200,
|
_idleSpeed: 200,
|
||||||
@ -198,63 +191,6 @@ export const CONSTANTS: {
|
|||||||
IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain
|
IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain
|
||||||
IntelligenceSingFnBaseExpGain: 1.5,
|
IntelligenceSingFnBaseExpGain: 1.5,
|
||||||
IntelligenceClassBaseExpGain: 0.01,
|
IntelligenceClassBaseExpGain: 0.01,
|
||||||
IntelligenceHackingMissionBaseExpGain: 3, // Hacking Mission difficulty multiplied by this to get exp gain
|
|
||||||
|
|
||||||
// Hacking Missions
|
|
||||||
// TODO Move this into Hacking Mission implementation
|
|
||||||
HackingMissionRepToDiffConversion: 10000, // Faction rep is divided by this to get mission difficulty
|
|
||||||
HackingMissionRepToRewardConversion: 7, // Faction rep divided byt his to get mission rep reward
|
|
||||||
HackingMissionSpamTimeIncrease: 25000, // How much time limit increase is gained when conquering a Spam Node (ms)
|
|
||||||
HackingMissionTransferAttackIncrease: 1.05, // Multiplier by which the attack for all Core Nodes is increased when conquering a Transfer Node
|
|
||||||
HackingMissionMiscDefenseIncrease: 1.05, // The amount by which every misc node's defense is multiplied when one is conquered
|
|
||||||
HackingMissionDifficultyToHacking: 135, // Difficulty is multiplied by this to determine enemy's "hacking" level (to determine effects of scan/attack, etc)
|
|
||||||
HackingMissionHowToPlay:
|
|
||||||
"Hacking missions are a minigame that, if won, will reward you with faction reputation.<br><br>" +
|
|
||||||
"In this game you control a set of Nodes and use them to try and defeat an enemy. Your Nodes " +
|
|
||||||
"are colored blue, while the enemy's are red. There are also other nodes on the map colored gray " +
|
|
||||||
"that initially belong to neither you nor the enemy. The goal of the game is " +
|
|
||||||
"to capture all of the enemy's Database nodes within the time limit. " +
|
|
||||||
"If you fail to do this, you will lose.<br><br>" +
|
|
||||||
"Each Node has three stats: Attack, Defense, and HP. There are five different actions that " +
|
|
||||||
"a Node can take:<br><br> " +
|
|
||||||
"Attack - Targets an enemy Node and lowers its HP. The effectiveness is determined by the owner's Attack, the Player's " +
|
|
||||||
"hacking level, and the enemy's defense.<br><br>" +
|
|
||||||
"Scan - Targets an enemy Node and lowers its Defense. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the " +
|
|
||||||
"enemy's defense.<br><br>" +
|
|
||||||
"Weaken - Targets an enemy Node and lowers its Attack. The effectiveness is determined by the owner's Attack, the Player's hacking level, and the enemy's " +
|
|
||||||
"defense.<br><br>" +
|
|
||||||
"Fortify - Raises the Node's Defense. The effectiveness is determined by your hacking level.<br><br>" +
|
|
||||||
"Overflow - Raises the Node's Attack but lowers its Defense. The effectiveness is determined by your hacking level.<br><br>" +
|
|
||||||
"Note that when determining the effectiveness of the above actions, the TOTAL Attack or Defense of the team is used, not just the " +
|
|
||||||
"Attack/Defense of the individual Node that is performing the action.<br><br>" +
|
|
||||||
"To capture a Node, you must lower its HP down to 0.<br><br>" +
|
|
||||||
"There are six different types of Nodes:<br><br>" +
|
|
||||||
"CPU Core - These are your main Nodes that are used to perform actions. Capable of performing every action<br><br>" +
|
|
||||||
"Firewall - Nodes with high defense. These Nodes can 'Fortify'<br><br>" +
|
|
||||||
"Database - A special type of Node. The player's objective is to conquer all of the enemy's Database Nodes within " +
|
|
||||||
"the time limit. These Nodes cannot perform any actions<br><br>" +
|
|
||||||
"Spam - Conquering one of these Nodes will slow the enemy's trace, giving the player additional time to complete " +
|
|
||||||
"the mission. These Nodes cannot perform any actions<br><br>" +
|
|
||||||
"Transfer - Conquering one of these nodes will increase the Attack of all of your CPU Cores by a small fixed percentage. " +
|
|
||||||
"These Nodes are capable of performing every action except the 'Attack' action<br><br>" +
|
|
||||||
"Shield - Nodes with high defense. These Nodes can 'Fortify'<br><br>" +
|
|
||||||
"To assign an action to a Node, you must first select one of your Nodes. This can be done by simply clicking on it. Double-clicking " +
|
|
||||||
"a node will select all of your Nodes of the same type (e.g. select all CPU Core Nodes or all Transfer Nodes). Note that only Nodes " +
|
|
||||||
"that can perform actions (CPU Core, Transfer, Shield, Firewall) can be selected. Selected Nodes will be denoted with a white highlight. After selecting a Node or multiple Nodes, " +
|
|
||||||
"select its action using the Action Buttons near the top of the screen. Every action also has a corresponding keyboard " +
|
|
||||||
"shortcut.<br><br>" +
|
|
||||||
"For certain actions such as attacking, scanning, and weakening, the Node performing the action must have a target. To target " +
|
|
||||||
"another node, simply click-and-drag from the 'source' Node to a target. A Node can only have one target, and you can target " +
|
|
||||||
"any Node that is adjacent to one of your Nodes (immediately above, below, or to the side. NOT diagonal). Furthermore, only CPU Cores and Transfer Nodes " +
|
|
||||||
"can target, since they are the only ones that can perform the related actions. To remove a target, you can simply click on the line that represents " +
|
|
||||||
"the connection between one of your Nodes and its target. Alternatively, you can select the 'source' Node and click the 'Drop Connection' button, " +
|
|
||||||
"or press 'd'.<br><br>" +
|
|
||||||
"Other Notes:<br><br>" +
|
|
||||||
"-Whenever a miscellenaous Node (not owned by the player or enemy) is conquered, the defense of all remaining miscellaneous Nodes that " +
|
|
||||||
"are not actively being targeted will increase by a fixed percentage.<br><br>" +
|
|
||||||
"-Whenever a Node is conquered, its stats are significantly reduced<br><br>" +
|
|
||||||
"-Miscellaneous Nodes slowly raise their defense over time<br><br>" +
|
|
||||||
"-Nodes slowly regenerate health over time.",
|
|
||||||
|
|
||||||
// Time-related constants
|
// Time-related constants
|
||||||
MillisecondsPer20Hours: 72000000,
|
MillisecondsPer20Hours: 72000000,
|
||||||
@ -285,6 +221,7 @@ export const CONSTANTS: {
|
|||||||
GameCyclesPerFiveMinutes: 300000 / 200,
|
GameCyclesPerFiveMinutes: 300000 / 200,
|
||||||
|
|
||||||
// Player Work & Action
|
// Player Work & Action
|
||||||
|
BaseFocusBonus: 0.8,
|
||||||
FactionWorkHacking: "Faction Hacking Work",
|
FactionWorkHacking: "Faction Hacking Work",
|
||||||
FactionWorkField: "Faction Field Work",
|
FactionWorkField: "Faction Field Work",
|
||||||
FactionWorkSecurity: "Faction Security Work",
|
FactionWorkSecurity: "Faction Security Work",
|
||||||
@ -344,48 +281,26 @@ export const CONSTANTS: {
|
|||||||
TotalNumBitNodes: 24,
|
TotalNumBitNodes: 24,
|
||||||
|
|
||||||
LatestUpdate: `
|
LatestUpdate: `
|
||||||
v0.54.0 - 2021-09-20 One big react node (hydroflame & community)
|
v0.55.0 - 2021-09-20 Material UI (hydroflame & community)
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
** UI **
|
** Global **
|
||||||
|
|
||||||
* The UI is now completely(ish) in react and I'm starting to implement
|
* The game is now 100% in typescript, react, and Material-UI
|
||||||
Material-UI everywhere. This will help make the game feel more consistent.
|
|
||||||
* Major help from (@threehams)
|
|
||||||
* New Terminal
|
|
||||||
* New Active Scripts page
|
|
||||||
* New sidebar.
|
|
||||||
* New Character overview
|
|
||||||
* New tutorial
|
|
||||||
* New options page
|
|
||||||
* New create program page (@Nolshine)
|
|
||||||
|
|
||||||
** Netscript **
|
|
||||||
|
|
||||||
* Add companyName to getPlayer
|
|
||||||
|
|
||||||
** Factions **
|
|
||||||
|
|
||||||
* Megacorp factions are no longer removed when installing.
|
|
||||||
|
|
||||||
** Corporation **
|
|
||||||
|
|
||||||
* All research tooltips are always visible.
|
|
||||||
* Smart supply is enabled by default if purchased (@Nolshine)
|
|
||||||
|
|
||||||
** Misc. **
|
** Misc. **
|
||||||
|
|
||||||
* Fix "Game saved" animation. (@Nolshine)
|
* Corporations can no longer bribe special factions
|
||||||
* Update commitCrime documentation (@Tryneus)
|
* Infiltration can no longer lose focus of the keyboard.
|
||||||
* Fix logbox scrolling weird (@Nolshine)
|
* Fix terminal line limit
|
||||||
* Fix weird scrolling in corporations (@BartKoppelmans)
|
* Added theme editor
|
||||||
* Fix typo (@BartKoppelmans & @Nolshine)
|
* Theme applies on game load (@Nolshine)
|
||||||
* Delete game now has a confirmation modal (@Nolshine)
|
* Sleeves no longer consume all bonus time for some actions
|
||||||
* Fix issue where skills would not get properly updated when entering new
|
* Fix a bug where the autocomlete list would get duplicates
|
||||||
BN. (@Nolshine)
|
* Fix tutorial not scaling properly on small screens
|
||||||
* Convert create gang to popup (@vmesecher)
|
* Import should be more consistent
|
||||||
* Fixed a bug that prevented travel to Sector-12 and New Tokyo when not using
|
* Typo with 'help' command
|
||||||
ASCII art.
|
* Fix infinite loop in casino
|
||||||
* nerf noodle bar
|
* nerf noodle bar
|
||||||
`,
|
`,
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import { CorporationConstants } from "./data/Constants";
|
import { CorporationConstants } from "./data/Constants";
|
||||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||||
import { createElement } from "../ui/uiHelpers/createElement";
|
|
||||||
import { EmployeePositions } from "./EmployeePositions";
|
import { EmployeePositions } from "./EmployeePositions";
|
||||||
import { ICorporation } from "./ICorporation";
|
import { ICorporation } from "./ICorporation";
|
||||||
import { numeralWrapper } from "../ui/numeralFormat";
|
|
||||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
|
||||||
import { OfficeSpace } from "./OfficeSpace";
|
import { OfficeSpace } from "./OfficeSpace";
|
||||||
import { IIndustry } from "./IIndustry";
|
import { IIndustry } from "./IIndustry";
|
||||||
|
|
||||||
@ -139,74 +136,6 @@ export class Employee {
|
|||||||
return mult;
|
return mult;
|
||||||
}
|
}
|
||||||
|
|
||||||
//'panel' is the DOM element on which to create the UI
|
|
||||||
createUI(panel: HTMLElement, corporation: ICorporation, industry: IIndustry): void {
|
|
||||||
const effCre = this.cre * corporation.getEmployeeCreMultiplier() * industry.getEmployeeCreMultiplier(),
|
|
||||||
effCha = this.cha * corporation.getEmployeeChaMultiplier() * industry.getEmployeeChaMultiplier(),
|
|
||||||
effInt = this.int * corporation.getEmployeeIntMultiplier() * industry.getEmployeeIntMultiplier(),
|
|
||||||
effEff = this.eff * corporation.getEmployeeEffMultiplier() * industry.getEmployeeEffMultiplier();
|
|
||||||
panel.style.color = "white";
|
|
||||||
panel.appendChild(
|
|
||||||
createElement("p", {
|
|
||||||
id: "cmpy-mgmt-employee-" + this.name + "-panel-text",
|
|
||||||
innerHTML:
|
|
||||||
"Morale: " +
|
|
||||||
formatNumber(this.mor, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Happiness: " +
|
|
||||||
formatNumber(this.hap, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Energy: " +
|
|
||||||
formatNumber(this.ene, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Intelligence: " +
|
|
||||||
formatNumber(effInt, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Charisma: " +
|
|
||||||
formatNumber(effCha, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Experience: " +
|
|
||||||
formatNumber(this.exp, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Creativity: " +
|
|
||||||
formatNumber(effCre, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Efficiency: " +
|
|
||||||
formatNumber(effEff, 3) +
|
|
||||||
"<br>" +
|
|
||||||
"Salary: " +
|
|
||||||
numeralWrapper.format(this.sal, "$0.000a") +
|
|
||||||
"/ s<br>",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
//Selector for employee position
|
|
||||||
const selector = createElement("select", {}) as HTMLSelectElement;
|
|
||||||
for (const key in EmployeePositions) {
|
|
||||||
if (EmployeePositions.hasOwnProperty(key)) {
|
|
||||||
selector.add(
|
|
||||||
createElement("option", {
|
|
||||||
text: EmployeePositions[key],
|
|
||||||
value: EmployeePositions[key],
|
|
||||||
}) as HTMLOptionElement,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selector.addEventListener("change", () => {
|
|
||||||
this.pos = selector.options[selector.selectedIndex].value;
|
|
||||||
});
|
|
||||||
|
|
||||||
//Set initial value of selector
|
|
||||||
for (let i = 0; i < selector.length; ++i) {
|
|
||||||
if (selector.options[i].value === this.pos) {
|
|
||||||
selector.selectedIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panel.appendChild(selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(): Employee {
|
copy(): Employee {
|
||||||
const employee = new Employee();
|
const employee = new Employee();
|
||||||
employee.name = this.name;
|
employee.name = this.name;
|
||||||
|
@ -16,7 +16,6 @@ import { Warehouse } from "./Warehouse";
|
|||||||
import { ICorporation } from "./ICorporation";
|
import { ICorporation } from "./ICorporation";
|
||||||
import { IIndustry } from "./IIndustry";
|
import { IIndustry } from "./IIndustry";
|
||||||
import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades";
|
import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades";
|
||||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
|
||||||
|
|
||||||
interface IParams {
|
interface IParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
@ -385,9 +384,6 @@ export class Industry implements IIndustry {
|
|||||||
const prod = this.products[prodName];
|
const prod = this.products[prodName];
|
||||||
if (prod === undefined) continue;
|
if (prod === undefined) continue;
|
||||||
warehouse.sizeUsed += prod.data[warehouse.loc][0] * prod.siz;
|
warehouse.sizeUsed += prod.data[warehouse.loc][0] * prod.siz;
|
||||||
if (prod.data[warehouse.loc][0] > 0) {
|
|
||||||
warehouse.breakdown += prodName + ": " + formatNumber(prod.data[warehouse.loc][0] * prod.siz, 0) + "<br>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user