mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-26 17:43:48 +01:00
commit
37ec5c733f
@ -1,13 +1,13 @@
|
||||
Collection of Quotes
|
||||
|
||||
The past is relevant only as data
|
||||
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.
|
||||
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,
|
||||
|
56
css/infiltration.scss
Normal file
56
css/infiltration.scss
Normal file
@ -0,0 +1,56 @@
|
||||
@import "theme";
|
||||
|
||||
.blinking-cursor {
|
||||
font-weight: 100;
|
||||
color: #2E3D48;
|
||||
-webkit-animation: 1s cursorblink step-end infinite;
|
||||
-moz-animation: 1s cursorblink step-end infinite;
|
||||
-ms-animation: 1s cursorblink step-end infinite;
|
||||
-o-animation: 1s cursorblink step-end infinite;
|
||||
animation: 1s cursorblink step-end infinite;
|
||||
}
|
||||
|
||||
@keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent;
|
||||
}
|
||||
50% {
|
||||
color: $hacker-green;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes cursorblink {
|
||||
from, to {
|
||||
color: transparent;
|
||||
}
|
||||
50% {
|
||||
color: $hacker-green;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent;
|
||||
}
|
||||
50% {
|
||||
color: $hacker-green;
|
||||
}
|
||||
}
|
||||
|
||||
@-ms-keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent;
|
||||
}
|
||||
50% {
|
||||
color: $hacker-green;
|
||||
}
|
||||
}
|
||||
|
||||
@-o-keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent;
|
||||
}
|
||||
50% {
|
||||
color: $hacker-green;
|
||||
}
|
||||
}
|
@ -69,3 +69,22 @@
|
||||
float: right;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.interactive-tutorial-command {
|
||||
background-color: #000;
|
||||
color: $hacker-green;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.interactive-tutorial-code {
|
||||
background-color: #272822;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.interactive-tutorial-tab {
|
||||
background-color: #555;
|
||||
color: #e6e6e6;
|
||||
padding: 3px;
|
||||
box-shadow: 0 0 3px #000;
|
||||
}
|
@ -150,38 +150,6 @@
|
||||
/* Infiltration */
|
||||
#infiltration-container {
|
||||
position: fixed;
|
||||
padding: 6px;
|
||||
|
||||
span {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#infiltration-left-panel,
|
||||
#infiltration-right-panel {
|
||||
display: inline-block;
|
||||
border: 1px solid #fff;
|
||||
width: 35%;
|
||||
height: 75%;
|
||||
top: 10px;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#infiltration-faction-select {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#infiltration-left-panel p,
|
||||
#infiltration-right-panel p {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
#infiltration-buttons {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#infiltration-buttons .a-link-button {
|
||||
display: inline;
|
||||
margin: 5px;
|
||||
width: 70%;
|
||||
}
|
||||
|
4
dist/engine.bundle.js
vendored
4
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/engineStyle.bundle.js
vendored
2
dist/engineStyle.bundle.js
vendored
@ -1,2 +1,2 @@
|
||||
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([845,0]),o()}({784:function(n,t,o){},786:function(n,t,o){},788:function(n,t,o){},790:function(n,t,o){},792:function(n,t,o){},794:function(n,t,o){},796:function(n,t,o){},798:function(n,t,o){},800:function(n,t,o){},802:function(n,t,o){},804:function(n,t,o){},806:function(n,t,o){},808:function(n,t,o){},810:function(n,t,o){},812:function(n,t,o){},814:function(n,t,o){},816:function(n,t,o){},818:function(n,t,o){},820:function(n,t,o){},822:function(n,t,o){},824:function(n,t,o){},826:function(n,t,o){},828:function(n,t,o){},830:function(n,t,o){},832:function(n,t,o){},834:function(n,t,o){},836:function(n,t,o){},838:function(n,t,o){},840:function(n,t,o){},842:function(n,t,o){},845:function(n,t,o){"use strict";o.r(t);o(844),o(842),o(840),o(838),o(836),o(834),o(832),o(830),o(828),o(826),o(824),o(822),o(820),o(818),o(816),o(814),o(812),o(810),o(808),o(806),o(804),o(802),o(800),o(798),o(796),o(794),o(792),o(790),o(788),o(786),o(784)}});
|
||||
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([852,0]),o()}({789:function(n,t,o){},791:function(n,t,o){},793:function(n,t,o){},795:function(n,t,o){},797:function(n,t,o){},799:function(n,t,o){},801:function(n,t,o){},803:function(n,t,o){},805:function(n,t,o){},807:function(n,t,o){},809:function(n,t,o){},811:function(n,t,o){},813:function(n,t,o){},815:function(n,t,o){},817:function(n,t,o){},819:function(n,t,o){},821:function(n,t,o){},823:function(n,t,o){},825:function(n,t,o){},827:function(n,t,o){},829:function(n,t,o){},831:function(n,t,o){},833:function(n,t,o){},835:function(n,t,o){},837:function(n,t,o){},839:function(n,t,o){},841:function(n,t,o){},843:function(n,t,o){},845:function(n,t,o){},847:function(n,t,o){},849:function(n,t,o){},852:function(n,t,o){"use strict";o.r(t);o(851),o(849),o(847),o(845),o(843),o(841),o(839),o(837),o(835),o(833),o(831),o(829),o(827),o(825),o(823),o(821),o(819),o(817),o(815),o(813),o(811),o(809),o(807),o(805),o(803),o(801),o(799),o(797),o(795),o(793),o(791),o(789)}});
|
||||
//# sourceMappingURL=engineStyle.bundle.js.map
|
86
dist/engineStyle.css
vendored
86
dist/engineStyle.css
vendored
@ -1355,33 +1355,8 @@ button {
|
||||
/* Infiltration */
|
||||
#infiltration-container {
|
||||
position: fixed;
|
||||
padding: 6px; }
|
||||
#infiltration-container span {
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
|
||||
#infiltration-left-panel,
|
||||
#infiltration-right-panel {
|
||||
display: inline-block;
|
||||
border: 1px solid #fff;
|
||||
width: 35%;
|
||||
height: 75%;
|
||||
top: 10px;
|
||||
overflow-y: auto;
|
||||
overflow-x: auto; }
|
||||
|
||||
#infiltration-faction-select {
|
||||
color: #fff; }
|
||||
|
||||
#infiltration-left-panel p,
|
||||
#infiltration-right-panel p {
|
||||
margin: 4px; }
|
||||
|
||||
#infiltration-buttons {
|
||||
margin-top: 20px; }
|
||||
|
||||
#infiltration-buttons .a-link-button {
|
||||
display: inline; }
|
||||
margin: 5px;
|
||||
width: 70%; }
|
||||
|
||||
/**
|
||||
* Styling for the Augmentations UI. This is the page that displays all of the
|
||||
@ -1814,6 +1789,22 @@ button {
|
||||
float: right;
|
||||
padding: 4px; }
|
||||
|
||||
.interactive-tutorial-command {
|
||||
background-color: #000;
|
||||
color: #adff2f;
|
||||
white-space: nowrap; }
|
||||
|
||||
.interactive-tutorial-code {
|
||||
background-color: #272822;
|
||||
color: white;
|
||||
padding: 3px; }
|
||||
|
||||
.interactive-tutorial-tab {
|
||||
background-color: #555;
|
||||
color: #e6e6e6;
|
||||
padding: 3px;
|
||||
box-shadow: 0 0 3px #000; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
* {
|
||||
@ -5037,5 +5028,46 @@ html {
|
||||
padding: 6px;
|
||||
width: 60%; }
|
||||
|
||||
/* COLORS */
|
||||
/* Attributes */
|
||||
.blinking-cursor {
|
||||
font-weight: 100;
|
||||
color: #2E3D48;
|
||||
-webkit-animation: 1s cursorblink step-end infinite;
|
||||
-moz-animation: 1s cursorblink step-end infinite;
|
||||
-ms-animation: 1s cursorblink step-end infinite;
|
||||
-o-animation: 1s cursorblink step-end infinite;
|
||||
animation: 1s cursorblink step-end infinite; }
|
||||
|
||||
@keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent; }
|
||||
50% {
|
||||
color: #adff2f; } }
|
||||
|
||||
@-moz-keyframes cursorblink {
|
||||
from, to {
|
||||
color: transparent; }
|
||||
50% {
|
||||
color: #adff2f; } }
|
||||
|
||||
@-webkit-keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent; }
|
||||
50% {
|
||||
color: #adff2f; } }
|
||||
|
||||
@-ms-keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent; }
|
||||
50% {
|
||||
color: #adff2f; } }
|
||||
|
||||
@-o-keyframes "cursorblink" {
|
||||
from, to {
|
||||
color: transparent; }
|
||||
50% {
|
||||
color: #adff2f; } }
|
||||
|
||||
|
||||
/*# sourceMappingURL=engineStyle.css.map*/
|
48
dist/vendor.bundle.js
vendored
48
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -3,6 +3,55 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
v0.52.0 - 2021-06-13 Infiltration 2.0 (hydroflame & community)
|
||||
--------------------------------------------------------------
|
||||
|
||||
**Infiltration**
|
||||
|
||||
* Completely reworked. Not the same mechanic at all.
|
||||
|
||||
**Terminal**
|
||||
|
||||
* tail is smarter. It automatically assume the only possible options in some
|
||||
cases.
|
||||
|
||||
**Intelligence**
|
||||
|
||||
* Now available when starting BN5 instead of after beating it for the first
|
||||
time.
|
||||
* Nerf the effect of intelligence on reputation gain.
|
||||
|
||||
**Augmentation**
|
||||
|
||||
* Added a new augmentation, the 'Unstable Circadian Modulator', whose
|
||||
gimmick is that its stats are randomized every hour.
|
||||
|
||||
**Netscript**
|
||||
|
||||
* 'getPlayer' is not a singularity function anymore.
|
||||
* 'hacknetNodes.constants' returns the correct values.
|
||||
* 'createGang' has been added.
|
||||
* 'inGang' has been added.
|
||||
|
||||
**Tutorial**
|
||||
|
||||
* Updated the tutorial. Made it look cleaner, fixed typos, etc.
|
||||
|
||||
**Misc.**
|
||||
|
||||
* Fix many typos in literature (@kwazygloo)
|
||||
* Fix being able to unfocus from gym and university.
|
||||
* Fix being able to do hacking missions while unfocused.
|
||||
* Fix many typos in Augmentation descriptions (@kwazygloo)
|
||||
* More numbers handle absurdly large values. (@Tesseract1234567890)
|
||||
* Fix many typos (@Tesseract1234567890)
|
||||
* Fixed an issue that caused a UI desync when sleeves were set to workout
|
||||
stats other than strength at the gym.
|
||||
* Fix weird alignment of donation text box and button. (@Tesseract1234567890)
|
||||
* Fixed an issue where reputation could be transfered to new jobs when unfocused.
|
||||
* Empty stack traces should no longer appear.
|
||||
* Purchasing anything with Infinity money doesn't result in NaN.
|
||||
|
||||
v0.51.10 - 2021-05-31 Focus Mark, Focus! (hydroflame)
|
||||
-----------------------------------------------------
|
||||
|
||||
|
@ -64,9 +64,9 @@ documentation_title = '{0} Documentation'.format(project)
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.51'
|
||||
version = '0.52'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.51.9'
|
||||
release = '0.52.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
12
doc/source/netscript/gangapi/createGang.rst
Normal file
12
doc/source/netscript/gangapi/createGang.rst
Normal file
@ -0,0 +1,12 @@
|
||||
createGang() Netscript Function
|
||||
======================================
|
||||
|
||||
.. js:function:: createGang(faction)
|
||||
|
||||
:RAM cost: 1 GB
|
||||
:param string faction: Name of faction
|
||||
:returns: ``true`` if a Gang was created with that faction.
|
||||
|
||||
Creates a Gang with that faction. You need to have access to Gangs, the
|
||||
faction must be one of the approved gang factions, and you must be a member
|
||||
of that faction for the creation to be successful.
|
7
doc/source/netscript/gangapi/inGang.rst
Normal file
7
doc/source/netscript/gangapi/inGang.rst
Normal file
@ -0,0 +1,7 @@
|
||||
inGang() Netscript Function
|
||||
======================================
|
||||
|
||||
.. js:function:: inGang()
|
||||
|
||||
:RAM cost: 1 GB
|
||||
:returns: ``true`` if the player is already in a gang.
|
@ -25,6 +25,8 @@ In :ref:`netscriptjs`::
|
||||
.. toctree::
|
||||
:caption: API Functions:
|
||||
|
||||
createGang() <gangapi/createGang>
|
||||
inGang() <gangapi/inGang>
|
||||
getMemberNames() <gangapi/getMemberNames>
|
||||
getGangInformation() <gangapi/getGangInformation>
|
||||
getOtherGangInformation() <gangapi/getOtherGangInformation>
|
||||
|
22
index.html
22
index.html
@ -65,7 +65,7 @@
|
||||
<span id="augmentations-notification" class="notification-off"> </span>
|
||||
</li>
|
||||
<li id="hacknet-nodes-tab" class="mainmenu-accordion-panel">
|
||||
<button id="hacknet-nodes-menu-link"> Hacknet Nodes </button>
|
||||
<button id="hacknet-nodes-menu-link"> Hacknet </button>
|
||||
</li>
|
||||
<li id="sleeves-tab" class="mainmenu-accordion-panel">
|
||||
<button id="sleeves-menu-link"> Sleeves </button>
|
||||
@ -264,25 +264,7 @@
|
||||
<div id="location-container" class="generic-menupage-container">
|
||||
</div>
|
||||
|
||||
<div id="infiltration-container" class="generic-menupage-container">
|
||||
<div id="infiltration-left-panel">
|
||||
<p id="infiltration-level-text"> </p>
|
||||
<div id="infiltration-buttons">
|
||||
<button class="a-link-button tooltip" id="infiltration-kill"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-knockout"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-stealthknockout"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-assassinate"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-hacksecurity"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-destroysecurity"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-sneak"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-pickdoor"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-bribe"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-escape"> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="infiltration-right-panel">
|
||||
<p id="infiltration-status-text"></p>
|
||||
</div>
|
||||
<div id="infiltration-container" class="generic-fullscreen-container">
|
||||
</div>
|
||||
|
||||
<div id="stock-market-container" class="generic-menupage-container">
|
||||
|
@ -126,5 +126,5 @@
|
||||
"watch": "webpack --watch --mode production",
|
||||
"watch:dev": "webpack --watch --mode development"
|
||||
},
|
||||
"version": "0.51.10"
|
||||
"version": "0.52.0"
|
||||
}
|
||||
|
@ -2157,15 +2157,31 @@ function installAugmentations() {
|
||||
dialogBoxCreate("You have not purchased any Augmentations to install!");
|
||||
return false;
|
||||
}
|
||||
var augmentationList = "";
|
||||
for (var i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||
var aug = Augmentations[Player.queuedAugmentations[i].name];
|
||||
let augmentationList = "";
|
||||
let nfgIndex = -1;
|
||||
for(let i = Player.queuedAugmentations.length-1; i >= 0; i--) {
|
||||
if(Player.queuedAugmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
|
||||
nfgIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
|
||||
const ownedAug = Player.queuedAugmentations[i];
|
||||
const aug = Augmentations[ownedAug.name];
|
||||
if (aug == null) {
|
||||
console.error(`Invalid augmentation: ${Player.queuedAugmentations[i].name}`);
|
||||
console.error(`Invalid augmentation: ${ownedAug.name}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ownedAug.name === AugmentationNames.NeuroFluxGovernor
|
||||
&& i !== nfgIndex) continue;
|
||||
|
||||
let level = null;
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
level = ` - ${ownedAug.level}`;
|
||||
}
|
||||
applyAugmentation(Player.queuedAugmentations[i]);
|
||||
augmentationList += (aug.name + "<br>");
|
||||
augmentationList += (aug.name + level + "<br>");
|
||||
}
|
||||
Player.queuedAugmentations = [];
|
||||
dialogBoxCreate("You slowly drift to sleep as scientists put you under in order " +
|
||||
|
@ -7,12 +7,23 @@ import * as React from "react";
|
||||
import { Augmentations } from "../../Augmentation/Augmentations";
|
||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||
import { Player } from "../../Player";
|
||||
import { IPlayerOwnedAugmentation } from "../../Augmentation/PlayerOwnedAugmentation";
|
||||
|
||||
import { AugmentationAccordion } from "../../ui/React/AugmentationAccordion";
|
||||
|
||||
export function PurchasedAugmentations(): React.ReactElement {
|
||||
const augs: React.ReactElement[] = [];
|
||||
for (const ownedAug of Player.queuedAugmentations) {
|
||||
// Only render the last NeuroFlux (there are no findLastIndex btw)
|
||||
let nfgIndex = -1;
|
||||
for(let i = Player.queuedAugmentations.length-1; i >= 0; i--) {
|
||||
if(Player.queuedAugmentations[i].name === AugmentationNames.NeuroFluxGovernor) {
|
||||
nfgIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < Player.queuedAugmentations.length; i++) {
|
||||
const ownedAug = Player.queuedAugmentations[i];
|
||||
if(ownedAug.name === AugmentationNames.NeuroFluxGovernor && i !== nfgIndex) continue;
|
||||
const aug = Augmentations[ownedAug.name];
|
||||
let level = null;
|
||||
if (ownedAug.name === AugmentationNames.NeuroFluxGovernor) {
|
||||
|
@ -6,7 +6,7 @@
|
||||
import { IMap } from "./types";
|
||||
|
||||
export const CONSTANTS: IMap<any> = {
|
||||
Version: "0.51.10",
|
||||
Version: "0.52.0",
|
||||
|
||||
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
||||
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
||||
@ -226,40 +226,46 @@ export const CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.51.10 - 2021-05-31 Focus Mark, Focus! (hydroflame)
|
||||
v0.52.0 - 2021-06-13 Infiltration 2.0 (hydroflame)
|
||||
-------
|
||||
|
||||
Focus
|
||||
* You can now use the terminal and write scripts while working for factions
|
||||
but you will gain reputation at a slower rate.
|
||||
Infiltration
|
||||
* Completely reworked. Not the same mechanic at all.
|
||||
|
||||
SF -1
|
||||
* Added a new SF -1: Bypass
|
||||
Terminal
|
||||
* tail is smarter. It automatically assume the only possible options in some
|
||||
cases.
|
||||
|
||||
Gang
|
||||
* "Vigilante justice"/"Ethical hacking" now reduces wanted level by a very
|
||||
small percentage as well an absolute value.
|
||||
Intelligence
|
||||
* Now available when starting BN5 instead of after beating it for the first
|
||||
time.
|
||||
* Nerf the effect of intelligence on reputation gain.
|
||||
|
||||
Augmentation
|
||||
* Added a new augmentation, the 'Unstable Circadian Modulator', whose
|
||||
gimmick is that its stats are randomized every hour.
|
||||
|
||||
Netscript
|
||||
* 'tFormat' now has a second argument to display with millisecond precision.
|
||||
* 'purchaseSleeveAug' can no longer purchase the same aug over and over for
|
||||
the same sleeve.
|
||||
* fix typo in logging for 'getServerSecurityLevel'
|
||||
* Fixed some weird issue where very rarely you would get 0 exp from 'grow'
|
||||
* 'getActionTime' now returns correct values for Diplomacy and Regeneration.
|
||||
* 'getPlayer' is not a singularity function anymore.
|
||||
* 'hacknetNodes.constants' returns the correct values.
|
||||
* 'createGang' has been added.
|
||||
* 'inGang' has been added.
|
||||
|
||||
Corporations
|
||||
* Fixed an exploit where you could get nearly infinite corporation funds by
|
||||
entering negative numbers in textboxes.
|
||||
* Fixed an exploit where shares could be sold again by clicking the
|
||||
"sell share" button via scripts.
|
||||
|
||||
Documentation
|
||||
* typo fix in purchaseTor
|
||||
* typo fix in basicgameplay/stats
|
||||
Tutorial
|
||||
* Updated the tutorial. Made it look cleaner, fixed typos, etc.
|
||||
|
||||
Misc.
|
||||
* Very large number will no longer appear as "$NaNt"
|
||||
* Hash capacity now displays in the "big number" format.
|
||||
* Fix many typos in literature (@kwazygloo)
|
||||
* Fix being able to unfocus from gym and university.
|
||||
* Fix being able to do hacking missions while unfocused.
|
||||
* Fix many typos in Augmentation descriptions (@kwazygloo)
|
||||
* More numbers handle absurdly large values. (@Tesseract1234567890)
|
||||
* Fix many typos (@Tesseract1234567890)
|
||||
* Fixed an issue that caused a UI desync when sleeves were set to workout
|
||||
stats other than strength at the gym.
|
||||
* Fix weird alignment of donation text box and button. (@Tesseract1234567890)
|
||||
* Fixed an issue where reputation could be transfered to new jobs when unfocused.
|
||||
* Empty stack traces should no longer appear.
|
||||
* Purchasing anything with Infinity money doesn't result in NaN.
|
||||
`,
|
||||
}
|
@ -726,7 +726,7 @@ class DevMenuComponent extends Component {
|
||||
<button className="std-button" onClick={this.addMoney(1e9)}>Add $1b</button>
|
||||
<button className="std-button" onClick={this.addMoney(1e12)}>Add $1t</button>
|
||||
<button className="std-button" onClick={this.addMoney(1e15)}>Add $1000t</button>
|
||||
<button className="std-button" onClick={this.addMoney(1e99)}>Add $1e99</button>
|
||||
<button className="std-button" onClick={this.addMoney(Infinity)}>Add $Infinity</button>
|
||||
<button className="std-button" onClick={this.upgradeRam}>Upgrade Home Computer's RAM</button>
|
||||
</div>
|
||||
<div className="row">
|
||||
|
@ -31,6 +31,7 @@ type IState = {
|
||||
|
||||
const inputStyleMarkup = {
|
||||
margin: "5px",
|
||||
height: "26px",
|
||||
}
|
||||
|
||||
export class DonateOption extends React.Component<IProps, IState> {
|
||||
|
@ -79,8 +79,7 @@ export class Info extends React.Component<IProps, any> {
|
||||
By doing so you will earn reputation for your faction. You will also gain
|
||||
reputation passively over time, although at a very slow rate. Earning
|
||||
reputation will allow you to purchase Augmentations through this faction, which
|
||||
are powerful upgrades that enhance your abilities. Note that you cannot use your
|
||||
terminal or create scripts when you are performing a task!
|
||||
are powerful upgrades that enhance your abilities.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
@ -29,11 +29,10 @@ export function PlayerInfo(props) {
|
||||
{Money(Player.money.toNumber())}<br />
|
||||
|
||||
{
|
||||
hasServers &&
|
||||
<><span>Hashes: {Hashes(Player.hashManager.hashes)} / {Hashes(Player.hashManager.capacity)}</span><br /></>
|
||||
hasServers && <><span>Hashes: {Hashes(Player.hashManager.hashes)} / {Hashes(Player.hashManager.capacity)}</span><br /></>
|
||||
}
|
||||
|
||||
<span>Total Hacknet Node Production: </span>
|
||||
<span>Total Hacknet {hasServers ? 'Server' : 'Node'} Production: </span>
|
||||
{prod}
|
||||
</p>
|
||||
)
|
||||
|
@ -129,7 +129,7 @@ export class HacknetRoot extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Hacknet Nodes</h1>
|
||||
<h1>Hacknet {hasHacknetServers() ? "Servers" : "Nodes"}</h1>
|
||||
<GeneralInfo />
|
||||
|
||||
<PurchaseButton cost={purchaseCost} multiplier={this.state.purchaseMultiplier} onClick={this.handlePurchaseButtonClick} />
|
||||
|
@ -8,7 +8,8 @@ export interface IEngine {
|
||||
loadFactionContent: () => void;
|
||||
loadFactionsContent: () => void;
|
||||
loadGangContent: () => void;
|
||||
loadInfiltrationContent: () => void;
|
||||
loadInfiltrationContent: (name: string, difficulty: number, maxLevel: number) => void;
|
||||
loadLocationContent: () => void;
|
||||
loadMissionContent: () => void;
|
||||
loadResleevingContent: () => void;
|
||||
loadStockMarketContent: () => void;
|
||||
|
7
src/Infiltration.d.ts
vendored
7
src/Infiltration.d.ts
vendored
@ -1,7 +0,0 @@
|
||||
import { LocationName } from "./Locations/data/LocationNames";
|
||||
|
||||
export declare function beginInfiltration(companyName: LocationName,
|
||||
startLevel: number,
|
||||
rewardVal: number,
|
||||
maxClearance: number,
|
||||
diff: number): void;
|
@ -1,864 +0,0 @@
|
||||
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";
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
|
||||
let InfiltrationScenarios = {
|
||||
Guards: "You see an armed security guard patrolling the area.",
|
||||
TechOnly: "The area is equipped with a state-of-the-art security system: cameras, laser tripwires, and sentry turrets.",
|
||||
TechOrLockedDoor: "The area is equipped with a state-of-the-art security system. There is a locked door on the side of the " +
|
||||
"room that can be used to bypass security.",
|
||||
Bots: "You see a few security bots patrolling the area.",
|
||||
}
|
||||
|
||||
function InfiltrationInstance(companyName, startLevel, val, maxClearance, diff) {
|
||||
this.companyName = companyName;
|
||||
this.clearanceLevel = 0;
|
||||
this.maxClearanceLevel = maxClearance;
|
||||
this.securityLevel = startLevel;
|
||||
this.difficulty = diff; // Affects how much security level increases. Represents a percentage
|
||||
this.baseValue = val; // Base value of company secrets
|
||||
this.secretsStolen = []; // Numbers representing value of stolen secrets
|
||||
|
||||
this.hackingExpGained = 0;
|
||||
this.strExpGained = 0;
|
||||
this.defExpGained = 0;
|
||||
this.dexExpGained = 0;
|
||||
this.agiExpGained = 0;
|
||||
this.chaExpGained = 0;
|
||||
this.intExpGained = 0;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.expMultiplier = function() {
|
||||
if (!this.clearanceLevel || isNaN(this.clearanceLevel) || !this.maxClearanceLevel ||isNaN(this.maxClearanceLevel)) return 1;
|
||||
return 2.5 * this.clearanceLevel / this.maxClearanceLevel;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainHackingExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.hackingExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedHackingExp = function() {
|
||||
if(!this.hackingExpGained || isNaN(this.hackingExpGained)) return 0;
|
||||
return Math.pow(this.hackingExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainStrengthExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.strExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedStrengthExp = function() {
|
||||
if (!this.strExpGained || isNaN(this.strExpGained)) return 0;
|
||||
return Math.pow(this.strExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDefenseExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.defExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDefenseExp = function() {
|
||||
if (!this.defExpGained || isNaN(this.defExpGained)) return 0;
|
||||
return Math.pow(this.defExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainDexterityExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.dexExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedDexterityExp = function() {
|
||||
if (!this.dexExpGained || isNaN(this.dexExpGained)) return 0;
|
||||
return Math.pow(this.dexExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainAgilityExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.agiExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedAgilityExp = function() {
|
||||
if (!this.agiExpGained || isNaN(this.agiExpGained)) return 0;
|
||||
return Math.pow(this.agiExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainCharismaExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.chaExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedCharismaExp = function() {
|
||||
if (!this.chaExpGained || isNaN(this.chaExpGained)) return 0;
|
||||
return Math.pow(this.chaExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.gainIntelligenceExp = function(amt) {
|
||||
if (isNaN(amt)) {return;}
|
||||
this.intExpGained += amt;
|
||||
}
|
||||
|
||||
InfiltrationInstance.prototype.calcGainedIntelligenceExp = function() {
|
||||
if(!this.intExpGained || isNaN(this.intExpGained)) return 0;
|
||||
return Math.pow(this.intExpGained * this.expMultiplier(), CONSTANTS.InfiltrationExpPow);
|
||||
}
|
||||
|
||||
function beginInfiltration(companyName, startLevel, val, maxClearance, diff) {
|
||||
var inst = new InfiltrationInstance(companyName, startLevel, val, maxClearance, diff);
|
||||
clearInfiltrationStatusText();
|
||||
nextInfiltrationLevel(inst);
|
||||
}
|
||||
|
||||
function endInfiltration(inst, success) {
|
||||
if (success) {infiltrationBoxCreate(inst);}
|
||||
|
||||
clearEventListeners("infiltration-kill");
|
||||
clearEventListeners("infiltration-knockout");
|
||||
clearEventListeners("infiltration-stealthknockout");
|
||||
clearEventListeners("infiltration-assassinate");
|
||||
clearEventListeners("infiltration-hacksecurity");
|
||||
clearEventListeners("infiltration-destroysecurity");
|
||||
clearEventListeners("infiltration-sneak");
|
||||
clearEventListeners("infiltration-pickdoor");
|
||||
clearEventListeners("infiltration-bribe");
|
||||
clearEventListeners("infiltration-escape");
|
||||
|
||||
Engine.loadLocationContent(false);
|
||||
}
|
||||
|
||||
function nextInfiltrationLevel(inst) {
|
||||
++inst.clearanceLevel;
|
||||
updateInfiltrationLevelText(inst);
|
||||
|
||||
//Buttons
|
||||
var killButton = clearEventListeners("infiltration-kill");
|
||||
var knockoutButton = clearEventListeners("infiltration-knockout");
|
||||
var stealthKnockoutButton = clearEventListeners("infiltration-stealthknockout");
|
||||
var assassinateButton = clearEventListeners("infiltration-assassinate");
|
||||
var hackSecurityButton = clearEventListeners("infiltration-hacksecurity");
|
||||
var destroySecurityButton = clearEventListeners("infiltration-destroysecurity");
|
||||
var sneakButton = clearEventListeners("infiltration-sneak");
|
||||
var pickdoorButton = clearEventListeners("infiltration-pickdoor");
|
||||
var bribeButton = clearEventListeners("infiltration-bribe");
|
||||
var escapeButton = clearEventListeners("infiltration-escape");
|
||||
|
||||
killButton.style.display = "none";
|
||||
knockoutButton.style.display = "none";
|
||||
stealthKnockoutButton.style.display = "none";
|
||||
assassinateButton.style.display = "none";
|
||||
hackSecurityButton.style.display = "none";
|
||||
destroySecurityButton.style.display = "none";
|
||||
sneakButton.style.display = "none";
|
||||
pickdoorButton.style.display = "none";
|
||||
bribeButton.style.display = "none";
|
||||
escapeButton.style.display = "none";
|
||||
|
||||
var rand = getRandomInt(0, 5); // This needs to change if more scenarios are added
|
||||
var scenario = null;
|
||||
switch (rand) {
|
||||
case 1:
|
||||
scenario = InfiltrationScenarios.TechOnly;
|
||||
hackSecurityButton.style.display = "block";
|
||||
destroySecurityButton.style.display = "block";
|
||||
sneakButton.style.display = "block";
|
||||
escapeButton.style.display = "block";
|
||||
break;
|
||||
case 2:
|
||||
scenario = InfiltrationScenarios.TechOrLockedDoor;
|
||||
hackSecurityButton.style.display = "block";
|
||||
destroySecurityButton.style.display = "block";
|
||||
sneakButton.style.display = "block";
|
||||
pickdoorButton.style.display = "block";
|
||||
escapeButton.style.display = "block";
|
||||
break;
|
||||
case 3:
|
||||
scenario = InfiltrationScenarios.Bots;
|
||||
killButton.style.display = "block";
|
||||
killButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationKill(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> killed the security bots! Unfortunately you alerted the " +
|
||||
"rest of the facility's security. The facility's security " +
|
||||
"level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
Player.karma -= 1;
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
var dmgTaken = Math.max(1, Math.round(1.5 * inst.securityLevel / Player.defense));
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to kill the security bots. The bots fight back " +
|
||||
"and raise the alarm! You take " + dmgTaken + " damage and " +
|
||||
"the facility's security level increases by " +
|
||||
formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
if (Player.takeDamage(dmgTaken)) {
|
||||
endInfiltration(inst, false);
|
||||
}
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
});
|
||||
assassinateButton.style.display = "block";
|
||||
assassinateButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationAssassinate(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> assassinated the security bots without being detected!");
|
||||
Player.karma -= 1;
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to assassinate the security bots. The bots have not detected " +
|
||||
"you but are now more alert for an intruder. The facility's security level " +
|
||||
"has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
});
|
||||
hackSecurityButton.style.display = "block";
|
||||
sneakButton.style.display = "block";
|
||||
escapeButton.style.display = "block";
|
||||
break;
|
||||
default: //0, 4-5
|
||||
scenario = InfiltrationScenarios.Guards;
|
||||
killButton.style.display = "block";
|
||||
killButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationKill(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> killed the security guard! Unfortunately you alerted the " +
|
||||
"rest of the facility's security. The facility's security " +
|
||||
"level has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
Player.karma -= 3;
|
||||
++Player.numPeopleKilled;
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense));
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to kill the security guard. The guard fights back " +
|
||||
"and raises the alarm! You take " + dmgTaken + " damage and " +
|
||||
"the facility's security level has increased by " +
|
||||
formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
if (Player.takeDamage(dmgTaken)) {
|
||||
endInfiltration(inst, false);
|
||||
}
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
});
|
||||
knockoutButton.style.display = "block";
|
||||
stealthKnockoutButton.style.display = "block";
|
||||
assassinateButton.style.display = "block";
|
||||
assassinateButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationAssassinate(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> assassinated the security guard without being detected!");
|
||||
Player.karma -= 3;
|
||||
++Player.numPeopleKilled;
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to assassinate the security guard. The guard has not detected " +
|
||||
"you but is now more alert for an intruder. The facility's security level " +
|
||||
"has increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
});
|
||||
sneakButton.style.display = "block";
|
||||
bribeButton.style.display = "block";
|
||||
escapeButton.style.display = "block";
|
||||
break;
|
||||
}
|
||||
|
||||
knockoutButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationKnockout(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> knocked out the security guard! " +
|
||||
"Unfortunately you made a lot of noise and alerted other security.");
|
||||
writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense));
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to knockout the security guard. The guard " +
|
||||
"raises the alarm and fights back! You take " + dmgTaken + " damage and " +
|
||||
"the facility's security level increases by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
if (Player.takeDamage(dmgTaken)) {
|
||||
endInfiltration(inst, false);
|
||||
}
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
stealthKnockoutButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationStealthKnockout(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> knocked out the security guard without making " +
|
||||
"any noise!");
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
var dmgTaken = Math.max(1, Math.round(inst.securityLevel / Player.defense));
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to stealthily knockout the security guard. The guard " +
|
||||
"raises the alarm and fights back! You take " + dmgTaken + " damage and " +
|
||||
"the facility's security level increases by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
if (Player.takeDamage(dmgTaken)) {
|
||||
endInfiltration(inst, false);
|
||||
}
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
hackSecurityButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationHack(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> hacked and disabled the security system!");
|
||||
writeInfiltrationStatusText("The facility's security level increased by " + ((res[1]*100) - 100).toString() + "%");
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to hack the security system. The facility's " +
|
||||
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
destroySecurityButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationDestroySecurity(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> and violently destroy the security system!");
|
||||
writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to destroy the security system. The facility's " +
|
||||
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
sneakButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationSneak(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> sneak past the security undetected!");
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> and were detected while trying to sneak past security! The facility's " +
|
||||
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
pickdoorButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationPickLockedDoor(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> pick the locked door!");
|
||||
writeInfiltrationStatusText("The facility's security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to pick the locked door. The facility's security level " +
|
||||
"increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
bribeButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var bribeAmt = CONSTANTS.InfiltrationBribeBaseAmount * inst.clearanceLevel;
|
||||
if (Player.money.lt(bribeAmt)) {
|
||||
writeInfiltrationStatusText("You do not have enough money to bribe the guard. " +
|
||||
"You need $" + bribeAmt);
|
||||
return false;
|
||||
}
|
||||
var res = attemptInfiltrationBribe(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> bribed a guard to let you through " +
|
||||
"to the next clearance level for $" + bribeAmt);
|
||||
Player.loseMoney(bribeAmt);
|
||||
endInfiltrationLevel(inst);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to bribe a guard! The guard is alerting " +
|
||||
"other security guards about your presence! The facility's " +
|
||||
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
escapeButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var res = attemptInfiltrationEscape(inst);
|
||||
if (res[0]) {
|
||||
writeInfiltrationStatusText("You <span class='success'>SUCCESSFULLY</span> escape from the facility with the stolen classified " +
|
||||
"documents and company secrets!");
|
||||
endInfiltration(inst, true);
|
||||
return false;
|
||||
} else {
|
||||
writeInfiltrationStatusText("You <span class='failure'>FAILED</span> to escape from the facility. You took 1 damage. The facility's " +
|
||||
"security level increased by " + formatNumber((res[1]*100)-100, 2).toString() + "%");
|
||||
if (Player.takeDamage(1)) {
|
||||
endInfiltration(inst, false);
|
||||
}
|
||||
}
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
updateInfiltrationLevelText(inst);
|
||||
return false;
|
||||
});
|
||||
|
||||
updateInfiltrationButtons(inst, scenario);
|
||||
writeInfiltrationStatusText("");
|
||||
writeInfiltrationStatusText("You are now on clearance level " + inst.clearanceLevel + ".<br>" +
|
||||
scenario);
|
||||
}
|
||||
|
||||
|
||||
function endInfiltrationLevel(inst) {
|
||||
// Check if you gained any secrets
|
||||
if (inst.clearanceLevel % 5 == 0) {
|
||||
var baseSecretValue = inst.baseValue * inst.clearanceLevel / 2;
|
||||
var secretValue = baseSecretValue * Player.faction_rep_mult *
|
||||
CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep;
|
||||
var secretMoneyValue = baseSecretValue * CONSTANTS.InfiltrationMoneyValue *
|
||||
BitNodeMultipliers.InfiltrationMoney;
|
||||
inst.secretsStolen.push(baseSecretValue);
|
||||
dialogBoxCreate("You found and stole a set of classified documents from the company. " +
|
||||
"These classified secrets could probably be sold for money (<span class='money-gold'>" +
|
||||
numeralWrapper.formatMoney(secretMoneyValue) + "</span>), or they " +
|
||||
"could be given to factions for reputation (<span class='light-yellow'>" + numeralWrapper.formatReputation(secretValue) + " rep</span>)");
|
||||
}
|
||||
|
||||
// Increase security level based on difficulty
|
||||
inst.securityLevel *= (1 + (inst.difficulty / 100));
|
||||
writeInfiltrationStatusText("You move on to the facility's next clearance level. This " +
|
||||
"clearance level has " + inst.difficulty + "% higher security");
|
||||
|
||||
// If this is max level, force endInfiltration
|
||||
if (inst.clearanceLevel >= inst.maxClearanceLevel) {
|
||||
endInfiltration(inst, true);
|
||||
} else {
|
||||
nextInfiltrationLevel(inst);
|
||||
}
|
||||
}
|
||||
|
||||
function writeInfiltrationStatusText(txt) {
|
||||
var statusTxt = document.getElementById("infiltration-status-text");
|
||||
statusTxt.innerHTML += (txt + "<br>");
|
||||
statusTxt.parentElement.scrollTop = statusTxt.scrollHeight;
|
||||
}
|
||||
|
||||
function clearInfiltrationStatusText() {
|
||||
document.getElementById("infiltration-status-text").innerHTML = "";
|
||||
}
|
||||
|
||||
function updateInfiltrationLevelText(inst) {
|
||||
var totalValue = 0;
|
||||
var totalMoneyValue = 0;
|
||||
for (var i = 0; i < inst.secretsStolen.length; ++i) {
|
||||
totalValue += (inst.secretsStolen[i] * Player.faction_rep_mult *
|
||||
CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep);
|
||||
totalMoneyValue += inst.secretsStolen[i] * CONSTANTS.InfiltrationMoneyValue *
|
||||
BitNodeMultipliers.InfiltrationMoney;
|
||||
}
|
||||
|
||||
// TODO: fix this to not rely on <pre> and whitespace for formatting...
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
document.getElementById("infiltration-level-text").innerHTML =
|
||||
"Facility name: " + inst.companyName + "<br>" +
|
||||
"Clearance Level: " + inst.clearanceLevel + "<br>" +
|
||||
"Security Level: " + numeralWrapper.formatInfiltrationSecurity(inst.securityLevel) + "<br><br>" +
|
||||
"Total value of stolen secrets<br>" +
|
||||
"Reputation: <span class='light-yellow'>" + numeralWrapper.formatReputation(totalValue, 3) + "</span><br>" +
|
||||
"Money: <span class='money-gold'>" + numeralWrapper.formatMoney(totalMoneyValue, 2) + "</span><br><br>" +
|
||||
"Hack exp gained: " + numeralWrapper.formatExp(inst.calcGainedHackingExp(), 3) + "<br>" +
|
||||
"Str exp gained: " + numeralWrapper.formatExp(inst.calcGainedStrengthExp(), 3) + "<br>" +
|
||||
"Def exp gained: " + numeralWrapper.formatExp(inst.calcGainedDefenseExp(), 3) + "<br>" +
|
||||
"Dex exp gained: " + numeralWrapper.formatExp(inst.calcGainedDexterityExp(), 3) + "<br>" +
|
||||
"Agi exp gained: " + numeralWrapper.formatExp(inst.calcGainedAgilityExp(), 3) + "<br>" +
|
||||
"Cha exp gained: " + numeralWrapper.formatExp(inst.calcGainedCharismaExp(), 3);
|
||||
/* eslint-enable no-irregular-whitespace */
|
||||
}
|
||||
|
||||
function updateInfiltrationButtons(inst, scenario) {
|
||||
var killChance = getInfiltrationKillChance(inst);
|
||||
var knockoutChance = getInfiltrationKnockoutChance(inst);
|
||||
var stealthKnockoutChance = getInfiltrationStealthKnockoutChance(inst);
|
||||
var assassinateChance = getInfiltrationAssassinateChance(inst);
|
||||
var destroySecurityChance = getInfiltrationDestroySecurityChance(inst);
|
||||
var hackChance = getInfiltrationHackChance(inst);
|
||||
var sneakChance = getInfiltrationSneakChance(inst);
|
||||
var lockpickChance = getInfiltrationPickLockedDoorChance(inst);
|
||||
var bribeChance = getInfiltrationBribeChance(inst);
|
||||
var escapeChance = getInfiltrationEscapeChance(inst);
|
||||
|
||||
document.getElementById("infiltration-escape").innerHTML = "Escape" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to escape the facility with the classified secrets and " +
|
||||
"documents you have stolen. You have a " +
|
||||
numeralWrapper.formatPercentage(escapeChance, 2) + " chance of success. If you fail, " +
|
||||
"the security level will increase by 5%.</span>";
|
||||
|
||||
switch(scenario) {
|
||||
case InfiltrationScenarios.TechOrLockedDoor:
|
||||
document.getElementById("infiltration-pickdoor").innerHTML = "Lockpick" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to pick the locked door. You have a " +
|
||||
numeralWrapper.formatPercentage(lockpickChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increased by 1%. If you fail, the " +
|
||||
"security level will increase by 3%.</span>";
|
||||
case InfiltrationScenarios.TechOnly:
|
||||
document.getElementById("infiltration-hacksecurity").innerHTML = "Hack" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to hack and disable the security system. You have a " +
|
||||
numeralWrapper.formatPercentage(hackChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increase by 3%. If you fail, " +
|
||||
"the security level will increase by 5%.</span>";
|
||||
|
||||
document.getElementById("infiltration-destroysecurity").innerHTML = "Destroy security" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to violently destroy the security system. You have a " +
|
||||
numeralWrapper.formatPercentage(destroySecurityChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increase by 5%. If you fail, the " +
|
||||
"security level will increase by 10%. </span>";
|
||||
|
||||
document.getElementById("infiltration-sneak").innerHTML = "Sneak" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to sneak past the security system. You have a " +
|
||||
numeralWrapper.formatPercentage(sneakChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 8%. </span>";
|
||||
break;
|
||||
case InfiltrationScenarios.Bots:
|
||||
document.getElementById("infiltration-kill").innerHTML = "Destroy bots" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to destroy the security bots through combat. You have a " +
|
||||
numeralWrapper.formatPercentage(killChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increase by 5%. If you fail, " +
|
||||
"the security level will increase by 10%. </span>";
|
||||
|
||||
document.getElementById("infiltration-assassinate").innerHTML = "Assassinate bots" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to stealthily destroy the security bots through assassination. You have a " +
|
||||
numeralWrapper.formatPercentage(assassinateChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 10%. </span>";
|
||||
|
||||
document.getElementById("infiltration-hacksecurity").innerHTML = "Hack bots" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to disable the security bots by hacking them. You have a " +
|
||||
numeralWrapper.formatPercentage(hackChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increase by 3%. If you fail, " +
|
||||
"the security level will increase by 5%. </span>";
|
||||
|
||||
document.getElementById("infiltration-sneak").innerHTML = "Sneak" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to sneak past the security bots. You have a " +
|
||||
numeralWrapper.formatPercentage(sneakChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 8%. </span>";
|
||||
break;
|
||||
|
||||
case InfiltrationScenarios.Guards:
|
||||
default:
|
||||
document.getElementById("infiltration-kill").innerHTML = "Kill" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to kill the security guard. You have a " +
|
||||
numeralWrapper.formatPercentage(killChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increase by 5%. If you fail, " +
|
||||
"the security level will decrease by 10%. </span>";
|
||||
|
||||
document.getElementById("infiltration-knockout").innerHTML = "Knockout" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to knockout the security guard. You have a " +
|
||||
numeralWrapper.formatPercentage(knockoutChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increase by 3%. If you fail, the " +
|
||||
"security level will increase by 10%. </span>";
|
||||
|
||||
document.getElementById("infiltration-stealthknockout").innerHTML = "Stealth Knockout" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to stealthily knockout the security guard. You have a " +
|
||||
numeralWrapper.formatPercentage(stealthKnockoutChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 10%. </span>";
|
||||
|
||||
document.getElementById("infiltration-assassinate").innerHTML = "Assassinate" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to assassinate the security guard. You have a " +
|
||||
numeralWrapper.formatPercentage(assassinateChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 5%. </span>";
|
||||
|
||||
document.getElementById("infiltration-sneak").innerHTML = "Sneak" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to sneak past the security guard. You have a " +
|
||||
numeralWrapper.formatPercentage(sneakChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 8%. </span>";
|
||||
|
||||
document.getElementById("infiltration-bribe").innerHTML = "Bribe" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to bribe the security guard. You have a " +
|
||||
numeralWrapper.formatPercentage(bribeChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 15%. </span>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let intWgt = CONSTANTS.IntelligenceInfiltrationWeight;
|
||||
|
||||
// Kill
|
||||
// Success: 5%, Failure 10%, -Karma
|
||||
function attemptInfiltrationKill(inst) {
|
||||
var chance = getInfiltrationKillChance(inst);
|
||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||
inst.gainDefenseExp(inst.securityLevel / 75) * Player.defense_exp_mult;
|
||||
inst.gainDexterityExp(inst.securityLevel / 75) * Player.dexterity_exp_mult;
|
||||
inst.gainAgilityExp(inst.securityLevel / 75) * Player.agility_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
inst.securityLevel *= 1.05;
|
||||
return [true, 1.05];
|
||||
} else {
|
||||
inst.securityLevel *= 1.1;
|
||||
return [false, 1.1];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationKillChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.strength +
|
||||
Player.dexterity +
|
||||
Player.agility) / (1.45 * lvl));
|
||||
}
|
||||
|
||||
|
||||
// Knockout
|
||||
// Success: 3%, Failure: 10%
|
||||
function attemptInfiltrationKnockout(inst) {
|
||||
var chance = getInfiltrationKnockoutChance(inst);
|
||||
inst.gainStrengthExp(inst.securityLevel / 70) * Player.strength_exp_mult;
|
||||
inst.gainDefenseExp(inst.securityLevel / 70) * Player.defense_exp_mult;
|
||||
inst.gainDexterityExp(inst.securityLevel / 70) * Player.dexterity_exp_mult;
|
||||
inst.gainAgilityExp(inst.securityLevel / 70) * Player.agility_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
inst.securityLevel *= 1.03;
|
||||
return [true, 1.03];
|
||||
} else {
|
||||
inst.securityLevel *= 1.1;
|
||||
return [false, 1.1];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationKnockoutChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.strength +
|
||||
Player.dexterity +
|
||||
Player.agility) / (1.7 * lvl));
|
||||
}
|
||||
|
||||
// Stealth knockout
|
||||
// Success: 0%, Failure: 10%
|
||||
function attemptInfiltrationStealthKnockout(inst) {
|
||||
var chance = getInfiltrationStealthKnockoutChance(inst);
|
||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||
inst.gainDexterityExp(inst.securityLevel / 60) * Player.dexterity_exp_mult;
|
||||
inst.gainAgilityExp(inst.securityLevel / 60) * Player.agility_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
return [true, 1];
|
||||
} else {
|
||||
inst.securityLevel *= 1.1;
|
||||
return [false, 1.1];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationStealthKnockoutChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(0.55 * Player.strength +
|
||||
2 * Player.dexterity +
|
||||
2 * Player.agility +
|
||||
intWgt * Player.intelligence) / (3 * lvl));
|
||||
}
|
||||
|
||||
// Assassination
|
||||
// Success: 0%, Failure: 5%, -Karma
|
||||
function attemptInfiltrationAssassinate(inst) {
|
||||
var chance = getInfiltrationAssassinateChance(inst);
|
||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||
inst.gainDexterityExp(inst.securityLevel / 55) * Player.dexterity_exp_mult;
|
||||
inst.gainAgilityExp(inst.securityLevel / 55) * Player.agility_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
return [true, 1];
|
||||
} else {
|
||||
inst.securityLevel *= 1.05;
|
||||
return [false, 1.05];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationAssassinateChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.dexterity +
|
||||
0.5 * Player.agility +
|
||||
intWgt * Player.intelligence) / (2 * lvl));
|
||||
}
|
||||
|
||||
|
||||
// Destroy security
|
||||
// Success: 5%, Failure: 10%
|
||||
function attemptInfiltrationDestroySecurity(inst) {
|
||||
var chance = getInfiltrationDestroySecurityChance(inst);
|
||||
inst.gainStrengthExp(inst.securityLevel / 75) * Player.strength_exp_mult;
|
||||
inst.gainDefenseExp(inst.securityLevel / 75) * Player.defense_exp_mult;
|
||||
inst.gainDexterityExp(inst.securityLevel / 75) * Player.dexterity_exp_mult;
|
||||
inst.gainAgilityExp(inst.securityLevel / 75) * Player.agility_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
inst.securityLevel *= 1.05;
|
||||
return [true, 1.05];
|
||||
} else {
|
||||
inst.securityLevel *= 1.1;
|
||||
return [false, 1.1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getInfiltrationDestroySecurityChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.strength +
|
||||
Player.dexterity +
|
||||
Player.agility) / (2 * lvl));
|
||||
}
|
||||
|
||||
|
||||
// Hack security
|
||||
// Success: 3%, Failure: 5%
|
||||
function attemptInfiltrationHack(inst) {
|
||||
var chance = getInfiltrationHackChance(inst);
|
||||
inst.gainHackingExp(inst.securityLevel / 30) * Player.hacking_exp_mult;
|
||||
inst.gainIntelligenceExp(inst.securityLevel / 680);
|
||||
if (Math.random() <= chance) {
|
||||
inst.securityLevel *= 1.03;
|
||||
return [true, 1.03];
|
||||
} else {
|
||||
inst.securityLevel *= 1.05;
|
||||
return [false, 1.05];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getInfiltrationHackChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.hacking_skill +
|
||||
(intWgt * Player.intelligence)) / lvl);
|
||||
}
|
||||
|
||||
// Sneak past security
|
||||
// Success: 0%, Failure: 8%
|
||||
function attemptInfiltrationSneak(inst) {
|
||||
var chance = getInfiltrationSneakChance(inst);
|
||||
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
return [true, 1];
|
||||
} else {
|
||||
inst.securityLevel *= 1.08;
|
||||
return [false, 1.08];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationSneakChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.agility +
|
||||
0.5 * Player.dexterity +
|
||||
intWgt * Player.intelligence) / (2 * lvl));
|
||||
}
|
||||
|
||||
// Pick locked door
|
||||
// Success: 1%, Failure: 3%
|
||||
function attemptInfiltrationPickLockedDoor(inst) {
|
||||
var chance = getInfiltrationPickLockedDoorChance(inst);
|
||||
inst.gainDexterityExp(inst.securityLevel / 25) * Player.dexterity_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
inst.securityLevel *= 1.01;
|
||||
return [true, 1.01];
|
||||
} else {
|
||||
inst.securityLevel *= 1.03;
|
||||
return [false, 1.03];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationPickLockedDoorChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.dexterity +
|
||||
intWgt * Player.intelligence) / lvl);
|
||||
}
|
||||
|
||||
// Bribe
|
||||
// Success: 0%, Failure: 15%,
|
||||
function attemptInfiltrationBribe(inst) {
|
||||
var chance = getInfiltrationBribeChance(inst);
|
||||
inst.gainCharismaExp(inst.securityLevel / 8) * Player.charisma_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
return [true, 1];
|
||||
} else {
|
||||
inst.securityLevel *= 1.15;
|
||||
return [false, 1.15];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationBribeChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(Player.charisma) / lvl);
|
||||
}
|
||||
|
||||
// Escape
|
||||
// Failure: 5%
|
||||
function attemptInfiltrationEscape(inst) {
|
||||
var chance = getInfiltrationEscapeChance(inst);
|
||||
inst.gainAgilityExp(inst.securityLevel / 30) * Player.agility_exp_mult;
|
||||
inst.gainDexterityExp(inst.securityLevel / 30) * Player.dexterity_exp_mult;
|
||||
if (Math.random() <= chance) {
|
||||
return [true, 1];
|
||||
} else {
|
||||
inst.securityLevel *= 1.05;
|
||||
return [false, 1.05];
|
||||
}
|
||||
}
|
||||
|
||||
function getInfiltrationEscapeChance(inst) {
|
||||
var lvl = inst.securityLevel;
|
||||
return Math.min(0.95,
|
||||
(2 * Player.agility +
|
||||
Player.dexterity +
|
||||
intWgt * Player.intelligence) / lvl);
|
||||
}
|
||||
|
||||
export {beginInfiltration};
|
48
src/Infiltration/Helper.tsx
Normal file
48
src/Infiltration/Helper.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import { Page, routing } from ".././ui/navigationTracking";
|
||||
import { Root } from "./ui/Root";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../IEngine";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
let container: HTMLElement = document.createElement('div');
|
||||
|
||||
(function() {
|
||||
function setContainer(): void {
|
||||
const c = document.getElementById("infiltration-container");
|
||||
if(c === null) throw new Error("huh?");
|
||||
container = c;
|
||||
document.removeEventListener("DOMContentLoaded", setContainer);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", setContainer);
|
||||
})();
|
||||
|
||||
function calcDifficulty(player: IPlayer, startingDifficulty: number): number {
|
||||
const totalStats = player.strength+
|
||||
player.defense+
|
||||
player.dexterity+
|
||||
player.agility+
|
||||
player.charisma;
|
||||
const difficulty = startingDifficulty-
|
||||
Math.pow(totalStats, 0.9)/250-
|
||||
(player.intelligence/1600);
|
||||
if(difficulty < 0) return 0;
|
||||
if(difficulty > 3) return 3;
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
export function displayInfiltrationContent(engine: IEngine, player: IPlayer, location: string, startingDifficulty: number, maxLevel: number): void {
|
||||
if (!routing.isOn(Page.Infiltration)) return;
|
||||
|
||||
const difficulty = calcDifficulty(player, startingDifficulty);
|
||||
|
||||
ReactDOM.render(<Root
|
||||
Engine={engine}
|
||||
Player={player}
|
||||
Location={location}
|
||||
StartingDifficulty={startingDifficulty}
|
||||
Difficulty={difficulty}
|
||||
MaxLevel={maxLevel}
|
||||
/>, container);
|
||||
}
|
107
src/Infiltration/ui/BackwardGame.tsx
Normal file
107
src/Infiltration/ui/BackwardGame.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 16000, min: 3, max: 4},
|
||||
Normal: {timer: 12500, min: 2, max: 3},
|
||||
Hard: {timer: 15000, min: 3, max: 4},
|
||||
Impossible: {timer: 8000, min: 4, max: 4},
|
||||
}
|
||||
|
||||
export function BackwardGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, min: 0, max: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [answer] = useState(makeAnswer(difficulty));
|
||||
const [guess, setGuess] = useState("");
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(event.keyCode === 16) return;
|
||||
const nextGuess = guess + event.key.toUpperCase();
|
||||
if(!answer.startsWith(nextGuess)) props.onFailure();
|
||||
else if (answer === nextGuess) props.onSuccess();
|
||||
else setGuess(nextGuess);
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Type it backward</h1>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<p style={{transform: 'scaleX(-1)'}}>{answer}</p>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<p>{guess}<BlinkingCursor /></p>
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
function makeAnswer(difficulty: Difficulty): string {
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
let answer = "";
|
||||
for(let i = 0; i < length; i++) {
|
||||
if(i > 0) answer += " "
|
||||
answer += words[Math.floor(Math.random() * words.length)];
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
const words = ["ALGORITHM", "ANALOG", "APP", "APPLICATION", "ARRAY", "BACKUP",
|
||||
"BANDWIDTH", "BINARY", "BIT", "BITE", "BITMAP", "BLOG", "BLOGGER",
|
||||
"BOOKMARK", "BOOT", "BROADBAND", "BROWSER", "BUFFER", "BUG", "BUS", "BYTE",
|
||||
"CACHE", "CAPS LOCK", "CAPTCHA", "CD", "CD-ROM", "CLIENT",
|
||||
"CLIPBOARD", "CLOUD", "COMPUTING", "COMMAND", "COMPILE", "COMPRESS",
|
||||
"COMPUTER", "CONFIGURE", "COOKIE", "COPY", "CPU",
|
||||
"CYBERCRIME", "CYBERSPACE", "DASHBOARD", "DATA", "MINING", "DATABASE",
|
||||
"DEBUG", "DECOMPRESS", "DELETE", "DESKTOP", "DEVELOPMENT", "DIGITAL",
|
||||
"DISK", "DNS", "DOCUMENT", "DOMAIN", "DOMAIN NAME", "DOT", "DOT MATRIX",
|
||||
"DOWNLOAD", "DRAG", "DVD", "DYNAMIC", "EMAIL", "EMOTICON", "ENCRYPT",
|
||||
"ENCRYPTION", "ENTER", "EXABYTE", "FAQ", "FILE", "FINDER", "FIREWALL",
|
||||
"FIRMWARE", "FLAMING", "FLASH", "FLASH DRIVE", "FLOPPY DISK", "FLOWCHART",
|
||||
"FOLDER", "FONT", "FORMAT", "FRAME", "FREEWARE", "GIGABYTE", "GRAPHICS",
|
||||
"HACK", "HACKER", "HARDWARE", "HOME PAGE", "HOST", "HTML", "HYPERLINK",
|
||||
"HYPERTEXT", "ICON", "INBOX", "INTEGER", "INTERFACE", "INTERNET",
|
||||
"IP ADDRESS", "ITERATION", "JAVA", "JOYSTICK", "JUNKMAIL", "KERNEL",
|
||||
"KEY", "KEYBOARD", "KEYWORD", "LAPTOP", "LASER PRINTER", "LINK", "LINUX",
|
||||
"LOG OUT", "LOGIC", "LOGIN", "LURKING", "MACINTOSH", "MACRO", "MAINFRAME",
|
||||
"MALWARE", "MEDIA", "MEMORY", "MIRROR", "MODEM", "MONITOR", "MOTHERBOARD",
|
||||
"MOUSE", "MULTIMEDIA", "NET", "NETWORK", "NODE", "NOTEBOOK", "COMPUTER",
|
||||
"OFFLINE", "ONLINE", "OPENSOURCE", "OPERATING", "SYSTEM", "OPTION", "OUTPUT",
|
||||
"PAGE", "PASSWORD", "PASTE", "PATH", "PHISHING", "PIRACY", "PIRATE",
|
||||
"PLATFORM", "PLUGIN", "PODCAST", "POPUP", "PORTAL", "PRINT", "PRINTER",
|
||||
"PRIVACY", "PROCESS", "PROGRAM", "PROGRAMMER", "PROTOCOL", "QUEUE",
|
||||
"QWERTY", "RAM", "REALTIME", "REBOOT", "RESOLUTION", "RESTORE", "ROM",
|
||||
"ROOT", "ROUTER", "RUNTIME", "SAVE", "SCAN", "SCANNER", "SCREEN",
|
||||
"SCREENSHOT", "SCRIPT", "SCROLL", "SCROLL", "SEARCH", "ENGINE",
|
||||
"SECURITY", "SERVER", "SHAREWARE", "SHELL", "SHIFT", "SHIFT KEY",
|
||||
"SNAPSHOT", "SOCIAL NETWORKING", "SOFTWARE", "SPAM", "SPAMMER",
|
||||
"SPREADSHEET", "SPYWARE", "STATUS", "STORAGE", "SUPERCOMPUTER", "SURF",
|
||||
"SYNTAX", "TABLE", "TAG", "TERMINAL", "TEMPLATE", "TERABYTE", "TEXT EDITOR",
|
||||
"THREAD", "TOOLBAR", "TRASH", "TROJAN HORSE", "TYPEFACE", "UNDO", "UNIX",
|
||||
"UPLOAD", "URL", "USER", "USER INTERFACE", "USERNAME", "UTILITY", "VERSION",
|
||||
"VIRTUAL", "VIRTUAL MEMORY", "VIRUS", "WEB", "WEBMASTER",
|
||||
"WEBSITE", "WIDGET", "WIKI", "WINDOW", "WINDOWS", "WIRELESS",
|
||||
"PROCESSOR", "WORKSTATION", "WEB", "WORM", "WWW", "XML",
|
||||
"ZIP"];
|
5
src/Infiltration/ui/BlinkingCursor.tsx
Normal file
5
src/Infiltration/ui/BlinkingCursor.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export function BlinkingCursor(): React.ReactElement {
|
||||
return (<span style={{fontSize: "1em"}} className={"blinking-cursor"}>|</span>);
|
||||
}
|
84
src/Infiltration/ui/BracketGame.tsx
Normal file
84
src/Infiltration/ui/BracketGame.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { BlinkingCursor } from "./BlinkingCursor";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer:8000, min: 2, max: 3},
|
||||
Normal: {timer:6000, min: 4, max: 5},
|
||||
Hard: {timer:4000, min: 4, max: 6},
|
||||
Impossible: {timer: 2500, min: 7, max: 7},
|
||||
}
|
||||
|
||||
function generateLeftSide(difficulty: Difficulty): string {
|
||||
let str = "";
|
||||
const length = random(difficulty.min, difficulty.max);
|
||||
for(let i = 0; i < length; i++) {
|
||||
str += ["[", '<', '(', '{'][Math.floor(Math.random()*4)];
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function getChar(event: React.KeyboardEvent<HTMLElement>): string {
|
||||
if(event.keyCode == 48 && event.shiftKey) return ")";
|
||||
if(event.keyCode == 221 && !event.shiftKey) return "]";
|
||||
if(event.keyCode == 221 && event.shiftKey) return "}";
|
||||
if(event.keyCode == 190 && event.shiftKey) return ">";
|
||||
return "";
|
||||
}
|
||||
|
||||
function match(left: string, right: string): boolean {
|
||||
return (left === '[' && right === ']') ||
|
||||
(left === '<' && right === '>') ||
|
||||
(left === '(' && right === ')') ||
|
||||
(left === '{' && right === '}');
|
||||
}
|
||||
|
||||
export function BracketGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer:0, min: 0, max: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [right, setRight] = useState("");
|
||||
const [left] = useState(generateLeftSide(difficulty));
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const char = getChar(event);
|
||||
if(!char) return;
|
||||
if(!match(left[left.length-right.length-1], char)) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
if(left.length === right.length+1) {
|
||||
props.onSuccess();
|
||||
return;
|
||||
}
|
||||
setRight(right+char);
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Close the brackets</h1>
|
||||
<p style={{fontSize: '5em'}}>{`${left}${right}`}<BlinkingCursor /></p>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
96
src/Infiltration/ui/BribeGame.tsx
Normal file
96
src/Infiltration/ui/BribeGame.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
size: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 12000, size: 6},
|
||||
Normal: {timer: 9000, size: 8},
|
||||
Hard: {timer: 5000, size: 9},
|
||||
Impossible: {timer: 2500, size: 12},
|
||||
}
|
||||
|
||||
export function BribeGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, size: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [choices] = useState(makeChoices(difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const k = event.keyCode;
|
||||
if(k === 32) {
|
||||
if(positive.includes(choices[index])) props.onSuccess();
|
||||
else props.onFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
let newIndex = index;
|
||||
if([38, 87, 68, 39].includes(k)) newIndex++;
|
||||
if([65, 37, 83, 40].includes(k)) newIndex--;
|
||||
while(newIndex < 0) newIndex += choices.length;
|
||||
while(newIndex > choices.length-1) newIndex -= choices.length;
|
||||
setIndex(newIndex);
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1>Say something nice about the guard.</h1>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<h2 style={{fontSize: "2em"}}>↑</h2>
|
||||
<h2 style={{fontSize: "2em"}}>{choices[index]}</h2>
|
||||
<h2 style={{fontSize: "2em"}}>↓</h2>
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
function shuffleArray(array: string[]): void {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
function makeChoices(difficulty: Difficulty): string[] {
|
||||
const choices = [];
|
||||
choices.push(positive[Math.floor(Math.random()*positive.length)]);
|
||||
for(let i = 0; i < difficulty.size; i++) {
|
||||
const option = negative[Math.floor(Math.random()*negative.length)];
|
||||
if(choices.includes(option)) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
choices.push(option);
|
||||
}
|
||||
shuffleArray(choices);
|
||||
return choices;
|
||||
}
|
||||
|
||||
const positive = ["affectionate","agreeable","bright","charming","creative",
|
||||
"determined","energetic","friendly","funny","generous","polite","likable",
|
||||
"diplomatic","helpful","giving","kind","hardworking","patient","dynamic",
|
||||
"loyal"];
|
||||
|
||||
const negative = ["aggressive","aloof","arrogant","big-headed","boastful",
|
||||
"boring","bossy","careless","clingy","couch potato","cruel","cynical",
|
||||
"grumpy","hot air","know it all","obnoxious","pain in the neck","picky",
|
||||
"tactless","thoughtless"];
|
65
src/Infiltration/ui/CheatCodeGame.tsx
Normal file
65
src/Infiltration/ui/CheatCodeGame.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { random, getArrow } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 13000, min: 6, max: 8},
|
||||
Normal: {timer: 7000, min: 7, max: 8},
|
||||
Hard: {timer: 5000, min: 8, max: 9},
|
||||
Impossible: {timer: 3000, min: 9, max: 9},
|
||||
}
|
||||
|
||||
export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, min: 0, max: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [code] = useState(generateCode(difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(code[index] !== getArrow(event)) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setIndex(index+1);
|
||||
if(index+1 >= code.length) props.onSuccess();
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Enter the Code!</h1>
|
||||
<p style={{fontSize: '5em'}}>{code[index]}</p>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
function generateCode(difficulty: Difficulty): string {
|
||||
const arrows = ['←', '→', '↑', '↓'];
|
||||
let code = '';
|
||||
for(let i = 0; i < random(difficulty.min, difficulty.max); i++) {
|
||||
let arrow = arrows[Math.floor(4*Math.random())];
|
||||
while(arrow === code[code.length-1]) arrow = arrows[Math.floor(4*Math.random())];
|
||||
code += arrow;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
26
src/Infiltration/ui/Countdown.tsx
Normal file
26
src/Infiltration/ui/Countdown.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
|
||||
interface IProps {
|
||||
onFinish: () => void;
|
||||
}
|
||||
|
||||
export function Countdown(props: IProps): React.ReactElement {
|
||||
const [x, setX] = useState(3);
|
||||
useEffect(() => {
|
||||
if(x === 0) {
|
||||
props.onFinish();
|
||||
return;
|
||||
}
|
||||
setTimeout(()=>setX(x-1), 200);
|
||||
});
|
||||
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<h1>Get Ready!</h1>
|
||||
<h1>{x}</h1>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
116
src/Infiltration/ui/Cyberpunk2077Game.tsx
Normal file
116
src/Infiltration/ui/Cyberpunk2077Game.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { getArrow } from "../utils";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
width: number;
|
||||
height: number;
|
||||
symbols: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 12500, width: 3, height: 3, symbols: 6},
|
||||
Normal: {timer: 15000, width: 4, height: 4, symbols: 7},
|
||||
Hard: {timer: 12500, width: 5, height: 5, symbols: 8},
|
||||
Impossible: {timer: 10000, width: 6, height: 6, symbols: 9},
|
||||
}
|
||||
|
||||
export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, width: 0, height: 0, symbols: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [grid] = useState(generatePuzzle(difficulty));
|
||||
const [answer] = useState(generateAnswer(grid, difficulty));
|
||||
const [index, setIndex] = useState(0);
|
||||
const [pos, setPos] = useState([0, 0]);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const move = [0, 0];
|
||||
const arrow = getArrow(event);
|
||||
switch(arrow) {
|
||||
case "↑":
|
||||
move[1]--;
|
||||
break;
|
||||
case "←":
|
||||
move[0]--;
|
||||
break;
|
||||
case "↓":
|
||||
move[1]++;
|
||||
break;
|
||||
case "→":
|
||||
move[0]++;
|
||||
break;
|
||||
}
|
||||
const next = [pos[0]+move[0], pos[1]+move[1]];
|
||||
next[0] = (next[0]+grid[0].length)%grid[0].length;
|
||||
next[1] = (next[1]+grid.length)%grid.length;
|
||||
setPos(next);
|
||||
|
||||
if(event.keyCode == 32) {
|
||||
const selected = grid[pos[1]][pos[0]];
|
||||
const expected = answer[index];
|
||||
if(selected !== expected) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setIndex(index+1);
|
||||
if(answer.length === index+1) props.onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
const fontSize = "2em";
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Match the symbols!</h1>
|
||||
<h2 style={{fontSize: fontSize}}>Targets: {answer.map((a, i) => {
|
||||
if(i == index)
|
||||
return <span key={`${i}`} style={{fontSize: "1em", color: 'blue'}}>{a} </span>
|
||||
return <span key={`${i}`} style={{fontSize: "1em"}}>{a} </span>
|
||||
})}</h2>
|
||||
<br />
|
||||
{grid.map((line, y) => <div key={y}><pre>{line.map((cell, x) => {
|
||||
if(x == pos[0] && y == pos[1])
|
||||
return <span key={`${x}${y}`} style={{fontSize: fontSize, color: 'blue'}}>{cell} </span>
|
||||
return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell} </span>
|
||||
})}</pre><br /></div>)}
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
function generateAnswer(grid: string[][], difficulty: Difficulty): string[] {
|
||||
const answer = [];
|
||||
for(let i = 0; i < Math.round(difficulty.symbols); i++) {
|
||||
answer.push(grid[Math.floor(Math.random()*grid.length)][Math.floor(Math.random()*grid[0].length)]);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
function randChar(): string {
|
||||
return "ABCDEF0123456789"[Math.floor(Math.random()*16)];
|
||||
}
|
||||
|
||||
function generatePuzzle(difficulty: Difficulty): string[][] {
|
||||
const puzzle = [];
|
||||
for(let i = 0; i < Math.round(difficulty.height); i++) {
|
||||
const line = [];
|
||||
for(let j = 0; j < Math.round(difficulty.width); j++) {
|
||||
line.push(randChar()+randChar());
|
||||
}
|
||||
puzzle.push(line);
|
||||
}
|
||||
return puzzle;
|
||||
}
|
32
src/Infiltration/ui/Difficulty.ts
Normal file
32
src/Infiltration/ui/Difficulty.ts
Normal file
@ -0,0 +1,32 @@
|
||||
interface DifficultySetting {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
interface DifficultySettings {
|
||||
Trivial: DifficultySetting;
|
||||
Normal: DifficultySetting;
|
||||
Hard: DifficultySetting;
|
||||
Impossible: DifficultySetting;
|
||||
}
|
||||
|
||||
// I could use `any` to simply some of this but I also want to take advantage
|
||||
// of the type safety that typescript provides. I'm just not sure how in this
|
||||
// case.
|
||||
export function interpolate(settings: DifficultySettings, n: number, out: DifficultySetting): DifficultySetting {
|
||||
// interpolates between 2 difficulties.
|
||||
function lerpD(a: DifficultySetting, b: DifficultySetting, t: number): DifficultySetting {
|
||||
// interpolates between 2 numbers.
|
||||
function lerp(x: number, y: number, t: number): number {
|
||||
return (1 - t) * x + t * y;
|
||||
}
|
||||
for(const key of Object.keys(a)) {
|
||||
out[key] = lerp(a[key], b[key], t);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
if(n < 0) return lerpD(settings.Trivial, settings.Trivial, 0);
|
||||
if(n >= 0 && n < 1) return lerpD(settings.Trivial, settings.Normal, n);
|
||||
if(n >= 1 && n < 2) return lerpD(settings.Normal, settings.Hard, n-1);
|
||||
if(n >= 2 && n < 3) return lerpD(settings.Hard, settings.Impossible, n-2);
|
||||
return lerpD(settings.Impossible, settings.Impossible, 0);
|
||||
}
|
132
src/Infiltration/ui/Game.tsx
Normal file
132
src/Infiltration/ui/Game.tsx
Normal file
@ -0,0 +1,132 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { Countdown } from "./Countdown";
|
||||
import { BracketGame } from "./BracketGame";
|
||||
import { SlashGame } from "./SlashGame";
|
||||
import { BackwardGame } from "./BackwardGame";
|
||||
import { BribeGame } from "./BribeGame";
|
||||
import { CheatCodeGame } from "./CheatCodeGame";
|
||||
import { Cyberpunk2077Game } from "./Cyberpunk2077Game";
|
||||
import { MinesweeperGame } from "./MinesweeperGame";
|
||||
import { WireCuttingGame } from "./WireCuttingGame";
|
||||
import { Victory } from "./Victory";
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
}
|
||||
|
||||
enum Stage {
|
||||
Countdown = 0,
|
||||
Minigame,
|
||||
Result,
|
||||
Sell,
|
||||
}
|
||||
|
||||
const minigames = [
|
||||
SlashGame,
|
||||
BracketGame,
|
||||
BackwardGame,
|
||||
BribeGame,
|
||||
CheatCodeGame,
|
||||
Cyberpunk2077Game,
|
||||
MinesweeperGame,
|
||||
WireCuttingGame,
|
||||
]
|
||||
|
||||
export function Game(props: IProps): React.ReactElement {
|
||||
const [level, setLevel] = useState(1);
|
||||
const [stage, setStage] = useState(Stage.Countdown);
|
||||
const [results, setResults] = useState('');
|
||||
const [gameIds, setGameIds] = useState({
|
||||
lastGames: [-1, -1],
|
||||
id: Math.floor(Math.random()*minigames.length),
|
||||
});
|
||||
|
||||
function nextGameId(): number {
|
||||
let id = gameIds.lastGames[0];
|
||||
const ids = [gameIds.lastGames[0], gameIds.lastGames[1], gameIds.id];
|
||||
while(ids.includes(id)) {
|
||||
id = Math.floor(Math.random()*minigames.length);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
function setupNextGame(): void {
|
||||
setGameIds({
|
||||
lastGames: [gameIds.lastGames[1], gameIds.id],
|
||||
id: nextGameId(),
|
||||
})
|
||||
}
|
||||
|
||||
function success(): void {
|
||||
pushResult(true);
|
||||
if(level === props.MaxLevel) {
|
||||
setStage(Stage.Sell);
|
||||
} else {
|
||||
setStage(Stage.Countdown);
|
||||
setLevel(level+1);
|
||||
}
|
||||
setupNextGame();
|
||||
}
|
||||
|
||||
function pushResult(win: boolean): void {
|
||||
setResults(old => {
|
||||
let next = old;
|
||||
next += win ? "✓" : "✗";
|
||||
if(next.length > 15) next = next.slice(1);
|
||||
return next;
|
||||
})
|
||||
}
|
||||
|
||||
function failure(): void {
|
||||
setStage(Stage.Countdown);
|
||||
pushResult(false);
|
||||
if(props.Player.takeDamage(props.StartingDifficulty*3)) {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if(menu === null) throw new Error("mainmenu-container not found");
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
setupNextGame();
|
||||
}
|
||||
|
||||
let stageComponent: React.ReactNode;
|
||||
switch(stage) {
|
||||
case Stage.Countdown:
|
||||
stageComponent = (<Countdown onFinish={() =>setStage(Stage.Minigame)} />);
|
||||
break;
|
||||
case Stage.Minigame: {
|
||||
const MiniGame = minigames[gameIds.id];
|
||||
stageComponent = (<MiniGame onSuccess={success} onFailure={failure} difficulty={props.Difficulty+level/50} />);
|
||||
break;
|
||||
}
|
||||
case Stage.Sell:
|
||||
stageComponent = (<Victory Player={props.Player} Engine={props.Engine} StartingDifficulty={props.StartingDifficulty} Difficulty={props.Difficulty} MaxLevel={props.MaxLevel} />);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
function Progress(): React.ReactElement {
|
||||
return <h4><span style={{color: "gray"}}>{results.slice(0, results.length-1)}</span>{results[results.length-1]}</h4>
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={3}>
|
||||
<h3>Level: {level} / {props.MaxLevel}</h3>
|
||||
<Progress />
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
{stageComponent}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
41
src/Infiltration/ui/GameTimer.tsx
Normal file
41
src/Infiltration/ui/GameTimer.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import LinearProgress from '@material-ui/core/LinearProgress';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { withStyles } from "@material-ui/core/styles";
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
|
||||
const TimerProgress = withStyles(() => ({
|
||||
bar: {
|
||||
transition: "none",
|
||||
backgroundColor: "#adff2f",
|
||||
},
|
||||
}))(LinearProgress);
|
||||
|
||||
interface IProps {
|
||||
millis: number;
|
||||
onExpire: () => void;
|
||||
}
|
||||
|
||||
export function GameTimer(props: IProps): React.ReactElement {
|
||||
const [v, setV] = useState(100);
|
||||
|
||||
const tick = 200;
|
||||
useEffect(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
setV(old => {
|
||||
if(old <= 0) props.onExpire();
|
||||
return old - tick / props.millis * 100;
|
||||
});
|
||||
}, tick);
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// https://stackoverflow.com/questions/55593367/disable-material-uis-linearprogress-animation
|
||||
// TODO(hydroflame): there's like a bug where it triggers the end before the
|
||||
// bar physically reaches the end
|
||||
return (<Grid item xs={12}>
|
||||
<TimerProgress variant="determinate" value={v} />
|
||||
</Grid>);
|
||||
}
|
||||
|
5
src/Infiltration/ui/IMinigameProps.tsx
Normal file
5
src/Infiltration/ui/IMinigameProps.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
export interface IMinigameProps {
|
||||
onSuccess: () => void;
|
||||
onFailure: () => void;
|
||||
difficulty: number;
|
||||
}
|
58
src/Infiltration/ui/Intro.tsx
Normal file
58
src/Infiltration/ui/Intro.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import React from 'react';
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
Location: string;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
start: () => void;
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
function diffStr(d: number): string {
|
||||
if(d<=0.5) return "trivial";
|
||||
if(d<=1.5) return "normal";
|
||||
if(d<=2.5) return "hard";
|
||||
return "impossible";
|
||||
}
|
||||
|
||||
export function Intro(props: IProps): React.ReactElement {
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<h1>Infiltrating {props.Location}</h1>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<h2>Difficulty: {diffStr(props.Difficulty)}, Maximum level: {props.MaxLevel}</h2>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<p>Infiltration is a series of short minigames that get
|
||||
progressively harder. You take damage for failing them. Reaching
|
||||
the maximum level rewards you with intel you can trade for money
|
||||
or reputation.</p>
|
||||
<br />
|
||||
<p>The minigames you play are randomly selected. It might take you
|
||||
few tries to get used to them.</p>
|
||||
<br />
|
||||
<p>No game require use of the mouse.</p>
|
||||
<br />
|
||||
<p>Spacebar is the default action/confirm button.</p>
|
||||
<br />
|
||||
<p>Everything that uses arrow can also use WASD</p>
|
||||
<br />
|
||||
<p>Sometimes the rest of the keyboard is used.</p>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={props.start} text={"Start"} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={props.cancel} text={"Cancel"} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
13
src/Infiltration/ui/KeyHandler.tsx
Normal file
13
src/Infiltration/ui/KeyHandler.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
interface IProps {
|
||||
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
export function KeyHandler(props: IProps): React.ReactElement {
|
||||
let elem: any;
|
||||
useEffect(() => elem.focus());
|
||||
|
||||
// invisible autofocused element that eats all the keypress for the minigames.
|
||||
return (<div tabIndex={1} ref={c => elem = c} onKeyDown={props.onKeyDown} />)
|
||||
}
|
129
src/Infiltration/ui/MinesweeperGame.tsx
Normal file
129
src/Infiltration/ui/MinesweeperGame.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
import { getArrow } from "../utils";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
width: number;
|
||||
height: number;
|
||||
mines: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 15000, width: 3, height: 3, mines: 4},
|
||||
Normal: {timer: 15000, width: 4, height: 4, mines: 7},
|
||||
Hard: {timer: 15000, width: 5, height: 5, mines: 11},
|
||||
Impossible: {timer: 15000, width: 6, height: 6, mines: 15},
|
||||
}
|
||||
|
||||
export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, width: 0, height: 0, mines: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [minefield] = useState(generateMinefield(difficulty));
|
||||
const [answer, setAnswer] = useState(generateEmptyField(difficulty));
|
||||
const [pos, setPos] = useState([0, 0]);
|
||||
const [memoryPhase, setMemoryPhase] = useState(true);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(memoryPhase) return;
|
||||
const move = [0, 0];
|
||||
const arrow = getArrow(event);
|
||||
switch(arrow) {
|
||||
case "↑":
|
||||
move[1]--;
|
||||
break;
|
||||
case "←":
|
||||
move[0]--;
|
||||
break;
|
||||
case "↓":
|
||||
move[1]++;
|
||||
break;
|
||||
case "→":
|
||||
move[0]++;
|
||||
break;
|
||||
}
|
||||
const next = [pos[0]+move[0], pos[1]+move[1]];
|
||||
next[0] = (next[0]+minefield[0].length)%minefield[0].length;
|
||||
next[1] = (next[1]+minefield.length)%minefield.length;
|
||||
setPos(next);
|
||||
|
||||
if(event.keyCode == 32) {
|
||||
if(!minefield[pos[1]][pos[0]]) {
|
||||
props.onFailure();
|
||||
return;
|
||||
}
|
||||
setAnswer(old => {
|
||||
old[pos[1]][pos[0]] = true;
|
||||
if(fieldEquals(minefield, old)) props.onSuccess();
|
||||
return old;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => setMemoryPhase(false), 2000);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>{memoryPhase?"Remember all the mines!": "Mark all the mines!"}</h1>
|
||||
{minefield.map((line, y) => <div key={y}><pre>{line.map((cell, x) => {
|
||||
if(memoryPhase) {
|
||||
if(minefield[y][x])
|
||||
return <span key={x}>[?] </span>
|
||||
return <span key={x}>[ ] </span>
|
||||
} else {
|
||||
if(x == pos[0] && y == pos[1])
|
||||
return <span key={x}>[X] </span>
|
||||
if(answer[y][x])
|
||||
return <span key={x}>[.] </span>
|
||||
return <span key={x}>[ ] </span>
|
||||
}
|
||||
})}</pre><br /></div>)}
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
function fieldEquals(a: boolean[][], b: boolean[][]): boolean {
|
||||
function count(field: boolean[][]): number {
|
||||
return field.flat().reduce((a, b) => a + (b?1:0), 0);
|
||||
}
|
||||
return count(a) === count(b);
|
||||
}
|
||||
|
||||
function generateEmptyField(difficulty: Difficulty): boolean[][] {
|
||||
const field = [];
|
||||
for(let i = 0; i < difficulty.height; i++) {
|
||||
field.push((new Array(Math.round(difficulty.width))).fill(false));
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
function generateMinefield(difficulty: Difficulty): boolean[][] {
|
||||
const field = generateEmptyField(difficulty);
|
||||
for(let i = 0; i < difficulty.mines; i++) {
|
||||
const x = Math.floor(Math.random()*field.length);
|
||||
const y = Math.floor(Math.random()*field[0].length);
|
||||
if (field[x][y]) {
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
field[x][y] = true;
|
||||
}
|
||||
return field;
|
||||
}
|
45
src/Infiltration/ui/Root.tsx
Normal file
45
src/Infiltration/ui/Root.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import React, { useState } from 'react';
|
||||
import { Intro } from "./Intro";
|
||||
import { Game } from "./Game";
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
Location: string;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
}
|
||||
|
||||
export function Root(props: IProps): React.ReactElement {
|
||||
const [start, setStart] = useState(false);
|
||||
|
||||
function cancel(): void {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if(menu === null) throw new Error("mainmenu-container not found");
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
|
||||
if(!start) {
|
||||
return (<Intro
|
||||
Player={props.Player}
|
||||
Engine={props.Engine}
|
||||
Location={props.Location}
|
||||
Difficulty={props.Difficulty}
|
||||
MaxLevel={props.MaxLevel}
|
||||
start={() => setStart(true)}
|
||||
cancel={cancel}
|
||||
/>)
|
||||
}
|
||||
|
||||
return (<Game
|
||||
Player={props.Player}
|
||||
Engine={props.Engine}
|
||||
StartingDifficulty={props.StartingDifficulty}
|
||||
Difficulty={props.Difficulty}
|
||||
MaxLevel={props.MaxLevel}
|
||||
/>);
|
||||
}
|
60
src/Infiltration/ui/SlashGame.tsx
Normal file
60
src/Infiltration/ui/SlashGame.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
window: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {window: 600},
|
||||
Normal: {window: 325},
|
||||
Hard: {window: 250},
|
||||
Impossible: {window: 150},
|
||||
}
|
||||
|
||||
export function SlashGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {window: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const [guarding, setGuarding] = useState(true);
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
if(event.keyCode !== 32) return;
|
||||
if(guarding) {
|
||||
props.onFailure();
|
||||
} else {
|
||||
props.onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let id2 = -1;
|
||||
const id = setTimeout(() => {
|
||||
setGuarding(false);
|
||||
id2 = setTimeout(()=>setGuarding(true), difficulty.window)
|
||||
}, Math.random()*3250+1500);
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
if(id2 !== -1) clearInterval(id2);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={5000} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Slash when his guard is down!</h1>
|
||||
<p style={{fontSize: '5em'}}>{guarding ? "!Guarding!" : "!ATTACKING!"}</p>
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
77
src/Infiltration/ui/Victory.tsx
Normal file
77
src/Infiltration/ui/Victory.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { Factions } from "../../Faction/Factions";
|
||||
import React, { useState } from 'react';
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
|
||||
|
||||
interface IProps {
|
||||
Player: IPlayer;
|
||||
Engine: IEngine;
|
||||
StartingDifficulty: number;
|
||||
Difficulty: number;
|
||||
MaxLevel: number;
|
||||
}
|
||||
|
||||
export function Victory(props: IProps): React.ReactElement {
|
||||
const [faction, setFaction] = useState('none');
|
||||
|
||||
function quitInfiltration(): void {
|
||||
const menu = document.getElementById("mainmenu-container");
|
||||
if(!menu) throw new Error('mainmenu-container somehow null');
|
||||
menu.style.visibility = "visible";
|
||||
props.Engine.loadLocationContent();
|
||||
}
|
||||
|
||||
const levelBonus = props.MaxLevel*Math.pow(1.01, props.MaxLevel);
|
||||
|
||||
const repGain = Math.pow(props.Difficulty+1, 1.1)*
|
||||
Math.pow(props.StartingDifficulty, 1.2)*
|
||||
30*levelBonus*BitNodeMultipliers.InfiltrationRep;
|
||||
|
||||
const moneyGain = Math.pow(props.Difficulty+1, 2)*
|
||||
Math.pow(props.StartingDifficulty, 3)*
|
||||
3e3*levelBonus*BitNodeMultipliers.InfiltrationMoney;
|
||||
|
||||
function sell(): void {
|
||||
props.Player.gainMoney(moneyGain);
|
||||
props.Player.recordMoneySource(moneyGain, 'infiltration');
|
||||
quitInfiltration();
|
||||
}
|
||||
|
||||
function trade(): void {
|
||||
if(faction === 'none') return;
|
||||
Factions[faction].playerReputation += repGain;
|
||||
quitInfiltration();
|
||||
}
|
||||
|
||||
function changeDropdown(event: React.ChangeEvent<HTMLSelectElement>): void {
|
||||
setFaction(event.target.value);
|
||||
}
|
||||
|
||||
return (<>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={10}>
|
||||
<h1>Infiltration successful!</h1>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<h2>You can trade the confidential information you found for money or reputation.</h2>
|
||||
<select className={"dropdown"} onChange={changeDropdown}>
|
||||
<option key={'none'} value={'none'}>{'none'}</option>
|
||||
{props.Player.factions.filter(f => Factions[f].getInfo().offersWork()).map(f => <option key={f} value={f}>{f}</option>)}
|
||||
</select>
|
||||
<StdButton onClick={trade} text={<>{"Trade for "}{Reputation(repGain)}{" reputation"}</>} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={sell} text={<>{"Sell for "}{Money(moneyGain)}</>} />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<StdButton onClick={quitInfiltration} text={"Quit"} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>)
|
||||
}
|
171
src/Infiltration/ui/WireCuttingGame.tsx
Normal file
171
src/Infiltration/ui/WireCuttingGame.tsx
Normal file
@ -0,0 +1,171 @@
|
||||
import React, { useState } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { IMinigameProps } from "./IMinigameProps";
|
||||
import { KeyHandler } from "./KeyHandler";
|
||||
import { GameTimer } from "./GameTimer";
|
||||
import { random } from "../utils";
|
||||
import { interpolate } from "./Difficulty";
|
||||
|
||||
interface Difficulty {
|
||||
[key: string]: number;
|
||||
timer: number;
|
||||
wiresmin: number;
|
||||
wiresmax: number;
|
||||
rules: number;
|
||||
}
|
||||
|
||||
const difficulties: {
|
||||
Trivial: Difficulty;
|
||||
Normal: Difficulty;
|
||||
Hard: Difficulty;
|
||||
Impossible: Difficulty;
|
||||
} = {
|
||||
Trivial: {timer: 9000, wiresmin: 4, wiresmax: 4, rules: 2},
|
||||
Normal: {timer: 7000, wiresmin: 6, wiresmax: 6, rules: 2},
|
||||
Hard: {timer: 5000, wiresmin: 8, wiresmax: 8, rules: 3},
|
||||
Impossible: {timer: 4000, wiresmin: 9, wiresmax: 9, rules: 4},
|
||||
}
|
||||
|
||||
|
||||
const types = [
|
||||
"|",
|
||||
".",
|
||||
"/",
|
||||
"-",
|
||||
"█",
|
||||
"#",
|
||||
]
|
||||
|
||||
const colors = [
|
||||
"red",
|
||||
"#FFC107",
|
||||
"blue",
|
||||
"white",
|
||||
]
|
||||
|
||||
const colorNames: any = {
|
||||
"red": "red",
|
||||
"#FFC107": "yellow",
|
||||
"blue": "blue",
|
||||
"white": "white",
|
||||
}
|
||||
|
||||
interface Wire {
|
||||
tpe: string;
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
interface Question {
|
||||
toString: () => string;
|
||||
shouldCut: (wire: Wire, index: number) => boolean;
|
||||
}
|
||||
|
||||
export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
|
||||
const difficulty: Difficulty = {timer: 0, wiresmin: 0, wiresmax: 0, rules: 0};
|
||||
interpolate(difficulties, props.difficulty, difficulty);
|
||||
const timer = difficulty.timer;
|
||||
const [wires] = useState(generateWires(difficulty));
|
||||
const [cutWires, setCutWires] = useState((new Array(wires.length)).fill(false));
|
||||
const [questions] = useState(generateQuestion(wires, difficulty));
|
||||
|
||||
function press(event: React.KeyboardEvent<HTMLElement>): void {
|
||||
event.preventDefault();
|
||||
const wireNum = parseInt(event.key);
|
||||
|
||||
if(wireNum < 1 || wireNum > wires.length || isNaN(wireNum)) return;
|
||||
setCutWires(old => {
|
||||
const next = [...old];
|
||||
next[wireNum-1] = true;
|
||||
if(!questions.some((q => q.shouldCut(wires[wireNum-1], wireNum-1)))) {
|
||||
props.onFailure();
|
||||
}
|
||||
|
||||
// check if we won
|
||||
const wiresToBeCut = [];
|
||||
for(let j = 0; j < wires.length; j++) {
|
||||
let shouldBeCut = false;
|
||||
for(let i = 0; i < questions.length; i++) {
|
||||
shouldBeCut = shouldBeCut || questions[i].shouldCut(wires[j], j)
|
||||
}
|
||||
wiresToBeCut.push(shouldBeCut);
|
||||
}
|
||||
if(wiresToBeCut.every((b, i) => b === next[i])) {
|
||||
props.onSuccess();
|
||||
}
|
||||
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
return (<Grid container spacing={3}>
|
||||
<GameTimer millis={timer} onExpire={props.onFailure} />
|
||||
<Grid item xs={12}>
|
||||
<h1 className={"noselect"}>Cut the wires with the following properties!</h1>
|
||||
{questions.map((question, i) => <h3 key={i}>{question.toString()}</h3>)}
|
||||
<pre>{(new Array(wires.length)).fill(0).map((_, i) => <span key={i}> {i+1} </span>)}</pre>
|
||||
{(new Array(8)).fill(0).map((_, i) => <div key={i}>
|
||||
<pre>
|
||||
{wires.map((wire, j) => {
|
||||
if((i === 3 || i === 4) && cutWires[j]) return <span key={j}> </span>;
|
||||
return <span key={j} style={{color: wire.colors[i%wire.colors.length]}}>|{wire.tpe}| </span>
|
||||
})}
|
||||
</pre>
|
||||
</div>)}
|
||||
<KeyHandler onKeyDown={press} />
|
||||
</Grid>
|
||||
</Grid>)
|
||||
}
|
||||
|
||||
function randomPositionQuestion(wires: Wire[]): Question {
|
||||
const index = Math.floor(Math.random() * wires.length);
|
||||
return {
|
||||
toString: (): string => {
|
||||
return `Cut wires number ${index+1}.`;
|
||||
},
|
||||
shouldCut: (wire: Wire, i: number): boolean => {
|
||||
return index === i;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function randomColorQuestion(wires: Wire[]): Question {
|
||||
const index = Math.floor(Math.random() * wires.length);
|
||||
const cutColor = wires[index].colors[0];
|
||||
return {
|
||||
toString: (): string => {
|
||||
return `Cut all wires colored ${colorNames[cutColor]}.`;
|
||||
},
|
||||
shouldCut: (wire: Wire): boolean => {
|
||||
return wire.colors.includes(cutColor);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function generateQuestion(wires: Wire[], difficulty: Difficulty): Question[] {
|
||||
const numQuestions = difficulty.rules;
|
||||
const questionGenerators = [
|
||||
randomPositionQuestion,
|
||||
randomColorQuestion,
|
||||
]
|
||||
const questions = [];
|
||||
for(let i = 0; i < numQuestions; i++) {
|
||||
questions.push(questionGenerators[i%2](wires));
|
||||
}
|
||||
return questions;
|
||||
}
|
||||
|
||||
function generateWires(difficulty: Difficulty): Wire[] {
|
||||
const wires = [];
|
||||
const numWires = random(difficulty.wiresmin, difficulty.wiresmax);
|
||||
for(let i = 0; i < numWires; i++) {
|
||||
const wireColors = [colors[Math.floor(Math.random()*colors.length)]];
|
||||
if(Math.random() < 0.15) {
|
||||
wireColors.push(colors[Math.floor(Math.random()*colors.length)]);
|
||||
}
|
||||
wires.push({
|
||||
tpe: types[Math.floor(Math.random()*types.length)],
|
||||
colors: wireColors,
|
||||
});
|
||||
}
|
||||
return wires;
|
||||
}
|
23
src/Infiltration/utils.ts
Normal file
23
src/Infiltration/utils.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
|
||||
export function random(min: number, max: number): number {
|
||||
return Math.random()*(max-min)+min;
|
||||
}
|
||||
|
||||
export function getArrow(event: React.KeyboardEvent<HTMLElement>): string {
|
||||
switch(event.keyCode) {
|
||||
case 38:
|
||||
case 87:
|
||||
return "↑";
|
||||
case 65:
|
||||
case 37:
|
||||
return "←";
|
||||
case 40:
|
||||
case 83:
|
||||
return "↓";
|
||||
case 39:
|
||||
case 68:
|
||||
return "→";
|
||||
}
|
||||
return '';
|
||||
}
|
@ -11,7 +11,6 @@ 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 = [
|
||||
"Start",
|
||||
@ -24,10 +23,10 @@ const orderedITutorialSteps = [
|
||||
"TerminalScan", // Using 'scan' Terminal command
|
||||
"TerminalScanAnalyze1", // Using 'scan-analyze' Terminal command
|
||||
"TerminalScanAnalyze2", // Using 'scan-analyze 3' Terminal command
|
||||
"TerminalConnect", // Connecting to foodnstuff
|
||||
"TerminalAnalyze", // Analyzing foodnstuff
|
||||
"TerminalNuke", // NUKE foodnstuff
|
||||
"TerminalManualHack", // Hack foodnstuff
|
||||
"TerminalConnect", // Connecting to n00dles
|
||||
"TerminalAnalyze", // Analyzing n00dles
|
||||
"TerminalNuke", // NUKE n00dles
|
||||
"TerminalManualHack", // Hack n00dles
|
||||
"TerminalHackingMechanics", // Explanation of hacking mechanics
|
||||
"TerminalCreateScript", // Create a script using 'nano'
|
||||
"TerminalTypeScript", // Script Editor page - Type script and then save & close
|
||||
@ -51,7 +50,7 @@ for (let i = 0; i < orderedITutorialSteps.length; ++i) {
|
||||
iTutorialSteps[orderedITutorialSteps[i]] = i;
|
||||
}
|
||||
|
||||
var ITutorial = {
|
||||
const ITutorial = {
|
||||
currStep: 0, // iTutorialSteps.Start
|
||||
isRunning: false,
|
||||
|
||||
@ -76,21 +75,21 @@ function iTutorialStart() {
|
||||
document.getElementById("interactive-tutorial-container").style.display = "block";
|
||||
|
||||
// Exit tutorial button
|
||||
var exitButton = clearEventListeners("interactive-tutorial-exit");
|
||||
const exitButton = clearEventListeners("interactive-tutorial-exit");
|
||||
exitButton.addEventListener("click", function() {
|
||||
iTutorialEnd();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Back button
|
||||
var backButton = clearEventListeners("interactive-tutorial-back");
|
||||
const backButton = clearEventListeners("interactive-tutorial-back");
|
||||
backButton.addEventListener("click", function() {
|
||||
iTutorialPrevStep();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Next button
|
||||
var nextButton = clearEventListeners("interactive-tutorial-next");
|
||||
const nextButton = clearEventListeners("interactive-tutorial-next");
|
||||
nextButton.addEventListener("click", function() {
|
||||
iTutorialNextStep();
|
||||
return false;
|
||||
@ -103,12 +102,12 @@ function iTutorialEvaluateStep() {
|
||||
if (!ITutorial.isRunning) {return;}
|
||||
|
||||
// Disable and clear main menu
|
||||
var terminalMainMenu = clearEventListeners("terminal-menu-link");
|
||||
var statsMainMenu = clearEventListeners("stats-menu-link");
|
||||
var activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
|
||||
var hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
|
||||
var cityMainMenu = clearEventListeners("city-menu-link");
|
||||
var tutorialMainMenu = clearEventListeners("tutorial-menu-link");
|
||||
const terminalMainMenu = clearEventListeners("terminal-menu-link");
|
||||
const statsMainMenu = clearEventListeners("stats-menu-link");
|
||||
const activeScriptsMainMenu = clearEventListeners("active-scripts-menu-link");
|
||||
const hacknetMainMenu = clearEventListeners("hacknet-nodes-menu-link");
|
||||
const cityMainMenu = clearEventListeners("city-menu-link");
|
||||
const tutorialMainMenu = clearEventListeners("tutorial-menu-link");
|
||||
terminalMainMenu.removeAttribute("class");
|
||||
statsMainMenu.removeAttribute("class");
|
||||
activeScriptsMainMenu.removeAttribute("class");
|
||||
@ -117,20 +116,20 @@ function iTutorialEvaluateStep() {
|
||||
tutorialMainMenu.removeAttribute("class");
|
||||
|
||||
// Interactive Tutorial Next button
|
||||
var nextBtn = document.getElementById("interactive-tutorial-next");
|
||||
const nextBtn = document.getElementById("interactive-tutorial-next");
|
||||
|
||||
switch(ITutorial.currStep) {
|
||||
case iTutorialSteps.Start:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("Welcome to Bitburner, a cyberpunk-themed incremental RPG! " +
|
||||
"The game takes place in a dark, dystopian future...The year is 2077...<br><br>" +
|
||||
"The game takes place in a dark, dystopian future... The year is 2077...<br><br>" +
|
||||
"This tutorial will show you the basics of the game. " +
|
||||
"You may skip the tutorial at any time.");
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.GoToCharacterPage:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("Let's start by heading to the Stats page. Click the 'Stats' tab on " +
|
||||
iTutorialSetText("Let's start by heading to the Stats page. Click the <code class='interactive-tutorial-tab flashing-button'>Stats</code> tab on " +
|
||||
"the main navigation menu (left-hand side of the screen)");
|
||||
nextBtn.style.display = "none";
|
||||
|
||||
@ -144,13 +143,13 @@ function iTutorialEvaluateStep() {
|
||||
break;
|
||||
case iTutorialSteps.CharacterPage:
|
||||
Engine.loadCharacterContent();
|
||||
iTutorialSetText("The Stats page shows a lot of important information about your progress, " +
|
||||
"such as your skills, money, and bonuses/multipliers. ")
|
||||
iTutorialSetText("The <code class='interactive-tutorial-tab'>Stats</code> page shows a lot of important information about your progress, " +
|
||||
"such as your skills, money, and bonuses. ")
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.CharacterGoToTerminalPage:
|
||||
Engine.loadCharacterContent();
|
||||
iTutorialSetText("Let's head to your computer's terminal by clicking the 'Terminal' tab on the " +
|
||||
iTutorialSetText("Let's head to your computer's terminal by clicking the <code class='interactive-tutorial-tab flashing-button'>Terminal</code> tab on the " +
|
||||
"main navigation menu.");
|
||||
nextBtn.style.display = "none";
|
||||
|
||||
@ -164,57 +163,56 @@ function iTutorialEvaluateStep() {
|
||||
break;
|
||||
case iTutorialSteps.TerminalIntro:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("The Terminal is used to interface with your home computer as well as " +
|
||||
iTutorialSetText("The <code class='interactive-tutorial-tab'>Terminal</code> is used to interface with your home computer as well as " +
|
||||
"all of the other machines around the world.");
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.TerminalHelp:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("Let's try it out. Start by entering the 'help' command into the Terminal " +
|
||||
iTutorialSetText("Let's try it out. Start by entering the <code class='interactive-tutorial-command'>help</code> command into the <code class='interactive-tutorial-tab'>Terminal</code> " +
|
||||
"(Don't forget to press Enter after typing the command)");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalLs:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("The 'help' command displays a list of all available Terminal commands, how to use them, " +
|
||||
"and a description of what they do. <br><br>Let's try another command. Enter the 'ls' command");
|
||||
iTutorialSetText("The <code class='interactive-tutorial-command'>help</code> command displays a list of all available <code class='interactive-tutorial-tab'>Terminal</code> commands, how to use them, " +
|
||||
"and a description of what they do. <br><br>Let's try another command. Enter the <code class='interactive-tutorial-command'>ls</code> command.");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalScan:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("'ls' is a basic command that shows all of the contents (programs/scripts) " +
|
||||
"on the computer. Right now, it shows that you have a program called 'NUKE.exe' on your computer. " +
|
||||
iTutorialSetText(" <code class='interactive-tutorial-command'>ls</code> is a basic command that shows files " +
|
||||
"on the computer. Right now, it shows that you have a program called <code class='interactive-tutorial-command'>NUKE.exe</code> on your computer. " +
|
||||
"We'll get to what this does later. <br><br>Using your home computer's terminal, you can connect " +
|
||||
"to other machines throughout the world. Let's do that now by first entering " +
|
||||
"the 'scan' command.");
|
||||
"the <code class='interactive-tutorial-command'>scan</code> command.");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalScanAnalyze1:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("The 'scan' command shows all available network connections. In other words, " +
|
||||
iTutorialSetText("The <code class='interactive-tutorial-command'>scan</code> command shows all available network connections. In other words, " +
|
||||
"it displays a list of all servers that can be connected to from your " +
|
||||
"current machine. A server is identified by either its IP or its hostname. <br><br> " +
|
||||
"current machine. A server is identified by its hostname. <br><br> " +
|
||||
"That's great and all, but there's so many servers. Which one should you go to? " +
|
||||
"The 'scan-analyze' command gives some more detailed information about servers on the " +
|
||||
"network. Try it now");
|
||||
"The <code class='interactive-tutorial-command'>scan-analyze</code> command gives some more detailed information about servers on the " +
|
||||
"network. Try it now!");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalScanAnalyze2:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("You just ran 'scan-analyze' with a depth of one. This command shows more detailed " +
|
||||
iTutorialSetText("You just ran <code class='interactive-tutorial-command'>scan-analyze</code> with a depth of one. This command shows more detailed " +
|
||||
"information about each server that you can connect to (servers that are a distance of " +
|
||||
"one node away). <br><br> It is also possible to run 'scan-analyze' with " +
|
||||
"a higher depth. Let's try a depth of two with the following command: 'scan-analyze 2'.")
|
||||
"one node away). <br><br> It is also possible to run <code class='interactive-tutorial-command'>scan-analyze</code> with " +
|
||||
"a higher depth. Let's try a depth of two with the following command: <code class='interactive-tutorial-command'>scan-analyze 2</code>.")
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalConnect:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("Now you can see information about all servers that are up to two nodes away, as well " +
|
||||
"as figure out how to navigate to those servers through the network. You can only connect to " +
|
||||
"a server that is one node away. To connect to a machine, use the 'connect [ip/hostname]' command. You can type in " +
|
||||
"the ip or the hostname, but dont use both.<br><br>" +
|
||||
"From the results of the 'scan-analyze' command, we can see that the 'foodnstuff' server is " +
|
||||
"only one node away. Let's connect so it now using: 'connect foodnstuff'");
|
||||
"a server that is one node away. To connect to a machine, use the <code class='interactive-tutorial-command'>connect [hostname]</code> command.<br><br>" +
|
||||
"From the results of the <code class='interactive-tutorial-command'>scan-analyze</code> command, we can see that the <code class='interactive-tutorial-command'>n00dles</code> server is " +
|
||||
"only one node away. Let's connect so it now using: <code class='interactive-tutorial-command'>connect n00dles</code>");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalAnalyze:
|
||||
@ -223,30 +221,30 @@ function iTutorialEvaluateStep() {
|
||||
"become digital and decentralized. People and corporations store their money " +
|
||||
"on servers and computers. Using your hacking abilities, you can hack servers " +
|
||||
"to steal money and gain experience. <br><br> " +
|
||||
"Before you try to hack a server, you should run diagnostics using the 'analyze' command");
|
||||
"Before you try to hack a server, you should run diagnostics using the <code class='interactive-tutorial-command'>analyze</code> command.");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalNuke:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("When the 'analyze' command finishes running it will show useful information " +
|
||||
"about hacking the server. <br><br> For this server, the required hacking skill is only 1, " +
|
||||
iTutorialSetText("When the <code class='interactive-tutorial-command'>analyze</code> command finishes running it will show useful information " +
|
||||
"about hacking the server. <br><br> For this server, the required hacking skill is only <span class='character-hack-cell'>1</span>, " +
|
||||
"which means you can hack it right now. However, in order to hack a server " +
|
||||
"you must first gain root access. The 'NUKE.exe' program that we saw earlier on your " +
|
||||
"you must first gain root access. The <code class='interactive-tutorial-command'>NUKE.exe</code> program that we saw earlier on your " +
|
||||
"home computer is a virus that will grant you root access to a machine if there are enough " +
|
||||
"open ports.<br><br> The 'analyze' results shows that there do not need to be any open ports " +
|
||||
"open ports.<br><br> The <code class='interactive-tutorial-command'>analyze</code> results shows that there do not need to be any open ports " +
|
||||
"on this machine for the NUKE virus to work, so go ahead and run the virus using the " +
|
||||
"'run NUKE.exe' command.");
|
||||
"<code class='interactive-tutorial-command'>run NUKE.exe</code> command.");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalManualHack:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("You now have root access! You can hack the server using the 'hack' command. " +
|
||||
iTutorialSetText("You now have root access! You can hack the server using the <code class='interactive-tutorial-command'>hack</code> command. " +
|
||||
"Try doing that now.");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalHackingMechanics:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("You are now attempting to hack the server. Note that performing a hack takes time and " +
|
||||
iTutorialSetText("You are now attempting to hack the server. Performing a hack takes time and " +
|
||||
"only has a certain percentage chance " +
|
||||
"of success. This time and success chance is determined by a variety of factors, including " +
|
||||
"your hacking skill and the server's security level.<br><br>" +
|
||||
@ -261,25 +259,22 @@ function iTutorialEvaluateStep() {
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("Hacking is the core mechanic of the game and is necessary for progressing. However, " +
|
||||
"you don't want to be hacking manually the entire time. You can automate your hacking " +
|
||||
"by writing scripts!<br><br>To create a new script or edit an existing one, you can use the 'nano' " +
|
||||
"command. Scripts must end with the '.script' extension. Let's make a script now by " +
|
||||
"entering 'nano foodnstuff.script' after the hack command finishes running (Sidenote: Pressing ctrl + c" +
|
||||
"by writing scripts!<br><br>To create a new script or edit an existing one, you can use the <code class='interactive-tutorial-command'>nano</code> " +
|
||||
"command. Scripts must end with the <code class='interactive-tutorial-command'>.script</code> extension. Let's make a script now by " +
|
||||
"entering <code class='interactive-tutorial-command'>nano n00dles.script</code> after the hack command finishes running (Sidenote: Pressing ctrl + c" +
|
||||
" will end a command like hack early)");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalTypeScript:
|
||||
Engine.loadScriptEditorContent("foodnstuff.script", "");
|
||||
Engine.loadScriptEditorContent("n00dles.script", "");
|
||||
iTutorialSetText("This is the script editor. You can use it to program your scripts. Scripts are " +
|
||||
"written in the Netscript language, a programming language created for " +
|
||||
"this game. <strong style='background-color:#444;'>There are details about the Netscript language in the documentation, which " +
|
||||
"can be accessed in the 'Tutorial' tab on the main navigation menu. I highly suggest you check " +
|
||||
"it out after this tutorial.</strong> For now, just copy " +
|
||||
"and paste the following code into the script editor: <br><br>" +
|
||||
"while(true) { <br>" +
|
||||
" hack('foodnstuff'); <br>" +
|
||||
"}<br><br> " +
|
||||
"written in a simplified version of javascript. Copy and paste the following code into the script editor: <br><br>" +
|
||||
"<pre class='interactive-tutorial-code'>" +
|
||||
"while(true) {\n" +
|
||||
" hack('n00dles');\n" +
|
||||
"}</pre>" +
|
||||
"For anyone with basic programming experience, this code should be straightforward. " +
|
||||
"This script will continuously hack the 'foodnstuff' server.<br><br>" +
|
||||
"This script will continuously hack the <code class='interactive-tutorial-command'>n00dles</code> server.<br><br>" +
|
||||
"To save and close the script editor, press the button in the bottom left, or press ctrl + b.");
|
||||
nextBtn.style.display = "none"; // next step triggered in saveAndCloseScriptEditor() (Script.js)
|
||||
break;
|
||||
@ -288,25 +283,25 @@ function iTutorialEvaluateStep() {
|
||||
iTutorialSetText("Now we'll run the script. Scripts require a certain amount of RAM to run, and can be " +
|
||||
"run on any machine which you have root access to. Different servers have different " +
|
||||
"amounts of RAM. You can also purchase more RAM for your home server.<br><br>To check how much " +
|
||||
"RAM is available on this machine, enter the 'free' command.");
|
||||
"RAM is available on this machine, enter the <code class='interactive-tutorial-command'>free</code> command.");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal commmand
|
||||
break;
|
||||
case iTutorialSteps.TerminalRunScript:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("We have 16GB of free RAM on this machine, which is enough to run our " +
|
||||
"script. Let's run our script using 'run foodnstuff.script'.");
|
||||
"script. Let's run our script using <code class='interactive-tutorial-command'>run n00dles.script</code>.");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal commmand
|
||||
break;
|
||||
case iTutorialSteps.TerminalGoToActiveScriptsPage:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("Your script is now running! The script might take a few seconds to 'fully start up'. " +
|
||||
"Your scripts will continuously run in the background and will automatically stop if " +
|
||||
"the code ever completes (the 'foodnstuff.script' will never complete because it " +
|
||||
iTutorialSetText("Your script is now running! " +
|
||||
"It will continuously run in the background and will automatically stop if " +
|
||||
"the code ever completes (the <code class='interactive-tutorial-command'>n00dles.script</code> will never complete because it " +
|
||||
"runs an infinite loop). <br><br>These scripts can passively earn you income and hacking experience. " +
|
||||
"Your scripts will also earn money and experience while you are offline, although at a " +
|
||||
"much slower rate. <br><br> " +
|
||||
"slightly slower rate. <br><br> " +
|
||||
"Let's check out some statistics for our running scripts by clicking the " +
|
||||
"'Active Scripts' link in the main navigation menu.");
|
||||
"<code class='interactive-tutorial-tab flashing-button'>Active Scripts</code> link in the main navigation menu.");
|
||||
nextBtn.style.display = "none";
|
||||
|
||||
// Flash 'Active Scripts' menu and set its tutorial click handler
|
||||
@ -319,10 +314,9 @@ function iTutorialEvaluateStep() {
|
||||
break;
|
||||
case iTutorialSteps.ActiveScriptsPage:
|
||||
Engine.loadActiveScriptsContent();
|
||||
iTutorialSetText("This page displays stats/information about all of your scripts that are " +
|
||||
"running across every existing server. You can use this to gauge how well " +
|
||||
"your scripts are doing. Let's go back to the Terminal now using the 'Terminal' " +
|
||||
"link.");
|
||||
iTutorialSetText("This page displays information about all of your scripts that are " +
|
||||
"running across every server. You can use this to gauge how well " +
|
||||
"your scripts are doing. Let's go back to the <code class='interactive-tutorial-tab flashing-button'>Terminal</code>");
|
||||
nextBtn.style.display = "none";
|
||||
|
||||
// Flash 'Terminal' button and set its tutorial click handler
|
||||
@ -336,27 +330,27 @@ function iTutorialEvaluateStep() {
|
||||
case iTutorialSteps.ActiveScriptsToTerminal:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("One last thing about scripts, each active script contains logs that detail " +
|
||||
"what it's doing. We can check these logs using the 'tail' command. Do that " +
|
||||
"now for the script we just ran by typing 'tail foodnstuff.script'");
|
||||
"what it's doing. We can check these logs using the <code class='interactive-tutorial-command'>tail</code> command. Do that " +
|
||||
"now for the script we just ran by typing <code class='interactive-tutorial-command'>tail n00dles.script</code>");
|
||||
nextBtn.style.display = "none"; // next step triggered by terminal command
|
||||
break;
|
||||
case iTutorialSteps.TerminalTailScript:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("The log for this script won't show much right now (it might show nothing at all) because it " +
|
||||
"just started running...but check back again in a few minutes! <br><br>" +
|
||||
"This pretty much covers the basics of hacking. To learn more about writing " +
|
||||
"scripts using the Netscript language, select the 'Tutorial' link in the " +
|
||||
"This covers the basics of hacking. To learn more about writing " +
|
||||
"scripts, select the <code class='interactive-tutorial-tab'>Tutorial</code> link in the " +
|
||||
"main navigation menu to look at the documentation. " +
|
||||
"<strong style='background-color:#444;'>If you are an experienced JavaScript " +
|
||||
"developer, I would highly suggest you check out the section on " +
|
||||
"NetscriptJS/Netscript 2.0.</strong><br><br>For now, let's move on to something else!");
|
||||
"NetscriptJS/Netscript 2.0, it's faster and more powerful.</strong><br><br>For now, let's move on to something else!");
|
||||
nextBtn.style.display = "inline-block";
|
||||
break;
|
||||
case iTutorialSteps.GoToHacknetNodesPage:
|
||||
Engine.loadTerminalContent();
|
||||
iTutorialSetText("Hacking is not the only way to earn money. One other way to passively " +
|
||||
"earn money is by purchasing and upgrading Hacknet Nodes. Let's go to " +
|
||||
"the 'Hacknet Nodes' page through the main navigation menu now.");
|
||||
"the <code class='interactive-tutorial-tab flashing-button'>Hacknet</code> page through the main navigation menu now.");
|
||||
nextBtn.style.display = "none";
|
||||
|
||||
// Flash 'Hacknet' menu and set its tutorial click handler
|
||||
@ -369,7 +363,7 @@ function iTutorialEvaluateStep() {
|
||||
break;
|
||||
case iTutorialSteps.HacknetNodesIntroduction:
|
||||
Engine.loadHacknetNodesContent();
|
||||
iTutorialSetText("From this page you can purchase new Hacknet Nodes and upgrade your " +
|
||||
iTutorialSetText("here you can purchase new Hacknet Nodes and upgrade your " +
|
||||
"existing ones. Let's purchase a new one now.");
|
||||
nextBtn.style.display = "none"; // Next step triggered by purchaseHacknet() (HacknetNode.js)
|
||||
break;
|
||||
@ -379,7 +373,7 @@ function iTutorialEvaluateStep() {
|
||||
"earn you money over time, both online and offline. When you get enough " +
|
||||
" money, you can upgrade " +
|
||||
"your newly-purchased Hacknet Node below.<br><br>" +
|
||||
"Let's go to the 'City' page through the main navigation menu.");
|
||||
"Let's go to the <code class='interactive-tutorial-tab flashing-button'>City</code> page through the main navigation menu.");
|
||||
nextBtn.style.display = "none";
|
||||
|
||||
// Flash 'City' menu and set its tutorial click handler
|
||||
@ -396,7 +390,7 @@ function iTutorialEvaluateStep() {
|
||||
"travel to. Each location has something that you can do. " +
|
||||
"There's a lot of content out in the world, make sure " +
|
||||
"you explore and discover!<br><br>" +
|
||||
"Lastly, click on the 'Tutorial' link in the main navigation menu.");
|
||||
"Lastly, click on the <code class='interactive-tutorial-tab flashing-button'>Tutorial</code> link in the main navigation menu.");
|
||||
nextBtn.style.display = "none";
|
||||
|
||||
// Flash 'Tutorial' menu and set its tutorial click handler
|
||||
@ -493,8 +487,8 @@ function iTutorialEnd() {
|
||||
document.getElementById("interactive-tutorial-container").style.display = "none";
|
||||
|
||||
// Create a popup with final introductory stuff
|
||||
var popupId = "interactive-tutorial-ending-popup";
|
||||
var txt = createElement("p", {
|
||||
const popupId = "interactive-tutorial-ending-popup";
|
||||
const txt = createElement("p", {
|
||||
innerHTML:
|
||||
"If you are new to the game, the following links may be useful for you!<br><br>" +
|
||||
"<a class='a-link-button' href='https://bitburner.readthedocs.io/en/latest/guidesandtips/gettingstartedguideforbeginnerprogrammers.html' target='_blank'>Getting Started Guide</a>" +
|
||||
@ -502,7 +496,7 @@ function iTutorialEnd() {
|
||||
"The Beginner's Guide to Hacking was added to your home computer! It contains some tips/pointers for starting out with the game. " +
|
||||
"To read it, go to Terminal and enter<br><br>cat " + LiteratureNames.HackersStartingHandbook,
|
||||
});
|
||||
var gotitBtn = createElement("a", {
|
||||
const gotitBtn = createElement("a", {
|
||||
class:"a-link-button", float:"right", padding:"6px", innerText:"Got it!",
|
||||
clickListener:()=>{
|
||||
removeElementById(popupId);
|
||||
@ -513,9 +507,16 @@ function iTutorialEnd() {
|
||||
Player.getHomeComputer().messages.push(LiteratureNames.HackersStartingHandbook);
|
||||
}
|
||||
|
||||
let textBox = null;
|
||||
(function() {
|
||||
function set() {
|
||||
textBox = document.getElementById("interactive-tutorial-text");
|
||||
document.removeEventListener("DOMContentLoaded", set);
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", set);
|
||||
})();
|
||||
|
||||
function iTutorialSetText(txt) {
|
||||
var textBox = document.getElementById("interactive-tutorial-text");
|
||||
if (textBox == null) {throw new Error("Could not find text box"); return;}
|
||||
textBox.innerHTML = txt;
|
||||
textBox.parentElement.scrollTop = 0; // this resets scroll position
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ import { LocationName } from "./data/LocationNames";
|
||||
import { LocationType } from "./LocationTypeEnum";
|
||||
|
||||
interface IInfiltrationMetadata {
|
||||
baseRewardValue: number;
|
||||
difficulty: number;
|
||||
maxClearanceLevel: number;
|
||||
startingSecurityLevel: number;
|
||||
}
|
||||
|
@ -11,10 +11,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 32,
|
||||
difficulty: 4.4,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 1350,
|
||||
maxClearanceLevel: 12,
|
||||
startingSecurityLevel: 8.18,
|
||||
},
|
||||
name: LocationName.AevumAeroCorp,
|
||||
types: [LocationType.Company],
|
||||
@ -22,10 +20,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 42,
|
||||
difficulty: 4.1,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 1350,
|
||||
maxClearanceLevel: 15,
|
||||
startingSecurityLevel: 8.19,
|
||||
},
|
||||
name: LocationName.AevumBachmanAndAssociates,
|
||||
types: [LocationType.Company],
|
||||
@ -33,10 +29,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 34,
|
||||
difficulty: 3.6,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 1800,
|
||||
maxClearanceLevel: 18,
|
||||
startingSecurityLevel: 9.55,
|
||||
},
|
||||
name: LocationName.AevumClarkeIncorporated,
|
||||
types: [LocationType.Company],
|
||||
@ -51,10 +45,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 116,
|
||||
difficulty: 6,
|
||||
maxClearanceLevel: 150,
|
||||
startingSecurityLevel: 4800,
|
||||
maxClearanceLevel: 37,
|
||||
startingSecurityLevel: 17.02,
|
||||
},
|
||||
name: LocationName.AevumECorp,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -64,10 +56,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 96,
|
||||
difficulty: 6.2,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 4140,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 15.54,
|
||||
},
|
||||
name: LocationName.AevumFulcrumTechnologies,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -77,10 +67,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 30,
|
||||
difficulty: 3.95,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 1260,
|
||||
maxClearanceLevel: 12,
|
||||
startingSecurityLevel: 7.89,
|
||||
},
|
||||
name: LocationName.AevumGalacticCybersystems,
|
||||
types: [LocationType.Company],
|
||||
@ -88,10 +76,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 10,
|
||||
difficulty: 1.4,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 144,
|
||||
maxClearanceLevel: 6,
|
||||
startingSecurityLevel: 3.29,
|
||||
},
|
||||
name: LocationName.AevumNetLinkTechnologies,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -101,10 +87,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 18,
|
||||
difficulty: 2.2,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 565,
|
||||
maxClearanceLevel: 6,
|
||||
startingSecurityLevel: 5.35,
|
||||
},
|
||||
name: LocationName.AevumPolice,
|
||||
types: [LocationType.Company],
|
||||
@ -112,10 +96,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 16,
|
||||
difficulty: 1.9,
|
||||
maxClearanceLevel: 20,
|
||||
startingSecurityLevel: 485,
|
||||
maxClearanceLevel: 5,
|
||||
startingSecurityLevel: 5.02,
|
||||
},
|
||||
name: LocationName.AevumRhoConstruction,
|
||||
types: [LocationType.Company],
|
||||
@ -137,10 +119,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Aevum,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 20,
|
||||
difficulty: 3,
|
||||
maxClearanceLevel: 30,
|
||||
startingSecurityLevel: 690,
|
||||
maxClearanceLevel: 7,
|
||||
startingSecurityLevel: 5.85,
|
||||
},
|
||||
name: LocationName.AevumWatchdogSecurity,
|
||||
types: [LocationType.Company],
|
||||
@ -153,10 +133,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Chongqing,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 100,
|
||||
difficulty: 6.1,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 4450,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 16.25,
|
||||
},
|
||||
name: LocationName.ChongqingKuaiGongInternational,
|
||||
types: [LocationType.Company],
|
||||
@ -164,10 +142,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Chongqing,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 52,
|
||||
difficulty: 6,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 2915,
|
||||
maxClearanceLevel: 18,
|
||||
startingSecurityLevel: 12.59,
|
||||
},
|
||||
name: LocationName.ChongqingSolarisSpaceSystems,
|
||||
types: [LocationType.Company],
|
||||
@ -175,10 +151,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Ishima,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 20,
|
||||
difficulty: 3.2,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 485,
|
||||
maxClearanceLevel: 12,
|
||||
startingSecurityLevel: 5.02,
|
||||
},
|
||||
name: LocationName.IshimaNovaMedical,
|
||||
types: [LocationType.Company],
|
||||
@ -186,10 +160,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Ishima,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 10,
|
||||
difficulty: 1.6,
|
||||
maxClearanceLevel: 40,
|
||||
startingSecurityLevel: 130,
|
||||
maxClearanceLevel: 10,
|
||||
startingSecurityLevel: 3.2,
|
||||
},
|
||||
name: LocationName.IshimaOmegaSoftware,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -199,10 +171,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Ishima,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 4.1,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 570,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 5.38,
|
||||
},
|
||||
name: LocationName.IshimaStormTechnologies,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -212,10 +182,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 28,
|
||||
difficulty: 4,
|
||||
maxClearanceLevel: 70,
|
||||
startingSecurityLevel: 1050,
|
||||
maxClearanceLevel: 17,
|
||||
startingSecurityLevel: 7.18,
|
||||
},
|
||||
name: LocationName.NewTokyoDefComm,
|
||||
types: [LocationType.Company],
|
||||
@ -223,10 +191,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 3.8,
|
||||
maxClearanceLevel: 80,
|
||||
startingSecurityLevel: 700,
|
||||
maxClearanceLevel: 20,
|
||||
startingSecurityLevel: 5.9,
|
||||
},
|
||||
name: LocationName.NewTokyoGlobalPharmaceuticals,
|
||||
types: [LocationType.Company],
|
||||
@ -239,10 +205,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.NewTokyo,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 22,
|
||||
difficulty: 3.5,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 605,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 5.52,
|
||||
},
|
||||
name: LocationName.NewTokyoVitaLife,
|
||||
types: [LocationType.Company, LocationType.Special],
|
||||
@ -250,10 +214,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 14,
|
||||
difficulty: 2.25,
|
||||
maxClearanceLevel: 40,
|
||||
startingSecurityLevel: 200,
|
||||
maxClearanceLevel: 10,
|
||||
startingSecurityLevel: 3.62,
|
||||
},
|
||||
name: LocationName.Sector12AlphaEnterprises,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -263,10 +225,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 46,
|
||||
difficulty: 4.2,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 2160,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 10.59,
|
||||
},
|
||||
name: LocationName.Sector12BladeIndustries,
|
||||
types: [LocationType.Company],
|
||||
@ -279,10 +239,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 18,
|
||||
difficulty: 2.5,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 405,
|
||||
maxClearanceLevel: 15,
|
||||
startingSecurityLevel: 4.66,
|
||||
},
|
||||
name: LocationName.Sector12CarmichaelSecurity,
|
||||
types: [LocationType.Company],
|
||||
@ -295,10 +253,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 4.3,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 700,
|
||||
maxClearanceLevel: 12,
|
||||
startingSecurityLevel: 5.9,
|
||||
},
|
||||
name: LocationName.Sector12DeltaOne,
|
||||
types: [LocationType.Company],
|
||||
@ -311,10 +267,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 58,
|
||||
difficulty: 7,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 1350,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 8.18,
|
||||
},
|
||||
name: LocationName.Sector12FourSigma,
|
||||
types: [LocationType.Company],
|
||||
@ -322,10 +276,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 32,
|
||||
difficulty: 5.4,
|
||||
maxClearanceLevel: 70,
|
||||
startingSecurityLevel: 730,
|
||||
maxClearanceLevel: 17,
|
||||
startingSecurityLevel: 6.02,
|
||||
},
|
||||
name: LocationName.Sector12IcarusMicrosystems,
|
||||
types: [LocationType.Company],
|
||||
@ -340,10 +292,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 8,
|
||||
difficulty: 1.8,
|
||||
maxClearanceLevel: 20,
|
||||
startingSecurityLevel: 120,
|
||||
maxClearanceLevel: 5,
|
||||
startingSecurityLevel: 3.13,
|
||||
},
|
||||
name: LocationName.Sector12JoesGuns,
|
||||
types: [LocationType.Company],
|
||||
@ -351,10 +301,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 114,
|
||||
difficulty: 6.75,
|
||||
maxClearanceLevel: 125,
|
||||
startingSecurityLevel: 4500,
|
||||
maxClearanceLevel: 31,
|
||||
startingSecurityLevel: 16.36,
|
||||
},
|
||||
name: LocationName.Sector12MegaCorp,
|
||||
types: [LocationType.Company],
|
||||
@ -381,10 +329,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Sector12,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 24,
|
||||
difficulty: 4.3,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 700,
|
||||
maxClearanceLevel: 12,
|
||||
startingSecurityLevel: 5.9,
|
||||
},
|
||||
name: LocationName.Sector12UniversalEnergy,
|
||||
types: [LocationType.Company],
|
||||
@ -392,10 +338,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 12,
|
||||
difficulty: 2.1,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 195,
|
||||
maxClearanceLevel: 15,
|
||||
startingSecurityLevel: 3.59,
|
||||
},
|
||||
name: LocationName.VolhavenCompuTek,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -405,10 +349,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 28,
|
||||
difficulty: 3,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 1080,
|
||||
maxClearanceLevel: 18,
|
||||
startingSecurityLevel: 7.28,
|
||||
},
|
||||
name: LocationName.VolhavenHeliosLabs,
|
||||
types: [LocationType.Company],
|
||||
@ -416,10 +358,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 14,
|
||||
difficulty: 2,
|
||||
maxClearanceLevel: 60,
|
||||
startingSecurityLevel: 340,
|
||||
maxClearanceLevel: 15,
|
||||
startingSecurityLevel: 4.35,
|
||||
},
|
||||
name: LocationName.VolhavenLexoCorp,
|
||||
types: [LocationType.Company],
|
||||
@ -434,10 +374,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 56,
|
||||
difficulty: 6.8,
|
||||
maxClearanceLevel: 200,
|
||||
startingSecurityLevel: 1460,
|
||||
maxClearanceLevel: 50,
|
||||
startingSecurityLevel: 8.53,
|
||||
},
|
||||
name: LocationName.VolhavenNWO,
|
||||
types: [LocationType.Company],
|
||||
@ -445,10 +383,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 44,
|
||||
difficulty: 4.4,
|
||||
maxClearanceLevel: 100,
|
||||
startingSecurityLevel: 1215,
|
||||
maxClearanceLevel: 25,
|
||||
startingSecurityLevel: 7.74,
|
||||
},
|
||||
name: LocationName.VolhavenOmniTekIncorporated,
|
||||
types: [LocationType.Company, LocationType.TechVendor],
|
||||
@ -458,10 +394,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 28,
|
||||
difficulty: 4.9,
|
||||
maxClearanceLevel: 90,
|
||||
startingSecurityLevel: 725,
|
||||
maxClearanceLevel: 22,
|
||||
startingSecurityLevel: 6,
|
||||
},
|
||||
name: LocationName.VolhavenOmniaCybersystems,
|
||||
types: [LocationType.Company],
|
||||
@ -469,10 +403,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
{
|
||||
city: CityName.Volhaven,
|
||||
infiltrationData: {
|
||||
baseRewardValue: 18,
|
||||
difficulty: 2.4,
|
||||
maxClearanceLevel: 75,
|
||||
startingSecurityLevel: 430,
|
||||
maxClearanceLevel: 18,
|
||||
startingSecurityLevel: 4.77,
|
||||
},
|
||||
name: LocationName.VolhavenSysCoreSecurities,
|
||||
types: [LocationType.Company],
|
||||
@ -505,3 +437,10 @@ export const LocationsMetadata: IConstructorParams[] = [
|
||||
types: [LocationType.StockMarket],
|
||||
},
|
||||
];
|
||||
|
||||
(function(){
|
||||
for(const loc of LocationsMetadata) {
|
||||
if(!loc || !loc.infiltrationData) continue
|
||||
console.log(loc.infiltrationData.startingSecurityLevel+2);
|
||||
}
|
||||
})();
|
@ -12,7 +12,6 @@ import { Locations } from "../Locations";
|
||||
import { LocationName } from "../data/LocationNames";
|
||||
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { beginInfiltration } from "../../Infiltration";
|
||||
|
||||
import { Companies } from "../../Company/Companies";
|
||||
import { Company } from "../../Company/Company";
|
||||
@ -193,13 +192,15 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
||||
startInfiltration(e: React.MouseEvent<HTMLElement>): void {
|
||||
if (!e.isTrusted) { return; }
|
||||
const loc = this.location;
|
||||
if (!loc.infiltrationData) {
|
||||
console.error(`trying to start infiltration at ${this.props.locName} but the infiltrationData is null`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.engine.loadInfiltrationContent();
|
||||
this.props.engine.loadInfiltrationContent(this.props.locName, loc.infiltrationData.startingSecurityLevel, loc.infiltrationData.maxClearanceLevel);
|
||||
|
||||
const data = loc.infiltrationData;
|
||||
if (data == null) { return; }
|
||||
this.props.p.singularityStopWork();
|
||||
beginInfiltration(this.props.locName, data.startingSecurityLevel, data.baseRewardValue, data.maxClearanceLevel, data.difficulty);
|
||||
}
|
||||
|
||||
work(e: React.MouseEvent<HTMLElement>): void {
|
||||
|
@ -223,6 +223,8 @@ export const RamCosts: IMap<any> = {
|
||||
|
||||
// Gang API
|
||||
gang : {
|
||||
createGang: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
inGang: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getMemberNames: () => RamCostConstants.ScriptGangApiBaseRamCost / 4,
|
||||
getGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
getOtherGangInformation: () => RamCostConstants.ScriptGangApiBaseRamCost / 2,
|
||||
|
@ -376,7 +376,7 @@ function NetscriptFunctions(workerScript) {
|
||||
const makeRuntimeErrorMsg = function(caller, msg) {
|
||||
const stack = (new Error()).stack.split('\n').slice(1);
|
||||
const scripts = workerScript.getServer().scripts;
|
||||
let userstack = [];
|
||||
const userstack = [];
|
||||
for(const stackline of stack) {
|
||||
let filename;
|
||||
for(const script of scripts) {
|
||||
@ -398,13 +398,12 @@ function NetscriptFunctions(workerScript) {
|
||||
const lineMatch = line.match(lineRe);
|
||||
const funcMatch = line.match(funcRe);
|
||||
if(lineMatch && funcMatch) {
|
||||
let func = funcMatch[1];
|
||||
return {line: lineMatch[1], func: func};
|
||||
return {line: lineMatch[1], func: funcMatch[1]};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
let call = {line: "-1", func: "unknown"};
|
||||
let chromeCall = parseChromeStackline(stackline);
|
||||
const chromeCall = parseChromeStackline(stackline);
|
||||
if (chromeCall) {
|
||||
call = chromeCall;
|
||||
}
|
||||
@ -430,7 +429,8 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
|
||||
workerScript.log(caller, msg);
|
||||
const rejectMsg = `${caller}: ${msg}<br><br>Stack:<br>${userstack.join('<br>')}`
|
||||
let rejectMsg = `${caller}: ${msg}`
|
||||
if(userstack.length !== 0) rejectMsg += `<br><br>Stack:<br>${userstack.join('<br>')}`;
|
||||
return makeRuntimeRejectMsg(workerScript, rejectMsg);
|
||||
}
|
||||
|
||||
@ -3603,6 +3603,30 @@ function NetscriptFunctions(workerScript) {
|
||||
|
||||
// Gang API
|
||||
gang: {
|
||||
createGang: function(faction) {
|
||||
updateDynamicRam("createGang", getRamCost("gang", "createGang"));
|
||||
// this list is copied from Faction/ui/Root.tsx
|
||||
const GangNames = [
|
||||
"Slum Snakes",
|
||||
"Tetrads",
|
||||
"The Syndicate",
|
||||
"The Dark Army",
|
||||
"Speakers for the Dead",
|
||||
"NiteSec",
|
||||
"The Black Hand",
|
||||
];
|
||||
if(!Player.canAccessGang() || !GangNames.includes(faction)) return false;
|
||||
if (Player.inGang()) return false;
|
||||
if(!Player.factions.includes(faction)) return false;
|
||||
|
||||
const isHacking = (faction === "NiteSec" || faction === "The Black Hand");
|
||||
Player.startGang(faction, isHacking);
|
||||
return true;
|
||||
},
|
||||
inGang: function() {
|
||||
updateDynamicRam("inGang", getRamCost("gang", "inGang"));
|
||||
return Player.inGang();
|
||||
},
|
||||
getMemberNames: function() {
|
||||
updateDynamicRam("getMemberNames", getRamCost("gang", "getMemberNames"));
|
||||
checkGangApiAccess("getMemberNames");
|
||||
@ -4386,7 +4410,7 @@ function NetscriptFunctions(workerScript) {
|
||||
},
|
||||
constants: function() {
|
||||
checkFormulasAccess("hacknetNodes.constants", 5);
|
||||
return Object.assign({}, HacknetNodeConstants, HacknetServerConstants);
|
||||
return Object.assign({}, HacknetNodeConstants);
|
||||
},
|
||||
},
|
||||
hacknetServers: {
|
||||
|
@ -176,6 +176,7 @@ export interface IPlayer {
|
||||
startGang(facName: string, isHacking: boolean): void;
|
||||
startWork(companyName: string): void;
|
||||
startWorkPartTime(companyName: string): void;
|
||||
takeDamage(amt: number): boolean;
|
||||
travel(to: CityName): boolean;
|
||||
giveExploit(exploit: Exploit): void;
|
||||
queryStatFromString(str: string): number;
|
||||
|
@ -394,6 +394,7 @@ export function loseMoney(money) {
|
||||
console.error("NaN passed into Player.loseMoney()");
|
||||
return;
|
||||
}
|
||||
if(this.money.eq(Infinity) && money === Infinity) return;
|
||||
this.money = this.money.minus(money);
|
||||
}
|
||||
|
||||
@ -660,7 +661,7 @@ export function work(numCycles) {
|
||||
|
||||
const penalty = this.cancelationPenalty();
|
||||
|
||||
const penaltyString = penalty === 0.5 ? 'half' : 'three quarter'
|
||||
const penaltyString = penalty === 0.5 ? 'half' : 'three-quarters'
|
||||
|
||||
var elem = document.getElementById("work-in-progress-text");
|
||||
ReactDOM.render(<>
|
||||
@ -1740,7 +1741,6 @@ export function applyForJob(entryPosType, sing=false) {
|
||||
}
|
||||
}
|
||||
|
||||
this.companyName = company.name;
|
||||
this.jobs[company.name] = pos.name;
|
||||
|
||||
document.getElementById("world-menu-header").click();
|
||||
@ -1863,7 +1863,6 @@ export function applyForEmployeeJob(sing=false) {
|
||||
export function applyForPartTimeEmployeeJob(sing=false) {
|
||||
var company = Companies[this.location]; //Company being applied to
|
||||
if (this.isQualified(company, CompanyPositions[posNames.PartTimeCompanyPositions[1]])) {
|
||||
this.companyName = company.name;
|
||||
this.jobs[company.name] = posNames.PartTimeCompanyPositions[1];
|
||||
document.getElementById("world-menu-header").click();
|
||||
document.getElementById("world-menu-header").click();
|
||||
|
@ -561,7 +561,7 @@ function updateSleeveTaskSelector(sleeve: Sleeve, elems: ISleeveUIElems, allSlee
|
||||
elems.taskDetailsSelector.add(createOptionElement(gymSelectorOptions[i]));
|
||||
|
||||
// Set initial value
|
||||
if (sleeve.gymStatType === gymSelectorOptions[i]) {
|
||||
if (sleeve.gymStatType === gymSelectorOptions[i].substring(6, 9).toLowerCase()) {
|
||||
elems.taskDetailsSelector.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ function mult(f: Faction): number {
|
||||
}
|
||||
|
||||
export function getHackingWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
return (p.hacking_skill + p.intelligence) /
|
||||
return (p.hacking_skill + p.intelligence/3) /
|
||||
CONSTANTS.MaxSkillLevel * p.faction_rep_mult *
|
||||
p.getIntelligenceBonus(0.25) * mult(f);
|
||||
p.getIntelligenceBonus(1) * mult(f);
|
||||
}
|
||||
|
||||
export function getFactionSecurityWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
@ -20,8 +20,9 @@ export function getFactionSecurityWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
p.strength / CONSTANTS.MaxSkillLevel +
|
||||
p.defense / CONSTANTS.MaxSkillLevel +
|
||||
p.dexterity / CONSTANTS.MaxSkillLevel +
|
||||
p.agility / CONSTANTS.MaxSkillLevel) / 4.5;
|
||||
return t * p.faction_rep_mult * mult(f);
|
||||
p.agility / CONSTANTS.MaxSkillLevel +
|
||||
p.intelligence / CONSTANTS.MaxSkillLevel) / 4.5;
|
||||
return t * p.faction_rep_mult * mult(f) * p.getIntelligenceBonus(1);
|
||||
}
|
||||
|
||||
export function getFactionFieldWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
@ -32,5 +33,5 @@ export function getFactionFieldWorkRepGain(p: IPlayer, f: Faction): number {
|
||||
p.agility / CONSTANTS.MaxSkillLevel +
|
||||
p.charisma / CONSTANTS.MaxSkillLevel +
|
||||
p.intelligence / CONSTANTS.MaxSkillLevel) / 5.5;
|
||||
return t * p.faction_rep_mult * mult(f);
|
||||
return t * p.faction_rep_mult * mult(f) * p.getIntelligenceBonus(1);
|
||||
}
|
||||
|
@ -231,12 +231,12 @@ function saveAndCloseScriptEditor() {
|
||||
|
||||
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||
//Make sure filename + code properly follow tutorial
|
||||
if (filename !== "foodnstuff.script") {
|
||||
dialogBoxCreate("Leave the script name as 'foodnstuff'!");
|
||||
if (filename !== "n00dles.script") {
|
||||
dialogBoxCreate("Leave the script name as 'n00dles'!");
|
||||
return;
|
||||
}
|
||||
code = code.replace(/\s/g, "");
|
||||
if (code.indexOf("while(true){hack('foodnstuff');}") == -1) {
|
||||
if (code.indexOf("while(true){hack('n00dles');}") == -1) {
|
||||
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
||||
return;
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStart
|
||||
import { Player } from "./Player";
|
||||
import { hackWorldDaemon } from "./RedPill";
|
||||
import { RunningScript } from "./Script/RunningScript";
|
||||
import { compareArrays } from "../utils/helpers/compareArrays";
|
||||
import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers";
|
||||
import {
|
||||
getCurrentEditor,
|
||||
@ -742,8 +743,8 @@ let Terminal = {
|
||||
|
||||
/****************** Interactive Tutorial Terminal Commands ******************/
|
||||
if (ITutorial.isRunning) {
|
||||
var foodnstuffServ = GetServerByHostname("foodnstuff");
|
||||
if (foodnstuffServ == null) {throw new Error("Could not get foodnstuff server"); return;}
|
||||
var n00dlesServ = GetServerByHostname("n00dles");
|
||||
if (n00dlesServ == null) {throw new Error("Could not get n00dles server"); return;}
|
||||
|
||||
switch(ITutorial.currStep) {
|
||||
case iTutorialSteps.TerminalHelp:
|
||||
@ -780,11 +781,11 @@ let Terminal = {
|
||||
case iTutorialSteps.TerminalConnect:
|
||||
if (commandArray.length == 2) {
|
||||
if ((commandArray[0] == "connect") &&
|
||||
(commandArray[1] == "foodnstuff" || commandArray[1] == foodnstuffServ.ip)) {
|
||||
(commandArray[1] == "n00dles" || commandArray[1] == n00dlesServ.ip)) {
|
||||
Player.getCurrentServer().isConnectedTo = false;
|
||||
Player.currentServer = foodnstuffServ.ip;
|
||||
Player.currentServer = n00dlesServ.ip;
|
||||
Player.getCurrentServer().isConnectedTo = true;
|
||||
post("Connected to foodnstuff");
|
||||
post("Connected to n00dles");
|
||||
iTutorialNextStep();
|
||||
} else {post("Wrong command! Try again!"); return;}
|
||||
} else {post("Bad command. Please follow the tutorial");}
|
||||
@ -804,8 +805,8 @@ let Terminal = {
|
||||
case iTutorialSteps.TerminalNuke:
|
||||
if (commandArray.length == 2 &&
|
||||
commandArray[0] == "run" && commandArray[1] == "NUKE.exe") {
|
||||
foodnstuffServ.hasAdminRights = true;
|
||||
post("NUKE successful! Gained root access to foodnstuff");
|
||||
n00dlesServ.hasAdminRights = true;
|
||||
post("NUKE successful! Gained root access to n00dles");
|
||||
iTutorialNextStep();
|
||||
} else {post("Bad command. Please follow the tutorial");}
|
||||
break;
|
||||
@ -817,8 +818,8 @@ let Terminal = {
|
||||
break;
|
||||
case iTutorialSteps.TerminalCreateScript:
|
||||
if (commandArray.length == 2 &&
|
||||
commandArray[0] == "nano" && commandArray[1] == "foodnstuff.script") {
|
||||
Engine.loadScriptEditorContent("foodnstuff.script", "");
|
||||
commandArray[0] == "nano" && commandArray[1] == "n00dles.script") {
|
||||
Engine.loadScriptEditorContent("n00dles.script", "");
|
||||
iTutorialNextStep();
|
||||
} else {post("Bad command. Please follow the tutorial");}
|
||||
break;
|
||||
@ -830,16 +831,16 @@ let Terminal = {
|
||||
break;
|
||||
case iTutorialSteps.TerminalRunScript:
|
||||
if (commandArray.length == 2 &&
|
||||
commandArray[0] == "run" && commandArray[1] == "foodnstuff.script") {
|
||||
commandArray[0] == "run" && commandArray[1] == "n00dles.script") {
|
||||
Terminal.runScript(commandArray);
|
||||
iTutorialNextStep();
|
||||
} else {post("Bad command. Please follow the tutorial");}
|
||||
break;
|
||||
case iTutorialSteps.ActiveScriptsToTerminal:
|
||||
if (commandArray.length == 2 &&
|
||||
commandArray[0] == "tail" && commandArray[1] == "foodnstuff.script") {
|
||||
commandArray[0] == "tail" && commandArray[1] == "n00dles.script") {
|
||||
// Check that the script exists on this machine
|
||||
var runningScript = findRunningScript("foodnstuff.script", [], Player.getCurrentServer());
|
||||
var runningScript = findRunningScript("n00dles.script", [], Player.getCurrentServer());
|
||||
if (runningScript == null) {
|
||||
post("Error: No such script exists");
|
||||
return;
|
||||
@ -1422,13 +1423,46 @@ let Terminal = {
|
||||
args.push(commandArray[i]);
|
||||
}
|
||||
|
||||
// Check that the script exists on this machine
|
||||
const runningScript = findRunningScript(scriptName, args, s);
|
||||
if (runningScript == null) {
|
||||
postError("No such script exists");
|
||||
// go over all the running scripts. If there's a perfect
|
||||
// match, use it!
|
||||
for (var i = 0; i < s.runningScripts.length; ++i) {
|
||||
if (s.runningScripts[i].filename === scriptName &&
|
||||
compareArrays(s.runningScripts[i].args, args)) {
|
||||
logBoxCreate(s.runningScripts[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Find all scripts that are potential candidates.
|
||||
const candidates = [];
|
||||
for (var i = 0; i < s.runningScripts.length; ++i) {
|
||||
// only scripts that have more arguments (equal arguments is already caught)
|
||||
if(s.runningScripts[i].args.length < args.length) continue;
|
||||
// make a smaller copy of the args.
|
||||
const args2 = s.runningScripts[i].args.slice(0, args.length);
|
||||
if (s.runningScripts[i].filename === scriptName &&
|
||||
compareArrays(args2, args)) {
|
||||
candidates.push(s.runningScripts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// If there's only 1 possible choice, use that.
|
||||
if(candidates.length === 1) {
|
||||
logBoxCreate(candidates[0]);
|
||||
return;
|
||||
}
|
||||
logBoxCreate(runningScript);
|
||||
|
||||
// otherwise lists all possible conflicting choices.
|
||||
if(candidates.length > 1) {
|
||||
postError("Found several potential candidates:");
|
||||
for(const candidate of candidates)
|
||||
postError(`${candidate.filename} ${candidate.args.join(' ')}`);
|
||||
postError("Script arguments need to be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
// if there's no candidate then we just don't know.
|
||||
postError("No such script exists.");
|
||||
} else {
|
||||
const runningScript = findRunningScriptByPid(commandArray[1], Player.getCurrentServer());
|
||||
if (runningScript == null) {
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
processPassiveFactionRepGain,
|
||||
inviteToFaction,
|
||||
} from "./Faction/FactionHelpers";
|
||||
import { displayInfiltrationContent } from "./Infiltration/Helper";
|
||||
import {
|
||||
getHackingWorkRepGain,
|
||||
getFactionSecurityWorkRepGain,
|
||||
@ -419,10 +420,13 @@ const Engine = {
|
||||
routing.navigateTo(Page.CinematicText);
|
||||
},
|
||||
|
||||
loadInfiltrationContent: function() {
|
||||
loadInfiltrationContent: function(name, difficulty, maxLevel) {
|
||||
Engine.hideAllContent();
|
||||
const mainMenu = document.getElementById("mainmenu-container");
|
||||
mainMenu.style.visibility = "hidden";
|
||||
Engine.Display.infiltrationContent.style.display = "block";
|
||||
routing.navigateTo(Page.Infiltration);
|
||||
displayInfiltrationContent(this, Player, name, difficulty, maxLevel);
|
||||
},
|
||||
|
||||
loadStockMarketContent: function() {
|
||||
@ -501,6 +505,8 @@ const Engine = {
|
||||
|
||||
Engine.Display.activeScriptsContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent);
|
||||
Engine.Display.infiltrationContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.infiltrationContent);
|
||||
|
||||
clearHacknetNodesUI();
|
||||
Engine.Display.createProgramContent.style.display = "none";
|
||||
@ -522,7 +528,6 @@ const Engine = {
|
||||
Engine.Display.workInProgressContent.style.display = "none";
|
||||
Engine.Display.redPillContent.style.display = "none";
|
||||
Engine.Display.cinematicTextContent.style.display = "none";
|
||||
Engine.Display.infiltrationContent.style.display = "none";
|
||||
Engine.Display.stockMarketContent.style.display = "none";
|
||||
Engine.Display.missionContent.style.display = "none";
|
||||
if (document.getElementById("gang-container")) {
|
||||
|
@ -31,3 +31,4 @@ import "../css/grid.min.css";
|
||||
import "../css/dev-menu.css";
|
||||
import "../css/casino.scss";
|
||||
import "../css/milestones.scss";
|
||||
import "../css/infiltration.scss";
|
@ -67,7 +67,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<span id="augmentations-notification" class="notification-off"> </span>
|
||||
</li>
|
||||
<li id="hacknet-nodes-tab" class="mainmenu-accordion-panel">
|
||||
<button id="hacknet-nodes-menu-link"> Hacknet Nodes </button>
|
||||
<button id="hacknet-nodes-menu-link"> Hacknet </button>
|
||||
</li>
|
||||
<li id="sleeves-tab" class="mainmenu-accordion-panel">
|
||||
<button id="sleeves-menu-link"> Sleeves </button>
|
||||
@ -277,25 +277,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<div id="location-container" class="generic-menupage-container">
|
||||
</div>
|
||||
|
||||
<div id="infiltration-container" class="generic-menupage-container">
|
||||
<div id="infiltration-left-panel">
|
||||
<p id="infiltration-level-text"> </p>
|
||||
<div id="infiltration-buttons">
|
||||
<button class="a-link-button tooltip" id="infiltration-kill"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-knockout"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-stealthknockout"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-assassinate"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-hacksecurity"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-destroysecurity"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-sneak"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-pickdoor"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-bribe"> </button>
|
||||
<button class="a-link-button tooltip" id="infiltration-escape"> </button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="infiltration-right-panel">
|
||||
<p id="infiltration-status-text"></p>
|
||||
</div>
|
||||
<div id="infiltration-container" class="generic-fullscreen-container">
|
||||
</div>
|
||||
|
||||
<div id="stock-market-container" class="generic-menupage-container">
|
||||
|
@ -16,6 +16,10 @@ import 'numeral/locales/ru';
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
|
||||
const extraFormats = [1e15, 1e18, 1e21, 1e24, 1e27, 1e30];
|
||||
const extraNotations = ['q', 'Q', 's', 'S', 'o', 'n'];
|
||||
|
||||
|
||||
class NumeralFormatter {
|
||||
// Default Locale
|
||||
defaultLocale = "en";
|
||||
@ -48,42 +52,52 @@ class NumeralFormatter {
|
||||
return this.format(n, "0.000a");
|
||||
}
|
||||
|
||||
formatHp(n: number): string {
|
||||
return this.format(n, "0");
|
||||
}
|
||||
|
||||
formatMoney(n: number): string {
|
||||
if(numeral.options.currentLocale === "en") {
|
||||
const extraFormats = [1e15, 1e18, 1e21, 1e24, 1e27, 1e30];
|
||||
const extraNotations = ['q', 'Q', 's', 'S', 'o', 'n'];
|
||||
for(let i = 0; i < extraFormats.length; i++) {
|
||||
if(extraFormats[i] < n && n <= extraFormats[i]*1000) {
|
||||
return '$'+this.format(n/extraFormats[i], '0.000')+extraNotations[i];
|
||||
}
|
||||
// TODO: leverage numeral.js to do it. This function also implies you can
|
||||
// use this format in some text field but you can't. ( "1t" will parse but
|
||||
// "1s" will not)
|
||||
formatReallyBigNumber(n: number, decimalPlaces = 3): string {
|
||||
if(n === Infinity) return "∞";
|
||||
for(let i = 0; i < extraFormats.length; i++) {
|
||||
if(extraFormats[i] < n && n <= extraFormats[i]*1000) {
|
||||
return this.format(n/extraFormats[i], '0.'+'0'.repeat(decimalPlaces))+extraNotations[i];
|
||||
}
|
||||
}
|
||||
if(Math.abs(n) < 1000) {
|
||||
return this.format(n, "$0.00");
|
||||
return this.format(n, '0.'+'0'.repeat(decimalPlaces));
|
||||
}
|
||||
const str = this.format(n, "$0.000a");
|
||||
if(str === "$NaNt") return '$'+this.format(n, '0.000e+0');
|
||||
const str = this.format(n, '0.'+'0'.repeat(decimalPlaces) + 'a');
|
||||
if(str === "NaNt") return this.format(n, '0.' + ' '.repeat(decimalPlaces) + 'e+0');
|
||||
return str;
|
||||
}
|
||||
|
||||
formatHp(n: number): string {
|
||||
if(n < 1e6){
|
||||
return this.format(n, "0,0");
|
||||
}
|
||||
return this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatMoney(n: number): string {
|
||||
return "$" + this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatSkill(n: number): string {
|
||||
return this.format(n, "0,0");
|
||||
if(n < 1e15){
|
||||
return this.format(n, "0,0");
|
||||
}
|
||||
return this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatExp(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
return this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatHashes(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
return this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatReputation(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
return this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatFavor(n: number): string {
|
||||
@ -104,11 +118,11 @@ class NumeralFormatter {
|
||||
}
|
||||
|
||||
formatRespect(n: number): string {
|
||||
return this.format(n, "0.00000a");
|
||||
return this.formatReallyBigNumber(n, 5);
|
||||
}
|
||||
|
||||
formatWanted(n: number): string {
|
||||
return this.format(n, "0.00000a");
|
||||
return this.formatReallyBigNumber(n, 5);
|
||||
}
|
||||
|
||||
formatMultiplier(n: number): string {
|
||||
@ -139,11 +153,11 @@ class NumeralFormatter {
|
||||
if (n < 1000) {
|
||||
return this.format(n, "0");
|
||||
}
|
||||
return this.format(n, "0.000a");
|
||||
return this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatInfiltrationSecurity(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
return this.formatReallyBigNumber(n);
|
||||
}
|
||||
|
||||
formatThreads(n: number): string {
|
||||
|
@ -1,120 +0,0 @@
|
||||
import { dialogBoxCreate } from "./DialogBox";
|
||||
import { clearEventListeners } from "./uiHelpers/clearEventListeners";
|
||||
import { numeralWrapper } from "../src/ui/numeralFormat";
|
||||
|
||||
import { BitNodeMultipliers } from "../src/BitNode/BitNodeMultipliers";
|
||||
import { CONSTANTS } from "../src/Constants";
|
||||
import { Factions } from "../src/Faction/Factions";
|
||||
import { Player } from "../src/Player";
|
||||
|
||||
//Keep track of last faction
|
||||
var lastFac = "";
|
||||
|
||||
/* InfiltrationBox.js */
|
||||
function infiltrationBoxClose() {
|
||||
var box = document.getElementById("infiltration-box-container");
|
||||
box.style.display = "none";
|
||||
}
|
||||
|
||||
function infiltrationBoxOpen() {
|
||||
var box = document.getElementById("infiltration-box-container");
|
||||
box.style.display = "flex";
|
||||
}
|
||||
|
||||
function infiltrationSetText(txt) {
|
||||
var textBox = document.getElementById("infiltration-box-text");
|
||||
textBox.innerHTML = txt;
|
||||
}
|
||||
|
||||
//ram argument is in GB
|
||||
function infiltrationBoxCreate(inst) {
|
||||
//Gain exp
|
||||
Player.gainHackingExp(inst.calcGainedHackingExp());
|
||||
Player.gainStrengthExp(inst.calcGainedStrengthExp());
|
||||
Player.gainDefenseExp(inst.calcGainedDefenseExp());
|
||||
Player.gainDexterityExp(inst.calcGainedDexterityExp());
|
||||
Player.gainAgilityExp(inst.calcGainedAgilityExp());
|
||||
Player.gainCharismaExp(inst.calcGainedCharismaExp());
|
||||
Player.gainIntelligenceExp(inst.calcGainedIntelligenceExp());
|
||||
|
||||
const expGainText = ["You gained:",
|
||||
`${numeralWrapper.formatExp(inst.calcGainedHackingExp(), 3)} hacking exp`,
|
||||
`${numeralWrapper.formatExp(inst.calcGainedStrengthExp(), 3)} str exp`,
|
||||
`${numeralWrapper.formatExp(inst.calcGainedDefenseExp(), 3)} def exp`,
|
||||
`${numeralWrapper.formatExp(inst.calcGainedDexterityExp(), 3)} dex exp`,
|
||||
`${numeralWrapper.formatExp(inst.calcGainedAgilityExp(), 3)} agi exp`,
|
||||
`${numeralWrapper.formatExp(inst.calcGainedCharismaExp(), 3)} cha exp`].join("\n");
|
||||
|
||||
var totalValue = 0;
|
||||
for (var i = 0; i < inst.secretsStolen.length; ++i) {
|
||||
totalValue += inst.secretsStolen[i];
|
||||
}
|
||||
if (totalValue == 0) {
|
||||
dialogBoxCreate("You successfully escaped the facility but you did not steal " +
|
||||
"anything of worth when infiltrating.<br><br>" + expGainText);
|
||||
return;
|
||||
}
|
||||
var facValue = totalValue * Player.faction_rep_mult *
|
||||
CONSTANTS.InfiltrationRepValue * BitNodeMultipliers.InfiltrationRep;
|
||||
var moneyValue = totalValue * CONSTANTS.InfiltrationMoneyValue * BitNodeMultipliers.InfiltrationMoney;
|
||||
infiltrationSetText("You can sell the classified documents and secrets " +
|
||||
"you stole from " + inst.companyName + " for <span class='money-gold'>" +
|
||||
numeralWrapper.formatMoney(moneyValue) + "</span> on the black market or you can give it " +
|
||||
"to a faction to gain <span class='light-yellow'>" + numeralWrapper.formatReputation(facValue) + " reputation</span> with " +
|
||||
"that faction.");
|
||||
var selector = document.getElementById("infiltration-faction-select");
|
||||
selector.innerHTML = "";
|
||||
for (let i = 0; i < Player.factions.length; ++i) {
|
||||
if (Player.factions[i] === "Bladeburners") { continue; }
|
||||
if (Player.inGang() && Player.gang.facName === Player.factions[i]) { continue; }
|
||||
selector.innerHTML += "<option value='" + Player.factions[i] +
|
||||
"'>" + Player.factions[i] + "</option>";
|
||||
}
|
||||
|
||||
//Set initial value, if applicable
|
||||
if (lastFac !== "") {
|
||||
for (let i = 0; i < selector.options.length; ++i) {
|
||||
if (selector.options[i].value === lastFac) {
|
||||
selector.selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sellButton = clearEventListeners("infiltration-box-sell");
|
||||
setTimeout(function() {
|
||||
sellButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
Player.gainMoney(moneyValue);
|
||||
Player.recordMoneySource(moneyValue, "infiltration");
|
||||
dialogBoxCreate("You sold the classified information you stole from " + inst.companyName +
|
||||
" for <span class='money-gold'>" + numeralWrapper.formatMoney(moneyValue) + "</span> on the black market!<br><br>" +
|
||||
expGainText);
|
||||
infiltrationBoxClose();
|
||||
return false;
|
||||
});
|
||||
}, 750);
|
||||
|
||||
var factionButton = clearEventListeners("infiltration-box-faction");
|
||||
setTimeout(function() {
|
||||
factionButton.addEventListener("click", function(e) {
|
||||
if (!e.isTrusted) {return false;}
|
||||
var facName = selector.options[selector.selectedIndex].value;
|
||||
lastFac = facName;
|
||||
var faction = Factions[facName];
|
||||
if (faction == null) {
|
||||
dialogBoxCreate("Error finding faction. This is a bug please report to developer");
|
||||
return false;
|
||||
}
|
||||
faction.playerReputation += facValue;
|
||||
dialogBoxCreate("You gave the classified information you stole from " + inst.companyName +
|
||||
" to " + facName + " and gained <span class='light-yellow'>" + numeralWrapper.formatReputation(facValue) + " reputation</span> with the faction. <br><br>" +
|
||||
expGainText);
|
||||
infiltrationBoxClose();
|
||||
return false;
|
||||
});
|
||||
}, 750);
|
||||
infiltrationBoxOpen();
|
||||
}
|
||||
|
||||
export {infiltrationBoxCreate};
|
Loading…
Reference in New Issue
Block a user