mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 15:43: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
|
||||
=========
|
||||
|
||||
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)
|
||||
-------------------------------------------
|
||||
|
||||
|
@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.54'
|
||||
version = '0.55'
|
||||
# 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
|
||||
# for a list of supported languages.
|
||||
|
60
index.html
60
index.html
@ -1,18 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta charset="utf-8"/>
|
||||
<title>Bitburner</title>
|
||||
<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="16x16" href="dist/favicon-16x16.png" />
|
||||
<link rel="manifest" href="dist/site.webmanifest" />
|
||||
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000" />
|
||||
<meta name="apple-mobile-web-app-title" content="Bitburner" />
|
||||
<meta name="application-name" content="Bitburner" />
|
||||
<meta name="msapplication-TileColor" content="#000000" />
|
||||
<meta name="msapplication-config" content="dist/browserconfig.xml" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<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="16x16" href="dist/favicon-16x16.png"/>
|
||||
<link rel="manifest" href="dist/site.webmanifest"/>
|
||||
<link rel="mask-icon" href="dist/safari-pinned-tab.svg" color="#000000"/>
|
||||
<meta name="apple-mobile-web-app-title" content="Bitburner"/>
|
||||
<meta name="application-name" content="Bitburner"/>
|
||||
<meta name="msapplication-TileColor" content="#000000"/>
|
||||
<meta name="msapplication-config" content="dist/browserconfig.xml"/>
|
||||
<meta name="theme-color" content="#ffffff"/>
|
||||
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
@ -30,31 +30,27 @@
|
||||
m.parentNode.insertBefore(a, m);
|
||||
})(window, document, "script", "https://www.google-analytics.com/analytics.js", "ga");
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
ga("create", "UA-100157497-1", "auto");
|
||||
ga("send", "pageview");
|
||||
</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" />
|
||||
<link href="dist/vendor.css" rel="stylesheet" />
|
||||
<link href="main.css" rel="stylesheet" />
|
||||
</head>
|
||||
*::-webkit-scrollbar {
|
||||
display: none; /* for Chrome, Safari, and Opera */
|
||||
}
|
||||
</style>
|
||||
<link rel="shortcut icon" href="favicon.ico"></head>
|
||||
<body>
|
||||
<div id="entire-game-container">
|
||||
<div id="mainmenu-container" style="display: flex; flex-direction: row"></div>
|
||||
|
||||
<!-- Status text -->
|
||||
<div id="status-text-container">
|
||||
<p id="status-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="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>
|
||||
<div id="root"/>
|
||||
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
|
||||
</html>
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
9
main.css
9
main.css
@ -2105,13 +2105,14 @@ input[type="checkbox"] {
|
||||
.popup-box-content {
|
||||
background-color: var(--my-background-color);
|
||||
padding: 12px;
|
||||
border: 5px solid var(--my-highlight-color);
|
||||
border: 2px solid #adff2f;
|
||||
width: 70%;
|
||||
max-height: 80%;
|
||||
overflow-y: auto;
|
||||
z-index: 11;
|
||||
/* Sit on top of the container */
|
||||
color: var(--my-font-color); }
|
||||
color: var(--my-font-color);
|
||||
box-shadow: 0 3px 5px -1px #090, 0 5px 8px 0 #090, 0 1px 14px 0 #090; }
|
||||
|
||||
.popup-box-input-div {
|
||||
margin: 2px; }
|
||||
@ -2169,12 +2170,12 @@ input[type="checkbox"] {
|
||||
max-height: 50%;
|
||||
z-index: 10;
|
||||
background-color: var(--my-background-color);
|
||||
border: 2px solid var(--my-highlight-color); }
|
||||
border: 2px solid #adff2f; }
|
||||
|
||||
.log-box-header {
|
||||
z-index: 1300;
|
||||
background-color: #333;
|
||||
border: 1px solid var(--my-highlight-color);
|
||||
border: 2px solid #adff2f;
|
||||
display: flex;
|
||||
flex: row nowrap;
|
||||
align-items: center;
|
||||
|
File diff suppressed because one or more lines are too long
@ -148,6 +148,7 @@
|
||||
"format": "prettier --write .",
|
||||
"start": "http-server -p 8000",
|
||||
"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",
|
||||
"build": "webpack --mode production",
|
||||
"build:dev": "webpack --mode development",
|
||||
|
@ -2044,6 +2044,28 @@ function initAugmentations(): void {
|
||||
}
|
||||
AddToAugmentations(SNA);
|
||||
|
||||
const NeuroreceptorManager = new Augmentation({
|
||||
name: AugmentationNames.NeuroreceptorManager,
|
||||
repCost: 0.75e5,
|
||||
moneyCost: 5.5e8,
|
||||
info:
|
||||
"A brain implant carefully assembled around the synapses, which " +
|
||||
"micromanages the activity and levels of various neuroreceptor " +
|
||||
"chemicals and modulates electrical acvitiy to optimize concentration, " +
|
||||
"allowing the user to multitask much more effectively.",
|
||||
stats: (
|
||||
<>
|
||||
This augmentation removes the penalty for not focusing on actions such as working in a job or working for a
|
||||
faction.
|
||||
</>
|
||||
),
|
||||
});
|
||||
NeuroreceptorManager.addToFactions(["Tian Di Hui"]);
|
||||
if (augmentationExists(AugmentationNames.NeuroreceptorManager)) {
|
||||
delete Augmentations[AugmentationNames.NeuroreceptorManager];
|
||||
}
|
||||
AddToAugmentations(NeuroreceptorManager);
|
||||
|
||||
// Special Bladeburner Augmentations
|
||||
const BladeburnersFactionName = "Bladeburners";
|
||||
if (factionExists(BladeburnersFactionName)) {
|
||||
@ -2384,6 +2406,7 @@ function initAugmentations(): void {
|
||||
hacknet_node_core_cost_mult: 1.1,
|
||||
hacknet_node_level_cost_mult: 1.1,
|
||||
work_money_mult: 0.9,
|
||||
stats: null,
|
||||
});
|
||||
StaneksGift1.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||
resetAugmentation(StaneksGift1);
|
||||
@ -2423,6 +2446,7 @@ function initAugmentations(): void {
|
||||
hacknet_node_core_cost_mult: 1.05 / 1.1,
|
||||
hacknet_node_level_cost_mult: 1.05 / 1.1,
|
||||
work_money_mult: 0.95 / 0.9,
|
||||
stats: null,
|
||||
});
|
||||
StaneksGift2.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||
resetAugmentation(StaneksGift2);
|
||||
@ -2462,6 +2486,7 @@ function initAugmentations(): void {
|
||||
hacknet_node_core_cost_mult: 1 / 1.05,
|
||||
hacknet_node_level_cost_mult: 1 / 1.05,
|
||||
work_money_mult: 1 / 0.95,
|
||||
stats: null,
|
||||
});
|
||||
StaneksGift3.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||
resetAugmentation(StaneksGift3);
|
||||
@ -2475,6 +2500,7 @@ function initAugmentations(): void {
|
||||
"(hydro notes: Finishes the BN, eventually)",
|
||||
prereqs: [AugmentationNames.StaneksGift3],
|
||||
isSpecial: true,
|
||||
stats: null,
|
||||
});
|
||||
StaneksGiftAscension4.addToFactions([ChurchOfTheMachineGodFactionName]);
|
||||
resetAugmentation(StaneksGiftAscension4);
|
||||
|
@ -41,6 +41,7 @@ export const AugmentationNames: IMap<string> = {
|
||||
CranialSignalProcessorsG4: "Cranial Signal Processors - Gen IV",
|
||||
CranialSignalProcessorsG5: "Cranial Signal Processors - Gen V",
|
||||
NeuronalDensification: "Neuronal Densification",
|
||||
NeuroreceptorManager: "Neuroreceptor Management Implant",
|
||||
NuoptimalInjectorImplant: "Nuoptimal Nootropic Injector Implant",
|
||||
SpeechEnhancement: "Speech Enhancement",
|
||||
FocusWire: "FocusWire",
|
||||
|
@ -10,6 +10,7 @@ import { PurchasedAugmentations } from "./PurchasedAugmentations";
|
||||
import { SourceFiles } from "./SourceFiles";
|
||||
|
||||
import { canGetBonus } from "../../ExportBonus";
|
||||
import { use } from "../../ui/Context";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
@ -22,6 +23,7 @@ interface IProps {
|
||||
}
|
||||
|
||||
export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((o) => !o);
|
||||
@ -69,10 +71,14 @@ export function AugmentationsRoot(props: IProps): React.ReactElement {
|
||||
Purchased Augmentations
|
||||
</Typography>
|
||||
<Box mx={2}>
|
||||
<Tooltip title={"'I never asked for this'"}>
|
||||
<Button onClick={props.installAugmentationsFn}>Install Augmentations</Button>
|
||||
<Tooltip title={<Typography>'I never asked for this'</Typography>}>
|
||||
<span>
|
||||
<Button disabled={player.queuedAugmentations.length === 0} onClick={props.installAugmentationsFn}>
|
||||
Install Augmentations
|
||||
</Button>
|
||||
</span>
|
||||
</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">
|
||||
Backup Save {exportBonusStr()}
|
||||
</Button>
|
||||
|
@ -396,6 +396,15 @@ BitNodes["BitNode9"] = new BitNode(
|
||||
<br />
|
||||
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT when installing
|
||||
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 { BitNodes } from "../BitNode";
|
||||
import { enterBitNode, setRedPillFlag } from "../../RedPill";
|
||||
import { PortalPopup } from "./PortalPopup";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { PortalModal } from "./PortalModal";
|
||||
import { CinematicText } from "../../ui/React/CinematicText";
|
||||
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 {
|
||||
n: number;
|
||||
@ -16,51 +48,53 @@ interface IPortalProps {
|
||||
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
|
||||
}
|
||||
function BitNodePortal(props: IPortalProps): React.ReactElement {
|
||||
const router = use.Router();
|
||||
const [portalOpen, setPortalOpen] = useState(false);
|
||||
const classes = useStyles();
|
||||
const bitNode = BitNodes[`BitNode${props.n}`];
|
||||
if (bitNode == null) {
|
||||
return <>O</>;
|
||||
}
|
||||
|
||||
let cssClass;
|
||||
let cssClass = classes.level0;
|
||||
if (props.n === 12 && props.level >= 2) {
|
||||
// Repeating BitNode
|
||||
cssClass = "level-2";
|
||||
} else {
|
||||
cssClass = `level-${props.level}`;
|
||||
cssClass = classes.level2;
|
||||
} else if (props.level === 1) {
|
||||
cssClass = classes.level1;
|
||||
} else if (props.level === 3) {
|
||||
cssClass = classes.level3;
|
||||
}
|
||||
|
||||
function openPortalPopup(): void {
|
||||
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,
|
||||
});
|
||||
if (props.level === 2) {
|
||||
cssClass = classes.level2;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`bitnode ${cssClass} tooltip`}
|
||||
aria-label={`enter-bitnode-${bitNode.number.toString()}`}
|
||||
onClick={openPortalPopup}
|
||||
>
|
||||
<strong>O</strong>
|
||||
<span className="tooltiptext">
|
||||
<strong>
|
||||
BitNode-{bitNode.number.toString()}
|
||||
<br />
|
||||
{bitNode.name}
|
||||
</strong>
|
||||
<br />
|
||||
{bitNode.desc}
|
||||
<br />
|
||||
</span>
|
||||
</button>
|
||||
<>
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
<strong>
|
||||
BitNode-{bitNode.number.toString()}: {bitNode.name}
|
||||
</strong>
|
||||
<br />
|
||||
{bitNode.desc}
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<span onClick={() => setPortalOpen(true)} className={cssClass}>
|
||||
<b>O</b>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<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 (
|
||||
// prettier-ignore
|
||||
<div className="noselect">
|
||||
<pre> O </pre>
|
||||
<pre> | O O | O O | </pre>
|
||||
<pre> O | | / __| \ | | O </pre>
|
||||
<pre> O | O | | O / | O | | O | O </pre>
|
||||
<pre> | | | | |_/ |/ | \_ \_| | | | | </pre>
|
||||
<pre> O | | | O | | O__/ | / \__ | | O | | | O </pre>
|
||||
<pre> | | | | | | | / /| O / \| | | | | | | </pre>
|
||||
<pre>O | | | \| | O / _/ | / O | |/ | | | O</pre>
|
||||
<pre>| | | |O / | | O / | O O | | \ O| | | |</pre>
|
||||
<pre>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</pre>
|
||||
<pre> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </pre>
|
||||
<pre> | | |_/ | | \| / | \_| | | </pre>
|
||||
<pre> \| / \| | / / \ |/ </pre>
|
||||
<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>
|
||||
<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>
|
||||
<pre> | | | / / \ \ | | | </pre>
|
||||
<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>
|
||||
<pre> \ | / / | | \ \ | / </pre>
|
||||
<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>
|
||||
<pre> \|| | | | | | | | | ||/ </pre>
|
||||
<pre> \| \_ | | | | | | _/ |/ </pre>
|
||||
<pre> \ \| / \ / \ |/ / </pre>
|
||||
<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>
|
||||
<pre> | | | | | | | | </pre>
|
||||
<pre> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </pre>
|
||||
<>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | O O | O O | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | / __| \ | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | O | | O / | O | | O | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | |_/ |/ | \_ \_| | | | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> O | | | O | | O__/ | / \__ | | O | | | O </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | / /| O / \| | | | | | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>O | | | \| | O / _/ | / O | |/ | | | O</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | | |O / | | O / | O O | | \ O| | | |</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}>| | |/ \/ / __| | |/ \ | \ | |__ \ \/ \| | |</Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| O | |_/ |\| \ <BitNodePortal n={13} level={nextSourceFileFlags[13]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} /> \__| \_| | O |/ </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | |_/ | | \| / | \_| | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| / \| | / / \ |/ </Typography>
|
||||
<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>
|
||||
<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>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | / / \ \ | | | </Typography>
|
||||
<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>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ | / / | | \ \ | / </Typography>
|
||||
<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>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \|| | | | | | | | | ||/ </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \| \_ | | | | | | _/ |/ </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \ \| / \ / \ |/ / </Typography>
|
||||
<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>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> | | | | | | | | </Typography>
|
||||
<Typography sx={{lineHeight: '1em',whiteSpace: 'pre'}}> \JUMP3R|JUMP|3R| |R3|PMUJ|R3PMUJ/ </Typography>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
@ -168,7 +202,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
||||
"> ",
|
||||
"> (Enter a new BitNode using the image above)",
|
||||
]} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return <></>;
|
||||
|
@ -2,18 +2,23 @@ import React from "react";
|
||||
|
||||
import { BitNodes } from "../BitNode";
|
||||
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 {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
n: number;
|
||||
level: number;
|
||||
destroyedBitNode: number;
|
||||
flume: boolean;
|
||||
router: IRouter;
|
||||
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 bitNode = BitNodes[bitNodeKey];
|
||||
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);
|
||||
return (
|
||||
<>
|
||||
<h1>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography variant="h4">
|
||||
BitNode-{props.n}: {bitNode.name}
|
||||
</h1>
|
||||
</Typography>
|
||||
<br />
|
||||
Source-File Level: {props.level} / {maxSourceFileLevel}
|
||||
<Typography>
|
||||
Source-File Level: {props.level} / {maxSourceFileLevel}
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}
|
||||
<Typography> Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}</Typography>
|
||||
<br />
|
||||
<br />
|
||||
{bitNode.info}
|
||||
<Typography>{bitNode.info}</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<button
|
||||
className="std-button"
|
||||
<Button
|
||||
onClick={() => {
|
||||
props.enter(props.router, props.flume, props.destroyedBitNode, props.n);
|
||||
removePopup(props.popupId);
|
||||
props.enter(router, props.flume, props.destroyedBitNode, props.n);
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
Enter BN{props.n}.{newLevel}
|
||||
</button>
|
||||
</>
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -20,7 +20,6 @@ class StatsMultiplier {
|
||||
|
||||
export interface IActionParams {
|
||||
name?: string;
|
||||
desc?: string;
|
||||
level?: number;
|
||||
maxLevel?: number;
|
||||
autoLevel?: boolean;
|
||||
@ -43,7 +42,6 @@ export interface IActionParams {
|
||||
|
||||
export class Action implements IAction {
|
||||
name = "";
|
||||
desc = "";
|
||||
|
||||
// Difficulty scales with level. See getDifficulty() method
|
||||
level = 1;
|
||||
@ -100,7 +98,6 @@ export class Action implements IAction {
|
||||
constructor(params: IActionParams | null = null) {
|
||||
// | null = null
|
||||
if (params && params.name) this.name = params.name;
|
||||
if (params && params.desc) this.desc = params.desc;
|
||||
|
||||
if (params && params.baseDifficulty) this.baseDifficulty = addOffset(params.baseDifficulty, 10);
|
||||
if (params && params.difficultyFac) this.difficultyFac = params.difficultyFac;
|
@ -1,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 {
|
||||
this.contracts["Tracking"] = new Contract({
|
||||
name: "Tracking",
|
||||
desc:
|
||||
"Identify and locate Synthoids. This contract involves reconnaissance " +
|
||||
"and information-gathering ONLY. Do NOT engage. Stealth is of the utmost importance.<br><br>" +
|
||||
"Successfully completing Tracking contracts will slightly improve your Synthoid population estimate for " +
|
||||
"whatever city you are currently in.",
|
||||
baseDifficulty: 125,
|
||||
difficultyFac: 1.02,
|
||||
rewardFac: 1.041,
|
||||
@ -1619,10 +1614,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.contracts["Bounty Hunter"] = new Contract({
|
||||
name: "Bounty Hunter",
|
||||
desc:
|
||||
"Hunt down and capture fugitive Synthoids. These Synthoids are wanted alive.<br><br>" +
|
||||
"Successfully completing a Bounty Hunter contract will lower the population in your " +
|
||||
"current city, and will also increase its chaos level.",
|
||||
baseDifficulty: 250,
|
||||
difficultyFac: 1.04,
|
||||
rewardFac: 1.085,
|
||||
@ -1651,10 +1642,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.contracts["Retirement"] = new Contract({
|
||||
name: "Retirement",
|
||||
desc:
|
||||
"Hunt down and retire (kill) rogue Synthoids.<br><br>" +
|
||||
"Successfully completing a Retirement contract will lower the population in your current " +
|
||||
"city, and will also increase its chaos level.",
|
||||
baseDifficulty: 200,
|
||||
difficultyFac: 1.03,
|
||||
rewardFac: 1.065,
|
||||
@ -1684,12 +1671,6 @@ export class Bladeburner implements IBladeburner {
|
||||
|
||||
this.operations["Investigation"] = new Operation({
|
||||
name: "Investigation",
|
||||
desc:
|
||||
"As a field agent, investigate and identify Synthoid " +
|
||||
"populations, movements, and operations.<br><br>Successful " +
|
||||
"Investigation ops will increase the accuracy of your " +
|
||||
"synthoid data.<br><br>" +
|
||||
"You will NOT lose HP from failed Investigation ops.",
|
||||
baseDifficulty: 400,
|
||||
difficultyFac: 1.03,
|
||||
rewardFac: 1.07,
|
||||
@ -1719,11 +1700,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Undercover Operation"] = new Operation({
|
||||
name: "Undercover Operation",
|
||||
desc:
|
||||
"Conduct undercover operations to identify hidden " +
|
||||
"and underground Synthoid communities and organizations.<br><br>" +
|
||||
"Successful Undercover ops will increase the accuracy of your synthoid " +
|
||||
"data.",
|
||||
baseDifficulty: 500,
|
||||
difficultyFac: 1.04,
|
||||
rewardFac: 1.09,
|
||||
@ -1754,7 +1730,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Sting Operation"] = new Operation({
|
||||
name: "Sting Operation",
|
||||
desc: "Conduct a sting operation to bait and capture particularly " + "notorious Synthoid criminals.",
|
||||
baseDifficulty: 650,
|
||||
difficultyFac: 1.04,
|
||||
rewardFac: 1.095,
|
||||
@ -1785,10 +1760,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Raid"] = new Operation({
|
||||
name: "Raid",
|
||||
desc:
|
||||
"Lead an assault on a known Synthoid community. Note that " +
|
||||
"there must be an existing Synthoid community in your current city " +
|
||||
"in order for this Operation to be successful.",
|
||||
baseDifficulty: 800,
|
||||
difficultyFac: 1.045,
|
||||
rewardFac: 1.1,
|
||||
@ -1819,10 +1790,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Stealth Retirement Operation"] = new Operation({
|
||||
name: "Stealth Retirement Operation",
|
||||
desc:
|
||||
"Lead a covert operation to retire Synthoids. The " +
|
||||
"objective is to complete the task without " +
|
||||
"drawing any attention. Stealth and discretion are key.",
|
||||
baseDifficulty: 1000,
|
||||
difficultyFac: 1.05,
|
||||
rewardFac: 1.11,
|
||||
@ -1854,10 +1821,6 @@ export class Bladeburner implements IBladeburner {
|
||||
});
|
||||
this.operations["Assassination"] = new Operation({
|
||||
name: "Assassination",
|
||||
desc:
|
||||
"Assassinate Synthoids that have been identified as " +
|
||||
"important, high-profile social and political leaders " +
|
||||
"in the Synthoid communities.",
|
||||
baseDifficulty: 1500,
|
||||
difficultyFac: 1.06,
|
||||
rewardFac: 1.14,
|
||||
@ -1900,7 +1863,7 @@ export class Bladeburner implements IBladeburner {
|
||||
if (this.action.type !== ActionTypes["Idle"]) {
|
||||
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
|
||||
if (this.automateEnabled) {
|
||||
msg += `<br><br>Your automation was disabled as well. You will have to re-enable it through the Bladeburner console`;
|
||||
msg += `<br /><br />Your automation was disabled as well. You will have to re-enable it through the Bladeburner console`;
|
||||
this.automateEnabled = false;
|
||||
}
|
||||
if (!Settings.SuppressBladeburnerPopup) {
|
@ -1,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 {
|
||||
name: string;
|
||||
desc: string;
|
||||
|
||||
// Difficulty scales with level. See getDifficulty() method
|
||||
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 { ContractPage } from "./ContractPage";
|
||||
import { OperationPage } from "./OperationPage";
|
||||
import { BlackOpPage } from "./BlackOpPage";
|
||||
import { SkillPage } from "./SkillPage";
|
||||
import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import Tabs from "@mui/material/Tabs";
|
||||
import Tab from "@mui/material/Tab";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function AllPages(props: IProps): React.ReactElement {
|
||||
const [page, setPage] = useState("General");
|
||||
const setRerender = useState(false)[1];
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
function Header(props: { name: string }): React.ReactElement {
|
||||
return (
|
||||
<a
|
||||
onClick={() => setPage(props.name)}
|
||||
className={page !== props.name ? "bladeburner-nav-button noselect" : "bladeburner-nav-button-inactive noselect"}
|
||||
>
|
||||
{props.name}
|
||||
</a>
|
||||
);
|
||||
function handleChange(event: React.SyntheticEvent, tab: number): void {
|
||||
setValue(tab);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header name={"General"} />
|
||||
<Header name={"Contracts"} />
|
||||
<Header name={"Operations"} />
|
||||
<Header name={"BlackOps"} />
|
||||
<Header name={"Skills"} />
|
||||
<div style={{ display: "block", margin: "4px", padding: "4px" }}>
|
||||
{page === "General" && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "Contracts" && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "Operations" && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "BlackOps" && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{page === "Skills" && <SkillPage bladeburner={props.bladeburner} />}
|
||||
</div>
|
||||
<span className="text">
|
||||
{stealthIcon} = This action requires stealth, {killIcon} = This action involves retirement
|
||||
</span>
|
||||
<Tabs variant="fullWidth" value={value} onChange={handleChange}>
|
||||
<Tab label="General" />
|
||||
<Tab label="Contracts" />
|
||||
<Tab label="Operations" />
|
||||
<Tab label="BlackOps" />
|
||||
<Tab label="Skills" />
|
||||
</Tabs>
|
||||
<Box sx={{ p: 1 }}>
|
||||
{value === 0 && <GeneralActionPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 1 && <ContractPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 2 && <OperationPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 3 && <BlackOpPage bladeburner={props.bladeburner} player={props.player} />}
|
||||
{value === 4 && <SkillPage bladeburner={props.bladeburner} />}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
26
src/Bladeburner/ui/Autolevel.tsx
Normal file
26
src/Bladeburner/ui/Autolevel.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from "react";
|
||||
import { IAction } from "../IAction";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import Switch from "@mui/material/Switch";
|
||||
|
||||
interface IProps {
|
||||
action: IAction;
|
||||
rerender: () => void;
|
||||
}
|
||||
|
||||
export function Autolevel(props: IProps): React.ReactElement {
|
||||
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
props.action.autoLevel = event.target.checked;
|
||||
props.rerender();
|
||||
}
|
||||
return (
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Tooltip title={<Typography>Automatically increase operation level when possible</Typography>}>
|
||||
<Typography> Autolevel:</Typography>
|
||||
</Tooltip>
|
||||
<Switch checked={props.action.autoLevel} onChange={onAutolevel} />
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -2,22 +2,29 @@ import React, { useState } from "react";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { TeamSizePopup } from "./TeamSizePopup";
|
||||
import { TeamSizeButton } from "./TeamSizeButton";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { BlackOperation } from "../BlackOperation";
|
||||
import { BlackOperations } from "../data/BlackOperations";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
import { StartButton } from "./StartButton";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: BlackOperation;
|
||||
}
|
||||
|
||||
export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
|
||||
if (isCompleted) {
|
||||
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>;
|
||||
@ -26,7 +33,6 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
const isActive =
|
||||
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
|
||||
props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
@ -34,70 +40,54 @@ export function BlackOpElem(props: IProps): React.ReactElement {
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes.BlackOperation;
|
||||
props.bladeburner.action.name = props.action.name;
|
||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function onTeam(): void {
|
||||
const popupId = "bladeburner-operation-set-team-size-popup";
|
||||
createPopup(popupId, TeamSizePopup, {
|
||||
bladeburner: props.bladeburner,
|
||||
action: props.action,
|
||||
popupId: popupId,
|
||||
});
|
||||
const actionData = BlackOperations[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Typography>
|
||||
{isActive ? (
|
||||
<>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
<>
|
||||
<CopyableText value={props.action.name} /> (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>
|
||||
{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>
|
||||
</>
|
||||
)}
|
||||
</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<p style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
||||
<Typography>{actionData.desc}</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<p style={{ display: "block", color: hasReqdRank ? "white" : "red" }}>
|
||||
<Typography color={hasReqdRank ? "primary" : "error"}>
|
||||
Required Rank: {formatNumber(props.action.reqdRank, 0)}
|
||||
</p>
|
||||
</Typography>
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
Estimated Success Chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
||||
{props.action.isStealth ? stealthIcon : <></>}
|
||||
{props.action.isKill ? killIcon : <></>}
|
||||
<Typography>
|
||||
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||
<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
</pre>
|
||||
</>
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -35,9 +35,7 @@ export function BlackOpList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{blackops.map((blackop: BlackOperation) => (
|
||||
<li key={blackop.name} className="bladeburner-action">
|
||||
<BlackOpElem bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
||||
</li>
|
||||
<BlackOpElem key={blackop.name} bladeburner={props.bladeburner} action={blackop} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { BlackOpList } from "./BlackOpList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,7 +12,7 @@ interface IProps {
|
||||
export function BlackOpPage(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
||||
<Typography>
|
||||
Black Operations (Black Ops) are special, one-time covert operations. Each Black Op must be unlocked
|
||||
successively by completing the one before it.
|
||||
<br />
|
||||
@ -21,7 +22,7 @@ export function BlackOpPage(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
Like normal operations, you may use a team for Black Ops. Failing a black op will incur heavy HP and rank
|
||||
losses.
|
||||
</p>
|
||||
</Typography>
|
||||
<BlackOpList bladeburner={props.bladeburner} player={props.player} />
|
||||
</>
|
||||
);
|
||||
|
@ -1,42 +1,39 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Stats } from "./Stats";
|
||||
import { Console } from "./Console";
|
||||
import { AllPages } from "./AllPages";
|
||||
|
||||
import { use } from "../../ui/Context";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Box from "@mui/material/Box";
|
||||
|
||||
export function BladeburnerRoot(): React.ReactElement {
|
||||
const player = use.Player();
|
||||
const router = use.Router();
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(rerender, 200);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
const bladeburner = player.bladeburner;
|
||||
if (bladeburner === null) return <></>;
|
||||
return (
|
||||
<div className="bladeburner-container">
|
||||
<div style={{ height: "60%", display: "block", position: "relative" }}>
|
||||
<div
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "30%",
|
||||
display: "inline-block",
|
||||
border: "1px solid white",
|
||||
}}
|
||||
>
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Grid container>
|
||||
<Grid item xs={6}>
|
||||
<Stats bladeburner={bladeburner} player={player} router={router} />
|
||||
</div>
|
||||
<Console bladeburner={bladeburner} player={player} />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: "70%",
|
||||
display: "block",
|
||||
border: "1px solid white",
|
||||
marginTop: "6px",
|
||||
padding: "6px",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<AllPages bladeburner={bladeburner} player={player} />
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Console bladeburner={bladeburner} player={player} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<AllPages bladeburner={bladeburner} player={player} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -2,18 +2,48 @@ import React, { useState, useRef, useEffect } from "react";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
|
||||
interface ILineProps {
|
||||
content: any;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
textfield: {
|
||||
margin: theme.spacing(0),
|
||||
width: "100%",
|
||||
},
|
||||
input: {
|
||||
backgroundColor: "#000",
|
||||
},
|
||||
nopadding: {
|
||||
padding: theme.spacing(0),
|
||||
},
|
||||
preformatted: {
|
||||
whiteSpace: "pre-wrap",
|
||||
margin: theme.spacing(0),
|
||||
},
|
||||
list: {
|
||||
padding: theme.spacing(0),
|
||||
height: "100%",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
function Line(props: ILineProps): React.ReactElement {
|
||||
return (
|
||||
<tr>
|
||||
<td className="bladeburner-console-line" style={{ color: "var(--my-font-color)", whiteSpace: "pre-wrap" }}>
|
||||
{props.content}
|
||||
</td>
|
||||
</tr>
|
||||
<ListItem sx={{ p: 0 }}>
|
||||
<Typography>{props.content}</Typography>
|
||||
</ListItem>
|
||||
);
|
||||
}
|
||||
|
||||
@ -23,15 +53,21 @@ interface IProps {
|
||||
}
|
||||
|
||||
export function Console(props: IProps): React.ReactElement {
|
||||
const lastRef = useRef<HTMLDivElement>(null);
|
||||
const classes = useStyles();
|
||||
const scrollHook = useRef<HTMLDivElement>(null);
|
||||
const [command, setCommand] = useState("");
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
function handleCommandChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setCommand(event.target.value);
|
||||
}
|
||||
|
||||
const [consoleHistoryIndex, setConsoleHistoryIndex] = useState(props.bladeburner.consoleHistory.length);
|
||||
|
||||
// TODO: Figure out how to actually make the scrolling work correctly.
|
||||
function scrollToBottom(): void {
|
||||
if (!lastRef.current) return;
|
||||
lastRef.current.scrollTop = lastRef.current.scrollHeight;
|
||||
if (!scrollHook.current) return;
|
||||
scrollHook.current.scrollTop = scrollHook.current.scrollHeight;
|
||||
}
|
||||
|
||||
function rerender(): void {
|
||||
@ -50,13 +86,11 @@ export function Console(props: IProps): React.ReactElement {
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
if (event.keyCode === 13) {
|
||||
event.preventDefault();
|
||||
const command = event.currentTarget.value;
|
||||
event.currentTarget.value = "";
|
||||
if (command.length > 0) {
|
||||
props.bladeburner.postToConsole("> " + command);
|
||||
props.bladeburner.executeConsoleCommands(props.player, command);
|
||||
setConsoleHistoryIndex(props.bladeburner.consoleHistory.length);
|
||||
rerender();
|
||||
setCommand("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,31 +139,34 @@ export function Console(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={lastRef} className="bladeburner-console-div">
|
||||
<table className="bladeburner-console-table">
|
||||
<tbody>
|
||||
{/*
|
||||
TODO: optimize this.
|
||||
using `i` as a key here isn't great because it'll re-render everything
|
||||
everytime the console reaches max length.
|
||||
*/}
|
||||
<Box height={"60vh"} display={"flex"} alignItems={"stretch"} component={Paper}>
|
||||
<Box>
|
||||
<List sx={{ height: "100%", overflow: "auto" }}>
|
||||
{props.bladeburner.consoleLogs.map((log: any, i: number) => (
|
||||
<Line key={i} content={log} />
|
||||
))}
|
||||
<tr key="input" id="bladeburner-console-input-row" className="bladeburner-console-input-row">
|
||||
<td className="bladeburner-console-input-cell">
|
||||
<pre>{"> "}</pre>
|
||||
<input
|
||||
autoFocus
|
||||
className="bladeburner-console-input"
|
||||
tabIndex={1}
|
||||
type="text"
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<TextField
|
||||
classes={{ root: classes.textfield }}
|
||||
autoFocus
|
||||
tabIndex={1}
|
||||
type="text"
|
||||
value={command}
|
||||
onChange={handleCommandChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
InputProps={{
|
||||
// for players to hook in
|
||||
className: classes.input,
|
||||
startAdornment: (
|
||||
<>
|
||||
<Typography>> </Typography>
|
||||
</>
|
||||
),
|
||||
spellCheck: false,
|
||||
}}
|
||||
/>
|
||||
</List>
|
||||
<div ref={scrollHook}></div>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -2,113 +2,78 @@ import React, { useState } from "react";
|
||||
import { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { Contracts } from "../data/Contracts";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IAction } from "../IAction";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
import { ActionLevel } from "./ActionLevel";
|
||||
import { Autolevel } from "./Autolevel";
|
||||
import { StartButton } from "./StartButton";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: IAction;
|
||||
}
|
||||
|
||||
export function ContractElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isActive =
|
||||
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes.Contract;
|
||||
props.bladeburner.action.name = props.action.name;
|
||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function increaseLevel(): void {
|
||||
++props.action.level;
|
||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function decreaseLevel(): void {
|
||||
--props.action.level;
|
||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
props.action.autoLevel = event.target.checked;
|
||||
setRerender((old) => !old);
|
||||
const actionData = Contracts[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Typography>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
</>
|
||||
) : (
|
||||
<CopyableText value={props.action.name} />
|
||||
)}
|
||||
</h2>
|
||||
{isActive ? (
|
||||
<p style={{ display: "block" }}>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
||||
Start
|
||||
</a>
|
||||
<CopyableText value={props.action.name} />
|
||||
<StartButton
|
||||
bladeburner={props.bladeburner}
|
||||
type={ActionTypes.Contract}
|
||||
name={props.action.name}
|
||||
rerender={rerender}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
||||
<span className="tooltiptext">
|
||||
{props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.ContractSuccessesPerLevel)} successes needed
|
||||
for next level
|
||||
</span>
|
||||
Level: {props.action.level} / {props.action.maxLevel}
|
||||
</pre>
|
||||
<a
|
||||
onClick={increaseLevel}
|
||||
style={{ padding: "2px", margin: "2px" }}
|
||||
className={`tooltip ${maxLevel ? "a-link-button-inactive" : "a-link-button"}`}
|
||||
>
|
||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↑
|
||||
</a>
|
||||
<a
|
||||
onClick={decreaseLevel}
|
||||
style={{ padding: "2px", margin: "2px" }}
|
||||
className={`tooltip ${props.action.level <= 1 ? "a-link-button-inactive" : "a-link-button"}`}
|
||||
>
|
||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↓
|
||||
</a>
|
||||
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
||||
<Typography>
|
||||
{actionData.desc}
|
||||
<br />
|
||||
<br />
|
||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
||||
{props.action.isStealth ? stealthIcon : <></>}
|
||||
{props.action.isKill ? killIcon : <></>}
|
||||
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||
<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
<br />
|
||||
@ -117,13 +82,9 @@ export function ContractElem(props: IProps): React.ReactElement {
|
||||
Successes: {props.action.successes}
|
||||
<br />
|
||||
Failures: {props.action.failures}
|
||||
</pre>
|
||||
</Typography>
|
||||
<br />
|
||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
||||
Autolevel:
|
||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
||||
</label>
|
||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
||||
</>
|
||||
<Autolevel rerender={rerender} action={props.action} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -14,9 +14,7 @@ export function ContractList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{names.map((name: string) => (
|
||||
<li key={name} className="bladeburner-action">
|
||||
<ContractElem bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
||||
</li>
|
||||
<ContractElem key={name} bladeburner={props.bladeburner} action={contracts[name]} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { ContractList } from "./ContractList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,14 +12,14 @@ interface IProps {
|
||||
export function ContractPage(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
||||
<Typography>
|
||||
Complete contracts in order to increase your Bladeburner rank and earn money. Failing a contract will cause you
|
||||
to lose HP, which can lead to hospitalization.
|
||||
<br />
|
||||
<br />
|
||||
You can unlock higher-level contracts by successfully completing them. Higher-level contracts are more
|
||||
difficult, but grant more rank, experience, and money.
|
||||
</p>
|
||||
</Typography>
|
||||
<ContractList bladeburner={props.bladeburner} player={props.player} />
|
||||
</>
|
||||
);
|
||||
|
@ -3,17 +3,28 @@ import { ActionTypes } from "../data/ActionTypes";
|
||||
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IAction } from "../IAction";
|
||||
import { GeneralActions } from "../data/GeneralActions";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
|
||||
import { StartButton } from "./StartButton";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: IAction;
|
||||
}
|
||||
|
||||
export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isActive = props.action.name === props.bladeburner.action.name;
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
@ -37,44 +48,44 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1))
|
||||
: -1;
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes[props.action.name as string];
|
||||
props.bladeburner.action.name = props.action.name;
|
||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
const actionData = GeneralActions[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Typography>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
</>
|
||||
) : (
|
||||
<CopyableText value={props.action.name} />
|
||||
)}
|
||||
</h2>
|
||||
{isActive ? (
|
||||
<p style={{ display: "block" }}>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
||||
Start
|
||||
</a>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Typography>
|
||||
<CopyableText value={props.action.name} />
|
||||
</Typography>
|
||||
<StartButton
|
||||
bladeburner={props.bladeburner}
|
||||
type={ActionTypes[props.action.name as string]}
|
||||
name={props.action.name}
|
||||
rerender={rerender}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }} dangerouslySetInnerHTML={{ __html: props.action.desc }}></pre>
|
||||
<Typography>{actionData.desc}</Typography>
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
<Typography>
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
{successChance !== -1 && (
|
||||
<>
|
||||
@ -82,7 +93,7 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
|
||||
Estimated success chance: {formatNumber(successChance * 100, 1)}%
|
||||
</>
|
||||
)}
|
||||
</pre>
|
||||
</>
|
||||
</Typography>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -20,9 +20,7 @@ export function GeneralActionList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{actions.map((action: Action) => (
|
||||
<li key={action.name} className="bladeburner-action">
|
||||
<GeneralActionElem bladeburner={props.bladeburner} action={action} player={props.player} />
|
||||
</li>
|
||||
<GeneralActionElem key={action.name} bladeburner={props.bladeburner} action={action} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { GeneralActionList } from "./GeneralActionList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,10 +12,7 @@ interface IProps {
|
||||
export function GeneralActionPage(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
||||
These are generic actions that will assist you in your Bladeburner duties. They will not affect your Bladeburner
|
||||
rank in any way.
|
||||
</p>
|
||||
<Typography>These are generic actions that will assist you in your Bladeburner duties.</Typography>
|
||||
<GeneralActionList bladeburner={props.bladeburner} player={props.player} />
|
||||
</>
|
||||
);
|
||||
|
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 { createProgressBarText } from "../../utils/helpers/createProgressBarText";
|
||||
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||
import { stealthIcon, killIcon } from "../data/Icons";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { TeamSizePopup } from "./TeamSizePopup";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { SuccessChance } from "./SuccessChance";
|
||||
import { ActionLevel } from "./ActionLevel";
|
||||
import { Autolevel } from "./Autolevel";
|
||||
import { StartButton } from "./StartButton";
|
||||
import { TeamSizeButton } from "./TeamSizeButton";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { Operation } from "../Operation";
|
||||
import { Operations } from "../data/Operations";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { CopyableText } from "../../ui/React/CopyableText";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
player: IPlayer;
|
||||
action: any;
|
||||
action: Operation;
|
||||
}
|
||||
|
||||
export function OperationElem(props: IProps): React.ReactElement {
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
const isActive =
|
||||
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
const computedActionTimeCurrent = Math.min(
|
||||
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
|
||||
props.bladeburner.actionTimeToComplete,
|
||||
);
|
||||
const maxLevel = props.action.level >= props.action.maxLevel;
|
||||
const actionTime = props.action.getActionTime(props.bladeburner);
|
||||
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
|
||||
|
||||
function onStart(): void {
|
||||
props.bladeburner.action.type = ActionTypes.Operation;
|
||||
props.bladeburner.action.name = props.action.name;
|
||||
props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function onTeam(): void {
|
||||
const popupId = "bladeburner-operation-set-team-size-popup";
|
||||
createPopup(popupId, TeamSizePopup, {
|
||||
bladeburner: props.bladeburner,
|
||||
action: props.action,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
function increaseLevel(): void {
|
||||
++props.action.level;
|
||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function decreaseLevel(): void {
|
||||
--props.action.level;
|
||||
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
props.action.autoLevel = event.target.checked;
|
||||
setRerender((old) => !old);
|
||||
const actionData = Operations[props.action.name];
|
||||
if (actionData === undefined) {
|
||||
throw new Error(`Cannot find data for ${props.action.name}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
{isActive ? (
|
||||
<>
|
||||
<Typography>
|
||||
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
|
||||
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
|
||||
</>
|
||||
) : (
|
||||
<CopyableText value={props.action.name} />
|
||||
)}
|
||||
</h2>
|
||||
{isActive ? (
|
||||
<p style={{ display: "block" }}>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
<Typography>
|
||||
{createProgressBarText({
|
||||
progress: computedActionTimeCurrent / props.bladeburner.actionTimeToComplete,
|
||||
})}
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<a onClick={onStart} className="a-link-button" style={{ margin: "3px", padding: "3px" }}>
|
||||
Start
|
||||
</a>
|
||||
<a onClick={onTeam} style={{ margin: "3px", padding: "3px" }} className="a-link-button">
|
||||
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
||||
</a>
|
||||
<CopyableText value={props.action.name} />
|
||||
<StartButton
|
||||
bladeburner={props.bladeburner}
|
||||
type={ActionTypes.Operation}
|
||||
name={props.action.name}
|
||||
rerender={rerender}
|
||||
/>
|
||||
<TeamSizeButton action={props.action} bladeburner={props.bladeburner} />
|
||||
</>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<pre className="tooltip" style={{ display: "inline-block" }}>
|
||||
<span className="tooltiptext">
|
||||
{props.action.getSuccessesNeededForNextLevel(BladeburnerConstants.OperationSuccessesPerLevel)} successes
|
||||
needed for next level
|
||||
</span>
|
||||
Level: {props.action.level} / {props.action.maxLevel}
|
||||
</pre>
|
||||
<a
|
||||
onClick={increaseLevel}
|
||||
style={{ padding: "2px", margin: "2px" }}
|
||||
className={`tooltip ${maxLevel ? "a-link-button-inactive" : "a-link-button"}`}
|
||||
>
|
||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↑
|
||||
</a>
|
||||
<a
|
||||
onClick={decreaseLevel}
|
||||
style={{ padding: "2px", margin: "2px" }}
|
||||
className={`tooltip ${props.action.level <= 1 ? "a-link-button-inactive" : "a-link-button"}`}
|
||||
>
|
||||
{isActive && <span className="tooltiptext">WARNING: changing the level will restart the Operation</span>}↓
|
||||
</a>
|
||||
|
||||
<ActionLevel action={props.action} bladeburner={props.bladeburner} isActive={isActive} rerender={rerender} />
|
||||
<br />
|
||||
<br />
|
||||
<pre style={{ display: "inline-block" }}>
|
||||
<span dangerouslySetInnerHTML={{ __html: props.action.desc }} />
|
||||
<Typography>
|
||||
{actionData.desc}
|
||||
<br />
|
||||
<br />
|
||||
Estimated success chance: <SuccessChance chance={estimatedSuccessChance} />{" "}
|
||||
{props.action.isStealth ? stealthIcon : <></>}
|
||||
{props.action.isKill ? killIcon : <></>}
|
||||
<SuccessChance action={props.action} bladeburner={props.bladeburner} />
|
||||
<br />
|
||||
Time Required: {convertTimeMsToTimeElapsedString(actionTime * 1000)}
|
||||
<br />
|
||||
@ -131,13 +85,9 @@ export function OperationElem(props: IProps): React.ReactElement {
|
||||
Successes: {props.action.successes}
|
||||
<br />
|
||||
Failures: {props.action.failures}
|
||||
</pre>
|
||||
</Typography>
|
||||
<br />
|
||||
<label className="tooltip" style={{ color: "white" }} htmlFor={autolevelCheckboxId}>
|
||||
Autolevel:
|
||||
<span className="tooltiptext">Automatically increase operation level when possible</span>
|
||||
</label>
|
||||
<input type="checkbox" id={autolevelCheckboxId} checked={props.action.autoLevel} onChange={onAutolevel} />
|
||||
</>
|
||||
<Autolevel rerender={rerender} action={props.action} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
@ -14,9 +14,7 @@ export function OperationList(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
{names.map((name: string) => (
|
||||
<li key={name} className="bladeburner-action">
|
||||
<OperationElem bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
||||
</li>
|
||||
<OperationElem key={name} bladeburner={props.bladeburner} action={operations[name]} player={props.player} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
||||
import { OperationList } from "./OperationList";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -11,7 +12,7 @@ interface IProps {
|
||||
export function OperationPage(props: IProps): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<p style={{ display: "block", margin: "4px", padding: "4px" }}>
|
||||
<Typography>
|
||||
Carry out operations for the Bladeburner division. Failing an operation will reduce your Bladeburner rank. It
|
||||
will also cause you to lose HP, which can lead to hospitalization. In general, operations are harder and more
|
||||
punishing than contracts, but are also more rewarding.
|
||||
@ -27,7 +28,7 @@ export function OperationPage(props: IProps): React.ReactElement {
|
||||
<br />
|
||||
You can unlock higher-level operations by successfully completing them. Higher-level operations are more
|
||||
difficult, but grant more rank and experience.
|
||||
</p>
|
||||
</Typography>
|
||||
<OperationList bladeburner={props.bladeburner} player={props.player} />
|
||||
</>
|
||||
);
|
||||
|
@ -3,6 +3,13 @@ import { CopyableText } from "../../ui/React/CopyableText";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Box from "@mui/material/Box";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
|
||||
interface IProps {
|
||||
skill: any;
|
||||
bladeburner: IBladeburner;
|
||||
@ -28,26 +35,26 @@ export function SkillElem(props: IProps): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 style={{ display: "inline-block" }}>
|
||||
<CopyableText value={props.skill.name} />
|
||||
</h2>
|
||||
<a
|
||||
onClick={onClick}
|
||||
style={{ display: "inline-block", margin: "3px", padding: "3px" }}
|
||||
className={canLevel && !maxLvl ? "a-link-button" : "a-link-button-inactive"}
|
||||
>
|
||||
Level
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
<p style={{ display: "block" }}>Level: {currentLevel}</p>
|
||||
<Paper sx={{ my: 1, p: 1 }}>
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<CopyableText variant="h6" color="primary" value={props.skill.name} />
|
||||
{!canLevel || maxLvl ? (
|
||||
<IconButton disabled>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton onClick={onClick}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
<Typography>Level: {currentLevel}</Typography>
|
||||
{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 (
|
||||
<>
|
||||
{Object.keys(Skills).map((skill: string) => (
|
||||
<li key={skill} className="bladeburner-action">
|
||||
<SkillElem bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
||||
</li>
|
||||
<SkillElem key={skill} bladeburner={props.bladeburner} skill={Skills[skill]} onUpgrade={props.onUpgrade} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -3,7 +3,8 @@ import { SkillList } from "./SkillList";
|
||||
import { BladeburnerConstants } from "../data/Constants";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
}
|
||||
@ -18,46 +19,45 @@ export function SkillPage(props: IProps): React.ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Typography>
|
||||
<strong>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</strong>
|
||||
</p>
|
||||
<p>
|
||||
You will gain one skill point every {BladeburnerConstants.RanksPerSkillPoint} ranks.
|
||||
<br />
|
||||
</Typography>
|
||||
<Typography>
|
||||
You will gain one skill point every{" "}
|
||||
{BladeburnerConstants.RanksPerSkillPoint * BitNodeMultipliers.BladeburnerSkillCost} ranks.
|
||||
<br />
|
||||
Note that when upgrading a skill, the benefit for that skill is additive. However, the effects of different
|
||||
skills with each other is multiplicative.
|
||||
<br />
|
||||
</p>
|
||||
<br />
|
||||
{valid(mults["successChanceAll"]) && <p>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</p>}
|
||||
</Typography>
|
||||
{valid(mults["successChanceAll"]) && (
|
||||
<Typography>Total Success Chance: x{formatNumber(mults["successChanceAll"], 3)}</Typography>
|
||||
)}
|
||||
{valid(mults["successChanceStealth"]) && (
|
||||
<p>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</p>
|
||||
<Typography>Stealth Success Chance: x{formatNumber(mults["successChanceStealth"], 3)}</Typography>
|
||||
)}
|
||||
{valid(mults["successChanceKill"]) && (
|
||||
<p>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}</p>
|
||||
<Typography>Retirement Success Chance: x{formatNumber(mults["successChanceKill"], 3)}</Typography>
|
||||
)}
|
||||
{valid(mults["successChanceContract"]) && (
|
||||
<p>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</p>
|
||||
<Typography>Contract Success Chance: x{formatNumber(mults["successChanceContract"], 3)}</Typography>
|
||||
)}
|
||||
{valid(mults["successChanceOperation"]) && (
|
||||
<p>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}</p>
|
||||
<Typography>Operation Success Chance: x{formatNumber(mults["successChanceOperation"], 3)}</Typography>
|
||||
)}
|
||||
{valid(mults["successChanceEstimate"]) && (
|
||||
<p>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}</p>
|
||||
<Typography>Synthoid Data Estimate: x{formatNumber(mults["successChanceEstimate"], 3)}</Typography>
|
||||
)}
|
||||
{valid(mults["actionTime"]) && <p>Action Time: x{formatNumber(mults["actionTime"], 3)}</p>}
|
||||
{valid(mults["effHack"]) && <p>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</p>}
|
||||
{valid(mults["effStr"]) && <p>Strength: x{formatNumber(mults["effStr"], 3)}</p>}
|
||||
{valid(mults["effDef"]) && <p>Defense: x{formatNumber(mults["effDef"], 3)}</p>}
|
||||
{valid(mults["effDex"]) && <p>Dexterity: x{formatNumber(mults["effDex"], 3)}</p>}
|
||||
{valid(mults["effAgi"]) && <p>Agility: x{formatNumber(mults["effAgi"], 3)}</p>}
|
||||
{valid(mults["effCha"]) && <p>Charisma: x{formatNumber(mults["effCha"], 3)}</p>}
|
||||
{valid(mults["effInt"]) && <p>Intelligence: x{formatNumber(mults["effInt"], 3)}</p>}
|
||||
{valid(mults["stamina"]) && <p>Stamina: x{formatNumber(mults["stamina"], 3)}</p>}
|
||||
{valid(mults["money"]) && <p>Contract Money: x{formatNumber(mults["money"], 3)}</p>}
|
||||
{valid(mults["expGain"]) && <p>Exp Gain: x{formatNumber(mults["expGain"], 3)}</p>}
|
||||
<br />
|
||||
{valid(mults["actionTime"]) && <Typography>Action Time: x{formatNumber(mults["actionTime"], 3)}</Typography>}
|
||||
{valid(mults["effHack"]) && <Typography>Hacking Skill: x{formatNumber(mults["effHack"], 3)}</Typography>}
|
||||
{valid(mults["effStr"]) && <Typography>Strength: x{formatNumber(mults["effStr"], 3)}</Typography>}
|
||||
{valid(mults["effDef"]) && <Typography>Defense: x{formatNumber(mults["effDef"], 3)}</Typography>}
|
||||
{valid(mults["effDex"]) && <Typography>Dexterity: x{formatNumber(mults["effDex"], 3)}</Typography>}
|
||||
{valid(mults["effAgi"]) && <Typography>Agility: x{formatNumber(mults["effAgi"], 3)}</Typography>}
|
||||
{valid(mults["effCha"]) && <Typography>Charisma: x{formatNumber(mults["effCha"], 3)}</Typography>}
|
||||
{valid(mults["effInt"]) && <Typography>Intelligence: x{formatNumber(mults["effInt"], 3)}</Typography>}
|
||||
{valid(mults["stamina"]) && <Typography>Stamina: x{formatNumber(mults["stamina"], 3)}</Typography>}
|
||||
{valid(mults["money"]) && <Typography>Contract Money: x{formatNumber(mults["money"], 3)}</Typography>}
|
||||
{valid(mults["expGain"]) && <Typography>Exp Gain: x{formatNumber(mults["expGain"], 3)}</Typography>}
|
||||
<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 { StatsTable } from "../../ui/React/StatsTable";
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { createPopup } from "../../ui/React/createPopup";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { IRouter } from "../../ui/Router";
|
||||
import { joinFaction } from "../../Faction/FactionHelpers";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
|
||||
import { TravelPopup } from "./TravelPopup";
|
||||
import { TravelModal } from "./TravelModal";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Paper from "@mui/material/Paper";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
@ -21,131 +24,141 @@ interface IProps {
|
||||
}
|
||||
|
||||
export function Stats(props: IProps): React.ReactElement {
|
||||
const [travelOpen, setTravelOpen] = useState(false);
|
||||
const setRerender = useState(false)[1];
|
||||
|
||||
const inFaction = props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction;
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => setRerender((old) => !old), 1000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
function openStaminaHelp(): void {
|
||||
dialogBoxCreate(
|
||||
"Performing actions will use up your stamina.<br><br>" +
|
||||
"Your max stamina is determined primarily by your agility stat.<br><br>" +
|
||||
"Your stamina gain rate is determined by both your agility and your " +
|
||||
"max stamina. Higher max stamina leads to a higher gain rate.<br><br>" +
|
||||
"Once your " +
|
||||
"stamina falls below 50% of its max value, it begins to negatively " +
|
||||
"affect the success rate of your contracts/operations. This penalty " +
|
||||
"is shown in the overview panel. If the penalty is 15%, then this means " +
|
||||
"your success rate would be multipled by 85% (100 - 15).<br><br>" +
|
||||
"Your max stamina and stamina gain rate can also be increased by " +
|
||||
"training, or through skills and Augmentation upgrades.",
|
||||
);
|
||||
}
|
||||
|
||||
function openPopulationHelp(): void {
|
||||
dialogBoxCreate(
|
||||
"The success rate of your contracts/operations depends on " +
|
||||
"the population of Synthoids in your current city. " +
|
||||
"The success rate that is shown to you is only an estimate, " +
|
||||
"and it is based on your Synthoid population estimate.<br><br>" +
|
||||
"Therefore, it is important that this Synthoid population estimate " +
|
||||
"is accurate so that you have a better idea of your " +
|
||||
"success rate for contracts/operations. Certain " +
|
||||
"actions will increase the accuracy of your population " +
|
||||
"estimate.<br><br>" +
|
||||
"The Synthoid populations of cities can change due to your " +
|
||||
"actions or random events. If random events occur, they will " +
|
||||
"be logged in the Bladeburner Console.",
|
||||
);
|
||||
}
|
||||
|
||||
function openTravel(): void {
|
||||
const popupId = "bladeburner-travel-popup";
|
||||
createPopup(popupId, TravelPopup, {
|
||||
bladeburner: props.bladeburner,
|
||||
popupId: popupId,
|
||||
});
|
||||
}
|
||||
|
||||
function openFaction(): void {
|
||||
if (!inFaction) return;
|
||||
const faction = Factions["Bladeburners"];
|
||||
if (faction.isMember) {
|
||||
props.router.toFaction(faction);
|
||||
} else {
|
||||
if (props.bladeburner.rank >= BladeburnerConstants.RankNeededForFaction) {
|
||||
joinFaction(faction);
|
||||
dialogBoxCreate("Congratulations! You were accepted into the Bladeburners faction");
|
||||
} else {
|
||||
dialogBoxCreate("You need a rank of 25 to join the Bladeburners Faction!");
|
||||
}
|
||||
if (!faction.isMember) {
|
||||
joinFaction(faction);
|
||||
}
|
||||
|
||||
props.router.toFaction(faction);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Rank: {formatNumber(props.bladeburner.rank, 2)}
|
||||
<span className="tooltiptext">Your rank within the Bladeburner division.</span>
|
||||
</p>
|
||||
<Paper sx={{ p: 1 }}>
|
||||
<Box display="flex">
|
||||
<Tooltip title={<Typography>Your rank within the Bladeburner division.</Typography>}>
|
||||
<Typography>Rank: {formatNumber(props.bladeburner.rank, 2)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p>
|
||||
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
||||
</p>
|
||||
<div className="help-tip" onClick={openStaminaHelp}>
|
||||
?
|
||||
</div>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Performing actions will use up your stamina.
|
||||
<br />
|
||||
<br />
|
||||
Your max stamina is determined primarily by your agility stat.
|
||||
<br />
|
||||
<br />
|
||||
Your stamina gain rate is determined by both your agility and your max stamina. Higher max stamina leads
|
||||
to a higher gain rate.
|
||||
<br />
|
||||
<br />
|
||||
Once your stamina falls below 50% of its max value, it begins to negatively affect the success rate of
|
||||
your contracts/operations. This penalty is shown in the overview panel. If the penalty is 15%, then this
|
||||
means your success rate would be multipled by 85% (100 - 15).
|
||||
<br />
|
||||
<br />
|
||||
Your max stamina and stamina gain rate can also be increased by training, or through skills and
|
||||
Augmentation upgrades.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Stamina: {formatNumber(props.bladeburner.stamina, 3)} / {formatNumber(props.bladeburner.maxStamina, 3)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p>Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%</p>
|
||||
<Typography>
|
||||
Stamina Penalty: {formatNumber((1 - props.bladeburner.calculateStaminaPenalty()) * 100, 1)}%
|
||||
</Typography>
|
||||
<br />
|
||||
<p>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</p>
|
||||
<p>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</p>
|
||||
<Typography>Team Size: {formatNumber(props.bladeburner.teamSize, 0)}</Typography>
|
||||
<Typography>Team Members Lost: {formatNumber(props.bladeburner.teamLost, 0)}</Typography>
|
||||
<br />
|
||||
<p>Num Times Hospitalized: {props.bladeburner.numHosp}</p>
|
||||
<p>
|
||||
<Typography>Num Times Hospitalized: {props.bladeburner.numHosp}</Typography>
|
||||
<Typography>
|
||||
Money Lost From Hospitalizations: <Money money={props.bladeburner.moneyLost} />
|
||||
</p>
|
||||
</Typography>
|
||||
<br />
|
||||
<p>Current City: {props.bladeburner.city}</p>
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
||||
<span className="tooltiptext">
|
||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city.
|
||||
</span>
|
||||
</p>
|
||||
<div className="help-tip" onClick={openPopulationHelp}>
|
||||
?
|
||||
</div>
|
||||
<Typography>Current City: {props.bladeburner.city}</Typography>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner division's estimate of how many Synthoids exist in your current city. An accurate
|
||||
population count increases success rate estimates.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Est. Synthoid Population: {numeralWrapper.formatPopulation(props.bladeburner.getCurrentCity().popEst)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
||||
<span className="tooltiptext">
|
||||
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
||||
</span>
|
||||
</p>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
This is your Bladeburner divison's estimate of how many Synthoid communities exist in your current city.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Est. Synthoid Communities: {formatNumber(props.bladeburner.getCurrentCity().comms, 0)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}
|
||||
<span className="tooltiptext">
|
||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a chaos
|
||||
level can make contracts and operations harder.
|
||||
</span>
|
||||
</p>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
The city's chaos level due to tensions and conflicts between humans and Synthoids. Having too high of a
|
||||
chaos level can make contracts and operations harder.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>City Chaos: {formatNumber(props.bladeburner.getCurrentCity().chaos)}</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
<br />
|
||||
<p className="tooltip" style={{ display: "inline-block" }}>
|
||||
Bonus time:{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
||||
)}
|
||||
<br />
|
||||
<span className="tooltiptext">
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by browser).
|
||||
Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||
</span>
|
||||
</p>
|
||||
<p>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</p>
|
||||
{(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000 > 15000 && (
|
||||
<>
|
||||
<Box display="flex">
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
You gain bonus time while offline or when the game is inactive (e.g. when the tab is throttled by
|
||||
browser). Bonus time makes the Bladeburner mechanic progress faster, up to 5x the normal speed.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>
|
||||
Bonus time:{" "}
|
||||
{convertTimeMsToTimeElapsedString(
|
||||
(props.bladeburner.storedCycles / BladeburnerConstants.CyclesPerSecond) * 1000,
|
||||
)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
|
||||
<br />
|
||||
<StatsTable
|
||||
rows={[
|
||||
@ -156,17 +169,15 @@ export function Stats(props: IProps): React.ReactElement {
|
||||
]}
|
||||
/>
|
||||
<br />
|
||||
<a onClick={openTravel} className="a-link-button" style={{ display: "inline-block" }}>
|
||||
Travel
|
||||
</a>
|
||||
<a onClick={openFaction} className="a-link-button tooltip" style={{ display: "inline-block" }}>
|
||||
<span className="tooltiptext">
|
||||
Apply to the Bladeburner Faction, or go to the faction page if you are already a member
|
||||
<Button onClick={() => setTravelOpen(true)}>Travel</Button>
|
||||
<Tooltip title={!inFaction ? <Typography>Rank 25 required.</Typography> : ""}>
|
||||
<span>
|
||||
<Button disabled={!inFaction} onClick={openFaction}>
|
||||
Faction
|
||||
</Button>
|
||||
</span>
|
||||
Faction
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
</>
|
||||
</Tooltip>
|
||||
<TravelModal open={travelOpen} onClose={() => setTravelOpen(false)} bladeburner={props.bladeburner} />
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
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 { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import { StealthIcon } from "./StealthIcon";
|
||||
import { KillIcon } from "./KillIcon";
|
||||
import { IAction } from "../IAction";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
|
||||
interface IProps {
|
||||
chance: number[];
|
||||
bladeburner: IBladeburner;
|
||||
action: IAction;
|
||||
}
|
||||
|
||||
export function SuccessChance(props: IProps): React.ReactElement {
|
||||
if (props.chance[0] === props.chance[1]) {
|
||||
return <>{formatNumber(props.chance[0] * 100, 1)}%</>;
|
||||
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
|
||||
|
||||
let chance = <></>;
|
||||
if (estimatedSuccessChance[0] === estimatedSuccessChance[1]) {
|
||||
chance = <>{formatNumber(estimatedSuccessChance[0] * 100, 1)}%</>;
|
||||
} else {
|
||||
chance = (
|
||||
<>
|
||||
{formatNumber(estimatedSuccessChance[0] * 100, 1)}% ~ {formatNumber(estimatedSuccessChance[1] * 100, 1)}%
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{formatNumber(props.chance[0] * 100, 1)}% ~ {formatNumber(props.chance[1] * 100, 1)}%
|
||||
Estimated success chance: {chance} {props.action.isStealth ? <StealthIcon /> : <></>}
|
||||
{props.action.isKill ? <KillIcon /> : <></>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
22
src/Bladeburner/ui/TeamSizeButton.tsx
Normal file
22
src/Bladeburner/ui/TeamSizeButton.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React, { useState } from "react";
|
||||
import { Operation } from "../Operation";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { TeamSizeModal } from "./TeamSizeModal";
|
||||
import { formatNumber } from "../../utils/StringHelperFunctions";
|
||||
import Button from "@mui/material/Button";
|
||||
interface IProps {
|
||||
action: Operation;
|
||||
bladeburner: IBladeburner;
|
||||
}
|
||||
export function TeamSizeButton(props: IProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button disabled={props.bladeburner.teamSize === 0} onClick={() => setOpen(true)}>
|
||||
Set Team Size (Curr Size: {formatNumber(props.action.teamCount, 0)})
|
||||
</Button>
|
||||
<TeamSizeModal open={open} onClose={() => setOpen(false)} action={props.action} bladeburner={props.bladeburner} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,16 +1,20 @@
|
||||
import React, { useState } from "react";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { Action } from "../Action";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
action: Action;
|
||||
popupId: string;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function TeamSizePopup(props: IProps): React.ReactElement {
|
||||
export function TeamSizeModal(props: IProps): React.ReactElement {
|
||||
const [teamSize, setTeamSize] = useState<number | undefined>();
|
||||
|
||||
function confirmTeamSize(): void {
|
||||
@ -21,25 +25,25 @@ export function TeamSizePopup(props: IProps): React.ReactElement {
|
||||
} else {
|
||||
props.action.teamCount = num;
|
||||
}
|
||||
removePopup(props.popupId);
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
function onTeamSize(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
const x = parseFloat(event.target.value);
|
||||
if (x > props.bladeburner.teamSize) setTeamSize(props.bladeburner.teamSize);
|
||||
else setTeamSize(x);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<Typography>
|
||||
Enter the amount of team members you would like to take on this Op. If you do not have the specified number of
|
||||
team members, then as many as possible will be used. Note that team members may be lost during operations.
|
||||
</p>
|
||||
<input
|
||||
autoFocus
|
||||
type="number"
|
||||
placeholder="Team size"
|
||||
className="text-input"
|
||||
onChange={(event) => setTeamSize(parseFloat(event.target.value))}
|
||||
/>
|
||||
<a className="a-link-button" onClick={confirmTeamSize}>
|
||||
</Typography>
|
||||
<TextField autoFocus type="number" placeholder="Team size" value={teamSize} onChange={onTeamSize} />
|
||||
<Button sx={{ mx: 2 }} onClick={confirmTeamSize}>
|
||||
Confirm
|
||||
</a>
|
||||
</>
|
||||
</Button>
|
||||
</Modal>
|
||||
);
|
||||
}
|
41
src/Bladeburner/ui/TravelModal.tsx
Normal file
41
src/Bladeburner/ui/TravelModal.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from "react";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { WorldMap } from "../../ui/React/WorldMap";
|
||||
import { Modal } from "../../ui/React/Modal";
|
||||
import { CityName } from "../../Locations/data/CityNames";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function TravelModal(props: IProps): React.ReactElement {
|
||||
function travel(city: string): void {
|
||||
props.bladeburner.city = city;
|
||||
props.onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose}>
|
||||
<>
|
||||
<Typography>
|
||||
Travel to a different city for your Bladeburner activities. This does not cost any money. The city you are in
|
||||
for your Bladeburner duties does not affect your location in the game otherwise.
|
||||
</Typography>
|
||||
{Settings.DisableASCIIArt ? (
|
||||
Object.values(CityName).map((city: CityName) => (
|
||||
<Button key={city} onClick={() => travel(city)}>
|
||||
{city}
|
||||
</Button>
|
||||
))
|
||||
) : (
|
||||
<WorldMap currentCity={props.bladeburner.city as CityName} onTravel={(city: CityName) => travel(city)} />
|
||||
)}
|
||||
</>
|
||||
</Modal>
|
||||
);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import React from "react";
|
||||
import { removePopup } from "../../ui/React/createPopup";
|
||||
import { IBladeburner } from "../IBladeburner";
|
||||
import { WorldMap } from "../../ui/React/WorldMap";
|
||||
import { CityName } from "../../Locations/data/CityNames";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
|
||||
interface IProps {
|
||||
bladeburner: IBladeburner;
|
||||
popupId: string;
|
||||
}
|
||||
|
||||
export function TravelPopup(props: IProps): React.ReactElement {
|
||||
function travel(city: string): void {
|
||||
props.bladeburner.city = city;
|
||||
removePopup(props.popupId);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
Travel to a different city for your Bladeburner activities. This does not cost any money. The city you are in
|
||||
for your Bladeburner duties does not affect your location in the game otherwise.
|
||||
</p>
|
||||
{Settings.DisableASCIIArt ? (
|
||||
Object.values(CityName).map((city: CityName) => (
|
||||
<button key={city} className="std-button" onClick={() => travel(city)}>
|
||||
{city}
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<WorldMap currentCity={props.bladeburner.city as CityName} onTravel={(city: CityName) => travel(city)} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -7,9 +7,11 @@ import { Deck } from "./CardDeck/Deck";
|
||||
import { Hand } from "./CardDeck/Hand";
|
||||
import { InputAdornment } from "@mui/material";
|
||||
import { ReactCard } from "./CardDeck/ReactCard";
|
||||
import { MuiTextField } from "../ui/React/MuiTextField";
|
||||
import { MuiButton } from "../ui/React/MuiButton";
|
||||
import { MuiPaper } from "../ui/React/MuiPaper";
|
||||
import Button from "@mui/material/Button";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
const MAX_BET = 100e6;
|
||||
|
||||
@ -305,10 +307,10 @@ export class Blackjack extends Game<Props, State> {
|
||||
const dealerHandValues = this.getHandDisplayValues(dealerHand);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{/* Wager input */}
|
||||
<div>
|
||||
<MuiTextField
|
||||
<Box>
|
||||
<TextField
|
||||
value={betInput}
|
||||
label={
|
||||
<>
|
||||
@ -322,93 +324,88 @@ export class Blackjack extends Game<Props, State> {
|
||||
error={wagerInvalid}
|
||||
helperText={wagerInvalid ? wagerInvalidHelperText : ""}
|
||||
type="number"
|
||||
variant="filled"
|
||||
style={{
|
||||
width: "200px",
|
||||
}}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">$</InputAdornment>,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Typography>$</Typography>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<p>
|
||||
<Typography>
|
||||
{"Total earnings this session: "}
|
||||
<Money money={gains} />
|
||||
</p>
|
||||
</div>
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Buttons */}
|
||||
{!gameInProgress ? (
|
||||
<div>
|
||||
<MuiButton onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
|
||||
Start
|
||||
</MuiButton>
|
||||
</div>
|
||||
<Button onClick={this.startOnClick} disabled={wagerInvalid || !this.canStartGame()}>
|
||||
Start
|
||||
</Button>
|
||||
) : (
|
||||
<div>
|
||||
<MuiButton onClick={this.playerHit}>Hit</MuiButton>
|
||||
<MuiButton color="secondary" onClick={this.playerStay}>
|
||||
<>
|
||||
<Button onClick={this.playerHit}>Hit</Button>
|
||||
<Button color="secondary" onClick={this.playerStay}>
|
||||
Stay
|
||||
</MuiButton>
|
||||
</div>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 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. */}
|
||||
{(gameInProgress || result !== Result.Pending) && (
|
||||
<div>
|
||||
<MuiPaper variant="outlined" elevation={2}>
|
||||
<pre>Player</pre>
|
||||
{playerHand.cards.map((card, i) => (
|
||||
<ReactCard card={card} key={i} />
|
||||
))}
|
||||
<>
|
||||
<Box display="flex">
|
||||
<Paper elevation={2}>
|
||||
<pre>Player</pre>
|
||||
{playerHand.cards.map((card, i) => (
|
||||
<ReactCard card={card} key={i} />
|
||||
))}
|
||||
|
||||
<pre>Value(s): </pre>
|
||||
{playerHandValues.map((value, i) => (
|
||||
<pre key={i}>{value}</pre>
|
||||
))}
|
||||
</MuiPaper>
|
||||
<pre>Value(s): </pre>
|
||||
{playerHandValues.map((value, i) => (
|
||||
<pre key={i}>{value}</pre>
|
||||
))}
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
<br />
|
||||
|
||||
<MuiPaper variant="outlined" elevation={2}>
|
||||
<pre>Dealer</pre>
|
||||
{dealerHand.cards.map((card, i) => (
|
||||
// Hide every card except the first while game is in progress
|
||||
<ReactCard card={card} hidden={gameInProgress && i !== 0} key={i} />
|
||||
))}
|
||||
<Box display="flex">
|
||||
<Paper elevation={2}>
|
||||
<pre>Dealer</pre>
|
||||
{dealerHand.cards.map((card, i) => (
|
||||
// Hide every card except the first while game is in progress
|
||||
<ReactCard card={card} hidden={gameInProgress && i !== 0} key={i} />
|
||||
))}
|
||||
|
||||
{!gameInProgress && (
|
||||
<>
|
||||
<pre>Value(s): </pre>
|
||||
{dealerHandValues.map((value, i) => (
|
||||
<pre key={i}>{value}</pre>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</MuiPaper>
|
||||
</div>
|
||||
{!gameInProgress && (
|
||||
<>
|
||||
<pre>Value(s): </pre>
|
||||
{dealerHandValues.map((value, i) => (
|
||||
<pre key={i}>{value}</pre>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Results from previous round */}
|
||||
{result !== Result.Pending && (
|
||||
<p>
|
||||
<Typography>
|
||||
{result}
|
||||
{this.isPlayerWinResult(result) && (
|
||||
<>
|
||||
{" You gained "}
|
||||
<Money money={this.state.bet} />
|
||||
</>
|
||||
)}
|
||||
{result === Result.DealerWon && (
|
||||
<>
|
||||
{" You lost "}
|
||||
<Money money={this.state.bet} />
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
{this.isPlayerWinResult(result) && <Money money={this.state.bet} />}
|
||||
{result === Result.DealerWon && <Money money={this.state.bet} />}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,44 @@
|
||||
import React, { FC } from "react";
|
||||
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 = {
|
||||
card: Card;
|
||||
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 }) => {
|
||||
const classes = useStyles();
|
||||
let suit: React.ReactNode;
|
||||
switch (card.suit) {
|
||||
case Suit.Clubs:
|
||||
@ -25,11 +57,11 @@ export const ReactCard: FC<Props> = ({ card, hidden }) => {
|
||||
throw new Error(`MissingCaseException: ${card.suit}`);
|
||||
}
|
||||
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>
|
||||
<div className={`suit`}>{hidden ? " - " : suit}</div>
|
||||
<span className={classes.value}>{hidden ? " - " : card.formatValue()}</span>
|
||||
<span>{hidden ? " - " : suit}</span>
|
||||
</>
|
||||
</div>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
@ -3,44 +3,32 @@
|
||||
*
|
||||
* 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 { StdButton } from "../ui/React/StdButton";
|
||||
import { BadRNG } from "./RNG";
|
||||
import { Game } from "./Game";
|
||||
import { win, reachedLimit } from "./Game";
|
||||
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 = {
|
||||
p: IPlayer;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
investment: number;
|
||||
result: any;
|
||||
status: string;
|
||||
playLock: boolean;
|
||||
};
|
||||
|
||||
const minPlay = 0;
|
||||
const maxPlay = 10e3;
|
||||
|
||||
export class CoinFlip extends Game<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
export function CoinFlip(props: IProps): React.ReactElement {
|
||||
const [investment, setInvestment] = useState(1000);
|
||||
const [result, setResult] = useState(<span> </span>);
|
||||
const [status, setStatus] = useState("");
|
||||
const [playLock, setPlayLock] = useState(false);
|
||||
|
||||
this.state = {
|
||||
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 {
|
||||
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
let investment: number = parseInt(e.currentTarget.value);
|
||||
if (isNaN(investment)) {
|
||||
investment = minPlay;
|
||||
@ -51,11 +39,11 @@ export class CoinFlip extends Game<IProps, IState> {
|
||||
if (investment < minPlay) {
|
||||
investment = minPlay;
|
||||
}
|
||||
this.setState({ investment: investment });
|
||||
setInvestment(investment);
|
||||
}
|
||||
|
||||
play(guess: string): void {
|
||||
if (this.reachedLimit(this.props.p)) return;
|
||||
function play(guess: string): void {
|
||||
if (reachedLimit(props.p)) return;
|
||||
const v = BadRNG.random();
|
||||
let letter: string;
|
||||
if (v < 0.5) {
|
||||
@ -64,39 +52,48 @@ export class CoinFlip extends Game<IProps, IState> {
|
||||
letter = "T";
|
||||
}
|
||||
const correct: boolean = guess === letter;
|
||||
this.setState({
|
||||
result: <span className={correct ? "text" : "failure"}>{letter}</span>,
|
||||
status: correct ? " win!" : "lose!",
|
||||
playLock: true,
|
||||
});
|
||||
setTimeout(() => this.setState({ playLock: false }), 250);
|
||||
|
||||
setResult(
|
||||
<Box display="flex">
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }} color={correct ? "primary" : "error"}>
|
||||
{letter}
|
||||
</Typography>
|
||||
</Box>,
|
||||
);
|
||||
setStatus(correct ? " win!" : "lose!");
|
||||
setPlayLock(true);
|
||||
|
||||
setTimeout(() => setPlayLock(false), 250);
|
||||
if (correct) {
|
||||
this.win(this.props.p, this.state.investment);
|
||||
win(props.p, investment);
|
||||
} 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 (
|
||||
<>
|
||||
<pre>{`+———————+`}</pre>
|
||||
<pre>{`| | | |`}</pre>
|
||||
<pre>
|
||||
{`| | `}
|
||||
{this.state.result}
|
||||
{` | |`}
|
||||
</pre>
|
||||
<pre>{`| | | |`}</pre>
|
||||
<pre>{`+———————+`}</pre>
|
||||
<span className="text">Play for: </span>
|
||||
<input type="number" className="text-input" onChange={this.updateInvestment} value={this.state.investment} />
|
||||
<br />
|
||||
<StdButton onClick={trusted(() => this.play("H"))} text={"Head!"} disabled={this.state.playLock} />
|
||||
<StdButton onClick={trusted(() => this.play("T"))} text={"Tail!"} disabled={this.state.playLock} />
|
||||
<h1>{this.state.status}</h1>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Typography>Result:</Typography> {result}
|
||||
<Box display="flex" alignItems="center">
|
||||
<TextField
|
||||
type="number"
|
||||
onChange={updateInvestment}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<>
|
||||
<Button onClick={trusted(() => play("H"))} disabled={playLock}>
|
||||
Head!
|
||||
</Button>
|
||||
<Button onClick={trusted(() => play("T"))} disabled={playLock}>
|
||||
Tail!
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Typography variant="h3">{status}</Typography>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -4,6 +4,19 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
|
||||
|
||||
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> {
|
||||
win(p: IPlayer, n: number): void {
|
||||
p.gainMoney(n);
|
||||
|
@ -1,25 +1,18 @@
|
||||
import * as React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { StdButton } from "../ui/React/StdButton";
|
||||
import { Money } from "../ui/React/Money";
|
||||
import { Game } from "./Game";
|
||||
import { win, reachedLimit } from "./Game";
|
||||
import { WHRNG } from "./RNG";
|
||||
import { trusted } from "./utils";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Button from "@mui/material/Button";
|
||||
import TextField from "@mui/material/TextField";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
investment: number;
|
||||
canPlay: boolean;
|
||||
status: string | JSX.Element;
|
||||
n: number;
|
||||
lock: boolean;
|
||||
strategy: Strategy;
|
||||
};
|
||||
|
||||
const minPlay = 0;
|
||||
const maxPlay = 1e7;
|
||||
|
||||
@ -118,48 +111,32 @@ function Single(s: number): Strategy {
|
||||
};
|
||||
}
|
||||
|
||||
export class Roulette extends Game<IProps, IState> {
|
||||
interval = -1;
|
||||
rng: WHRNG;
|
||||
export function Roulette(props: IProps): React.ReactElement {
|
||||
const [rng] = useState(new WHRNG(new Date().getTime()));
|
||||
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) {
|
||||
super(props);
|
||||
useEffect(() => {
|
||||
const i = window.setInterval(step, 50);
|
||||
return () => clearInterval(i);
|
||||
});
|
||||
|
||||
this.rng = new WHRNG(new Date().getTime());
|
||||
this.state = {
|
||||
investment: 1000,
|
||||
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) });
|
||||
function step(): void {
|
||||
if (!lock) {
|
||||
setN(Math.floor(Math.random() * 37));
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
|
||||
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
let investment: number = parseInt(e.currentTarget.value);
|
||||
if (isNaN(investment)) {
|
||||
investment = minPlay;
|
||||
@ -170,265 +147,312 @@ export class Roulette extends Game<IProps, IState> {
|
||||
if (investment < minPlay) {
|
||||
investment = minPlay;
|
||||
}
|
||||
this.setState({ investment: investment });
|
||||
setInvestment(investment);
|
||||
}
|
||||
|
||||
currentNumber(): string {
|
||||
if (this.state.n === 0) return "0";
|
||||
const color = isRed(this.state.n) ? "R" : "B";
|
||||
return `${this.state.n}${color}`;
|
||||
function currentNumber(): string {
|
||||
if (n === 0) return "0";
|
||||
const color = isRed(n) ? "R" : "B";
|
||||
return `${n}${color}`;
|
||||
}
|
||||
|
||||
play(s: Strategy): void {
|
||||
if (this.reachedLimit(this.props.p)) return;
|
||||
this.setState({
|
||||
canPlay: false,
|
||||
lock: false,
|
||||
status: "playing",
|
||||
strategy: s,
|
||||
});
|
||||
function play(s: Strategy): void {
|
||||
if (reachedLimit(props.p)) return;
|
||||
|
||||
setCanPlay(false);
|
||||
setLock(false);
|
||||
setStatus("playing");
|
||||
setStrategy(s);
|
||||
|
||||
setTimeout(() => {
|
||||
let n = Math.floor(this.rng.random() * 37);
|
||||
let n = Math.floor(rng.random() * 37);
|
||||
let status = <></>;
|
||||
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!
|
||||
if (playerWin && Math.random() > 0.9) {
|
||||
playerWin = false;
|
||||
while (this.state.strategy.match(n)) {
|
||||
while (strategy.match(n)) {
|
||||
n = (n + 1) % 36;
|
||||
}
|
||||
}
|
||||
if (playerWin) {
|
||||
gain = this.state.investment * this.state.strategy.payout;
|
||||
gain = investment * strategy.payout;
|
||||
status = (
|
||||
<>
|
||||
won <Money money={gain} />
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
gain = -this.state.investment;
|
||||
gain = -investment;
|
||||
status = (
|
||||
<>
|
||||
lost <Money money={-gain} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
this.win(this.props.p, gain);
|
||||
this.setState({
|
||||
canPlay: true,
|
||||
lock: true,
|
||||
status: status,
|
||||
n: n,
|
||||
});
|
||||
this.reachedLimit(this.props.p);
|
||||
win(props.p, gain);
|
||||
|
||||
setCanPlay(true);
|
||||
setLock(true);
|
||||
setStatus(status);
|
||||
setN(n);
|
||||
|
||||
reachedLimit(props.p);
|
||||
}, 1600);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
return (
|
||||
<>
|
||||
<h1>{this.currentNumber()}</h1>
|
||||
<input
|
||||
type="number"
|
||||
className="text-input"
|
||||
onChange={this.updateInvestment}
|
||||
placeholder={"Amount to play"}
|
||||
value={this.state.investment}
|
||||
disabled={!this.state.canPlay}
|
||||
/>
|
||||
<h1>{this.state.status}</h1>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<StdButton text={"3"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(3)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"6"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(6)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"9"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(9)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"12"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(12)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"15"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(15)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"18"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(18)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"21"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(21)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"24"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(24)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"27"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(27)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"30"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(30)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"33"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(33)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"36"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(36)))} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<StdButton text={"2"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(2)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"5"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(5)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"8"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(8)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"11"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(11)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"14"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(14)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"17"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(17)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"20"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(20)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"23"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(23)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"26"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(26)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"29"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(29)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"32"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(32)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"35"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(35)))} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<StdButton text={"1"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(1)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"4"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(4)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"7"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(7)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"10"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(10)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"13"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(13)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"16"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(16)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"19"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(19)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"22"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(22)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"25"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(25)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"28"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(28)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"31"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(31)))} />
|
||||
</td>
|
||||
<td>
|
||||
<StdButton text={"34"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(34)))} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<StdButton
|
||||
text={"1 to 12"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Third1))}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={4}>
|
||||
<StdButton
|
||||
text={"13 to 24"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Third2))}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={4}>
|
||||
<StdButton
|
||||
text={"25 to 36"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Third3))}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={2}>
|
||||
<StdButton
|
||||
text={"Red"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Red))}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={2}>
|
||||
<StdButton
|
||||
text={"Black"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Black))}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={2}>
|
||||
<StdButton
|
||||
text={"Odd"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Odd))}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={2}>
|
||||
<StdButton
|
||||
text={"Even"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Even))}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={2}>
|
||||
<StdButton
|
||||
text={"High"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.High))}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={2}>
|
||||
<StdButton
|
||||
text={"Low"}
|
||||
disabled={!this.state.canPlay}
|
||||
onClick={trusted(() => this.play(strategies.Low))}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<StdButton text={"0"} disabled={!this.state.canPlay} onClick={trusted(() => this.play(Single(0)))} />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Typography variant="h4">{currentNumber()}</Typography>
|
||||
<TextField type="number" onChange={updateInvestment} placeholder={"Amount to play"} disabled={!canPlay} />
|
||||
<Typography variant="h4">{status}</Typography>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(3)))}>
|
||||
3
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(6)))}>
|
||||
6
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(9)))}>
|
||||
9
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(12)))}>
|
||||
12
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(15)))}>
|
||||
15
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(18)))}>
|
||||
18
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(21)))}>
|
||||
21
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(24)))}>
|
||||
24
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(27)))}>
|
||||
27
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(30)))}>
|
||||
30
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(33)))}>
|
||||
33
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(36)))}>
|
||||
36
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(2)))}>
|
||||
2
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(5)))}>
|
||||
5
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(8)))}>
|
||||
8
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(11)))}>
|
||||
11
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(14)))}>
|
||||
14
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(17)))}>
|
||||
17
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(20)))}>
|
||||
20
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(23)))}>
|
||||
23
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(26)))}>
|
||||
26
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(29)))}>
|
||||
29
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(32)))}>
|
||||
32
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(35)))}>
|
||||
35
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(1)))}>
|
||||
1
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(4)))}>
|
||||
4
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(7)))}>
|
||||
7
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(10)))}>
|
||||
10
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(13)))}>
|
||||
13
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(16)))}>
|
||||
16
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(19)))}>
|
||||
19
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(22)))}>
|
||||
22
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(25)))}>
|
||||
25
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(28)))}>
|
||||
28
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(31)))}>
|
||||
31
|
||||
</Button>
|
||||
</td>
|
||||
<td>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(Single(34)))}>
|
||||
34
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third1))}>
|
||||
1 to 12
|
||||
</Button>
|
||||
</td>
|
||||
<td colSpan={4}>
|
||||
<Button disabled={!canPlay} onClick={trusted(() => play(strategies.Third2))}>
|
||||
13 to 24
|
||||
</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 { StdButton } from "../ui/React/StdButton";
|
||||
import { Money } from "../ui/React/Money";
|
||||
import { WHRNG } from "./RNG";
|
||||
import { Game } from "./Game";
|
||||
import { win, reachedLimit } from "./Game";
|
||||
import { trusted } from "./utils";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Button from "@mui/material/Button";
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
};
|
||||
|
||||
type IState = {
|
||||
index: number[];
|
||||
locks: number[];
|
||||
investment: number;
|
||||
canPlay: boolean;
|
||||
status: string | JSX.Element;
|
||||
};
|
||||
|
||||
// statically shuffled array of symbols.
|
||||
const symbols = [
|
||||
"D",
|
||||
@ -147,104 +141,76 @@ const payLines = [
|
||||
const minPlay = 0;
|
||||
const maxPlay = 1e6;
|
||||
|
||||
export class SlotMachine extends Game<IProps, IState> {
|
||||
rng: WHRNG;
|
||||
interval = -1;
|
||||
export function SlotMachine(props: IProps): React.ReactElement {
|
||||
const [rng] = useState(new WHRNG(props.p.totalPlaytime));
|
||||
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) {
|
||||
super(props);
|
||||
this.rng = new WHRNG(this.props.p.totalPlaytime);
|
||||
useEffect(() => {
|
||||
const i = window.setInterval(step, 50);
|
||||
return () => clearInterval(i);
|
||||
});
|
||||
|
||||
this.state = {
|
||||
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 {
|
||||
function step(): void {
|
||||
let stoppedOne = false;
|
||||
const index = this.state.index.slice();
|
||||
for (const i in index) {
|
||||
if (index[i] === this.state.locks[i] && !stoppedOne) continue;
|
||||
index[i] = (index[i] + 1) % symbols.length;
|
||||
const copy = index.slice();
|
||||
for (const i in copy) {
|
||||
if (copy[i] === locks[i] && !stoppedOne) continue;
|
||||
copy[i] = (copy[i] + 1) % symbols.length;
|
||||
stoppedOne = true;
|
||||
}
|
||||
|
||||
this.setState({ index: index });
|
||||
setIndex(copy);
|
||||
|
||||
if (stoppedOne && index.every((e, i) => e === this.state.locks[i])) {
|
||||
this.checkWinnings();
|
||||
if (stoppedOne && copy.every((e, i) => e === locks[i])) {
|
||||
checkWinnings();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
getTable(): string[][] {
|
||||
function getTable(): string[][] {
|
||||
return [
|
||||
[
|
||||
symbols[(this.state.index[0] + symbols.length - 1) % symbols.length],
|
||||
symbols[(this.state.index[1] + symbols.length - 1) % symbols.length],
|
||||
symbols[(this.state.index[2] + symbols.length - 1) % symbols.length],
|
||||
symbols[(this.state.index[3] + symbols.length - 1) % symbols.length],
|
||||
symbols[(this.state.index[4] + symbols.length - 1) % symbols.length],
|
||||
symbols[(index[0] + symbols.length - 1) % symbols.length],
|
||||
symbols[(index[1] + symbols.length - 1) % symbols.length],
|
||||
symbols[(index[2] + symbols.length - 1) % symbols.length],
|
||||
symbols[(index[3] + 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[this.state.index[1]],
|
||||
symbols[this.state.index[2]],
|
||||
symbols[this.state.index[3]],
|
||||
symbols[this.state.index[4]],
|
||||
],
|
||||
[
|
||||
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],
|
||||
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],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
play(): void {
|
||||
if (this.reachedLimit(this.props.p)) return;
|
||||
this.setState({ status: "playing" });
|
||||
this.win(this.props.p, -this.state.investment);
|
||||
if (!this.state.canPlay) return;
|
||||
this.unlock();
|
||||
setTimeout(this.lock, this.rng.random() * 2000 + 1000);
|
||||
function play(): void {
|
||||
if (reachedLimit(props.p)) return;
|
||||
setStatus("playing");
|
||||
win(props.p, -investment);
|
||||
if (!canPlay) return;
|
||||
unlock();
|
||||
setTimeout(lock, rng.random() * 2000 + 1000);
|
||||
}
|
||||
|
||||
lock(): void {
|
||||
this.setState({
|
||||
locks: [
|
||||
Math.floor(this.rng.random() * symbols.length),
|
||||
Math.floor(this.rng.random() * symbols.length),
|
||||
Math.floor(this.rng.random() * symbols.length),
|
||||
Math.floor(this.rng.random() * symbols.length),
|
||||
Math.floor(this.rng.random() * symbols.length),
|
||||
],
|
||||
});
|
||||
function lock(): void {
|
||||
setLocks([
|
||||
Math.floor(rng.random() * symbols.length),
|
||||
Math.floor(rng.random() * symbols.length),
|
||||
Math.floor(rng.random() * symbols.length),
|
||||
Math.floor(rng.random() * symbols.length),
|
||||
Math.floor(rng.random() * symbols.length),
|
||||
]);
|
||||
}
|
||||
|
||||
checkWinnings(): void {
|
||||
const t = this.getTable();
|
||||
function checkWinnings(): void {
|
||||
const t = getTable();
|
||||
const getPaylineData = function (payline: number[][]): string[] {
|
||||
const data = [];
|
||||
for (const point of payline) {
|
||||
@ -263,35 +229,31 @@ export class SlotMachine extends Game<IProps, IState> {
|
||||
return count;
|
||||
};
|
||||
|
||||
let gains = -this.state.investment;
|
||||
let gains = -investment;
|
||||
for (const payline of payLines) {
|
||||
const data = getPaylineData(payline);
|
||||
const count = countSequence(data);
|
||||
if (count < 3) continue;
|
||||
const payout = getPayout(data[0], count - 3);
|
||||
gains += this.state.investment * payout;
|
||||
this.win(this.props.p, this.state.investment * payout);
|
||||
gains += investment * payout;
|
||||
win(props.p, investment * payout);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
status: (
|
||||
<>
|
||||
{gains > 0 ? "gained" : "lost"} <Money money={Math.abs(gains)} />
|
||||
</>
|
||||
),
|
||||
canPlay: true,
|
||||
});
|
||||
if (this.reachedLimit(this.props.p)) return;
|
||||
setStatus(
|
||||
<>
|
||||
{gains > 0 ? "gained" : "lost"} <Money money={Math.abs(gains)} />
|
||||
</>,
|
||||
);
|
||||
setCanPlay(true);
|
||||
if (reachedLimit(props.p)) return;
|
||||
}
|
||||
|
||||
unlock(): void {
|
||||
this.setState({
|
||||
locks: [-1, -1, -1, -1, -1],
|
||||
canPlay: false,
|
||||
});
|
||||
function unlock(): void {
|
||||
setLocks([-1, -1, -1, -1, -1]);
|
||||
setCanPlay(false);
|
||||
}
|
||||
|
||||
updateInvestment(e: React.FormEvent<HTMLInputElement>): void {
|
||||
function updateInvestment(e: React.ChangeEvent<HTMLInputElement>): void {
|
||||
let investment: number = parseInt(e.currentTarget.value);
|
||||
if (isNaN(investment)) {
|
||||
investment = minPlay;
|
||||
@ -302,53 +264,49 @@ export class SlotMachine extends Game<IProps, IState> {
|
||||
if (investment < minPlay) {
|
||||
investment = minPlay;
|
||||
}
|
||||
this.setState({ investment: investment });
|
||||
setInvestment(investment);
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const t = this.getTable();
|
||||
// prettier-ignore
|
||||
return (
|
||||
const t = getTable();
|
||||
// prettier-ignore
|
||||
return (
|
||||
<>
|
||||
<pre>+———————————————————————+</pre>
|
||||
<pre>| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |</pre>
|
||||
<pre>| | | | | | | |</pre>
|
||||
<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>
|
||||
<pre>| | | | | | | |</pre>
|
||||
<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>
|
||||
<pre>+———————————————————————+</pre>
|
||||
<input
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>+———————————————————————+</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {t[0][0]} | {t[0][1]} | {t[0][2]} | {t[0][3]} | {t[0][4]} | |</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | | | | | | |</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | {symbols[index[0]]} | {symbols[index[1]]} | {symbols[index[2]]} | {symbols[index[3]]} | {symbols[index[4]]} | |</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>| | | | | | | |</Typography>
|
||||
<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>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>+———————————————————————+</Typography>
|
||||
<TextField
|
||||
type="number"
|
||||
className="text-input"
|
||||
onChange={this.updateInvestment}
|
||||
onChange={updateInvestment}
|
||||
placeholder={"Amount to play"}
|
||||
value={this.state.investment}
|
||||
disabled={!this.state.canPlay}
|
||||
disabled={!canPlay}
|
||||
InputProps={{endAdornment:(<Button
|
||||
onClick={trusted(play)}
|
||||
disabled={!canPlay}
|
||||
>Spin!</Button>)}}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={trusted(this.play)}
|
||||
text={"Spin!"}
|
||||
disabled={!this.state.canPlay}
|
||||
/>
|
||||
<h1>{this.state.status}</h1>
|
||||
<h2>Pay lines</h2>
|
||||
|
||||
<Typography variant="h4">{status}</Typography>
|
||||
<Typography>Pay lines</Typography>
|
||||
|
||||
<pre>----- ····· ·····</pre>
|
||||
<pre>····· ----- ·····</pre>
|
||||
<pre>····· ····· -----</pre>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>----- ····· ·····</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ----- ·····</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ····· -----</Typography>
|
||||
<br />
|
||||
|
||||
<pre>··^·· \···/ \···/</pre>
|
||||
<pre>·/·\· ·\·/· ·---·</pre>
|
||||
<pre>/···\ ··v·· ·····</pre>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>··^·· \···/ \···/</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>·/·\· ·\·/· ·---·</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>/···\ ··v·· ·····</Typography>
|
||||
<br />
|
||||
|
||||
<pre>····· ·---· ·····</pre>
|
||||
<pre>·---· /···\ \···/</pre>
|
||||
<pre>/···\ ····· ·---·</pre>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>····· ·---· ·····</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>·---· /···\ \···/</Typography>
|
||||
<Typography sx={{ lineHeight: "1em", whiteSpace: "pre" }}>/···\ ····· ·---·</Typography>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 { Generic_fromJSON, Generic_toJSON, Reviver } from "./utils/JSONReviver";
|
||||
import { createPopup, removePopup } from "./ui/React/createPopup";
|
||||
import { CodingContractPopup } from "./ui/React/CodingContractPopup";
|
||||
import { CodingContractEvent } from "./ui/React/CodingContractModal";
|
||||
|
||||
/* 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
|
||||
*/
|
||||
async prompt(): Promise<CodingContractResult> {
|
||||
const popupId = `coding-contract-prompt-popup-${this.fn}`;
|
||||
return new Promise<CodingContractResult>((resolve) => {
|
||||
createPopup(
|
||||
popupId,
|
||||
CodingContractPopup,
|
||||
{
|
||||
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);
|
||||
},
|
||||
const props = {
|
||||
c: this,
|
||||
onClose: () => {
|
||||
resolve(CodingContractResult.Cancelled);
|
||||
},
|
||||
() => 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;
|
||||
MilliPerCycle: number;
|
||||
CorpFactionRepRequirement: number;
|
||||
BaseFocusBonus: number;
|
||||
BaseCostFor1GBOfRamHome: number;
|
||||
BaseCostFor1GBOfRamServer: number;
|
||||
TravelCost: number;
|
||||
@ -46,14 +47,6 @@ export const CONSTANTS: {
|
||||
IntelligenceTerminalHackBaseExpGain: number;
|
||||
IntelligenceSingFnBaseExpGain: number;
|
||||
IntelligenceClassBaseExpGain: number;
|
||||
IntelligenceHackingMissionBaseExpGain: number;
|
||||
HackingMissionRepToDiffConversion: number;
|
||||
HackingMissionRepToRewardConversion: number;
|
||||
HackingMissionSpamTimeIncrease: number;
|
||||
HackingMissionTransferAttackIncrease: number;
|
||||
HackingMissionMiscDefenseIncrease: number;
|
||||
HackingMissionDifficultyToHacking: number;
|
||||
HackingMissionHowToPlay: string;
|
||||
MillisecondsPer20Hours: number;
|
||||
GameCyclesPer20Hours: number;
|
||||
MillisecondsPer10Hours: number;
|
||||
@ -121,7 +114,7 @@ export const CONSTANTS: {
|
||||
TotalNumBitNodes: number;
|
||||
LatestUpdate: string;
|
||||
} = {
|
||||
Version: "0.54.0",
|
||||
Version: "0.55.0",
|
||||
|
||||
// Speed (in ms) at which the main loop is updated
|
||||
_idleSpeed: 200,
|
||||
@ -198,63 +191,6 @@ export const CONSTANTS: {
|
||||
IntelligenceTerminalHackBaseExpGain: 200, // Hacking exp divided by this to determine int exp gain
|
||||
IntelligenceSingFnBaseExpGain: 1.5,
|
||||
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
|
||||
MillisecondsPer20Hours: 72000000,
|
||||
@ -285,6 +221,7 @@ export const CONSTANTS: {
|
||||
GameCyclesPerFiveMinutes: 300000 / 200,
|
||||
|
||||
// Player Work & Action
|
||||
BaseFocusBonus: 0.8,
|
||||
FactionWorkHacking: "Faction Hacking Work",
|
||||
FactionWorkField: "Faction Field Work",
|
||||
FactionWorkSecurity: "Faction Security Work",
|
||||
@ -344,48 +281,26 @@ export const CONSTANTS: {
|
||||
TotalNumBitNodes: 24,
|
||||
|
||||
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
|
||||
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)
|
||||
* The game is now 100% in typescript, react, and Material-UI
|
||||
|
||||
** Misc. **
|
||||
|
||||
* Fix "Game saved" animation. (@Nolshine)
|
||||
* Update commitCrime documentation (@Tryneus)
|
||||
* Fix logbox scrolling weird (@Nolshine)
|
||||
* Fix weird scrolling in corporations (@BartKoppelmans)
|
||||
* Fix typo (@BartKoppelmans & @Nolshine)
|
||||
* Delete game now has a confirmation modal (@Nolshine)
|
||||
* Fix issue where skills would not get properly updated when entering new
|
||||
BN. (@Nolshine)
|
||||
* Convert create gang to popup (@vmesecher)
|
||||
* Fixed a bug that prevented travel to Sector-12 and New Tokyo when not using
|
||||
ASCII art.
|
||||
* 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
|
||||
`,
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { CorporationConstants } from "./data/Constants";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { createElement } from "../ui/uiHelpers/createElement";
|
||||
import { EmployeePositions } from "./EmployeePositions";
|
||||
import { ICorporation } from "./ICorporation";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||
import { OfficeSpace } from "./OfficeSpace";
|
||||
import { IIndustry } from "./IIndustry";
|
||||
|
||||
@ -139,74 +136,6 @@ export class Employee {
|
||||
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 {
|
||||
const employee = new Employee();
|
||||
employee.name = this.name;
|
||||
|
@ -16,7 +16,6 @@ import { Warehouse } from "./Warehouse";
|
||||
import { ICorporation } from "./ICorporation";
|
||||
import { IIndustry } from "./IIndustry";
|
||||
import { IndustryUpgrade, IndustryUpgrades } from "./IndustryUpgrades";
|
||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||
|
||||
interface IParams {
|
||||
name?: string;
|
||||
@ -385,9 +384,6 @@ export class Industry implements IIndustry {
|
||||
const prod = this.products[prodName];
|
||||
if (prod === undefined) continue;
|
||||
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