mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-20 13:15:48 +01:00
commit
112f00f3d7
45
Quotes.txt
Normal file
45
Quotes.txt
Normal file
@ -0,0 +1,45 @@
|
||||
Collection of Quotes
|
||||
|
||||
The past is relevant only as data
|
||||
|
||||
Pull on the new flesh like borrowed gloves and burn your fingers once again.
|
||||
|
||||
A weapon is a tool. A tool for killing and destroying. And there will be times
|
||||
when you must kill and destroy. Then you will choose and equip yourself with the tools
|
||||
that you need. But remember the weakness of weapons. They are an extension --
|
||||
You are the killer and destroyer. You are whole, with or without them.
|
||||
|
||||
For all that we have done, as a civilization, as individuals, the universe is
|
||||
not stable, and nor is any single thing within it. Stars consume themselves,
|
||||
the universe itself rushes apart, and we ourselves are composed of matter in
|
||||
constant flux. Colonies of cells in temporary alliance, replicating and
|
||||
decaying and housed within, an incandescent cloud of electrical impulse and
|
||||
precariously stacked carbon code memory. This is reality, this is self knowledge,
|
||||
and the perception of it will, of course, make you dizzy.
|
||||
|
||||
You are still young and stupid. Human life has no value. Haven't you learned
|
||||
that yet, Takeshi, with all you've seen? It has no value, intrinsic to itself.
|
||||
Machines cost money to build. Raw materials cost money to extract. But people?"
|
||||
She made a tiny spitting sound. "You can always get some more people. they
|
||||
reproduce like cancer cells, whether you want them or not. They are abundant,
|
||||
Takeshi. Why should they be valuable? Do you know that it costs us less to
|
||||
recruit and use up a real snuff whore than it does to set up and run the virtual
|
||||
equivalent format. Real human flesh is cheaper than a machine. It's the axiomatic
|
||||
truth of our times.
|
||||
|
||||
Peace is an illusion, no matter how tranquil the world seems, peace doesn't last long.
|
||||
Peace is a struggle against our very nature. A skin we sketch over the bone, muscle,
|
||||
and sinew of our own innate savagery.
|
||||
|
||||
The human eye is a wonderful device. With a little effort, it can fail to see even
|
||||
the most glaring injustice.
|
||||
|
||||
Humanity has spread to the stars. We set out like ancient seafarers to explore
|
||||
the limitless ocean of space. But no matter how far we venture into the unknown,
|
||||
the worst monsters are those we bring with us.
|
||||
|
||||
What we believe shapes who we are. Belief can bring us salvation or destruction.
|
||||
But when you believe a lie for too long, the truth doesn't set you free. It tears
|
||||
you apart.
|
||||
|
||||
We aren't meant to live forever. It corrupts even the best of us.
|
@ -31,13 +31,12 @@
|
||||
border: 1px solid #fff;
|
||||
margin: 2px;
|
||||
padding: 2px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bladeburner-nav-button {
|
||||
@extend %bladeburner-nav-button;
|
||||
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
background-color: #3d4044;
|
||||
}
|
||||
|
49
css/codemirror-overrides.scss
Normal file
49
css/codemirror-overrides.scss
Normal file
@ -0,0 +1,49 @@
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Customized styling for the Code Mirror editor
|
||||
*/
|
||||
|
||||
#codemirror-form-wrapper {
|
||||
height: 80%;
|
||||
margin: 10px 0px 0px 6px;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
z-index: 1;
|
||||
font-family: $fontFamily;
|
||||
font-size: $defaultFontSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight matches
|
||||
*/
|
||||
.cm-matchhighlight {
|
||||
background-color: #8F908A;
|
||||
}
|
||||
|
||||
.CodeMirror-selection-highlight-scrollbar {
|
||||
background-color: #8F908A;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Invisibles
|
||||
*/
|
||||
.cm-whitespace::before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
color: #404F7D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vim command display
|
||||
*/
|
||||
#codemirror-vim-command-display-wrapper {
|
||||
background-color: white;
|
||||
font-size: 13px;
|
||||
height: 30px;
|
||||
margin-left: 6px;
|
||||
}
|
@ -18,116 +18,6 @@
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
/* Script Editor */
|
||||
#script-editor-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#javascript-editor {
|
||||
margin: 10px;
|
||||
height: 80%;
|
||||
width: 100%;
|
||||
margin-left: 6px;
|
||||
padding-left: 6px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
z-index: 1;
|
||||
font-family: $fontFamily;
|
||||
}
|
||||
|
||||
.ace_line,
|
||||
.ace_line * {
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ace_text-input {
|
||||
font-size: $defaultFontSize;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* This temp element is used for auto adjusting filename field */
|
||||
.tmp-element {
|
||||
visibility: hidden;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#script-editor-container {
|
||||
position: fixed;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#script-editor-buttons-wrapper {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#script-editor-wrapper {
|
||||
height: 100%;
|
||||
width: 70%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#script-editor-filename-wrapper {
|
||||
background-color: #555;
|
||||
margin-left: 6px;
|
||||
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;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
resize: none;
|
||||
color: #fff;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#script-editor-options-panel fieldset {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding: 2px;
|
||||
font-size: $defaultFontSize * 0.75;
|
||||
}
|
||||
|
||||
/* Active scripts */
|
||||
.active-scripts-list {
|
||||
list-style-type: none;
|
||||
@ -537,70 +427,3 @@
|
||||
display: inline;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
/* Stock market */
|
||||
#stock-market-container {
|
||||
position: fixed;
|
||||
padding: 6px;
|
||||
|
||||
p {
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
}
|
||||
a {
|
||||
font-size: $defaultFontSize * 0.875;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Change font size of Stock TIcker headers */
|
||||
#stock-market-list li {
|
||||
button {
|
||||
font-size: $defaultFontSize;
|
||||
}
|
||||
}
|
||||
|
||||
#stock-market-container p {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#stock-market-container a {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#stock-market-watchlist-filter {
|
||||
width: 50%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.stock-market-input {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin: 2px;
|
||||
background-color: #000;
|
||||
border: 1px solid #fff;
|
||||
color: var(--my-font-color);
|
||||
}
|
||||
|
||||
.stock-market-position-text {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.stock-market-order-list {
|
||||
overflow-y: auto;
|
||||
max-height: 100px;
|
||||
}
|
||||
|
||||
.stock-market-order-cancel-btn {
|
||||
background-color: #000;
|
||||
border: 1px solid #fff;
|
||||
color: var(--my-font-color);
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@
|
||||
z-index: 10;
|
||||
width: 50%;
|
||||
height: auto;
|
||||
max-height: 40%;
|
||||
max-height: 50%;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
margin: -10% 0 0 -25%;
|
||||
|
22
css/redpill.scss
Normal file
22
css/redpill.scss
Normal file
@ -0,0 +1,22 @@
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for the Red Pill screen (the BitNode selection UI)
|
||||
*/
|
||||
#red-pill-container {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
|
||||
.bitnode {
|
||||
color: #00f;
|
||||
}
|
||||
|
||||
.bitnode-destroyed {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.bitnode:hover,
|
||||
.bitnode-destroyed:hover {
|
||||
color: #fff;
|
||||
}
|
28
css/resleeving.scss
Normal file
28
css/resleeving.scss
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Styling for the Re-Sleeving Page
|
||||
*/
|
||||
@import "theme";
|
||||
|
||||
.resleeve-container {
|
||||
border: 1px solid white;
|
||||
margin: 4px;
|
||||
width: 75%;
|
||||
|
||||
p {
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
}
|
||||
}
|
||||
|
||||
.resleeve-panel {
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.resleeve-aug-selector {
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
|
||||
option {
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
}
|
||||
}
|
123
css/scripteditor.scss
Normal file
123
css/scripteditor.scss
Normal file
@ -0,0 +1,123 @@
|
||||
@import "mixins";
|
||||
@import "theme";
|
||||
|
||||
/**
|
||||
* Styling for Script Editor (both Ace and CodeMirror)
|
||||
*/
|
||||
|
||||
#script-editor-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#ace-editor {
|
||||
margin: 10px;
|
||||
height: 80%;
|
||||
width: 100%;
|
||||
margin-left: 6px;
|
||||
padding-left: 6px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
z-index: 1;
|
||||
font-family: $fontFamily;
|
||||
}
|
||||
|
||||
/* This temp element is used for auto adjusting filename field */
|
||||
.tmp-element {
|
||||
visibility: hidden;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#script-editor-container {
|
||||
position: fixed;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#script-editor-buttons-wrapper {
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
#script-editor-wrapper {
|
||||
height: 100%;
|
||||
width: 70%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#script-editor-filename-wrapper {
|
||||
background-color: #555;
|
||||
margin-left: 6px;
|
||||
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;
|
||||
display: inline-block;
|
||||
float: center;
|
||||
resize: none;
|
||||
color: #fff;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Specific overrides for Ace Editor */
|
||||
.ace_line,
|
||||
.ace_line * {
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ace_text-input {
|
||||
font-size: $defaultFontSize;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */
|
29
css/sleeves.scss
Normal file
29
css/sleeves.scss
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Styling for the Sleeves Management page
|
||||
*/
|
||||
@import "theme";
|
||||
|
||||
.sleeve-container {
|
||||
border: 1px solid white;
|
||||
margin: 4px;
|
||||
width: 75%;
|
||||
|
||||
p {
|
||||
font-size: $defaultFontSize * 0.875;
|
||||
}
|
||||
}
|
||||
|
||||
.sleeves-page-info {
|
||||
display: "block";
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.sleeve-panel {
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
padding: 2px;
|
||||
|
||||
select {
|
||||
display: block;
|
||||
}
|
||||
}
|
66
css/stockmarket.scss
Normal file
66
css/stockmarket.scss
Normal file
@ -0,0 +1,66 @@
|
||||
@import "theme";
|
||||
|
||||
#stock-market-container {
|
||||
position: fixed;
|
||||
padding: 6px;
|
||||
|
||||
p {
|
||||
font-size: $defaultFontSize * 0.8125;
|
||||
}
|
||||
a {
|
||||
font-size: $defaultFontSize * 0.875;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
#stock-market-list li {
|
||||
button {
|
||||
font-size: $defaultFontSize;
|
||||
}
|
||||
}
|
||||
|
||||
#stock-market-container p {
|
||||
padding: 6px;
|
||||
margin: 6px;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#stock-market-container a {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#stock-market-watchlist-filter {
|
||||
width: 50%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.stock-market-input {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin: 2px;
|
||||
background-color: #000;
|
||||
border: 1px solid #fff;
|
||||
color: var(--my-font-color);
|
||||
}
|
||||
|
||||
.stock-market-position-text {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.stock-market-order-list {
|
||||
overflow-y: auto;
|
||||
max-height: 100px;
|
||||
}
|
||||
|
||||
.stock-market-order-cancel-btn {
|
||||
background-color: #000;
|
||||
border: 1px solid #fff;
|
||||
color: var(--my-font-color);
|
||||
margin: 2px;
|
||||
padding: 0;
|
||||
}
|
@ -35,11 +35,6 @@ li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#entire-game-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
@ -67,5 +67,6 @@
|
||||
}
|
||||
|
||||
#terminal-input-text-box {
|
||||
margin-left: 2px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
@ -46,18 +46,6 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#red-pill-container,
|
||||
#cinematic-text-container {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.bitnode {
|
||||
color: #00f;
|
||||
}
|
||||
.bitnode-destroyed {
|
||||
color: #f00;
|
||||
}
|
||||
.bitnode:hover,
|
||||
.bitnode-destroyed:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
2
dist/engine.bundle.js
vendored
2
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
205
dist/engine.css
vendored
205
dist/engine.css
vendored
@ -1,3 +1,46 @@
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/**
|
||||
* Customized styling for the Code Mirror editor
|
||||
*/
|
||||
#codemirror-form-wrapper {
|
||||
height: 80%;
|
||||
margin: 10px 0px 0px 6px; }
|
||||
|
||||
.CodeMirror {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 2px solid var(--my-highlight-color);
|
||||
z-index: 1;
|
||||
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman";
|
||||
font-size: 16px; }
|
||||
|
||||
/**
|
||||
* Highlight matches
|
||||
*/
|
||||
.cm-matchhighlight {
|
||||
background-color: #8F908A; }
|
||||
|
||||
.CodeMirror-selection-highlight-scrollbar {
|
||||
background-color: #8F908A; }
|
||||
|
||||
/**
|
||||
* Show Invisibles
|
||||
*/
|
||||
.cm-whitespace::before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
color: #404F7D; }
|
||||
|
||||
/**
|
||||
* Vim command display
|
||||
*/
|
||||
#codemirror-vim-command-display-wrapper {
|
||||
background-color: white;
|
||||
font-size: 13px;
|
||||
height: 30px;
|
||||
margin-left: 6px; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/* COLORS */
|
||||
@ -40,10 +83,6 @@ ul {
|
||||
li {
|
||||
list-style-type: none; }
|
||||
|
||||
span {
|
||||
margin: 4px;
|
||||
padding: 4px; }
|
||||
|
||||
#entire-game-container {
|
||||
background-color: transparent; }
|
||||
|
||||
@ -771,29 +810,18 @@ button {
|
||||
white-space: pre; }
|
||||
|
||||
#terminal-input-text-box {
|
||||
margin-left: 2px;
|
||||
flex: 1 1 auto; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
|
||||
terminal which has its own page) */
|
||||
.generic-menupage-container {
|
||||
height: 100%;
|
||||
padding-left: 10px;
|
||||
margin-left: 10%;
|
||||
width: 99%;
|
||||
overflow-y: scroll; }
|
||||
|
||||
/* Character Info */
|
||||
#character-container {
|
||||
padding-top: 10px;
|
||||
position: fixed; }
|
||||
|
||||
/* Script Editor */
|
||||
/**
|
||||
* Styling for Script Editor (both Ace and CodeMirror)
|
||||
*/
|
||||
#script-editor-container {
|
||||
background-color: transparent; }
|
||||
|
||||
#javascript-editor {
|
||||
#ace-editor {
|
||||
margin: 10px;
|
||||
height: 80%;
|
||||
width: 100%;
|
||||
@ -805,16 +833,6 @@ button {
|
||||
z-index: 1;
|
||||
font-family: "Lucida Console", "Lucida Sans Unicode", "Fira Mono", "Consolas", "Courier New", Courier, monospace, "Times New Roman"; }
|
||||
|
||||
.ace_line,
|
||||
.ace_line * {
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
|
||||
.ace_text-input {
|
||||
font-size: 16px;
|
||||
background-color: transparent; }
|
||||
|
||||
/* This temp element is used for auto adjusting filename field */
|
||||
.tmp-element {
|
||||
visibility: hidden;
|
||||
@ -877,13 +895,45 @@ button {
|
||||
padding: 2px;
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
color: #fff; }
|
||||
color: #fff;
|
||||
max-height: 50%; }
|
||||
|
||||
#script-editor-options-panel fieldset {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding: 2px;
|
||||
font-size: 12px; }
|
||||
#script-editor-options-panel fieldset input {
|
||||
margin: 2px; }
|
||||
|
||||
/* Specific overrides for Ace Editor */
|
||||
.ace_line,
|
||||
.ace_line * {
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
|
||||
.ace_text-input {
|
||||
font-size: 16px;
|
||||
background-color: transparent; }
|
||||
|
||||
/* Specified overrides for Code mirror Editor are defined in codemirror-override.scss */
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/* CSS for different main menu pages, such as character info, script editor, etc (but excluding
|
||||
terminal which has its own page) */
|
||||
.generic-menupage-container {
|
||||
height: 100%;
|
||||
padding-left: 10px;
|
||||
margin-left: 10%;
|
||||
width: 99%;
|
||||
overflow-y: scroll; }
|
||||
|
||||
/* Character Info */
|
||||
#character-container {
|
||||
padding-top: 10px;
|
||||
position: fixed; }
|
||||
|
||||
/* Active scripts */
|
||||
.active-scripts-list {
|
||||
@ -1222,7 +1272,26 @@ button {
|
||||
display: inline;
|
||||
width: 25%; }
|
||||
|
||||
/* Stock market */
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/**
|
||||
* Styling for the Red Pill screen (the BitNode selection UI)
|
||||
*/
|
||||
#red-pill-container {
|
||||
position: fixed; }
|
||||
|
||||
.bitnode {
|
||||
color: #00f; }
|
||||
|
||||
.bitnode-destroyed {
|
||||
color: #f00; }
|
||||
|
||||
.bitnode:hover,
|
||||
.bitnode-destroyed:hover {
|
||||
color: #fff; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
#stock-market-container {
|
||||
position: fixed;
|
||||
padding: 6px; }
|
||||
@ -1235,13 +1304,12 @@ button {
|
||||
margin-left: 10px;
|
||||
display: block; }
|
||||
|
||||
/* Change font size of Stock TIcker headers */
|
||||
#stock-market-list li button {
|
||||
font-size: 16px; }
|
||||
|
||||
#stock-market-container p {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
padding: 6px;
|
||||
margin: 6px;
|
||||
width: 70%; }
|
||||
|
||||
#stock-market-container a {
|
||||
@ -1318,20 +1386,9 @@ button {
|
||||
text-decoration: none;
|
||||
cursor: pointer; }
|
||||
|
||||
#red-pill-container,
|
||||
#cinematic-text-container {
|
||||
position: fixed; }
|
||||
|
||||
.bitnode {
|
||||
color: #00f; }
|
||||
|
||||
.bitnode-destroyed {
|
||||
color: #f00; }
|
||||
|
||||
.bitnode:hover,
|
||||
.bitnode-destroyed:hover {
|
||||
color: #fff; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
/* Pop-up boxes */
|
||||
@ -1393,7 +1450,7 @@ button {
|
||||
z-index: 10;
|
||||
width: 50%;
|
||||
height: auto;
|
||||
max-height: 40%;
|
||||
max-height: 50%;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
margin: -10% 0 0 -25%;
|
||||
@ -2047,12 +2104,11 @@ button {
|
||||
.bladeburner-nav-button, .bladeburner-nav-button-inactive {
|
||||
border: 1px solid #fff;
|
||||
margin: 2px;
|
||||
padding: 2px; }
|
||||
|
||||
.bladeburner-nav-button {
|
||||
padding: 2px;
|
||||
color: #fff; }
|
||||
.bladeburner-nav-button:hover {
|
||||
background-color: #3d4044; }
|
||||
|
||||
.bladeburner-nav-button:hover {
|
||||
background-color: #3d4044; }
|
||||
|
||||
.bladeburner-nav-button-inactive {
|
||||
text-decoration: none;
|
||||
@ -2136,6 +2192,51 @@ button {
|
||||
margin: 1px;
|
||||
padding: 1px; }
|
||||
|
||||
/**
|
||||
* Styling for the Sleeves Management page
|
||||
*/
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
.sleeve-container {
|
||||
border: 1px solid white;
|
||||
margin: 4px;
|
||||
width: 75%; }
|
||||
.sleeve-container p {
|
||||
font-size: 14px; }
|
||||
|
||||
.sleeves-page-info {
|
||||
display: "block";
|
||||
width: 75%; }
|
||||
|
||||
.sleeve-panel {
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
padding: 2px; }
|
||||
.sleeve-panel select {
|
||||
display: block; }
|
||||
|
||||
/**
|
||||
* Styling for the Re-Sleeving Page
|
||||
*/
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
.resleeve-container {
|
||||
border: 1px solid white;
|
||||
margin: 4px;
|
||||
width: 75%; }
|
||||
.resleeve-container p {
|
||||
font-size: 13px; }
|
||||
|
||||
.resleeve-panel {
|
||||
display: inline-block;
|
||||
margin: 0px;
|
||||
padding: 2px; }
|
||||
|
||||
.resleeve-aug-selector {
|
||||
font-size: 13px; }
|
||||
.resleeve-aug-selector option {
|
||||
font-size: 13px; }
|
||||
|
||||
/* required LIB STYLES */
|
||||
/* .Treant se automatski dodaje na svaki chart conatiner */
|
||||
.Treant {
|
||||
|
219
dist/vendor.bundle.js
vendored
219
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
5527
dist/vendor.css
vendored
5527
dist/vendor.css
vendored
File diff suppressed because one or more lines are too long
14
doc/source/advancedgameplay.rst
Normal file
14
doc/source/advancedgameplay.rst
Normal file
@ -0,0 +1,14 @@
|
||||
Advanced Gameplay
|
||||
=================
|
||||
This section documents Bitburner gameplay elements that are **not** immediately
|
||||
available and/or accessible to the player. These gameplay mechanics
|
||||
must be unlocked.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 5
|
||||
:caption: Elements:
|
||||
|
||||
BitNodes <advancedgameplay/bitnodes>
|
||||
Source-Files <advancedgameplay/sourcefiles>
|
||||
Intelligence <advancedgameplay/intelligence>
|
||||
Sleeves <advancedgameplay/sleeves>
|
63
doc/source/advancedgameplay/bitnodes.rst
Normal file
63
doc/source/advancedgameplay/bitnodes.rst
Normal file
@ -0,0 +1,63 @@
|
||||
.. _gameplay_bitnodes:
|
||||
|
||||
.. warning:: This page contains spoilers regarding the game's story/plot-line.
|
||||
|
||||
BitNodes
|
||||
========
|
||||
A BitNode is an important part of the game's storyline. In the game, you discover
|
||||
what BitNodes are by following the trail of clues left by the mysterious jump3r
|
||||
(essentially a minimal questline).
|
||||
|
||||
What is a BitNode
|
||||
^^^^^^^^^^^^^^^^^
|
||||
A BitNode is the complex simulated reality in which you reside. By following the messages
|
||||
from jump3r, you discover that humanity was enslaved by an advanced alien race, called
|
||||
the Enders, using virtual simulations that trapped the minds of humans.
|
||||
|
||||
However, the Enders didn't just create a single virtual reality to enslave humans, but many
|
||||
different simulations. In other words, there are many different BitNodes that exist.
|
||||
These BitNode are very different from each other.
|
||||
|
||||
jump3r tells you that the only hope for humanity is to destroy all of these BitNodes.
|
||||
Therefore, the end goal for the player is to enter and then destroy each BitNode at least once.
|
||||
|
||||
Destroying a BitNode resets most of the player's progress but grants the player a
|
||||
powerful second-tier persistent upgrade called a :ref:`Source-File <gameplay_sourcefiles>`.
|
||||
Different BitNodes grant different Source-Files.
|
||||
|
||||
Each BitNode has unique characteristics that are related to varying backstories. For example,
|
||||
in one BitNode the world is in the middle of a financial catastrophe with a collapsing
|
||||
market. In this BitNode, most forms of income such as working at a company or Hacknet
|
||||
Nodes are significantly less profitable. Servers have less money on them and lowered
|
||||
growth rates, but it is easier to lower their security level using the weaken() Netscript function.
|
||||
|
||||
Furthermore, some BitNodes introduce new content and mechanics. For example there is one
|
||||
BitNode that grants access to the :ref:`Netscript Singularity Functions <netscript_singularityfunctions>`.
|
||||
There is another BitNode in which you can manage a gang to earn money and reputation.
|
||||
|
||||
How to destroy a BitNode
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Initially, the only way to destroy a BitNode is to join the Daedalus :ref:`Daedalus <gameplay_factions>`.
|
||||
From Daedalus, the player can obtain an Augmentation called 'The Red Pill', which doesn't cost any money
|
||||
but does require a good amount of faction reputation.
|
||||
|
||||
After installing 'The Red Pill', the player must search for and then manually hack a
|
||||
server called 'w0r1d_d43m0n'. This server requires a hacking level of 3000 in order
|
||||
to successfully hack it. This will destroy the player's current BitNode.
|
||||
|
||||
There is a second method of destroying a BitNode, but it must be unlocked by first
|
||||
destroying BitNode-6 or BitNode-7 (Bladeburners).
|
||||
|
||||
.. todo:: Link to Bladeburner documentation page here
|
||||
|
||||
When the player destroys a BitNode, most of his/her progress will be reset. This includes things
|
||||
such as Augmentations and RAM upgrades on the home computer. The only things that will persist
|
||||
through destroying BitNodes is:
|
||||
|
||||
* Source-Files
|
||||
* Scripts on the home computer
|
||||
|
||||
BitNode Details
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
TODO
|
21
doc/source/advancedgameplay/intelligence.rst
Normal file
21
doc/source/advancedgameplay/intelligence.rst
Normal file
@ -0,0 +1,21 @@
|
||||
.. _gameplay_intelligence:
|
||||
|
||||
Intelligence
|
||||
============
|
||||
Intelligence is a :ref:`stat <gameplay_stats>` that is unlocked by having
|
||||
:ref:`Source-File 5 <gameplay_sourcefiles>` (i.e. Destroying BitNode-5).
|
||||
|
||||
Intelligence is unique because it is permanent and persistent. It never gets reset
|
||||
back to 1. However, gaining Intelligence experience is extremely slow. The methods
|
||||
of gaining Intelligence exp is also hidden. You won't know when you gain
|
||||
experience and how much. It is a stat that gradually builds up as you continue
|
||||
to play the game.
|
||||
|
||||
Intelligence will boost your production for many actions in the game, including:
|
||||
|
||||
* Hacking
|
||||
* Infiltration
|
||||
* Hacking Missions
|
||||
* Crime success rate
|
||||
* Bladeburner
|
||||
* Reputation gain for companies & factions
|
67
doc/source/advancedgameplay/sleeves.rst
Normal file
67
doc/source/advancedgameplay/sleeves.rst
Normal file
@ -0,0 +1,67 @@
|
||||
.. _gameplay_sleeves:
|
||||
|
||||
Sleeves
|
||||
=======
|
||||
When VitaLife unveiled their Persona Core technology that allowed people to digitize
|
||||
and transfer their consciousness into other vessels, human bodies became nothing more
|
||||
than 'sleeves' for the human consciousness. This technology thus became known as
|
||||
"Sleeve technology".
|
||||
|
||||
Sleeve technology unlocks two different gameplay features:
|
||||
|
||||
* Duplicate Sleeves
|
||||
* Re-sleeving
|
||||
|
||||
Sleeve technology is unlocked in :ref:`BitNode-10 <gameplay_bitnodes>`.
|
||||
|
||||
Duplicate Sleeves
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Duplicate Sleeves are MK-V Synthoids (synthetic androids) into which your consciuosness
|
||||
has been copied. In other words, these Synthoids contain a perfect duplicate of your mind.
|
||||
|
||||
Duplicate Sleeves are essentially clones which you can use to perform work-type actions,
|
||||
such as working for a company/faction or committing a crime. When sleeves perform these tasks,
|
||||
they will earn money, experience, and reputation.
|
||||
|
||||
Sleeves are their own individuals, which means they each have their own experience and stats.
|
||||
|
||||
When a sleeve earns experience, it earns experience for itself, the player's
|
||||
original consciousness, as well as all of the player's other sleeves.
|
||||
|
||||
Synchronization
|
||||
~~~~~~~~~~~~~~~
|
||||
Synchronization is a measure of how aligned your consciousness is with that of your
|
||||
Duplicate Sleeves. It is a numeral value between 1 and 100, and it affects how much experience
|
||||
is earned when the sleeve is performing a task.
|
||||
|
||||
Let N be the sleeve's synchronization. When the sleeve earns experience by performing
|
||||
a task, both the sleeve and the player's original host consciousness of N% of the
|
||||
amount of experience normally earned by the task. All of the player's other sleeves
|
||||
earn ((N/100)^2 * 100)% of the experience.
|
||||
|
||||
Synchronization can be increased by assigning sleeves to the 'Synchronize' task.
|
||||
|
||||
Sleeve Shock
|
||||
~~~~~~~~~~~~
|
||||
Sleeve shock is a measure of how much trauma the sleeve has due to being placed in a new
|
||||
body. It is a numeral value between 0 and 99, where 99 indicates full shock and 0 indicates
|
||||
no shock. Shock affects the amount of experience earned by the sleeve.
|
||||
|
||||
Sleeve shock slowly decreases over time. You can further increase the rate at which
|
||||
it decreases by assigning sleeves to the 'Shock Recovery' task.
|
||||
|
||||
Re-sleeving
|
||||
^^^^^^^^^^^
|
||||
Re-sleeving is the process of digitizing and transferring your consciousness into a
|
||||
new human body, or "sleeve". When you re-sleeve into a new body, your stat experience
|
||||
and Augmentations get replaced with those of the new body.
|
||||
|
||||
In order to re-sleeve, you must purchase new bodies. This can be done at VitaLife in
|
||||
New Tokyo. Once you purchase a body to re-sleeve into, the effects will take
|
||||
place immediately.
|
||||
|
||||
Note that resleeving **REMOVES** all of your currently-installed Augmentations,
|
||||
and replaces them with the ones provided by the purchased sleeve. However,
|
||||
Augmentations that are purchased but not installed will **not** be removed. If you have purchased
|
||||
an Augmentation and then re-sleeve into a body which already has that Augmentation,
|
||||
it will be removed since you cannot have duplicate Augmentations.
|
86
doc/source/advancedgameplay/sourcefiles.rst
Normal file
86
doc/source/advancedgameplay/sourcefiles.rst
Normal file
@ -0,0 +1,86 @@
|
||||
.. _gameplay_sourcefiles:
|
||||
|
||||
.. warning:: This page contains spoilers regarding the game's story/plot-line.
|
||||
|
||||
Source-Files
|
||||
============
|
||||
Source-Files are a type of persistent upgrade that are more powerful than Augmentations.
|
||||
Source-Files are received by destroying a BitNode. There are many different BitNodes
|
||||
in the game and each BitNode will grant a different Source-File when it is destroyed.
|
||||
|
||||
A Source-File can be upgraded by destroying its corresponding BitNode a second or
|
||||
third time (AKA playing through that BitNode again). It can be upgraded to a maximum
|
||||
of level 3.
|
||||
|
||||
List of all Source-Files
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-1: Source Genesis | * Lets the player start with 32 GB of RAM on home computer |
|
||||
| | * Increases all of the player's multipliers by 16%/24%/28% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-2: Rise of the Underworld | * Increases the player's crime success rate, crime money, and |
|
||||
| | charisma multipliers by 24%/36%/42% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-3: Corporatocracy | * Lets the player create Corporations in other BitNodes (although some |
|
||||
| | BitNodes will disable this mechanic) |
|
||||
| | * Increases the player's charisma and company salary multipliers by 8%/12%/14% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-4: The Singularity | * Lets the player access and use Netscript Singularity Functions in other BitNodes. |
|
||||
| | * Each level of this Source-File opens up more of the Singularity Functions to use |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-5: Artificial Intelligence | * Unlocks :ref:`gameplay_intelligence` |
|
||||
| | * Unlocks getBitNodeMultipliers() Netscript function |
|
||||
| | * Increases all of the player's hacking-related multipliers by 8%/12%/14% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-6: Bladeburners | * Unlocks the Bladeburner feature in other BitNodes |
|
||||
| | * Increases all of the player's level and experience gain rate multipliers for |
|
||||
| | combat stats by 8%/12%/14% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-7: Bladeburners 2079 | * Allows the player to access the :ref:`netscript_bladeburnerapi` in other BitNodes |
|
||||
| | * Increases all of the player's Bladeburner multipliers by 8%/12%/14% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-8: Ghost of Wall Street | * Increases the player's hacking growth multiplier by 12%/18%/21% |
|
||||
| | * Level 1 grants permanent access to :ref:`WSE <gameplay_stock_market>` and |
|
||||
| | :ref:`TIX API <netscript_tixapi>` |
|
||||
| | * Level 2 grants permanent access to shorting stocks |
|
||||
| | * Level 3 grants permanent access to use limit/stop orders |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-9: Coming Soon | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-10: Digital Carbon | * Each level of this grants a Duplicate Sleeve |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-11: The Big Crash | * Company favor increases both the player's salary and reputation gain at that |
|
||||
| | company by 1% per favor (rather than just the reputation gain) |
|
||||
| | * Increases the player's company salary and reputation gain multipliers by |
|
||||
| | 24%/36%/42% |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| BitNode-12: The Recursion | * There is no maximum level for this Source-File |
|
||||
| | * Each level of this Source-File increases all of the player's multipliers by 1%. |
|
||||
| | * This affect is multiplicative with itself. This means that level N of this |
|
||||
| | Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers |
|
||||
| | that decrease) |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
||||
| | |
|
||||
+------------------------------------+-------------------------------------------------------------------------------------+
|
@ -2,7 +2,7 @@
|
||||
|
||||
Companies
|
||||
=========
|
||||
When exploring the :ref:`world <World>`, you can visit various companies. At
|
||||
When exploring the :ref:`world <gameplay_world>`, you can visit various companies. At
|
||||
these companies, you can apply for jobs.
|
||||
|
||||
Working a job lets you earn money, experience, and reputation with that company.
|
||||
|
@ -4,7 +4,7 @@ Crimes
|
||||
======
|
||||
Commiting crimes is an active gameplay mechanic that allows the player to train
|
||||
their stats and potentially earn money. The player can attempt to commit crimes
|
||||
by visiting 'The Slums' through the 'City' tab (:ref:`Keyboard shortcut <_shortcuts>` Alt + w).
|
||||
by visiting 'The Slums' through the 'City' tab (:ref:`Keyboard shortcut <shortcuts>` Alt + w).
|
||||
'The Slums' is available in every city.
|
||||
|
||||
|
||||
|
@ -110,7 +110,7 @@ List of Factions and their Requirements
|
||||
| | Clarke | * Have 200k reputation with | |
|
||||
| | Incorporated | the Corporation | |
|
||||
+ +----------------+-----------------------------------------+-------------------------------+
|
||||
| | Fulcrum Secret | * Have 200k reputation with | |
|
||||
| | Fulcrum Secret | * Have 250k reputation with | |
|
||||
| | Technologies | the Corporation | |
|
||||
| | | * Hack fulcrumassets manually | |
|
||||
+---------------------+----------------+-----------------------------------------+-------------------------------+
|
||||
|
@ -3,6 +3,30 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v0.43.0 - 2/4/2019
|
||||
------------------
|
||||
|
||||
* Added BitNode-10: Digital Carbon
|
||||
|
||||
* Stock Market Changes:
|
||||
* Each stock now has a maximum number of shares you can purchase (both Long and Short positions combined)
|
||||
* Added getStockMaxShares() Netscript function to the TIX API
|
||||
* The cost of 4S Market Data TIX API Access increased from $20b to $25b
|
||||
|
||||
* Job Changes:
|
||||
* You can now hold multiple jobs at once. This means you no longer lose reputation when leaving a company
|
||||
* Because of this change, the getCharacterInformation() Netscript function returns a slightly different value
|
||||
|
||||
* Script Editor Changes:
|
||||
* Added new script editor: CodeMirror. You can choose between the old editor (Ace) or CodeMirror
|
||||
* Navigation keyboard shortcuts no longer work if the script editor is focused
|
||||
|
||||
* Trying to programmatically run a script (run(), exec()) with a 'threads' argument of 0 will now cause the function to return false without running the script
|
||||
* Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB)
|
||||
* The maximum amount, maximum RAM, and cost of purchasing servers can now vary between different BitNodes (new BitNode multipliers)
|
||||
* Pop-up dialog boxes are a little bit bigger
|
||||
* Bug Fix: When importing scripts, "./" will now be properly ignored (e.g. import { foo } from "./lib.script" )
|
||||
|
||||
v0.42.0 - 1/8/2019
|
||||
------------------
|
||||
|
||||
|
@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.0'
|
||||
version = '0.43'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '1.0'
|
||||
release = '0.43.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@ -92,7 +92,11 @@ todo_include_todos = True
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'agogo'
|
||||
#html_theme = 'agogo'
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_options = {
|
||||
"navigation_depth": 5,
|
||||
}
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
@ -178,7 +182,10 @@ texinfo_documents = [
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||
|
||||
|
||||
def setup(app):
|
||||
print("Initializing (setup())");
|
||||
app.add_stylesheet('maxwidthoverride.css')
|
||||
|
@ -21,7 +21,9 @@ secrets that you've been searching for.
|
||||
|
||||
Netscript <netscript>
|
||||
Basic Gameplay <basicgameplay>
|
||||
Advanced Gameplay <advancedgameplay>
|
||||
Keyboard Shortcuts <shortcuts>
|
||||
Script Editors <scripteditors>
|
||||
Game Frozen or Stuck? <gamefrozen>
|
||||
Changelog <changelog>
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
.. _netscript_bladeburnerapi:
|
||||
|
||||
Netscript Bladeburner API
|
||||
=========================
|
||||
Netscript provides the following API for interacting with the game's Bladeburner mechanic.
|
||||
|
@ -380,8 +380,10 @@ run
|
||||
Run a script as a separate process. This function can only be used to run scripts located on the current server (the server
|
||||
running the script that calls this function).
|
||||
|
||||
Returns true if the script is successfully started, and false otherwise. Requires a significant amount of RAM to run this
|
||||
command.
|
||||
Returns true if the script is successfully started, and false otherwise.
|
||||
|
||||
Running this function with a *numThreads* argument of 0 will return false without running the script.
|
||||
However, running this function with a negative *numThreads* argument will cause a runtime error.
|
||||
|
||||
The simplest way to use the *run* command is to call it with just the script name. The following example will run
|
||||
'foo.script' single-threaded with no arguments::
|
||||
@ -415,6 +417,9 @@ exec
|
||||
|
||||
Returns true if the script is successfully started, and false otherwise.
|
||||
|
||||
Running this function with a *numThreads* argument of 0 will return false without running the script.
|
||||
However, running this function with a negative *numThreads* argument will cause a runtime error.
|
||||
|
||||
The simplest way to use the *exec* command is to call it with just the script name and the target server.
|
||||
The following example will try to run *generic-hack.script* on the *foodnstuff* server::
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
.. _netscript_tixapi:
|
||||
|
||||
Netscript Trade Information eXchange (TIX) API
|
||||
==============================================
|
||||
|
||||
The Trade Information eXchange (TIX) is the communications protocol supported by the World Stock Exchange (WSE).
|
||||
The WSE provides an API that allows you to automatically communicate with the
|
||||
`Stock Market <http://bitburner.wikia.com/wiki/Stock_Market>`_. This API lets you write code using Netscript
|
||||
:ref:`Stock Market <gameplay_stock_market>`.
|
||||
This API lets you write code using Netscript
|
||||
to build automated trading systems and create your own algorithmic trading strategies. Access to this
|
||||
TIX API can be purchased by visiting the World Stock Exchange in-game.
|
||||
|
||||
@ -62,6 +65,19 @@ getStockPosition
|
||||
sharesShort = pos[2];
|
||||
avgPxShort = pos[3];
|
||||
|
||||
getStockMaxShares
|
||||
-----------------
|
||||
|
||||
|
||||
.. js:function:: getStockMaxShares(sym)
|
||||
|
||||
:param string sym: Stock symbol
|
||||
:RAM cost: 2 GB
|
||||
|
||||
Returns the maximum number of shares that the stock has. This is the maximum
|
||||
amount of the stock that can be purchased in both the Long and Short
|
||||
positions combined
|
||||
|
||||
buyStock
|
||||
--------
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
.. _netscript_singularityfunctions:
|
||||
|
||||
Netscript Singularity Functions
|
||||
===============================
|
||||
|
||||
@ -164,9 +166,9 @@ getCharacterInformation
|
||||
{
|
||||
bitnode: Current BitNode number
|
||||
city: Name of city you are currently in
|
||||
company: Name of company
|
||||
factions: Array of factions you are currently a member of
|
||||
jobTitle: Name of job
|
||||
jobs: Array of all companies at which you have jobs
|
||||
jobTitle: Array of job positions for all companies you are employed at. Same order as 'jobs'
|
||||
tor: Boolean indicating whether or not you have a tor router
|
||||
|
||||
// The following is an object with many of the player's multipliers from Augmentations/Source Files
|
||||
|
140
doc/source/scripteditors.rst
Normal file
140
doc/source/scripteditors.rst
Normal file
@ -0,0 +1,140 @@
|
||||
.. _scripteditors:
|
||||
|
||||
Script Editors
|
||||
==============
|
||||
Third-party libraries are used to implement the game's Script Editor(s). There are
|
||||
currently two options for the Script Editor:
|
||||
|
||||
* `Ace <https://ace.c9.io/>`_
|
||||
* `CodeMirror <https://codemirror.net/>`_
|
||||
|
||||
You can select which of the two editors you want to use on the Script Editor page
|
||||
('Create Script' on the main menu).
|
||||
|
||||
Ace was the game's original Script Editor, while CodeMirror was added later in
|
||||
v0.43.0. The two editors share many of the same features, so there is not a significant
|
||||
difference between the two. Currently, CodeMirror is slightly more modern,
|
||||
more customizable, and has a few quality-of-life improvements compared to Ace.
|
||||
|
||||
Universal Key Bindings
|
||||
----------------------
|
||||
These keyboard shortcuts are available in both the Ace and CodeMirror editors, regardless
|
||||
of what key binding option you are using:
|
||||
|
||||
============= ===========================================================================
|
||||
Shortcut Action
|
||||
============= ===========================================================================
|
||||
Ctrl + b Save script and return to :ref:`terminal`
|
||||
Ctrl + space Show Autocomplete Hints
|
||||
============= ===========================================================================
|
||||
|
||||
.. _scripteditor_linter:
|
||||
|
||||
Linter
|
||||
------
|
||||
Both script editors contain a linter, which is a tool that analyzes your
|
||||
code and flags anything it thinks might be an error. You can see
|
||||
warnings and errors from the linter on the left-hand side of the script editor. There
|
||||
will be an icon on whatever lines the linter thinks might be problematic. Hovering
|
||||
over the icon will display information on what the issue is.
|
||||
|
||||
Note that **just because the linter shows an error/warning, this does NOT automatically mean that**
|
||||
**your script is broken and will fail to run.** This is especially true if you are using
|
||||
:ref:`netscriptjs`. The linter used by the script editors isn't necessarily perfect or
|
||||
up-to-date. Furthermore, the linter does not affect anything when you actually run scripts.
|
||||
|
||||
Ace
|
||||
---
|
||||
The following documents what the different settings/options do for the Ace editor,
|
||||
as well as the different key binding settings. Note that the
|
||||
information for the key bindings may not be completely comprehensive. You'll
|
||||
have to dig into the editor source code if you want to learn more.
|
||||
|
||||
Settings
|
||||
~~~~~~~~
|
||||
|
||||
===================== ===========================================================================================================
|
||||
Setting Effect
|
||||
===================== ===========================================================================================================
|
||||
Theme Switch between different color schemes
|
||||
Key Binding Switch between different key binding options. This changes what keyboard shortcuts are available
|
||||
Highlight Active Line When enabled, the line on which the cursor currently resides will be highlighted.
|
||||
Show Invisibles When enabled, you will be able to view hidden whitespace characters such as spaces, tabs, and newlines.
|
||||
Use Soft Tab When enabled, tabs will be replaced with spaces
|
||||
Max Error Count Specifies the (approximate) number of lines that will be linted
|
||||
===================== ===========================================================================================================
|
||||
|
||||
Ace Key Bindings
|
||||
~~~~~~~~~~~~~~~~
|
||||
For Ace, the "Ace" Key Binding setting uses the default configuration. A list of these
|
||||
`can be found here <https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts>`_.
|
||||
|
||||
Vim Key Bindings
|
||||
~~~~~~~~~~~~~~~~
|
||||
For Ace, the "Vim" Key Binding setting configures the editor to use
|
||||
`Vim <https://en.wikipedia.org/wiki/Vim_(text_editor)>`_ key mappings. Note that while this tries
|
||||
to emulate Vim features as faithfully as possible, it is not a complete Vim implementation.
|
||||
|
||||
Since I'm not familiar with Vim, I'll leave
|
||||
`Ace's Vim Mode implementation here <https://github.com/ajaxorg/ace/blob/96088d0fc292daf0706b2d029cc03c3799be5974/lib/ace/keyboard/vim.js#L860>`_,
|
||||
which I believe shows most of the implemented features.
|
||||
|
||||
Note that the following Vim Ex commands will properly save the script and/or quit the editor in game:
|
||||
|
||||
======= ==============================================
|
||||
Command Effect
|
||||
======= ==============================================
|
||||
:w Save the script and return to :ref:`terminal`
|
||||
:q Return to :ref:`terminal` **WITHOUT** saving
|
||||
:x Save the script and return to :ref:`terminal`
|
||||
:wq Save the script and return to :ref:`terminal`
|
||||
======= ==============================================
|
||||
|
||||
Emacs Key Bindings
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
For Ace, the "Emacs" Key Binding setting configures the editor to use
|
||||
`Emacs <https://en.wikipedia.org/wiki/Emacs>`_ key mappings. Note that while this tries
|
||||
to emulate the Emacs key mappings as faithfully as possible, it won't necessarily be a
|
||||
complete implementation.
|
||||
|
||||
Since I'm not familiar with Emacs, I'll leave
|
||||
`Ace's Emacs Mode implementation here <https://github.com/ajaxorg/ace/blob/96088d0fc292daf0706b2d029cc03c3799be5974/lib/ace/keyboard/emacs.js#L343>`_,
|
||||
which I believe shows most of the implemented features.
|
||||
|
||||
CodeMirror
|
||||
----------
|
||||
The following documents what the different settings/options do for the CodeMirror editor,
|
||||
as well as the shortcuts for the different key binding settings. Note that the
|
||||
information for the key bindings may not be completely comprehensive. You'll
|
||||
have to dig into the editor source code if you want to learn everything.
|
||||
|
||||
Settings
|
||||
~~~~~~~~
|
||||
========================== ===========================================================================================================
|
||||
Setting Effect
|
||||
========================== ===========================================================================================================
|
||||
Theme Switch between different color schemes
|
||||
Key Binding Switch between different key binding options. This changes what keyboard shortcuts are available
|
||||
Highlight Active Line When enabled, the line on which the cursor currently resides will be highlighted.
|
||||
Show Invisibles When enabled, you will be able to view hidden whitespace characters such as spaces, tabs, and newlines.
|
||||
Use Soft Tab When enabled, tabs will be replaced with spaces
|
||||
Auto-Close Brackets/Quotes When enabled, any opening brackets or quotes that are typed will be closed
|
||||
Enable Linting Enable/Disable the :ref:`scripteditor_linter`
|
||||
Continue Comments When enabled, pressing 'Enter' inside a comment block will continue the comment on the next line
|
||||
========================== ===========================================================================================================
|
||||
|
||||
Default Key Bindings
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
.. todo:: Fill out
|
||||
|
||||
Sublime Key Bindings
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
.. todo:: Fill out
|
||||
|
||||
Vim Key Bindings
|
||||
~~~~~~~~~~~~~~~~
|
||||
.. todo:: Fill out
|
||||
|
||||
Emacs Key Bindings
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
.. todo:: Fill out
|
@ -18,7 +18,7 @@ These shortcuts are almost always available. Exceptions include:
|
||||
========== ===========================================================================
|
||||
Shortcut Action
|
||||
========== ===========================================================================
|
||||
Alt + t Switch to :doc:`terminal`
|
||||
Alt + t Switch to :ref:`terminal`
|
||||
Alt + c Switch to 'Stats' page
|
||||
Alt + e Switch to Script Editor. Will open up the last-edited file or a new file
|
||||
Alt + s Switch to 'Active Scripts' page
|
||||
@ -35,24 +35,11 @@ Alt + o Switch to 'Options' page
|
||||
|
||||
Script Editor
|
||||
-------------
|
||||
These shortcuts are available only in the Script Editor
|
||||
|
||||
============= ===========================================================================
|
||||
Shortcut Action
|
||||
============= ===========================================================================
|
||||
Ctrl + b Save script and return to :doc:`terminal`
|
||||
Ctrl + space Function autocompletion
|
||||
============= ===========================================================================
|
||||
|
||||
In the Script Editor you can configure your key binding mode to three preset options:
|
||||
|
||||
* `Ace <https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts>`_
|
||||
* Vim
|
||||
* Emacs
|
||||
See the :ref:`Script Editor <scripteditors>` documentation for more details.
|
||||
|
||||
Terminal Shortcuts
|
||||
------------------
|
||||
These shortcuts are available only in the :doc:`terminal`
|
||||
These shortcuts are available only in the :ref:`terminal`
|
||||
|
||||
============= ===========================================================================
|
||||
Shortcut Action
|
||||
@ -66,7 +53,7 @@ Tab Autocomplete command
|
||||
Terminal Bash Shortcuts
|
||||
-----------------------
|
||||
These shortcuts were implemented to better emulate a bash shell. They must be enabled
|
||||
in your :doc:`terminal`'s *.fconf* file. This can be done be entering the Terminal command::
|
||||
in your :ref:`terminal`'s *.fconf* file. This can be done be entering the Terminal command::
|
||||
|
||||
nano .fconf
|
||||
|
||||
@ -92,10 +79,13 @@ Alt + f Move cursor to next word
|
||||
Ctrl + h/d Delete previous character ('Backspace')
|
||||
============= ===========================================================================
|
||||
|
||||
Misc Shortcuts
|
||||
--------------
|
||||
Popup/Dialog Box Shortcuts
|
||||
--------------------------
|
||||
The following shortcuts work if there are any popup or dialog boxes on the screen.
|
||||
|
||||
============= ===========================================================================
|
||||
Shortcut Action
|
||||
============= ===========================================================================
|
||||
Esc Close a script's log window
|
||||
Esc Close the current popup, cancelling any prompts on a dialog box
|
||||
Enter Clicks the "Yes/Confirm" option for every dialog box
|
||||
============= ===========================================================================
|
||||
|
3
doc/source/ystatic/maxwidthoverride.css
Normal file
3
doc/source/ystatic/maxwidthoverride.css
Normal file
@ -0,0 +1,3 @@
|
||||
.wy-nav-content {
|
||||
max-width: none;
|
||||
}
|
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
49
index.html
49
index.html
@ -65,6 +65,9 @@
|
||||
<li id="hacknet-nodes-tab" class="mainmenu-accordion-panel">
|
||||
<button id="hacknet-nodes-menu-link"> Hacknet Nodes </button>
|
||||
</li>
|
||||
<li id="sleeves-tab" class="mainmenu-accordion-panel">
|
||||
<button id="sleeves-menu-link"> Sleeves </button>
|
||||
</li>
|
||||
|
||||
<!-- World dropdown -->
|
||||
<li id="world-menu-header-li">
|
||||
@ -115,7 +118,11 @@
|
||||
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1"/>
|
||||
</div>
|
||||
|
||||
<div id="javascript-editor"></div>
|
||||
<div id="ace-editor"></div>
|
||||
<form id="codemirror-form-wrapper"><textarea id="codemirror-editor"></textarea></form>
|
||||
<div id="codemirror-vim-command-display-wrapper">
|
||||
Key Buffer: <span id="codemirror-vim-command-display"></span>
|
||||
</div>
|
||||
|
||||
<div id="script-editor-buttons-wrapper"></div> <!-- Buttons below script editor -->
|
||||
</div> <!-- End wrapper -->
|
||||
@ -123,26 +130,21 @@
|
||||
<div id="script-editor-options-panel">
|
||||
<h1 style="color:white;"> Script Editor Options </h1>
|
||||
<fieldset>
|
||||
<label for="script-editor-option-theme">Theme</label>
|
||||
<select id="script-editor-option-theme">
|
||||
<option value="Chaos">Chaos</option>
|
||||
<option value="Chrome">Chrome</option>
|
||||
<option value="Monokai">Monokai</option>
|
||||
<option value="Solarized_Dark">Solarized Dark</option>
|
||||
<option value="Solarized_Light">Solarized Light</option>
|
||||
<option value="Terminal">Terminal</option>
|
||||
<option value="Twilight">Twilight</option>
|
||||
<option value="XCode">XCode</option>
|
||||
<label for="script-editor-option-editor">Editor</label>
|
||||
<select id="script-editor-option-editor">
|
||||
<option value="Ace">Ace</option>
|
||||
<option value="CodeMirror">CodeMirror</option>
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="script-editor-option-theme">Theme</label>
|
||||
<select id="script-editor-option-theme"></select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="script-editor-option-keybinding">Key Binding</label>
|
||||
<select id="script-editor-option-keybinding">
|
||||
<option value="ace">Ace</option>
|
||||
<option value="vim">Vim</option>
|
||||
<option value="emacs">Emacs</option>
|
||||
</select>
|
||||
<select id="script-editor-option-keybinding"></select>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
@ -160,11 +162,11 @@
|
||||
<input type="checkbox" name="script-editor-option-usesofttab" id="script-editor-option-usesofttab" checked>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
|
||||
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr"/>
|
||||
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
|
||||
</fieldset>
|
||||
<fieldset id="script-editor-option-flex1-fieldset"></fieldset>
|
||||
<fieldset id="script-editor-option-flex2-fieldset"></fieldset>
|
||||
<fieldset id="script-editor-option-flex3-fieldset"></fieldset>
|
||||
<fieldset id="script-editor-option-flex4-fieldset"></fieldset>
|
||||
|
||||
</div> <!-- End script editor options panel -->
|
||||
</div>
|
||||
|
||||
@ -594,8 +596,11 @@
|
||||
<!-- City Hall -->
|
||||
<a id="location-cityhall-create-corporation" class="a-link-button">Create a Corporation</a>
|
||||
|
||||
<!-- Bladeburner@NSA -->
|
||||
<!-- Bladeburner @ NSA -->
|
||||
<a id="location-nsa-bladeburner" class="a-link-button">Bladeburner Division</a>
|
||||
|
||||
<!-- Re-sleeving @ VitaLife -->
|
||||
<a id="location-vitalife-resleeve" class="a-link-button">Re-Sleeve</a>
|
||||
</div>
|
||||
|
||||
<div id="infiltration-container" class="generic-menupage-container">
|
||||
|
1138
package-lock.json
generated
1138
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@
|
||||
"autosize": "^4.0.2",
|
||||
"bluebird": "^3.5.1",
|
||||
"brace": "^0.11.1",
|
||||
"codemirror": "^5.43.0",
|
||||
"decimal.js": "7.2.3",
|
||||
"enhanced-resolve": "^4.0.0",
|
||||
"escodegen": "^1.11.0",
|
||||
@ -22,6 +23,7 @@
|
||||
"file-saver": "^1.3.8",
|
||||
"interpret": "^1.0.0",
|
||||
"jquery": "^3.3.1",
|
||||
"jshint": "^2.9.7",
|
||||
"json-loader": "^0.5.4",
|
||||
"jsplumb": "^2.6.8",
|
||||
"jszip": "^3.1.5",
|
||||
@ -58,10 +60,9 @@
|
||||
"lodash": "^4.17.10",
|
||||
"mini-css-extract-plugin": "^0.4.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^3.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-lcov-reporter": "^1.0.0",
|
||||
"node-sass": "^4.9.2",
|
||||
"nsp": "^3.2.1",
|
||||
"node-sass": "^4.10.0",
|
||||
"raw-loader": "~0.5.0",
|
||||
"sass-loader": "^7.0.3",
|
||||
"script-loader": "~0.7.0",
|
||||
|
156
src/Augmentation/Augmentation.ts
Normal file
156
src/Augmentation/Augmentation.ts
Normal file
@ -0,0 +1,156 @@
|
||||
// Class definition for a single Augmentation object
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
interface IConstructorParams {
|
||||
info: string;
|
||||
moneyCost: number;
|
||||
name: string;
|
||||
prereqs?: string[];
|
||||
repCost: number;
|
||||
|
||||
hacking_mult?: number;
|
||||
strength_mult?: number;
|
||||
defense_mult?: number;
|
||||
dexterity_mult?: number;
|
||||
agility_mult?: number;
|
||||
charisma_mult?: number;
|
||||
hacking_exp_mult?: number;
|
||||
strength_exp_mult?: number;
|
||||
defense_exp_mult?: number;
|
||||
dexterity_exp_mult?: number;
|
||||
agility_exp_mult?: number;
|
||||
charisma_exp_mult?: number;
|
||||
hacking_chance_mult?: number;
|
||||
hacking_speed_mult?: number;
|
||||
hacking_money_mult?: number;
|
||||
hacking_grow_mult?: number;
|
||||
company_rep_mult?: number;
|
||||
faction_rep_mult?: number;
|
||||
crime_money_mult?: number;
|
||||
crime_success_mult?: number;
|
||||
work_money_mult?: number;
|
||||
hacknet_node_money_mult?: number;
|
||||
hacknet_node_purchase_cost_mult?: number;
|
||||
hacknet_node_ram_cost_mult?: number;
|
||||
hacknet_node_core_cost_mult?: number;
|
||||
hacknet_node_level_cost_mult?: number;
|
||||
bladeburner_max_stamina_mult?: number;
|
||||
bladeburner_stamina_gain_mult?: number;
|
||||
bladeburner_analysis_mult?: number;
|
||||
bladeburner_success_chance_mult?: number;
|
||||
}
|
||||
|
||||
export class Augmentation {
|
||||
// Initiatizes a Augmentation object from a JSON save state.
|
||||
static fromJSON(value: any): Augmentation {
|
||||
return Generic_fromJSON(Augmentation, value.data);
|
||||
}
|
||||
|
||||
// How much money this costs to buy
|
||||
baseCost: number = 0;
|
||||
|
||||
// How much faction reputation is required to unlock this
|
||||
baseRepRequirement: number = 0;
|
||||
|
||||
// Description of what this Aug is and what it does
|
||||
info: string = "";
|
||||
|
||||
// Augmentation level - for repeatable Augs like NeuroFlux Governor
|
||||
level: number = 0;
|
||||
|
||||
// Name of Augmentation
|
||||
name: string = "";
|
||||
|
||||
// Whether the player owns this Augmentation
|
||||
owned: boolean = false;
|
||||
|
||||
// Array of names of all prerequisites
|
||||
prereqs: string[] = [];
|
||||
|
||||
// Multipliers given by this Augmentation. Must match the property name in
|
||||
// The Player/Person classes
|
||||
mults: IMap<number> = {}
|
||||
|
||||
constructor(params: IConstructorParams={ info: "", moneyCost: 0, name: "", repCost: 0 }) {
|
||||
this.name = params.name;
|
||||
this.info = params.info;
|
||||
this.prereqs = params.prereqs ? params.prereqs : [];
|
||||
|
||||
this.baseRepRequirement = params.repCost * CONSTANTS.AugmentationRepMultiplier * BitNodeMultipliers.AugmentationRepCost;
|
||||
this.baseCost = params.moneyCost * CONSTANTS.AugmentationCostMultiplier * BitNodeMultipliers.AugmentationMoneyCost;
|
||||
|
||||
this.level = 0;
|
||||
|
||||
// Set multipliers
|
||||
if (params.hacking_mult) { this.mults.hacking_mult = params.hacking_mult; }
|
||||
if (params.strength_mult) { this.mults.strength_mult = params.strength_mult; }
|
||||
if (params.defense_mult) { this.mults.defense_mult = params.defense_mult; }
|
||||
if (params.dexterity_mult) { this.mults.dexterity_mult = params.dexterity_mult; }
|
||||
if (params.agility_mult) { this.mults.agility_mult = params.agility_mult; }
|
||||
if (params.charisma_mult) { this.mults.charisma_mult = params.charisma_mult; }
|
||||
if (params.hacking_exp_mult) { this.mults.hacking_exp_mult = params.hacking_exp_mult; }
|
||||
if (params.strength_exp_mult) { this.mults.strength_exp_mult = params.strength_exp_mult; }
|
||||
if (params.defense_exp_mult) { this.mults.defense_exp_mult = params.defense_exp_mult; }
|
||||
if (params.dexterity_exp_mult) { this.mults.dexterity_exp_mult = params.dexterity_exp_mult; }
|
||||
if (params.agility_exp_mult) { this.mults.agility_exp_mult = params.agility_exp_mult; }
|
||||
if (params.charisma_exp_mult) { this.mults.charisma_exp_mult = params.charisma_exp_mult; }
|
||||
if (params.hacking_chance_mult) { this.mults.hacking_chance_mult = params.hacking_chance_mult; }
|
||||
if (params.hacking_speed_mult) { this.mults.hacking_speed_mult = params.hacking_speed_mult; }
|
||||
if (params.hacking_money_mult) { this.mults.hacking_money_mult = params.hacking_money_mult; }
|
||||
if (params.hacking_grow_mult) { this.mults.hacking_grow_mult = params.hacking_grow_mult; }
|
||||
if (params.company_rep_mult) { this.mults.company_rep_mult = params.company_rep_mult; }
|
||||
if (params.faction_rep_mult) { this.mults.faction_rep_mult = params.faction_rep_mult; }
|
||||
if (params.crime_money_mult) { this.mults.crime_money_mult = params.crime_money_mult; }
|
||||
if (params.crime_success_mult) { this.mults.crime_success_mult = params.crime_success_mult; }
|
||||
if (params.work_money_mult) { this.mults.work_money_mult = params.work_money_mult; }
|
||||
if (params.hacknet_node_money_mult) { this.mults.hacknet_node_money_mult = params.hacknet_node_money_mult; }
|
||||
if (params.hacknet_node_purchase_cost_mult) { this.mults.hacknet_node_purchase_cost_mult = params.hacknet_node_purchase_cost_mult; }
|
||||
if (params.hacknet_node_ram_cost_mult) { this.mults.hacknet_node_ram_cost_mult = params.hacknet_node_ram_cost_mult; }
|
||||
if (params.hacknet_node_core_cost_mult) { this.mults.hacknet_node_core_cost_mult = params.hacknet_node_core_cost_mult; }
|
||||
if (params.hacknet_node_level_cost_mult) { this.mults.hacknet_node_level_cost_mult = params.hacknet_node_level_cost_mult; }
|
||||
if (params.bladeburner_max_stamina_mult) { this.mults.bladeburner_max_stamina_mult = params.bladeburner_max_stamina_mult; }
|
||||
if (params.bladeburner_stamina_gain_mult) { this.mults.bladeburner_stamina_gain_mult = params.bladeburner_stamina_gain_mult; }
|
||||
if (params.bladeburner_analysis_mult) { this.mults.bladeburner_analysis_mult = params.bladeburner_analysis_mult; }
|
||||
if (params.bladeburner_success_chance_mult) { this.mults.bladeburner_success_chance_mult = params.bladeburner_success_chance_mult; }
|
||||
}
|
||||
|
||||
// Adds this Augmentation to the specified Factions
|
||||
addToFactions(factionList: string[]): void {
|
||||
for (let i = 0; i < factionList.length; ++i) {
|
||||
const faction: Faction | null = Factions[factionList[i]];
|
||||
if (faction == null) {
|
||||
console.warn(`In Augmentation.addToFactions(), could not find faction with this name: ${factionList[i]}`);
|
||||
continue;
|
||||
}
|
||||
faction!.augmentations.push(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds this Augmentation to all Factions
|
||||
addToAllFactions(): void {
|
||||
for (const fac in Factions) {
|
||||
if (Factions.hasOwnProperty(fac)) {
|
||||
const facObj: Faction | null = Factions[fac];
|
||||
if (facObj == null) {
|
||||
console.warn(`Invalid Faction object in addToAllFactions(). Key value: ${fac}`);
|
||||
continue;
|
||||
}
|
||||
facObj!.augmentations.push(this.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the current object to a JSON save state.
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("Augmentation", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.Augmentation = Augmentation;
|
File diff suppressed because it is too large
Load Diff
4
src/Augmentation/Augmentations.ts
Normal file
4
src/Augmentation/Augmentations.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { Augmentation } from "./Augmentation";
|
||||
import { IMap } from "../types";
|
||||
|
||||
export let Augmentations: IMap<Augmentation> = {};
|
13
src/Augmentation/PlayerOwnedAugmentation.ts
Normal file
13
src/Augmentation/PlayerOwnedAugmentation.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export class PlayerOwnedAugmentation {
|
||||
level: number = 1;
|
||||
name: string = "";
|
||||
|
||||
constructor(name: string = "") {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPlayerOwnedAugmentation {
|
||||
level: number;
|
||||
name: string;
|
||||
}
|
114
src/Augmentation/data/AugmentationNames.ts
Normal file
114
src/Augmentation/data/AugmentationNames.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { IMap } from "../../types";
|
||||
|
||||
export let AugmentationNames: IMap<string> = {
|
||||
Targeting1: "Augmented Targeting I",
|
||||
Targeting2: "Augmented Targeting II",
|
||||
Targeting3: "Augmented Targeting III",
|
||||
SyntheticHeart: "Synthetic Heart",
|
||||
SynfibrilMuscle: "Synfibril Muscle",
|
||||
CombatRib1: "Combat Rib I",
|
||||
CombatRib2: "Combat Rib II",
|
||||
CombatRib3: "Combat Rib III",
|
||||
NanofiberWeave: "Nanofiber Weave",
|
||||
SubdermalArmor: "NEMEAN Subdermal Weave",
|
||||
WiredReflexes: "Wired Reflexes",
|
||||
GrapheneBoneLacings: "Graphene Bone Lacings",
|
||||
BionicSpine: "Bionic Spine",
|
||||
GrapheneBionicSpine: "Graphene Bionic Spine Upgrade",
|
||||
BionicLegs: "Bionic Legs",
|
||||
GrapheneBionicLegs: "Graphene Bionic Legs Upgrade",
|
||||
SpeechProcessor: "Speech Processor Implant",
|
||||
TITN41Injection: "TITN-41 Gene-Modification Injection",
|
||||
EnhancedSocialInteractionImplant: "Enhanced Social Interaction Implant",
|
||||
BitWire: "BitWire",
|
||||
ArtificialBioNeuralNetwork: "Artificial Bio-neural Network Implant",
|
||||
ArtificialSynapticPotentiation: "Artificial Synaptic Potentiation",
|
||||
EnhancedMyelinSheathing: "Enhanced Myelin Sheathing",
|
||||
SynapticEnhancement: "Synaptic Enhancement Implant",
|
||||
NeuralRetentionEnhancement: "Neural-Retention Enhancement",
|
||||
DataJack: "DataJack",
|
||||
ENM: "Embedded Netburner Module",
|
||||
ENMCore: "Embedded Netburner Module Core Implant",
|
||||
ENMCoreV2: "Embedded Netburner Module Core V2 Upgrade",
|
||||
ENMCoreV3: "Embedded Netburner Module Core V3 Upgrade",
|
||||
ENMAnalyzeEngine: "Embedded Netburner Module Analyze Engine",
|
||||
ENMDMA: "Embedded Netburner Module Direct Memory Access Upgrade",
|
||||
Neuralstimulator: "Neuralstimulator",
|
||||
NeuralAccelerator: "Neural Accelerator",
|
||||
CranialSignalProcessorsG1: "Cranial Signal Processors - Gen I",
|
||||
CranialSignalProcessorsG2: "Cranial Signal Processors - Gen II",
|
||||
CranialSignalProcessorsG3: "Cranial Signal Processors - Gen III",
|
||||
CranialSignalProcessorsG4: "Cranial Signal Processors - Gen IV",
|
||||
CranialSignalProcessorsG5: "Cranial Signal Processors - Gen V",
|
||||
NeuronalDensification: "Neuronal Densification",
|
||||
NuoptimalInjectorImplant: "Nuoptimal Nootropic Injector Implant",
|
||||
SpeechEnhancement: "Speech Enhancement",
|
||||
FocusWire: "FocusWire",
|
||||
PCDNI: "PC Direct-Neural Interface",
|
||||
PCDNIOptimizer: "PC Direct-Neural Interface Optimization Submodule",
|
||||
PCDNINeuralNetwork: "PC Direct-Neural Interface NeuroNet Injector",
|
||||
ADRPheromone1: "ADR-V1 Pheromone Gene",
|
||||
ADRPheromone2: "ADR-V2 Pheromone Gene",
|
||||
HacknetNodeCPUUpload: "Hacknet Node CPU Architecture Neural-Upload",
|
||||
HacknetNodeCacheUpload: "Hacknet Node Cache Architecture Neural-Upload",
|
||||
HacknetNodeNICUpload: "Hacknet Node NIC Architecture Neural-Upload",
|
||||
HacknetNodeKernelDNI: "Hacknet Node Kernel Direct-Neural Interface",
|
||||
HacknetNodeCoreDNI: "Hacknet Node Core Direct-Neural Interface",
|
||||
NeuroFluxGovernor: "NeuroFlux Governor",
|
||||
Neurotrainer1: "Neurotrainer I",
|
||||
Neurotrainer2: "Neurotrainer II",
|
||||
Neurotrainer3: "Neurotrainer III",
|
||||
Hypersight: "HyperSight Corneal Implant",
|
||||
LuminCloaking1: "LuminCloaking-V1 Skin Implant",
|
||||
LuminCloaking2: "LuminCloaking-V2 Skin Implant",
|
||||
HemoRecirculator: "HemoRecirculator",
|
||||
SmartSonar: "SmartSonar Implant",
|
||||
PowerRecirculator: "Power Recirculation Core",
|
||||
QLink: "QLink",
|
||||
TheRedPill: "The Red Pill",
|
||||
SPTN97: "SPTN-97 Gene Modification",
|
||||
HiveMind: "ECorp HVMind Implant",
|
||||
CordiARCReactor: "CordiARC Fusion Reactor",
|
||||
SmartJaw: "SmartJaw",
|
||||
Neotra: "Neotra",
|
||||
Xanipher: "Xanipher",
|
||||
nextSENS: "nextSENS Gene Modification",
|
||||
OmniTekInfoLoad: "OmniTek InfoLoad",
|
||||
PhotosyntheticCells: "Photosynthetic Cells",
|
||||
Neurolink: "BitRunners Neurolink",
|
||||
TheBlackHand: "The Black Hand",
|
||||
CRTX42AA: "CRTX42-AA Gene Modification",
|
||||
Neuregen: "Neuregen Gene Modification",
|
||||
CashRoot: "CashRoot Starter Kit",
|
||||
NutriGen: "NutriGen Implant",
|
||||
INFRARet: "INFRARET Enhancement",
|
||||
DermaForce: "DermaForce Particle Barrier",
|
||||
GrapheneBrachiBlades: "Graphene BranchiBlades Upgrade",
|
||||
GrapheneBionicArms: "Graphene Bionic Arms Upgrade",
|
||||
BrachiBlades: "BrachiBlades",
|
||||
BionicArms: "Bionic Arms",
|
||||
SNA: "Social Negotiation Assistant (S.N.A)",
|
||||
EsperEyewear: "EsperTech Bladeburner Eyewear",
|
||||
EMS4Recombination: "EMS-4 Recombination",
|
||||
OrionShoulder: "ORION-MKIV Shoulder",
|
||||
HyperionV1: "Hyperion Plasma Cannon V1",
|
||||
HyperionV2: "Hyperion Plasma Cannon V2",
|
||||
GolemSerum: "GOLEM Serum",
|
||||
VangelisVirus: "Vangelis Virus",
|
||||
VangelisVirus3: "Vangelis Virus 3.0",
|
||||
INTERLINKED: "I.N.T.E.R.L.I.N.K.E.D",
|
||||
BladeRunner: "Blade's Runners",
|
||||
BladeArmor: "BLADE-51b Tesla Armor",
|
||||
BladeArmorPowerCells: "BLADE-51b Tesla Armor: Power Cells Upgrade",
|
||||
BladeArmorEnergyShielding: "BLADE-51b Tesla Armor: Energy Shielding Upgrade",
|
||||
BladeArmorUnibeam: "BLADE-51b Tesla Armor: Unibeam Upgrade",
|
||||
BladeArmorOmnibeam: "BLADE-51b Tesla Armor: Omnibeam Upgrade",
|
||||
BladeArmorIPU: "BLADE-51b Tesla Armor: IPU Upgrade",
|
||||
BladesSimulacrum: "The Blade's Simulacrum",
|
||||
|
||||
//Wasteland Augs
|
||||
//PepBoy: "P.E.P-Boy", Plasma Energy Projection System
|
||||
//PepBoyForceField Generates plasma force fields
|
||||
//PepBoyBlasts Generate high density plasma concussive blasts
|
||||
//PepBoyDataStorage STore more data on pep boy,
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import {BitNodeMultipliers} from "./BitNodeMultipliers";
|
||||
import {Player} from "./Player";
|
||||
import { BitNodeMultipliers } from "./BitNodeMultipliers";
|
||||
import { Player } from "../Player";
|
||||
|
||||
function BitNode(n, name, desc="", info="") {
|
||||
this.number = n;
|
||||
@ -56,7 +56,6 @@ function initBitNodes() {
|
||||
"The price and reputation cost of all Augmentations is tripled<br>" +
|
||||
"The starting and maximum amount of money on servers is reduced by 75%<br>" +
|
||||
"Server growth rate is reduced by 80%<br>" +
|
||||
"You will start out with $150b so that you can start your corporation<br>" +
|
||||
"You now only need 75 favour with a faction in order to donate to it, rather than 150<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 3, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File lets you create corporations on other BitNodes (although " +
|
||||
@ -157,7 +156,22 @@ function initBitNodes() {
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
BitNodes["BitNode9"] = new BitNode(9, "Do Androids Dream?", "COMING SOON");
|
||||
BitNodes["BitNode10"] = new BitNode(10, "MegaCorp", "COMING SOON"); //Not sure yet
|
||||
BitNodes["BitNode10"] = new BitNode(10, "Digital Carbon", "Your body is not who you are",
|
||||
"In 2084, VitaLife unveiled to the world the Persona Core, a technology that allowed people " +
|
||||
"to digitize their consciousness. Their consciousness could then be transferred into Synthoids " +
|
||||
"or other bodies by trasmitting the digitized data. Human bodies became nothing more than 'sleeves' for the " +
|
||||
"human consciousness. Mankind had finally achieved immortality - at least for those that could afford it.<br><br>" +
|
||||
"This BitNode unlocks Sleeve technology. Sleeve technology allows you to:<br><br>" +
|
||||
"1. Re-sleeve: Purchase and transfer your consciousness into a new body<br>" +
|
||||
"2. Duplicate Sleeves: Duplicate your consciousness into Synthoids, allowing you to perform different tasks synchronously<br><br>" +
|
||||
"In this BitNode:<br><br>" +
|
||||
"Your stats are significantly decreased.<br>" +
|
||||
"All methods of gaining money are half as profitable (except Stock Market)<br>" +
|
||||
"Purchased servers are more expensive, have less max RAM, and a lower maximum limit<br>" +
|
||||
"Augmentations are 5x as expensive and require twice as much reputation<br><br>" +
|
||||
"Destroying this BitNode will give you Source-File 10, or if you already have this Source-File it will " +
|
||||
"upgrade its level up to a maximum of 3. This Source-File unlocks Sleeve technology in other BitNodes. " +
|
||||
"Each level of this Source-File also grants you a Duplicate Sleeve");
|
||||
BitNodes["BitNode11"] = new BitNode(11, "The Big Crash", "Okay. Sell it all.",
|
||||
"The 2050s was defined by the massive amounts of violent civil unrest and anarchic rebellion that rose all around the world. It was this period " +
|
||||
"of disorder that eventually lead to the governmental reformation of many global superpowers, most notably " +
|
||||
@ -306,6 +320,27 @@ function initBitNodeMultipliers() {
|
||||
BitNodeMultipliers.CorporationValuation = 0;
|
||||
BitNodeMultipliers.CodingContractMoney = 0;
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
BitNodeMultipliers.HackingLevelMultiplier = 0.2;
|
||||
BitNodeMultipliers.StrengthLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.DefenseLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.DexterityLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.AgilityLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.CharismaLevelMultiplier = 0.4;
|
||||
BitNodeMultipliers.CompanyWorkMoney = 0.5;
|
||||
BitNodeMultipliers.CrimeMoney = 0.5;
|
||||
BitNodeMultipliers.HacknetNodeMoney = 0.5;
|
||||
BitNodeMultipliers.ManualHackMoney = 0.5;
|
||||
BitNodeMultipliers.ScriptHackMoney = 0.5;
|
||||
BitNodeMultipliers.CodingContractMoney = 0.5;
|
||||
BitNodeMultipliers.InfiltrationMoney = 0.5;
|
||||
BitNodeMultipliers.CorporationValuation = 0.5;
|
||||
BitNodeMultipliers.AugmentationMoneyCost = 5;
|
||||
BitNodeMultipliers.AugmentationRepCost = 2;
|
||||
BitNodeMultipliers.PurchasedServerCost = 5;
|
||||
BitNodeMultipliers.PurchasedServerLimit = 0.6;
|
||||
BitNodeMultipliers.PurchasedServerMaxRam = 0.5;
|
||||
break;
|
||||
case 11: //The Big Crash
|
||||
BitNodeMultipliers.ServerMaxMoney = 0.1;
|
||||
BitNodeMultipliers.ServerStartingMoney = 0.1;
|
@ -4,6 +4,11 @@
|
||||
* player toward the intended strategy. Unless they really want to play the long, slow game of waiting...
|
||||
*/
|
||||
interface IBitNodeMultipliers {
|
||||
/**
|
||||
* Influences how quickly the player's agility level (not exp) scales
|
||||
*/
|
||||
AgilityLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences the base cost to purchase an augmentation.
|
||||
*/
|
||||
@ -24,6 +29,11 @@ interface IBitNodeMultipliers {
|
||||
*/
|
||||
BladeburnerSkillCost: number;
|
||||
|
||||
/**
|
||||
* Influences how quickly the player's charisma level (not exp) scales
|
||||
*/
|
||||
CharismaLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences the experience gained for each ability when a player completes a class.
|
||||
*/
|
||||
@ -59,6 +69,16 @@ interface IBitNodeMultipliers {
|
||||
*/
|
||||
CrimeMoney: number;
|
||||
|
||||
/**
|
||||
* Influences how quickly the player's defense level (not exp) scales
|
||||
*/
|
||||
DefenseLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences how quickly the player's dexterity level (not exp) scales
|
||||
*/
|
||||
DexterityLevelMultiplier: number;
|
||||
|
||||
/**
|
||||
* Influences how much rep the player gains in each faction simply by being a member.
|
||||
*/
|
||||
@ -105,6 +125,20 @@ interface IBitNodeMultipliers {
|
||||
*/
|
||||
ManualHackMoney: number;
|
||||
|
||||
/**
|
||||
* Influence how much it costs to purchase a server
|
||||
*/
|
||||
PurchasedServerCost: number;
|
||||
|
||||
/**
|
||||
* Influences the maximum number of purchased servers you can have
|
||||
*/
|
||||
PurchasedServerLimit: number;
|
||||
|
||||
/**
|
||||
* Influences the maximum allowed RAM for a purchased server
|
||||
*/
|
||||
PurchasedServerMaxRam: number;
|
||||
/**
|
||||
* Influences the minimum favor the player must have with a faction before they can donate to gain rep.
|
||||
*/
|
||||
@ -139,6 +173,11 @@ interface IBitNodeMultipliers {
|
||||
* Influences the weaken amount per invocation against a server.
|
||||
*/
|
||||
ServerWeakenRate: number;
|
||||
|
||||
/**
|
||||
* Influences how quickly the player's strength level (not exp) scales
|
||||
*/
|
||||
StrengthLevelMultiplier: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,6 +186,11 @@ interface IBitNodeMultipliers {
|
||||
// tslint:disable-next-line:variable-name
|
||||
export const BitNodeMultipliers: IBitNodeMultipliers = {
|
||||
HackingLevelMultiplier: 1,
|
||||
StrengthLevelMultiplier: 1,
|
||||
DefenseLevelMultiplier: 1,
|
||||
DexterityLevelMultiplier: 1,
|
||||
AgilityLevelMultiplier: 1,
|
||||
CharismaLevelMultiplier: 1,
|
||||
|
||||
ServerGrowthRate: 1,
|
||||
ServerMaxMoney: 1,
|
||||
@ -154,6 +198,10 @@ export const BitNodeMultipliers: IBitNodeMultipliers = {
|
||||
ServerStartingSecurity: 1,
|
||||
ServerWeakenRate: 1,
|
||||
|
||||
PurchasedServerCost: 1,
|
||||
PurchasedServerLimit: 1,
|
||||
PurchasedServerMaxRam: 1,
|
||||
|
||||
CompanyWorkMoney: 1,
|
||||
CrimeMoney: 1,
|
||||
HacknetNodeMoney: 1,
|
1
src/BitNode/README.md
Normal file
1
src/BitNode/README.md
Normal file
@ -0,0 +1 @@
|
||||
Contains implementation of BitNodes and BitNode-specific mechanics
|
@ -1,5 +1,6 @@
|
||||
import { Augmentations , AugmentationNames } from "./Augmentations";
|
||||
import { BitNodeMultipliers } from "./BitNodeMultipliers";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Engine } from "./engine";
|
||||
import { Faction } from "./Faction/Faction";
|
||||
|
@ -84,7 +84,7 @@ function sanitizeRewardType(rewardType) {
|
||||
if (type === CodingContractRewardType.FactionReputationAll && factionsThatAllowHacking.length === 0) {
|
||||
type = CodingContractRewardType.CompanyReputation;
|
||||
}
|
||||
if (type === CodingContractRewardType.CompanyReputation && Player.companyName === "") {
|
||||
if (type === CodingContractRewardType.CompanyReputation && Object.keys(Player.jobs).length === 0) {
|
||||
type = CodingContractRewardType.Money;
|
||||
}
|
||||
|
||||
@ -115,8 +115,9 @@ function getRandomReward() {
|
||||
reward.name = randFaction;
|
||||
break;
|
||||
case CodingContractRewardType.CompanyReputation:
|
||||
if (Player.companyName !== "") {
|
||||
reward.name = Player.companyName;
|
||||
const allJobs = Object.keys(Player.jobs);
|
||||
if (allJobs.length > 0) {
|
||||
reward.name = allJobs[getRandomInt(0, allJobs.length - 1)];
|
||||
} else {
|
||||
reward.type = CodingContractRewardType.Money;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export let CONSTANTS: IMap<any> = {
|
||||
BaseCostFor1GBOfRamServer: 55000, //1 GB of RAM
|
||||
BaseCostFor1GBOfRamHacknetNode: 30000,
|
||||
|
||||
TravelCost: 200000,
|
||||
TravelCost: 200e3,
|
||||
|
||||
BaseCostForHacknetNode: 1000,
|
||||
BaseCostForHacknetNodeCore: 500000,
|
||||
@ -104,10 +104,11 @@ export let CONSTANTS: IMap<any> = {
|
||||
NumNetscriptPorts: 20,
|
||||
|
||||
//Server constants
|
||||
ServerBaseGrowthRate: 1.03, //Unadjusted Growth rate
|
||||
ServerMaxGrowthRate: 1.0035, //Maximum possible growth rate (max rate accounting for server security)
|
||||
ServerFortifyAmount: 0.002, //Amount by which server's security increases when its hacked/grown
|
||||
ServerWeakenAmount: 0.05, //Amount by which server's security decreases when weakened
|
||||
HomeComputerMaxRam: 1073741824, // 2 ^ 30
|
||||
ServerBaseGrowthRate: 1.03, // Unadjusted Growth rate
|
||||
ServerMaxGrowthRate: 1.0035, // Maximum possible growth rate (max rate accounting for server security)
|
||||
ServerFortifyAmount: 0.002, // Amount by which server's security increases when its hacked/grown
|
||||
ServerWeakenAmount: 0.05, // Amount by which server's security decreases when weakened
|
||||
|
||||
PurchasedServerLimit: 25,
|
||||
PurchasedServerMaxRam: 1048576, //2^20
|
||||
@ -129,7 +130,7 @@ export let CONSTANTS: IMap<any> = {
|
||||
WSEAccountCost: 200e6,
|
||||
TIXAPICost: 5e9,
|
||||
MarketData4SCost: 1e9,
|
||||
MarketDataTixApi4SCost: 20e9,
|
||||
MarketDataTixApi4SCost: 25e9,
|
||||
StockMarketCommission: 100e3,
|
||||
|
||||
//Hospital/Health
|
||||
@ -275,6 +276,9 @@ export let CONSTANTS: IMap<any> = {
|
||||
CodingContractBaseCompanyRepGain: 4000,
|
||||
CodingContractBaseMoneyGain: 50e6,
|
||||
|
||||
// BitNode/Source-File related stuff
|
||||
TotalNumBitNodes: 24,
|
||||
|
||||
/* Tutorial related things */
|
||||
TutorialNetworkingText: "Servers are a central part of the game. You start with a single personal server (your home computer) " +
|
||||
"and you can purchase additional servers as you progress through the game. Connecting to other servers " +
|
||||
@ -506,40 +510,27 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.42.0
|
||||
* Corporation Changes:
|
||||
** Corporation can now be self-funded with $150b or using seed money in exchange for 500m newly-issued shares
|
||||
** In BitNode-3, you no longer start with $150b
|
||||
** Changed initial market prices for many materials
|
||||
** Changed the way a material's demand, competition, and market price change over time
|
||||
** The sale price of materials can no longer be marked-up as high
|
||||
** Added a Research Tree mechanic. Spend Scientific Research on permanent upgrades for each industry
|
||||
** You can now redistribute earnings to shareholders (including yourself) as dividends
|
||||
** Cost of "Smart Supply" upgraded reduced from $50b to $25b
|
||||
** Now has offline progress, which works similarly to the Gang/Bladeburner mechanics
|
||||
** Slightly reduced the amount of money offered to you by investment firms
|
||||
** Employee salaries now slowly increase over time
|
||||
** Slightly reduced the effect "Real Estate" has on the Production Multiplier for the Agriculture industry
|
||||
** Changed the way your Corporation's value is calculated (this is what determines stock price)
|
||||
** After taking your corporation public, it is now possible to issue new shares to raise capital
|
||||
** Issuing new shares can only be done once every 12 hours
|
||||
** Buying back shares must now be done at a premium
|
||||
** Selling shares can now only be done once per hour
|
||||
** Selling large amounts of shares now immediately impacts stock price (during the transaction)
|
||||
** Reduced the initial cost of the DreamSense upgrade from $8b to $4b, but increased its price multiplier
|
||||
** Reduced the price multiplier for ABC SalesBots upgrade
|
||||
v0.43.0
|
||||
* Added BitNode-10: Digital Carbon
|
||||
|
||||
* Added getOrders() Netscript function to the TIX API
|
||||
* Added getAugmentationPrereq() Singularity function (by havocmayhem)
|
||||
* Added hackAnalyzePercent() and hackAnalyzeThreads() Netscript functions
|
||||
* Stock Market, Travel, and Corporation main menu links are now properly styled
|
||||
* Many pop-up/dialog boxes now support the 'Enter' and 'Esc' hotkeys. If you find a pop-up/dialog box that doesnt support this, let me know specifically which one ('Enter' for the default option, 'Esc' for cancelling and closing the pop-up box)
|
||||
* Added "brace_style = preserve_inline" configuration to Script Editor Beautifier
|
||||
* ServerProfiler.exe can now be purchased from the Dark Web
|
||||
* Added an option to copy save data to clipboard
|
||||
* Added total multiplier information on the "Augmentations" page
|
||||
* Bug Fix: gymWorkout() Singularity function should now work properly with Millenium Fitness Gym
|
||||
* Began migrating gameplay information to the ReadTheDocs documentation
|
||||
`
|
||||
* Stock Market Changes:
|
||||
** Each stock now has a maximum number of shares you can purchase (both Long and Short positions combined)
|
||||
** Added getStockMaxShares() Netscript function to the TIX API
|
||||
** The cost of 4S Market Data TIX API Access increased from $20b to $25b
|
||||
|
||||
* Job Changes:
|
||||
** You can now hold multiple jobs at once. This means you no longer lose reputation when leaving a company
|
||||
** Because of this change, the getCharacterInformation() Netscript function returns a slightly different value
|
||||
|
||||
* Script Editor Changes:
|
||||
** Added new script editor: CodeMirror. You can choose between the old editor (Ace) or CodeMirror
|
||||
** Navigation keyboard shortcuts no longer work if the script editor is focused
|
||||
|
||||
* Trying to programmatically run a script (run(), exec()) with a 'threads' argument of 0 will now cause the function to return false without running the script
|
||||
* Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB)
|
||||
* The maximum amount, maximum RAM, and cost of purchasing servers can now vary between different BitNodes (new BitNode multipliers)
|
||||
* Pop-up dialog boxes are a little bit bigger
|
||||
* Bug Fix: When importing scripts, "./" will now be properly ignored (e.g. import { foo } from "./lib.script" )
|
||||
`
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import { MaterialSizes } from "./MaterialSizes";
|
||||
import { Product } from "./Product";
|
||||
import { ResearchMap } from "./ResearchMap";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNodeMultipliers";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { showLiterature } from "../Literature";
|
||||
|
147
src/Crime/Crime.ts
Normal file
147
src/Crime/Crime.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import { CONSTANTS } from "../Constants";
|
||||
export interface IConstructorParams {
|
||||
hacking_success_weight?: number;
|
||||
strength_success_weight?: number;
|
||||
defense_success_weight?: number;
|
||||
dexterity_success_weight?: number;
|
||||
agility_success_weight?: number;
|
||||
charisma_success_weight?: number;
|
||||
hacking_exp?: number;
|
||||
strength_exp?: number;
|
||||
defense_exp?: number;
|
||||
dexterity_exp?: number;
|
||||
agility_exp?: number;
|
||||
charisma_exp?: number;
|
||||
intelligence_exp?: number;
|
||||
|
||||
kills?: number;
|
||||
}
|
||||
|
||||
interface IPlayer {
|
||||
startCrime(crimeType: string,
|
||||
hackExp: number,
|
||||
strExp: number,
|
||||
defExp: number,
|
||||
dexExp: number,
|
||||
agiExp: number,
|
||||
chaExp: number,
|
||||
money: number,
|
||||
time: number,
|
||||
singParams: any): void;
|
||||
|
||||
hacking_skill: number;
|
||||
strength: number;
|
||||
defense: number;
|
||||
dexterity: number;
|
||||
agility: number;
|
||||
charisma: number;
|
||||
intelligence: number;
|
||||
|
||||
crime_success_mult: number;
|
||||
}
|
||||
|
||||
export class Crime {
|
||||
// Number representing the difficulty of the crime. Used for success chance calculations
|
||||
difficulty: number = 0;
|
||||
|
||||
// Amount of karma lost for SUCCESSFULLY committing this crime
|
||||
karma: number = 0;
|
||||
|
||||
// How many people die as a result of this crime
|
||||
kills: number = 0;
|
||||
|
||||
// How much money is given by the
|
||||
money: number = 0;
|
||||
|
||||
// Name of crime
|
||||
name: string = "";
|
||||
|
||||
// Milliseconds it takes to attempt the crime
|
||||
time: number = 0;
|
||||
|
||||
// Corresponding type in CONSTANTS. Contains a description for the crime activity
|
||||
type: string = "";
|
||||
|
||||
// Weighting factors that determine how stats affect the success rate of this crime
|
||||
hacking_success_weight: number = 0;
|
||||
strength_success_weight: number = 0;
|
||||
defense_success_weight: number = 0;
|
||||
dexterity_success_weight: number = 0;
|
||||
agility_success_weight: number = 0;
|
||||
charisma_success_weight: number = 0;
|
||||
|
||||
// How much stat experience is granted by this crime
|
||||
hacking_exp: number = 0;
|
||||
strength_exp: number = 0;
|
||||
defense_exp: number = 0;
|
||||
dexterity_exp: number = 0;
|
||||
agility_exp: number = 0;
|
||||
charisma_exp: number = 0;
|
||||
intelligence_exp: number = 0;
|
||||
|
||||
constructor(name: string = "",
|
||||
type: string = "",
|
||||
time: number = 0,
|
||||
money: number = 0,
|
||||
difficulty: number = 0,
|
||||
karma: number = 0,
|
||||
params: IConstructorParams={}) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.time = time;
|
||||
this.money = money;
|
||||
this.difficulty = difficulty;
|
||||
this.karma = karma;
|
||||
|
||||
this.hacking_success_weight = params.hacking_success_weight ? params.hacking_success_weight : 0;
|
||||
this.strength_success_weight = params.strength_success_weight ? params.strength_success_weight : 0;
|
||||
this.defense_success_weight = params.defense_success_weight ? params.defense_success_weight : 0;
|
||||
this.dexterity_success_weight = params.dexterity_success_weight ? params.dexterity_success_weight : 0;
|
||||
this.agility_success_weight = params.agility_success_weight ? params.agility_success_weight : 0;
|
||||
this.charisma_success_weight = params.charisma_success_weight ? params.charisma_success_weight : 0;
|
||||
|
||||
this.hacking_exp = params.hacking_exp ? params.hacking_exp : 0;
|
||||
this.strength_exp = params.strength_exp ? params.strength_exp : 0;
|
||||
this.defense_exp = params.defense_exp ? params.defense_exp : 0;
|
||||
this.dexterity_exp = params.dexterity_exp ? params.dexterity_exp : 0;
|
||||
this.agility_exp = params.agility_exp ? params.agility_exp : 0;
|
||||
this.charisma_exp = params.charisma_exp ? params.charisma_exp : 0;
|
||||
this.intelligence_exp = params.intelligence_exp ? params.intelligence_exp : 0;
|
||||
|
||||
this.kills = params.kills ? params.kills : 0;
|
||||
}
|
||||
|
||||
commit(p: IPlayer, div: number=1, singParams: any=null): number {
|
||||
if (div <= 0) { div = 1; }
|
||||
p.startCrime(
|
||||
this.type,
|
||||
this.hacking_exp/div,
|
||||
this.strength_exp/div,
|
||||
this.defense_exp/div,
|
||||
this.dexterity_exp/div,
|
||||
this.agility_exp/div,
|
||||
this.charisma_exp/div,
|
||||
this.money/div,
|
||||
this.time,
|
||||
singParams
|
||||
);
|
||||
|
||||
return this.time;
|
||||
}
|
||||
|
||||
successRate(p: IPlayer): number {
|
||||
let chance: number = (this.hacking_success_weight * p.hacking_skill +
|
||||
this.strength_success_weight * p.strength +
|
||||
this.defense_success_weight * p.defense +
|
||||
this.dexterity_success_weight * p.dexterity +
|
||||
this.agility_success_weight * p.agility +
|
||||
this.charisma_success_weight * p.charisma +
|
||||
CONSTANTS.IntelligenceCrimeWeight * p.intelligence);
|
||||
chance /= CONSTANTS.MaxSkillLevel;
|
||||
chance /= this.difficulty;
|
||||
chance *= p.crime_success_mult;
|
||||
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
}
|
63
src/Crime/CrimeHelpers.js
Normal file
63
src/Crime/CrimeHelpers.js
Normal file
@ -0,0 +1,63 @@
|
||||
import { Crimes } from "./Crimes";
|
||||
|
||||
import { Player } from "../Player";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
|
||||
export function determineCrimeSuccess(type, moneyGained) {
|
||||
var chance = 0;
|
||||
var found = false;
|
||||
for(const i in Crimes) {
|
||||
const crime = Crimes[i];
|
||||
if(crime.type == type) {
|
||||
chance = crime.successRate(Player);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
console.log(crime);
|
||||
dialogBoxCreate("ERR: Unrecognized crime type. This is probably a bug please contact the developer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.random() <= chance) {
|
||||
//Success
|
||||
Player.gainMoney(moneyGained);
|
||||
return true;
|
||||
} else {
|
||||
//Failure
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function findCrime(roughName) {
|
||||
if (roughName.includes("shoplift")) {
|
||||
return Crimes.Shoplift;
|
||||
} else if (roughName.includes("rob") && roughName.includes("store")) {
|
||||
return Crimes.RobStore;
|
||||
} else if (roughName.includes("mug")) {
|
||||
return Crimes.Mug;
|
||||
} else if (roughName.includes("larceny")) {
|
||||
return Crimes.Larceny;
|
||||
} else if (roughName.includes("drugs")) {
|
||||
return Crimes.DealDrugs;
|
||||
} else if (roughName.includes("bond") && roughName.includes("forge")) {
|
||||
return Crimes.BondForgery;
|
||||
} else if (roughName.includes("traffick") && roughName.includes("arms")) {
|
||||
return Crimes.TraffickArms;
|
||||
} else if (roughName.includes("homicide")) {
|
||||
return Crimes.Homicide;
|
||||
} else if (roughName.includes("grand") && roughName.includes("auto")) {
|
||||
return Crimes.GrandTheftAuto;
|
||||
} else if (roughName.includes("kidnap")) {
|
||||
return Crimes.Kidnap;
|
||||
} else if (roughName.includes("assassinate")) {
|
||||
return Crimes.Assassination;
|
||||
} else if (roughName.includes("heist")) {
|
||||
return Crimes.Heist;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
163
src/Crime/Crimes.ts
Normal file
163
src/Crime/Crimes.ts
Normal file
@ -0,0 +1,163 @@
|
||||
import { Crime } from "./Crime";
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IMap } from "../types";
|
||||
|
||||
export const Crimes: IMap<Crime> = {
|
||||
Shoplift: new Crime("Shoplift", CONSTANTS.CrimeShoplift, 2e3, 15e3, 1/20, 0.1, {
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
|
||||
dexterity_exp: 2,
|
||||
agility_exp: 2,
|
||||
}),
|
||||
|
||||
RobStore: new Crime("Rob Store", CONSTANTS.CrimeRobStore, 60e3, 400e3, 1/5, 0.5, {
|
||||
hacking_exp: 30,
|
||||
dexterity_exp: 45,
|
||||
agility_exp: 45,
|
||||
|
||||
hacking_success_weight: 0.5 ,
|
||||
dexterity_success_weight: 2,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 0.25 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Mug: new Crime("Mug", CONSTANTS.CrimeMug, 4e3, 36e3, 1/5, 0.25, {
|
||||
strength_exp: 3,
|
||||
defense_exp: 3,
|
||||
dexterity_exp: 3,
|
||||
agility_exp: 3,
|
||||
|
||||
strength_success_weight: 1.5,
|
||||
defense_success_weight: 0.5,
|
||||
dexterity_success_weight: 1.5,
|
||||
agility_success_weight: 0.5,
|
||||
}),
|
||||
|
||||
Larceny: new Crime("Larceny", CONSTANTS.CrimeLarceny, 90e3, 800e3, 1/3, 1.5, {
|
||||
hacking_exp: 45,
|
||||
dexterity_exp: 60,
|
||||
agility_exp: 60,
|
||||
|
||||
hacking_success_weight: 0.5,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 0.5 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
DealDrugs: new Crime("Deal Drugs", CONSTANTS.CrimeDrugs, 10e3, 120e3, 1, 0.5, {
|
||||
dexterity_exp: 5,
|
||||
agility_exp: 5,
|
||||
charisma_exp: 10,
|
||||
|
||||
charisma_success_weight: 3,
|
||||
dexterity_success_weight: 2,
|
||||
agility_success_weight: 1,
|
||||
}),
|
||||
|
||||
BondForgery: new Crime("Bond Forgery", CONSTANTS.CrimeBondForgery, 300e3, 4.5e6, 1/2, 0.1, {
|
||||
hacking_exp: 100,
|
||||
dexterity_exp: 150,
|
||||
charisma_exp: 15,
|
||||
|
||||
hacking_success_weight: 0.05,
|
||||
dexterity_success_weight: 1.25,
|
||||
|
||||
intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
TraffickArms: new Crime("Traffick Arms", CONSTANTS.CrimeTraffickArms, 40e3, 600e3, 2, 1, {
|
||||
strength_exp: 20,
|
||||
defense_exp: 20,
|
||||
dexterity_exp: 20,
|
||||
agility_exp: 20,
|
||||
charisma_exp: 40,
|
||||
|
||||
charisma_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
defense_success_weight: 1,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
}),
|
||||
|
||||
Homicide: new Crime("Homicide", CONSTANTS.CrimeHomicide, 3e3, 45e3, 1, 3, {
|
||||
strength_exp: 2,
|
||||
defense_exp: 2,
|
||||
dexterity_exp: 2,
|
||||
agility_exp: 2,
|
||||
|
||||
strength_success_weight: 2,
|
||||
defense_success_weight: 2,
|
||||
dexterity_success_weight: 0.5,
|
||||
agility_success_weight: 0.5,
|
||||
|
||||
kills: 1,
|
||||
}),
|
||||
|
||||
GrandTheftAuto: new Crime("Grand Theft Auto", CONSTANTS.CrimeGrandTheftAuto, 80e3, 1.6e6, 8, 5, {
|
||||
strength_exp: 20,
|
||||
defense_exp: 20,
|
||||
dexterity_exp: 20,
|
||||
agility_exp: 80,
|
||||
charisma_exp: 40,
|
||||
|
||||
hacking_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
dexterity_success_weight: 4,
|
||||
agility_success_weight: 2,
|
||||
charisma_success_weight: 2,
|
||||
|
||||
intelligence_exp: CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Kidnap: new Crime("Kidnap", CONSTANTS.CrimeKidnap, 120e3, 3.6e6, 5, 6, {
|
||||
strength_exp: 80,
|
||||
defense_exp: 80,
|
||||
dexterity_exp: 80,
|
||||
agility_exp: 80,
|
||||
charisma_exp: 80,
|
||||
|
||||
charisma_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Assassination: new Crime("Assassination", CONSTANTS.CrimeAssassination, 300e3, 12e6, 8, 10, {
|
||||
strength_exp: 300,
|
||||
defense_exp: 300,
|
||||
dexterity_exp: 300,
|
||||
agility_exp: 300,
|
||||
|
||||
strength_success_weight: 1,
|
||||
dexterity_success_weight: 2,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 5 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
|
||||
kills: 1,
|
||||
}),
|
||||
|
||||
Heist: new Crime("Heist", CONSTANTS.CrimeHeist, 600e3, 120e6, 18, 15, {
|
||||
hacking_exp: 450,
|
||||
strength_exp: 450,
|
||||
defense_exp: 450,
|
||||
dexterity_exp: 450,
|
||||
agility_exp: 450,
|
||||
charisma_exp: 450,
|
||||
|
||||
hacking_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
defense_success_weight: 1,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
charisma_success_weight: 1,
|
||||
|
||||
intelligence_exp: 10 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
};
|
275
src/Crimes.js
275
src/Crimes.js
@ -1,275 +0,0 @@
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import {Player} from "./Player";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
|
||||
|
||||
function Crime(name, type, time, money, difficulty, karma, params) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.time = time;
|
||||
this.money = money;
|
||||
this.difficulty = difficulty;
|
||||
this.karma = karma;
|
||||
|
||||
this.hacking_success_weight = params.hacking_success_weight ? params.hacking_success_weight : 0;
|
||||
this.strength_success_weight = params.strength_success_weight ? params.strength_success_weight : 0;
|
||||
this.defense_success_weight = params.defense_success_weight ? params.defense_success_weight : 0;
|
||||
this.dexterity_success_weight = params.dexterity_success_weight ? params.dexterity_success_weight : 0;
|
||||
this.agility_success_weight = params.agility_success_weight ? params.agility_success_weight : 0;
|
||||
this.charisma_success_weight = params.charisma_success_weight ? params.charisma_success_weight : 0;
|
||||
|
||||
this.hacking_exp = params.hacking_exp ? params.hacking_exp : 0;
|
||||
this.strength_exp = params.strength_exp ? params.strength_exp : 0;
|
||||
this.defense_exp = params.defense_exp ? params.defense_exp : 0;
|
||||
this.dexterity_exp = params.dexterity_exp ? params.dexterity_exp : 0;
|
||||
this.agility_exp = params.agility_exp ? params.agility_exp : 0;
|
||||
this.charisma_exp = params.charisma_exp ? params.charisma_exp : 0;
|
||||
this.intelligence_exp = params.intelligence_exp ? params.intelligence_exp : 0;
|
||||
|
||||
this.kills = params.kills ? params.kills : 0;
|
||||
}
|
||||
|
||||
Crime.prototype.commit = function(div=1, singParams=null) {
|
||||
if (div <= 0) {div = 1;}
|
||||
Player.crimeType = this.type;
|
||||
Player.startCrime(
|
||||
this.hacking_exp/div,
|
||||
this.strength_exp/div,
|
||||
this.defense_exp/div,
|
||||
this.dexterity_exp/div,
|
||||
this.agility_exp/div,
|
||||
this.charisma_exp/div,
|
||||
this.money/div, this.time, singParams);
|
||||
return this.time;
|
||||
}
|
||||
|
||||
Crime.prototype.successRate = function() {
|
||||
var chance = (this.hacking_success_weight * Player.hacking_skill +
|
||||
this.strength_success_weight * Player.strength +
|
||||
this.defense_success_weight * Player.defense +
|
||||
this.dexterity_success_weight * Player.dexterity +
|
||||
this.agility_success_weight * Player.agility +
|
||||
this.charisma_success_weight * Player.charisma +
|
||||
CONSTANTS.IntelligenceCrimeWeight * Player.intelligence);
|
||||
chance /= CONSTANTS.MaxSkillLevel;
|
||||
chance /= this.difficulty;
|
||||
chance *= Player.crime_success_mult;
|
||||
return Math.min(chance, 1);
|
||||
}
|
||||
|
||||
const Crimes = {
|
||||
Shoplift: new Crime("Shoplift", CONSTANTS.CrimeShoplift, 2e3, 15e3, 1/20, 0.1, {
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
|
||||
dexterity_exp: 2,
|
||||
agility_exp: 2,
|
||||
}),
|
||||
|
||||
RobStore: new Crime("Rob Store", CONSTANTS.CrimeRobStore, 60e3, 400e3, 1/5, 0.5, {
|
||||
hacking_exp: 30,
|
||||
dexterity_exp: 45,
|
||||
agility_exp: 45,
|
||||
|
||||
hacking_success_weight: 0.5 ,
|
||||
dexterity_success_weight: 2,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 0.25 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Mug: new Crime("Mug", CONSTANTS.CrimeMug, 4e3, 36e3, 1/5, 0.25, {
|
||||
strength_exp: 3,
|
||||
defense_exp: 3,
|
||||
dexterity_exp: 3,
|
||||
agility_exp: 3,
|
||||
|
||||
strength_success_weight: 1.5,
|
||||
defense_success_weight: 0.5,
|
||||
dexterity_success_weight: 1.5,
|
||||
agility_success_weight: 0.5,
|
||||
}),
|
||||
|
||||
Larceny: new Crime("Larceny", CONSTANTS.CrimeLarceny, 90e3, 800e3, 1/3, 1.5, {
|
||||
hacking_exp: 45,
|
||||
dexterity_exp: 60,
|
||||
agility_exp: 60,
|
||||
|
||||
hacking_success_weight: 0.5,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 0.5 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
DealDrugs: new Crime("Deal Drugs", CONSTANTS.CrimeDrugs, 10e3, 120e3, 1, 0.5, {
|
||||
dexterity_exp: 5,
|
||||
agility_exp: 5,
|
||||
charisma_exp: 10,
|
||||
|
||||
charisma_success_weight: 3,
|
||||
dexterity_success_weight: 2,
|
||||
agility_success_weight: 1,
|
||||
}),
|
||||
|
||||
BondForgery: new Crime("Bond Forgery", CONSTANTS.CrimeBondForgery, 300e3, 4.5e6, 1/2, 0.1, {
|
||||
hacking_exp: 100,
|
||||
dexterity_exp: 150,
|
||||
charisma_exp: 15,
|
||||
|
||||
hacking_success_weight: 0.05,
|
||||
dexterity_success_weight: 1.25,
|
||||
|
||||
intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
TraffickArms: new Crime("Traffick Arms", CONSTANTS.CrimeTraffickArms, 40e3, 600e3, 2, 1, {
|
||||
strength_exp: 20,
|
||||
defense_exp: 20,
|
||||
dexterity_exp: 20,
|
||||
agility_exp: 20,
|
||||
charisma_exp: 40,
|
||||
|
||||
charisma_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
defense_success_weight: 1,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
}),
|
||||
|
||||
Homicide: new Crime("Homicide", CONSTANTS.CrimeHomicide, 3e3, 45e3, 1, 3, {
|
||||
strength_exp: 2,
|
||||
defense_exp: 2,
|
||||
dexterity_exp: 2,
|
||||
agility_exp: 2,
|
||||
|
||||
strength_success_weight: 2,
|
||||
defense_success_weight: 2,
|
||||
dexterity_success_weight: 0.5,
|
||||
agility_success_weight: 0.5,
|
||||
|
||||
kills: 1,
|
||||
}),
|
||||
|
||||
GrandTheftAuto: new Crime("Grand Theft Auto", CONSTANTS.CrimeGrandTheftAuto, 80e3, 1.6e6, 8, 5, {
|
||||
strength_exp: 20,
|
||||
defense_exp: 20,
|
||||
dexterity_exp: 20,
|
||||
agility_exp: 80,
|
||||
charisma_exp: 40,
|
||||
|
||||
hacking_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
dexterity_success_weight: 4,
|
||||
agility_success_weight: 2,
|
||||
charisma_success_weight: 2,
|
||||
|
||||
intelligence_exp: CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Kidnap: new Crime("Kidnap", CONSTANTS.CrimeKidnap, 120e3, 3.6e6, 5, 6, {
|
||||
strength_exp: 80,
|
||||
defense_exp: 80,
|
||||
dexterity_exp: 80,
|
||||
agility_exp: 80,
|
||||
charisma_exp: 80,
|
||||
|
||||
charisma_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 2 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
|
||||
Assassination: new Crime("Assassination", CONSTANTS.CrimeAssassination, 300e3, 12e6, 8, 10, {
|
||||
strength_exp: 300,
|
||||
defense_exp: 300,
|
||||
dexterity_exp: 300,
|
||||
agility_exp: 300,
|
||||
|
||||
strength_success_weight: 1,
|
||||
dexterity_success_weight: 2,
|
||||
agility_success_weight: 1,
|
||||
|
||||
intelligence_exp: 5 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
|
||||
kills: 1,
|
||||
}),
|
||||
|
||||
Heist: new Crime("Heist", CONSTANTS.CrimeHeist, 600e3, 120e6, 18, 15, {
|
||||
hacking_exp: 450,
|
||||
strength_exp: 450,
|
||||
defense_exp: 450,
|
||||
dexterity_exp: 450,
|
||||
agility_exp: 450,
|
||||
charisma_exp: 450,
|
||||
|
||||
hacking_success_weight: 1,
|
||||
strength_success_weight: 1,
|
||||
defense_success_weight: 1,
|
||||
dexterity_success_weight: 1,
|
||||
agility_success_weight: 1,
|
||||
charisma_success_weight: 1,
|
||||
|
||||
intelligence_exp: 10 * CONSTANTS.IntelligenceCrimeBaseExpGain,
|
||||
}),
|
||||
};
|
||||
|
||||
function determineCrimeSuccess(type, moneyGained) {
|
||||
var chance = 0;
|
||||
var found = false;
|
||||
for(const i in Crimes) {
|
||||
const crime = Crimes[i];
|
||||
if(crime.type == type) {
|
||||
chance = crime.successRate();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
console.log(crime);
|
||||
dialogBoxCreate("ERR: Unrecognized crime type. This is probably a bug please contact the developer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.random() <= chance) {
|
||||
//Success
|
||||
Player.gainMoney(moneyGained);
|
||||
return true;
|
||||
} else {
|
||||
//Failure
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function findCrime(roughName) {
|
||||
if (roughName.includes("shoplift")) {
|
||||
return Crimes.Shoplift;
|
||||
} else if (roughName.includes("rob") && roughName.includes("store")) {
|
||||
return Crimes.RobStore;
|
||||
} else if (roughName.includes("mug")) {
|
||||
return Crimes.Mug;
|
||||
} else if (roughName.includes("larceny")) {
|
||||
return Crimes.Larceny;
|
||||
} else if (roughName.includes("drugs")) {
|
||||
return Crimes.DealDrugs;
|
||||
} else if (roughName.includes("bond") && roughName.includes("forge")) {
|
||||
return Crimes.BondForgery;
|
||||
} else if (roughName.includes("traffick") && roughName.includes("arms")) {
|
||||
return Crimes.TraffickArms;
|
||||
} else if (roughName.includes("homicide")) {
|
||||
return Crimes.Homicide;
|
||||
} else if (roughName.includes("grand") && roughName.includes("auto")) {
|
||||
return Crimes.GrandTheftAuto;
|
||||
} else if (roughName.includes("kidnap")) {
|
||||
return Crimes.Kidnap;
|
||||
} else if (roughName.includes("assassinate")) {
|
||||
return Crimes.Assassination;
|
||||
} else if (roughName.includes("heist")) {
|
||||
return Crimes.Heist;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export {determineCrimeSuccess,findCrime,Crimes};
|
@ -1,4 +1,4 @@
|
||||
import { AugmentationNames } from "./Augmentations";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { generateRandomContract } from "./CodingContractGenerator";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
@ -6,13 +6,17 @@ import { Player } from "./Player";
|
||||
import { AllServers } from "./Server";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { StockMarket,
|
||||
SymbolToStockMap } from "./StockMarket";
|
||||
import { Stock } from "./Stock";
|
||||
SymbolToStockMap } from "./StockMarket/StockMarket";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import { Terminal } from "./Terminal";
|
||||
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText } from "../utils/uiHelpers/getSelectData";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
const devMenuContainerId = "dev-menu-container";
|
||||
@ -222,7 +226,7 @@ export function createDevMenu() {
|
||||
innerText: "Receive Invite to Faction",
|
||||
});
|
||||
|
||||
// Augmentations / Source Files
|
||||
// Augmentations
|
||||
const augmentationsHeader = createElement("h2", {innerText: "Augmentations"});
|
||||
|
||||
const augmentationsDropdown = createElement("select", {
|
||||
@ -242,6 +246,32 @@ export function createDevMenu() {
|
||||
innerText: "Queue Augmentation",
|
||||
})
|
||||
|
||||
// Source Files
|
||||
const sourceFilesHeader = createElement("h2", { innerText: "Source-Files" });
|
||||
|
||||
const removeSourceFileDropdown = createElement("select", {
|
||||
class: "dropdown",
|
||||
margin: "5px",
|
||||
});
|
||||
for (let i = 0; i < 24; ++i) {
|
||||
removeSourceFileDropdown.add(createOptionElement(String(i)));
|
||||
}
|
||||
|
||||
const removeSourceFileButton = createElement("button", {
|
||||
class: "std-button",
|
||||
clickListener: () => {
|
||||
const numToRemove = parseInt(getSelectText(removeSourceFileDropdown));
|
||||
for (let i = 0; i < Player.sourceFiles.length; ++i) {
|
||||
if (Player.sourceFiles[i].n === numToRemove) {
|
||||
Player.sourceFiles.splice(i, 1);
|
||||
hackWorldDaemon(Player.bitNodeN, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
innerText: "Remove Source File and Trigger Bitflume",
|
||||
});
|
||||
|
||||
// Programs
|
||||
const programsHeader = createElement("h2", {innerText: "Programs"});
|
||||
|
||||
@ -508,6 +538,9 @@ export function createDevMenu() {
|
||||
devMenuContainer.appendChild(augmentationsHeader);
|
||||
devMenuContainer.appendChild(augmentationsDropdown);
|
||||
devMenuContainer.appendChild(augmentationsQueueButton);
|
||||
devMenuContainer.appendChild(sourceFilesHeader);
|
||||
devMenuContainer.appendChild(removeSourceFileDropdown);
|
||||
devMenuContainer.appendChild(removeSourceFileButton);
|
||||
devMenuContainer.appendChild(programsHeader);
|
||||
devMenuContainer.appendChild(programsAddDropdown);
|
||||
devMenuContainer.appendChild(programsAddButton);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Augmentations, AugmentationNames,
|
||||
PlayerOwnedAugmentation } from "../Augmentations";
|
||||
import { BitNodeMultipliers } from "../BitNodeMultipliers";
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { PlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { Engine } from "../engine";
|
||||
import { Faction } from "./Faction";
|
||||
@ -9,8 +10,8 @@ import { FactionInfos } from "./FactionInfo";
|
||||
import { Locations} from "../Location";
|
||||
import { HackingMission, setInMission } from "../Missions";
|
||||
import { Player } from "../Player";
|
||||
import { PurchaseAugmentationsOrderSetting } from "../SettingEnums";
|
||||
import { Settings } from "../Settings";
|
||||
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
|
||||
import {Page, routing} from "../ui/navigationTracking";
|
||||
import {numeralWrapper} from "../ui/numeralFormat";
|
||||
@ -480,7 +481,7 @@ function createFactionAugmentationDisplayElements(augmentationsList, augs, facti
|
||||
}
|
||||
|
||||
var item = createElement("li");
|
||||
var span = createElement("span", {display:"inline-block"});
|
||||
var span = createElement("span", { display:"inline-block", margin: "4px", padding: "4px" });
|
||||
var aDiv = createElement("div", {tooltip:aug.info});
|
||||
var aElem = createElement("a", {
|
||||
innerText:aug.name, display:"inline",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BitNodeMultipliers } from "./BitNodeMultipliers";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { Player } from "./Player";
|
||||
import { Server } from "./Server";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {BitNodeMultipliers} from "./BitNodeMultipliers";
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import {Engine} from "./engine";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Engine } from "./engine";
|
||||
import {iTutorialSteps, iTutorialNextStep,
|
||||
ITutorial} from "./InteractiveTutorial";
|
||||
import {Player} from "./Player";
|
||||
|
@ -1,12 +1,12 @@
|
||||
import {BitNodeMultipliers} from "./BitNodeMultipliers";
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import {Engine} from "./engine";
|
||||
import {Player} from "./Player";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
|
||||
import {getRandomInt} from "../utils/helpers/getRandomInt";
|
||||
import {infiltrationBoxCreate} from "../utils/InfiltrationBox";
|
||||
import {formatNumber} from "../utils/StringHelperFunctions";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Engine } from "./engine";
|
||||
import { Player } from "./Player";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { infiltrationBoxCreate } from "../utils/InfiltrationBox";
|
||||
import { formatNumber } from "../utils/StringHelperFunctions";
|
||||
|
||||
/* Infiltration.js
|
||||
*
|
||||
|
@ -1,10 +1,12 @@
|
||||
import {Engine} from "./engine";
|
||||
import {Player} from "./Player";
|
||||
import {Settings} from "./Settings";
|
||||
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
|
||||
import {createElement} from "../utils/uiHelpers/createElement";
|
||||
import {createPopup} from "../utils/uiHelpers/createPopup";
|
||||
import {removeElementById} from "../utils/uiHelpers/removeElementById";
|
||||
import { Engine } from "./engine";
|
||||
import { Player } from "./Player";
|
||||
import { Settings } from "./Settings/Settings";
|
||||
import { initializeMainMenuLinks } from "./ui/MainMenu/Links";
|
||||
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
|
||||
import { clearEventListeners } from "../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../utils/uiHelpers/createElement";
|
||||
import { createPopup } from "../utils/uiHelpers/createPopup";
|
||||
import { removeElementById } from "../utils/uiHelpers/removeElementById";
|
||||
|
||||
//Ordered array of keys to Interactive Tutorial Steps
|
||||
const orderedITutorialSteps = [
|
||||
@ -472,7 +474,19 @@ function iTutorialEnd() {
|
||||
}
|
||||
|
||||
console.log("Ending interactive tutorial");
|
||||
|
||||
// Initialize references to main menu links
|
||||
// We have to call initializeMainMenuLinks() again because the Interactive Tutorial
|
||||
// re-creates Main menu links with clearEventListeners()
|
||||
if (!initializeMainMenuLinks()) {
|
||||
const errorMsg = "Failed to initialize Main Menu Links. Please try refreshing the page. " +
|
||||
"If that doesn't work, report the issue to the developer";
|
||||
exceptionAlert(new Error(errorMsg));
|
||||
console.error(errorMsg);
|
||||
return;
|
||||
}
|
||||
Engine.init();
|
||||
|
||||
ITutorial.currStep = iTutorialSteps.End;
|
||||
ITutorial.isRunning = false;
|
||||
document.getElementById("interactive-tutorial-container").style.display = "none";
|
||||
|
135
src/Location.js
135
src/Location.js
@ -5,16 +5,18 @@ import {getJobRequirementText} from "./Company/GetJobRequiremen
|
||||
import * as posNames from "./Company/data/CompanyPositionNames";
|
||||
import { Corporation } from "./Corporation/Corporation";
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import {Crimes} from "./Crimes";
|
||||
import { Crimes } from "./Crime/Crimes";
|
||||
import {Engine} from "./engine";
|
||||
import {beginInfiltration} from "./Infiltration";
|
||||
import {hasBladeburnerSF} from "./NetscriptFunctions";
|
||||
import {Locations} from "./Locations";
|
||||
import {Player} from "./Player";
|
||||
import {Server, AllServers, AddToAllServers} from "./Server";
|
||||
import {purchaseServer,
|
||||
purchaseRamForHomeComputer} from "./ServerPurchases";
|
||||
import {Settings} from "./Settings";
|
||||
import { getPurchaseServerCost,
|
||||
purchaseServer,
|
||||
purchaseRamForHomeComputer} from "./ServerPurchases";
|
||||
import {Settings} from "./Settings/Settings";
|
||||
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps";
|
||||
|
||||
import {numeralWrapper} from "./ui/numeralFormat";
|
||||
@ -121,6 +123,8 @@ function displayLocationContent() {
|
||||
|
||||
var nsaBladeburner = document.getElementById("location-nsa-bladeburner");
|
||||
|
||||
const vitalifeResleeve = document.getElementById("location-vitalife-resleeve");
|
||||
|
||||
var loc = Player.location;
|
||||
|
||||
returnToWorld.addEventListener("click", function() {
|
||||
@ -188,16 +192,16 @@ function displayLocationContent() {
|
||||
purchaseHomeRam.style.display = "none";
|
||||
purchaseHomeCores.style.display = "none";
|
||||
|
||||
purchase2gb.innerHTML = "Purchase 2GB Server - $" + formatNumber(2*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase4gb.innerHTML = "Purchase 4GB Server - $" + formatNumber(4*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase8gb.innerHTML = "Purchase 8GB Server - $" + formatNumber(8*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase16gb.innerHTML = "Purchase 16GB Server - $" + formatNumber(16*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase32gb.innerHTML = "Purchase 32GB Server - $" + formatNumber(32*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase64gb.innerHTML = "Purchase 64GB Server - $" + formatNumber(64*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase128gb.innerHTML = "Purchase 128GB Server - $" + formatNumber(128*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase256gb.innerHTML = "Purchase 256GB Server - $" + formatNumber(256*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase512gb.innerHTML = "Purchase 512GB Server - $" + formatNumber(512*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase1tb.innerHTML = "Purchase 1TB Server - $" + formatNumber(1024*CONSTANTS.BaseCostFor1GBOfRamServer, 2);
|
||||
purchase2gb.innerHTML = "Purchase 2GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(2));
|
||||
purchase4gb.innerHTML = "Purchase 4GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(4));
|
||||
purchase8gb.innerHTML = "Purchase 8GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(8));
|
||||
purchase16gb.innerHTML = "Purchase 16GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(16));
|
||||
purchase32gb.innerHTML = "Purchase 32GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(32));
|
||||
purchase64gb.innerHTML = "Purchase 64GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(64));
|
||||
purchase128gb.innerHTML = "Purchase 128GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(128));
|
||||
purchase256gb.innerHTML = "Purchase 256GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(256));
|
||||
purchase512gb.innerHTML = "Purchase 512GB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(512));
|
||||
purchase1tb.innerHTML = "Purchase 1TB Server - " + numeralWrapper.formatMoney(getPurchaseServerCost(1024));
|
||||
if (!SpecialServerIps.hasOwnProperty("Darkweb Server")) {
|
||||
purchaseTor.classList.add("a-link-button");
|
||||
purchaseTor.classList.remove("a-link-button-bought");
|
||||
@ -237,10 +241,11 @@ function displayLocationContent() {
|
||||
|
||||
cityHallCreateCorporation.style.display = "none";
|
||||
nsaBladeburner.style.display = "none";
|
||||
vitalifeResleeve.style.display = "none";
|
||||
|
||||
//Check if the player is employed at this Location. If he is, display the "Work" button,
|
||||
//update the job title, etc.
|
||||
if (loc != "" && loc === Player.companyName) {
|
||||
if (loc != "" && Object.keys(Player.jobs).includes(loc)) {
|
||||
let company = Companies[loc];
|
||||
|
||||
jobTitle.style.display = "block";
|
||||
@ -249,7 +254,7 @@ function displayLocationContent() {
|
||||
locationTxtDiv1.style.display = "block";
|
||||
locationTxtDiv2.style.display = "block";
|
||||
locationTxtDiv3.style.display = "block";
|
||||
jobTitle.innerHTML = "Job Title: " + Player.companyPosition;
|
||||
jobTitle.innerHTML = `Job Title: ${Player.jobs[loc]}`;
|
||||
let repGain = company.getFavorGain();
|
||||
if (repGain.length != 2) {repGain = 0;}
|
||||
repGain = repGain[0];
|
||||
@ -264,16 +269,16 @@ function displayLocationContent() {
|
||||
"favor you gain depends on how much reputation you have with the company</span>";
|
||||
work.style.display = "block";
|
||||
|
||||
let currPos = CompanyPositions[Player.companyPosition];
|
||||
let currPos = CompanyPositions[Player.jobs[loc]];
|
||||
if (currPos == null) {
|
||||
throw new Error("Player's companyPosition property has an invalid value");
|
||||
}
|
||||
|
||||
work.addEventListener("click", function() {
|
||||
if (currPos.isPartTimeJob() || currPos.isSoftwareConsultantJob() || currPos.isBusinessConsultantJob()) {
|
||||
Player.startWorkPartTime();
|
||||
Player.startWorkPartTime(loc);
|
||||
} else {
|
||||
Player.startWork();
|
||||
Player.startWork(loc);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
@ -761,6 +766,10 @@ function displayLocationContent() {
|
||||
businessJob.style.display = "block";
|
||||
setInfiltrateButton(infiltrate, Locations.NewTokyoVitaLife,
|
||||
605, 22, 100, 3.5);
|
||||
if (Player.bitNodeN === 10 || SourceFileFlags[10]) {
|
||||
vitalifeResleeve.style.display = "block";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Locations.NewTokyoGlobalPharmaceuticals:
|
||||
@ -979,18 +988,18 @@ function displayLocationContent() {
|
||||
case Locations.NewTokyoSlums:
|
||||
case Locations.IshimaSlums:
|
||||
case Locations.VolhavenSlums:
|
||||
var shopliftChance = Crimes.Shoplift.successRate();
|
||||
var robStoreChance = Crimes.RobStore.successRate();
|
||||
var mugChance = Crimes.Mug.successRate();
|
||||
var larcenyChance = Crimes.Larceny.successRate();
|
||||
var drugsChance = Crimes.DealDrugs.successRate();
|
||||
var bondChance = Crimes.BondForgery.successRate();
|
||||
var armsChance = Crimes.TraffickArms.successRate();
|
||||
var homicideChance = Crimes.Homicide.successRate();
|
||||
var gtaChance = Crimes.GrandTheftAuto.successRate();
|
||||
var kidnapChance = Crimes.Kidnap.successRate();
|
||||
var assassinateChance = Crimes.Assassination.successRate();
|
||||
var heistChance = Crimes.Heist.successRate();
|
||||
var shopliftChance = Crimes.Shoplift.successRate(Player);
|
||||
var robStoreChance = Crimes.RobStore.successRate(Player);
|
||||
var mugChance = Crimes.Mug.successRate(Player);
|
||||
var larcenyChance = Crimes.Larceny.successRate(Player);
|
||||
var drugsChance = Crimes.DealDrugs.successRate(Player);
|
||||
var bondChance = Crimes.BondForgery.successRate(Player);
|
||||
var armsChance = Crimes.TraffickArms.successRate(Player);
|
||||
var homicideChance = Crimes.Homicide.successRate(Player);
|
||||
var gtaChance = Crimes.GrandTheftAuto.successRate(Player);
|
||||
var kidnapChance = Crimes.Kidnap.successRate(Player);
|
||||
var assassinateChance = Crimes.Assassination.successRate(Player);
|
||||
var heistChance = Crimes.Heist.successRate(Player);
|
||||
|
||||
slumsDescText.style.display = "block";
|
||||
slumsShoplift.style.display = "block";
|
||||
@ -1043,8 +1052,8 @@ function displayLocationContent() {
|
||||
|
||||
// Make the "Apply to be Employee and Waiter" texts disappear if you already hold the job
|
||||
// Includes part-time stuff
|
||||
if (loc == Player.companyName) {
|
||||
var currPos = Player.companyPosition;
|
||||
if (Object.keys(Player.jobs).includes(loc)) {
|
||||
var currPos = Player.jobs[loc];
|
||||
|
||||
if (currPos == "Employee") {
|
||||
employeeJob.style.display = "none";
|
||||
@ -1634,6 +1643,8 @@ function initLocationButtons() {
|
||||
|
||||
var nsaBladeburner = document.getElementById("location-nsa-bladeburner");
|
||||
|
||||
const vitalifeResleeve = document.getElementById("location-vitalife-resleeve");
|
||||
|
||||
var hospitalTreatment = document.getElementById("location-hospital-treatment");
|
||||
|
||||
softwareJob.addEventListener("click", function(e) {
|
||||
@ -1716,61 +1727,61 @@ function initLocationButtons() {
|
||||
|
||||
purchase2gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(2, 2 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(2);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase4gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(4, 4 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(4);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase8gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(8, 8 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(8);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase16gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(16, 16 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(16);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase32gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(32, 32 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(32);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase64gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(64, 64 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(64);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase128gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(128, 128 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(128);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase256gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(256, 256 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(256);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase512gb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(512, 512 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(512);
|
||||
return false;
|
||||
});
|
||||
|
||||
purchase1tb.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
purchaseServerBoxCreate(1024, 1024 * CONSTANTS.BaseCostFor1GBOfRamServer);
|
||||
purchaseServerBoxCreate(1024);
|
||||
return false;
|
||||
});
|
||||
|
||||
@ -1874,73 +1885,73 @@ function initLocationButtons() {
|
||||
|
||||
slumsShoplift.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.Shoplift.commit();
|
||||
Crimes.Shoplift.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsRobStore.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.RobStore.commit();
|
||||
Crimes.RobStore.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsMug.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.Mug.commit();
|
||||
Crimes.Mug.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsLarceny.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.Larceny.commit();
|
||||
Crimes.Larceny.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsDealDrugs.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.DealDrugs.commit();
|
||||
Crimes.DealDrugs.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsBondForgery.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.BondForgery.commit();
|
||||
Crimes.BondForgery.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsTrafficArms.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.TraffickArms.commit();
|
||||
Crimes.TraffickArms.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsHomicide.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.Homicide.commit();
|
||||
Crimes.Homicide.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsGta.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.GrandTheftAuto.commit();
|
||||
Crimes.GrandTheftAuto.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsKidnap.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.Kidnap.commit();
|
||||
Crimes.Kidnap.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsAssassinate.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.Assassination.commit();
|
||||
Crimes.Assassination.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
slumsHeist.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Crimes.Heist.commit();
|
||||
Crimes.Heist.commit(Player);
|
||||
return false;
|
||||
});
|
||||
|
||||
@ -2041,6 +2052,10 @@ function initLocationButtons() {
|
||||
}
|
||||
});
|
||||
|
||||
vitalifeResleeve.addEventListener("click", function() {
|
||||
Engine.loadResleevingContent();
|
||||
});
|
||||
|
||||
hospitalTreatment.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
if (Player.hp < 0) {Player.hp = 0;}
|
||||
@ -2250,13 +2265,19 @@ function travelBoxCreate(destCityName, cost) {
|
||||
yesNoBoxCreate("Would you like to travel to " + destCityName + "? The trip will cost $" + formatNumber(cost, 2) + ".");
|
||||
}
|
||||
|
||||
function purchaseServerBoxCreate(ram, cost) {
|
||||
function purchaseServerBoxCreate(ram) {
|
||||
const cost = getPurchaseServerCost(ram);
|
||||
if (cost === Infinity) {
|
||||
dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer");
|
||||
return;
|
||||
}
|
||||
|
||||
var yesBtn = yesNoTxtInpBoxGetYesButton();
|
||||
var noBtn = yesNoTxtInpBoxGetNoButton();
|
||||
yesBtn.innerHTML = "Purchase Server";
|
||||
noBtn.innerHTML = "Cancel";
|
||||
yesBtn.addEventListener("click", function() {
|
||||
purchaseServer(ram, cost);
|
||||
purchaseServer(ram);
|
||||
yesNoTxtInpBoxClose();
|
||||
});
|
||||
noBtn.addEventListener("click", function() {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Augmentations,
|
||||
Augmentation,
|
||||
AugmentationNames } from "./Augmentations";
|
||||
import { Augmentatation } from "./Augmentation/Augmentation";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import { inMission } from "./Missions";
|
||||
import { Player } from "./Player";
|
||||
import { redPillFlag } from "./RedPill";
|
||||
import { GetServerByHostname } from "./Server";
|
||||
import { Settings } from "./Settings";
|
||||
import { Settings } from "./Settings/Settings";
|
||||
import { dialogBoxCreate,
|
||||
dialogBoxOpened} from "../utils/DialogBox";
|
||||
import {Reviver, Generic_toJSON,
|
||||
|
@ -1,12 +1,12 @@
|
||||
import {BitNodeMultipliers} from "./BitNodeMultipliers";
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import {Player} from "./Player";
|
||||
import {Environment} from "./NetscriptEnvironment";
|
||||
import {WorkerScript, addWorkerScript} from "./NetscriptWorker";
|
||||
import {Server, getServer} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
import {Script, findRunningScript,
|
||||
RunningScript} from "./Script";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Player } from "./Player";
|
||||
import { Environment } from "./NetscriptEnvironment";
|
||||
import { WorkerScript, addWorkerScript} from "./NetscriptWorker";
|
||||
import { Server, getServer} from "./Server";
|
||||
import { Settings } from "./Settings/Settings";
|
||||
import { Script, findRunningScript,
|
||||
RunningScript } from "./Script";
|
||||
|
||||
import {parse, Node} from "../utils/acorn";
|
||||
import {arrayToString} from "../utils/helpers/arrayToString";
|
||||
@ -860,6 +860,7 @@ function runScriptFromScript(server, scriptname, args, workerScript, threads=1)
|
||||
var script = server.scripts[i];
|
||||
var ramUsage = script.ramUsage;
|
||||
threads = Math.round(Number(threads)); //Convert to number and round
|
||||
if (threads === 0) { return Promise.resolve(false); }
|
||||
ramUsage = ramUsage * threads;
|
||||
var ramAvailable = server.maxRam - server.ramUsed;
|
||||
|
||||
|
@ -2,11 +2,13 @@ var sprintf = require('sprintf-js').sprintf,
|
||||
vsprintf = require('sprintf-js').vsprintf
|
||||
|
||||
import {updateActiveScriptsItems} from "./ActiveScriptsUI";
|
||||
import {Augmentations, Augmentation,
|
||||
augmentationExists, installAugmentations,
|
||||
AugmentationNames} from "./Augmentations";
|
||||
import {BitNodeMultipliers} from "./BitNodeMultipliers";
|
||||
import {determineCrimeSuccess, findCrime} from "./Crimes";
|
||||
import { Augmentation } from "./Augmentation/Augmentation";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import { augmentationExists,
|
||||
installAugmentations } from "./Augmentation/AugmentationHelpers";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { determineCrimeSuccess, findCrime } from "./Crime/CrimeHelpers";
|
||||
import {Bladeburner} from "./Bladeburner";
|
||||
import {Company} from "./Company/Company";
|
||||
import {Companies, companyExists} from "./Company/Companies";
|
||||
@ -38,14 +40,17 @@ import {Script, findRunningScript, RunningScript,
|
||||
import {Server, getServer, AddToAllServers,
|
||||
AllServers, processSingleServerGrowth,
|
||||
GetServerByHostname, numCycleForGrowth} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
import { getPurchaseServerCost,
|
||||
getPurchaseServerLimit,
|
||||
getPurchaseServerMaxRam } from "./ServerPurchases";
|
||||
import {Settings} from "./Settings/Settings";
|
||||
import {SpecialServerIps} from "./SpecialServerIps";
|
||||
import {Stock} from "./Stock";
|
||||
import {Stock} from "./StockMarket/Stock";
|
||||
import {StockMarket, StockSymbols, SymbolToStockMap,
|
||||
initStockMarket, initSymbolToStockMap, buyStock,
|
||||
sellStock, updateStockPlayerPosition,
|
||||
shortStock, sellShort, OrderTypes,
|
||||
PositionTypes, placeOrder, cancelOrder} from "./StockMarket";
|
||||
PositionTypes, placeOrder, cancelOrder} from "./StockMarket/StockMarket";
|
||||
import {post} from "./ui/postToTerminal";
|
||||
import {TextFile, getTextFile, createTextFile} from "./TextFile";
|
||||
|
||||
@ -232,24 +237,6 @@ function NetscriptFunctions(workerScript) {
|
||||
return server.getContract(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} ram The amount of server RAM to calculate cost of.
|
||||
* @exception {Error} If the value passed in is not numeric, out of range, or too large of a value.
|
||||
* @returns {number} The cost of
|
||||
*/
|
||||
const getPurchaseServerRamCostGuard = (ram) => {
|
||||
const guardedRam = Math.round(ram);
|
||||
if (isNaN(guardedRam) || !isPowerOfTwo(guardedRam)) {
|
||||
throw Error("failed due to invalid ram argument. Must be numeric and a power of 2");
|
||||
}
|
||||
|
||||
if (guardedRam > CONSTANTS.PurchasedServerMaxRam) {
|
||||
throw Error("failed because specified RAM was too high. Maximum RAM on a purchased server is " + CONSTANTS.PurchasedServerMaxRam + "GB");
|
||||
}
|
||||
|
||||
return guardedRam * CONSTANTS.BaseCostFor1GBOfRamServer;
|
||||
};
|
||||
|
||||
return {
|
||||
hacknet : {
|
||||
numNodes : function() {
|
||||
@ -417,7 +404,7 @@ function NetscriptFunctions(workerScript) {
|
||||
// Check argument validity
|
||||
const server = safeGetServer(ip, 'hackAnalyzeThreads');
|
||||
if (isNaN(hackAmount)) {
|
||||
throw makeRuntimeRejectMsg(workerScript, `Invalid growth argument passed into growthAnalyze: ${hackAmount}. Must be numeric`);
|
||||
throw makeRuntimeRejectMsg(workerScript, `Invalid growth argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric`);
|
||||
}
|
||||
|
||||
if (hackAmount < 0 || hackAmount > server.moneyAvailable) {
|
||||
@ -819,7 +806,7 @@ function NetscriptFunctions(workerScript) {
|
||||
if (scriptname === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "run() call has incorrect number of arguments. Usage: run(scriptname, [numThreads], [arg1], [arg2]...)");
|
||||
}
|
||||
if (isNaN(threads) || threads < 1) {
|
||||
if (isNaN(threads) || threads < 0) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Invalid argument for thread count passed into run(). Must be numeric and greater than 0");
|
||||
}
|
||||
var argsForNewScript = [];
|
||||
@ -841,7 +828,7 @@ function NetscriptFunctions(workerScript) {
|
||||
if (scriptname === undefined || ip === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "exec() call has incorrect number of arguments. Usage: exec(scriptname, server, [numThreads], [arg1], [arg2]...)");
|
||||
}
|
||||
if (isNaN(threads) || threads < 1) {
|
||||
if (isNaN(threads) || threads < 0) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Invalid argument for thread count passed into exec(). Must be numeric and greater than 0");
|
||||
}
|
||||
var argsForNewScript = [];
|
||||
@ -866,7 +853,7 @@ function NetscriptFunctions(workerScript) {
|
||||
if (scriptname === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "spawn() call has incorrect number of arguments. Usage: spawn(scriptname, numThreads, [arg1], [arg2]...)");
|
||||
}
|
||||
if (isNaN(threads) || threads < 1) {
|
||||
if (isNaN(threads) || threads < 0) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Invalid argument for thread count passed into run(). Must be numeric and greater than 0");
|
||||
}
|
||||
var argsForNewScript = [];
|
||||
@ -1537,6 +1524,22 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
|
||||
},
|
||||
getStockMaxShares : function(symbol) {
|
||||
if (workerScript.checkingRam) {
|
||||
return updateStaticRam("getStockMaxShares", CONSTANTS.ScriptGetStockRamCost);
|
||||
}
|
||||
updateDynamicRam("getStockMaxShares", CONSTANTS.ScriptGetStockRamCost);
|
||||
|
||||
if (!Player.hasTixApiAccess) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "You don't have TIX API Access! Cannot use getStockMaxShares()");
|
||||
}
|
||||
const stock = SymbolToStockMap[symbol];
|
||||
if (stock == null) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Invalid stock symbol passed into getStockMaxShares()");
|
||||
}
|
||||
|
||||
return stock.maxShares;
|
||||
},
|
||||
buyStock : function(symbol, shares) {
|
||||
if (workerScript.checkingRam) {
|
||||
return updateStaticRam("buyStock", CONSTANTS.ScriptBuySellStockRamCost);
|
||||
@ -1556,6 +1559,7 @@ function NetscriptFunctions(workerScript) {
|
||||
shares = Math.round(shares);
|
||||
if (shares === 0) {return 0;}
|
||||
|
||||
// Does player have enough money?
|
||||
var totalPrice = stock.price * shares;
|
||||
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
|
||||
workerScript.scriptRef.log("Not enough money to purchase " + formatNumber(shares, 0) + " shares of " +
|
||||
@ -1564,6 +1568,13 @@ function NetscriptFunctions(workerScript) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Would this purchase exceed the maximum number of shares?
|
||||
if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) {
|
||||
workerScript.scriptRef.log(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ` +
|
||||
`${stock.maxShares} shares.`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var origTotal = stock.playerShares * stock.playerAvgPx;
|
||||
Player.loseMoney(totalPrice + CONSTANTS.StockMarketCommission);
|
||||
var newTotal = origTotal + totalPrice;
|
||||
@ -1885,7 +1896,7 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
updateDynamicRam("getPurchasedServerLimit", CONSTANTS.ScriptGetPurchasedServerLimit);
|
||||
|
||||
return CONSTANTS.PurchasedServerLimit;
|
||||
return getPurchaseServerLimit();
|
||||
},
|
||||
getPurchasedServerMaxRam: function() {
|
||||
if (workerScript.checkingRam) {
|
||||
@ -1893,7 +1904,7 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
updateDynamicRam("getPurchasedServerMaxRam", CONSTANTS.ScriptGetPurchasedServerMaxRam);
|
||||
|
||||
return CONSTANTS.PurchasedServerMaxRam;
|
||||
return getPurchaseServerMaxRam();
|
||||
},
|
||||
getPurchasedServerCost: function(ram) {
|
||||
if (workerScript.checkingRam) {
|
||||
@ -1901,11 +1912,9 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
updateDynamicRam("getPurchasedServerCost", CONSTANTS.ScriptGetPurchaseServerRamCost);
|
||||
|
||||
let cost = 0;
|
||||
try {
|
||||
cost = getPurchaseServerRamCostGuard(ram);
|
||||
} catch (e) {
|
||||
workerScript.scriptRef.log("ERROR: 'getPurchasedServerCost()' " + e.message);
|
||||
const cost = getPurchaseServerCost(ram);
|
||||
if (cost === Infinity) {
|
||||
workerScript.scriptRef.log("ERROR: 'getPurchasedServerCost()' failed due to an invalid 'ram' argument");
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
@ -1919,26 +1928,23 @@ function NetscriptFunctions(workerScript) {
|
||||
var hostnameStr = String(hostname);
|
||||
hostnameStr = hostnameStr.replace(/\s+/g, '');
|
||||
if (hostnameStr == "") {
|
||||
workerScript.scriptRef.log("ERROR: Passed empty string for hostname argument of purchaseServer()");
|
||||
workerScript.log("ERROR: Passed empty string for hostname argument of purchaseServer()");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (Player.purchasedServers.length >= CONSTANTS.PurchasedServerLimit) {
|
||||
workerScript.scriptRef.log("ERROR: You have reached the maximum limit of " + CONSTANTS.PurchasedServerLimit +
|
||||
" servers. You cannot purchase any more.");
|
||||
if (Player.purchasedServers.length >= getPurchaseServerLimit()) {
|
||||
workerScript.log(`ERROR: You have reached the maximum limit of ${getPurchaseServerLimit()} servers. You cannot purchase any more.`);
|
||||
return "";
|
||||
}
|
||||
|
||||
let cost = 0;
|
||||
try {
|
||||
cost = getPurchaseServerRamCostGuard(ram);
|
||||
} catch (e) {
|
||||
workerScript.scriptRef.log("ERROR: 'purchaseServer()' " + e.message);
|
||||
return "";
|
||||
const cost = getPurchaseServerCost(ram);
|
||||
if (cost === Infinity) {
|
||||
workerScript.log("ERROR: 'purchaseServer()' failed due to an invalid 'ram' argument");
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
if (Player.money.lt(cost)) {
|
||||
workerScript.scriptRef.log("ERROR: Not enough money to purchase server. Need $" + formatNumber(cost, 2));
|
||||
workerScript.log("ERROR: Not enough money to purchase server. Need $" + formatNumber(cost, 2));
|
||||
return "";
|
||||
}
|
||||
var newServ = new Server({
|
||||
@ -2878,16 +2884,12 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
}
|
||||
|
||||
var companyPositionTitle = "";
|
||||
if (CompanyPositions[Player.companyPosition] instanceof CompanyPosition) {
|
||||
companyPositionTitle = Player.companyPosition;
|
||||
}
|
||||
return {
|
||||
bitnode: Player.bitNodeN,
|
||||
city: Player.city,
|
||||
company: Player.companyName,
|
||||
factions: Player.factions.slice(),
|
||||
jobTitle: companyPositionTitle,
|
||||
jobs: Object.keys(Player.jobs),
|
||||
jobTitles: Object.values(Player.jobs),
|
||||
mult: {
|
||||
agility: Player.agility_mult,
|
||||
agilityExp: Player.agility_exp_mult,
|
||||
@ -2968,16 +2970,20 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
}
|
||||
|
||||
const cost = Player.getUpgradeHomeRamCost();
|
||||
// Check if we're at max RAM
|
||||
const homeComputer = Player.getHomeComputer();
|
||||
if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) {
|
||||
workerScript.log(`ERROR: upgradeHomeRam() failed because your home computer is at max RAM`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const cost = Player.getUpgradeHomeRamCost();
|
||||
if (Player.money.lt(cost)) {
|
||||
workerScript.scriptRef.log("ERROR: upgradeHomeRam() failed because you don't have enough money");
|
||||
return false;
|
||||
}
|
||||
|
||||
var homeComputer = Player.getHomeComputer();
|
||||
homeComputer.maxRam *= 2;
|
||||
|
||||
Player.loseMoney(cost);
|
||||
|
||||
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
|
||||
@ -3002,7 +3008,7 @@ function NetscriptFunctions(workerScript) {
|
||||
|
||||
return Player.getUpgradeHomeRamCost();
|
||||
},
|
||||
workForCompany : function() {
|
||||
workForCompany : function(companyName) {
|
||||
var ramCost = CONSTANTS.ScriptSingularityFn2RamCost;
|
||||
if (Player.bitNodeN !== 4) {ramCost *= CONSTANTS.ScriptSingularityFnRamMult;}
|
||||
if (workerScript.checkingRam) {
|
||||
@ -3016,13 +3022,33 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
}
|
||||
|
||||
if (inMission) {
|
||||
workerScript.scriptRef.log("ERROR: workForCompany() failed because you are in the middle of a mission.");
|
||||
return;
|
||||
// Sanitize input
|
||||
if (companyName == null) {
|
||||
companyName = Player.companyName;
|
||||
}
|
||||
|
||||
const companyPosition = CompanyPositions[Player.companyPosition];
|
||||
if (Player.companyPosition === "" || !(companyPosition instanceof CompanyPosition)) {
|
||||
// Make sure its a valid company
|
||||
if (companyName == null || companyName === "" || !(Companies[companyName] instanceof Company)) {
|
||||
workerScript.scriptRef.log(`ERROR: workForCompany() failed because of an invalid company specified: ${companyName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure player is actually employed at the comapny
|
||||
if (!Object.keys(Player.jobs).includes(companyName)) {
|
||||
workerScript.scriptRef.log(`ERROR: workForCompany() failed because you do not have a job at ${companyName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cant work while in a mission
|
||||
if (inMission) {
|
||||
workerScript.scriptRef.log("ERROR: workForCompany() failed because you are in the middle of a mission.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to make sure company position data is valid
|
||||
const companyPositionName = Player.jobs[companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (companyPositionName === "" || !(companyPosition instanceof CompanyPosition)) {
|
||||
workerScript.scriptRef.log("ERROR: workForCompany() failed because you do not have a job");
|
||||
return false;
|
||||
}
|
||||
@ -3035,12 +3061,12 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
|
||||
if (companyPosition.isPartTimeJob()) {
|
||||
Player.startWorkPartTime();
|
||||
Player.startWorkPartTime(companyName);
|
||||
} else {
|
||||
Player.startWork();
|
||||
Player.startWork(companyName);
|
||||
}
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.workForCompany == null) {
|
||||
workerScript.log(`Began working at ${Player.companyName} as a ${Player.companyPosition}`);
|
||||
workerScript.log(`Began working at ${Player.companyName} as a ${companyPositionName}`);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@ -3116,7 +3142,7 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
if (res) {
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.applyToCompany == null) {
|
||||
workerScript.log(`You were offered a new job at ${companyName} as a ${Player.companyPosition}`);
|
||||
workerScript.log(`You were offered a new job at ${companyName} as a ${Player.jobs[companyName]}`);
|
||||
}
|
||||
} else {
|
||||
if (workerScript.disableLogs.ALL == null && workerScript.disableLogs.applyToCompany == null) {
|
||||
@ -3554,7 +3580,7 @@ function NetscriptFunctions(workerScript) {
|
||||
if(workerScript.disableLogs.ALL == null && workerScript.disableLogs.commitCrime == null) {
|
||||
workerScript.scriptRef.log("Attempting to commit crime: "+crime.name+"...");
|
||||
}
|
||||
return crime.commit(1, {workerscript: workerScript});
|
||||
return crime.commit(Player, 1, {workerscript: workerScript});
|
||||
},
|
||||
getCrimeChance : function(crimeRoughName) {
|
||||
var ramCost = CONSTANTS.ScriptSingularityFn3RamCost;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Settings} from "./Settings";
|
||||
import {Settings} from "./Settings/Settings";
|
||||
|
||||
function NetscriptPort() {
|
||||
this.data = [];
|
||||
|
@ -12,7 +12,7 @@ import {NetscriptFunctions} from "./NetscriptFunctions";
|
||||
import {executeJSScript} from "./NetscriptJSEvaluator";
|
||||
import {NetscriptPort} from "./NetscriptPort";
|
||||
import {AllServers} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
import {Settings} from "./Settings/Settings";
|
||||
|
||||
import {generate} from 'escodegen';
|
||||
|
||||
@ -347,6 +347,9 @@ function processNetscript1Imports(code, workerScript) {
|
||||
ImportDeclaration: (node) => {
|
||||
hasImports = true;
|
||||
let scriptName = node.source.value;
|
||||
if (scriptName.startsWith("./")) {
|
||||
scriptName = scriptName.slice(2);
|
||||
}
|
||||
let script = getScript(scriptName);
|
||||
if (script == null) {
|
||||
throw new Error("'Import' failed due to invalid script: " + scriptName);
|
||||
@ -496,19 +499,6 @@ function runScriptsLoop() {
|
||||
} else {
|
||||
p = startNetscript1Script(workerScripts[i]);
|
||||
if (!(p instanceof Promise)) {continue;}
|
||||
/*
|
||||
try {
|
||||
var ast = parse(workerScripts[i].code, {sourceType:"module"});
|
||||
//console.log(ast);
|
||||
} catch (e) {
|
||||
console.log("Error parsing script: " + workerScripts[i].name);
|
||||
dialogBoxCreate("Syntax ERROR in " + workerScripts[i].name + ":<br>" + e);
|
||||
workerScripts[i].env.stopFlag = true;
|
||||
continue;
|
||||
}
|
||||
workerScripts[i].running = true;
|
||||
p = evaluate(ast, workerScripts[i]);
|
||||
*/
|
||||
}
|
||||
|
||||
//Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
@ -521,7 +511,7 @@ function runScriptsLoop() {
|
||||
}).catch(function(w) {
|
||||
if (w instanceof Error) {
|
||||
dialogBoxCreate("Script runtime unknown error. This is a bug please contact game developer");
|
||||
console.log("ERROR: Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
console.error("Evaluating workerscript returns an Error. THIS SHOULDN'T HAPPEN: " + w.toString());
|
||||
return;
|
||||
} else if (w.constructor === Array && w.length === 2 && w[0] === "RETURNSTATEMENT") {
|
||||
//Script ends with a return statement
|
||||
|
74
src/PersonObjects/IPlayer.ts
Normal file
74
src/PersonObjects/IPlayer.ts
Normal file
@ -0,0 +1,74 @@
|
||||
// Interface for an object that represents the player (PlayerObject)
|
||||
// Used because at the time of implementation, the PlayerObject
|
||||
// cant be converted to TypeScript.
|
||||
//
|
||||
// Only contains the needed properties for Sleeve implementation
|
||||
import { Resleeve } from "./Resleeving/Resleeve";
|
||||
import { Sleeve } from "./Sleeve/Sleeve";
|
||||
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { IPlayerOwnedSourceFile } from "../SourceFile/PlayerOwnedSourceFile";
|
||||
|
||||
export interface IPlayer {
|
||||
// Class members
|
||||
augmentations: IPlayerOwnedAugmentation[];
|
||||
bladeburner: any;
|
||||
companyName: string;
|
||||
corporation: any;
|
||||
factions: string[];
|
||||
hasWseAccount: boolean;
|
||||
jobs: IMap<string>;
|
||||
money: any;
|
||||
queuedAugmentations: IPlayerOwnedAugmentation[];
|
||||
resleeves: Resleeve[];
|
||||
sleeves: Sleeve[];
|
||||
sourceFiles: IPlayerOwnedSourceFile[];
|
||||
|
||||
// Stats
|
||||
hacking_skill: number;
|
||||
strength: number;
|
||||
defense: number;
|
||||
dexterity: number;
|
||||
agility: number;
|
||||
charisma: number;
|
||||
intelligence: number;
|
||||
|
||||
// Experience
|
||||
hacking_exp: number;
|
||||
strength_exp: number;
|
||||
defense_exp: number;
|
||||
dexterity_exp: number;
|
||||
agility_exp: number;
|
||||
charisma_exp: number;
|
||||
|
||||
// Multipliers
|
||||
crime_success_mult: number;
|
||||
|
||||
// Methods
|
||||
canAfford(cost: number): boolean;
|
||||
gainHackingExp(exp: number): void;
|
||||
gainStrengthExp(exp: number): void;
|
||||
gainDefenseExp(exp: number): void;
|
||||
gainDexterityExp(exp: number): void;
|
||||
gainAgilityExp(exp: number): void;
|
||||
gainCharismaExp(exp: number): void;
|
||||
gainMoney(money: number): void;
|
||||
hasCorporation(): boolean;
|
||||
inBladeburner(): boolean;
|
||||
inGang(): boolean;
|
||||
loseMoney(money: number): void;
|
||||
reapplyAllAugmentations(resetMultipliers: boolean): void;
|
||||
reapplyAllSourceFiles(): void;
|
||||
startCrime(crimeType: string,
|
||||
hackExp: number,
|
||||
strExp: number,
|
||||
defExp: number,
|
||||
dexExp: number,
|
||||
agiExp: number,
|
||||
chaExp: number,
|
||||
money: number,
|
||||
time: number,
|
||||
singParams: any): void;
|
||||
}
|
@ -1,63 +1,11 @@
|
||||
// Base class representing a person-like object
|
||||
import { BitNodeMultipliers } from "../BitNodeMultipliers";
|
||||
import { Augmentation } from "../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../Augmentation/Augmentations";
|
||||
import { IPlayerOwnedAugmentation } from "../Augmentation/PlayerOwnedAugmentation";
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { Cities } from "../Locations/Cities";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
// Interface for an object that represents the player (PlayerObject)
|
||||
// Used because at the time of implementation, the PlayerObject
|
||||
// cant be converted to TypeScript.
|
||||
//
|
||||
// Only contains the needed properties for Sleeve implementation
|
||||
export interface IPlayer {
|
||||
companyName: string;
|
||||
factions: string[];
|
||||
money: any;
|
||||
gainHackingExp(exp: number): void;
|
||||
gainStrengthExp(exp: number): void;
|
||||
gainDefenseExp(exp: number): void;
|
||||
gainDexterityExp(exp: number): void;
|
||||
gainAgilityExp(exp: number): void;
|
||||
gainCharismaExp(exp: number): void;
|
||||
gainMoney(money: number): void;
|
||||
loseMoney(money: number): void;
|
||||
}
|
||||
|
||||
// Interface for a Crime object
|
||||
// Used because at the time of implementation, the Crime object has not been converted
|
||||
// to Typescript
|
||||
export interface ICrime {
|
||||
name: string;
|
||||
type: string;
|
||||
time: number;
|
||||
money: number;
|
||||
difficulty: number;
|
||||
karma: number;
|
||||
|
||||
hacking_success_weight: number;
|
||||
strength_success_weight: number;
|
||||
defense_success_weight: number;
|
||||
dexterity_success_weight: number;
|
||||
agility_success_weight: number;
|
||||
charisma_success_weight: number;
|
||||
|
||||
hacking_exp: number;
|
||||
strength_exp: number;
|
||||
defense_exp: number;
|
||||
dexterity_exp: number;
|
||||
agility_exp: number;
|
||||
charisma_exp: number;
|
||||
intelligence_exp: number;
|
||||
|
||||
kills: number;
|
||||
}
|
||||
|
||||
// Interface for Faction object
|
||||
// Used because at the time of implementation, the Faction object has not been
|
||||
// converted to TypeScript
|
||||
export interface IFaction {
|
||||
name: string;
|
||||
playerReputation: number;
|
||||
}
|
||||
import { IMap } from "../types";
|
||||
|
||||
// Interface that defines a generic object used to track experience/money
|
||||
// earnings for tasks
|
||||
@ -87,95 +35,91 @@ export abstract class Person {
|
||||
/**
|
||||
* Stats
|
||||
*/
|
||||
hacking_skill: number;
|
||||
strength: number;
|
||||
defense: number;
|
||||
dexterity: number;
|
||||
agility: number;
|
||||
charisma: number;
|
||||
hp: number;
|
||||
max_hp: number;
|
||||
hacking_skill: number = 1;
|
||||
strength: number = 1;
|
||||
defense: number = 1;
|
||||
dexterity: number = 1;
|
||||
agility: number = 1;
|
||||
charisma: number = 1;
|
||||
hp: number = 10;
|
||||
max_hp: number = 10;
|
||||
|
||||
/**
|
||||
* Experience
|
||||
*/
|
||||
hacking_exp: number = 0;
|
||||
strength_exp: number = 0;
|
||||
defense_exp: number = 0;
|
||||
dexterity_exp: number = 0;
|
||||
agility_exp: number = 0;
|
||||
charisma_exp: number = 0;
|
||||
intelligence_exp: number = 0;
|
||||
|
||||
/**
|
||||
* Multipliers
|
||||
*/
|
||||
hacking_exp: number;
|
||||
strength_exp: number;
|
||||
defense_exp: number;
|
||||
dexterity_exp: number;
|
||||
agility_exp: number;
|
||||
charisma_exp: number;
|
||||
intelligence_exp: number;
|
||||
hacking_mult: number = 1;
|
||||
strength_mult: number = 1;
|
||||
defense_mult: number = 1;
|
||||
dexterity_mult: number = 1;
|
||||
agility_mult: number = 1;
|
||||
charisma_mult: number = 1;
|
||||
|
||||
hacking_mult: number;
|
||||
strength_mult: number;
|
||||
defense_mult: number;
|
||||
dexterity_mult: number;
|
||||
agility_mult: number;
|
||||
charisma_mult: number;
|
||||
hacking_exp_mult: number = 1;
|
||||
strength_exp_mult: number = 1;
|
||||
defense_exp_mult: number = 1;
|
||||
dexterity_exp_mult: number = 1;
|
||||
agility_exp_mult: number = 1;
|
||||
charisma_exp_mult: number = 1;
|
||||
|
||||
hacking_exp_mult: number;
|
||||
strength_exp_mult: number;
|
||||
defense_exp_mult: number;
|
||||
dexterity_exp_mult: number;
|
||||
agility_exp_mult: number;
|
||||
charisma_exp_mult: number;
|
||||
hacking_chance_mult: number = 1;
|
||||
hacking_speed_mult: number = 1;
|
||||
hacking_money_mult: number = 1;
|
||||
hacking_grow_mult: number = 1;
|
||||
|
||||
company_rep_mult: number;
|
||||
faction_rep_mult: number;
|
||||
company_rep_mult: number = 1;
|
||||
faction_rep_mult: number = 1;
|
||||
|
||||
crime_money_mult: number;
|
||||
crime_success_mult: number;
|
||||
crime_money_mult: number = 1;
|
||||
crime_success_mult: number = 1;
|
||||
|
||||
work_money_mult: number;
|
||||
work_money_mult: number = 1;
|
||||
|
||||
hacknet_node_money_mult: number = 1;
|
||||
hacknet_node_purchase_cost_mult: number = 1;
|
||||
hacknet_node_ram_cost_mult: number = 1;
|
||||
hacknet_node_core_cost_mult: number = 1;
|
||||
hacknet_node_level_cost_mult: number = 1;
|
||||
|
||||
bladeburner_max_stamina_mult: number = 1;
|
||||
bladeburner_stamina_gain_mult: number = 1;
|
||||
bladeburner_analysis_mult: number = 1;
|
||||
bladeburner_success_chance_mult : number = 1;
|
||||
|
||||
/**
|
||||
* Augmentations
|
||||
*/
|
||||
augmentations: IPlayerOwnedAugmentation[] = [];
|
||||
queuedAugmentations: IPlayerOwnedAugmentation[] = [];
|
||||
|
||||
/**
|
||||
* City that the person is in
|
||||
*/
|
||||
city: string;
|
||||
city: string = Cities.Sector12;
|
||||
|
||||
constructor() {
|
||||
this.hacking_skill = 1;
|
||||
this.strength = 1;
|
||||
this.defense = 1;
|
||||
this.dexterity = 1;
|
||||
this.agility = 1;
|
||||
this.charisma = 1;
|
||||
this.hp = 10;
|
||||
this.max_hp = 10;
|
||||
constructor() {}
|
||||
|
||||
// Multipliers
|
||||
this.hacking_exp = 0;
|
||||
this.strength_exp = 0;
|
||||
this.defense_exp = 0;
|
||||
this.dexterity_exp = 0;
|
||||
this.agility_exp = 0;
|
||||
this.charisma_exp = 0;
|
||||
this.intelligence_exp = 0;
|
||||
|
||||
this.hacking_mult = 1;
|
||||
this.strength_mult = 1;
|
||||
this.defense_mult = 1;
|
||||
this.dexterity_mult = 1;
|
||||
this.agility_mult = 1;
|
||||
this.charisma_mult = 1;
|
||||
|
||||
this.hacking_exp_mult = 1;
|
||||
this.strength_exp_mult = 1;
|
||||
this.defense_exp_mult = 1;
|
||||
this.dexterity_exp_mult = 1;
|
||||
this.agility_exp_mult = 1;
|
||||
this.charisma_exp_mult = 1;
|
||||
|
||||
this.company_rep_mult = 1;
|
||||
this.faction_rep_mult = 1;
|
||||
|
||||
this.crime_money_mult = 1;
|
||||
this.crime_success_mult = 1;
|
||||
|
||||
this.work_money_mult = 1;
|
||||
|
||||
this.city = Cities.Sector12;
|
||||
/**
|
||||
* Updates this object's multipliers for the given augmentation
|
||||
*/
|
||||
applyAugmentation(aug: Augmentation) {
|
||||
for (const mult in aug.mults) {
|
||||
if ((<any>this)[mult] == null) {
|
||||
console.warn(`Augmentation has unrecognized multiplier property: ${mult}`);
|
||||
} else {
|
||||
(<any>this)[mult] *= aug.mults[mult];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,11 +199,11 @@ export abstract class Person {
|
||||
*/
|
||||
updateStatLevels(): void {
|
||||
this.hacking_skill = Math.max(1, Math.floor(this.calculateStat(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier)));
|
||||
this.strength = Math.max(1, Math.floor(this.calculateStat(this.strength_exp, this.strength_mult)));
|
||||
this.defense = Math.max(1, Math.floor(this.calculateStat(this.defense_exp, this.defense_mult)));
|
||||
this.dexterity = Math.max(1, Math.floor(this.calculateStat(this.dexterity_exp, this.dexterity_mult)));
|
||||
this.agility = Math.max(1, Math.floor(this.calculateStat(this.agility_exp, this.agility_mult)));
|
||||
this.charisma = Math.max(1, Math.floor(this.calculateStat(this.charisma_exp, this.charisma_mult)));
|
||||
this.strength = Math.max(1, Math.floor(this.calculateStat(this.strength_exp, this.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier)));
|
||||
this.defense = Math.max(1, Math.floor(this.calculateStat(this.defense_exp, this.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier)));
|
||||
this.dexterity = Math.max(1, Math.floor(this.calculateStat(this.dexterity_exp, this.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier)));
|
||||
this.agility = Math.max(1, Math.floor(this.calculateStat(this.agility_exp, this.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier)));
|
||||
this.charisma = Math.max(1, Math.floor(this.calculateStat(this.charisma_exp, this.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier)));
|
||||
|
||||
const ratio: number = this.hp / this.max_hp;
|
||||
this.max_hp = Math.floor(10 + this.defense / 10);
|
||||
|
10
src/PersonObjects/Resleeving/README.md
Normal file
10
src/PersonObjects/Resleeving/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
Implements the Re-sleeving feature, which allows players to purchase a new body
|
||||
that comes with pre-existing Augmentations and experience. Note that purchasing
|
||||
a new body causes you to lose all of your old Augmentations and experience
|
||||
|
||||
This feature is introduced in BitNode-10, and destroying BitNode-10 allows
|
||||
the user to use it in other BitNodes (provided that they purchase the required
|
||||
cortical stack Augmentation)
|
||||
|
||||
While they are based on the same concept, this feature is different than the
|
||||
"Duplicate Sleeve" mechanic (which is referred to as just "Sleeve" in the source code).
|
61
src/PersonObjects/Resleeving/Resleeve.ts
Normal file
61
src/PersonObjects/Resleeving/Resleeve.ts
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Implements the Resleeve class, which defines a new body
|
||||
* that the player can "re-sleeve" into.
|
||||
*/
|
||||
import { Person } from "../Person";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../../utils/JSONReviver";
|
||||
|
||||
export class Resleeve extends Person {
|
||||
/**
|
||||
* Initiatizes a Resleeve object from a JSON save state.
|
||||
*/
|
||||
static fromJSON(value: any): Resleeve {
|
||||
return Generic_fromJSON(Resleeve, value.data);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
getCost(): number {
|
||||
// Each experience point adds this to the cost
|
||||
const CostPerExp: number = 25e3;
|
||||
|
||||
// Final cost is multiplied by this constant ^ # Augs
|
||||
const NumAugsExponent: number = 1.2;
|
||||
|
||||
// Get total exp in this re-sleeve
|
||||
let totalExp: number = this.hacking_exp +
|
||||
this.strength_exp +
|
||||
this.defense_exp +
|
||||
this.dexterity_exp +
|
||||
this.agility_exp +
|
||||
this.charisma_exp;
|
||||
|
||||
// Get total base Augmentation cost for this re-sleeve
|
||||
let totalAugmentationCost: number = 0;
|
||||
for (let i = 0; i < this.augmentations.length; ++i) {
|
||||
const aug: Augmentation | null = Augmentations[this.augmentations[i].name];
|
||||
if (aug == null) {
|
||||
console.error(`Could not find Augmentation ${this.augmentations[i].name}`);
|
||||
continue;
|
||||
}
|
||||
totalAugmentationCost += aug!.baseCost;
|
||||
}
|
||||
|
||||
return (totalExp * CostPerExp) + (totalAugmentationCost * Math.pow(NumAugsExponent, this.augmentations.length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the current object to a JSON save state.
|
||||
*/
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("Resleeve", this);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.Resleeve = Resleeve;
|
120
src/PersonObjects/Resleeving/Resleeving.ts
Normal file
120
src/PersonObjects/Resleeving/Resleeving.ts
Normal file
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Implements the Re-sleeving mechanic for BitNode-10.
|
||||
* This allows the player to purchase and "use" new sleeves at VitaLife.
|
||||
* These new sleeves come with different starting experience and Augmentations
|
||||
* The cost of these new sleeves scales based on the exp and Augs.
|
||||
*
|
||||
* Note that this is different from the "Sleeve mechanic". The "Sleeve" mechanic
|
||||
* provides new sleeves, essentially clones. This Re-sleeving mechanic lets
|
||||
* the player purchase a new body with pre-existing Augmentations and experience
|
||||
*
|
||||
* As of right now, this feature is only available in BitNode 10
|
||||
*/
|
||||
import { Resleeve } from "./Resleeve";
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { IPlayerOwnedAugmentation,
|
||||
PlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
|
||||
import { getRandomInt } from "../../../utils/helpers/getRandomInt";
|
||||
|
||||
|
||||
// Executes the actual re-sleeve when one is purchased
|
||||
export function purchaseResleeve(r: Resleeve, p: IPlayer): boolean {
|
||||
const cost: number = r.getCost();
|
||||
if (!p.canAfford(cost)) {
|
||||
return false;
|
||||
}
|
||||
p.loseMoney(cost);
|
||||
|
||||
// Set the player's exp
|
||||
p.hacking_exp = r.hacking_exp;
|
||||
p.strength_exp = r.strength_exp;
|
||||
p.defense_exp = r.defense_exp;
|
||||
p.dexterity_exp = r.dexterity_exp;
|
||||
p.agility_exp = r.agility_exp;
|
||||
p.charisma_exp = r.charisma_exp;
|
||||
|
||||
// Reset Augmentation "owned" data
|
||||
for (const augKey in Augmentations) {
|
||||
Augmentations[augKey].owned = false;
|
||||
}
|
||||
|
||||
// Clear all of the player's augmentations, except the NeuroFlux Governor
|
||||
// which is kept
|
||||
for (let i = p.augmentations.length - 1; i >= 0; --i) {
|
||||
if (p.augmentations[i].name !== AugmentationNames.NeuroFluxGovernor) {
|
||||
p.augmentations.splice(i, 1);
|
||||
} else {
|
||||
// NeuroFlux Governor
|
||||
Augmentations[AugmentationNames.NeuroFluxGovernor].owned = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < r.augmentations.length; ++i) {
|
||||
p.augmentations.push(new PlayerOwnedAugmentation(r.augmentations[i].name));
|
||||
Augmentations[r.augmentations[i].name].owned = true;
|
||||
}
|
||||
|
||||
// The player's purchased Augmentations should remain the same, but any purchased
|
||||
// Augmentations that are given by the resleeve should be removed so there are no duplicates
|
||||
for (let i = p.queuedAugmentations.length - 1; i >= 0; --i) {
|
||||
const name: string = p.queuedAugmentations[i].name;
|
||||
|
||||
if (p.augmentations.filter((e: IPlayerOwnedAugmentation) => {return e.name !== AugmentationNames.NeuroFluxGovernor && e.name === name}).length >= 1) {
|
||||
p.queuedAugmentations.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
p.reapplyAllAugmentations(true);
|
||||
p.reapplyAllSourceFiles(); //Multipliers get reset, so have to re-process source files too
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates all of the Re-sleeves that will be available for purchase at VitaLife
|
||||
export function generateResleeves(): Resleeve[] {
|
||||
const NumResleeves: number = 40; // Total number of Resleeves to generate
|
||||
|
||||
let ret: Resleeve[] = [];
|
||||
for (let i = 0; i < NumResleeves; ++i) {
|
||||
// i will be a number indicating how "powerful" the Re-sleeve should be
|
||||
let r: Resleeve = new Resleeve();
|
||||
|
||||
// Generate experience
|
||||
const expMult: number = (5 * i) + 1;
|
||||
r.hacking_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.strength_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.defense_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.dexterity_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.agility_exp = expMult * getRandomInt(1000, 5000);
|
||||
r.charisma_exp = expMult * getRandomInt(1000, 5000);
|
||||
|
||||
// Generate Augs
|
||||
// Augmentation prequisites will be ignored for this
|
||||
const baseNumAugs: number = Math.max(2, Math.ceil((i + 3) / 2));
|
||||
const numAugs: number = getRandomInt(baseNumAugs, baseNumAugs + 2);
|
||||
const augKeys: string[] = Object.keys(Augmentations);
|
||||
for (let a = 0; a < numAugs; ++a) {
|
||||
// Get a random aug
|
||||
const randIndex: number = getRandomInt(0, augKeys.length - 1)
|
||||
const randKey: string = augKeys[randIndex];
|
||||
if (randKey === AugmentationNames.TheRedPill) {
|
||||
continue; // A sleeve can't have The Red Pill
|
||||
}
|
||||
const randAug: Augmentation | null = Augmentations[randKey];
|
||||
r.augmentations.push({name: randAug!.name, level: 1});
|
||||
r.applyAugmentation(Augmentations[randKey]);
|
||||
r.updateStatLevels();
|
||||
|
||||
// Remove Augmentation so that there are no duplicates
|
||||
augKeys.splice(randIndex, 1);
|
||||
}
|
||||
|
||||
ret.push(r);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
360
src/PersonObjects/Resleeving/ResleevingUI.ts
Normal file
360
src/PersonObjects/Resleeving/ResleevingUI.ts
Normal file
@ -0,0 +1,360 @@
|
||||
/**
|
||||
* Module for handling the Re-sleeving UI
|
||||
*/
|
||||
import { Resleeve } from "./Resleeve";
|
||||
import { generateResleeves,
|
||||
purchaseResleeve } from "./Resleeving";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { Augmentation } from "../../Augmentation/Augmentation";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Page,
|
||||
routing } from "../../ui/navigationTracking";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
import { exceptionAlert } from "../../../utils/helpers/exceptionAlert";
|
||||
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { removeElement } from "../../../utils/uiHelpers/removeElement";
|
||||
|
||||
interface IResleeveUIElems {
|
||||
container: HTMLElement | null;
|
||||
statsPanel: HTMLElement | null;
|
||||
stats: HTMLElement | null;
|
||||
multipliersButton: HTMLElement | null;
|
||||
augPanel: HTMLElement | null;
|
||||
augSelector: HTMLSelectElement | null;
|
||||
augDescription: HTMLElement | null;
|
||||
costPanel: HTMLElement | null;
|
||||
costText: HTMLElement | null;
|
||||
buyButton: HTMLElement | null;
|
||||
}
|
||||
|
||||
interface IPageUIElems {
|
||||
container: HTMLElement | null;
|
||||
info: HTMLElement | null;
|
||||
sortTag: HTMLElement | null;
|
||||
sortSelector: HTMLSelectElement | null;
|
||||
resleeveList: HTMLElement | null;
|
||||
resleeves: IResleeveUIElems[] | null;
|
||||
}
|
||||
|
||||
const UIElems: IPageUIElems = {
|
||||
container: null,
|
||||
info: null,
|
||||
sortTag: null,
|
||||
sortSelector: null,
|
||||
resleeveList: null,
|
||||
resleeves: null,
|
||||
}
|
||||
|
||||
let playerRef: IPlayer | null;
|
||||
|
||||
export function createResleevesPage(p: IPlayer) {
|
||||
if (!routing.isOn(Page.Resleeves)) { return; }
|
||||
|
||||
try {
|
||||
playerRef = p;
|
||||
|
||||
UIElems.container = createElement("div", {
|
||||
class: "generic-menupage-container",
|
||||
id: "resleeves-container",
|
||||
position: "fixed",
|
||||
});
|
||||
|
||||
UIElems.info = createElement("p", {
|
||||
display: "block",
|
||||
innerHTML: "Re-sleeving is the process of digitizing and transferring your consciousness " +
|
||||
"into a new human body, or 'sleeve'. Here at VitaLife, you can purchase new " +
|
||||
"specially-engineered bodies for the re-sleeve process. Many of these bodies " +
|
||||
"even come with genetic and cybernetic Augmentations!<br><br>" +
|
||||
"Re-sleeving will change your experience for every stat. It will also REMOVE " +
|
||||
"all of your currently-installed Augmentations, and replace " +
|
||||
"them with the ones provided by the purchased sleeve. However, Augmentations that you have " +
|
||||
"purchased but not installed will NOT be removed. If you have purchased an " +
|
||||
"Augmentation and then re-sleeve into a body which already has that Augmentation, " +
|
||||
"it will be removed (since you cannot have duplicate Augmentations).<br><br>" +
|
||||
"NOTE: The stats and multipliers displayed on this page do NOT include your bonuses from " +
|
||||
"Source-File.",
|
||||
width: "75%",
|
||||
});
|
||||
|
||||
// Randomly create all Resleeves if they dont already exist
|
||||
if (p.resleeves.length === 0) {
|
||||
p.resleeves = generateResleeves();
|
||||
}
|
||||
|
||||
// Create a selector for sorting the list of Resleeves
|
||||
UIElems.sortTag = createElement("p", {
|
||||
display: "inline-block",
|
||||
innerText: "Sort By: "
|
||||
});
|
||||
UIElems.sortSelector = createElement("select") as HTMLSelectElement;
|
||||
|
||||
enum SortOption {
|
||||
Cost = "Cost",
|
||||
Hacking = "Hacking",
|
||||
Strength = "Strength",
|
||||
Defense = "Defense",
|
||||
Dexterity = "Dexterity",
|
||||
Agility = "Agility",
|
||||
Charisma = "Charisma",
|
||||
AverageCombatStats = "AverageCombat",
|
||||
AverageAllStats = "AverageAllStats",
|
||||
TotalNumAugmentations = "TotalNumAugmentations",
|
||||
}
|
||||
|
||||
UIElems.sortSelector!.add(createOptionElement("Cost", SortOption.Cost));
|
||||
UIElems.sortSelector!.add(createOptionElement("Hacking Level", SortOption.Hacking));
|
||||
UIElems.sortSelector!.add(createOptionElement("Strength Level", SortOption.Strength));
|
||||
UIElems.sortSelector!.add(createOptionElement("Defense Level", SortOption.Defense));
|
||||
UIElems.sortSelector!.add(createOptionElement("Dexterity Level", SortOption.Dexterity));
|
||||
UIElems.sortSelector!.add(createOptionElement("Agility Level", SortOption.Agility));
|
||||
UIElems.sortSelector!.add(createOptionElement("Charisma Level", SortOption.Charisma));
|
||||
UIElems.sortSelector!.add(createOptionElement("Average Combat Stats", SortOption.AverageCombatStats));
|
||||
UIElems.sortSelector!.add(createOptionElement("Average Stats", SortOption.AverageAllStats));
|
||||
UIElems.sortSelector!.add(createOptionElement("Number of Augmentations", SortOption.TotalNumAugmentations));
|
||||
|
||||
UIElems.resleeveList = createElement("ul");
|
||||
UIElems.sortSelector!.onchange = () => {
|
||||
removeChildrenFromElement(UIElems.resleeveList);
|
||||
UIElems.resleeves = [];
|
||||
|
||||
// Helper function for averaging
|
||||
function getAverage(...values: number[]) {
|
||||
let sum: number = 0;
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
sum += values[i];
|
||||
}
|
||||
|
||||
return sum / values.length;
|
||||
}
|
||||
|
||||
const sortOpt = getSelectValue(UIElems.sortSelector!);
|
||||
switch (sortOpt) {
|
||||
case SortOption.Hacking:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.hacking_skill - b.hacking_skill;
|
||||
});
|
||||
break;
|
||||
case SortOption.Strength:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.strength - b.strength;
|
||||
});
|
||||
break;
|
||||
case SortOption.Defense:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.defense - b.defense;
|
||||
});
|
||||
break;
|
||||
case SortOption.Dexterity:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.dexterity - b.dexterity;
|
||||
});
|
||||
break;
|
||||
case SortOption.Agility:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.agility - b.agility;
|
||||
});
|
||||
break;
|
||||
case SortOption.Charisma:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.charisma - b.charisma;
|
||||
});
|
||||
break;
|
||||
case SortOption.AverageCombatStats:
|
||||
p.resleeves.sort((a, b) => {
|
||||
let aAvg = getAverage(a.strength, a.defense, a.dexterity, a.agility);
|
||||
let bAvg = getAverage(b.strength, b.defense, b.dexterity, b.agility);
|
||||
|
||||
return aAvg - bAvg;
|
||||
});
|
||||
break;
|
||||
case SortOption.AverageAllStats:
|
||||
p.resleeves.sort((a, b) => {
|
||||
let aAvg = getAverage(a.hacking_skill, a.strength, a.defense, a.dexterity, a.agility, a.charisma);
|
||||
let bAvg = getAverage(b.hacking_skill, b.strength, b.defense, b.dexterity, b.agility, b.charisma);
|
||||
|
||||
return aAvg - bAvg;
|
||||
});
|
||||
break;
|
||||
case SortOption.TotalNumAugmentations:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.augmentations.length - b.augmentations.length;
|
||||
});
|
||||
break;
|
||||
case SortOption.Cost:
|
||||
default:
|
||||
p.resleeves.sort((a, b) => {
|
||||
return a.getCost() - b.getCost();
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// Create UI for all Resleeves
|
||||
for (const resleeve of p.resleeves) {
|
||||
const resleeveUi = createResleeveUi(resleeve);
|
||||
UIElems.resleeveList!.appendChild(resleeveUi.container!);
|
||||
UIElems.resleeves!.push(resleeveUi);
|
||||
}
|
||||
}
|
||||
UIElems.sortSelector!.dispatchEvent(new Event('change')); // Force onchange event
|
||||
|
||||
UIElems.container.appendChild(UIElems.info);
|
||||
UIElems.container.appendChild(createElement("br"));
|
||||
UIElems.container.appendChild(UIElems.sortTag);
|
||||
UIElems.container.appendChild(UIElems.sortSelector);
|
||||
UIElems.container.appendChild(UIElems.resleeveList);
|
||||
|
||||
document.getElementById("entire-game-container")!.appendChild(UIElems.container);
|
||||
} catch(e) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearResleevesPage() {
|
||||
if (UIElems.container instanceof HTMLElement) {
|
||||
removeElement(UIElems.container);
|
||||
}
|
||||
|
||||
for (const prop in UIElems) {
|
||||
(<any>UIElems)[prop] = null;
|
||||
}
|
||||
|
||||
playerRef = null;
|
||||
}
|
||||
|
||||
function createResleeveUi(resleeve: Resleeve): IResleeveUIElems {
|
||||
const elems: IResleeveUIElems = {
|
||||
container: null,
|
||||
statsPanel: null,
|
||||
stats: null,
|
||||
multipliersButton: null,
|
||||
augPanel: null,
|
||||
augSelector: null,
|
||||
augDescription: null,
|
||||
costPanel: null,
|
||||
costText: null,
|
||||
buyButton: null,
|
||||
};
|
||||
|
||||
if (!routing.isOn(Page.Resleeves)) { return elems; }
|
||||
|
||||
elems.container = createElement("div", {
|
||||
class: "resleeve-container",
|
||||
display: "block",
|
||||
});
|
||||
|
||||
elems.statsPanel = createElement("div", { class: "resleeve-panel", width: "30%" });
|
||||
elems.stats = createElement("p", {
|
||||
class: "resleeve-stats-text",
|
||||
innerHTML:
|
||||
`Hacking: ${numeralWrapper.format(resleeve.hacking_skill, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.hacking_exp)} exp)<br>` +
|
||||
`Strength: ${numeralWrapper.format(resleeve.strength, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.strength_exp)} exp)<br>` +
|
||||
`Defense: ${numeralWrapper.format(resleeve.defense, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.defense_exp)} exp)<br>` +
|
||||
`Dexterity: ${numeralWrapper.format(resleeve.dexterity, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.dexterity_exp)} exp)<br>` +
|
||||
`Agility: ${numeralWrapper.format(resleeve.agility, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.agility_exp)} exp)<br>` +
|
||||
`Charisma: ${numeralWrapper.format(resleeve.charisma, "0,0")} (${numeralWrapper.formatBigNumber(resleeve.charisma_exp)} exp)<br>` +
|
||||
`# Augmentations: ${resleeve.augmentations.length}`,
|
||||
});
|
||||
elems.multipliersButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Multipliers",
|
||||
clickListener: () => {
|
||||
dialogBoxCreate(
|
||||
[
|
||||
"<h2><u>Total Multipliers:</u></h2>",
|
||||
`Hacking Level multiplier: ${numeralWrapper.formatPercentage(resleeve.hacking_mult)}`,
|
||||
`Hacking Experience multiplier: ${numeralWrapper.formatPercentage(resleeve.hacking_exp_mult)}`,
|
||||
`Strength Level multiplier: ${numeralWrapper.formatPercentage(resleeve.strength_mult)}`,
|
||||
`Strength Experience multiplier: ${numeralWrapper.formatPercentage(resleeve.strength_exp_mult)}`,
|
||||
`Defense Level multiplier: ${numeralWrapper.formatPercentage(resleeve.defense_mult)}`,
|
||||
`Defense Experience multiplier: ${numeralWrapper.formatPercentage(resleeve.defense_exp_mult)}`,
|
||||
`Dexterity Level multiplier: ${numeralWrapper.formatPercentage(resleeve.dexterity_mult)}`,
|
||||
`Dexterity Experience multiplier: ${numeralWrapper.formatPercentage(resleeve.dexterity_exp_mult)}`,
|
||||
`Agility Level multiplier: ${numeralWrapper.formatPercentage(resleeve.agility_mult)}`,
|
||||
`Agility Experience multiplier: ${numeralWrapper.formatPercentage(resleeve.agility_exp_mult)}`,
|
||||
`Charisma Level multiplier: ${numeralWrapper.formatPercentage(resleeve.charisma_mult)}`,
|
||||
`Charisma Experience multiplier: ${numeralWrapper.formatPercentage(resleeve.charisma_exp_mult)}`,
|
||||
`Hacking Chance multiplier: ${numeralWrapper.formatPercentage(resleeve.hacking_chance_mult)}`,
|
||||
`Hacking Speed multiplier: ${numeralWrapper.formatPercentage(resleeve.hacking_speed_mult)}`,
|
||||
`Hacking Money multiplier: ${numeralWrapper.formatPercentage(resleeve.hacking_money_mult)}`,
|
||||
`Hacking Growth multiplier: ${numeralWrapper.formatPercentage(resleeve.hacking_grow_mult)}`,
|
||||
`Salary multiplier: ${numeralWrapper.formatPercentage(resleeve.work_money_mult)}`,
|
||||
`Company Reputation Gain multiplier: ${numeralWrapper.formatPercentage(resleeve.company_rep_mult)}`,
|
||||
`Faction Reputation Gain multiplier: ${numeralWrapper.formatPercentage(resleeve.faction_rep_mult)}`,
|
||||
`Crime Money multiplier: ${numeralWrapper.formatPercentage(resleeve.crime_money_mult)}`,
|
||||
`Crime Success multiplier: ${numeralWrapper.formatPercentage(resleeve.crime_success_mult)}`,
|
||||
`Hacknet Income multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_money_mult)}`,
|
||||
`Hacknet Purchase Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_purchase_cost_mult)}`,
|
||||
`Hacknet Level Upgrade Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_level_cost_mult)}`,
|
||||
`Hacknet Ram Upgrade Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_ram_cost_mult)}`,
|
||||
`Hacknet Core Upgrade Cost multiplier: ${numeralWrapper.formatPercentage(resleeve.hacknet_node_core_cost_mult)}`,
|
||||
`Bladeburner Max Stamina multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_max_stamina_mult)}`,
|
||||
`Bladeburner Stamina Gain multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_stamina_gain_mult)}`,
|
||||
`Bladeburner Field Analysis multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_analysis_mult)}`,
|
||||
`Bladeburner Success Chance multiplier: ${numeralWrapper.formatPercentage(resleeve.bladeburner_success_chance_mult)}`
|
||||
].join("<br>"), false
|
||||
)
|
||||
}
|
||||
});
|
||||
elems.statsPanel.appendChild(elems.stats);
|
||||
elems.statsPanel.appendChild(elems.multipliersButton);
|
||||
|
||||
elems.augPanel = createElement("div", { class: "resleeve-panel", width: "50%" });
|
||||
elems.augSelector = createElement("select", { class: "resleeve-aug-selector" }) as HTMLSelectElement;
|
||||
elems.augDescription = createElement("p");
|
||||
for (let i = 0; i < resleeve.augmentations.length; ++i) {
|
||||
elems.augSelector.add(createOptionElement(resleeve.augmentations[i].name));
|
||||
};
|
||||
elems.augSelector.addEventListener("change", () => {
|
||||
updateAugDescription(elems);
|
||||
});
|
||||
elems.augSelector.dispatchEvent(new Event('change')); // Set inital description by manually triggering change event
|
||||
elems.augPanel.appendChild(elems.augSelector);
|
||||
elems.augPanel.appendChild(elems.augDescription);
|
||||
|
||||
const cost: number = resleeve.getCost();
|
||||
elems.costPanel = createElement("div", { class: "resleeve-panel", width: "20%" });
|
||||
elems.costText = createElement("p", {
|
||||
innerText: `It costs ${numeralWrapper.formatMoney(cost)} ` +
|
||||
`to purchase this Sleeve.`,
|
||||
});
|
||||
elems.buyButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Purchase",
|
||||
clickListener: () => {
|
||||
if (purchaseResleeve(resleeve, playerRef!)) {
|
||||
dialogBoxCreate(`You re-sleeved for ${numeralWrapper.formatMoney(cost)}!`, false);
|
||||
} else {
|
||||
dialogBoxCreate(`You cannot afford to re-sleeve into this body`, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
elems.costPanel.appendChild(elems.costText);
|
||||
elems.costPanel.appendChild(elems.buyButton);
|
||||
|
||||
elems.container.appendChild(elems.statsPanel);
|
||||
elems.container.appendChild(elems.augPanel);
|
||||
elems.container.appendChild(elems.costPanel);
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
function updateAugDescription(elems: IResleeveUIElems) {
|
||||
const augName: string = getSelectValue(elems.augSelector);
|
||||
const aug: Augmentation | null = Augmentations[augName];
|
||||
if (aug == null) {
|
||||
console.warn(`Could not find Augmentation with name ${augName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
elems.augDescription!.innerHTML = aug!.info;
|
||||
}
|
9
src/PersonObjects/Sleeve/README.md
Normal file
9
src/PersonObjects/Sleeve/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
Implements the "Duplicate Sleeves" feature, which allows the player to purchase
|
||||
new duplicate sleeves. These are synthetic bodies that contain the player's
|
||||
cloned consciousness. The player can use these sleeves to perform
|
||||
different tasks synchronously.
|
||||
|
||||
This feature is introduced and unlocked in BitNode-10.
|
||||
|
||||
Note that while they are based on the same concept, this feature is different
|
||||
than the "Re-sleeving" mechanic (which is referred to as "Resleeve" in the source code).
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Sleeves are clones of the player that can be used to perform
|
||||
* different tasks synchronously.
|
||||
* Sleeves are bodies that contain the player's cloned consciousness.
|
||||
* The player can use these bodies to perform different tasks synchronously.
|
||||
*
|
||||
* Each sleeve is its own individual, meaning it has its own stats/exp
|
||||
*
|
||||
@ -8,21 +8,29 @@
|
||||
*/
|
||||
import { SleeveTaskType } from "./SleeveTaskTypesEnum";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
import { Person,
|
||||
IPlayer,
|
||||
ICrime,
|
||||
IFaction,
|
||||
ITaskTracker,
|
||||
createTaskTracker } from "../Person";
|
||||
|
||||
import { BitNodeMultipliers } from "../../BitNodeMultipliers";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
import { Crime } from "../../Crime/Crime";
|
||||
import { Crimes } from "../../Crime/Crimes";
|
||||
|
||||
import { Cities } from "../../Locations/Cities";
|
||||
|
||||
import { Companies } from "../../Company/Companies";
|
||||
import { Company } from "../../Company/Company";
|
||||
import { CompanyPosition } from "../../Company/CompanyPosition";
|
||||
import { CompanyPositions } from "../../Company/CompanyPositions";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { FactionWorkType } from "../../Faction/FactionWorkTypeEnum";
|
||||
|
||||
import { Locations } from "../../Locations";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../../utils/JSONReviver";
|
||||
@ -35,19 +43,24 @@ export class Sleeve extends Person {
|
||||
return Generic_fromJSON(Sleeve, value.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the type of crime the sleeve is currently attempting
|
||||
* Must match the name of a Crime object
|
||||
*/
|
||||
crimeType: string = "";
|
||||
|
||||
/**
|
||||
* Enum value for current task
|
||||
*/
|
||||
currentTask: SleeveTaskType = SleeveTaskType.Idle;
|
||||
|
||||
/**
|
||||
* Description of current task. Used only for logging purposes
|
||||
*/
|
||||
currentTaskDescription: string = "";
|
||||
|
||||
/**
|
||||
* For what company/faction the current task is assigned to.
|
||||
* Only applicable when working for faction or company, obviously
|
||||
* Contains details about the sleeve's current task. The info stored
|
||||
* in this depends on the task type
|
||||
*
|
||||
* Faction/Company Work: Name of Faction/Company
|
||||
* Crime: Money earned if successful
|
||||
* Class/Gym: Name of university/gym
|
||||
*/
|
||||
currentTaskLocation: string = "";
|
||||
|
||||
@ -86,6 +99,11 @@ export class Sleeve extends Person {
|
||||
*/
|
||||
gainRatesForTask: ITaskTracker = createTaskTracker();
|
||||
|
||||
/**
|
||||
* String that stores what stat the sleeve is training at the gym
|
||||
*/
|
||||
gymStatType: string = "";
|
||||
|
||||
/**
|
||||
* Keeps track of events/notifications for this sleeve
|
||||
*/
|
||||
@ -101,6 +119,8 @@ export class Sleeve extends Person {
|
||||
* Sleeve shock. Number between 1 and 100
|
||||
* Trauma/shock that comes with being in a sleeve. Experience earned
|
||||
* is multipled by shock%. This gets applied before synchronization
|
||||
*
|
||||
* Reputation earned is also multiplied by shock%
|
||||
*/
|
||||
shock: number = 1;
|
||||
|
||||
@ -118,29 +138,17 @@ export class Sleeve extends Person {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
/*
|
||||
this.currentTask = SleeveTaskType.Idle;
|
||||
this.currentTaskDescription = "";
|
||||
this.currentTaskTime = 0;
|
||||
this.currentTaskMaxTime = 0;
|
||||
this.earningsForSleeves = createTaskTracker();
|
||||
this.earningsForPlayer = createTaskTracker();
|
||||
this.earningsForTask = createTaskTracker();
|
||||
this.gainRatesForTask = createTaskTracker();
|
||||
this.logs = [];
|
||||
this.memory = 0;
|
||||
this.shock = 1;
|
||||
this.storedCycles = 0;
|
||||
this.sync = 1;
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit crimes
|
||||
*/
|
||||
commitCrime(p: IPlayer, crime: ICrime): void {
|
||||
commitCrime(p: IPlayer, crimeKey: string): boolean {
|
||||
const crime: Crime | null = Crimes[crimeKey];
|
||||
if (!(crime instanceof Crime)) { return false; }
|
||||
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask();
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
}
|
||||
@ -151,29 +159,94 @@ export class Sleeve extends Person {
|
||||
this.gainRatesForTask.dex = crime.dexterity_exp * this.dexterity_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
this.gainRatesForTask.agi = crime.agility_exp * this.agility_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
this.gainRatesForTask.cha = crime.charisma_exp * this.charisma_exp_mult * BitNodeMultipliers.CrimeExpGain;
|
||||
this.gainRatesForTask.money = crime.money * this.crime_money_mult * BitNodeMultipliers.CrimeMoney;
|
||||
|
||||
this.currentTaskLocation = String(this.gainRatesForTask.money);
|
||||
|
||||
this.crimeType = crimeKey;
|
||||
this.currentTaskMaxTime = crime.time;
|
||||
|
||||
this.currentTask = SleeveTaskType.Crime;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to stop the current task
|
||||
*/
|
||||
finishTask(): void {
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
} else {
|
||||
finishTask(p: IPlayer): ITaskTracker {
|
||||
let retValue: ITaskTracker = createTaskTracker(); // Amount of exp to be gained by other sleeves
|
||||
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
// For crimes, all experience and money is gained at the end
|
||||
if (this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
const crime: Crime | null = Crimes[this.crimeType];
|
||||
if (!(crime instanceof Crime)) {
|
||||
console.error(`Invalid data stored in sleeve.crimeType: ${this.crimeType}`);
|
||||
this.resetTaskStatus();
|
||||
return retValue;
|
||||
}
|
||||
if (Math.random() < crime.successRate(p)) {
|
||||
// Success
|
||||
const successGainRates: ITaskTracker = createTaskTracker();
|
||||
|
||||
const keysForIteration: (keyof ITaskTracker)[] = (<(keyof ITaskTracker)[]>Object.keys(successGainRates));
|
||||
for (let i = 0; i < keysForIteration.length; ++i) {
|
||||
const key = keysForIteration[i];
|
||||
successGainRates[key] = this.gainRatesForTask[key] * 2;
|
||||
}
|
||||
retValue = this.gainExperience(p, successGainRates);
|
||||
this.gainMoney(p, this.gainRatesForTask);
|
||||
} else {
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask);
|
||||
}
|
||||
|
||||
// Do not reset task to IDLE
|
||||
this.currentTaskTime = 0;
|
||||
return retValue;
|
||||
}
|
||||
} else {
|
||||
// For other crimes... I dont think anything else needs to be done
|
||||
}
|
||||
|
||||
this.resetTaskStatus();
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Earn experience for any stats (supports multiple)
|
||||
* This function also handles experience propogating to Player and other sleeves
|
||||
*/
|
||||
gainExperience(p: IPlayer, exp: ITaskTracker, numCycles: number=1): ITaskTracker {
|
||||
gainExperience(p: IPlayer, exp: ITaskTracker, numCycles: number=1, fromOtherSleeve: boolean=false): ITaskTracker {
|
||||
// If the experience is coming from another sleeve, it is not multiplied by anything.
|
||||
// Also the player does not earn anything
|
||||
if (fromOtherSleeve) {
|
||||
if (exp.hack > 0) {
|
||||
this.hacking_exp += exp.hack;
|
||||
}
|
||||
|
||||
if (exp.str > 0) {
|
||||
this.strength_exp += exp.str;
|
||||
}
|
||||
|
||||
if (exp.def > 0) {
|
||||
this.defense_exp += exp.def;
|
||||
}
|
||||
|
||||
if (exp.dex > 0) {
|
||||
this.dexterity_exp += exp.dex;
|
||||
}
|
||||
|
||||
if (exp.agi > 0) {
|
||||
this.agility_exp += exp.agi;
|
||||
}
|
||||
|
||||
if (exp.cha > 0) {
|
||||
this.charisma_exp += exp.cha;
|
||||
}
|
||||
|
||||
return createTaskTracker();
|
||||
}
|
||||
|
||||
// Experience is first multiplied by shock. Then 'synchronization'
|
||||
// is accounted for
|
||||
const multFac = (this.shock / 100) * (this.sync / 100) * numCycles;
|
||||
@ -203,7 +276,7 @@ export class Sleeve extends Person {
|
||||
this.defense_exp += pDefExp;
|
||||
p.gainDefenseExp(pDefExp);
|
||||
this.earningsForPlayer.def += pDefExp;
|
||||
this.earningsForTask.dex += pDefExp;
|
||||
this.earningsForTask.def += pDefExp;
|
||||
}
|
||||
|
||||
if (pDexExp > 0) {
|
||||
@ -251,28 +324,49 @@ export class Sleeve extends Person {
|
||||
* Earn money for player
|
||||
*/
|
||||
gainMoney(p: IPlayer, task: ITaskTracker, numCycles: number=1): void {
|
||||
p.gainMoney(task.money * numCycles);
|
||||
const gain: number = (task.money * numCycles);
|
||||
this.earningsForTask.money += gain;
|
||||
this.earningsForPlayer.money += gain;
|
||||
p.gainMoney(gain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reputation gain for the current task
|
||||
* Only applicable when working for company or faction
|
||||
*/
|
||||
getRepGain(): number {
|
||||
getRepGain(p: IPlayer): number {
|
||||
if (this.currentTask === SleeveTaskType.Faction) {
|
||||
switch (this.factionWorkType) {
|
||||
case FactionWorkType.Hacking:
|
||||
return this.getFactionHackingWorkRepGain();
|
||||
return this.getFactionHackingWorkRepGain() * (this.shock / 100);
|
||||
case FactionWorkType.Field:
|
||||
return this.getFactionFieldWorkRepGain();
|
||||
return this.getFactionFieldWorkRepGain() * (this.shock / 100);
|
||||
case FactionWorkType.Security:
|
||||
return this.getFactionSecurityWorkRepGain();
|
||||
return this.getFactionSecurityWorkRepGain() * (this.shock / 100);
|
||||
default:
|
||||
console.warn(`Invalid Sleeve.factionWorkType property in Sleeve.getRepGain(): ${this.factionWorkType}`);
|
||||
return 0;
|
||||
}
|
||||
} else if (this.currentTask === SleeveTaskType.Company) {
|
||||
return 0;
|
||||
const companyName: string = this.currentTaskLocation;
|
||||
const company: Company | null = Companies[companyName];
|
||||
if (company == null) {
|
||||
console.error(`Invalid company found when trying to calculate rep gain: ${companyName}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]];
|
||||
if (companyPosition == null) {
|
||||
console.error(`Invalid company position name found when trying to calculate rep gain: ${p.jobs[companyName]}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const jobPerformance: number = companyPosition!.calculateJobPerformance(this.hacking_skill, this.strength,
|
||||
this.defense, this.dexterity,
|
||||
this.agility, this.charisma);
|
||||
const favorMult = 1 + (company!.favor / 100);
|
||||
|
||||
return jobPerformance * this.company_rep_mult * favorMult;
|
||||
} else {
|
||||
console.warn(`Sleeve.getRepGain() called for invalid task type: ${this.currentTask}`);
|
||||
return 0;
|
||||
@ -298,14 +392,9 @@ export class Sleeve extends Person {
|
||||
this.storedCycles += numCycles;
|
||||
if (this.storedCycles < CyclesPerSecond) { return null; }
|
||||
|
||||
// Shock gradually goes towards 100
|
||||
this.shock = Math.max(100, this.shock + (0.0001 * this.storedCycles));
|
||||
|
||||
if (this.currentTask === SleeveTaskType.Idle) { return null; }
|
||||
|
||||
let time = this.storedCycles * CONSTANTS.MilliPerCycle;
|
||||
let cyclesUsed = this.storedCycles;
|
||||
if (this.currentTaskTime + time > this.currentTaskMaxTime) {
|
||||
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime + time > this.currentTaskMaxTime) {
|
||||
time = this.currentTaskMaxTime - this.currentTaskTime;
|
||||
cyclesUsed = Math.floor(time / CONSTANTS.MilliPerCycle);
|
||||
|
||||
@ -317,9 +406,15 @@ export class Sleeve extends Person {
|
||||
}
|
||||
this.currentTaskTime += time;
|
||||
|
||||
// Shock gradually goes towards 100
|
||||
this.shock = Math.min(100, this.shock + (0.0001 * this.storedCycles));
|
||||
|
||||
let retValue: ITaskTracker = createTaskTracker();
|
||||
switch (this.currentTask) {
|
||||
case SleeveTaskType.Idle:
|
||||
break;
|
||||
case SleeveTaskType.Class:
|
||||
case SleeveTaskType.Gym:
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
|
||||
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
|
||||
break;
|
||||
@ -327,34 +422,50 @@ export class Sleeve extends Person {
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
|
||||
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
|
||||
|
||||
// TODO REP for both this and company
|
||||
const fac = Factions[this.currentTaskLocation];
|
||||
// Gain faction reputation
|
||||
const fac: Faction = Factions[this.currentTaskLocation];
|
||||
if (!(fac instanceof Faction)) {
|
||||
console.error(`Invalid faction for Sleeve task: ${this.currentTaskLocation}`);
|
||||
break;
|
||||
}
|
||||
|
||||
fac.playerReputation += (this.getRepGain(p) * cyclesUsed);
|
||||
break;
|
||||
case SleeveTaskType.Company:
|
||||
retValue = this.gainExperience(p, this.gainRatesForTask, cyclesUsed);
|
||||
this.gainMoney(p, this.gainRatesForTask, cyclesUsed);
|
||||
|
||||
const company: Company = Companies[this.currentTaskLocation];
|
||||
if (!(company instanceof Company)) {
|
||||
console.error(`Invalid company for Sleeve task: ${this.currentTaskLocation}`);
|
||||
break;
|
||||
}
|
||||
|
||||
company!.playerReputation += (this.getRepGain(p) * cyclesUsed);
|
||||
break;
|
||||
case SleeveTaskType.Recovery:
|
||||
this.shock = Math.max(100, this.shock + (0.001 * this.storedCycles));
|
||||
this.shock = Math.min(100, this.shock + (0.0001 * cyclesUsed));
|
||||
break;
|
||||
case SleeveTaskType.Sync:
|
||||
this.sync = Math.max(100, this.sync + (0.001 * this.storedCycles));
|
||||
this.sync = Math.min(100, this.sync + (0.0001 * cyclesUsed));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.currentTaskMaxTime !== 0 && this.currentTaskTime >= this.currentTaskMaxTime) {
|
||||
this.finishTask();
|
||||
if (this.currentTask === SleeveTaskType.Crime) {
|
||||
retValue = this.finishTask(p);
|
||||
} else {
|
||||
this.finishTask(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.updateStatLevels();
|
||||
|
||||
this.storedCycles -= cyclesUsed;
|
||||
|
||||
// TODO Finish this
|
||||
return retValue;
|
||||
}
|
||||
|
||||
@ -368,6 +479,9 @@ export class Sleeve extends Person {
|
||||
this.currentTaskTime = 0;
|
||||
this.currentTaskMaxTime = 0;
|
||||
this.factionWorkType = FactionWorkType.None;
|
||||
this.crimeType = "";
|
||||
this.currentTaskLocation = "";
|
||||
this.gymStatType = "";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -375,7 +489,7 @@ export class Sleeve extends Person {
|
||||
*/
|
||||
takeUniversityCourse(p: IPlayer, universityName: string, className: string): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask();
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
}
|
||||
@ -387,16 +501,19 @@ export class Sleeve extends Person {
|
||||
switch (universityName.toLowerCase()) {
|
||||
case Locations.AevumSummitUniversity.toLowerCase():
|
||||
if (this.city !== Cities.Aevum) { return false; }
|
||||
this.currentTaskLocation = Locations.AevumSummitUniversity;
|
||||
costMult = 4;
|
||||
expMult = 3;
|
||||
break;
|
||||
case Locations.Sector12RothmanUniversity.toLowerCase():
|
||||
if (this.city !== Cities.Sector12) { return false; }
|
||||
this.currentTaskLocation = Locations.Sector12RothmanUniversity;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case Locations.VolhavenZBInstituteOfTechnology.toLowerCase():
|
||||
if (this.city !== Cities.Volhaven) { return false; }
|
||||
this.currentTaskLocation = Locations.VolhavenZBInstituteOfTechnology;
|
||||
costMult = 5;
|
||||
expMult = 4;
|
||||
break;
|
||||
@ -404,9 +521,6 @@ export class Sleeve extends Person {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Number of game cycles in a second
|
||||
const cps: number = 1000 / CONSTANTS.MilliPerCycle;
|
||||
|
||||
// Set experience/money gains based on class
|
||||
// TODO Refactor University Courses into its own class or something
|
||||
const baseStudyComputerScienceExp: number = 0.5;
|
||||
@ -464,32 +578,86 @@ export class Sleeve extends Person {
|
||||
}
|
||||
|
||||
/**
|
||||
* Work for a company
|
||||
* Start work for one of the player's companies
|
||||
* Returns boolean indicating success
|
||||
*/
|
||||
workForCompany(p: IPlayer): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Work for one of the player's factions
|
||||
*/
|
||||
workForFaction(p: IPlayer, factionName: string, workType: string): boolean {
|
||||
if (!(Factions[factionName] instanceof Faction) || !p.factions.includes(factionName)) {
|
||||
workForCompany(p: IPlayer, companyName: string): boolean {
|
||||
if (!(Companies[companyName] instanceof Company) || p.jobs[companyName] == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask();
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
}
|
||||
|
||||
const company: Company | null = Companies[companyName];
|
||||
const companyPosition: CompanyPosition | null = CompanyPositions[p.jobs[companyName]];
|
||||
if (company == null) { throw new Error(`Invalid company name specified in Sleeve.workForCompany(): ${companyName}`); }
|
||||
if (companyPosition == null) { throw new Error(`Invalid CompanyPosition data in Sleeve.workForCompany(): ${companyName}`); }
|
||||
this.gainRatesForTask.money = companyPosition.baseSalary *
|
||||
company.salaryMultiplier *
|
||||
this.work_money_mult *
|
||||
BitNodeMultipliers.CompanyWorkMoney;
|
||||
this.gainRatesForTask.hack = companyPosition.hackingExpGain *
|
||||
company.expMultiplier *
|
||||
this.hacking_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.str = companyPosition.strengthExpGain *
|
||||
company.expMultiplier *
|
||||
this.strength_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.def = companyPosition.defenseExpGain *
|
||||
company.expMultiplier *
|
||||
this.defense_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.dex = companyPosition.dexterityExpGain *
|
||||
company.expMultiplier *
|
||||
this.dexterity_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.agi = companyPosition.agilityExpGain *
|
||||
company.expMultiplier *
|
||||
this.agility_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.cha = companyPosition.charismaExpGain *
|
||||
company.expMultiplier *
|
||||
this.charisma_exp_mult *
|
||||
BitNodeMultipliers.FactionWorkExpGain;
|
||||
|
||||
this.currentTaskLocation = companyName;
|
||||
this.currentTask = SleeveTaskType.Company;
|
||||
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer8Hours;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start work for one of the player's factions
|
||||
* Returns boolean indicating success
|
||||
*/
|
||||
workForFaction(p: IPlayer, factionName: string, workType: string): boolean {
|
||||
if (!(Factions[factionName] instanceof Faction) || !p.factions.includes(factionName)) {
|
||||
throw new Error(`Invalid Faction specified for Sleeve.workForFaction(): ${factionName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
}
|
||||
|
||||
const factionInfo = Factions[factionName].getInfo();
|
||||
|
||||
// Set type of work (hacking/field/security), and the experience gains
|
||||
const sanitizedWorkType: string = workType.toLowerCase();
|
||||
if (sanitizedWorkType.includes("hack")) {
|
||||
if (!factionInfo.offerHackingWork) { return false; }
|
||||
this.factionWorkType = FactionWorkType.Hacking;
|
||||
this.gainRatesForTask.hack = .15 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
} else if (sanitizedWorkType.includes("field")) {
|
||||
if (!factionInfo.offerFieldWork) { return false; }
|
||||
this.factionWorkType = FactionWorkType.Field;
|
||||
this.gainRatesForTask.hack = .1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.str = .1 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
@ -498,6 +666,7 @@ export class Sleeve extends Person {
|
||||
this.gainRatesForTask.agi = .1 * this.agility_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.cha = .1 * this.charisma_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
} else if (sanitizedWorkType.includes("security")) {
|
||||
if (!factionInfo.offerSecurityWork) { return false; }
|
||||
this.factionWorkType = FactionWorkType.Security;
|
||||
this.gainRatesForTask.hack = .1 * this.hacking_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
this.gainRatesForTask.str = .15 * this.strength_exp_mult * BitNodeMultipliers.FactionWorkExpGain;
|
||||
@ -510,6 +679,7 @@ export class Sleeve extends Person {
|
||||
|
||||
this.currentTaskLocation = factionName;
|
||||
this.currentTask = SleeveTaskType.Faction;
|
||||
this.currentTaskMaxTime = CONSTANTS.MillisecondsPer20Hours;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -519,7 +689,7 @@ export class Sleeve extends Person {
|
||||
*/
|
||||
workoutAtGym(p: IPlayer, gymName: string, stat: string): boolean {
|
||||
if (this.currentTask !== SleeveTaskType.Idle) {
|
||||
this.finishTask();
|
||||
this.finishTask(p);
|
||||
} else {
|
||||
this.resetTaskStatus();
|
||||
}
|
||||
@ -531,26 +701,31 @@ export class Sleeve extends Person {
|
||||
switch (gymName.toLowerCase()) {
|
||||
case Locations.AevumCrushFitnessGym.toLowerCase():
|
||||
if (this.city != Cities.Aevum) { return false; }
|
||||
this.currentTaskLocation = Locations.AevumCrushFitnessGym;
|
||||
costMult = 3;
|
||||
expMult = 2;
|
||||
break;
|
||||
case Locations.AevumSnapFitnessGym.toLowerCase():
|
||||
if (this.city != Cities.Aevum) { return false; }
|
||||
this.currentTaskLocation = Locations.AevumSnapFitnessGym;
|
||||
costMult = 10;
|
||||
expMult = 5;
|
||||
break;
|
||||
case Locations.Sector12IronGym.toLowerCase():
|
||||
if (this.city != Cities.Sector12) { return false; }
|
||||
this.currentTaskLocation = Locations.Sector12IronGym;
|
||||
costMult = 1;
|
||||
expMult = 1;
|
||||
break;
|
||||
case Locations.Sector12PowerhouseGym.toLowerCase():
|
||||
if (this.city != Cities.Sector12) { return false; }
|
||||
this.currentTaskLocation = Locations.Sector12PowerhouseGym;
|
||||
costMult = 20;
|
||||
expMult = 10;
|
||||
break;
|
||||
case Locations.VolhavenMilleniumFitnessGym:
|
||||
if (this.city != Cities.Volhaven) { return false; }
|
||||
this.currentTaskLocation = Locations.VolhavenMilleniumFitnessGym;
|
||||
costMult = 7;
|
||||
expMult = 4;
|
||||
break;
|
||||
@ -558,9 +733,6 @@ export class Sleeve extends Person {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Number of game cycles in a second
|
||||
const cps = 1000 / CONSTANTS.MilliPerCycle;
|
||||
|
||||
// Set experience/money gains based on class
|
||||
// TODO Refactor University Courses into its own class or something
|
||||
const baseGymExp: number = 1;
|
||||
@ -582,7 +754,8 @@ export class Sleeve extends Person {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.currentTask = SleeveTaskType.Class;
|
||||
this.gymStatType = stat;
|
||||
this.currentTask = SleeveTaskType.Gym;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
* Enum for different types of tasks that a Sleeve can perform
|
||||
*/
|
||||
export enum SleeveTaskType {
|
||||
Class,
|
||||
Company,
|
||||
Crime,
|
||||
Faction,
|
||||
// Same Order as selectable order in UI
|
||||
Idle,
|
||||
Company,
|
||||
Faction,
|
||||
Crime,
|
||||
Class,
|
||||
Gym,
|
||||
Recovery,
|
||||
Sync,
|
||||
}
|
||||
|
741
src/PersonObjects/Sleeve/SleeveUI.ts
Normal file
741
src/PersonObjects/Sleeve/SleeveUI.ts
Normal file
@ -0,0 +1,741 @@
|
||||
/**
|
||||
* Module for handling the Sleeve UI
|
||||
*/
|
||||
import { Sleeve } from "./Sleeve";
|
||||
import { SleeveTaskType } from "./SleeveTaskTypesEnum";
|
||||
import { SleeveFaq } from "./data/SleeveFaq";
|
||||
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { Locations } from "../../Locations";
|
||||
|
||||
import { Faction } from "../../Faction/Faction";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import { FactionWorkType } from "../../Faction/FactionWorkTypeEnum";
|
||||
|
||||
import { Cities } from "../../Locations/Cities";
|
||||
import { Crime } from "../../Crime/Crime";
|
||||
import { Crimes } from "../../Crime/Crimes";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { Page,
|
||||
routing } from "../../ui/navigationTracking";
|
||||
|
||||
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||
|
||||
import { createProgressBarText } from "../../../utils/helpers/createProgressBarText";
|
||||
import { exceptionAlert } from "../../../utils/helpers/exceptionAlert";
|
||||
|
||||
import { clearEventListeners } from "../../../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../../utils/uiHelpers/createOptionElement";
|
||||
import { createPopup } from "../../../utils/uiHelpers/createPopup";
|
||||
import { createPopupCloseButton } from "../../../utils/uiHelpers/createPopupCloseButton";
|
||||
import { getSelectValue } from "../../../utils/uiHelpers/getSelectData";
|
||||
import { removeChildrenFromElement } from "../../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { removeElement } from "../../../utils/uiHelpers/removeElement";
|
||||
import { removeElementById } from "../../../utils/uiHelpers/removeElementById";
|
||||
|
||||
// Object that keeps track of all DOM elements for the UI for a single Sleeve
|
||||
interface ISleeveUIElems {
|
||||
container: HTMLElement | null;
|
||||
statsPanel: HTMLElement | null;
|
||||
stats: HTMLElement | null;
|
||||
moreStatsButton: HTMLElement | null;
|
||||
travelButton: HTMLElement | null;
|
||||
taskPanel: HTMLElement | null;
|
||||
taskSelector: HTMLSelectElement | null;
|
||||
taskDetailsSelector: HTMLSelectElement | null;
|
||||
taskDetailsSelector2: HTMLSelectElement | null;
|
||||
taskDescription: HTMLElement | null;
|
||||
taskSetButton: HTMLElement | null;
|
||||
taskProgressBar: HTMLElement | null;
|
||||
earningsPanel: HTMLElement | null;
|
||||
currentEarningsInfo: HTMLElement | null;
|
||||
totalEarningsButton: HTMLElement | null;
|
||||
}
|
||||
|
||||
// Object that keeps track of all DOM elements for the entire Sleeve UI
|
||||
interface IPageUIElems {
|
||||
container: HTMLElement | null;
|
||||
docButton: HTMLElement | null;
|
||||
faqButton: HTMLElement | null;
|
||||
info: HTMLElement | null;
|
||||
sleeveList: HTMLElement | null;
|
||||
sleeves: ISleeveUIElems[] | null;
|
||||
}
|
||||
|
||||
const UIElems: IPageUIElems = {
|
||||
container: null,
|
||||
docButton: null,
|
||||
faqButton: null,
|
||||
info: null,
|
||||
sleeveList: null,
|
||||
sleeves: null,
|
||||
}
|
||||
|
||||
// Creates the UI for the entire Sleeves page
|
||||
let playerRef: IPlayer | null;
|
||||
export function createSleevesPage(p: IPlayer) {
|
||||
if (!routing.isOn(Page.Sleeves)) { return; }
|
||||
|
||||
try {
|
||||
playerRef = p;
|
||||
|
||||
UIElems.container = createElement("div", {
|
||||
class: "generic-menupage-container",
|
||||
id: "sleeves-container",
|
||||
position: "fixed",
|
||||
});
|
||||
|
||||
UIElems.info = createElement("p", {
|
||||
class: "sleeves-page-info",
|
||||
innerHTML: "Duplicate Sleeves are MK-V Synthoids (synthetic androids) into which your " +
|
||||
"consciousness has been copied. In other words, these Synthoids contain " +
|
||||
"a perfect duplicate of your mind.<br><br>" +
|
||||
"Sleeves can be used to perform different tasks synchronously.<br><br>",
|
||||
});
|
||||
|
||||
UIElems.faqButton = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "FAQ",
|
||||
clickListener: () => {
|
||||
dialogBoxCreate(SleeveFaq, false);
|
||||
}
|
||||
});
|
||||
|
||||
UIElems.docButton = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Documentation",
|
||||
});
|
||||
|
||||
UIElems.sleeveList = createElement("ul");
|
||||
UIElems.sleeves = [];
|
||||
|
||||
// Create UI modules for all Sleeve
|
||||
for (const sleeve of p.sleeves) {
|
||||
const sleeveUi = createSleeveUi(sleeve, p.sleeves);
|
||||
UIElems.sleeveList.appendChild(sleeveUi.container!);
|
||||
UIElems.sleeves.push(sleeveUi);
|
||||
}
|
||||
|
||||
UIElems.container.appendChild(UIElems.info);
|
||||
UIElems.container.appendChild(UIElems.faqButton);
|
||||
UIElems.container.appendChild(UIElems.sleeveList);
|
||||
|
||||
document.getElementById("entire-game-container")!.appendChild(UIElems.container);
|
||||
} catch(e) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the UI for the entire Sleeves page
|
||||
export function updateSleevesPage() {
|
||||
if (!routing.isOn(Page.Sleeves)) { return; }
|
||||
|
||||
try {
|
||||
for (let i = 0; i < playerRef!.sleeves.length; ++i) {
|
||||
const sleeve: Sleeve = playerRef!.sleeves[i];
|
||||
const elems: ISleeveUIElems = UIElems.sleeves![i];
|
||||
updateSleeveUi(sleeve!, elems!);
|
||||
}
|
||||
} catch(e) {
|
||||
exceptionAlert(e);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearSleevesPage() {
|
||||
if (UIElems.container instanceof HTMLElement) {
|
||||
removeElement(UIElems.container);
|
||||
}
|
||||
|
||||
for (const prop in UIElems) {
|
||||
(<any>UIElems)[prop] = null;
|
||||
}
|
||||
|
||||
playerRef = null;
|
||||
}
|
||||
|
||||
// Creates the UI for a single Sleeve
|
||||
// Returns an object containing the DOM elements in the UI (ISleeveUIElems)
|
||||
function createSleeveUi(sleeve: Sleeve, allSleeves: Sleeve[]): ISleeveUIElems {
|
||||
const elems: ISleeveUIElems = {
|
||||
container: null,
|
||||
statsPanel: null,
|
||||
stats: null,
|
||||
moreStatsButton: null,
|
||||
travelButton: null,
|
||||
taskPanel: null,
|
||||
taskSelector: null,
|
||||
taskDetailsSelector: null,
|
||||
taskDetailsSelector2: null,
|
||||
taskDescription: null,
|
||||
taskSetButton: null,
|
||||
taskProgressBar: null,
|
||||
earningsPanel: null,
|
||||
currentEarningsInfo: null,
|
||||
totalEarningsButton: null,
|
||||
}
|
||||
|
||||
if (!routing.isOn(Page.Sleeves)) { return elems; }
|
||||
|
||||
elems.container = createElement("div", {
|
||||
class: "sleeve-container",
|
||||
display: "block",
|
||||
});
|
||||
|
||||
elems.statsPanel = createElement("div", { class: "sleeve-panel", width: "25%" });
|
||||
elems.stats = createElement("p", { class: "sleeve-stats-text" });
|
||||
elems.moreStatsButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "More Stats",
|
||||
clickListener: () => {
|
||||
dialogBoxCreate(
|
||||
[
|
||||
"<h2><u>Stats:</u></h2>",
|
||||
`Hacking: ${sleeve.hacking_skill} (${numeralWrapper.formatBigNumber(sleeve.hacking_exp)} exp)`,
|
||||
`Strength: ${sleeve.strength} (${numeralWrapper.formatBigNumber(sleeve.strength_exp)} exp)`,
|
||||
`Defense: ${sleeve.defense} (${numeralWrapper.formatBigNumber(sleeve.defense_exp)} exp)`,
|
||||
`Dexterity: ${sleeve.dexterity} (${numeralWrapper.formatBigNumber(sleeve.dexterity_exp)} exp)`,
|
||||
`Agility: ${sleeve.agility} (${numeralWrapper.formatBigNumber(sleeve.agility_exp)} exp)`,
|
||||
`Charisma: ${sleeve.charisma} (${numeralWrapper.formatBigNumber(sleeve.charisma_exp)} exp)<br>`,
|
||||
"<h2><u>Multipliers:</u></h2>",
|
||||
`Hacking Level multiplier: ${numeralWrapper.formatPercentage(sleeve.hacking_mult)}`,
|
||||
`Hacking Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.hacking_exp_mult)}`,
|
||||
`Strength Level multiplier: ${numeralWrapper.formatPercentage(sleeve.strength_mult)}`,
|
||||
`Strength Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.strength_exp_mult)}`,
|
||||
`Defense Level multiplier: ${numeralWrapper.formatPercentage(sleeve.defense_mult)}`,
|
||||
`Defense Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.defense_exp_mult)}`,
|
||||
`Dexterity Level multiplier: ${numeralWrapper.formatPercentage(sleeve.dexterity_mult)}`,
|
||||
`Dexterity Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.dexterity_exp_mult)}`,
|
||||
`Agility Level multiplier: ${numeralWrapper.formatPercentage(sleeve.agility_mult)}`,
|
||||
`Agility Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.agility_exp_mult)}`,
|
||||
`Charisma Level multiplier: ${numeralWrapper.formatPercentage(sleeve.charisma_mult)}`,
|
||||
`Charisma Experience multiplier: ${numeralWrapper.formatPercentage(sleeve.charisma_exp_mult)}`,
|
||||
`Faction Reputation Gain multiplier: ${numeralWrapper.formatPercentage(sleeve.faction_rep_mult)}`,
|
||||
`Company Reputation Gain multiplier: ${numeralWrapper.formatPercentage(sleeve.company_rep_mult)}`,
|
||||
`Salary multiplier: ${numeralWrapper.formatPercentage(sleeve.work_money_mult)}`,
|
||||
`Crime Money multiplier: ${numeralWrapper.formatPercentage(sleeve.crime_money_mult)}`,
|
||||
`Crime Success multiplier: ${numeralWrapper.formatPercentage(sleeve.crime_success_mult)}`,
|
||||
].join("<br>"), false
|
||||
);
|
||||
}
|
||||
});
|
||||
elems.travelButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Travel",
|
||||
clickListener: () => {
|
||||
const popupId: string = "sleeve-travel-popup";
|
||||
const popupArguments: HTMLElement[] = [];
|
||||
popupArguments.push(createPopupCloseButton(popupId, { class: "std-button" }));
|
||||
popupArguments.push(createElement("p", {
|
||||
innerText: "Have this sleeve travel to a different city. This affects " +
|
||||
"the gyms and universities at which this sleeve can study. " +
|
||||
`Traveling to a different city costs ${numeralWrapper.formatMoney(CONSTANTS.TravelCost)}. ` +
|
||||
"It will also CANCEL the sleeve's current task (setting it to idle)",
|
||||
}));
|
||||
for (const label in Cities) {
|
||||
if (sleeve.city === Cities[label]) { continue; }
|
||||
(function(sleeve, label) {
|
||||
popupArguments.push(createElement("div", {
|
||||
// Reusing this css class. It adds a border and makes it so that
|
||||
// the background color changes when you hover
|
||||
class: "cmpy-mgmt-find-employee-option",
|
||||
innerText: Cities[label],
|
||||
clickListener: () => {
|
||||
if (!playerRef!.canAfford(CONSTANTS.TravelCost)) {
|
||||
dialogBoxCreate("You cannot afford to have this sleeve travel to another city", false);
|
||||
return false;
|
||||
}
|
||||
sleeve.city = Cities[label];
|
||||
playerRef!.loseMoney(CONSTANTS.TravelCost);
|
||||
sleeve.resetTaskStatus();
|
||||
removeElementById(popupId);
|
||||
updateSleeveUi(sleeve, elems);
|
||||
updateSleeveTaskSelector(sleeve, elems, allSleeves);
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
})(sleeve, label);
|
||||
}
|
||||
|
||||
createPopup(popupId, popupArguments);
|
||||
}
|
||||
})
|
||||
elems.statsPanel.appendChild(elems.stats);
|
||||
elems.statsPanel.appendChild(elems.moreStatsButton);
|
||||
elems.statsPanel.appendChild(elems.travelButton);
|
||||
|
||||
elems.taskPanel = createElement("div", { class: "sleeve-panel", width: "40%" });
|
||||
elems.taskSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskSelector.add(createOptionElement("------"));
|
||||
elems.taskSelector.add(createOptionElement("Work for Company"));
|
||||
elems.taskSelector.add(createOptionElement("Work for Faction"));
|
||||
elems.taskSelector.add(createOptionElement("Commit Crime"));
|
||||
elems.taskSelector.add(createOptionElement("Take University Course"));
|
||||
elems.taskSelector.add(createOptionElement("Workout at Gym"));
|
||||
elems.taskSelector.add(createOptionElement("Shock Recovery"));
|
||||
elems.taskSelector.add(createOptionElement("Synchronize"));
|
||||
elems.taskDetailsSelector = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDetailsSelector2 = createElement("select") as HTMLSelectElement;
|
||||
elems.taskDescription = createElement("p");
|
||||
elems.taskProgressBar = createElement("p");
|
||||
elems.taskSelector.addEventListener("change", () => {
|
||||
updateSleeveTaskSelector(sleeve, elems, allSleeves);
|
||||
});
|
||||
elems.taskSelector.selectedIndex = sleeve.currentTask; // Set initial value for Task Selector
|
||||
elems.taskSelector.dispatchEvent(new Event('change'));
|
||||
updateSleeveTaskDescription(sleeve, elems);
|
||||
elems.taskSetButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "Set Task",
|
||||
clickListener: () => {
|
||||
setSleeveTask(sleeve, elems);
|
||||
}
|
||||
});
|
||||
elems.taskPanel.appendChild(elems.taskSelector);
|
||||
elems.taskPanel.appendChild(elems.taskDetailsSelector);
|
||||
elems.taskPanel.appendChild(elems.taskDetailsSelector2);
|
||||
elems.taskPanel.appendChild(elems.taskSetButton);
|
||||
elems.taskPanel.appendChild(elems.taskDescription);
|
||||
elems.taskPanel.appendChild(elems.taskProgressBar);
|
||||
|
||||
elems.earningsPanel = createElement("div", { class: "sleeve-panel", width: "35%" });
|
||||
elems.currentEarningsInfo = createElement("p");
|
||||
elems.totalEarningsButton = createElement("button", {
|
||||
class: "std-button",
|
||||
innerText: "More Earnings Info",
|
||||
clickListener: () => {
|
||||
dialogBoxCreate(
|
||||
[
|
||||
"<h2><u>Earnings for Current Task:</u></h2>",
|
||||
`Money: ${numeralWrapper.formatMoney(sleeve.earningsForTask.money)}`,
|
||||
`Hacking Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.hack)}`,
|
||||
`Strength Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.str)}`,
|
||||
`Defense Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.def)}`,
|
||||
`Dexterity Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.dex)}`,
|
||||
`Agility Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.agi)}`,
|
||||
`Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForTask.cha)}<br>`,
|
||||
"<h2><u>Total Earnings for Host Consciousness:</u></h2>",
|
||||
`Money: ${numeralWrapper.formatMoney(sleeve.earningsForPlayer.money)}`,
|
||||
`Hacking Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.hack)}`,
|
||||
`Strength Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.str)}`,
|
||||
`Defense Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.def)}`,
|
||||
`Dexterity Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.dex)}`,
|
||||
`Agility Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.agi)}`,
|
||||
`Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForPlayer.cha)}<br>`,
|
||||
"<h2><u>Total Earnings for Other Sleeves:</u></h2>",
|
||||
`Money: ${numeralWrapper.formatMoney(sleeve.earningsForSleeves.money)}`,
|
||||
`Hacking Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.hack)}`,
|
||||
`Strength Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.str)}`,
|
||||
`Defense Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.def)}`,
|
||||
`Dexterity Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.dex)}`,
|
||||
`Agility Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.agi)}`,
|
||||
`Charisma Exp: ${numeralWrapper.formatBigNumber(sleeve.earningsForSleeves.cha)}`,
|
||||
].join("<br>"), false
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
elems.earningsPanel.appendChild(elems.currentEarningsInfo);
|
||||
elems.earningsPanel.appendChild(elems.totalEarningsButton);
|
||||
|
||||
updateSleeveUi(sleeve, elems);
|
||||
|
||||
elems.container.appendChild(elems.statsPanel);
|
||||
elems.container.appendChild(elems.taskPanel);
|
||||
elems.container.appendChild(elems.earningsPanel);
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
// Updates the UI for a single Sleeve
|
||||
function updateSleeveUi(sleeve: Sleeve, elems: ISleeveUIElems) {
|
||||
if (!routing.isOn(Page.Sleeves)) { return; }
|
||||
|
||||
elems.stats!.innerHTML = [`Hacking: ${numeralWrapper.format(sleeve.hacking_skill, "0,0")}`,
|
||||
`Strength: ${numeralWrapper.format(sleeve.strength, "0,0")}`,
|
||||
`Defense: ${numeralWrapper.format(sleeve.defense, "0,0")}`,
|
||||
`Dexterity: ${numeralWrapper.format(sleeve.dexterity, "0,0")}`,
|
||||
`Agility: ${numeralWrapper.format(sleeve.agility, "0,0")}`,
|
||||
`Charisma: ${numeralWrapper.format(sleeve.charisma, "0,0")}`,
|
||||
`HP: ${numeralWrapper.format(sleeve.hp, "0,0")} / ${numeralWrapper.format(sleeve.max_hp, "0,0")}`,
|
||||
`City: ${sleeve.city}`,
|
||||
`Shock: ${numeralWrapper.format(100 - sleeve.shock, "0,0.000")}`,
|
||||
`Sync: ${numeralWrapper.format(sleeve.sync, "0,0.000")}`].join("<br>");
|
||||
|
||||
let repGainText: string = "";
|
||||
if (sleeve.currentTask === SleeveTaskType.Company || sleeve.currentTask === SleeveTaskType.Faction) {
|
||||
const repGain: number = sleeve.getRepGain(playerRef!);
|
||||
repGainText = `Reputation: ${numeralWrapper.format(5 * repGain, "0.00")} / s`
|
||||
}
|
||||
|
||||
if (sleeve.currentTask === SleeveTaskType.Crime) {
|
||||
elems.currentEarningsInfo!.innerHTML = [
|
||||
`Earnings (Pre-Synchronization):`,
|
||||
`Money: ${numeralWrapper.formatMoney(parseFloat(sleeve.currentTaskLocation))} if successful`,
|
||||
`Hacking Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.hack, "0.00")} (2x if successful)`,
|
||||
`Strength Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.str, "0.00")} (2x if successful)`,
|
||||
`Defense Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.def, "0.00")} (2x if successful)`,
|
||||
`Dexterity Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.dex, "0.00")} (2x if successful)`,
|
||||
`Agility Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.agi, "0.00")} (2x if successful)`,
|
||||
`Charisma Exp: ${numeralWrapper.format(sleeve.gainRatesForTask.cha, "0.00")} (2x if successful)`
|
||||
].join("<br>");
|
||||
|
||||
elems.taskProgressBar!.innerText = createProgressBarText({
|
||||
progress: sleeve.currentTaskTime / sleeve.currentTaskMaxTime,
|
||||
totalTicks: 25,
|
||||
});
|
||||
} else {
|
||||
const lines = [
|
||||
`Earnings (Pre-Synchronization):`,
|
||||
`Money: ${numeralWrapper.formatMoney(5 * sleeve.gainRatesForTask.money)} / s`,
|
||||
`Hacking Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.hack, "0.00")} / s`,
|
||||
`Strength Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.str, "0.00")} / s`,
|
||||
`Defense Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.def, "0.00")} / s`,
|
||||
`Dexterity Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.dex, "0.00")} / s`,
|
||||
`Agility Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.agi, "0.00")} / s`,
|
||||
`Charisma Exp: ${numeralWrapper.format(5 * sleeve.gainRatesForTask.cha, "0.00")} / s`
|
||||
];
|
||||
if (repGainText !== "") { lines.push(repGainText); }
|
||||
elems.currentEarningsInfo!.innerHTML = lines.join("<br>");
|
||||
|
||||
elems.taskProgressBar!.innerText = "";
|
||||
}
|
||||
}
|
||||
|
||||
const universitySelectorOptions: string[] = [
|
||||
"Study Computer Science",
|
||||
"Data Structures",
|
||||
"Networks",
|
||||
"Algorithms",
|
||||
"Management",
|
||||
"Leadership"
|
||||
];
|
||||
|
||||
const gymSelectorOptions: string[] = [
|
||||
"Train Strength",
|
||||
"Train Defense",
|
||||
"Train Dexterity",
|
||||
"Train Agility"
|
||||
];
|
||||
|
||||
// Whenever a new task is selected, the "details" selector must update accordingly
|
||||
function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSleeves: Sleeve[]) {
|
||||
if (playerRef == null) {
|
||||
throw new Error(`playerRef is null in updateSleeveTaskSelector()`);
|
||||
}
|
||||
|
||||
// Array of all companies that other sleeves are working at
|
||||
const forbiddenCompanies: string[] = [];
|
||||
for (const otherSleeve of allSleeves) {
|
||||
if (sleeve === otherSleeve) { continue; }
|
||||
if (otherSleeve.currentTask === SleeveTaskType.Company) {
|
||||
forbiddenCompanies.push(otherSleeve.currentTaskLocation);
|
||||
}
|
||||
}
|
||||
|
||||
// Array of all factions that other sleeves are working for
|
||||
const forbiddenFactions: string[] = [];
|
||||
for (const otherSleeve of allSleeves) {
|
||||
if (sleeve === otherSleeve) { continue; }
|
||||
if (otherSleeve.currentTask === SleeveTaskType.Faction) {
|
||||
forbiddenFactions.push(otherSleeve.currentTaskLocation);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset Selectors
|
||||
removeChildrenFromElement(elems.taskDetailsSelector);
|
||||
removeChildrenFromElement(elems.taskDetailsSelector2);
|
||||
elems.taskDetailsSelector2 = clearEventListeners(elems.taskDetailsSelector2!) as HTMLSelectElement;
|
||||
|
||||
const value: string = getSelectValue(elems.taskSelector);
|
||||
switch(value) {
|
||||
case "Work for Company":
|
||||
let companyCount: number = 0;
|
||||
const allJobs: string[] = Object.keys(playerRef!.jobs!);
|
||||
for (let i = 0; i < allJobs.length; ++i) {
|
||||
if (!forbiddenCompanies.includes(allJobs[i])) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(allJobs[i]));
|
||||
|
||||
// Set initial value of the 'Details' selector
|
||||
if (sleeve.currentTaskLocation === allJobs[i]) {
|
||||
elems.taskDetailsSelector!.selectedIndex = companyCount;
|
||||
}
|
||||
|
||||
++companyCount;
|
||||
}
|
||||
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("------"));
|
||||
}
|
||||
break;
|
||||
case "Work for Faction":
|
||||
let factionCount: number = 0;
|
||||
for (let i = 0; i < playerRef!.factions!.length; ++i) {
|
||||
const fac: string = playerRef!.factions[i]!;
|
||||
if (!forbiddenFactions.includes(fac)) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(fac));
|
||||
|
||||
// Set initial value of the 'Details' Selector
|
||||
if (sleeve.currentTaskLocation === fac) {
|
||||
elems.taskDetailsSelector!.selectedIndex = factionCount;
|
||||
}
|
||||
|
||||
++factionCount;
|
||||
}
|
||||
}
|
||||
|
||||
// The available faction work types depends on the faction
|
||||
elems.taskDetailsSelector!.addEventListener("change", () => {
|
||||
const facName = getSelectValue(elems.taskDetailsSelector!);
|
||||
const faction: Faction | null = Factions[facName];
|
||||
if (faction == null) {
|
||||
console.warn(`Invalid faction name when trying to update Sleeve Task Selector: ${facName}`);
|
||||
return;
|
||||
}
|
||||
const facInfo = faction.getInfo();
|
||||
removeChildrenFromElement(elems.taskDetailsSelector2!);
|
||||
let numOptionsAdded = 0;
|
||||
if (facInfo.offerHackingWork) {
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("Hacking Contracts"));
|
||||
if (sleeve.factionWorkType === FactionWorkType.Hacking) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = numOptionsAdded;
|
||||
}
|
||||
++numOptionsAdded;
|
||||
}
|
||||
if (facInfo.offerFieldWork) {
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("Field Work"));
|
||||
if (sleeve.factionWorkType === FactionWorkType.Field) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = numOptionsAdded;
|
||||
}
|
||||
++numOptionsAdded;
|
||||
}
|
||||
if (facInfo.offerSecurityWork) {
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("Security Work"));
|
||||
if (sleeve.factionWorkType === FactionWorkType.Security) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = numOptionsAdded;
|
||||
}
|
||||
++numOptionsAdded;
|
||||
}
|
||||
});
|
||||
elems.taskDetailsSelector!.dispatchEvent(new Event("change"));
|
||||
break;
|
||||
case "Commit Crime":
|
||||
let i = 0;
|
||||
for (const crimeLabel in Crimes) {
|
||||
const name: string = Crimes[crimeLabel].name;
|
||||
elems.taskDetailsSelector!.add(createOptionElement(name, crimeLabel));
|
||||
|
||||
// Set initial value for crime type
|
||||
if (sleeve.crimeType === "") { continue; }
|
||||
const crime: Crime | null = Crimes[sleeve.crimeType];
|
||||
if (crime == null) { continue; }
|
||||
if (name === crime!.name) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("------"));
|
||||
break;
|
||||
case "Take University Course":
|
||||
// First selector has class type
|
||||
for (let i = 0; i < universitySelectorOptions.length; ++i) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(universitySelectorOptions[i]));
|
||||
}
|
||||
|
||||
// Second selector has which university
|
||||
switch (sleeve.city) {
|
||||
case Cities.Aevum:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumSummitUniversity));
|
||||
break;
|
||||
case Cities.Sector12:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12RothmanUniversity));
|
||||
break;
|
||||
case Cities.Volhaven:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.VolhavenZBInstituteOfTechnology));
|
||||
break;
|
||||
default:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("No university available in city!"));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "Workout at Gym":
|
||||
// First selector has what stat is being trained
|
||||
for (let i = 0; i < gymSelectorOptions.length; ++i) {
|
||||
elems.taskDetailsSelector!.add(createOptionElement(gymSelectorOptions[i]));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.gymStatType === gymSelectorOptions[i]) {
|
||||
elems.taskDetailsSelector!.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Second selector has gym
|
||||
// In this switch statement we also set the initial value of the second selector
|
||||
switch (sleeve.city) {
|
||||
case Cities.Aevum:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumCrushFitnessGym));
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.AevumSnapFitnessGym));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.currentTaskLocation === Locations.AevumCrushFitnessGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 0;
|
||||
} else if (sleeve.currentTaskLocation === Locations.AevumSnapFitnessGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 1;
|
||||
}
|
||||
break;
|
||||
case Cities.Sector12:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12IronGym));
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.Sector12PowerhouseGym));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.currentTaskLocation === Locations.Sector12IronGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 0;
|
||||
} else if (sleeve.currentTaskLocation === Locations.Sector12PowerhouseGym) {
|
||||
elems.taskDetailsSelector2!.selectedIndex = 1;
|
||||
}
|
||||
break;
|
||||
case Cities.Volhaven:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement(Locations.VolhavenMilleniumFitnessGym));
|
||||
break;
|
||||
default:
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("No gym available in city!"));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
case "Synchronize":
|
||||
case "------":
|
||||
// No options in "Details" selector
|
||||
elems.taskDetailsSelector!.add(createOptionElement("------"));
|
||||
elems.taskDetailsSelector2!.add(createOptionElement("------"));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function setSleeveTask(sleeve: Sleeve, elems: ISleeveUIElems): boolean {
|
||||
try {
|
||||
if (playerRef == null) {
|
||||
throw new Error("playerRef is null in Sleeve UI's setSleeveTask()");
|
||||
}
|
||||
|
||||
const taskValue: string = getSelectValue(elems.taskSelector);
|
||||
const detailValue: string = getSelectValue(elems.taskDetailsSelector);
|
||||
const detailValue2: string = getSelectValue(elems.taskDetailsSelector2);
|
||||
|
||||
let res: boolean = false;
|
||||
switch(taskValue) {
|
||||
case "------":
|
||||
elems.taskDescription!.innerText = "This sleeve is currently idle";
|
||||
break;
|
||||
case "Work for Company":
|
||||
res = sleeve.workForCompany(playerRef!, detailValue);
|
||||
break;
|
||||
case "Work for Faction":
|
||||
res = sleeve.workForFaction(playerRef!, detailValue, detailValue2);
|
||||
break;
|
||||
case "Commit Crime":
|
||||
res = sleeve.commitCrime(playerRef!, detailValue);
|
||||
break;
|
||||
case "Take University Course":
|
||||
res = sleeve.takeUniversityCourse(playerRef!, detailValue2, detailValue);
|
||||
break;
|
||||
case "Workout at Gym":
|
||||
res = sleeve.workoutAtGym(playerRef!, detailValue2, detailValue);
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
sleeve.currentTask = SleeveTaskType.Recovery;
|
||||
res = true;
|
||||
break;
|
||||
case "Synchronize":
|
||||
sleeve.currentTask = SleeveTaskType.Sync;
|
||||
res = true;
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid/Unrecognized taskValue in setSleeveTask(): ${taskValue}`);
|
||||
}
|
||||
|
||||
if (res) {
|
||||
updateSleeveTaskDescription(sleeve, elems);
|
||||
} else {
|
||||
switch (taskValue) {
|
||||
case "Work for Faction":
|
||||
elems.taskDescription!.innerText = "Failed to assign sleeve to task. This is most likely because the selected faction does not offer the selected work type.";
|
||||
break;
|
||||
default:
|
||||
elems.taskDescription!.innerText = "Failed to assign sleeve to task. Invalid choice(s).";
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (routing.isOn(Page.Sleeves)) {
|
||||
updateSleevesPage();
|
||||
|
||||
// Update the task selector for all sleeves by triggering a change event
|
||||
for (const e of UIElems.sleeves!) {
|
||||
e.taskSelector!.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch(e) {
|
||||
console.error(`Exception caught in setSleeveTask(): ${e}`);
|
||||
exceptionAlert(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function updateSleeveTaskDescription(sleeve: Sleeve, elems: ISleeveUIElems): void {
|
||||
try {
|
||||
if (playerRef == null) {
|
||||
throw new Error("playerRef is null in Sleeve UI's setSleeveTask()");
|
||||
}
|
||||
|
||||
const taskValue: string = getSelectValue(elems.taskSelector);
|
||||
const detailValue: string = getSelectValue(elems.taskDetailsSelector);
|
||||
const detailValue2: string = getSelectValue(elems.taskDetailsSelector2);
|
||||
|
||||
switch(taskValue) {
|
||||
case "------":
|
||||
elems.taskDescription!.innerText = "This sleeve is currently idle";
|
||||
break;
|
||||
case "Work for Company":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently working your job at ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Work for Faction":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently doing ${detailValue2} for ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Commit Crime":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently attempting to ${Crimes[detailValue].type} (Success Rate: ${numeralWrapper.formatPercentage(Crimes[detailValue].successRate(playerRef))}).`;
|
||||
break;
|
||||
case "Take University Course":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently studying/taking a course at ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Workout at Gym":
|
||||
elems.taskDescription!.innerText = `This sleeve is currently working out at ${sleeve.currentTaskLocation}.`;
|
||||
break;
|
||||
case "Shock Recovery":
|
||||
elems.taskDescription!.innerText = "This sleeve is currently set to focus on shock recovery. This causes " +
|
||||
"the Sleeve's shock to decrease at a faster rate.";
|
||||
break;
|
||||
case "Synchronize":
|
||||
elems.taskDescription!.innerText = "This sleeve is currently set to synchronize with the original consciousness. " +
|
||||
"This causes the Sleeve's synchronization to increase."
|
||||
break;
|
||||
default:
|
||||
console.error(`Invalid/Unrecognized taskValue in updateSleeveTaskDescription(): ${taskValue}`);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(`Exception caught in updateSleeveTaskDescription(): ${e}`);
|
||||
exceptionAlert(e);
|
||||
}
|
||||
}
|
36
src/PersonObjects/Sleeve/data/SleeveFaq.ts
Normal file
36
src/PersonObjects/Sleeve/data/SleeveFaq.ts
Normal file
@ -0,0 +1,36 @@
|
||||
export const SleeveFaq: string =
|
||||
[
|
||||
"<strong><u>How do sleeves work?</strong></u><br>",
|
||||
"Sleeves are essentially clones. You can use them to perform any work type",
|
||||
"action, such as working for a company/faction or committing a crime.",
|
||||
"Having sleeves perform these tasks earns you money, experience, and reputation.<br><br>",
|
||||
"Sleeves are their own individuals, which means they each have their own",
|
||||
"experience and stats.<br><br>",
|
||||
"When a sleeve earns experience, it earns experience for itself, the player's",
|
||||
"original 'consciousness', as well as all of the player's other sleeves.<br><br>",
|
||||
|
||||
"<strong><u>What is Synchronization (Sync)?</strong></u><br>",
|
||||
"Synchronization is a measure of how aligned your consciousness is with",
|
||||
"that of your Duplicate Sleeves. It is a numerical value between 1 and 100, and",
|
||||
"it affects how much experience is earned when the sleeve is performing a task.<br><br>",
|
||||
"Let N be the sleeve's synchronization. When the sleeve earns experience by performing a",
|
||||
"task, both the sleeve and the player's original host consciousness earn N%",
|
||||
"of the amount of experience normally earned by the task. All of the player's",
|
||||
"other sleeves earn ((N/100)^2 * 100)% of the experience.<br><br>",
|
||||
"Synchronization can be increased by assigning sleeves to the 'Synchronize' task.<br><br>",
|
||||
|
||||
"<strong><u>What is Shock?</u></strong><br>",
|
||||
"Sleeve shock is a measure of how much trauma the sleeve has due to being placed in a new",
|
||||
"body. It is a numerical value between 0 and 99, where 99 indicates full shock and 0 indicates",
|
||||
"no shock. Shock affects the amount of experience earned by the sleeve.<br><br>",
|
||||
"Sleeve shock slowly decreases over time. You can further increase the rate at which",
|
||||
"it decreases by assigning sleeves to the 'Shock Recovery' task.<br><br>",
|
||||
|
||||
"<strong><u>Why can't I work for this company or faction?</u></strong><br>",
|
||||
"Only one of your sleeves can work for a given company/faction a time.",
|
||||
"To clarify further, if you have two sleeves they can work for two different",
|
||||
"companies, but they cannot both work for the same company.<br><br>",
|
||||
|
||||
"<strong><u>Do sleeves get reset when installing Augmentations or switching BitNodes?</u></strong><br>",
|
||||
"Sleeves are reset when switching BitNodes, but not when installing Augmentations."
|
||||
].join(" ");
|
220
src/Player.js
220
src/Player.js
@ -1,8 +1,9 @@
|
||||
import { Augmentations,
|
||||
applyAugmentation,
|
||||
AugmentationNames,
|
||||
PlayerOwnedAugmentation } from "./Augmentations";
|
||||
import { BitNodeMultipliers } from "./BitNodeMultipliers";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import { applyAugmentation } from "./Augmentation/AugmentationHelpers";
|
||||
import { PlayerOwnedAugmentation } from "./Augmentation/PlayerOwnedAugmentation";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { Bladeburner } from "./Bladeburner";
|
||||
import { CodingContractRewardType } from "./CodingContracts";
|
||||
import { Company } from "./Company/Company";
|
||||
import { Companies } from "./Company/Companies";
|
||||
@ -13,7 +14,8 @@ import * as posNames from "./Company/data/CompanyPosi
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import { Corporation } from "./Corporation/Corporation";
|
||||
import { Programs } from "./Programs/Programs";
|
||||
import {determineCrimeSuccess, Crimes} from "./Crimes";
|
||||
import { determineCrimeSuccess } from "./Crime/CrimeHelpers";
|
||||
import { Crimes } from "./Crime/Crimes";
|
||||
import {Engine} from "./engine";
|
||||
import { Faction } from "./Faction/Faction";
|
||||
import { Factions } from "./Faction/Factions";
|
||||
@ -21,10 +23,12 @@ import { displayFactionContent } from "./Faction/FactionHelpers";
|
||||
import {Gang, resetGangs} from "./Gang";
|
||||
import {Locations} from "./Locations";
|
||||
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
|
||||
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
|
||||
import {AllServers, Server, AddToAllServers} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
import {Settings} from "./Settings/Settings";
|
||||
import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps";
|
||||
import {SourceFiles, applySourceFile} from "./SourceFile";
|
||||
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import Decimal from "decimal.js";
|
||||
import {numeralWrapper} from "./ui/numeralFormat";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
@ -98,9 +102,13 @@ function PlayerObject() {
|
||||
this.city = Locations.Sector12;
|
||||
this.location = "";
|
||||
|
||||
//Company Information
|
||||
// Jobs that the player holds
|
||||
// Map of company name (key) -> name of company position (value. Just the name, not the CompanyPosition object)
|
||||
// The CompanyPosition name must match a key value in CompanyPositions
|
||||
this.jobs = {};
|
||||
|
||||
// Company at which player is CURRENTLY working (only valid when the player is actively working)
|
||||
this.companyName = ""; // Name of Company. Must match a key value in Companies map
|
||||
this.companyPosition = ""; // Name of Company Position. Must match a key value in CompanyPositions map
|
||||
|
||||
//Servers
|
||||
this.currentServer = ""; //IP address of Server currently being accessed through terminal
|
||||
@ -190,6 +198,11 @@ function PlayerObject() {
|
||||
this.bladeburner_analysis_mult = 1; //Field Analysis Only
|
||||
this.bladeburner_success_chance_mult = 1;
|
||||
|
||||
// Sleeves & Re-sleeving
|
||||
this.sleeves = [];
|
||||
this.resleeves = [];
|
||||
this.sleevesFromCovenant = 0; // # of Duplicate sleeves purchased from the covenant
|
||||
|
||||
//bitnode
|
||||
this.bitNodeN = 1;
|
||||
|
||||
@ -256,7 +269,7 @@ PlayerObject.prototype.prestigeAugmentation = function() {
|
||||
this.location = "";
|
||||
|
||||
this.companyName = "";
|
||||
this.companyPosition = "";
|
||||
this.jobs = {};
|
||||
|
||||
this.purchasedServers = [];
|
||||
|
||||
@ -265,6 +278,14 @@ PlayerObject.prototype.prestigeAugmentation = function() {
|
||||
|
||||
this.queuedAugmentations = [];
|
||||
|
||||
this.resleeves = [];
|
||||
|
||||
for (let i = 0; i < this.sleeves.length; ++i) {
|
||||
if (this.sleeves[i] instanceof Sleeve) {
|
||||
this.sleeves[i].resetTaskStatus();
|
||||
}
|
||||
}
|
||||
|
||||
this.isWorking = false;
|
||||
this.currentWorkFactionName = "";
|
||||
this.currentWorkFactionDescription = "";
|
||||
@ -336,7 +357,7 @@ PlayerObject.prototype.prestigeSourceFile = function() {
|
||||
this.location = "";
|
||||
|
||||
this.companyName = "";
|
||||
this.companyPosition = "";
|
||||
this.jobs = {};
|
||||
|
||||
this.purchasedServers = [];
|
||||
|
||||
@ -346,6 +367,14 @@ PlayerObject.prototype.prestigeSourceFile = function() {
|
||||
this.queuedAugmentations = [];
|
||||
this.augmentations = [];
|
||||
|
||||
this.resleeves = [];
|
||||
|
||||
// Duplicate sleeves are reset to level 1 every Bit Node (but the number of sleeves you have persists)
|
||||
this.sleeves.length = SourceFileFlags[10] + this.sleevesFromCovenant;
|
||||
for (let i = 0; i < this.sleeves.length; ++i) {
|
||||
this.sleeves[i] = new Sleeve();
|
||||
}
|
||||
|
||||
this.isWorking = false;
|
||||
this.currentWorkFactionName = "";
|
||||
this.currentWorkFactionDescription = "";
|
||||
@ -434,11 +463,11 @@ PlayerObject.prototype.calculateSkill = function(exp, mult=1) {
|
||||
|
||||
PlayerObject.prototype.updateSkillLevels = function() {
|
||||
this.hacking_skill = Math.max(1, Math.floor(this.calculateSkill(this.hacking_exp, this.hacking_mult * BitNodeMultipliers.HackingLevelMultiplier)));
|
||||
this.strength = this.calculateSkill(this.strength_exp, this.strength_mult);
|
||||
this.defense = this.calculateSkill(this.defense_exp, this.defense_mult);
|
||||
this.dexterity = this.calculateSkill(this.dexterity_exp, this.dexterity_mult);
|
||||
this.agility = this.calculateSkill(this.agility_exp, this.agility_mult);
|
||||
this.charisma = this.calculateSkill(this.charisma_exp, this.charisma_mult);
|
||||
this.strength = Math.max(1, Math.floor(this.calculateSkill(this.strength_exp, this.strength_mult * BitNodeMultipliers.StrengthLevelMultiplier)));
|
||||
this.defense = Math.max(1, Math.floor(this.calculateSkill(this.defense_exp, this.defense_mult * BitNodeMultipliers.DefenseLevelMultiplier)));
|
||||
this.dexterity = Math.max(1, Math.floor(this.calculateSkill(this.dexterity_exp, this.dexterity_mult * BitNodeMultipliers.DexterityLevelMultiplier)));
|
||||
this.agility = Math.max(1, Math.floor(this.calculateSkill(this.agility_exp, this.agility_mult * BitNodeMultipliers.AgilityLevelMultiplier)));
|
||||
this.charisma = Math.max(1, Math.floor(this.calculateSkill(this.charisma_exp, this.charisma_mult * BitNodeMultipliers.CharismaLevelMultiplier)));
|
||||
|
||||
if (this.intelligence > 0) {
|
||||
this.intelligence = Math.floor(this.calculateSkill(this.intelligence_exp));
|
||||
@ -524,6 +553,14 @@ PlayerObject.prototype.loseMoney = function(money) {
|
||||
this.money = this.money.minus(money);
|
||||
}
|
||||
|
||||
PlayerObject.prototype.canAfford = function(cost) {
|
||||
if (isNaN(cost)) {
|
||||
console.error(`NaN passed into Player.canAfford()`);
|
||||
return false;
|
||||
}
|
||||
return this.money.gte(cost);
|
||||
}
|
||||
|
||||
PlayerObject.prototype.gainHackingExp = function(exp) {
|
||||
if (isNaN(exp)) {
|
||||
console.log("ERR: NaN passed into Player.gainHackingExp()"); return;
|
||||
@ -665,9 +702,10 @@ PlayerObject.prototype.processWorkEarnings = function(numCycles=1) {
|
||||
}
|
||||
|
||||
/* Working for Company */
|
||||
PlayerObject.prototype.startWork = function() {
|
||||
PlayerObject.prototype.startWork = function(companyName) {
|
||||
this.resetWorkStatus();
|
||||
this.isWorking = true;
|
||||
this.companyName = companyName;
|
||||
this.workType = CONSTANTS.WorkTypeCompany;
|
||||
|
||||
this.workHackExpGainRate = this.getWorkHackExpGain();
|
||||
@ -718,8 +756,10 @@ PlayerObject.prototype.work = function(numCycles) {
|
||||
companyRep = comp.playerReputation;
|
||||
}
|
||||
|
||||
const position = this.jobs[this.companyName];
|
||||
|
||||
var txt = document.getElementById("work-in-progress-text");
|
||||
txt.innerHTML = "You are currently working as a " + this.companyPosition +
|
||||
txt.innerHTML = "You are currently working as a " + position +
|
||||
" at " + this.companyName + " (Current Company Reputation: " +
|
||||
numeralWrapper.format(companyRep, '0,0') + ")<br><br>" +
|
||||
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
|
||||
@ -786,9 +826,10 @@ PlayerObject.prototype.finishWork = function(cancelled, sing=false) {
|
||||
this.resetWorkStatus();
|
||||
}
|
||||
|
||||
PlayerObject.prototype.startWorkPartTime = function() {
|
||||
PlayerObject.prototype.startWorkPartTime = function(companyName) {
|
||||
this.resetWorkStatus();
|
||||
this.isWorking = true;
|
||||
this.companyName = companyName;
|
||||
this.workType = CONSTANTS.WorkTypeCompanyPartTime;
|
||||
|
||||
this.workHackExpGainRate = this.getWorkHackExpGain();
|
||||
@ -838,9 +879,11 @@ PlayerObject.prototype.workPartTime = function(numCycles) {
|
||||
companyRep = comp.playerReputation;
|
||||
}
|
||||
|
||||
const position = this.jobs[this.companyName];
|
||||
|
||||
var txt = document.getElementById("work-in-progress-text");
|
||||
txt.innerHTML = "You are currently working as a " + this.companyPosition +
|
||||
" at " + Player.companyName + " (Current Company Reputation: " +
|
||||
txt.innerHTML = "You are currently working as a " + position +
|
||||
" at " + this.companyName + " (Current Company Reputation: " +
|
||||
numeralWrapper.format(companyRep, '0,0') + ")<br><br>" +
|
||||
"You have been working for " + convertTimeMsToTimeElapsedString(this.timeWorked) + "<br><br>" +
|
||||
"You have earned: <br><br>" +
|
||||
@ -1073,9 +1116,10 @@ PlayerObject.prototype.getWorkMoneyGain = function() {
|
||||
if (hasBn11SF) { bn11Mult = 1 + (company.favor / 100); }
|
||||
|
||||
// Get base salary
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (companyPosition == null) {
|
||||
console.error(`Could not find CompanyPosition object for ${this.companyPosition}. Work salary will be 0`);
|
||||
console.error(`Could not find CompanyPosition object for ${companyPositionName}. Work salary will be 0`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1085,10 +1129,11 @@ PlayerObject.prototype.getWorkMoneyGain = function() {
|
||||
//Hack exp gained per game cycle
|
||||
PlayerObject.prototype.getWorkHackExpGain = function() {
|
||||
const company = Companies[this.companyName];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (company == null || companyPosition == null) {
|
||||
console.error([`Could not find Company object for ${this.companyName}`,
|
||||
`or CompanyPosition object for ${this.companyPosition}.`,
|
||||
`or CompanyPosition object for ${companyPositionName}.`,
|
||||
`Work hack exp gain will be 0`].join(" "));
|
||||
return 0;
|
||||
}
|
||||
@ -1099,10 +1144,11 @@ PlayerObject.prototype.getWorkHackExpGain = function() {
|
||||
//Str exp gained per game cycle
|
||||
PlayerObject.prototype.getWorkStrExpGain = function() {
|
||||
const company = Companies[this.companyName];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (company == null || companyPosition == null) {
|
||||
console.error([`Could not find Company object for ${this.companyName}`,
|
||||
`or CompanyPosition object for ${this.companyPosition}.`,
|
||||
`or CompanyPosition object for ${companyPositionName}.`,
|
||||
`Work str exp gain will be 0`].join(" "));
|
||||
return 0;
|
||||
}
|
||||
@ -1113,10 +1159,11 @@ PlayerObject.prototype.getWorkStrExpGain = function() {
|
||||
//Def exp gained per game cycle
|
||||
PlayerObject.prototype.getWorkDefExpGain = function() {
|
||||
const company = Companies[this.companyName];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (company == null || companyPosition == null) {
|
||||
console.error([`Could not find Company object for ${this.companyName}`,
|
||||
`or CompanyPosition object for ${this.companyPosition}.`,
|
||||
`or CompanyPosition object for ${companyPositionName}.`,
|
||||
`Work def exp gain will be 0`].join(" "));
|
||||
return 0;
|
||||
}
|
||||
@ -1127,10 +1174,11 @@ PlayerObject.prototype.getWorkDefExpGain = function() {
|
||||
//Dex exp gained per game cycle
|
||||
PlayerObject.prototype.getWorkDexExpGain = function() {
|
||||
const company = Companies[this.companyName];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (company == null || companyPosition == null) {
|
||||
console.error([`Could not find Company object for ${this.companyName}`,
|
||||
`or CompanyPosition object for ${this.companyPosition}.`,
|
||||
`or CompanyPosition object for ${companyPositionName}.`,
|
||||
`Work dex exp gain will be 0`].join(" "));
|
||||
return 0;
|
||||
}
|
||||
@ -1141,10 +1189,11 @@ PlayerObject.prototype.getWorkDexExpGain = function() {
|
||||
//Agi exp gained per game cycle
|
||||
PlayerObject.prototype.getWorkAgiExpGain = function() {
|
||||
const company = Companies[this.companyName];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (company == null || companyPosition == null) {
|
||||
console.error([`Could not find Company object for ${this.companyName}`,
|
||||
`or CompanyPosition object for ${this.companyPosition}.`,
|
||||
`or CompanyPosition object for ${companyPositionName}.`,
|
||||
`Work agi exp gain will be 0`].join(" "));
|
||||
return 0;
|
||||
}
|
||||
@ -1155,10 +1204,11 @@ PlayerObject.prototype.getWorkAgiExpGain = function() {
|
||||
//Charisma exp gained per game cycle
|
||||
PlayerObject.prototype.getWorkChaExpGain = function() {
|
||||
const company = Companies[this.companyName];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (company == null || companyPosition == null) {
|
||||
console.error([`Could not find Company object for ${this.companyName}`,
|
||||
`or CompanyPosition object for ${this.companyPosition}.`,
|
||||
`or CompanyPosition object for ${companyPositionName}.`,
|
||||
`Work cha exp gain will be 0`].join(" "));
|
||||
return 0;
|
||||
}
|
||||
@ -1169,10 +1219,11 @@ PlayerObject.prototype.getWorkChaExpGain = function() {
|
||||
//Reputation gained per game cycle
|
||||
PlayerObject.prototype.getWorkRepGain = function() {
|
||||
const company = Companies[this.companyName];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
const companyPositionName = this.jobs[this.companyName];
|
||||
const companyPosition = CompanyPositions[companyPositionName];
|
||||
if (company == null || companyPosition == null) {
|
||||
console.error([`Could not find Company object for ${this.companyName}`,
|
||||
`or CompanyPosition object for ${this.companyPosition}.`,
|
||||
`or CompanyPosition object for ${companyPositionName}.`,
|
||||
`Work rep gain will be 0`].join(" "));
|
||||
return 0;
|
||||
}
|
||||
@ -1455,7 +1506,9 @@ PlayerObject.prototype.finishClass = function(sing=false) {
|
||||
}
|
||||
|
||||
//The EXP and $ gains are hardcoded. Time is in ms
|
||||
PlayerObject.prototype.startCrime = function(hackExp, strExp, defExp, dexExp, agiExp, chaExp, money, time, singParams=null) {
|
||||
PlayerObject.prototype.startCrime = function(crimeType, hackExp, strExp, defExp, dexExp, agiExp, chaExp, money, time, singParams=null) {
|
||||
this.crimeType = crimeType;
|
||||
|
||||
this.resetWorkStatus();
|
||||
this.isWorking = true;
|
||||
this.workType = CONSTANTS.WorkTypeCrime;
|
||||
@ -1669,7 +1722,7 @@ PlayerObject.prototype.applyForJob = function(entryPosType, sing=false) {
|
||||
if (this.companyName !== "") {
|
||||
currCompany = Companies[this.companyName];
|
||||
}
|
||||
const currPositionName = this.companyPosition;
|
||||
const currPositionName = this.jobs[this.companyName];
|
||||
|
||||
// Get company that's being applied to
|
||||
const company = Companies[this.location]; //Company being applied to
|
||||
@ -1726,33 +1779,14 @@ PlayerObject.prototype.applyForJob = function(entryPosType, sing=false) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Lose reputation from a Company if you are leaving it for another job
|
||||
let leaveCompany = false;
|
||||
let oldCompanyName = "";
|
||||
if (currCompany != null) {
|
||||
if (currCompany.name != company.name) {
|
||||
leaveCompany = true;
|
||||
oldCompanyName = currCompany.name;
|
||||
currCompany.playerReputation -= 1000;
|
||||
if (currCompany.playerReputation < 0) { currCompany.playerReputation = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
this.companyName = company.name;
|
||||
this.companyPosition = pos.name;
|
||||
this.jobs[company.name] = pos.name;
|
||||
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
|
||||
if (leaveCompany) {
|
||||
if (sing) { return true; }
|
||||
dialogBoxCreate([`Congratulations! You were offered a new job at ${this.companyName} as a ${pos.name}!`,
|
||||
`You lost 1000 reputation at your old company ${oldCompanyName} because you left.`].join("<br>"));
|
||||
} else {
|
||||
if (sing) { return true; }
|
||||
dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!");
|
||||
}
|
||||
if (sing) { return true; }
|
||||
dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!");
|
||||
|
||||
Engine.loadLocationContent();
|
||||
}
|
||||
@ -1772,7 +1806,8 @@ PlayerObject.prototype.getNextCompanyPosition = function(company, entryPosType)
|
||||
//If the entry pos type and the player's current position have the same type,
|
||||
//return the player's "nextCompanyPosition". Otherwise return the entryposType
|
||||
//Employed at this company, so just return the next position if it exists.
|
||||
const currentPosition = CompanyPositions[this.companyPosition];
|
||||
const currentPositionName = this.jobs[this.companyName];
|
||||
const currentPosition = CompanyPositions[currentPositionName];
|
||||
if ((currentPosition.isSoftwareJob() && entryPosType.isSoftwareJob()) ||
|
||||
(currentPosition.isITJob() && entryPosType.isITJob()) ||
|
||||
(currentPosition.isBusinessJob() && entryPosType.isBusinessJob()) ||
|
||||
@ -1849,7 +1884,7 @@ PlayerObject.prototype.applyForEmployeeJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[1]])) {
|
||||
this.companyName = company.name;
|
||||
this.companyPosition = posNames.MiscCompanyPositions[1];
|
||||
this.jobs[company.name] = posNames.MiscCompanyPositions[1];
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
@ -1865,7 +1900,7 @@ PlayerObject.prototype.applyForPartTimeEmployeeJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[1]])) {
|
||||
this.companyName = company.name;
|
||||
this.companyPosition = posNames.PartTimeCompanyPositions[1];
|
||||
this.jobs[company.name] = posNames.PartTimeCompanyPositions[1];
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
@ -1881,7 +1916,7 @@ PlayerObject.prototype.applyForWaiterJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions[posNames.MiscCompanyPositions[0]])) {
|
||||
this.companyName = company.name;
|
||||
this.companyPosition = posNames.MiscCompanyPositions[0];
|
||||
this.jobs[company.name] = posNames.MiscCompanyPositions[0];
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
@ -1897,7 +1932,7 @@ PlayerObject.prototype.applyForPartTimeWaiterJob = function(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[0]])) {
|
||||
this.companyName = company.name;
|
||||
this.companyPosition = posNames.PartTimeCompanyPositions[0];
|
||||
this.jobs[company.name] = posNames.PartTimeCompanyPositions[0];
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
@ -1992,6 +2027,9 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
companyRep = company.playerReputation;
|
||||
}
|
||||
|
||||
const allCompanies = Object.keys(this.jobs);
|
||||
const allPositions = Object.values(this.jobs);
|
||||
|
||||
//Illuminati
|
||||
var illuminatiFac = Factions["Illuminati"];
|
||||
if (!illuminatiFac.isBanned && !illuminatiFac.isMember && !illuminatiFac.alreadyInvited &&
|
||||
@ -2030,14 +2068,14 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
//ECorp
|
||||
var ecorpFac = Factions["ECorp"];
|
||||
if (!ecorpFac.isBanned && !ecorpFac.isMember && !ecorpFac.alreadyInvited &&
|
||||
this.companyName == Locations.AevumECorp && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.AevumECorp) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(ecorpFac);
|
||||
}
|
||||
|
||||
//MegaCorp
|
||||
var megacorpFac = Factions["MegaCorp"];
|
||||
if (!megacorpFac.isBanned && !megacorpFac.isMember && !megacorpFac.alreadyInvited &&
|
||||
this.companyName == Locations.Sector12MegaCorp && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.Sector12MegaCorp) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(megacorpFac);
|
||||
}
|
||||
|
||||
@ -2045,42 +2083,42 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
var bachmanandassociatesFac = Factions["Bachman & Associates"];
|
||||
if (!bachmanandassociatesFac.isBanned && !bachmanandassociatesFac.isMember &&
|
||||
!bachmanandassociatesFac.alreadyInvited &&
|
||||
this.companyName == Locations.AevumBachmanAndAssociates && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.AevumBachmanAndAssociates) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(bachmanandassociatesFac);
|
||||
}
|
||||
|
||||
//Blade Industries
|
||||
var bladeindustriesFac = Factions["Blade Industries"];
|
||||
if (!bladeindustriesFac.isBanned && !bladeindustriesFac.isMember && !bladeindustriesFac.alreadyInvited &&
|
||||
this.companyName == Locations.Sector12BladeIndustries && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.Sector12BladeIndustries) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(bladeindustriesFac);
|
||||
}
|
||||
|
||||
//NWO
|
||||
var nwoFac = Factions["NWO"];
|
||||
if (!nwoFac.isBanned && !nwoFac.isMember && !nwoFac.alreadyInvited &&
|
||||
this.companyName == Locations.VolhavenNWO && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.VolhavenNWO) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(nwoFac);
|
||||
}
|
||||
|
||||
//Clarke Incorporated
|
||||
var clarkeincorporatedFac = Factions["Clarke Incorporated"];
|
||||
if (!clarkeincorporatedFac.isBanned && !clarkeincorporatedFac.isMember && !clarkeincorporatedFac.alreadyInvited &&
|
||||
this.companyName == Locations.AevumClarkeIncorporated && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.AevumClarkeIncorporated) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(clarkeincorporatedFac);
|
||||
}
|
||||
|
||||
//OmniTek Incorporated
|
||||
var omnitekincorporatedFac = Factions["OmniTek Incorporated"];
|
||||
if (!omnitekincorporatedFac.isBanned && !omnitekincorporatedFac.isMember && !omnitekincorporatedFac.alreadyInvited &&
|
||||
this.companyName == Locations.VolhavenOmniTekIncorporated && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.VolhavenOmniTekIncorporated) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(omnitekincorporatedFac);
|
||||
}
|
||||
|
||||
//Four Sigma
|
||||
var foursigmaFac = Factions["Four Sigma"];
|
||||
if (!foursigmaFac.isBanned && !foursigmaFac.isMember && !foursigmaFac.alreadyInvited &&
|
||||
this.companyName == Locations.Sector12FourSigma && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.Sector12FourSigma) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(foursigmaFac);
|
||||
}
|
||||
|
||||
@ -2088,7 +2126,7 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
var kuaigonginternationalFac = Factions["KuaiGong International"];
|
||||
if (!kuaigonginternationalFac.isBanned && !kuaigonginternationalFac.isMember &&
|
||||
!kuaigonginternationalFac.alreadyInvited &&
|
||||
this.companyName == Locations.ChongqingKuaiGongInternational && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
allCompanies.includes(Locations.ChongqingKuaiGongInternational) && companyRep >= CONSTANTS.CorpFactionRepRequirement) {
|
||||
invitedFactions.push(kuaigonginternationalFac);
|
||||
}
|
||||
|
||||
@ -2101,7 +2139,7 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
if (!fulcrumsecrettechonologiesFac.isBanned && !fulcrumsecrettechonologiesFac.isMember &&
|
||||
!fulcrumsecrettechonologiesFac.alreadyInvited &&
|
||||
fulcrumSecretServer.manuallyHacked &&
|
||||
this.companyName == Locations.AevumFulcrumTechnologies && companyRep >= 250000) {
|
||||
allCompanies.includes(Locations.AevumFulcrumTechnologies) && companyRep >= 250000) {
|
||||
invitedFactions.push(fulcrumsecrettechonologiesFac);
|
||||
}
|
||||
}
|
||||
@ -2184,8 +2222,8 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
if (!speakersforthedeadFac.isBanned && !speakersforthedeadFac.isMember && !speakersforthedeadFac.alreadyInvited &&
|
||||
this.hacking_skill >= 100 && this.strength >= 300 && this.defense >= 300 &&
|
||||
this.dexterity >= 300 && this.agility >= 300 && this.numPeopleKilled >= 30 &&
|
||||
this.karma <= -45 && this.companyName != Locations.Sector12CIA &&
|
||||
this.companyName != Locations.Sector12NSA) {
|
||||
this.karma <= -45 && !allCompanies.includes(Locations.Sector12CIA) &&
|
||||
!allCompanies.includes(Locations.Sector12NSA)) {
|
||||
invitedFactions.push(speakersforthedeadFac);
|
||||
}
|
||||
|
||||
@ -2194,8 +2232,8 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
if (!thedarkarmyFac.isBanned && !thedarkarmyFac.isMember && !thedarkarmyFac.alreadyInvited &&
|
||||
this.hacking_skill >= 300 && this.strength >= 300 && this.defense >= 300 &&
|
||||
this.dexterity >= 300 && this.agility >= 300 && this.city == Locations.Chongqing &&
|
||||
this.numPeopleKilled >= 5 && this.karma <= -45 && this.companyName != Locations.Sector12CIA &&
|
||||
this.companyName != Locations.Sector12NSA) {
|
||||
this.numPeopleKilled >= 5 && this.karma <= -45 && !allCompanies.includes(Locations.Sector12CIA) &&
|
||||
!allCompanies.includes(Locations.Sector12NSA)) {
|
||||
invitedFactions.push(thedarkarmyFac);
|
||||
}
|
||||
|
||||
@ -2206,18 +2244,16 @@ PlayerObject.prototype.checkForFactionInvitations = function() {
|
||||
this.dexterity >= 200 && this.agility >= 200 &&
|
||||
(this.city == Locations.Aevum || this.city == Locations.Sector12) &&
|
||||
this.money.gte(10000000) && this.karma <= -90 &&
|
||||
this.companyName != Locations.Sector12CIA && this.companyName != Locations.Sector12NSA) {
|
||||
!allCompanies.includes(Locations.Sector12CIA) && !allCompanies.includes(Locations.Sector12NSA)) {
|
||||
invitedFactions.push(thesyndicateFac);
|
||||
}
|
||||
|
||||
//Silhouette
|
||||
var silhouetteFac = Factions["Silhouette"];
|
||||
const companyPosition = CompanyPositions[this.companyPosition];
|
||||
if (!silhouetteFac.isBanned && !silhouetteFac.isMember && !silhouetteFac.alreadyInvited &&
|
||||
companyPosition != null &&
|
||||
(companyPosition.name == "Chief Technology Officer" ||
|
||||
companyPosition.name == "Chief Financial Officer" ||
|
||||
companyPosition.name == "Chief Executive Officer") &&
|
||||
(allPositions.includes("Chief Technology Officer") ||
|
||||
allPositions.includes("Chief Financial Officer") ||
|
||||
allPositions.includes("Chief Executive Officer")) &&
|
||||
this.money.gte(15000000) && this.karma <= -22) {
|
||||
invitedFactions.push(silhouetteFac);
|
||||
}
|
||||
@ -2289,6 +2325,18 @@ PlayerObject.prototype.startGang = function(factionName, hacking) {
|
||||
this.gang = new Gang(factionName, hacking);
|
||||
}
|
||||
|
||||
/*************** Corporation ****************/
|
||||
PlayerObject.prototype.hasCorporation = function() {
|
||||
if (this.corporation == null) { return false; }
|
||||
return (this.corporation instanceof Corporation);
|
||||
}
|
||||
|
||||
/*************** Bladeburner ****************/
|
||||
PlayerObject.prototype.inBladeburner = function() {
|
||||
if (this.bladeburner == null) { return false; }
|
||||
return (this.bladeburner instanceof Bladeburner);
|
||||
}
|
||||
|
||||
/************* BitNodes **************/
|
||||
PlayerObject.prototype.setBitNodeNumber = function(n) {
|
||||
this.bitNodeN = n;
|
||||
|
@ -1,7 +1,9 @@
|
||||
import {deleteActiveScriptsItem} from "./ActiveScriptsUI";
|
||||
import {Augmentations, augmentationExists,
|
||||
initAugmentations, AugmentationNames} from "./Augmentations";
|
||||
import {initBitNodeMultipliers} from "./BitNode";
|
||||
import { Augmentations } from "./Augmentation/Augmentations";
|
||||
import { augmentationExists,
|
||||
initAugmentations } from "./Augmentation/AugmentationHelpers";
|
||||
import { AugmentationNames } from "./Augmentation/data/AugmentationNames";
|
||||
import { initBitNodeMultipliers } from "./BitNode/BitNode";
|
||||
import {Bladeburner} from "./Bladeburner";
|
||||
import {writeCinematicText} from "./CinematicText";
|
||||
import {Companies, initCompanies} from "./Company/Companies";
|
||||
@ -24,12 +26,13 @@ import {AllServers, AddToAllServers,
|
||||
initForeignServers, Server,
|
||||
prestigeAllServers,
|
||||
prestigeHomeComputer} from "./Server";
|
||||
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import {SpecialServerIps, SpecialServerIpsMap,
|
||||
prestigeSpecialServerIps,
|
||||
SpecialServerNames} from "./SpecialServerIps";
|
||||
import {initStockMarket, initSymbolToStockMap,
|
||||
stockMarketContentCreated,
|
||||
setStockMarketContentCreated} from "./StockMarket";
|
||||
setStockMarketContentCreated} from "./StockMarket/StockMarket";
|
||||
import {Terminal, postNetburnerText} from "./Terminal";
|
||||
import Decimal from "decimal.js";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
@ -169,6 +172,7 @@ function prestigeAugmentation() {
|
||||
//Prestige by destroying Bit Node and gaining a Source File
|
||||
function prestigeSourceFile() {
|
||||
initBitNodeMultipliers();
|
||||
updateSourceFileFlags(Player);
|
||||
|
||||
Player.prestigeSourceFile();
|
||||
prestigeWorkerScripts(); //Delete all Worker Scripts objects
|
||||
@ -307,6 +311,11 @@ function prestigeSourceFile() {
|
||||
Player.hasTixApiAccess = true;
|
||||
}
|
||||
|
||||
// Bit Node 10: Digital Carbon
|
||||
if (Player.bitNodeN === 10) {
|
||||
dialogBoxCreate("Visit VitaLife in New Tokyo if you'd like to purchase a new sleeve!");
|
||||
}
|
||||
|
||||
//Reset Stock market, gang, and corporation
|
||||
if (Player.hasWseAccount) {
|
||||
initStockMarket();
|
||||
|
@ -1,10 +1,11 @@
|
||||
import {BitNodes} from "./BitNode";
|
||||
import {Engine} from "./engine";
|
||||
import {Player} from "./Player";
|
||||
import {prestigeSourceFile} from "./Prestige";
|
||||
import {SourceFiles, SourceFile,
|
||||
PlayerOwnedSourceFile} from "./SourceFile";
|
||||
import {Terminal} from "./Terminal";
|
||||
import { BitNodes } from "./BitNode/BitNode";
|
||||
import { Engine } from "./engine";
|
||||
import { Player } from "./Player";
|
||||
import { prestigeSourceFile } from "./Prestige";
|
||||
import { SourceFiles,
|
||||
SourceFile } from "./SourceFile";
|
||||
import { PlayerOwnedSourceFile } from "./SourceFile/PlayerOwnedSourceFile";
|
||||
import { Terminal } from "./Terminal";
|
||||
|
||||
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
@ -56,7 +57,7 @@ function hackWorldDaemon(currentNodeNumber, flume=false) {
|
||||
// Clear Red Pill screen first
|
||||
var container = document.getElementById("red-pill-content");
|
||||
removeChildrenFromElement(container);
|
||||
|
||||
|
||||
redPillFlag = true;
|
||||
Engine.loadRedPillContent();
|
||||
return writeRedPillLine("[ERROR] SEMPOOL INVALID").then(function() {
|
||||
@ -212,7 +213,8 @@ function loadBitVerse(destroyedBitNodeNum, flume=false) {
|
||||
var elem = clearEventListeners(elemId);
|
||||
if (elem == null) {return;}
|
||||
if (i === 1 || i === 2 || i === 3 || i === 4 || i === 5 ||
|
||||
i === 6 || i === 7 || i === 8 || i === 11 || i === 12) {
|
||||
i === 6 || i === 7 || i === 8 || i === 10 || i === 11 ||
|
||||
i === 12) {
|
||||
elem.addEventListener("click", function() {
|
||||
var bitNodeKey = "BitNode" + i;
|
||||
var bitNode = BitNodes[bitNodeKey];
|
||||
|
@ -14,9 +14,9 @@ import {loadMessages, initMessages, Messages} from "./Message";
|
||||
import {Player, loadPlayer} from "./Player";
|
||||
import {loadAllRunningScripts} from "./Script";
|
||||
import {AllServers, loadAllServers} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
import {Settings} from "./Settings/Settings";
|
||||
import {loadSpecialServerIps, SpecialServerIps} from "./SpecialServerIps";
|
||||
import {loadStockMarket, StockMarket} from "./StockMarket";
|
||||
import {loadStockMarket, StockMarket} from "./StockMarket/StockMarket";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {gameOptionsBoxClose} from "../utils/GameOptions";
|
||||
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
|
||||
@ -151,6 +151,16 @@ function evaluateVersionCompatibility(ver) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This version allowed players to hold multiple jobs
|
||||
if (ver < "0.43.0") {
|
||||
if (Player.companyName !== "" && Player.companyPosition != null && Player.companyPosition !== "") {
|
||||
console.log("Copied player's companyName and companyPosition properties to the Player.jobs map for v0.43.0");
|
||||
Player.jobs[Player.companyName] = Player.companyPosition;
|
||||
}
|
||||
|
||||
delete Player.companyPosition;
|
||||
}
|
||||
}
|
||||
|
||||
function loadGame(saveString) {
|
||||
|
299
src/Script.js
299
src/Script.js
@ -1,19 +1,3 @@
|
||||
var ace = require('brace');
|
||||
var beautify = require('js-beautify').js_beautify;
|
||||
require('brace/mode/javascript');
|
||||
require('../netscript');
|
||||
require('brace/theme/chaos');
|
||||
require('brace/theme/chrome');
|
||||
require('brace/theme/monokai');
|
||||
require('brace/theme/solarized_dark');
|
||||
require('brace/theme/solarized_light');
|
||||
require('brace/theme/terminal');
|
||||
require('brace/theme/twilight');
|
||||
require('brace/theme/xcode');
|
||||
require("brace/keybinding/vim");
|
||||
require("brace/keybinding/emacs");
|
||||
require("brace/ext/language_tools");
|
||||
|
||||
// Importing this doesn't work for some reason.
|
||||
const walk = require("acorn/dist/walk");
|
||||
|
||||
@ -26,8 +10,11 @@ import {evaluateImport} from "./NetscriptEvaluator";
|
||||
import {NetscriptFunctions} from "./NetscriptFunctions";
|
||||
import {addWorkerScript, WorkerScript} from "./NetscriptWorker";
|
||||
import {Player} from "./Player";
|
||||
import { AceEditor } from "./ScriptEditor/Ace";
|
||||
import { CodeMirrorEditor } from "./ScriptEditor/CodeMirror";
|
||||
import {AllServers, processSingleServerGrowth} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
import { Settings } from "./Settings/Settings";
|
||||
import { EditorSetting } from "./Settings/SettingEnums";
|
||||
import {post} from "./ui/postToTerminal";
|
||||
import {TextFile} from "./TextFile";
|
||||
import {parse, Node} from "../utils/acorn";
|
||||
@ -41,47 +28,40 @@ import {createElement} from "../utils/uiHelpers/createE
|
||||
import {getTimestamp} from "../utils/helpers/getTimestamp";
|
||||
import {roundToTwo} from "../utils/helpers/roundToTwo";
|
||||
|
||||
var keybindings = {
|
||||
ace: null,
|
||||
vim: "ace/keyboard/vim",
|
||||
emacs: "ace/keyboard/emacs",
|
||||
};
|
||||
|
||||
function isScriptFilename(f) {
|
||||
return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns");
|
||||
}
|
||||
|
||||
var scriptEditorRamCheck = null, scriptEditorRamText = null;
|
||||
function scriptEditorInit() {
|
||||
//Create buttons at the bottom of script editor
|
||||
var wrapper = document.getElementById("script-editor-buttons-wrapper");
|
||||
// Wrapper container that holds all the buttons below the script editor
|
||||
const wrapper = document.getElementById("script-editor-buttons-wrapper");
|
||||
if (wrapper == null) {
|
||||
console.log("Error finding 'script-editor-buttons-wrapper'");
|
||||
return;
|
||||
console.error("Could not find 'script-editor-buttons-wrapper'");
|
||||
return false;
|
||||
}
|
||||
var beautifyButton = createElement("a", {
|
||||
class:"a-link-button", display:"inline-block",
|
||||
innerText:"Beautify",
|
||||
|
||||
// Beautify button
|
||||
const beautifyButton = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Beautify",
|
||||
clickListener:()=>{
|
||||
beautifyScript();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
var closeButton = createElement("a", {
|
||||
class:"a-link-button", display:"inline-block",
|
||||
innerText:"Save & Close (Ctrl/Cmd + b)",
|
||||
clickListener:()=>{
|
||||
saveAndCloseScriptEditor();
|
||||
let editor = getCurrentEditor();
|
||||
if (editor != null) {
|
||||
editor.beautifyScript();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Text that displays RAM calculation
|
||||
scriptEditorRamText = createElement("p", {
|
||||
display:"inline-block", margin:"10px", id:"script-editor-status-text"
|
||||
});
|
||||
|
||||
var checkboxLabel = createElement("label", {
|
||||
// Label for checkbox (defined below)
|
||||
const checkboxLabel = createElement("label", {
|
||||
for:"script-editor-ram-check", margin:"4px", marginTop: "8px",
|
||||
innerText:"Dynamic RAM Usage Checker", color:"white",
|
||||
tooltip:"Enable/Disable the dynamic RAM Usage display. You may " +
|
||||
@ -89,18 +69,34 @@ function scriptEditorInit() {
|
||||
"performance issues"
|
||||
});
|
||||
|
||||
// Checkbox for enabling/disabling dynamic RAM calculation
|
||||
scriptEditorRamCheck = createElement("input", {
|
||||
type:"checkbox", name:"script-editor-ram-check", id:"script-editor-ram-check",
|
||||
margin:"4px", marginTop: "8px",
|
||||
});
|
||||
scriptEditorRamCheck.checked = true;
|
||||
|
||||
var documentationButton = createElement("a", {
|
||||
display:"inline-block", class:"a-link-button", innerText:"Netscript Documentation",
|
||||
// Link to Netscript documentation
|
||||
const documentationButton = createElement("a", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
href:"https://bitburner.readthedocs.io/en/latest/index.html",
|
||||
target:"_blank"
|
||||
innerText:"Netscript Documentation",
|
||||
target:"_blank",
|
||||
});
|
||||
|
||||
// Save and Close button
|
||||
const closeButton = createElement("button", {
|
||||
class: "std-button",
|
||||
display: "inline-block",
|
||||
innerText: "Save & Close (Ctrl/Cmd + b)",
|
||||
clickListener:()=>{
|
||||
saveAndCloseScriptEditor();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Add all buttons to the UI
|
||||
wrapper.appendChild(beautifyButton);
|
||||
wrapper.appendChild(closeButton);
|
||||
wrapper.appendChild(scriptEditorRamText);
|
||||
@ -108,146 +104,86 @@ function scriptEditorInit() {
|
||||
wrapper.appendChild(checkboxLabel);
|
||||
wrapper.appendChild(documentationButton);
|
||||
|
||||
//Initialize ACE Script editor
|
||||
var editor = ace.edit('javascript-editor');
|
||||
editor.getSession().setMode('ace/mode/netscript');
|
||||
editor.setTheme('ace/theme/monokai');
|
||||
document.getElementById('javascript-editor').style.fontSize='16px';
|
||||
editor.setOption("showPrintMargin", false);
|
||||
// Initialize editors
|
||||
const initParams = {
|
||||
saveAndCloseFn: saveAndCloseScriptEditor,
|
||||
quitFn: Engine.loadTerminalContent,
|
||||
}
|
||||
|
||||
/* Script editor options */
|
||||
//Theme
|
||||
var themeDropdown = document.getElementById("script-editor-option-theme");
|
||||
if (Settings.EditorTheme) {
|
||||
var initialIndex = 2;
|
||||
for (var i = 0; i < themeDropdown.options.length; ++i) {
|
||||
if (themeDropdown.options[i].value === Settings.EditorTheme) {
|
||||
initialIndex = i;
|
||||
break;
|
||||
}
|
||||
AceEditor.init(initParams);
|
||||
CodeMirrorEditor.init(initParams);
|
||||
|
||||
// Setup the selector for which Editor to use
|
||||
const editorSelector = document.getElementById("script-editor-option-editor");
|
||||
if (editorSelector == null) {
|
||||
console.error(`Could not find DOM Element for editor selector (id=script-editor-option-editor)`);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < editorSelector.options.length; ++i) {
|
||||
if (editorSelector.options[i].value === Settings.Editor) {
|
||||
editorSelector.selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
themeDropdown.selectedIndex = initialIndex;
|
||||
} else {
|
||||
themeDropdown.selectedIndex = 2;
|
||||
}
|
||||
|
||||
themeDropdown.onchange = function() {
|
||||
var val = themeDropdown.value;
|
||||
Settings.EditorTheme = val;
|
||||
var themePath = "ace/theme/" + val.toLowerCase();
|
||||
editor.setTheme(themePath);
|
||||
};
|
||||
themeDropdown.onchange();
|
||||
|
||||
//Keybinding
|
||||
var keybindingDropdown = document.getElementById("script-editor-option-keybinding");
|
||||
if (Settings.EditorKeybinding) {
|
||||
var initialIndex = 0;
|
||||
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
|
||||
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
|
||||
initialIndex = i;
|
||||
editorSelector.onchange = () => {
|
||||
const opt = editorSelector.value;
|
||||
switch (opt) {
|
||||
case EditorSetting.Ace:
|
||||
const codeMirrorCode = CodeMirrorEditor.getCode();
|
||||
const codeMirrorFn = CodeMirrorEditor.getFilename();
|
||||
AceEditor.create();
|
||||
CodeMirrorEditor.setInvisible();
|
||||
AceEditor.openScript(codeMirrorFn, codeMirrorCode);
|
||||
break;
|
||||
}
|
||||
case EditorSetting.CodeMirror:
|
||||
const aceCode = AceEditor.getCode();
|
||||
const aceFn = AceEditor.getFilename();
|
||||
CodeMirrorEditor.create();
|
||||
AceEditor.setInvisible();
|
||||
CodeMirrorEditor.openScript(aceFn, aceCode);
|
||||
break;
|
||||
default:
|
||||
console.error(`Unrecognized Editor Setting: ${opt}`);
|
||||
return;
|
||||
}
|
||||
keybindingDropdown.selectedIndex = initialIndex;
|
||||
} else {
|
||||
keybindingDropdown.selectedIndex = 0;
|
||||
}
|
||||
keybindingDropdown.onchange = function() {
|
||||
var val = keybindingDropdown.value;
|
||||
Settings.EditorKeybinding = val;
|
||||
editor.setKeyboardHandler(keybindings[val.toLowerCase()]);
|
||||
};
|
||||
keybindingDropdown.onchange();
|
||||
|
||||
//Highlight Active line
|
||||
var highlightActiveChkBox = document.getElementById("script-editor-option-highlightactiveline");
|
||||
highlightActiveChkBox.onchange = function() {
|
||||
editor.setHighlightActiveLine(highlightActiveChkBox.checked);
|
||||
};
|
||||
|
||||
//Show Invisibles
|
||||
var showInvisiblesChkBox = document.getElementById("script-editor-option-showinvisibles");
|
||||
showInvisiblesChkBox.onchange = function() {
|
||||
editor.setShowInvisibles(showInvisiblesChkBox.checked);
|
||||
};
|
||||
|
||||
//Use Soft Tab
|
||||
var softTabChkBox = document.getElementById("script-editor-option-usesofttab");
|
||||
softTabChkBox.onchange = function() {
|
||||
editor.getSession().setUseSoftTabs(softTabChkBox.checked);
|
||||
};
|
||||
|
||||
//Jshint Maxerr
|
||||
var maxerr = document.getElementById("script-editor-option-maxerr");
|
||||
var maxerrLabel = document.getElementById("script-editor-option-maxerror-value-label");
|
||||
maxerrLabel.innerHTML = maxerr.value;
|
||||
maxerr.onchange = function() {
|
||||
editor.getSession().$worker.send("changeOptions", [{maxerr:maxerr.value}]);
|
||||
maxerrLabel.innerHTML = maxerr.value;
|
||||
Settings.Editor = opt;
|
||||
}
|
||||
|
||||
//Configure some of the VIM keybindings
|
||||
ace.config.loadModule('ace/keyboard/vim', function(module) {
|
||||
var VimApi = module.CodeMirror.Vim;
|
||||
VimApi.defineEx('write', 'w', function(cm, input) {
|
||||
saveAndCloseScriptEditor();
|
||||
});
|
||||
VimApi.defineEx('quit', 'q', function(cm, input) {
|
||||
Engine.loadTerminalContent();
|
||||
});
|
||||
VimApi.defineEx('xwritequit', 'x', function(cm, input) {
|
||||
saveAndCloseScriptEditor();
|
||||
});
|
||||
VimApi.defineEx('wqwritequit', 'wq', function(cm, input) {
|
||||
saveAndCloseScriptEditor();
|
||||
});
|
||||
});
|
||||
editorSelector.onchange(); // Trigger the onchange event handler
|
||||
}
|
||||
|
||||
//Function autocompleter
|
||||
editor.setOption("enableBasicAutocompletion", true);
|
||||
var autocompleter = {
|
||||
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||
if (prefix.length === 0) {callback(null, []); return;}
|
||||
var words = [];
|
||||
var fns = NetscriptFunctions(null);
|
||||
for (let name in fns) {
|
||||
if (fns.hasOwnProperty(name)) {
|
||||
words.push({
|
||||
name: name,
|
||||
value: name,
|
||||
});
|
||||
|
||||
//Get functions from namespaces
|
||||
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
|
||||
if (namespaces.includes(name)) {
|
||||
let namespace = fns[name];
|
||||
if (typeof namespace !== "object") {continue;}
|
||||
let namespaceFns = Object.keys(namespace);
|
||||
for (let i = 0; i < namespaceFns.length; ++i) {
|
||||
words.push({
|
||||
name: namespaceFns[i],
|
||||
value: namespaceFns[i],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(null, words);
|
||||
},
|
||||
export function getCurrentEditor() {
|
||||
switch (Settings.Editor) {
|
||||
case EditorSetting.Ace:
|
||||
return AceEditor;
|
||||
case EditorSetting.CodeMirror:
|
||||
return CodeMirrorEditor;
|
||||
default:
|
||||
console.log(`Invalid Editor Setting: ${Settings.Editor}`);
|
||||
throw new Error(`Invalid Editor Setting: ${Settings.Editor}`);
|
||||
return null;
|
||||
}
|
||||
editor.completers = [autocompleter];
|
||||
}
|
||||
|
||||
//Updates RAM usage in script
|
||||
async function updateScriptEditorContent() {
|
||||
export async function updateScriptEditorContent() {
|
||||
var filename = document.getElementById("script-editor-filename").value;
|
||||
if (scriptEditorRamCheck == null || !scriptEditorRamCheck.checked || !isScriptFilename(filename)) {
|
||||
scriptEditorRamText.innerText = "N/A";
|
||||
return;
|
||||
}
|
||||
var editor = ace.edit('javascript-editor');
|
||||
var code = editor.getValue();
|
||||
|
||||
let code;
|
||||
try {
|
||||
code = getCurrentEditor().getCode();
|
||||
} catch(e) {
|
||||
scriptEditorRamText.innerText = "RAM: ERROR";
|
||||
return;
|
||||
}
|
||||
|
||||
var codeCopy = code.repeat(1);
|
||||
var ramUsage = await calculateRamUsage(codeCopy);
|
||||
if (ramUsage !== -1) {
|
||||
@ -269,20 +205,17 @@ $(document).keydown(function(e) {
|
||||
}
|
||||
});
|
||||
|
||||
function beautifyScript() {
|
||||
var editor = ace.edit('javascript-editor');
|
||||
var code = editor.getValue();
|
||||
code = beautify(code, {
|
||||
indent_size: 4,
|
||||
brace_style: "preserve-inline",
|
||||
});
|
||||
editor.setValue(code);
|
||||
}
|
||||
|
||||
function saveAndCloseScriptEditor() {
|
||||
var filename = document.getElementById("script-editor-filename").value;
|
||||
var editor = ace.edit('javascript-editor');
|
||||
var code = editor.getValue();
|
||||
|
||||
let code;
|
||||
try {
|
||||
code = getCurrentEditor().getCode();
|
||||
} catch(e) {
|
||||
dialogBoxCreate("Something went wrong when trying to save (getCurrentEditor().getCode()). Please report to game developer with details");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||
//Make sure filename + code properly follow tutorial
|
||||
if (filename !== "foodnstuff.script") {
|
||||
@ -387,8 +320,7 @@ function Script(fn = "", code = "", server = "") {
|
||||
Script.prototype.saveScript = function() {
|
||||
if (routing.isOn(Page.ScriptEditor)) {
|
||||
//Update code and filename
|
||||
var editor = ace.edit('javascript-editor');
|
||||
var code = editor.getValue();
|
||||
const code = getCurrentEditor().getCode();
|
||||
this.code = code.replace(/^\s+|\s+$/g, '');
|
||||
|
||||
var filename = document.getElementById("script-editor-filename").value;
|
||||
@ -483,8 +415,11 @@ async function parseOnlyRamCalculate(server, code, workerScript) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
const script = server.getScript(nextModule);
|
||||
if (!script) return -1; // No such script on the server.
|
||||
const script = server.getScript(nextModule.startsWith("./") ? nextModule.slice(2) : nextModule);
|
||||
if (!script) {
|
||||
console.warn("Invalid script");
|
||||
return -1; // No such script on the server.
|
||||
}
|
||||
code = script.code;
|
||||
}
|
||||
|
||||
@ -1096,5 +1031,5 @@ AllServersMap.fromJSON = function(value) {
|
||||
|
||||
Reviver.constructors.AllServersMap = AllServersMap;
|
||||
|
||||
export {updateScriptEditorContent, loadAllRunningScripts, findRunningScript,
|
||||
export {loadAllRunningScripts, findRunningScript,
|
||||
RunningScript, Script, AllServersMap, scriptEditorInit, isScriptFilename};
|
||||
|
319
src/ScriptEditor/Ace.js
Normal file
319
src/ScriptEditor/Ace.js
Normal file
@ -0,0 +1,319 @@
|
||||
import { ScriptEditor } from "./ScriptEditor";
|
||||
|
||||
const ace = require('brace');
|
||||
|
||||
require('brace/mode/javascript');
|
||||
require('./AceNetscriptMode');
|
||||
require('brace/theme/chaos');
|
||||
require('brace/theme/chrome');
|
||||
require('brace/theme/monokai');
|
||||
require('brace/theme/solarized_dark');
|
||||
require('brace/theme/solarized_light');
|
||||
require('brace/theme/terminal');
|
||||
require('brace/theme/twilight');
|
||||
require('brace/theme/xcode');
|
||||
require("brace/keybinding/vim");
|
||||
require("brace/keybinding/emacs");
|
||||
require("brace/ext/language_tools");
|
||||
|
||||
import { NetscriptFunctions } from "../NetscriptFunctions";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { AceKeybindingSetting } from "../Settings/SettingEnums";
|
||||
|
||||
import { clearEventListeners } from "../../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText,
|
||||
getSelectValue } from "../../utils/uiHelpers/getSelectData";
|
||||
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
|
||||
|
||||
// Wrapper for Ace editor
|
||||
const Keybindings = {
|
||||
ace: null,
|
||||
vim: "ace/keyboard/vim",
|
||||
emacs: "ace/keyboard/emacs",
|
||||
};
|
||||
|
||||
function validateInitializationParamters(params) {
|
||||
if (params.saveAndCloseFn == null) { return false; } // Save & close button function
|
||||
if (params.quitFn == null) { return false; } // Quitting editor, aka Engine.loadTerminalContent
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class AceEditorWrapper extends ScriptEditor {
|
||||
constructor() {
|
||||
super();
|
||||
this.vimCommandDisplayWrapper = null;
|
||||
}
|
||||
|
||||
init(params) {
|
||||
if (this.editor != null) {
|
||||
console.error(`AceEditor.init() called when it's already initialized`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate/Sanitize input
|
||||
if (!validateInitializationParamters(params)) {
|
||||
console.error(`'params' argument passed into initAceEditor() does not have proper properties`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the filename input
|
||||
this.filenameInput = document.getElementById("script-editor-filename");
|
||||
if (this.filenameInput == null) {
|
||||
console.error(`Could not get Script Editor filename element (id=script-editor-filename)`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize ACE Script editor
|
||||
this.editor = ace.edit('ace-editor');
|
||||
this.editor.getSession().setMode('ace/mode/netscript');
|
||||
this.editor.setTheme('ace/theme/monokai');
|
||||
const editorElement = document.getElementById('ace-editor');
|
||||
if (editorElement == null) { return false; }
|
||||
editorElement.style.fontSize = '16px';
|
||||
this.editor.setOption("showPrintMargin", false);
|
||||
|
||||
// Configure some of the VIM keybindings
|
||||
ace.config.loadModule('ace/keyboard/vim', function(module) {
|
||||
var VimApi = module.CodeMirror.Vim;
|
||||
VimApi.defineEx('write', 'w', function(cm, input) {
|
||||
params.saveAndCloseFn();
|
||||
});
|
||||
VimApi.defineEx('quit', 'q', function(cm, input) {
|
||||
params.quitFn();
|
||||
});
|
||||
VimApi.defineEx('xwritequit', 'x', function(cm, input) {
|
||||
params.saveAndCloseFn();
|
||||
});
|
||||
VimApi.defineEx('wqwritequit', 'wq', function(cm, input) {
|
||||
params.saveAndCloseFn();
|
||||
});
|
||||
});
|
||||
|
||||
// Store a reference to the VIM command display
|
||||
this.vimCommandDisplayWrapper = document.getElementById("codemirror-vim-command-display-wrapper");
|
||||
if (this.vimCommandDisplayWrapper == null) {
|
||||
console.error(`Could not get Vim Command Display element (id=codemirror-vim-command-display-wrapper)`);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Function autocompleter
|
||||
this.editor.setOption("enableBasicAutocompletion", true);
|
||||
var autocompleter = {
|
||||
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||
if (prefix.length === 0) {callback(null, []); return;}
|
||||
var words = [];
|
||||
var fns = NetscriptFunctions(null);
|
||||
for (let name in fns) {
|
||||
if (fns.hasOwnProperty(name)) {
|
||||
words.push({
|
||||
name: name,
|
||||
value: name,
|
||||
});
|
||||
|
||||
//Get functions from namespaces
|
||||
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
|
||||
if (namespaces.includes(name)) {
|
||||
let namespace = fns[name];
|
||||
if (typeof namespace !== "object") {continue;}
|
||||
let namespaceFns = Object.keys(namespace);
|
||||
for (let i = 0; i < namespaceFns.length; ++i) {
|
||||
words.push({
|
||||
name: namespaceFns[i],
|
||||
value: namespaceFns[i],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(null, words);
|
||||
},
|
||||
}
|
||||
this.editor.completers = [autocompleter];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
initialized() {
|
||||
return (this.editor != null);
|
||||
}
|
||||
|
||||
// Create the configurable Options for this Editor
|
||||
create() {
|
||||
function safeGetElementById(id, whatFor="") {
|
||||
const elem = document.getElementById(id);
|
||||
if (elem == null) {
|
||||
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
function safeClearEventListeners(id, whatFor="") {
|
||||
const elem = clearEventListeners(id);
|
||||
if (elem == null) {
|
||||
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
try {
|
||||
const optionsPanel = safeGetElementById("script-editor-options-panel", "Script Editor Options Panel");
|
||||
|
||||
// Set editor to visible
|
||||
const elem = document.getElementById("ace-editor");
|
||||
if (elem instanceof HTMLElement) {
|
||||
elem.style.display = "block";
|
||||
}
|
||||
|
||||
// Make sure the Vim command display from CodeMirror is invisible
|
||||
if (this.vimCommandDisplayWrapper instanceof HTMLElement) {
|
||||
this.vimCommandDisplayWrapper.style.display = "none";
|
||||
}
|
||||
|
||||
// Theme
|
||||
const themeDropdown = safeClearEventListeners("script-editor-option-theme", "Theme Selector");
|
||||
removeChildrenFromElement(themeDropdown);
|
||||
themeDropdown.add(createOptionElement("Chaos"));
|
||||
themeDropdown.add(createOptionElement("Chrome"));
|
||||
themeDropdown.add(createOptionElement("Monokai"));
|
||||
themeDropdown.add(createOptionElement("Solarized Dark", "Solarized_Dark"));
|
||||
themeDropdown.add(createOptionElement("Solarized Light", "Solarized_Light"));
|
||||
themeDropdown.add(createOptionElement("Terminal"));
|
||||
themeDropdown.add(createOptionElement("Twilight"));
|
||||
themeDropdown.add(createOptionElement("XCode"));
|
||||
if (Settings.EditorTheme) {
|
||||
var initialIndex = 2;
|
||||
for (var i = 0; i < themeDropdown.options.length; ++i) {
|
||||
if (themeDropdown.options[i].value === Settings.EditorTheme) {
|
||||
initialIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
themeDropdown.selectedIndex = initialIndex;
|
||||
} else {
|
||||
themeDropdown.selectedIndex = 2;
|
||||
}
|
||||
|
||||
themeDropdown.onchange = () => {
|
||||
const val = themeDropdown.value;
|
||||
Settings.EditorTheme = val;
|
||||
const themePath = "ace/theme/" + val.toLowerCase();
|
||||
this.editor.setTheme(themePath);
|
||||
};
|
||||
themeDropdown.onchange();
|
||||
|
||||
// Keybinding
|
||||
const keybindingDropdown = safeClearEventListeners("script-editor-option-keybinding", "Keybinding Selector");
|
||||
removeChildrenFromElement(keybindingDropdown);
|
||||
keybindingDropdown.add(createOptionElement("Ace", AceKeybindingSetting.Ace));
|
||||
keybindingDropdown.add(createOptionElement("Vim", AceKeybindingSetting.Vim));
|
||||
keybindingDropdown.add(createOptionElement("Emacs", AceKeybindingSetting.Emacs));
|
||||
if (Settings.EditorKeybinding) {
|
||||
// Sanitize the Keybinding setting
|
||||
if (!(Object.values(AceKeybindingSetting).includes(Settings.EditorKeybinding))) {
|
||||
Settings.EditorKeybinding = AceKeybindingSetting.Ace;
|
||||
}
|
||||
var initialIndex = 0;
|
||||
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
|
||||
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
|
||||
initialIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
keybindingDropdown.selectedIndex = initialIndex;
|
||||
} else {
|
||||
keybindingDropdown.selectedIndex = 0;
|
||||
}
|
||||
keybindingDropdown.onchange = () => {
|
||||
var val = keybindingDropdown.value;
|
||||
Settings.EditorKeybinding = val;
|
||||
this.editor.setKeyboardHandler(Keybindings[val.toLowerCase()]);
|
||||
};
|
||||
keybindingDropdown.onchange();
|
||||
|
||||
// Highlight Active line
|
||||
const highlightActiveChkBox = safeClearEventListeners("script-editor-option-highlightactiveline", "Active Line Checkbox");
|
||||
highlightActiveChkBox.onchange = () => {
|
||||
this.editor.setHighlightActiveLine(highlightActiveChkBox.checked);
|
||||
};
|
||||
|
||||
// Show Invisibles
|
||||
const showInvisiblesChkBox = safeClearEventListeners("script-editor-option-showinvisibles", "Show Invisible Checkbox");
|
||||
showInvisiblesChkBox.onchange = () => {
|
||||
this.editor.setShowInvisibles(showInvisiblesChkBox.checked);
|
||||
};
|
||||
|
||||
// Use Soft Tab
|
||||
const softTabChkBox = safeClearEventListeners("script-editor-option-usesofttab", "Soft Tab Checkbox");
|
||||
softTabChkBox.onchange = () => {
|
||||
this.editor.getSession().setUseSoftTabs(softTabChkBox.checked);
|
||||
};
|
||||
|
||||
// Some helper functions for dealing with flexible options
|
||||
function resetFlexibleOption(id) {
|
||||
const fieldset = safeGetElementById(id);
|
||||
removeChildrenFromElement(fieldset);
|
||||
fieldset.style.display = "block";
|
||||
return fieldset;
|
||||
}
|
||||
|
||||
function removeFlexibleOption(id) {
|
||||
// This doesn't really remove it, just sets it to invisible
|
||||
const fieldset = resetFlexibleOption(id);
|
||||
fieldset.style.display = "none";
|
||||
return fieldset;
|
||||
}
|
||||
|
||||
// Jshint Maxerr (Flex 1)
|
||||
const flex1Fieldset = resetFlexibleOption("script-editor-option-flex1-fieldset");
|
||||
const flex1Id = "script-editor-option-maxerr";
|
||||
const flex1ValueLabel = createElement("em", { innerText: "200" });
|
||||
flex1Fieldset.appendChild(createElement("label", {
|
||||
for: flex1Id,
|
||||
innerText: "Max Error Count",
|
||||
}));
|
||||
const flex1Input = createElement("input", {
|
||||
id: flex1Id,
|
||||
max: "1000",
|
||||
min: "50",
|
||||
name: flex1Id,
|
||||
step: "1",
|
||||
type: "range",
|
||||
value: "200",
|
||||
changeListener: () => {
|
||||
this.editor.getSession().$worker.send("changeOptions", [{maxerr:flex1Input.value}]);
|
||||
flex1ValueLabel.innerText = flex1Input.value;
|
||||
}
|
||||
});
|
||||
flex1Fieldset.appendChild(flex1Input);
|
||||
flex1Fieldset.appendChild(flex1ValueLabel);
|
||||
|
||||
// Nothing for Flex Options 2-4
|
||||
removeFlexibleOption("script-editor-option-flex2-fieldset");
|
||||
removeFlexibleOption("script-editor-option-flex3-fieldset");
|
||||
removeFlexibleOption("script-editor-option-flex4-fieldset");
|
||||
} catch(e) {
|
||||
console.error(`Exception caught: ${e}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
isFocused() {
|
||||
if (this.editor == null) { return false; }
|
||||
return this.editor.isFocused();
|
||||
}
|
||||
|
||||
// Sets the editor to be invisible. Does not require this class to be initialized
|
||||
setInvisible() {
|
||||
const elem = document.getElementById("ace-editor");
|
||||
if (elem instanceof HTMLElement) {
|
||||
elem.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const AceEditor = new AceEditorWrapper();
|
@ -94,8 +94,8 @@ let NetscriptFunctions =
|
||||
"installAugmentations|" +
|
||||
|
||||
// TIX API
|
||||
"getStockPrice|getStockPosition|getStockSymbols|buyStock|sellStock|" +
|
||||
"shortStock|sellShort|" +
|
||||
"getStockPrice|getStockPosition|getStockSymbols|getStockMaxShares|" +
|
||||
"buyStock|sellStock|shortStock|sellShort|" +
|
||||
"placeOrder|cancelOrder|getOrders|getStockVolatility|getStockForecast|" +
|
||||
"purchase4SMarketData|purchase4SMarketDataTixApi|" +
|
||||
|
577
src/ScriptEditor/CodeMirror.js
Normal file
577
src/ScriptEditor/CodeMirror.js
Normal file
@ -0,0 +1,577 @@
|
||||
// Wrapper for CodeMirror editor
|
||||
// https://github.com/codemirror/codemirror
|
||||
import { ScriptEditor } from "./ScriptEditor";
|
||||
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
|
||||
import 'codemirror/theme/monokai.css';
|
||||
import 'codemirror/theme/3024-day.css';
|
||||
import 'codemirror/theme/3024-night.css';
|
||||
import 'codemirror/theme/abcdef.css';
|
||||
import 'codemirror/theme/ambiance-mobile.css';
|
||||
import 'codemirror/theme/ambiance.css';
|
||||
import 'codemirror/theme/base16-dark.css';
|
||||
import 'codemirror/theme/base16-light.css';
|
||||
import 'codemirror/theme/bespin.css';
|
||||
import 'codemirror/theme/blackboard.css';
|
||||
import 'codemirror/theme/cobalt.css';
|
||||
import 'codemirror/theme/colorforth.css';
|
||||
import 'codemirror/theme/darcula.css';
|
||||
import 'codemirror/theme/dracula.css';
|
||||
import 'codemirror/theme/duotone-dark.css';
|
||||
import 'codemirror/theme/duotone-light.css';
|
||||
import 'codemirror/theme/eclipse.css';
|
||||
import 'codemirror/theme/elegant.css';
|
||||
import 'codemirror/theme/erlang-dark.css';
|
||||
import 'codemirror/theme/gruvbox-dark.css';
|
||||
import 'codemirror/theme/hopscotch.css';
|
||||
import 'codemirror/theme/icecoder.css';
|
||||
import 'codemirror/theme/idea.css';
|
||||
import 'codemirror/theme/isotope.css';
|
||||
import 'codemirror/theme/lesser-dark.css';
|
||||
import 'codemirror/theme/liquibyte.css';
|
||||
import 'codemirror/theme/lucario.css';
|
||||
import 'codemirror/theme/material.css';
|
||||
import 'codemirror/theme/mbo.css';
|
||||
import 'codemirror/theme/mdn-like.css';
|
||||
import 'codemirror/theme/midnight.css';
|
||||
import 'codemirror/theme/neat.css';
|
||||
import 'codemirror/theme/neo.css';
|
||||
import 'codemirror/theme/night.css';
|
||||
import 'codemirror/theme/oceanic-next.css';
|
||||
import 'codemirror/theme/panda-syntax.css';
|
||||
import 'codemirror/theme/paraiso-dark.css';
|
||||
import 'codemirror/theme/paraiso-light.css';
|
||||
import 'codemirror/theme/pastel-on-dark.css';
|
||||
import 'codemirror/theme/railscasts.css';
|
||||
import 'codemirror/theme/rubyblue.css';
|
||||
import 'codemirror/theme/seti.css';
|
||||
import 'codemirror/theme/shadowfox.css';
|
||||
import 'codemirror/theme/solarized.css';
|
||||
import 'codemirror/theme/ssms.css';
|
||||
import 'codemirror/theme/the-matrix.css';
|
||||
import 'codemirror/theme/tomorrow-night-bright.css';
|
||||
import 'codemirror/theme/tomorrow-night-eighties.css';
|
||||
import 'codemirror/theme/ttcn.css';
|
||||
import 'codemirror/theme/twilight.css';
|
||||
import 'codemirror/theme/vibrant-ink.css';
|
||||
import 'codemirror/theme/xq-dark.css';
|
||||
import 'codemirror/theme/xq-light.css';
|
||||
import 'codemirror/theme/yeti.css';
|
||||
import 'codemirror/theme/zenburn.css';
|
||||
|
||||
import "../../css/codemirror-overrides.scss";
|
||||
|
||||
import CodeMirror from "codemirror/lib/codemirror.js";
|
||||
import "codemirror/mode/javascript/javascript.js";
|
||||
import "./CodeMirrorNetscriptMode";
|
||||
|
||||
import 'codemirror/keymap/sublime.js';
|
||||
import 'codemirror/keymap/vim.js';
|
||||
import 'codemirror/keymap/emacs.js';
|
||||
|
||||
import 'codemirror/addon/comment/continuecomment.js';
|
||||
import 'codemirror/addon/dialog/dialog.css';
|
||||
import 'codemirror/addon/dialog/dialog.js';
|
||||
import 'codemirror/addon/edit/closebrackets.js';
|
||||
import 'codemirror/addon/edit/matchbrackets.js';
|
||||
import 'codemirror/addon/fold/foldcode.js';
|
||||
import 'codemirror/addon/fold/foldgutter.js';
|
||||
import 'codemirror/addon/fold/foldgutter.css';
|
||||
import 'codemirror/addon/fold/brace-fold.js';
|
||||
import 'codemirror/addon/fold/indent-fold.js';
|
||||
import 'codemirror/addon/fold/comment-fold.js';
|
||||
import 'codemirror/addon/hint/javascript-hint.js';
|
||||
import 'codemirror/addon/hint/show-hint.js';
|
||||
import 'codemirror/addon/hint/show-hint.css';
|
||||
import 'codemirror/addon/lint/lint.js';
|
||||
import 'codemirror/addon/lint/lint.css';
|
||||
import 'codemirror/addon/search/match-highlighter.js';
|
||||
import 'codemirror/addon/selection/active-line.js';
|
||||
|
||||
window.JSHINT = require('jshint').JSHINT;
|
||||
import './CodeMirrorNetscriptLint.js';
|
||||
|
||||
import { NetscriptFunctions } from "../NetscriptFunctions";
|
||||
import { CodeMirrorKeybindingSetting,
|
||||
CodeMirrorThemeSetting } from "../Settings/SettingEnums";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
|
||||
import { clearEventListeners } from "../../utils/uiHelpers/clearEventListeners";
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
import { createOptionElement } from "../../utils/uiHelpers/createOptionElement";
|
||||
import { getSelectText,
|
||||
getSelectValue } from "../../utils/uiHelpers/getSelectData";
|
||||
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
|
||||
|
||||
// Max number of invisibles to be shown in a group if the "Show Invisibles" option
|
||||
// is marked
|
||||
const MaxInvisibles = 20;
|
||||
|
||||
function validateInitializationParamters(params) {
|
||||
if (params.saveAndCloseFn == null) { return false; } // Save & close button function
|
||||
if (params.quitFn == null) { return false; } // Quitting editor, aka Engine.loadTerminalContent
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class CodeMirrorEditorWrapper extends ScriptEditor {
|
||||
constructor() {
|
||||
super();
|
||||
this.vimCommandDisplay = null;
|
||||
this.vimCommandDisplayWrapper = null;
|
||||
this.tabsStyleElement = null;
|
||||
}
|
||||
|
||||
init(params) {
|
||||
if (this.editor != null) {
|
||||
console.error(`CodeMirrorEditor.init() called when it's already initialized`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate/Sanitize input
|
||||
if (!validateInitializationParamters(params)) {
|
||||
console.error(`'params' argument passed into CodeMirrorEditor.init() does not have proper properties`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the filename input
|
||||
this.filenameInput = document.getElementById("script-editor-filename");
|
||||
if (this.filenameInput == null) {
|
||||
console.error(`Could not get Script Editor filename element (id=script-editor-filename)`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add styling for the "Show Invisibles" option for spaces
|
||||
const classBase = '.CodeMirror .cm-whitespace-';
|
||||
const spaceChar = '·';
|
||||
const style = document.createElement('style');
|
||||
|
||||
style.setAttribute('data-name', 'js-show-invisibles');
|
||||
|
||||
let rules = '';
|
||||
let spaceChars = '';
|
||||
|
||||
for (let i = 1; i <= MaxInvisibles; ++i) {
|
||||
spaceChars += spaceChar;
|
||||
|
||||
const rule = classBase + i + '::before { content: "' + spaceChars + '";}\n';
|
||||
rules += rule;
|
||||
}
|
||||
|
||||
style.textContent = rules;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Add an element for the "Show Invisible" option for tabs
|
||||
this.tabsStyleElement = document.createElement('style');
|
||||
document.head.appendChild(this.tabsStyleElement);
|
||||
|
||||
// Store a reference to the VIM command display
|
||||
this.vimCommandDisplay = document.getElementById("codemirror-vim-command-display");
|
||||
this.vimCommandDisplayWrapper = document.getElementById("codemirror-vim-command-display-wrapper");
|
||||
|
||||
// Define a "Save" command for CodeMirror so shortcuts like Ctrl + s
|
||||
// will save in-game
|
||||
CodeMirror.commands.save = function() { params.saveAndCloseFn(); }
|
||||
|
||||
// Add Netscript Functions to the autocompleter
|
||||
const netscriptFns = [];
|
||||
var fnsObj = NetscriptFunctions(null);
|
||||
for (let name in fnsObj) {
|
||||
if (fnsObj.hasOwnProperty(name)) {
|
||||
netscriptFns.push(name);
|
||||
|
||||
//Get functions from namespaces
|
||||
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
|
||||
if (namespaces.includes(name)) {
|
||||
let namespace = fnsObj[name];
|
||||
if (typeof namespace !== "object") {continue;}
|
||||
let namespaceFns = Object.keys(namespace);
|
||||
for (let i = 0; i < namespaceFns.length; ++i) {
|
||||
netscriptFns.push(namespaceFns[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeMirror.hint.netscript = function(editor) {
|
||||
const origList = CodeMirror.hint.javascript(editor) || {from: editor.getCursor(), to: editor.getCursor(), list: []};
|
||||
origList.list.push(...netscriptFns);
|
||||
let list = origList.list || [];
|
||||
let cursor = editor.getCursor();
|
||||
let currentLine = editor.getLine(cursor.line);
|
||||
let start = cursor.ch;
|
||||
let end = start;
|
||||
while (end < currentLine.length && /[\w$]+/.test(currentLine.charAt(end))) ++end;
|
||||
while (start && /[\w$]+/.test(currentLine.charAt(start - 1))) --start;
|
||||
let curWord = start != end && currentLine.slice(start, end);
|
||||
let regex = new RegExp('^' + curWord, 'i');
|
||||
let result = {
|
||||
list: (!curWord ? list : list.filter(function (item) {
|
||||
return item.match(regex);
|
||||
})).sort(),
|
||||
from: CodeMirror.Pos(cursor.line, start),
|
||||
to: CodeMirror.Pos(cursor.line, end)
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Configure VIM keybindings
|
||||
var VimApi = CodeMirror.Vim;
|
||||
VimApi.defineEx('write', 'w', function(cm, input) {
|
||||
params.saveAndCloseFn();
|
||||
});
|
||||
VimApi.defineEx('quit', 'q', function(cm, input) {
|
||||
params.quitFn();
|
||||
});
|
||||
VimApi.defineEx('xwritequit', 'x', function(cm, input) {
|
||||
params.saveAndCloseFn();
|
||||
});
|
||||
VimApi.defineEx('wqwritequit', 'wq', function(cm, input) {
|
||||
params.saveAndCloseFn();
|
||||
});
|
||||
}
|
||||
|
||||
initialized() {
|
||||
return (this.filenameInput != null);
|
||||
}
|
||||
|
||||
create() {
|
||||
function safeGetElementById(id, whatFor="") {
|
||||
const elem = document.getElementById(id);
|
||||
if (elem == null) {
|
||||
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
function safeClearEventListeners(id, whatFor="") {
|
||||
const elem = clearEventListeners(id);
|
||||
if (elem == null) {
|
||||
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!this.initialized()) {
|
||||
console.warn(`CodeMirrorEditor.create() called when editor was not initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get and sanitize the keybinding (keymap) setting
|
||||
if (!(Object.values(CodeMirrorKeybindingSetting).includes(Settings.EditorKeybinding))) {
|
||||
Settings.EditorKeybinding = CodeMirrorKeybindingSetting.Default;
|
||||
}
|
||||
|
||||
// Initialize CodeMirror Editor
|
||||
const textAreaElement = safeGetElementById("codemirror-editor", "CodeMirror Textarea");
|
||||
const formElement = safeGetElementById("codemirror-form-wrapper", "CodeMirror Form Wrapper");
|
||||
formElement.style.display = "block";
|
||||
|
||||
this.editor = CodeMirror.fromTextArea(textAreaElement, {
|
||||
autofocus: true,
|
||||
extraKeys: { "Ctrl-Space": "autocomplete" },
|
||||
foldGutter: true,
|
||||
gutters: ["CodeMirror-lint-markers", "CodeMirror-linenumbers", "CodeMirror-foldgutter"],
|
||||
highlightSelectionMatches: true,
|
||||
hintOptions: { hint: CodeMirror.hint.netscript },
|
||||
indentUnit: 4,
|
||||
keyMap: "default",
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
maxInvisibles: 32,
|
||||
mode: "netscript",
|
||||
theme: Settings.EditorTheme,
|
||||
});
|
||||
|
||||
// Setup Theme Option
|
||||
const themeDropdown = safeClearEventListeners("script-editor-option-theme", "Theme Selector");
|
||||
removeChildrenFromElement(themeDropdown);
|
||||
const themeOptions = Object.keys(CodeMirrorThemeSetting);
|
||||
for (let i = 0; i < themeOptions.length; ++i) {
|
||||
const themeKey = themeOptions[i];
|
||||
const themeValue = CodeMirrorThemeSetting[themeKey];
|
||||
themeDropdown.add(createOptionElement(themeKey, themeValue));
|
||||
}
|
||||
if (Settings.EditorTheme) {
|
||||
var initialIndex = 0;
|
||||
for (var i = 0; i < themeDropdown.options.length; ++i) {
|
||||
if (themeDropdown.options[i].value === Settings.EditorTheme) {
|
||||
initialIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
themeDropdown.selectedIndex = initialIndex;
|
||||
} else {
|
||||
themeDropdown.selectedIndex = 0;
|
||||
}
|
||||
|
||||
themeDropdown.onchange = () => {
|
||||
const val = themeDropdown.value;
|
||||
Settings.EditorTheme = val;
|
||||
this.editor.setOption("theme", val);
|
||||
};
|
||||
themeDropdown.onchange();
|
||||
|
||||
// Setup Keymap Option
|
||||
const keybindingDropdown = safeClearEventListeners("script-editor-option-keybinding", "Keymap Selector");
|
||||
if (keybindingDropdown == null) {
|
||||
console.error(`Could not find Script Editor's keybinding selector element (id="script-editor-option-keybinding")`);
|
||||
return false;
|
||||
}
|
||||
removeChildrenFromElement(keybindingDropdown);
|
||||
keybindingDropdown.add(createOptionElement("Default", CodeMirrorKeybindingSetting.Default));
|
||||
keybindingDropdown.add(createOptionElement("Sublime", CodeMirrorKeybindingSetting.Sublime));
|
||||
keybindingDropdown.add(createOptionElement("Vim", CodeMirrorKeybindingSetting.Vim));
|
||||
keybindingDropdown.add(createOptionElement("Emacs", CodeMirrorKeybindingSetting.Emacs));
|
||||
if (Settings.EditorKeybinding) {
|
||||
var initialIndex = 0;
|
||||
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
|
||||
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
|
||||
initialIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
keybindingDropdown.selectedIndex = initialIndex;
|
||||
} else {
|
||||
keybindingDropdown.selectedIndex = 0;
|
||||
}
|
||||
keybindingDropdown.onchange = () => {
|
||||
// Set Vim command display to be invisible initially
|
||||
this.vimCommandDisplayWrapper.style.display = "none";
|
||||
|
||||
const val = keybindingDropdown.value;
|
||||
Settings.EditorKeybinding = val;
|
||||
this.editor.removeKeyMap(CodeMirror.keyMap.default);
|
||||
this.editor.removeKeyMap(CodeMirror.keyMap.sublime);
|
||||
this.editor.removeKeyMap(CodeMirror.keyMap.emacs);
|
||||
this.editor.removeKeyMap(CodeMirror.keyMap.vim);
|
||||
|
||||
// Setup the VIM command display
|
||||
let keys = '';
|
||||
const handleVimKeyPress = (key) => {
|
||||
keys = keys + key;
|
||||
this.vimCommandDisplay.innerHTML = keys;
|
||||
}
|
||||
const handleVimCommandDone = (e) => {
|
||||
keys = '';
|
||||
this.vimCommandDisplay.innerHTML = keys;
|
||||
}
|
||||
if (val === CodeMirrorKeybindingSetting.Vim) {
|
||||
this.vimCommandDisplayWrapper.style.display = "block";
|
||||
this.editor.on('vim-keypress', handleVimKeyPress);
|
||||
this.editor.on('vim-command-done', handleVimCommandDone);
|
||||
|
||||
} else {
|
||||
this.vimCommandDisplayWrapper.style.display = "none";
|
||||
this.editor.off('vim-keypress', handleVimKeyPress);
|
||||
this.editor.off('vim-command-done', handleVimCommandDone);
|
||||
}
|
||||
|
||||
this.editor.addKeyMap(val);
|
||||
this.editor.setOption("keyMap", val);
|
||||
};
|
||||
keybindingDropdown.onchange();
|
||||
|
||||
// Highlight Active line
|
||||
const highlightActiveChkBox = safeClearEventListeners("script-editor-option-highlightactiveline", "Active Line Checkbox");
|
||||
highlightActiveChkBox.onchange = () => {
|
||||
this.editor.setOption("styleActiveLine", highlightActiveChkBox.checked);
|
||||
};
|
||||
highlightActiveChkBox.onchange();
|
||||
|
||||
// Show Invisibles
|
||||
const showInvisiblesChkBox = safeClearEventListeners("script-editor-option-showinvisibles", "Show Invisible Checkbox");
|
||||
showInvisiblesChkBox.onchange = () => {
|
||||
const overlayMode = {
|
||||
name: 'invisibles',
|
||||
token: function nextToken(stream) {
|
||||
var ret,
|
||||
spaces = 0,
|
||||
space = stream.peek() === ' ';
|
||||
|
||||
if (space) {
|
||||
while (space && spaces < MaxInvisibles) {
|
||||
++spaces;
|
||||
|
||||
stream.next();
|
||||
space = stream.peek() === ' ';
|
||||
}
|
||||
|
||||
ret = 'whitespace whitespace-' + spaces;
|
||||
} else {
|
||||
while (!stream.eol() && !space) {
|
||||
stream.next();
|
||||
|
||||
space = stream.peek() === ' ';
|
||||
}
|
||||
|
||||
ret = 'cm-eol';
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
if (showInvisiblesChkBox.checked) {
|
||||
// Spaces
|
||||
this.editor.addOverlay(overlayMode);
|
||||
|
||||
// Tabs
|
||||
this.tabsStyleElement.innerHTML = ".cm-tab {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVEhLYxgFo2BkAGYH/9r/QFoAxIGyhx6AORzZA4xD1TcHNjYzQplDB2CLgaECYHkADIZqqgGBoZdsRsHgBgwMAB8iFHF42AERAAAAAElFTkSuQmCC);background-position: right;background-repeat: no-repeat;}";
|
||||
} else {
|
||||
this.editor.removeOverlay("invisibles");
|
||||
this.tabsStyleElement.innerHTML = "";
|
||||
}
|
||||
};
|
||||
showInvisiblesChkBox.onchange();
|
||||
|
||||
//Use Soft Tab
|
||||
const softTabChkBox = safeClearEventListeners("script-editor-option-usesofttab", "Soft Tab Checkbox");
|
||||
softTabChkBox.onchange = () => {
|
||||
this.editor.setOption("indentWithTabs", !softTabChkBox.checked);
|
||||
if (softTabChkBox.checked) {
|
||||
this.editor.addKeyMap({
|
||||
name: "soft-tabs-keymap",
|
||||
"Tab": function (cm) {
|
||||
if (cm.somethingSelected()) {
|
||||
var sel = cm.getSelection("\n");
|
||||
// Indent only if there are multiple lines selected, or if the selection spans a full line
|
||||
if (sel.length > 0 && (sel.indexOf("\n") > -1 || sel.length === cm.getLine(cm.getCursor().line).length)) {
|
||||
cm.indentSelection("add");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cm.options.indentWithTabs)
|
||||
cm.execCommand("insertTab");
|
||||
else
|
||||
cm.execCommand("insertSoftTab");
|
||||
},
|
||||
"Shift-Tab": function (cm) {
|
||||
cm.indentSelection("subtract");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.editor.removeKeyMap("soft-tabs-keymap");
|
||||
}
|
||||
};
|
||||
softTabChkBox.onchange();
|
||||
|
||||
// Some helper functions for dealing with flexible options
|
||||
function resetFlexibleOption(id) {
|
||||
const fieldset = safeGetElementById(id);
|
||||
removeChildrenFromElement(fieldset);
|
||||
fieldset.style.display = "block";
|
||||
return fieldset;
|
||||
}
|
||||
|
||||
function removeFlexibleOption(id) {
|
||||
// This doesn't really remove it, just sets it to invisible
|
||||
const fieldset = resetFlexibleOption(id);
|
||||
fieldset.style.display = "none";
|
||||
return fieldset;
|
||||
}
|
||||
|
||||
// Flex 1: Automatically Close Brackets and Quotes
|
||||
const flex1Fieldset = resetFlexibleOption("script-editor-option-flex1-fieldset");
|
||||
const flex1Id = "script-editor-option-flex1";
|
||||
flex1Fieldset.appendChild(createElement("label", {
|
||||
for: flex1Id,
|
||||
innerText: "Auto-Close Brackets/Quotes",
|
||||
}));
|
||||
|
||||
const flex1Checkbox = createElement("input", {
|
||||
checked: true,
|
||||
id: flex1Id,
|
||||
name: flex1Id,
|
||||
type: "checkbox",
|
||||
});
|
||||
flex1Fieldset.appendChild(flex1Checkbox);
|
||||
flex1Checkbox.onchange = () => {
|
||||
this.editor.setOption("autoCloseBrackets", flex1Checkbox.checked);
|
||||
};
|
||||
flex1Checkbox.onchange();
|
||||
|
||||
// Flex 2: Disable/Enable Linting
|
||||
const flex2Fieldset = resetFlexibleOption("script-editor-option-flex2-fieldset");
|
||||
const flex2Id = "script-editor-option-flex2";
|
||||
flex2Fieldset.appendChild(createElement("label", {
|
||||
for: flex2Id,
|
||||
innerText: "Enable Linting",
|
||||
}));
|
||||
|
||||
const flex2Checkbox = createElement("input", {
|
||||
checked: true,
|
||||
id: flex2Id,
|
||||
name: flex2Id,
|
||||
type: "checkbox",
|
||||
});
|
||||
flex2Fieldset.appendChild(flex2Checkbox);
|
||||
flex2Checkbox.onchange = () => {
|
||||
if (flex2Checkbox.checked) {
|
||||
this.editor.setOption("lint", CodeMirror.lint.netscript);
|
||||
} else {
|
||||
this.editor.setOption("lint", false);
|
||||
}
|
||||
}
|
||||
flex2Checkbox.onchange();
|
||||
|
||||
// Flex 3: Continue Comments
|
||||
const flex3Fieldset = resetFlexibleOption("script-editor-option-flex3-fieldset");
|
||||
const flex3Id = "script-editor-option-flex3";
|
||||
flex3Fieldset.appendChild(createElement("label", {
|
||||
for: flex3Id,
|
||||
innerText: "Continue Comments",
|
||||
}));
|
||||
|
||||
const flex3Checkbox = createElement("input", {
|
||||
checked: true,
|
||||
id: flex3Id,
|
||||
name: flex3Id,
|
||||
type: "checkbox",
|
||||
});
|
||||
flex3Fieldset.appendChild(flex3Checkbox);
|
||||
flex3Checkbox.onchange = () => {
|
||||
this.editor.setOption("continueComments", flex3Checkbox.checked);
|
||||
}
|
||||
flex3Checkbox.onchange();
|
||||
|
||||
removeFlexibleOption("script-editor-option-flex4-fieldset");
|
||||
|
||||
this.editor.refresh();
|
||||
} catch(e) {
|
||||
console.error(`Exception caught: ${e}. ${e.stack}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
isFocused() {
|
||||
if (this.editor == null) { return false; }
|
||||
return this.editor.hasFocus();
|
||||
}
|
||||
|
||||
// Sets the editor to be invisible
|
||||
setInvisible() {
|
||||
if (!this.initialized()) {
|
||||
console.warn(`CodeMirrorEditor.setInvisible() called when editor was not initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.editor != null) {
|
||||
this.editor.toTextArea();
|
||||
this.editor = null;
|
||||
}
|
||||
|
||||
const elem = document.getElementById("codemirror-form-wrapper");
|
||||
if (elem instanceof HTMLElement) {
|
||||
elem.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const CodeMirrorEditor = new CodeMirrorEditorWrapper();
|
82
src/ScriptEditor/CodeMirrorNetscriptLint.js
Normal file
82
src/ScriptEditor/CodeMirrorNetscriptLint.js
Normal file
@ -0,0 +1,82 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("codemirror/lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["codemirror/lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
// declare global: JSHINT
|
||||
|
||||
function validator(text, options) {
|
||||
if (!window.JSHINT) {
|
||||
if (window.console) {
|
||||
window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
// To ignore the 'async/await' errors, we'll manually edit the code ('text')
|
||||
// that gets processed by JSHINT to include the ignore directory
|
||||
const splitText = text.split("\n");
|
||||
const ignoreDirective = " // jshint ignore:line";
|
||||
for (let i = 0; i < splitText.length; ++i) {
|
||||
if (splitText[i].match(/.*async function.+{/g)) {
|
||||
splitText[i] += ignoreDirective;
|
||||
} else if (splitText[i].match(/.*await.+;/g)) {
|
||||
splitText[i] += ignoreDirective;
|
||||
}
|
||||
}
|
||||
const sanitizedText = splitText.join("\n");
|
||||
|
||||
// Configure JSHINT options
|
||||
if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
|
||||
options.indent = 1; // JSHint default value is 4
|
||||
|
||||
options.esversion = 6;
|
||||
|
||||
JSHINT(sanitizedText, options, options.globals);
|
||||
var errors = JSHINT.data().errors, result = [];
|
||||
if (errors) parseErrors(errors, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CodeMirror.registerHelper("lint", "netscript", validator);
|
||||
|
||||
function parseErrors(errors, output) {
|
||||
for ( var i = 0; i < errors.length; i++) {
|
||||
var error = errors[i];
|
||||
if (error) {
|
||||
if (error.line == 0) { continue; }
|
||||
if (error.line < 0) {
|
||||
if (window.console) {
|
||||
window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var start = error.character - 1, end = start + 1;
|
||||
if (error.evidence) {
|
||||
var index = error.evidence.substring(start).search(/.\b/);
|
||||
if (index > -1) {
|
||||
end += index;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to format expected by validation service
|
||||
var hint = {
|
||||
message: error.reason,
|
||||
severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error",
|
||||
from: CodeMirror.Pos(error.line - 1, start),
|
||||
to: CodeMirror.Pos(error.line - 1, end)
|
||||
};
|
||||
|
||||
output.push(hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
1128
src/ScriptEditor/CodeMirrorNetscriptMode.js
Normal file
1128
src/ScriptEditor/CodeMirrorNetscriptMode.js
Normal file
File diff suppressed because it is too large
Load Diff
58
src/ScriptEditor/ScriptEditor.js
Normal file
58
src/ScriptEditor/ScriptEditor.js
Normal file
@ -0,0 +1,58 @@
|
||||
// Base Script Editor class for the Ace/CodeMirror/etc. wrappers
|
||||
const beautify = require('js-beautify').js_beautify;
|
||||
|
||||
export class ScriptEditor {
|
||||
constructor() {
|
||||
this.editor = null; // Stores the CodeMirror editor reference
|
||||
this.filenameInput = null; // Stores the filename input DOM element
|
||||
}
|
||||
|
||||
init() {
|
||||
throw new Error(`Tried to initialize base ScriptEditor class`);
|
||||
}
|
||||
|
||||
beautifyScript() {
|
||||
if (this.editor == null) {
|
||||
console.warn(`ScriptEditor.beautifyScript() called when editor was not initialized`);
|
||||
return;
|
||||
}
|
||||
let code = this.editor.getValue();
|
||||
code = beautify(code, {
|
||||
indent_size: 4,
|
||||
brace_style: "preserve-inline",
|
||||
});
|
||||
this.editor.setValue(code);
|
||||
}
|
||||
|
||||
openScript(filename="", code="") {
|
||||
if (this.editor == null || this.filenameInput == null) {
|
||||
console.warn(`ScriptEditor.openScript() called when editor was not initialized`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (filename != "") {
|
||||
this.filenameInput.value = filename;
|
||||
this.editor.setValue(code);
|
||||
}
|
||||
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
getCode() {
|
||||
if (this.editor == null) {
|
||||
console.warn(`ScriptEditor.getCode() called when editor was not initialized`);
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.editor.getValue();
|
||||
}
|
||||
|
||||
getFilename() {
|
||||
if (this.filenameInput == null) {
|
||||
console.warn(`ScriptEditor.getFilename() called when editor was not initialized`);
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.filenameInput.value;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { BitNodeMultipliers } from "./BitNodeMultipliers";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CodingContract,
|
||||
ContractTypes } from "./CodingContracts";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
|
@ -1,16 +1,46 @@
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import {Player} from "./Player";
|
||||
import {Server, AllServers, AddToAllServers} from "./Server";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {createRandomIp} from "../utils/IPAddress";
|
||||
import {yesNoTxtInpBoxGetInput} from "../utils/YesNoBox";
|
||||
|
||||
|
||||
/* Functions to handle any server-related purchasing:
|
||||
* Purchasing new servers
|
||||
* Purchasing more RAM for home computer
|
||||
/**
|
||||
* Implements functions for purchasing servers or purchasing more RAM for
|
||||
* the home computer
|
||||
*/
|
||||
function purchaseServer(ram, cost) {
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Player } from "./Player";
|
||||
import { Server,
|
||||
AllServers,
|
||||
AddToAllServers} from "./Server";
|
||||
import { dialogBoxCreate } from "../utils/DialogBox";
|
||||
import { createRandomIp } from "../utils/IPAddress";
|
||||
import { yesNoTxtInpBoxGetInput } from "../utils/YesNoBox";
|
||||
import { isPowerOfTwo } from "../utils/helpers/isPowerOfTwo";
|
||||
|
||||
// Returns the cost of purchasing a server with the given RAM
|
||||
// Returns Infinity for invalid 'ram' arguments
|
||||
export function getPurchaseServerCost(ram) {
|
||||
const sanitizedRam = Math.round(ram);
|
||||
if (isNaN(sanitizedRam) || !isPowerOfTwo(sanitizedRam)) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
if (sanitizedRam > getPurchaseServerMaxRam()) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
return sanitizedRam * CONSTANTS.BaseCostFor1GBOfRamServer * BitNodeMultipliers.PurchasedServerCost;
|
||||
}
|
||||
|
||||
export function getPurchaseServerLimit() {
|
||||
return Math.round(CONSTANTS.PurchasedServerLimit * BitNodeMultipliers.PurchasedServerLimit);
|
||||
}
|
||||
|
||||
export function getPurchaseServerMaxRam() {
|
||||
// TODO ensure this is a power of 2?
|
||||
return Math.round(CONSTANTS.PurchasedServerMaxRam * BitNodeMultipliers.PurchasedServerMaxRam);
|
||||
}
|
||||
|
||||
// Manually purchase a server (NOT through Netscript)
|
||||
export function purchaseServer(ram) {
|
||||
const cost = getPurchaseServerCost(ram);
|
||||
|
||||
//Check if player has enough money
|
||||
if (Player.money.lt(cost)) {
|
||||
dialogBoxCreate("You don't have enough money to purchase this server!");
|
||||
@ -18,8 +48,8 @@ function purchaseServer(ram, cost) {
|
||||
}
|
||||
|
||||
//Maximum server limit
|
||||
if (Player.purchasedServers.length >= CONSTANTS.PurchasedServerLimit) {
|
||||
dialogBoxCreate("You have reached the maximum limit of " + CONSTANTS.PurchasedServerLimit + " servers. " +
|
||||
if (Player.purchasedServers.length >= getPurchaseServerLimit()) {
|
||||
dialogBoxCreate("You have reached the maximum limit of " + getPurchaseServerLimit() + " servers. " +
|
||||
"You cannot purchase any more. You can " +
|
||||
"delete some of your purchased servers using the deleteServer() Netscript function in a script");
|
||||
return;
|
||||
@ -51,19 +81,22 @@ function purchaseServer(ram, cost) {
|
||||
dialogBoxCreate("Server successfully purchased with hostname " + hostname);
|
||||
}
|
||||
|
||||
|
||||
function purchaseRamForHomeComputer(cost) {
|
||||
// Manually upgrade RAM on home computer (NOT through Netscript)
|
||||
export function purchaseRamForHomeComputer(cost) {
|
||||
if (Player.money.lt(cost)) {
|
||||
dialogBoxCreate("You do not have enough money to purchase additional RAM for your home computer");
|
||||
return;
|
||||
}
|
||||
|
||||
var homeComputer = Player.getHomeComputer();
|
||||
homeComputer.maxRam *= 2;
|
||||
const homeComputer = Player.getHomeComputer();
|
||||
if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) {
|
||||
dialogBoxCreate(`You cannot upgrade your home computer RAM because it is at its maximum possible value`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
homeComputer.maxRam *= 2;
|
||||
Player.loseMoney(cost);
|
||||
|
||||
dialogBoxCreate("Purchased additional RAM for home computer! It now has " + homeComputer.maxRam + "GB of RAM.");
|
||||
}
|
||||
|
||||
export {purchaseServer, purchaseRamForHomeComputer};
|
||||
|
@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Enum Of allowed values for the 'OwnedAugmentationsOrder' setting
|
||||
*/
|
||||
export enum OwnedAugmentationsOrderSetting {
|
||||
Alphabetically,
|
||||
AcquirementTime,
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum Of allowed values for the 'OwnedAugmentationsOrder' setting
|
||||
*/
|
||||
export enum PurchaseAugmentationsOrderSetting {
|
||||
Cost,
|
||||
Default,
|
||||
Reputation,
|
||||
}
|
106
src/Settings/SettingEnums.ts
Normal file
106
src/Settings/SettingEnums.ts
Normal file
@ -0,0 +1,106 @@
|
||||
// Enums that defined allowed values for setting configuration
|
||||
|
||||
/**
|
||||
* Allowed values for 'Keybinding/Keymap' setting in Ace editor
|
||||
*/
|
||||
export enum AceKeybindingSetting {
|
||||
Ace = "ace",
|
||||
Emacs = "emacs",
|
||||
Vim = "vim",
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed values for 'Keybinding/Keymap' setting in Code Mirror editor
|
||||
*/
|
||||
export enum CodeMirrorKeybindingSetting {
|
||||
Default = "default",
|
||||
Emacs = "emacs",
|
||||
Sublime = "sublime",
|
||||
Vim = "vim",
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed values for 'Theme' setting in Code Mirror editor
|
||||
*/
|
||||
export enum CodeMirrorThemeSetting {
|
||||
Monokai = "monokai",
|
||||
Day_3024 = "3024-day",
|
||||
Night_3024 = "3024-night",
|
||||
abcdef = "abcdef",
|
||||
Ambiance_mobile = "ambiance-mobile",
|
||||
Ambiance = "ambiance",
|
||||
Base16_dark = "base16-dark",
|
||||
Base16_light = "base16-light",
|
||||
Bespin = "bespin",
|
||||
Blackboard = "blackboard",
|
||||
Cobalt = "cobalt",
|
||||
Colorforth = "colorforth",
|
||||
Darcula = "darcula",
|
||||
Dracula = "dracula",
|
||||
Duotone_dark = "duotone-dark",
|
||||
Duotone_light = "duotone-light",
|
||||
Eclipse = "eclipse",
|
||||
Elegant = "elegant",
|
||||
Erlang_dark = "erlang-dark",
|
||||
Gruvbox_dark = "gruvbox-dark",
|
||||
Hopscotch = "hopscotch",
|
||||
Icecoder = "icecoder",
|
||||
Idea = "idea",
|
||||
Isotope = "isotope",
|
||||
Lesser_dark = "lesser-dark",
|
||||
Liquibyte = "liquibyte",
|
||||
Lucario = "lucario",
|
||||
Material = "material",
|
||||
Mbo = "mbo",
|
||||
Mdn_like = "mdn-like",
|
||||
Midnight = "midnight",
|
||||
Neat = "neat",
|
||||
Neo = "neo",
|
||||
Night = "night",
|
||||
Oceanic_next = "oceanic-next",
|
||||
Panda_syntax = "panda-syntax",
|
||||
Paraiso_dark = "paraiso-dark",
|
||||
Paraiso_light = "paraiso-light",
|
||||
Pastel_on_dark = "pastel-on-dark",
|
||||
Railscasts = "railscasts",
|
||||
Rubyblue = "rubyblue",
|
||||
Seti = "seti",
|
||||
Shadowfox = "shadowfox",
|
||||
Solarized = "solarized",
|
||||
ssms = "ssms",
|
||||
The_matrix = "the-matrix",
|
||||
Tomorrow_night_bright = "tomorrow-night-bright",
|
||||
Tomorrow_night_eighties = "tomorrow-night-eighties",
|
||||
Ttcn = "ttcn",
|
||||
Twilight = "twilight",
|
||||
Vibrant_ink = "vibrant-ink",
|
||||
xq_dark = "xq-dark",
|
||||
xq_light = "xq-light",
|
||||
Yeti = "yeti",
|
||||
Zenburn = "zenburn",
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed values for the "Editor" setting
|
||||
*/
|
||||
export enum EditorSetting {
|
||||
Ace = "Ace",
|
||||
CodeMirror = "CodeMirror",
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed values for the 'OwnedAugmentationsOrder' setting
|
||||
*/
|
||||
export enum PurchaseAugmentationsOrderSetting {
|
||||
Cost,
|
||||
Default,
|
||||
Reputation,
|
||||
}
|
||||
|
||||
/**
|
||||
* Allowed values for the 'OwnedAugmentationsOrder' setting
|
||||
*/
|
||||
export enum OwnedAugmentationsOrderSetting {
|
||||
Alphabetically,
|
||||
AcquirementTime,
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
import { ISelfInitializer, ISelfLoading } from "./types";
|
||||
import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums";
|
||||
import { ISelfInitializer, ISelfLoading } from "../types";
|
||||
import { AceKeybindingSetting,
|
||||
CodeMirrorKeybindingSetting,
|
||||
CodeMirrorThemeSetting,
|
||||
EditorSetting,
|
||||
OwnedAugmentationsOrderSetting,
|
||||
PurchaseAugmentationsOrderSetting } from "./SettingEnums";
|
||||
|
||||
/**
|
||||
* Represents the default settings the player could customize.
|
||||
@ -65,17 +70,22 @@ interface IDefaultSettings {
|
||||
* Represents all possible settings the player wants to customize to their play style.
|
||||
*/
|
||||
interface ISettings extends IDefaultSettings {
|
||||
/**
|
||||
* Which editor should be used (CodeMirror or Ace)?
|
||||
*/
|
||||
Editor: EditorSetting;
|
||||
|
||||
/**
|
||||
* The keybinding to use in the script editor.
|
||||
* TODO: This should really be an enum of allowed values.
|
||||
*/
|
||||
EditorKeybinding: string;
|
||||
EditorKeybinding: AceKeybindingSetting | CodeMirrorKeybindingSetting;
|
||||
|
||||
/**
|
||||
* The theme used in the script editor.
|
||||
* TODO: This should really be an enum of allowed values.
|
||||
*/
|
||||
EditorTheme: string;
|
||||
EditorTheme: string | CodeMirrorThemeSetting;
|
||||
|
||||
/**
|
||||
* What order the player's owned Augmentations/Source Files should be displayed in
|
||||
@ -110,7 +120,8 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
AutosaveInterval: defaultSettings.AutosaveInterval,
|
||||
CodeInstructionRunTime: 25,
|
||||
DisableHotkeys: defaultSettings.DisableHotkeys,
|
||||
EditorKeybinding: "ace",
|
||||
Editor: EditorSetting.Ace,
|
||||
EditorKeybinding: AceKeybindingSetting.Ace,
|
||||
EditorTheme: "Monokai",
|
||||
Locale: "en",
|
||||
MaxLogCapacity: defaultSettings.MaxLogCapacity,
|
@ -1,5 +1,5 @@
|
||||
import {Player} from "./Player";
|
||||
import {BitNodes} from "./BitNode";
|
||||
import { Player } from "./Player";
|
||||
import { BitNodes } from "./BitNode/BitNode";
|
||||
|
||||
/* SourceFile.js */
|
||||
//Each SourceFile corresponds to a BitNode with the same number
|
||||
@ -63,7 +63,8 @@ function initSourceFiles() {
|
||||
"This Source-File also increases your hacking growth multipliers by: " +
|
||||
"<br>Level 1: 12%<br>Level 2: 18%<br>Level 3: 21%");
|
||||
SourceFiles["SourceFile9"] = new SourceFile(9);
|
||||
SourceFiles["SourceFile10"] = new SourceFile(10);
|
||||
SourceFiles["SourceFile10"] = new SourceFile(10, "This Source-File unlocks Sleeve technology in other BitNodes. Each level of this " +
|
||||
"Source-File also grants you a Duplicate Sleeve");
|
||||
SourceFiles["SourceFile11"] = new SourceFile(11, "This Source-File makes it so that company favor increases BOTH the player's salary and reputation gain rate " +
|
||||
"at that company by 1% per favor (rather than just the reputation gain). This Source-File also " +
|
||||
" increases the player's company salary and reputation gain multipliers by:<br><br>" +
|
||||
@ -74,11 +75,6 @@ function initSourceFiles() {
|
||||
"In other words, level N of this Source-File will result in a multiplier of 1.01^N (or 0.99^N for multipliers that decrease)");
|
||||
}
|
||||
|
||||
function PlayerOwnedSourceFile(number, level) {
|
||||
this.n = number;
|
||||
this.lvl = level;
|
||||
}
|
||||
|
||||
//Takes in a PlayerOwnedSourceFile as the "srcFile" argument
|
||||
function applySourceFile(srcFile) {
|
||||
var srcFileKey = "SourceFile" + srcFile.n;
|
||||
@ -192,6 +188,9 @@ function applySourceFile(srcFile) {
|
||||
var incMult = 1 + (mult / 100);
|
||||
Player.hacking_grow_mult *= incMult;
|
||||
break;
|
||||
case 10: // Digital Carbon
|
||||
// No effects, just grants sleeves
|
||||
break;
|
||||
case 11: //The Big Crash
|
||||
var mult = 0;
|
||||
for (var i = 0; i < srcFile.lvl; ++i) {
|
||||
@ -246,4 +245,4 @@ function applySourceFile(srcFile) {
|
||||
sourceFileObject.owned = true;
|
||||
}
|
||||
|
||||
export {SourceFiles, PlayerOwnedSourceFile, applySourceFile, initSourceFiles};
|
||||
export {SourceFiles, applySourceFile, initSourceFiles};
|
||||
|
17
src/SourceFile/PlayerOwnedSourceFile.ts
Normal file
17
src/SourceFile/PlayerOwnedSourceFile.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export class PlayerOwnedSourceFile {
|
||||
// Source-File level
|
||||
lvl: number = 1;
|
||||
|
||||
// Source-File number
|
||||
n: number = 1;
|
||||
|
||||
constructor(n: number, level: number) {
|
||||
this.n = n;
|
||||
this.lvl = level;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPlayerOwnedSourceFile {
|
||||
lvl: number;
|
||||
n: number;
|
||||
}
|
18
src/SourceFile/SourceFileFlags.ts
Normal file
18
src/SourceFile/SourceFileFlags.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// Contains an array containing information about the player's source files
|
||||
// Array[n] returns what level the player has of Source-File N.
|
||||
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
export const SourceFileFlags: number[] = Array(CONSTANTS.TotalNumBitNodes + 1); // Skip 0
|
||||
|
||||
export function updateSourceFileFlags(p: IPlayer) {
|
||||
for (let i = 0; i < SourceFileFlags.length; ++i) {
|
||||
SourceFileFlags[i] = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < p.sourceFiles.length; ++i) {
|
||||
const sf = p.sourceFiles[i];
|
||||
SourceFileFlags[sf.n] = sf.lvl;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
|
||||
import { getRandomInt } from "../utils/helpers/getRandomInt";
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { getRandomInt } from "../../utils/helpers/getRandomInt";
|
||||
|
||||
/**
|
||||
* Represents the valuation of a company in the World Stock Exchange.
|
||||
@ -22,6 +22,11 @@ export class Stock {
|
||||
*/
|
||||
readonly cap: number;
|
||||
|
||||
/**
|
||||
* Maximum number of shares that player can own (both long and short combined)
|
||||
*/
|
||||
readonly maxShares: number;
|
||||
|
||||
/**
|
||||
* Maximum volatility
|
||||
*/
|
||||
@ -73,12 +78,20 @@ export class Stock {
|
||||
*/
|
||||
readonly symbol: string;
|
||||
|
||||
/**
|
||||
* Total number of shares of this stock
|
||||
* This is different than maxShares, as this is like authorized stock while
|
||||
* maxShares is outstanding stock.
|
||||
*/
|
||||
readonly totalShares: number;
|
||||
|
||||
constructor(name: string = "",
|
||||
symbol: string = "",
|
||||
mv: number = 1,
|
||||
b: boolean = true,
|
||||
otlkMag: number = 0,
|
||||
initPrice: number = 10e3) {
|
||||
initPrice: number = 10e3,
|
||||
marketCap: number = 1e12) {
|
||||
this.name = name;
|
||||
this.symbol = symbol;
|
||||
this.price = initPrice;
|
||||
@ -91,6 +104,14 @@ export class Stock {
|
||||
this.otlkMag = otlkMag;
|
||||
this.cap = getRandomInt(initPrice * 1e3, initPrice * 25e3);
|
||||
|
||||
// Total shares is determined by market cap, and is rounded to nearest 100k
|
||||
let totalSharesUnrounded: number = (marketCap / initPrice);
|
||||
this.totalShares = Math.round(totalSharesUnrounded / 1e5) * 1e5;
|
||||
|
||||
// Max Shares (Outstanding shares) is a percentage of total shares
|
||||
const outstandingSharePercentage: number = 0.2;
|
||||
this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e5) * 1e5;
|
||||
|
||||
this.posTxtEl = null;
|
||||
}
|
||||
|
145
src/StockMarket.js → src/StockMarket/StockMarket.js
Executable file → Normal file
145
src/StockMarket.js → src/StockMarket/StockMarket.js
Executable file → Normal file
@ -1,27 +1,29 @@
|
||||
import {CONSTANTS} from "./Constants";
|
||||
import {Locations} from "./Locations";
|
||||
import {hasWallStreetSF, wallStreetSFLvl} from "./NetscriptFunctions";
|
||||
import {WorkerScript} from "./NetscriptWorker";
|
||||
import {Player} from "./Player";
|
||||
import {Stock} from "./Stock";
|
||||
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
|
||||
import {CONSTANTS} from "../Constants";
|
||||
import {Locations} from "../Locations";
|
||||
import {hasWallStreetSF, wallStreetSFLvl} from "../NetscriptFunctions";
|
||||
import {WorkerScript} from "../NetscriptWorker";
|
||||
import {Player} from "../Player";
|
||||
|
||||
import {Page, routing} from ".././ui/navigationTracking";
|
||||
import {numeralWrapper} from ".././ui/numeralFormat";
|
||||
|
||||
import {dialogBoxCreate} from "../../utils/DialogBox";
|
||||
import {clearEventListeners} from "../../utils/uiHelpers/clearEventListeners";
|
||||
import {Reviver, Generic_toJSON,
|
||||
Generic_fromJSON} from "../utils/JSONReviver";
|
||||
import {Page, routing} from "./ui/navigationTracking";
|
||||
import {numeralWrapper} from "./ui/numeralFormat";
|
||||
import {exceptionAlert} from "../utils/helpers/exceptionAlert";
|
||||
import {getRandomInt} from "../utils/helpers/getRandomInt";
|
||||
import {KEY} from "../utils/helpers/keyCodes";
|
||||
import {createElement} from "../utils/uiHelpers/createElement";
|
||||
import {removeChildrenFromElement} from "../utils/uiHelpers/removeChildrenFromElement";
|
||||
import {removeElementById} from "../utils/uiHelpers/removeElementById";
|
||||
Generic_fromJSON} from "../../utils/JSONReviver";
|
||||
import {exceptionAlert} from "../../utils/helpers/exceptionAlert";
|
||||
import {getRandomInt} from "../../utils/helpers/getRandomInt";
|
||||
import {KEY} from "../../utils/helpers/keyCodes";
|
||||
import {createElement} from "../../utils/uiHelpers/createElement";
|
||||
import {removeChildrenFromElement} from "../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import {removeElementById} from "../../utils/uiHelpers/removeElementById";
|
||||
import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
|
||||
yesNoBoxGetYesButton, yesNoBoxGetNoButton,
|
||||
yesNoTxtInpBoxGetYesButton, yesNoTxtInpBoxGetNoButton,
|
||||
yesNoTxtInpBoxGetInput, yesNoBoxClose,
|
||||
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox";
|
||||
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../../utils/YesNoBox";
|
||||
|
||||
var OrderTypes = {
|
||||
LimitBuy: "Limit Buy Order",
|
||||
@ -228,135 +230,135 @@ function initStockMarket() {
|
||||
const randInt = getRandomInt;
|
||||
|
||||
var ecorp = Locations.AevumECorp;
|
||||
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], randInt(40, 50) / 100, true, 19, randInt(17e3, 28e3));
|
||||
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], randInt(40, 50) / 100, true, 19, randInt(17e3, 28e3), 2.4e12);
|
||||
StockMarket[ecorp] = ecorpStk;
|
||||
|
||||
var megacorp = Locations.Sector12MegaCorp;
|
||||
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], randInt(40,50)/100, true, 19, randInt(24e3, 34e3));
|
||||
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], randInt(40,50)/100, true, 19, randInt(24e3, 34e3), 2.4e12);
|
||||
StockMarket[megacorp] = megacorpStk;
|
||||
|
||||
var blade = Locations.Sector12BladeIndustries;
|
||||
var bladeStk = new Stock(blade, StockSymbols[blade], randInt(70, 80)/100, true, 13, randInt(12e3, 25e3));
|
||||
var bladeStk = new Stock(blade, StockSymbols[blade], randInt(70, 80)/100, true, 13, randInt(12e3, 25e3), 1.6e12);
|
||||
StockMarket[blade] = bladeStk;
|
||||
|
||||
var clarke = Locations.AevumClarkeIncorporated;
|
||||
var clarkeStk = new Stock(clarke, StockSymbols[clarke], randInt(65, 75)/100, true, 12, randInt(10e3, 25e3));
|
||||
var clarkeStk = new Stock(clarke, StockSymbols[clarke], randInt(65, 75)/100, true, 12, randInt(10e3, 25e3), 1.5e12);
|
||||
StockMarket[clarke] = clarkeStk;
|
||||
|
||||
var omnitek = Locations.VolhavenOmniTekIncorporated;
|
||||
var omnitekStk = new Stock(omnitek, StockSymbols[omnitek], randInt(60, 70)/100, true, 12, randInt(32e3, 43e3));
|
||||
var omnitekStk = new Stock(omnitek, StockSymbols[omnitek], randInt(60, 70)/100, true, 12, randInt(32e3, 43e3), 1.8e12);
|
||||
StockMarket[omnitek] = omnitekStk;
|
||||
|
||||
var foursigma = Locations.Sector12FourSigma;
|
||||
var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], randInt(100, 110)/100, true, 17, randInt(50e3, 80e3));
|
||||
var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], randInt(100, 110)/100, true, 17, randInt(50e3, 80e3), 2e12);
|
||||
StockMarket[foursigma] = foursigmaStk;
|
||||
|
||||
var kuaigong = Locations.ChongqingKuaiGongInternational;
|
||||
var kuaigongStk = new Stock(kuaigong, StockSymbols[kuaigong], randInt(75, 85)/100, true, 10, randInt(16e3, 28e3));
|
||||
var kuaigongStk = new Stock(kuaigong, StockSymbols[kuaigong], randInt(75, 85)/100, true, 10, randInt(16e3, 28e3), 1.9e12);
|
||||
StockMarket[kuaigong] = kuaigongStk;
|
||||
|
||||
var fulcrum = Locations.AevumFulcrumTechnologies;
|
||||
var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], randInt(120, 130)/100, true, 16, randInt(29e3, 36e3));
|
||||
var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], randInt(120, 130)/100, true, 16, randInt(29e3, 36e3), 2e12);
|
||||
StockMarket[fulcrum] = fulcrumStk;
|
||||
|
||||
var storm = Locations.IshimaStormTechnologies;
|
||||
var stormStk = new Stock(storm, StockSymbols[storm], randInt(80, 90)/100, true, 7, randInt(20e3, 25e3));
|
||||
var stormStk = new Stock(storm, StockSymbols[storm], randInt(80, 90)/100, true, 7, randInt(20e3, 25e3), 1.2e12);
|
||||
StockMarket[storm] = stormStk;
|
||||
|
||||
var defcomm = Locations.NewTokyoDefComm;
|
||||
var defcommStk = new Stock(defcomm, StockSymbols[defcomm], randInt(60, 70)/100, true, 10, randInt(6e3, 19e3));
|
||||
var defcommStk = new Stock(defcomm, StockSymbols[defcomm], randInt(60, 70)/100, true, 10, randInt(6e3, 19e3), 900e9);
|
||||
StockMarket[defcomm] = defcommStk;
|
||||
|
||||
var helios = Locations.VolhavenHeliosLabs;
|
||||
var heliosStk = new Stock(helios, StockSymbols[helios], randInt(55, 65)/100, true, 9, randInt(10e3, 18e3));
|
||||
var heliosStk = new Stock(helios, StockSymbols[helios], randInt(55, 65)/100, true, 9, randInt(10e3, 18e3), 825e9);
|
||||
StockMarket[helios] = heliosStk;
|
||||
|
||||
var vitalife = Locations.NewTokyoVitaLife;
|
||||
var vitalifeStk = new Stock(vitalife, StockSymbols[vitalife], randInt(70, 80)/100, true, 7, randInt(8e3, 14e3));
|
||||
var vitalifeStk = new Stock(vitalife, StockSymbols[vitalife], randInt(70, 80)/100, true, 7, randInt(8e3, 14e3), 1e12);
|
||||
StockMarket[vitalife] = vitalifeStk;
|
||||
|
||||
var icarus = Locations.Sector12IcarusMicrosystems;
|
||||
var icarusStk = new Stock(icarus, StockSymbols[icarus], randInt(60, 70)/100, true, 7.5, randInt(12e3, 24e3));
|
||||
var icarusStk = new Stock(icarus, StockSymbols[icarus], randInt(60, 70)/100, true, 7.5, randInt(12e3, 24e3), 800e9);
|
||||
StockMarket[icarus] = icarusStk;
|
||||
|
||||
var universalenergy = Locations.Sector12UniversalEnergy;
|
||||
var universalenergyStk = new Stock(universalenergy, StockSymbols[universalenergy], randInt(50, 60)/100, true, 10, randInt(16e3, 29e3));
|
||||
var universalenergyStk = new Stock(universalenergy, StockSymbols[universalenergy], randInt(50, 60)/100, true, 10, randInt(16e3, 29e3), 900e9);
|
||||
StockMarket[universalenergy] = universalenergyStk;
|
||||
|
||||
var aerocorp = Locations.AevumAeroCorp;
|
||||
var aerocorpStk = new Stock(aerocorp, StockSymbols[aerocorp], randInt(55, 65)/100, true, 6, randInt(8e3, 17e3));
|
||||
var aerocorpStk = new Stock(aerocorp, StockSymbols[aerocorp], randInt(55, 65)/100, true, 6, randInt(8e3, 17e3), 640e9);
|
||||
StockMarket[aerocorp] = aerocorpStk;
|
||||
|
||||
var omnia = Locations.VolhavenOmniaCybersystems;
|
||||
var omniaStk = new Stock(omnia, StockSymbols[omnia], randInt(65, 75)/100, true, 4.5, randInt(6e3, 15e3));
|
||||
var omniaStk = new Stock(omnia, StockSymbols[omnia], randInt(65, 75)/100, true, 4.5, randInt(6e3, 15e3), 600e9);
|
||||
StockMarket[omnia] = omniaStk;
|
||||
|
||||
var solaris = Locations.ChongqingSolarisSpaceSystems;
|
||||
var solarisStk = new Stock(solaris, StockSymbols[solaris], randInt(70, 80)/100, true, 8.5, randInt(14e3, 28e3));
|
||||
var solarisStk = new Stock(solaris, StockSymbols[solaris], randInt(70, 80)/100, true, 8.5, randInt(14e3, 28e3), 705e9);
|
||||
StockMarket[solaris] = solarisStk;
|
||||
|
||||
var globalpharm = Locations.NewTokyoGlobalPharmaceuticals;
|
||||
var globalpharmStk = new Stock(globalpharm, StockSymbols[globalpharm], randInt(55, 65)/100, true, 10.5, randInt(12e3, 30e3));
|
||||
var globalpharmStk = new Stock(globalpharm, StockSymbols[globalpharm], randInt(55, 65)/100, true, 10.5, randInt(12e3, 30e3), 695e9);
|
||||
StockMarket[globalpharm] = globalpharmStk;
|
||||
|
||||
var nova = Locations.IshimaNovaMedical;
|
||||
var novaStk = new Stock(nova, StockSymbols[nova], randInt(70, 80)/100, true, 5, randInt(15e3, 27e3));
|
||||
var novaStk = new Stock(nova, StockSymbols[nova], randInt(70, 80)/100, true, 5, randInt(15e3, 27e3), 600e9);
|
||||
StockMarket[nova] = novaStk;
|
||||
|
||||
var watchdog = Locations.AevumWatchdogSecurity;
|
||||
var watchdogStk = new Stock(watchdog, StockSymbols[watchdog], randInt(240, 260)/100, true, 1.5, randInt(4e3, 8.5e3));
|
||||
var watchdogStk = new Stock(watchdog, StockSymbols[watchdog], randInt(240, 260)/100, true, 1.5, randInt(4e3, 8.5e3), 450e9);
|
||||
StockMarket[watchdog] = watchdogStk;
|
||||
|
||||
var lexocorp = Locations.VolhavenLexoCorp;
|
||||
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], randInt(115, 135)/100, true, 6, randInt(4.5e3, 8e3));
|
||||
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], randInt(115, 135)/100, true, 6, randInt(4.5e3, 8e3), 300e9);
|
||||
StockMarket[lexocorp] = lexocorpStk;
|
||||
|
||||
var rho = Locations.AevumRhoConstruction;
|
||||
var rhoStk = new Stock(rho, StockSymbols[rho], randInt(50, 70)/100, true, 1, randInt(2e3, 7e3));
|
||||
var rhoStk = new Stock(rho, StockSymbols[rho], randInt(50, 70)/100, true, 1, randInt(2e3, 7e3), 180e9);
|
||||
StockMarket[rho] = rhoStk;
|
||||
|
||||
var alpha = Locations.Sector12AlphaEnterprises;
|
||||
var alphaStk = new Stock(alpha, StockSymbols[alpha], randInt(175, 205)/100, true, 10, randInt(4e3, 8.5e3));
|
||||
var alphaStk = new Stock(alpha, StockSymbols[alpha], randInt(175, 205)/100, true, 10, randInt(4e3, 8.5e3), 240e9);
|
||||
StockMarket[alpha] = alphaStk;
|
||||
|
||||
var syscore = Locations.VolhavenSysCoreSecurities;
|
||||
var syscoreStk = new Stock(syscore, StockSymbols[syscore], randInt(150, 170)/100, true, 3, randInt(3e3, 8e3));
|
||||
var syscoreStk = new Stock(syscore, StockSymbols[syscore], randInt(150, 170)/100, true, 3, randInt(3e3, 8e3), 200e9);
|
||||
StockMarket[syscore] = syscoreStk;
|
||||
|
||||
var computek = Locations.VolhavenCompuTek;
|
||||
var computekStk = new Stock(computek, StockSymbols[computek], randInt(80, 100)/100, true, 4, randInt(1e3, 6e3));
|
||||
var computekStk = new Stock(computek, StockSymbols[computek], randInt(80, 100)/100, true, 4, randInt(1e3, 6e3), 185e9);
|
||||
StockMarket[computek] = computekStk;
|
||||
|
||||
var netlink = Locations.AevumNetLinkTechnologies;
|
||||
var netlinkStk = new Stock(netlink, StockSymbols[netlink], randInt(400, 430)/100, true, 1, randInt(1e3, 5e3));
|
||||
var netlinkStk = new Stock(netlink, StockSymbols[netlink], randInt(400, 430)/100, true, 1, randInt(1e3, 5e3), 58e9);
|
||||
StockMarket[netlink] = netlinkStk;
|
||||
|
||||
var omega = Locations.IshimaOmegaSoftware;
|
||||
var omegaStk = new Stock(omega, StockSymbols[omega], randInt(90, 110)/100, true, 0.5, randInt(1e3, 8e3));
|
||||
var omegaStk = new Stock(omega, StockSymbols[omega], randInt(90, 110)/100, true, 0.5, randInt(1e3, 8e3), 60e9);
|
||||
StockMarket[omega] = omegaStk;
|
||||
|
||||
var fns = Locations.Sector12FoodNStuff;
|
||||
var fnsStk = new Stock(fns, StockSymbols[fns], randInt(70, 80)/100, false, 1, randInt(500, 4.5e3));
|
||||
var fnsStk = new Stock(fns, StockSymbols[fns], randInt(70, 80)/100, false, 1, randInt(500, 4.5e3), 45e9);
|
||||
StockMarket[fns] = fnsStk;
|
||||
|
||||
var sigmacosm = "Sigma Cosmetics";
|
||||
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], randInt(260, 300)/100, true, 0, randInt(1.5e3, 3.5e3));
|
||||
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], randInt(260, 300)/100, true, 0, randInt(1.5e3, 3.5e3), 30e9);
|
||||
StockMarket[sigmacosm] = sigmacosmStk;
|
||||
|
||||
var joesguns = "Joes Guns";
|
||||
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], randInt(360, 400)/100, true, 1, randInt(250, 1.5e3));
|
||||
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], randInt(360, 400)/100, true, 1, randInt(250, 1.5e3), 42e9);
|
||||
StockMarket[joesguns] = joesgunsStk;
|
||||
|
||||
var catalyst = "Catalyst Ventures";
|
||||
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], randInt(120, 175)/100, true, 13.5, randInt(250, 1.5e3));
|
||||
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], randInt(120, 175)/100, true, 13.5, randInt(250, 1.5e3), 100e9);
|
||||
StockMarket[catalyst] = catalystStk;
|
||||
|
||||
var microdyne = "Microdyne Technologies";
|
||||
var microdyneStk = new Stock(microdyne, StockSymbols[microdyne], randInt(70, 80)/100, true, 8, randInt(15e3, 30e3));
|
||||
var microdyneStk = new Stock(microdyne, StockSymbols[microdyne], randInt(70, 80)/100, true, 8, randInt(15e3, 30e3), 360e9);
|
||||
StockMarket[microdyne] = microdyneStk;
|
||||
|
||||
var titanlabs = "Titan Laboratories";
|
||||
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], randInt(50, 70)/100, true, 11, randInt(12e3, 24e3));
|
||||
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], randInt(50, 70)/100, true, 11, randInt(12e3, 24e3), 420e9);
|
||||
StockMarket[titanlabs] = titanlabsStk;
|
||||
|
||||
var orders = {};
|
||||
@ -411,6 +413,7 @@ function buyStock(stock, shares) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Does player have enough money?
|
||||
var totalPrice = stock.price * shares;
|
||||
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
|
||||
dialogBoxCreate("You do not have enough money to purchase this. You need " +
|
||||
@ -418,6 +421,13 @@ function buyStock(stock, shares) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Would this purchase exceed the maximum number of shares?
|
||||
if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) {
|
||||
dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ` +
|
||||
`${numeralWrapper.formatBigNumber(stock.maxShares)} shares.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
var origTotal = stock.playerShares * stock.playerAvgPx;
|
||||
Player.loseMoney(totalPrice + CONSTANTS.StockMarketCommission);
|
||||
var newTotal = origTotal + totalPrice;
|
||||
@ -470,6 +480,7 @@ function shortStock(stock, shares, workerScript=null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Does the player have enough money?
|
||||
var totalPrice = stock.price * shares;
|
||||
if (Player.money.lt(totalPrice + CONSTANTS.StockMarketCommission)) {
|
||||
if (tixApi) {
|
||||
@ -484,6 +495,19 @@ function shortStock(stock, shares, workerScript=null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Would this purchase exceed the maximum number of shares?
|
||||
if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) {
|
||||
if (tixApi) {
|
||||
workerScript.scriptRef.log("ERROR: shortStock() failed because purchasing this many short shares would exceed " +
|
||||
`${stock.symbol}'s maximum number of shares.`);
|
||||
} else {
|
||||
dialogBoxCreate(`You cannot purchase this many shares. ${stock.symbol} has a maximum of ` +
|
||||
`${stock.maxShares} shares.`);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var origTotal = stock.playerShortShares * stock.playerAvgShortPx;
|
||||
Player.loseMoney(totalPrice + CONSTANTS.StockMarketCommission);
|
||||
var newTotal = origTotal + totalPrice;
|
||||
@ -1190,6 +1214,7 @@ function createStockTicker(stock) {
|
||||
switch (ordType) {
|
||||
case "Market Order":
|
||||
var shares = Math.floor((money - COMM) / stock.price);
|
||||
shares = Math.min(shares, Math.round(stock.maxShares - stock.playerShares - stock.playerShortShares));
|
||||
pos === PositionTypes.Long ? buyStock(stock, shares) : shortStock(stock, shares, null);
|
||||
break;
|
||||
case "Limit Order":
|
||||
@ -1206,6 +1231,7 @@ function createStockTicker(stock) {
|
||||
type = OrderTypes.StopBuy;
|
||||
}
|
||||
var shares = Math.floor((money-COMM) / price);
|
||||
shares = Math.min(shares, Math.round(stock.maxShares - stock.playerShares - stock.playerShortShares));
|
||||
placeOrder(stock, shares, price, type, pos);
|
||||
yesNoTxtInpBoxClose();
|
||||
});
|
||||
@ -1384,25 +1410,26 @@ function updateStockPlayerPosition(stock) {
|
||||
if (isNaN(shortPercentageGains)) { shortPercentageGains = 0; }
|
||||
|
||||
stock.posTxtEl.innerHTML =
|
||||
"<h1 class='tooltip stock-market-position-text'>Long Position: " +
|
||||
`Max Shares: ${numeralWrapper.format(stock.maxShares, "0.000a")}<br>` +
|
||||
"<h3 class='tooltip stock-market-position-text'>Long Position: " +
|
||||
"<span class='tooltiptext'>Shares in the long position will increase " +
|
||||
"in value if the price of the corresponding stock increases</span></h1>" +
|
||||
"in value if the price of the corresponding stock increases</span></h3>" +
|
||||
"<br>Shares: " + numeralWrapper.format(stock.playerShares, '0,0') +
|
||||
"<br>Average Price: " + numeralWrapper.format(stock.playerAvgPx, '$0.000a') +
|
||||
" (Total Cost: " + numeralWrapper.format(totalCost, '$0.000a') + ")" +
|
||||
"<br>Profit: " + numeralWrapper.format(gains, '$0.000a') +
|
||||
" (" + numeralWrapper.format(percentageGains, '0.00%') + ")<br><br>";
|
||||
" (" + numeralWrapper.format(percentageGains, '0.00%') + ")<br>";
|
||||
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) {
|
||||
stock.posTxtEl.innerHTML +=
|
||||
"<h1 class='tooltip stock-market-position-text'>Short Position: " +
|
||||
"<br><h3 class='tooltip stock-market-position-text'>Short Position: " +
|
||||
"<span class='tooltiptext'>Shares in short position will increase " +
|
||||
"in value if the price of the corresponding stock decreases</span></h1>" +
|
||||
"in value if the price of the corresponding stock decreases</span></h3>" +
|
||||
"<br>Shares: " + numeralWrapper.format(stock.playerShortShares, '0,0') +
|
||||
"<br>Average Price: " + numeralWrapper.format(stock.playerAvgShortPx, '$0.000a') +
|
||||
" (Total Cost: " + numeralWrapper.format(shortTotalCost, '$0.000a') + ")" +
|
||||
"<br>Profit: " + numeralWrapper.format(shortGains, '$0.000a') +
|
||||
"<br>Average Price: " + numeralWrapper.formatMoney(stock.playerAvgShortPx) +
|
||||
" (Total Cost: " + numeralWrapper.formatMoney(shortTotalCost) + ")" +
|
||||
"<br>Profit: " + numeralWrapper.formatMoney(shortGains) +
|
||||
" (" + numeralWrapper.format(shortPercentageGains, '0.00%') + ")" +
|
||||
"<br><br><h1 class='stock-market-position-text'>Orders: </h1>";
|
||||
"<br><br><h3 class='stock-market-position-text'>Orders:</h3>";
|
||||
}
|
||||
|
||||
}
|
@ -30,7 +30,7 @@ import {findRunningScript, RunningScript,
|
||||
AllServersMap, isScriptFilename} from "./Script";
|
||||
import {AllServers, GetServerByHostname,
|
||||
getServer, Server} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
import {Settings} from "./Settings/Settings";
|
||||
import {SpecialServerIps,
|
||||
SpecialServerNames} from "./SpecialServerIps";
|
||||
import {getTextFile} from "./TextFile";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user