mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-29 19:13:49 +01:00
parent
39b4048603
commit
19f51b684b
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;
|
||||||
|
}
|
||||||
|
}
|
@ -150,38 +150,6 @@
|
|||||||
/* Infiltration */
|
/* Infiltration */
|
||||||
#infiltration-container {
|
#infiltration-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding: 6px;
|
margin: 5px;
|
||||||
|
width: 70%;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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
|
//# sourceMappingURL=engineStyle.bundle.js.map
|
70
dist/engineStyle.css
vendored
70
dist/engineStyle.css
vendored
@ -1355,33 +1355,8 @@ button {
|
|||||||
/* Infiltration */
|
/* Infiltration */
|
||||||
#infiltration-container {
|
#infiltration-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding: 6px; }
|
margin: 5px;
|
||||||
#infiltration-container span {
|
width: 70%; }
|
||||||
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; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Styling for the Augmentations UI. This is the page that displays all of the
|
* Styling for the Augmentations UI. This is the page that displays all of the
|
||||||
@ -5037,5 +5012,46 @@ html {
|
|||||||
padding: 6px;
|
padding: 6px;
|
||||||
width: 60%; }
|
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*/
|
/*# 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
20
index.html
20
index.html
@ -264,25 +264,7 @@
|
|||||||
<div id="location-container" class="generic-menupage-container">
|
<div id="location-container" class="generic-menupage-container">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="infiltration-container" class="generic-menupage-container">
|
<div id="infiltration-container" class="generic-fullscreen-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>
|
</div>
|
||||||
|
|
||||||
<div id="stock-market-container" class="generic-menupage-container">
|
<div id="stock-market-container" class="generic-menupage-container">
|
||||||
|
@ -31,7 +31,7 @@ type IState = {
|
|||||||
|
|
||||||
const inputStyleMarkup = {
|
const inputStyleMarkup = {
|
||||||
margin: "5px",
|
margin: "5px",
|
||||||
height: "26px"
|
height: "26px",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DonateOption extends React.Component<IProps, IState> {
|
export class DonateOption extends React.Component<IProps, IState> {
|
||||||
|
@ -8,7 +8,8 @@ export interface IEngine {
|
|||||||
loadFactionContent: () => void;
|
loadFactionContent: () => void;
|
||||||
loadFactionsContent: () => void;
|
loadFactionsContent: () => void;
|
||||||
loadGangContent: () => void;
|
loadGangContent: () => void;
|
||||||
loadInfiltrationContent: () => void;
|
loadInfiltrationContent: (name: string, difficulty: number, maxLevel: number) => void;
|
||||||
|
loadLocationContent: () => void;
|
||||||
loadMissionContent: () => void;
|
loadMissionContent: () => void;
|
||||||
loadResleevingContent: () => void;
|
loadResleevingContent: () => void;
|
||||||
loadStockMarketContent: () => 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 increase 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 '';
|
||||||
|
}
|
@ -6,8 +6,6 @@ import { LocationName } from "./data/LocationNames";
|
|||||||
import { LocationType } from "./LocationTypeEnum";
|
import { LocationType } from "./LocationTypeEnum";
|
||||||
|
|
||||||
interface IInfiltrationMetadata {
|
interface IInfiltrationMetadata {
|
||||||
baseRewardValue: number;
|
|
||||||
difficulty: number;
|
|
||||||
maxClearanceLevel: number;
|
maxClearanceLevel: number;
|
||||||
startingSecurityLevel: number;
|
startingSecurityLevel: number;
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 32,
|
maxClearanceLevel: 12,
|
||||||
difficulty: 4.4,
|
startingSecurityLevel: 8.18,
|
||||||
maxClearanceLevel: 50,
|
|
||||||
startingSecurityLevel: 1350,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumAeroCorp,
|
name: LocationName.AevumAeroCorp,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -22,10 +20,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 42,
|
maxClearanceLevel: 15,
|
||||||
difficulty: 4.1,
|
startingSecurityLevel: 8.19,
|
||||||
maxClearanceLevel: 60,
|
|
||||||
startingSecurityLevel: 1350,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumBachmanAndAssociates,
|
name: LocationName.AevumBachmanAndAssociates,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -33,10 +29,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 34,
|
maxClearanceLevel: 18,
|
||||||
difficulty: 3.6,
|
startingSecurityLevel: 9.55,
|
||||||
maxClearanceLevel: 75,
|
|
||||||
startingSecurityLevel: 1800,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumClarkeIncorporated,
|
name: LocationName.AevumClarkeIncorporated,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -51,10 +45,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 116,
|
maxClearanceLevel: 37,
|
||||||
difficulty: 6,
|
startingSecurityLevel: 17.02,
|
||||||
maxClearanceLevel: 150,
|
|
||||||
startingSecurityLevel: 4800,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumECorp,
|
name: LocationName.AevumECorp,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -64,10 +56,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 96,
|
maxClearanceLevel: 25,
|
||||||
difficulty: 6.2,
|
startingSecurityLevel: 15.54,
|
||||||
maxClearanceLevel: 100,
|
|
||||||
startingSecurityLevel: 4140,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumFulcrumTechnologies,
|
name: LocationName.AevumFulcrumTechnologies,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -77,10 +67,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 30,
|
maxClearanceLevel: 12,
|
||||||
difficulty: 3.95,
|
startingSecurityLevel: 7.89,
|
||||||
maxClearanceLevel: 50,
|
|
||||||
startingSecurityLevel: 1260,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumGalacticCybersystems,
|
name: LocationName.AevumGalacticCybersystems,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -88,10 +76,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 10,
|
maxClearanceLevel: 6,
|
||||||
difficulty: 1.4,
|
startingSecurityLevel: 3.29,
|
||||||
maxClearanceLevel: 25,
|
|
||||||
startingSecurityLevel: 144,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumNetLinkTechnologies,
|
name: LocationName.AevumNetLinkTechnologies,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -101,10 +87,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 18,
|
maxClearanceLevel: 6,
|
||||||
difficulty: 2.2,
|
startingSecurityLevel: 5.35,
|
||||||
maxClearanceLevel: 25,
|
|
||||||
startingSecurityLevel: 565,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumPolice,
|
name: LocationName.AevumPolice,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -112,10 +96,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 16,
|
maxClearanceLevel: 5,
|
||||||
difficulty: 1.9,
|
startingSecurityLevel: 5.02,
|
||||||
maxClearanceLevel: 20,
|
|
||||||
startingSecurityLevel: 485,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumRhoConstruction,
|
name: LocationName.AevumRhoConstruction,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -137,10 +119,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Aevum,
|
city: CityName.Aevum,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 20,
|
maxClearanceLevel: 7,
|
||||||
difficulty: 3,
|
startingSecurityLevel: 5.85,
|
||||||
maxClearanceLevel: 30,
|
|
||||||
startingSecurityLevel: 690,
|
|
||||||
},
|
},
|
||||||
name: LocationName.AevumWatchdogSecurity,
|
name: LocationName.AevumWatchdogSecurity,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -153,10 +133,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Chongqing,
|
city: CityName.Chongqing,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 100,
|
maxClearanceLevel: 25,
|
||||||
difficulty: 6.1,
|
startingSecurityLevel: 16.25,
|
||||||
maxClearanceLevel: 100,
|
|
||||||
startingSecurityLevel: 4450,
|
|
||||||
},
|
},
|
||||||
name: LocationName.ChongqingKuaiGongInternational,
|
name: LocationName.ChongqingKuaiGongInternational,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -164,10 +142,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Chongqing,
|
city: CityName.Chongqing,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 52,
|
maxClearanceLevel: 18,
|
||||||
difficulty: 6,
|
startingSecurityLevel: 12.59,
|
||||||
maxClearanceLevel: 75,
|
|
||||||
startingSecurityLevel: 2915,
|
|
||||||
},
|
},
|
||||||
name: LocationName.ChongqingSolarisSpaceSystems,
|
name: LocationName.ChongqingSolarisSpaceSystems,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -175,10 +151,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Ishima,
|
city: CityName.Ishima,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 20,
|
maxClearanceLevel: 12,
|
||||||
difficulty: 3.2,
|
startingSecurityLevel: 5.02,
|
||||||
maxClearanceLevel: 50,
|
|
||||||
startingSecurityLevel: 485,
|
|
||||||
},
|
},
|
||||||
name: LocationName.IshimaNovaMedical,
|
name: LocationName.IshimaNovaMedical,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -186,10 +160,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Ishima,
|
city: CityName.Ishima,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 10,
|
maxClearanceLevel: 10,
|
||||||
difficulty: 1.6,
|
startingSecurityLevel: 3.2,
|
||||||
maxClearanceLevel: 40,
|
|
||||||
startingSecurityLevel: 130,
|
|
||||||
},
|
},
|
||||||
name: LocationName.IshimaOmegaSoftware,
|
name: LocationName.IshimaOmegaSoftware,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -199,10 +171,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Ishima,
|
city: CityName.Ishima,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 24,
|
maxClearanceLevel: 25,
|
||||||
difficulty: 4.1,
|
startingSecurityLevel: 5.38,
|
||||||
maxClearanceLevel: 100,
|
|
||||||
startingSecurityLevel: 570,
|
|
||||||
},
|
},
|
||||||
name: LocationName.IshimaStormTechnologies,
|
name: LocationName.IshimaStormTechnologies,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -212,10 +182,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.NewTokyo,
|
city: CityName.NewTokyo,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 28,
|
maxClearanceLevel: 17,
|
||||||
difficulty: 4,
|
startingSecurityLevel: 7.18,
|
||||||
maxClearanceLevel: 70,
|
|
||||||
startingSecurityLevel: 1050,
|
|
||||||
},
|
},
|
||||||
name: LocationName.NewTokyoDefComm,
|
name: LocationName.NewTokyoDefComm,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -223,10 +191,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.NewTokyo,
|
city: CityName.NewTokyo,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 24,
|
maxClearanceLevel: 20,
|
||||||
difficulty: 3.8,
|
startingSecurityLevel: 5.9,
|
||||||
maxClearanceLevel: 80,
|
|
||||||
startingSecurityLevel: 700,
|
|
||||||
},
|
},
|
||||||
name: LocationName.NewTokyoGlobalPharmaceuticals,
|
name: LocationName.NewTokyoGlobalPharmaceuticals,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -239,10 +205,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.NewTokyo,
|
city: CityName.NewTokyo,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 22,
|
maxClearanceLevel: 25,
|
||||||
difficulty: 3.5,
|
startingSecurityLevel: 5.52,
|
||||||
maxClearanceLevel: 100,
|
|
||||||
startingSecurityLevel: 605,
|
|
||||||
},
|
},
|
||||||
name: LocationName.NewTokyoVitaLife,
|
name: LocationName.NewTokyoVitaLife,
|
||||||
types: [LocationType.Company, LocationType.Special],
|
types: [LocationType.Company, LocationType.Special],
|
||||||
@ -250,10 +214,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 14,
|
maxClearanceLevel: 10,
|
||||||
difficulty: 2.25,
|
startingSecurityLevel: 3.62,
|
||||||
maxClearanceLevel: 40,
|
|
||||||
startingSecurityLevel: 200,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12AlphaEnterprises,
|
name: LocationName.Sector12AlphaEnterprises,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -263,10 +225,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 46,
|
maxClearanceLevel: 25,
|
||||||
difficulty: 4.2,
|
startingSecurityLevel: 10.59,
|
||||||
maxClearanceLevel: 100,
|
|
||||||
startingSecurityLevel: 2160,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12BladeIndustries,
|
name: LocationName.Sector12BladeIndustries,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -279,10 +239,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 18,
|
maxClearanceLevel: 15,
|
||||||
difficulty: 2.5,
|
startingSecurityLevel: 4.66,
|
||||||
maxClearanceLevel: 60,
|
|
||||||
startingSecurityLevel: 405,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12CarmichaelSecurity,
|
name: LocationName.Sector12CarmichaelSecurity,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -295,10 +253,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 24,
|
maxClearanceLevel: 12,
|
||||||
difficulty: 4.3,
|
startingSecurityLevel: 5.9,
|
||||||
maxClearanceLevel: 50,
|
|
||||||
startingSecurityLevel: 700,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12DeltaOne,
|
name: LocationName.Sector12DeltaOne,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -311,10 +267,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 58,
|
maxClearanceLevel: 25,
|
||||||
difficulty: 7,
|
startingSecurityLevel: 8.18,
|
||||||
maxClearanceLevel: 100,
|
|
||||||
startingSecurityLevel: 1350,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12FourSigma,
|
name: LocationName.Sector12FourSigma,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -322,10 +276,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 32,
|
maxClearanceLevel: 17,
|
||||||
difficulty: 5.4,
|
startingSecurityLevel: 6.02,
|
||||||
maxClearanceLevel: 70,
|
|
||||||
startingSecurityLevel: 730,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12IcarusMicrosystems,
|
name: LocationName.Sector12IcarusMicrosystems,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -340,10 +292,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 8,
|
maxClearanceLevel: 5,
|
||||||
difficulty: 1.8,
|
startingSecurityLevel: 3.13,
|
||||||
maxClearanceLevel: 20,
|
|
||||||
startingSecurityLevel: 120,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12JoesGuns,
|
name: LocationName.Sector12JoesGuns,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -351,10 +301,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 114,
|
maxClearanceLevel: 31,
|
||||||
difficulty: 6.75,
|
startingSecurityLevel: 16.36,
|
||||||
maxClearanceLevel: 125,
|
|
||||||
startingSecurityLevel: 4500,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12MegaCorp,
|
name: LocationName.Sector12MegaCorp,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -381,10 +329,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Sector12,
|
city: CityName.Sector12,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 24,
|
maxClearanceLevel: 12,
|
||||||
difficulty: 4.3,
|
startingSecurityLevel: 5.9,
|
||||||
maxClearanceLevel: 50,
|
|
||||||
startingSecurityLevel: 700,
|
|
||||||
},
|
},
|
||||||
name: LocationName.Sector12UniversalEnergy,
|
name: LocationName.Sector12UniversalEnergy,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -392,10 +338,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Volhaven,
|
city: CityName.Volhaven,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 12,
|
maxClearanceLevel: 15,
|
||||||
difficulty: 2.1,
|
startingSecurityLevel: 3.59,
|
||||||
maxClearanceLevel: 60,
|
|
||||||
startingSecurityLevel: 195,
|
|
||||||
},
|
},
|
||||||
name: LocationName.VolhavenCompuTek,
|
name: LocationName.VolhavenCompuTek,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -405,10 +349,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Volhaven,
|
city: CityName.Volhaven,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 28,
|
maxClearanceLevel: 18,
|
||||||
difficulty: 3,
|
startingSecurityLevel: 7.28,
|
||||||
maxClearanceLevel: 75,
|
|
||||||
startingSecurityLevel: 1080,
|
|
||||||
},
|
},
|
||||||
name: LocationName.VolhavenHeliosLabs,
|
name: LocationName.VolhavenHeliosLabs,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -416,10 +358,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Volhaven,
|
city: CityName.Volhaven,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 14,
|
maxClearanceLevel: 15,
|
||||||
difficulty: 2,
|
startingSecurityLevel: 4.35,
|
||||||
maxClearanceLevel: 60,
|
|
||||||
startingSecurityLevel: 340,
|
|
||||||
},
|
},
|
||||||
name: LocationName.VolhavenLexoCorp,
|
name: LocationName.VolhavenLexoCorp,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -434,10 +374,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Volhaven,
|
city: CityName.Volhaven,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 56,
|
maxClearanceLevel: 50,
|
||||||
difficulty: 6.8,
|
startingSecurityLevel: 8.53,
|
||||||
maxClearanceLevel: 200,
|
|
||||||
startingSecurityLevel: 1460,
|
|
||||||
},
|
},
|
||||||
name: LocationName.VolhavenNWO,
|
name: LocationName.VolhavenNWO,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -445,10 +383,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Volhaven,
|
city: CityName.Volhaven,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 44,
|
maxClearanceLevel: 25,
|
||||||
difficulty: 4.4,
|
startingSecurityLevel: 7.74,
|
||||||
maxClearanceLevel: 100,
|
|
||||||
startingSecurityLevel: 1215,
|
|
||||||
},
|
},
|
||||||
name: LocationName.VolhavenOmniTekIncorporated,
|
name: LocationName.VolhavenOmniTekIncorporated,
|
||||||
types: [LocationType.Company, LocationType.TechVendor],
|
types: [LocationType.Company, LocationType.TechVendor],
|
||||||
@ -458,10 +394,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Volhaven,
|
city: CityName.Volhaven,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 28,
|
maxClearanceLevel: 22,
|
||||||
difficulty: 4.9,
|
startingSecurityLevel: 6,
|
||||||
maxClearanceLevel: 90,
|
|
||||||
startingSecurityLevel: 725,
|
|
||||||
},
|
},
|
||||||
name: LocationName.VolhavenOmniaCybersystems,
|
name: LocationName.VolhavenOmniaCybersystems,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -469,10 +403,8 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
{
|
{
|
||||||
city: CityName.Volhaven,
|
city: CityName.Volhaven,
|
||||||
infiltrationData: {
|
infiltrationData: {
|
||||||
baseRewardValue: 18,
|
maxClearanceLevel: 18,
|
||||||
difficulty: 2.4,
|
startingSecurityLevel: 4.77,
|
||||||
maxClearanceLevel: 75,
|
|
||||||
startingSecurityLevel: 430,
|
|
||||||
},
|
},
|
||||||
name: LocationName.VolhavenSysCoreSecurities,
|
name: LocationName.VolhavenSysCoreSecurities,
|
||||||
types: [LocationType.Company],
|
types: [LocationType.Company],
|
||||||
@ -505,3 +437,10 @@ export const LocationsMetadata: IConstructorParams[] = [
|
|||||||
types: [LocationType.StockMarket],
|
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 { LocationName } from "../data/LocationNames";
|
||||||
|
|
||||||
import { IEngine } from "../../IEngine";
|
import { IEngine } from "../../IEngine";
|
||||||
import { beginInfiltration } from "../../Infiltration";
|
|
||||||
|
|
||||||
import { Companies } from "../../Company/Companies";
|
import { Companies } from "../../Company/Companies";
|
||||||
import { Company } from "../../Company/Company";
|
import { Company } from "../../Company/Company";
|
||||||
@ -193,13 +192,15 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
|||||||
startInfiltration(e: React.MouseEvent<HTMLElement>): void {
|
startInfiltration(e: React.MouseEvent<HTMLElement>): void {
|
||||||
if (!e.isTrusted) { return; }
|
if (!e.isTrusted) { return; }
|
||||||
const loc = this.location;
|
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;
|
const data = loc.infiltrationData;
|
||||||
if (data == null) { return; }
|
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 {
|
work(e: React.MouseEvent<HTMLElement>): void {
|
||||||
|
@ -176,6 +176,7 @@ export interface IPlayer {
|
|||||||
startGang(facName: string, isHacking: boolean): void;
|
startGang(facName: string, isHacking: boolean): void;
|
||||||
startWork(companyName: string): void;
|
startWork(companyName: string): void;
|
||||||
startWorkPartTime(companyName: string): void;
|
startWorkPartTime(companyName: string): void;
|
||||||
|
takeDamage(amt: number): boolean;
|
||||||
travel(to: CityName): boolean;
|
travel(to: CityName): boolean;
|
||||||
giveExploit(exploit: Exploit): void;
|
giveExploit(exploit: Exploit): void;
|
||||||
queryStatFromString(str: string): number;
|
queryStatFromString(str: string): number;
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
processPassiveFactionRepGain,
|
processPassiveFactionRepGain,
|
||||||
inviteToFaction,
|
inviteToFaction,
|
||||||
} from "./Faction/FactionHelpers";
|
} from "./Faction/FactionHelpers";
|
||||||
|
import { displayInfiltrationContent } from "./Infiltration/Helper";
|
||||||
import {
|
import {
|
||||||
getHackingWorkRepGain,
|
getHackingWorkRepGain,
|
||||||
getFactionSecurityWorkRepGain,
|
getFactionSecurityWorkRepGain,
|
||||||
@ -419,10 +420,13 @@ const Engine = {
|
|||||||
routing.navigateTo(Page.CinematicText);
|
routing.navigateTo(Page.CinematicText);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadInfiltrationContent: function() {
|
loadInfiltrationContent: function(name, difficulty, maxLevel) {
|
||||||
Engine.hideAllContent();
|
Engine.hideAllContent();
|
||||||
|
const mainMenu = document.getElementById("mainmenu-container");
|
||||||
|
mainMenu.style.visibility = "hidden";
|
||||||
Engine.Display.infiltrationContent.style.display = "block";
|
Engine.Display.infiltrationContent.style.display = "block";
|
||||||
routing.navigateTo(Page.Infiltration);
|
routing.navigateTo(Page.Infiltration);
|
||||||
|
displayInfiltrationContent(this, Player, name, difficulty, maxLevel);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadStockMarketContent: function() {
|
loadStockMarketContent: function() {
|
||||||
@ -501,6 +505,8 @@ const Engine = {
|
|||||||
|
|
||||||
Engine.Display.activeScriptsContent.style.display = "none";
|
Engine.Display.activeScriptsContent.style.display = "none";
|
||||||
ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent);
|
ReactDOM.unmountComponentAtNode(Engine.Display.activeScriptsContent);
|
||||||
|
Engine.Display.infiltrationContent.style.display = "none";
|
||||||
|
ReactDOM.unmountComponentAtNode(Engine.Display.infiltrationContent);
|
||||||
|
|
||||||
clearHacknetNodesUI();
|
clearHacknetNodesUI();
|
||||||
Engine.Display.createProgramContent.style.display = "none";
|
Engine.Display.createProgramContent.style.display = "none";
|
||||||
@ -522,7 +528,6 @@ const Engine = {
|
|||||||
Engine.Display.workInProgressContent.style.display = "none";
|
Engine.Display.workInProgressContent.style.display = "none";
|
||||||
Engine.Display.redPillContent.style.display = "none";
|
Engine.Display.redPillContent.style.display = "none";
|
||||||
Engine.Display.cinematicTextContent.style.display = "none";
|
Engine.Display.cinematicTextContent.style.display = "none";
|
||||||
Engine.Display.infiltrationContent.style.display = "none";
|
|
||||||
Engine.Display.stockMarketContent.style.display = "none";
|
Engine.Display.stockMarketContent.style.display = "none";
|
||||||
Engine.Display.missionContent.style.display = "none";
|
Engine.Display.missionContent.style.display = "none";
|
||||||
if (document.getElementById("gang-container")) {
|
if (document.getElementById("gang-container")) {
|
||||||
|
@ -31,3 +31,4 @@ import "../css/grid.min.css";
|
|||||||
import "../css/dev-menu.css";
|
import "../css/dev-menu.css";
|
||||||
import "../css/casino.scss";
|
import "../css/casino.scss";
|
||||||
import "../css/milestones.scss";
|
import "../css/milestones.scss";
|
||||||
|
import "../css/infiltration.scss";
|
@ -277,25 +277,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
|||||||
<div id="location-container" class="generic-menupage-container">
|
<div id="location-container" class="generic-menupage-container">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="infiltration-container" class="generic-menupage-container">
|
<div id="infiltration-container" class="generic-fullscreen-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>
|
</div>
|
||||||
|
|
||||||
<div id="stock-market-container" class="generic-menupage-container">
|
<div id="stock-market-container" class="generic-menupage-container">
|
||||||
|
@ -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