Compare commits

..

126 Commits

Author SHA1 Message Date
Olivier Gagnon
50cf362b3b v0.55.0 2021-10-01 16:22:33 -04:00
Olivier Gagnon
5ba7b2796d build gang in mui 2021-10-01 15:39:56 -04:00
Olivier Gagnon
9cbb525da3 remove hacking missions 2021-10-01 15:26:12 -04:00
Olivier Gagnon
0ae8b72188 100% mui I think 2021-10-01 15:19:37 -04:00
Olivier Gagnon
1e641468f7 build the little mui conversions 2021-10-01 13:40:16 -04:00
Olivier Gagnon
3187bb990d Everything except Gang is Muified 2021-10-01 13:36:59 -04:00
Olivier Gagnon
4e8bb96f3f removing some of the classes 2021-10-01 13:08:37 -04:00
Olivier Gagnon
97c04a1037 logbox manager, alert manager and fix bitverse colors 2021-10-01 10:39:09 -04:00
Olivier Gagnon
62cd8ffcc6 pre-dialogbox-convert 2021-10-01 01:00:50 -04:00
Olivier Gagnon
0d9caac455 mui-fy some modals 2021-09-30 20:06:40 -04:00
Olivier Gagnon
f701cbffa7 lint 2021-09-30 18:57:44 -04:00
Olivier Gagnon
73d0dd98f2 stock market in Mui 2021-09-30 18:56:09 -04:00
Olivier Gagnon
c05be66c60 corp in mui 2021-09-30 17:24:08 -04:00
Olivier Gagnon
b0e4a2a775 finalize corp in mui 2021-09-30 17:02:07 -04:00
Olivier Gagnon
86ddc940aa convert some corp to mui 2021-09-30 15:12:06 -04:00
Olivier Gagnon
510fcedf90 convert some corp to mui 2021-09-30 13:51:55 -04:00
Olivier Gagnon
5cce1c255c convert some corp to mui 2021-09-29 19:05:25 -04:00
Olivier Gagnon
854239ceb1 convert some corp to mui 2021-09-29 17:41:19 -04:00
Olivier Gagnon
73834d03cd faction dont rerender 2021-09-29 10:33:21 -04:00
Olivier Gagnon
cefd499ff6 use tabs for corp 2021-09-29 01:54:17 -04:00
Olivier Gagnon
c5713fa6d8 learned and implemented default props 2021-09-29 01:49:22 -04:00
Olivier Gagnon
b0739f8942 fix bad scritp not dying properly 2021-09-28 20:48:44 -04:00
Olivier Gagnon
a6d7f93111 work on corp mui 2021-09-28 19:38:51 -04:00
Olivier Gagnon
86678b6290 convert blade to mui 2021-09-27 17:09:48 -04:00
Olivier Gagnon
498a204c88 rm unused log 2021-09-26 21:38:21 -04:00
Olivier Gagnon
d6584386ff set disabled color 2021-09-26 21:37:22 -04:00
Olivier Gagnon
b5abbbc240 hacknet nodes in mui 2021-09-26 21:26:25 -04:00
Olivier Gagnon
514b467e27 Merge branch 'dev' of github.com:danielyxie/bitburner into dev 2021-09-26 21:22:13 -04:00
Olivier Gagnon
1fc2e6fd2a added unfocus aug 2021-09-26 21:11:49 -04:00
hydroflame
b758807ba1 Merge pull request #1384 from vmesecher/dev
Fixes Corporation bribe uninentionally affect Bladeburners under cert…
2021-09-26 20:59:12 -04:00
Olivier Gagnon
14e6dd0158 sleeves to mui 2021-09-26 20:55:38 -04:00
vmesecher
ce578206eb Fixes Corporation bribe uninentionally affect Bladeburners under certain circumstances. Also contains small lint fixes found by running npm run lint. 2021-09-25 15:43:30 -07:00
Olivier Gagnon
3289f76cd0 safeguards 2021-09-25 17:29:51 -04:00
Olivier Gagnon
3aacab504b convert casino to mui 2021-09-25 15:42:37 -04:00
Olivier Gagnon
6d179be018 buttons should have typograaphy as child 2021-09-25 15:41:00 -04:00
Olivier Gagnon
86da356478 convert most of the city to mui 2021-09-25 15:34:12 -04:00
Olivier Gagnon
c0e1706128 rm the top level utils folder 2021-09-25 14:45:12 -04:00
Olivier Gagnon
06f716c0fa moved a bunch of files 2021-09-25 14:42:57 -04:00
Olivier Gagnon
07bc697477 remove uneeded part of the tutorial 2021-09-25 14:21:34 -04:00
Olivier Gagnon
60a91cd9f7 index.html is empty now 2021-09-25 14:14:50 -04:00
Olivier Gagnon
97624395c1 convert game saved to snackbar, index.html is nearly empty now 2021-09-25 14:10:32 -04:00
Olivier Gagnon
d49fea4cbc convert unclickable inside the react tree 2021-09-25 14:00:38 -04:00
Olivier Gagnon
125e9484f7 Work on hacknet to mui 2021-09-25 13:52:26 -04:00
Olivier Gagnon
ef17f0d617 ui 2021-09-25 13:31:42 -04:00
Olivier Gagnon
f7aa393a8f fix getNodeStats error 2021-09-25 13:03:09 -04:00
Olivier Gagnon
7fb2b8b590 fix tooltips 2021-09-25 11:23:56 -04:00
Olivier Gagnon
cba40c71b2 mui stuff 2021-09-25 03:09:27 -04:00
Olivier Gagnon
4254cc2807 small improvements to augmentation page. 2021-09-25 02:42:21 -04:00
Olivier Gagnon
8b15adda8a lint 2021-09-25 02:36:49 -04:00
Olivier Gagnon
b0f20c8c8f lint 2021-09-25 01:26:03 -04:00
Olivier Gagnon
5c6c472b64 Augmentations in mui 2021-09-25 01:06:17 -04:00
Olivier Gagnon
5170c0e004 create script in mui 2021-09-24 23:36:28 -04:00
Olivier Gagnon
fe18c55173 removed some unused files. 2021-09-24 23:09:18 -04:00
Olivier Gagnon
69c9b20e68 sleeve ui says bonus time and sleeves cant consume all bonus time at once. 2021-09-24 23:02:27 -04:00
Olivier Gagnon
2d45784102 theme editor now allows import/export 2021-09-24 22:54:25 -04:00
Olivier Gagnon
65cb519801 fix infiltration keybinding 2021-09-24 22:15:19 -04:00
Olivier Gagnon
76e6cb4ecc forgot one ts in utils 2021-09-24 22:04:30 -04:00
hydroflame
7b6f9293c7 Merge pull request #1379 from danielyxie/netscript-functions-ts
all typescript
2021-09-24 21:50:09 -04:00
Olivier Gagnon
dc2bf871cf all typescript 2021-09-24 21:49:49 -04:00
Olivier Gagnon
1d349c25f7 imrpove terminal performance 2021-09-24 19:12:53 -04:00
Olivier Gagnon
5484c64a95 build script not dying 2021-09-24 19:10:20 -04:00
Olivier Gagnon
ec33fb411c fix scripts not dying 2021-09-24 19:09:19 -04:00
Olivier Gagnon
2a966d0726 build the typescript conversion 2021-09-24 18:43:30 -04:00
Olivier Gagnon
29143999a6 move JSInterpreter 2021-09-24 18:42:13 -04:00
Olivier Gagnon
a32b8eabe3 SaveObject in ts 2021-09-24 18:40:17 -04:00
Olivier Gagnon
43723a3fbb engine in ts 2021-09-24 18:29:25 -04:00
Olivier Gagnon
ad75fa5ebc convert to ts 2021-09-24 18:13:20 -04:00
Olivier Gagnon
4abc1df840 convert to ts 2021-09-24 17:56:30 -04:00
Olivier Gagnon
da488e586b convert to ts 2021-09-24 17:16:14 -04:00
Olivier Gagnon
413333c919 convert to ts 2021-09-24 17:07:53 -04:00
Olivier Gagnon
4f219a3214 convert to ts 2021-09-24 16:37:42 -04:00
Olivier Gagnon
2e05f14c0d convert to ts 2021-09-24 16:34:21 -04:00
Olivier Gagnon
47f54a11c3 ts convertion 2021-09-24 16:02:38 -04:00
Olivier Gagnon
b8faa9dc0b convert player to ts 2021-09-23 18:47:43 -04:00
Olivier Gagnon
8fd6b2e7da convert some files to ts 2021-09-23 13:30:13 -04:00
Olivier Gagnon
cdd9c174e7 electron always dev tools 2021-09-23 13:15:27 -04:00
Olivier Gagnon
e6291a09a0 refresh theme on load 2021-09-22 13:43:23 -04:00
hydroflame
9ccfca3c72 Merge pull request #1368 from MartinFournier/theme-load-at-startup
Load theme from settings during game load (#1364)
2021-09-22 13:40:27 -04:00
Martin Fournier
346024af45 Load theme from settings during game load (#1364) 2021-09-22 13:33:41 -04:00
Olivier Gagnon
66a2adaeb4 update prettier 2021-09-22 12:56:55 -04:00
Olivier Gagnon
c1945ab12e fix donation 2021-09-22 12:49:29 -04:00
Olivier Gagnon
3a7c64872a fix messge problem 2021-09-22 12:38:13 -04:00
Olivier Gagnon
06edf5b70c theme editor with color picker 2021-09-22 12:06:03 -04:00
hydroflame
d2008e86b4 Merge pull request #1361 from MartinFournier/theme-color-picker
Add material-ui-color picker in theme editor
2021-09-22 12:03:44 -04:00
Martin Fournier
32d1affb50 Add material-ui-color picker in theme editor 2021-09-22 12:00:00 -04:00
Olivier Gagnon
558b671206 few bugfix 2021-09-22 11:32:04 -04:00
Olivier Gagnon
a954259e25 can buy trp 2021-09-22 10:59:58 -04:00
Olivier Gagnon
64c7831c81 convert milestones to mui 2021-09-22 03:33:15 -04:00
Olivier Gagnon
61dd393bb5 convert tutorial screen to mui 2021-09-22 03:30:06 -04:00
Olivier Gagnon
28aca06208 convert work in progress to mui 2021-09-22 03:25:12 -04:00
Olivier Gagnon
c79fa240e1 Factions have a property explaining if they should keep on install 2021-09-22 03:09:37 -04:00
Olivier Gagnon
e1741778f9 add new sort option 2021-09-22 02:56:15 -04:00
Olivier Gagnon
80560ce9f6 build theme editor 2021-09-22 02:20:29 -04:00
Olivier Gagnon
f9a4eadb71 removed some of fconf 2021-09-22 01:36:17 -04:00
Olivier Gagnon
96f0879230 fix bug with nano 2021-09-22 01:10:29 -04:00
Olivier Gagnon
9c40cf6f28 fix infiltration timer 2021-09-22 01:03:25 -04:00
Olivier Gagnon
55901f0574 Let player copy terminal 2021-09-22 00:57:37 -04:00
Olivier Gagnon
43f0746be7 fix ls not working on dir 2021-09-22 00:48:13 -04:00
Olivier Gagnon
8eeed583c2 corp research show research points 2021-09-22 00:42:45 -04:00
Olivier Gagnon
dc518e7032 asdf 2021-09-21 20:39:25 -04:00
Olivier Gagnon
0c932dd4d1 build bunch of fixes 2021-09-21 20:30:00 -04:00
Olivier Gagnon
c94ec2f170 remove message from covenant when you're maxed sleeves 2021-09-21 18:04:47 -04:00
Olivier Gagnon
6111c50eb1 updatedco 2021-09-21 17:47:27 -04:00
Olivier Gagnon
8977f299e5 respect disable ascii art 2021-09-21 17:46:01 -04:00
Olivier Gagnon
2a8b1c2116 Fix corp not moving 2021-09-21 17:38:51 -04:00
Olivier Gagnon
9a6b185141 few fixes 2021-09-21 17:36:42 -04:00
Olivier Gagnon
501ce70702 re-add back button from faction to factions 2021-09-21 17:13:35 -04:00
Olivier Gagnon
9b94692942 fix faction screen size 2021-09-21 17:12:16 -04:00
Olivier Gagnon
06880c68e1 fix casino infinite loop 2021-09-21 16:58:20 -04:00
Olivier Gagnon
637f88efcd add bug report button 2021-09-21 16:50:33 -04:00
Olivier Gagnon
57a5c8b0b4 add difficulty to bitnode screen 2021-09-21 16:49:38 -04:00
Olivier Gagnon
c3ac16f330 electron open pages in external browser 2021-09-21 13:32:06 -04:00
Olivier Gagnon
c564de40b3 add f5f8 to electron 2021-09-21 13:29:16 -04:00
Olivier Gagnon
1b26d25a53 aug screen popup 2021-09-21 13:21:25 -04:00
Olivier Gagnon
7aa2d00460 fix aug page 2021-09-21 13:08:05 -04:00
Olivier Gagnon
ffa9600302 active scritp production typo and add new page size 2021-09-21 11:50:48 -04:00
hydroflame
db35fde42e Merge pull request #1307 from Nolshine/improve_gang_ascend_modal
improve gang ascend modal
2021-09-21 11:04:23 -04:00
hydroflame
d73040fdc5 Merge pull request #1319 from Nolshine/fix_missing_onclick_for_bulk_purchase
fix 'confirm bulk purchase' button missing onclick
2021-09-21 11:03:39 -04:00
hydroflame
a38df2e613 Merge pull request #1318 from Nolshine/visibility_icons
implement visibility icon toggling
2021-09-21 11:03:25 -04:00
hydroflame
b931ab5566 Merge pull request #1323 from Nolshine/documentation_1273
amend deprecation warning warning on getAugmentationCost's doc
2021-09-21 11:03:11 -04:00
Nolshine
4e44e784c6 amend warning on getAugmentationCost doc
now directs players to use the desired functions
2021-09-21 14:11:51 +01:00
Nolshine
aa4451b12d fix 'confirm bulk purchase' button missing onclick 2021-09-21 12:22:04 +01:00
Nolshine
2a52f6fa2b implement visibility icon toggling 2021-09-21 07:07:47 +01:00
Olivier Gagnon
8b7723338b change electron so it works. 2021-09-20 22:30:11 -04:00
Olivier Gagnon
9e62438b43 Rework faction augs menu 2021-09-20 20:42:13 -04:00
Nolshine
a9c57e23a5 improve gang ascend modal 2021-09-20 06:47:13 +01:00
533 changed files with 16811 additions and 25010 deletions

View File

@@ -3,4 +3,5 @@ doc/build/
dist/
tests/*.bundle.*
src/ThirdParty/*
src/JSInterpreter.js
src/JSInterpreter.js
main.bundle.js

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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? */
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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%;
}
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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%;
}

View File

@@ -1,3 +0,0 @@
.milestones-container {
width: 60%;
}

View File

@@ -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;
}

View File

@@ -1,244 +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: 5px solid var(--my-highlight-color);
width: 70%;
max-height: 80%;
overflow-y: auto;
z-index: 11; /* Sit on top of the container */
color: var(--my-font-color);
}
.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 var(--my-highlight-color);
}
.log-box-header {
z-index: 1300;
background-color: #333;
border: 1px solid var(--my-highlight-color);
display: flex;
flex: row nowrap;
align-items: center;
justify-content: space-between;
}
.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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

133
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -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)
-------------------------------------------

View File

@@ -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.

View File

@@ -28,6 +28,8 @@ grow() Netscript Function
server, but there is no required hacking level to run the command. It also
raises the security level of the target server by 0.004 per thread.
Action time is calculated at the start, effect is calculated at the end.
Example:
.. code-block:: javascript

View File

@@ -27,6 +27,8 @@ hack() Netscript Function
A successful :doc:`hack<hack>` on a server will raise that server's security
level by 0.002.
Action time is calculated at the start, effect is calculated at the end.
Example:
.. code-block:: javascript

View File

@@ -3,7 +3,9 @@ getAugmentationCost() Netscript Function
.. js:function:: getAugmentationCost(augName)
.. warning:: This function is deprecated.
.. warning:: This function is deprecated. It still functions, but new
scripts should prefer :doc:`getAugmentationPrice<getAugmentationPrice>`
and :doc:`getAugmentationRepReq<getAugmentationRepReq>` instead.
:RAM cost: 5 GB

View File

@@ -1,10 +1,11 @@
const { app, BrowserWindow, Menu } = require("electron");
const { app, BrowserWindow, Menu, globalShortcut, shell } = require("electron");
Menu.setApplicationMenu(false);
function createWindow() {
const win = new BrowserWindow({
show: false,
webPreferences: {
devTools: false,
devTools: true,
},
});
@@ -12,6 +13,24 @@ function createWindow() {
win.maximize();
win.loadFile("index.html");
win.show();
win.webContents.openDevTools();
globalShortcut.register("f5", function () {
win.loadFile("index.html");
});
globalShortcut.register("f8", function () {
win.loadFile("index.html", { query: { noScripts: "true" } });
});
win.webContents.on("new-window", function (e, url) {
// make sure local urls stay in electron perimeter
if (url.substr(0, "file://".length) === "file://") {
return;
}
// and open every other protocols on the browser
e.preventDefault();
shell.openExternal(url);
});
}
app.whenReady().then(() => {

View File

@@ -36,19 +36,20 @@
ga("send", "pageview");
</script>
<link rel="shortcut icon" href="favicon.ico"><link href="dist/vendor.css" rel="stylesheet"><link href="main.css" rel="stylesheet"></head>
<style>
body {
background-color: black;
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
}
body::-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="unclickable" style="display: none">Click on this to upgrade your Source-File -1!</div>
<div id="root"/>
<script type="text/javascript" src="dist/vendor.bundle.js"></script><script type="text/javascript" src="main.bundle.js"></script></body>
<script src="src/ThirdParty/raphael.min.js"></script>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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,16 +2170,17 @@ 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;
justify-content: space-between; }
justify-content: space-between;
cursor: grab; }
.log-box-log-container {
overflow-y: auto; }

File diff suppressed because one or more lines are too long

1687
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@
"@mui/lab": "^5.0.0-alpha.46",
"@mui/material": "^5.0.0-rc.1",
"@mui/styles": "^5.0.0-rc.1",
"@types/escodegen": "^0.0.7",
"@types/js-beautify": "^1.13.2",
"@types/numeral": "0.0.25",
"@types/react": "^17.0.21",
@@ -43,6 +44,7 @@
"jszip": "^3.7.0",
"loader-runner": "^2.3.0",
"loader-utils": "^1.1.0",
"material-ui-color": "^1.2.0",
"mathjax-full": "^3.2.0",
"mathjax-react": "^1.0.6",
"memory-fs": "~0.4.1",
@@ -58,7 +60,8 @@
"treant-js": "^1.0.1",
"unused-webpack-plugin": "^2.4.0",
"uuid": "^3.2.1",
"w3c-blob": "0.0.1"
"w3c-blob": "0.0.1",
"webpack-deadcode-plugin": "^0.1.15"
},
"description": "A cyberpunk-themed incremental game",
"devDependencies": {
@@ -145,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",

View File

@@ -1,14 +1,19 @@
# npm install electron --save-dev
# npm install electron-packager --save-dev
mkdir -p .package/dist || true
mkdir -p .package/dist/src/ThirdParty || true
mkdir -p .package/src/ThirdParty || true
cp index.html .package
cp electron/* .package
cp dist/engine.bundle.js .package/dist
cp dist/engineStyle.css .package/dist
# The css files
cp dist/vendor.css .package/dist
cp dist/engineStyle.bundle.js .package/dist
cp dist/vendor.bundle.js .package/dist
cp main.css .package/main.css
# The js files.
cp dist/vendor.bundle.js .package/dist/vendor.bundle.js
cp main.bundle.js .package/main.bundle.js
cp src/ThirdParty/raphael.min.js .package/src/ThirdParty/raphael.min.js
npm run package-electron

View File

@@ -8,11 +8,11 @@ import { Factions } from "../Faction/Factions";
import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
interface IConstructorParams {
export interface IConstructorParams {
info: string | JSX.Element;
stats?: JSX.Element;
stats?: JSX.Element | null;
isSpecial?: boolean;
moneyCost: number;
name: string;
@@ -369,7 +369,7 @@ export class Augmentation {
info: string | JSX.Element;
// Description of the stats, often autogenerated, sometimes manually written.
stats: JSX.Element;
stats: JSX.Element | null;
// Any Augmentation not immediately available in BitNode-1 is special (e.g. Bladeburner augs)
isSpecial = false;
@@ -507,8 +507,9 @@ export class Augmentation {
this.mults.bladeburner_success_chance_mult = params.bladeburner_success_chance_mult;
}
if (params.stats) this.stats = params.stats;
else this.stats = generateStatsDescription(this.mults, params.programs, params.startingMoney);
if (params.stats === undefined)
this.stats = generateStatsDescription(this.mults, params.programs, params.startingMoney);
else this.stats = params.stats;
}
// Adds this Augmentation to the specified Factions

View File

@@ -1,2 +0,0 @@
export declare function isRepeatableAug(aug: Augmentation): boolean;
export declare function installAugmentations(): void;

View File

@@ -1,6 +1,6 @@
import { Augmentation } from "./Augmentation";
import { Augmentation, IConstructorParams } from "./Augmentation";
import { Augmentations } from "./Augmentations";
import { PlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { PlayerOwnedAugmentation, IPlayerOwnedAugmentation } from "./PlayerOwnedAugmentation";
import { AugmentationNames } from "./data/AugmentationNames";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
@@ -11,20 +11,20 @@ import { prestigeAugmentation } from "../Prestige";
import { Programs } from "../Programs/Programs";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { clearObject } from "../../utils/helpers/clearObject";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { clearObject } from "../utils/helpers/clearObject";
import { WHRNG } from "../Casino/RNG";
import React from "react";
function AddToAugmentations(aug) {
var name = aug.name;
function AddToAugmentations(aug: Augmentation): void {
const name = aug.name;
Augmentations[name] = aug;
}
function getRandomBonus() {
var bonuses = [
function getRandomBonus(): any {
const bonuses = [
{
bonuses: {
hacking_chance_mult: 1.25,
@@ -111,8 +111,8 @@ function getRandomBonus() {
return bonuses[Math.floor(bonuses.length * randomNumber.random())];
}
function initAugmentations() {
for (var name in Factions) {
function initAugmentations(): void {
for (const name in Factions) {
if (Factions.hasOwnProperty(name)) {
Factions[name].augmentations = [];
}
@@ -124,7 +124,7 @@ function initAugmentations() {
//Time-Based Augment Test
const randomBonuses = getRandomBonus();
const UnstableCircadianModulatorParams = {
const UnstableCircadianModulatorParams: IConstructorParams = {
name: AugmentationNames.UnstableCircadianModulator,
moneyCost: 5e9,
repCost: 3.625e5,
@@ -133,7 +133,7 @@ function initAugmentations() {
"unpredictable results based on your circadian rhythm.",
};
Object.keys(randomBonuses.bonuses).forEach(
(key) => (UnstableCircadianModulatorParams[key] = randomBonuses.bonuses[key]),
(key) => ((UnstableCircadianModulatorParams as any)[key] = randomBonuses.bonuses[key]),
);
const UnstableCircadianModulator = new Augmentation(UnstableCircadianModulatorParams);
@@ -211,7 +211,7 @@ function initAugmentations() {
name: AugmentationNames.Targeting3,
moneyCost: 1.15e8,
repCost: 2.75e4,
info: "The latest version of the 'Augmented Targeting' implant adds the ability to " + "lock-on and track threats.",
info: "The latest version of the 'Augmented Targeting' implant adds the ability to lock-on and track threats.",
prereqs: [AugmentationNames.Targeting2],
dexterity_mult: 1.3,
});
@@ -1555,7 +1555,7 @@ function initAugmentations() {
repCost: 2.5e6,
moneyCost: 0,
info: "It's time to leave the cave.",
stats: <></>,
stats: null,
});
RedPill.addToFactions(["Daedalus"]);
if (augmentationExists(AugmentationNames.TheRedPill)) {
@@ -1595,7 +1595,7 @@ function initAugmentations() {
"exactly the implant does, but they promise that it will greatly " +
"enhance your abilities.",
hacking_grow_mult: 3,
stats: <></>,
stats: null,
});
HiveMind.addToFactions(["ECorp"]);
if (augmentationExists(AugmentationNames.HiveMind)) {
@@ -2044,6 +2044,28 @@ function initAugmentations() {
}
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)) {
@@ -2349,7 +2371,7 @@ function initAugmentations() {
CONSTANTS.MultipleAugMultiplier * [1, 0.96, 0.94, 0.93][SourceFileFlags[11]],
Player.queuedAugmentations.length,
);
for (var name in Augmentations) {
for (const name in Augmentations) {
if (Augmentations.hasOwnProperty(name)) {
Augmentations[name].baseCost *= mult;
}
@@ -2359,29 +2381,26 @@ function initAugmentations() {
}
//Resets an Augmentation during (re-initizliation)
function resetAugmentation(newAugObject) {
function resetAugmentation(newAugObject: Augmentation): void {
if (!(newAugObject instanceof Augmentation)) {
throw new Error("Invalid argument 'newAugObject' passed into resetAugmentation");
}
var name = newAugObject.name;
const name = newAugObject.name;
if (augmentationExists(name)) {
delete Augmentations[name];
}
AddToAugmentations(newAugObject);
}
function applyAugmentation(aug, reapply = false) {
function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void {
Augmentations[aug.name].owned = true;
const augObj = Augmentations[aug.name];
// Apply multipliers
for (const mult in augObj.mults) {
if (Player[mult] == null) {
console.warn(`Augmentation has unrecognized multiplier property: ${mult}`);
} else {
Player[mult] *= augObj.mults[mult];
}
const v = Player.getMult(mult) * augObj.mults[mult];
Player.setMult(mult, v);
}
// Special logic for NeuroFlux Governor
@@ -2400,12 +2419,12 @@ function applyAugmentation(aug, reapply = false) {
// Push onto Player's Augmentation list
if (!reapply) {
var ownedAug = new PlayerOwnedAugmentation(aug.name);
const ownedAug = new PlayerOwnedAugmentation(aug.name);
Player.augmentations.push(ownedAug);
}
}
function installAugmentations() {
function installAugmentations(): boolean {
if (Player.queuedAugmentations.length == 0) {
dialogBoxCreate("You have not purchased any Augmentations to install!");
return false;
@@ -2443,13 +2462,14 @@ function installAugmentations() {
"<br>You wake up in your home...you feel different...",
);
prestigeAugmentation();
return true;
}
function augmentationExists(name) {
function augmentationExists(name: string): boolean {
return Augmentations.hasOwnProperty(name);
}
export function isRepeatableAug(aug) {
export function isRepeatableAug(aug: Augmentation): boolean {
const augName = aug instanceof Augmentation ? aug.name : aug;
if (augName === AugmentationNames.NeuroFluxGovernor) {

View File

@@ -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",

View File

@@ -2,92 +2,98 @@
* Root React component for the Augmentations UI page that display all of your
* owned and purchased Augmentations and Source-Files.
*/
import * as React from "react";
import React, { useState, useEffect } from "react";
import { InstalledAugmentationsAndSourceFiles } from "./InstalledAugmentationsAndSourceFiles";
import { InstalledAugmentations } from "./InstalledAugmentations";
import { PlayerMultipliers } from "./PlayerMultipliers";
import { PurchasedAugmentations } from "./PurchasedAugmentations";
import { SourceFiles } from "./SourceFiles";
import { Player } from "../../Player";
import { StdButton } from "../../ui/React/StdButton";
import { canGetBonus } from "../../ExportBonus";
import { use } from "../../ui/Context";
type IProps = {
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import Box from "@mui/material/Box";
interface IProps {
exportGameFn: () => void;
installAugmentationsFn: () => void;
};
type IState = {
rerender: boolean;
};
export class AugmentationsRoot extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
rerender: false,
};
this.export = this.export.bind(this);
}
export(): void {
this.props.exportGameFn();
this.setState({
rerender: !this.state.rerender,
});
}
render(): React.ReactNode {
function exportBonusStr(): string {
if (canGetBonus()) return "(+1 favor to all factions)";
return "";
}
return (
<>
<div className="augmentations-content">
<h1>Purchased Augmentations</h1>
<p>
Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to
install them.
</p>
<p>WARNING: Installing your Augmentations resets most of your progress, including:</p>
<br />
<p>- Stats/Skill levels and Experience</p>
<p>- Money</p>
<p>- Scripts on every computer but your home computer</p>
<p>- Purchased servers</p>
<p>- Hacknet Nodes</p>
<p>- Faction/Company reputation</p>
<p>- Stocks</p>
<br />
<p>
Installing Augmentations lets you start over with the perks and benefits granted by all of the Augmentations
you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your home computer (but
you will lose all programs besides NUKE.exe)
</p>
<StdButton
onClick={this.props.installAugmentationsFn}
text="Install Augmentations"
tooltip="'I never asked for this'"
/>
<StdButton
addClasses="flashing-button"
onClick={this.export}
text={`Backup Save ${exportBonusStr()}`}
tooltip="It's always a good idea to backup/export your save!"
/>
<PurchasedAugmentations />
<h1>Installed Augmentations</h1>
<p>
{`List of all Augmentations ${Player.sourceFiles.length > 0 ? "and Source Files " : ""} ` +
`that have been installed. You have gained the effects of these.`}
</p>
<InstalledAugmentationsAndSourceFiles />
<br /> <br />
<PlayerMultipliers />
</div>
</>
);
}
}
export function AugmentationsRoot(props: IProps): React.ReactElement {
const player = use.Player();
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((o) => !o);
}
useEffect(() => {
const id = setInterval(rerender, 200);
return () => clearInterval(id);
}, []);
function doExport(): void {
props.exportGameFn();
rerender();
}
function exportBonusStr(): string {
if (canGetBonus()) return "(+1 favor to all factions)";
return "";
}
return (
<>
<Typography variant="h4">Augmentations</Typography>
<Box mx={2}>
<Typography>
Below is a list of all Augmentations you have purchased but not yet installed. Click the button below to
install them.
</Typography>
<Typography>WARNING: Installing your Augmentations resets most of your progress, including:</Typography>
<br />
<Typography>- Stats/Skill levels and Experience</Typography>
<Typography>- Money</Typography>
<Typography>- Scripts on every computer but your home computer</Typography>
<Typography>- Purchased servers</Typography>
<Typography>- Hacknet Nodes</Typography>
<Typography>- Faction/Company reputation</Typography>
<Typography>- Stocks</Typography>
<br />
<Typography>
Installing Augmentations lets you start over with the perks and benefits granted by all of the Augmentations
you have ever installed. Also, you will keep any scripts and RAM/Core upgrades on your home computer (but you
will lose all programs besides NUKE.exe)
</Typography>
</Box>
<Typography variant="h4" color="primary">
Purchased Augmentations
</Typography>
<Box mx={2}>
<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={<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>
</Tooltip>
<PurchasedAugmentations />
</Box>
<Typography variant="h4">Installed Augmentations</Typography>
<Box mx={2}>
<Typography>
List of all Augmentations that have been installed. You have gained the effects of these.
</Typography>
<InstalledAugmentations />
</Box>
<PlayerMultipliers />
<SourceFiles />
</>
);
}

View File

@@ -1,19 +1,28 @@
/**
* React Component for displaying a list of the player's installed Augmentations
* on the Augmentations UI
* React Component for displaying all of the player's installed Augmentations and
* Source-Files.
*
* It also contains 'configuration' buttons that allow you to change how the
* Augs/SF's are displayed
*/
import * as React from "react";
import { Player } from "../../Player";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import React, { useState } from "react";
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
import { Augmentations } from "../../Augmentation/Augmentations";
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Settings } from "../../Settings/Settings";
import { use } from "../../ui/Context";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import List from "@mui/material/List";
export function InstalledAugmentations(): React.ReactElement {
const sourceAugs = Player.augmentations.slice();
const setRerender = useState(true)[1];
const player = use.Player();
const sourceAugs = player.augmentations.slice();
if (Settings.OwnedAugmentationsOrder === OwnedAugmentationsOrderSetting.Alphabetically) {
sourceAugs.sort((aug1, aug2) => {
@@ -21,20 +30,42 @@ export function InstalledAugmentations(): React.ReactElement {
});
}
const augs = sourceAugs.map((e) => {
const aug = Augmentations[e.name];
function rerender(): void {
setRerender((old) => !old);
}
let level = null;
if (e.name === AugmentationNames.NeuroFluxGovernor) {
level = e.level;
}
function sortByAcquirementTime(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
rerender();
}
return (
<li key={e.name}>
<AugmentationAccordion aug={aug} level={level} />
</li>
);
});
function sortInOrder(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
rerender();
}
return <>{augs}</>;
return (
<>
<Tooltip title={"Sorts the Augmentations alphabetically in numeral order"}>
<Button onClick={sortInOrder}>Sort in Order</Button>
</Tooltip>
<Tooltip title={"Sorts the Augmentations based on when you acquired them (same as default)"}>
<Button sx={{ mx: 2 }} onClick={sortByAcquirementTime}>
Sort by Acquirement Time
</Button>
</Tooltip>
<List dense>
{sourceAugs.map((e) => {
const aug = Augmentations[e.name];
let level = null;
if (e.name === AugmentationNames.NeuroFluxGovernor) {
level = e.level;
}
return <AugmentationAccordion key={aug.name} aug={aug} level={level} />;
})}
</List>
</>
);
}

View File

@@ -1,115 +0,0 @@
/**
* React Component for displaying all of the player's installed Augmentations and
* Source-Files.
*
* It also contains 'configuration' buttons that allow you to change how the
* Augs/SF's are displayed
*/
import * as React from "react";
import { InstalledAugmentations } from "./InstalledAugmentations";
import { ListConfiguration } from "./ListConfiguration";
import { OwnedSourceFiles } from "./OwnedSourceFiles";
import { SourceFileMinus1 } from "./SourceFileMinus1";
import { Settings } from "../../Settings/Settings";
import { OwnedAugmentationsOrderSetting } from "../../Settings/SettingEnums";
type IProps = {
// nothing special.
};
type IState = {
rerenderFlag: boolean;
};
export class InstalledAugmentationsAndSourceFiles extends React.Component<IProps, IState> {
listRef: React.RefObject<HTMLUListElement>;
constructor(props: IProps) {
super(props);
this.state = {
rerenderFlag: false,
};
this.collapseAllHeaders = this.collapseAllHeaders.bind(this);
this.expandAllHeaders = this.expandAllHeaders.bind(this);
this.sortByAcquirementTime = this.sortByAcquirementTime.bind(this);
this.sortInOrder = this.sortInOrder.bind(this);
this.listRef = React.createRef();
}
collapseAllHeaders(): void {
const ul = this.listRef.current;
if (ul == null) {
return;
}
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (ticker.classList.contains("active")) {
ticker.click();
}
}
}
expandAllHeaders(): void {
const ul = this.listRef.current;
if (ul == null) {
return;
}
const tickers = ul.getElementsByClassName("accordion-header");
for (let i = 0; i < tickers.length; ++i) {
const ticker = tickers[i];
if (!(ticker instanceof HTMLButtonElement)) {
continue;
}
if (!ticker.classList.contains("active")) {
ticker.click();
}
}
}
rerender(): void {
this.setState((prevState) => {
return {
rerenderFlag: !prevState.rerenderFlag,
};
});
}
sortByAcquirementTime(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.AcquirementTime;
this.rerender();
}
sortInOrder(): void {
Settings.OwnedAugmentationsOrder = OwnedAugmentationsOrderSetting.Alphabetically;
this.rerender();
}
render(): React.ReactNode {
return (
<>
<ListConfiguration
collapseAllButtonsFn={this.collapseAllHeaders}
expandAllButtonsFn={this.expandAllHeaders}
sortByAcquirementTimeFn={this.sortByAcquirementTime}
sortInOrderFn={this.sortInOrder}
/>
<ul className="augmentations-list" ref={this.listRef}>
<SourceFileMinus1 />
<OwnedSourceFiles />
<InstalledAugmentations />
</ul>
</>
);
}
}

View File

@@ -1,33 +0,0 @@
/**
* React Component for configuring the way installed augmentations and
* Source-Files are displayed in the Augmentations UI
*/
import * as React from "react";
import { StdButton } from "../../ui/React/StdButton";
type IProps = {
collapseAllButtonsFn: () => void;
expandAllButtonsFn: () => void;
sortByAcquirementTimeFn: () => void;
sortInOrderFn: () => void;
};
export function ListConfiguration(props: IProps): React.ReactElement {
return (
<>
<StdButton onClick={props.expandAllButtonsFn} text="Expand All" />
<StdButton onClick={props.collapseAllButtonsFn} text="Collapse All" />
<StdButton
onClick={props.sortInOrderFn}
text="Sort in Order"
tooltip="Sorts the Augmentations alphabetically and Source-Files in numeral order"
/>
<StdButton
onClick={props.sortByAcquirementTimeFn}
text="Sort by Acquirement Time"
tooltip="Sorts the Augmentations and Source-Files based on when you acquired them (same as default)"
/>
</>
);
}

View File

@@ -20,20 +20,18 @@ export function OwnedSourceFiles(): React.ReactElement {
});
}
const sfs = sourceSfs.map((e) => {
const srcFileKey = "SourceFile" + e.n;
const sfObj = SourceFiles[srcFileKey];
if (sfObj == null) {
console.error(`Invalid source file number: ${e.n}`);
return null;
}
return (
<>
{sourceSfs.map((e) => {
const srcFileKey = "SourceFile" + e.n;
const sfObj = SourceFiles[srcFileKey];
if (sfObj == null) {
console.error(`Invalid source file number: ${e.n}`);
return null;
}
return (
<li key={e.n}>
<SourceFileAccordion level={e.lvl} sf={sfObj} />
</li>
);
});
return <>{sfs}</>;
return <SourceFileAccordion key={e.n} level={e.lvl} sf={sfObj} />;
})}
</>
);
}

View File

@@ -6,6 +6,11 @@ import * as React from "react";
import { Player } from "../../Player";
import { numeralWrapper } from "../../ui/numeralFormat";
import { Augmentations } from "../Augmentations";
import { Table, TableCell } from "../../ui/React/Table";
import TableBody from "@mui/material/TableBody";
import TableRow from "@mui/material/TableRow";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
function calculateAugmentedStats(): any {
const augP: any = {};
@@ -19,62 +24,73 @@ function calculateAugmentedStats(): any {
return augP;
}
export function PlayerMultipliers(): React.ReactElement {
const mults = calculateAugmentedStats();
function MultiplierTable(rows: any[]): React.ReactElement {
function improvements(r: number): JSX.Element[] {
let elems: JSX.Element[] = [];
if (r) {
elems = [<td key="2">&nbsp;{"=>"}&nbsp;</td>, <td key="3">{numeralWrapper.formatPercentage(r)}</td>];
}
return elems;
}
function Improvements({ r }: { r: number }): React.ReactElement {
if (r) {
return (
<table>
<tbody>
{rows.map((r: any) => (
<tr key={r[0]}>
<td key="0">
<span>{r[0]} multiplier:&nbsp;</span>
</td>
<td key="1" style={{ textAlign: "right" }}>
{numeralWrapper.formatPercentage(r[1])}
</td>
{improvements(r[2])}
</tr>
))}
</tbody>
</table>
<>
<TableCell key="2">
<Typography>&nbsp;{"=>"}&nbsp;</Typography>
</TableCell>
<TableCell key="3">
<Typography>{numeralWrapper.formatPercentage(r)}</Typography>
</TableCell>
</>
);
}
return <></>;
}
function MultiplierTable({ rows }: { rows: [string, number, number][] }): React.ReactElement {
return (
<Table size="small" padding="none">
<TableBody>
{rows.map((r: any) => (
<TableRow key={r[0]}>
<TableCell key="0">
<Typography noWrap>{r[0]} multiplier:&nbsp;</Typography>
</TableCell>
<TableCell key="1" style={{ textAlign: "right" }}>
<Typography noWrap>{numeralWrapper.formatPercentage(r[1])}</Typography>
</TableCell>
<Improvements r={r[2]} />
</TableRow>
))}
</TableBody>
</Table>
);
}
export function PlayerMultipliers(): React.ReactElement {
const mults = calculateAugmentedStats();
function BladeburnerMults(): React.ReactElement {
if (!Player.canAccessBladeburner()) return <></>;
return (
<>
{MultiplierTable([
[
"Bladeburner Success Chance",
Player.bladeburner_success_chance_mult,
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
],
[
"Bladeburner Max Stamina",
Player.bladeburner_max_stamina_mult,
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
],
[
"Bladeburner Stamina Gain",
Player.bladeburner_stamina_gain_mult,
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
],
[
"Bladeburner Field Analysis",
Player.bladeburner_analysis_mult,
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
],
])}
<MultiplierTable
rows={[
[
"Bladeburner Success Chance",
Player.bladeburner_success_chance_mult,
Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult,
],
[
"Bladeburner Max Stamina",
Player.bladeburner_max_stamina_mult,
Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult,
],
[
"Bladeburner Stamina Gain",
Player.bladeburner_stamina_gain_mult,
Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult,
],
[
"Bladeburner Field Analysis",
Player.bladeburner_analysis_mult,
Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult,
],
]}
/>
<br />
</>
);
@@ -82,99 +98,116 @@ export function PlayerMultipliers(): React.ReactElement {
return (
<>
<p>
<strong>
<u>Multipliers:</u>
</strong>
</p>
<br />
{MultiplierTable([
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult],
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult],
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult],
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult],
])}
<br />
<Typography variant="h4">Multipliers</Typography>
<Box mx={2}>
<MultiplierTable
rows={[
["Hacking Chance ", Player.hacking_chance_mult, Player.hacking_chance_mult * mults.hacking_chance_mult],
["Hacking Speed ", Player.hacking_speed_mult, Player.hacking_speed_mult * mults.hacking_speed_mult],
["Hacking Money ", Player.hacking_money_mult, Player.hacking_money_mult * mults.hacking_money_mult],
["Hacking Growth ", Player.hacking_grow_mult, Player.hacking_grow_mult * mults.hacking_grow_mult],
]}
/>
<br />
{MultiplierTable([
["Hacking Level ", Player.hacking_mult, Player.hacking_mult * mults.hacking_mult],
["Hacking Experience ", Player.hacking_exp_mult, Player.hacking_exp_mult * mults.hacking_exp_mult],
])}
<br />
<MultiplierTable
rows={[
["Hacking Level ", Player.hacking_mult, Player.hacking_mult * mults.hacking_mult],
["Hacking Experience ", Player.hacking_exp_mult, Player.hacking_exp_mult * mults.hacking_exp_mult],
]}
/>
<br />
{MultiplierTable([
["Strength Level ", Player.strength_mult, Player.strength_mult * mults.strength_mult],
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult],
])}
<br />
<MultiplierTable
rows={[
["Strength Level ", Player.strength_mult, Player.strength_mult * mults.strength_mult],
["Strength Experience ", Player.strength_exp_mult, Player.strength_exp_mult * mults.strength_exp_mult],
]}
/>
<br />
{MultiplierTable([
["Defense Level ", Player.defense_mult, Player.defense_mult * mults.defense_mult],
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult],
])}
<br />
<MultiplierTable
rows={[
["Defense Level ", Player.defense_mult, Player.defense_mult * mults.defense_mult],
["Defense Experience ", Player.defense_exp_mult, Player.defense_exp_mult * mults.defense_exp_mult],
]}
/>
<br />
{MultiplierTable([
["Dexterity Level ", Player.dexterity_mult, Player.dexterity_mult * mults.dexterity_mult],
["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult],
])}
<br />
<MultiplierTable
rows={[
["Dexterity Level ", Player.dexterity_mult, Player.dexterity_mult * mults.dexterity_mult],
["Dexterity Experience ", Player.dexterity_exp_mult, Player.dexterity_exp_mult * mults.dexterity_exp_mult],
]}
/>
<br />
{MultiplierTable([
["Agility Level ", Player.agility_mult, Player.agility_mult * mults.agility_mult],
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult],
])}
<br />
<MultiplierTable
rows={[
["Agility Level ", Player.agility_mult, Player.agility_mult * mults.agility_mult],
["Agility Experience ", Player.agility_exp_mult, Player.agility_exp_mult * mults.agility_exp_mult],
]}
/>
<br />
{MultiplierTable([
["Charisma Level ", Player.charisma_mult, Player.charisma_mult * mults.charisma_mult],
["Charisma Experience ", Player.charisma_exp_mult, Player.charisma_exp_mult * mults.charisma_exp_mult],
])}
<br />
<MultiplierTable
rows={[
["Charisma Level ", Player.charisma_mult, Player.charisma_mult * mults.charisma_mult],
["Charisma Experience ", Player.charisma_exp_mult, Player.charisma_exp_mult * mults.charisma_exp_mult],
]}
/>
<br />
{MultiplierTable([
[
"Hacknet Node production ",
Player.hacknet_node_money_mult,
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
],
[
"Hacknet Node purchase cost ",
Player.hacknet_node_purchase_cost_mult,
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
],
[
"Hacknet Node RAM upgrade cost ",
Player.hacknet_node_ram_cost_mult,
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
],
[
"Hacknet Node Core purchase cost ",
Player.hacknet_node_core_cost_mult,
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
],
[
"Hacknet Node level upgrade cost ",
Player.hacknet_node_level_cost_mult,
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
],
])}
<br />
<MultiplierTable
rows={[
[
"Hacknet Node production ",
Player.hacknet_node_money_mult,
Player.hacknet_node_money_mult * mults.hacknet_node_money_mult,
],
[
"Hacknet Node purchase cost ",
Player.hacknet_node_purchase_cost_mult,
Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult,
],
[
"Hacknet Node RAM upgrade cost ",
Player.hacknet_node_ram_cost_mult,
Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult,
],
[
"Hacknet Node Core purchase cost ",
Player.hacknet_node_core_cost_mult,
Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult,
],
[
"Hacknet Node level upgrade cost ",
Player.hacknet_node_level_cost_mult,
Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult,
],
]}
/>
<br />
{MultiplierTable([
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult],
["Faction reputation gain ", Player.faction_rep_mult, Player.faction_rep_mult * mults.faction_rep_mult],
["Salary ", Player.work_money_mult, Player.work_money_mult * mults.work_money_mult],
])}
<br />
<MultiplierTable
rows={[
["Company reputation gain ", Player.company_rep_mult, Player.company_rep_mult * mults.company_rep_mult],
["Faction reputation gain ", Player.faction_rep_mult, Player.faction_rep_mult * mults.faction_rep_mult],
["Salary ", Player.work_money_mult, Player.work_money_mult * mults.work_money_mult],
]}
/>
<br />
{MultiplierTable([
["Crime success ", Player.crime_success_mult, Player.crime_success_mult * mults.crime_success_mult],
["Crime money ", Player.crime_money_mult, Player.crime_money_mult * mults.crime_money_mult],
])}
<br />
<MultiplierTable
rows={[
["Crime success ", Player.crime_success_mult, Player.crime_success_mult * mults.crime_success_mult],
["Crime money ", Player.crime_money_mult, Player.crime_money_mult * mults.crime_money_mult],
]}
/>
<br />
<BladeburnerMults />
<BladeburnerMults />
</Box>
</>
);
}

View File

@@ -9,6 +9,7 @@ import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
import { Player } from "../../Player";
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
import List from "@mui/material/List";
export function PurchasedAugmentations(): React.ReactElement {
const augs: React.ReactElement[] = [];
@@ -29,12 +30,8 @@ export function PurchasedAugmentations(): React.ReactElement {
level = ownedAug.level;
}
augs.push(
<li key={`${ownedAug.name}${ownedAug.level}`}>
<AugmentationAccordion aug={aug} level={level} />
</li>,
);
augs.push(<AugmentationAccordion key={aug.name} aug={aug} level={level} />);
}
return <ul className="augmentations-list">{augs}</ul>;
return <List dense>{augs}</List>;
}

View File

@@ -2,14 +2,22 @@
* React Component for displaying a list of the player's Source-Files
* on the Augmentations UI
*/
import * as React from "react";
import React, { useState } from "react";
import { Player } from "../../Player";
import { Exploit, ExploitName } from "../../Exploits/Exploit";
import { BBAccordion } from "../../ui/React/BBAccordion";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemText from "@mui/material/ListItemText";
import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess";
export function SourceFileMinus1(): React.ReactElement {
const [open, setOpen] = useState(false);
const exploits = Player.exploits;
if (exploits.length === 0) {
@@ -17,33 +25,35 @@ export function SourceFileMinus1(): React.ReactElement {
}
return (
<li key={-1}>
<BBAccordion
headerContent={
<>
Source-File -1: Exploits in the BitNodes
<br />
Level {exploits.length} / ?
</>
}
panelContent={
<>
<p>
This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web
ecosystem.
</p>
<p>It increases all of the player's multipliers by 0.1%</p>
<br />
<Box component={Paper}>
<ListItemButton onClick={() => setOpen((old) => !old)}>
<ListItemText
primary={
<Typography style={{ whiteSpace: "pre-wrap" }}>
Source-File -1: Exploits in the BitNodes
<br />
Level {exploits.length} / ?
</Typography>
}
/>
{open ? <ExpandLess color="primary" /> : <ExpandMore color="primary" />}
</ListItemButton>
<Collapse in={open} unmountOnExit>
<Box m={4}>
<Typography>
This Source-File can only be acquired with obscure knowledge of the game, javascript, and the web ecosystem.
</Typography>
<Typography>It increases all of the player's multipliers by 0.1%</Typography>
<br />
<p>You have found the following exploits:</p>
<ul>
{exploits.map((c: Exploit) => (
<li key={c}>* {ExploitName(c)}</li>
))}
</ul>
</>
}
/>
</li>
<Typography>You have found the following exploits:</Typography>
<Box mx={2}>
{exploits.map((c: Exploit) => (
<Typography key={c}>* {ExploitName(c)}</Typography>
))}
</Box>
</Box>
</Collapse>
</Box>
);
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import { SourceFileMinus1 } from "./SourceFileMinus1";
import { OwnedSourceFiles } from "./OwnedSourceFiles";
import List from "@mui/material/List";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
export function SourceFiles(): React.ReactElement {
return (
<>
<Typography variant="h4">Source Files</Typography>
<Box mx={2}>
<List dense>
<SourceFileMinus1 />
<OwnedSourceFiles />
</List>
</Box>
</>
);
}

View File

@@ -16,8 +16,11 @@ class BitNode {
// BitNode number
number: number;
constructor(n: number, name: string, desc = "", info: JSX.Element = <></>) {
difficulty: 0 | 1 | 2;
constructor(n: number, difficulty: 0 | 1 | 2, name: string, desc = "", info: JSX.Element = <></>) {
this.number = n;
this.difficulty = difficulty;
this.name = name;
this.desc = desc;
this.info = info;
@@ -28,6 +31,7 @@ export const BitNodes: IMap<BitNode> = {};
BitNodes["BitNode1"] = new BitNode(
1,
0,
"Source Genesis",
"The original BitNode",
(
@@ -54,6 +58,7 @@ BitNodes["BitNode1"] = new BitNode(
);
BitNodes["BitNode2"] = new BitNode(
2,
0,
"Rise of the Underworld",
"From the shadows, they rose", //Gangs
(
@@ -101,6 +106,7 @@ BitNodes["BitNode2"] = new BitNode(
);
BitNodes["BitNode3"] = new BitNode(
3,
0,
"Corporatocracy",
"The Price of Civilization",
(
@@ -140,6 +146,7 @@ BitNodes["BitNode3"] = new BitNode(
);
BitNodes["BitNode4"] = new BitNode(
4,
1,
"The Singularity",
"The Man and the Machine",
(
@@ -164,6 +171,7 @@ BitNodes["BitNode4"] = new BitNode(
);
BitNodes["BitNode5"] = new BitNode(
5,
1,
"Artificial Intelligence",
"Posthuman",
(
@@ -211,6 +219,7 @@ BitNodes["BitNode5"] = new BitNode(
);
BitNodes["BitNode6"] = new BitNode(
6,
1,
"Bladeburners",
"Like Tears in Rain",
(
@@ -255,6 +264,7 @@ BitNodes["BitNode6"] = new BitNode(
);
BitNodes["BitNode7"] = new BitNode(
7,
2,
"Bladeburners 2079",
"More human than humans",
(
@@ -303,6 +313,7 @@ BitNodes["BitNode7"] = new BitNode(
);
BitNodes["BitNode8"] = new BitNode(
8,
2,
"Ghost of Wall Street",
"Money never sleeps",
(
@@ -347,6 +358,7 @@ BitNodes["BitNode8"] = new BitNode(
);
BitNodes["BitNode9"] = new BitNode(
9,
2,
"Hacktocracy",
"Hacknet Unleashed",
(
@@ -389,6 +401,7 @@ BitNodes["BitNode9"] = new BitNode(
);
BitNodes["BitNode10"] = new BitNode(
10,
2,
"Digital Carbon",
"Your body is not who you are",
(
@@ -428,6 +441,7 @@ BitNodes["BitNode10"] = new BitNode(
);
BitNodes["BitNode11"] = new BitNode(
11,
1,
"The Big Crash",
"Okay. Sell it all.",
(
@@ -492,6 +506,7 @@ BitNodes["BitNode11"] = new BitNode(
);
BitNodes["BitNode12"] = new BitNode(
12,
0,
"The Recursion",
"Repeat.",
(
@@ -507,18 +522,18 @@ BitNodes["BitNode12"] = new BitNode(
),
);
// Books: Frontera, Shiner
BitNodes["BitNode13"] = new BitNode(13, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, "", "COMING SOON");
BitNodes["BitNode17"] = new BitNode(17, "", "COMING SOON");
BitNodes["BitNode18"] = new BitNode(18, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, "", "COMING SOON");
BitNodes["BitNode20"] = new BitNode(20, "", "COMING SOON");
BitNodes["BitNode21"] = new BitNode(21, "", "COMING SOON");
BitNodes["BitNode22"] = new BitNode(22, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, "", "COMING SOON");
BitNodes["BitNode24"] = new BitNode(24, "", "COMING SOON");
BitNodes["BitNode13"] = new BitNode(13, 2, "fOS", "COMING SOON"); //Unlocks the new game mode and the rest of the BitNodes
BitNodes["BitNode14"] = new BitNode(14, 2, "", "COMING SOON");
BitNodes["BitNode15"] = new BitNode(15, 2, "", "COMING SOON");
BitNodes["BitNode16"] = new BitNode(16, 2, "", "COMING SOON");
BitNodes["BitNode17"] = new BitNode(17, 2, "", "COMING SOON");
BitNodes["BitNode18"] = new BitNode(18, 2, "", "COMING SOON");
BitNodes["BitNode19"] = new BitNode(19, 2, "", "COMING SOON");
BitNodes["BitNode20"] = new BitNode(20, 2, "", "COMING SOON");
BitNodes["BitNode21"] = new BitNode(21, 2, "", "COMING SOON");
BitNodes["BitNode22"] = new BitNode(22, 2, "", "COMING SOON");
BitNodes["BitNode23"] = new BitNode(23, 2, "", "COMING SOON");
BitNodes["BitNode24"] = new BitNode(24, 2, "", "COMING SOON");
export function initBitNodeMultipliers(p: IPlayer): void {
if (p.bitNodeN == null) {

View 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>
);
}

View File

@@ -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>
</>
);
}

View File

@@ -2,11 +2,43 @@ import React, { useState } from "react";
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
import { IRouter } from "../../ui/Router";
import { BitNodes } from "../BitNode";
import { enterBitNode } from "../../RedPill";
import { PortalPopup } from "./PortalPopup";
import { createPopup } from "../../ui/React/createPopup";
import { enterBitNode, setRedPillFlag } from "../../RedPill";
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}
/>
</>
);
}
@@ -71,6 +105,7 @@ interface IProps {
}
export function BitverseRoot(props: IProps): React.ReactElement {
setRedPillFlag(true);
const player = use.Player();
const enter = enterBitNode;
const destroyed = player.bitNodeN;
@@ -114,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 | |_/ |\| \ O \__| \_| | 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 | |_/ |\| \ O \__| \_| | 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 />
@@ -167,7 +202,7 @@ export function BitverseRoot(props: IProps): React.ReactElement {
"> ",
"> (Enter a new BitNode using the image above)",
]} />
</div>
</>
);
return <></>;

View File

@@ -0,0 +1,55 @@
import React from "react";
import { BitNodes } from "../BitNode";
import { IRouter } from "../../ui/Router";
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;
enter: (router: IRouter, flume: boolean, destroyedBitNode: number, newBitNode: number) => void;
}
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}`);
const maxSourceFileLevel = props.n === 12 ? "∞" : "3";
const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3);
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography variant="h4">
BitNode-{props.n}: {bitNode.name}
</Typography>
<br />
<Typography>
Source-File Level: {props.level} / {maxSourceFileLevel}
</Typography>
<br />
<br />
<Typography> Difficulty: {["easy", "normal", "hard"][bitNode.difficulty]}</Typography>
<br />
<br />
<Typography>{bitNode.info}</Typography>
<br />
<br />
<Button
onClick={() => {
props.enter(router, props.flume, props.destroyedBitNode, props.n);
props.onClose();
}}
>
Enter BN{props.n}.{newLevel}
</Button>
</Modal>
);
}

View File

@@ -1,46 +0,0 @@
import React from "react";
import { BitNodes } from "../BitNode";
import { IRouter } from "../../ui/Router";
import { removePopup } from "../../ui/React/createPopup";
interface IProps {
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 {
const bitNodeKey = "BitNode" + props.n;
const bitNode = BitNodes[bitNodeKey];
if (bitNode == null) throw new Error(`Could not find BitNode object for number: ${props.n}`);
const maxSourceFileLevel = props.n === 12 ? "∞" : "3";
const newLevel = Math.min(props.level + 1, props.n === 12 ? Infinity : 3);
return (
<>
<h1>
BitNode-{props.n}: {bitNode.name}
</h1>
<br />
Source-File Level: {props.level} / {maxSourceFileLevel}
<br />
<br />
{bitNode.info}
<br />
<br />
<button
className="std-button"
onClick={() => {
props.enter(props.router, props.flume, props.destroyedBitNode, props.n);
removePopup(props.popupId);
}}
>
Enter BN{props.n}.{newLevel}
</button>
</>
);
}

View File

@@ -1,7 +1,7 @@
import { Player } from "../Player";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { addOffset } from "../../utils/helpers/addOffset";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { addOffset } from "../utils/helpers/addOffset";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { BladeburnerConstants } from "./data/Constants";
import { IBladeburner } from "./IBladeburner";
import { IAction, ISuccessChanceParams } from "./IAction";
@@ -20,7 +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;

View File

@@ -1,5 +1,5 @@
import { IActionIdentifier } from "./IActionIdentifier";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
interface IParams {
name?: string;

View File

@@ -1,5 +1,5 @@
import { Operation, IOperationParams } from "./Operation";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
export class BlackOperation extends Operation {
constructor(params: IOperationParams | null = null) {

View File

@@ -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,
},
});
})();

View 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,
},
});
})();

View File

@@ -1,4 +1,4 @@
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../../utils/JSONReviver";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver";
import { IBladeburner } from "./IBladeburner";
import { IActionIdentifier } from "./IActionIdentifier";
import { ActionIdentifier } from "./ActionIdentifier";
@@ -9,7 +9,7 @@ import { BlackOperation } from "./BlackOperation";
import { Operation } from "./Operation";
import { Contract } from "./Contract";
import { GeneralActions } from "./GeneralActions";
import { formatNumber } from "../../utils/StringHelperFunctions";
import { formatNumber } from "../utils/StringHelperFunctions";
import { Skills } from "./Skills";
import { Skill } from "./Skill";
import { City } from "./City";
@@ -17,21 +17,21 @@ import { IAction } from "./IAction";
import { IPlayer } from "../PersonObjects/IPlayer";
import { IRouter } from "../ui/Router";
import { ConsoleHelpText } from "./data/Help";
import { exceptionAlert } from "../../utils/helpers/exceptionAlert";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { BladeburnerConstants } from "./data/Constants";
import { numeralWrapper } from "../ui/numeralFormat";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { addOffset } from "../../utils/helpers/addOffset";
import { addOffset } from "../utils/helpers/addOffset";
import { Faction } from "../Faction/Faction";
import { Factions, factionExists } from "../Faction/Factions";
import { calculateHospitalizationCost } from "../Hospital/Hospital";
import { redPillFlag } from "../RedPill";
import { dialogBoxCreate } from "../../utils/DialogBox";
import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Settings } from "../Settings/Settings";
import { Augmentations } from "../Augmentation/Augmentations";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { getTimestamp } from "../../utils/helpers/getTimestamp";
import { getTimestamp } from "../utils/helpers/getTimestamp";
import { joinFaction } from "../Faction/FactionHelpers";
import { WorkerScript } from "../Netscript/WorkerScript";
@@ -1586,11 +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) {

View File

@@ -1,7 +1,7 @@
import { BladeburnerConstants } from "./data/Constants";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { addOffset } from "../../utils/helpers/addOffset";
import { getRandomInt } from "../utils/helpers/getRandomInt";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { addOffset } from "../utils/helpers/addOffset";
interface IChangePopulationByCountParams {
estChange: number;

View File

@@ -1,6 +1,6 @@
import { IBladeburner } from "./IBladeburner";
import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
export class Contract extends Action {
constructor(params: IActionParams | null = null) {

View File

@@ -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>",
});
})();

View 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,
});
})();

View File

@@ -18,7 +18,6 @@ export interface ISuccessChanceParams {
export interface IAction {
name: string;
desc: string;
// Difficulty scales with level. See getDifficulty() method
level: number;

View File

@@ -1,7 +1,7 @@
import { IBladeburner } from "./IBladeburner";
import { BladeburnerConstants } from "./data/Constants";
import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
export interface IOperationParams extends IActionParams {
reqdRank?: number;

View 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.</>,
},
};

View 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.
</>
),
},
};

View 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 />
</>
),
},
};

View File

@@ -1,4 +1,4 @@
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
import { getRandomInt } from "../../utils/helpers/getRandomInt";
export const Growths: {
[key: string]: (() => number) | undefined;

View 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.
</>
),
},
};

View 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>
);
}

View File

@@ -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>
</>
);
}

View 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>
);
}

View File

@@ -1,23 +1,30 @@
import React, { useState } from "react";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
import { stealthIcon, killIcon } from "../data/Icons";
import { createPopup } from "../../ui/React/createPopup";
import { TeamSizePopup } from "./TeamSizePopup";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { TeamSizeButton } from "./TeamSizeButton";
import { IBladeburner } from "../IBladeburner";
import { BlackOperation } from "../BlackOperation";
import { BlackOperations } from "../data/BlackOperations";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText";
import { SuccessChance } from "./SuccessChance";
import { StartButton } from "./StartButton";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
action: any;
action: BlackOperation;
}
export function BlackOpElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const isCompleted = props.bladeburner.blackops[props.action.name] != null;
if (isCompleted) {
return <h2 style={{ display: "block" }}>{props.action.name} (COMPLETED)</h2>;
@@ -26,7 +33,6 @@ export function BlackOpElem(props: IProps): React.ReactElement {
const isActive =
props.bladeburner.action.type === ActionTypes["BlackOperation"] &&
props.action.name === props.bladeburner.action.name;
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
const actionTime = props.action.getActionTime(props.bladeburner);
const hasReqdRank = props.bladeburner.rank >= props.action.reqdRank;
const computedActionTimeCurrent = Math.min(
@@ -34,70 +40,54 @@ export function BlackOpElem(props: IProps): React.ReactElement {
props.bladeburner.actionTimeToComplete,
);
function onStart(): void {
props.bladeburner.action.type = ActionTypes.BlackOperation;
props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
}
function onTeam(): void {
const popupId = "bladeburner-operation-set-team-size-popup";
createPopup(popupId, TeamSizePopup, {
bladeburner: props.bladeburner,
action: props.action,
popupId: popupId,
});
const actionData = BlackOperations[props.action.name];
if (actionData === undefined) {
throw new Error(`Cannot find data for ${props.action.name}`);
}
return (
<>
<h2 style={{ display: "inline-block" }}>
<Paper sx={{ my: 1, p: 1 }}>
<Typography>
{isActive ? (
<>
<CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} /{" "}
{formatNumber(props.bladeburner.actionTimeToComplete, 0)})
<>
<CopyableText value={props.action.name} /> (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>
);
}

View File

@@ -22,7 +22,8 @@ export function BlackOpList(props: IProps): React.ReactElement {
});
blackops = blackops.filter(
(blackop: BlackOperation, i: number) => !(
(blackop: BlackOperation, i: number) =>
!(
props.bladeburner.blackops[blackops[i].name] == null &&
i !== 0 &&
props.bladeburner.blackops[blackops[i - 1].name] == null
@@ -34,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} />
))}
</>
);

View File

@@ -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} />
</>
);

View File

@@ -1,7 +1,7 @@
import React from "react";
import { use } from "../../ui/Context";
import { CinematicText } from "../../ui/React/CinematicText";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
export function BladeburnerCinematic(): React.ReactElement {
const router = use.Router();

View File

@@ -1,45 +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 { IBladeburner } from "../IBladeburner";
import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
interface IProps {
bladeburner: IBladeburner;
}
export function BladeburnerRoot(props: IProps): React.ReactElement {
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",
}}
>
<Stats bladeburner={props.bladeburner} player={player} router={router} />
</div>
<Console bladeburner={props.bladeburner} player={player} />
</div>
<div
style={{
width: "70%",
display: "block",
border: "1px solid white",
marginTop: "6px",
padding: "6px",
position: "relative",
}}
>
<AllPages bladeburner={props.bladeburner} player={player} />
</div>
</div>
<Box display="flex" flexDirection="column">
<Grid container>
<Grid item xs={6}>
<Stats bladeburner={bladeburner} player={player} router={router} />
</Grid>
<Grid item xs={6}>
<Console bladeburner={bladeburner} player={player} />
</Grid>
</Grid>
<AllPages bladeburner={bladeburner} player={player} />
</Box>
);
}

View File

@@ -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>&gt;&nbsp;</Typography>
</>
),
spellCheck: false,
}}
/>
</List>
<div ref={scrollHook}></div>
</Box>
</Box>
);
}

View File

@@ -1,114 +1,79 @@
import React, { useState } from "react";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { stealthIcon, killIcon } from "../data/Icons";
import { BladeburnerConstants } from "../data/Constants";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { Contracts } from "../data/Contracts";
import { IBladeburner } from "../IBladeburner";
import { IAction } from "../IAction";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText";
import { ActionLevel } from "./ActionLevel";
import { Autolevel } from "./Autolevel";
import { StartButton } from "./StartButton";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
action: any;
action: IAction;
}
export function ContractElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const isActive =
props.bladeburner.action.type === ActionTypes["Contract"] && props.action.name === props.bladeburner.action.name;
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
const computedActionTimeCurrent = Math.min(
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
props.bladeburner.actionTimeToComplete,
);
const maxLevel = props.action.level >= props.action.maxLevel;
const actionTime = props.action.getActionTime(props.bladeburner);
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
function onStart(): void {
props.bladeburner.action.type = ActionTypes.Contract;
props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
}
function increaseLevel(): void {
++props.action.level;
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
}
function decreaseLevel(): void {
--props.action.level;
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
}
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
props.action.autoLevel = event.target.checked;
setRerender((old) => !old);
const actionData = Contracts[props.action.name];
if (actionData === undefined) {
throw new Error(`Cannot find data for ${props.action.name}`);
}
return (
<>
<h2 style={{ display: "inline-block" }}>
{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>
);
}

View File

@@ -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} />
))}
</>
);

View File

@@ -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} />
</>
);

View File

@@ -1,19 +1,30 @@
import React, { useState } from "react";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { IBladeburner } from "../IBladeburner";
import { IAction } from "../IAction";
import { GeneralActions } from "../data/GeneralActions";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CopyableText } from "../../ui/React/CopyableText";
import { StartButton } from "./StartButton";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
action: any;
action: IAction;
}
export function GeneralActionElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const isActive = props.action.name === props.bladeburner.action.name;
const computedActionTimeCurrent = Math.min(
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
@@ -37,44 +48,44 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
? Math.max(0, Math.min(props.bladeburner.getRecruitmentSuccessChance(props.player), 1))
: -1;
function onStart(): void {
props.bladeburner.action.type = ActionTypes[props.action.name as string];
props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
const actionData = GeneralActions[props.action.name];
if (actionData === undefined) {
throw new Error(`Cannot find data for ${props.action.name}`);
}
return (
<>
<h2 style={{ display: "inline-block" }}>
{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>
);
}

View File

@@ -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} />
))}
</>
);

View File

@@ -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} />
</>
);

View 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>;
}

View File

@@ -1,128 +1,82 @@
import React, { useState } from "react";
import { ActionTypes } from "../data/ActionTypes";
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../../utils/StringHelperFunctions";
import { stealthIcon, killIcon } from "../data/Icons";
import { BladeburnerConstants } from "../data/Constants";
import { createPopup } from "../../ui/React/createPopup";
import { TeamSizePopup } from "./TeamSizePopup";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { createProgressBarText } from "../../utils/helpers/createProgressBarText";
import { formatNumber, convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
import { SuccessChance } from "./SuccessChance";
import { ActionLevel } from "./ActionLevel";
import { Autolevel } from "./Autolevel";
import { StartButton } from "./StartButton";
import { TeamSizeButton } from "./TeamSizeButton";
import { IBladeburner } from "../IBladeburner";
import { Operation } from "../Operation";
import { Operations } from "../data/Operations";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CopyableText } from "../../ui/React/CopyableText";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
interface IProps {
bladeburner: IBladeburner;
player: IPlayer;
action: any;
action: Operation;
}
export function OperationElem(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((old) => !old);
}
const isActive =
props.bladeburner.action.type === ActionTypes["Operation"] && props.action.name === props.bladeburner.action.name;
const estimatedSuccessChance = props.action.getEstSuccessChance(props.bladeburner);
const computedActionTimeCurrent = Math.min(
props.bladeburner.actionTimeCurrent + props.bladeburner.actionTimeOverflow,
props.bladeburner.actionTimeToComplete,
);
const maxLevel = props.action.level >= props.action.maxLevel;
const actionTime = props.action.getActionTime(props.bladeburner);
const autolevelCheckboxId = `bladeburner-${props.action.name}-autolevel-checkbox`;
function onStart(): void {
props.bladeburner.action.type = ActionTypes.Operation;
props.bladeburner.action.name = props.action.name;
props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
}
function onTeam(): void {
const popupId = "bladeburner-operation-set-team-size-popup";
createPopup(popupId, TeamSizePopup, {
bladeburner: props.bladeburner,
action: props.action,
popupId: popupId,
});
}
function increaseLevel(): void {
++props.action.level;
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
}
function decreaseLevel(): void {
--props.action.level;
if (isActive) props.bladeburner.startAction(props.player, props.bladeburner.action);
setRerender((old) => !old);
}
function onAutolevel(event: React.ChangeEvent<HTMLInputElement>): void {
props.action.autoLevel = event.target.checked;
setRerender((old) => !old);
const actionData = Operations[props.action.name];
if (actionData === undefined) {
throw new Error(`Cannot find data for ${props.action.name}`);
}
return (
<>
<h2 style={{ display: "inline-block" }}>
{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>
);
}

Some files were not shown because too many files have changed in this diff Show More