mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 07:33:48 +01:00
v0.51.6 (#905)
* Make command `cd` without arguments an alias for `cd /` (#853) In most shells `cd` without arguments takes you to the home directory of the current user. I keep trying to do this due to muscle memory from working in terminals, so I figured I'd make it do something useful. There is no home directory in the game, but going to / is the closest thing we have, since that is the starting point for the user in the game. * Add new `backdoor` terminal command (#852) * Add the backdoor command to the terminal This command will perform a manual hack without rewarding money. It will be used for the story, mainly for faction hacking tests * Add tab completion for backdoor command * Add help text for backdoor command * Change condition syntax to be more consistent with others * Extract reused code block so it is always called after actions * Update documentation for new backdoor command Modified references to manual hack as it isn't for factions anymore * Remove extra parenthesis * Rename manuallyHacked to backdoorInstalled * Fix typo * Change faction test messages to use backdoor instad of hack * Rename more instances of manuallyHacked * fixed typo in helptext of darkweb buy (#858) * Fix typos and unify descriptions of augmentations (#859) Made an attempt to... - give all "+rep% company/faction" the same text - make all augmentations with a single effect use a single line to describe the effect - make all effects end with a period * Made Cashroot starter kit display its tooltip with the money formatted properly and in gold * fix typo in docs (#860) * Initial code for Casino Card Deck implementation * Casino Blackjack Implementation * Update some tools (eslint, typescript) * Blackjack code cleanup * Update README_contribution * Update ScriptHelpers.js (#861) expand error message * More augmentation typo fixes (#862) * Add Netscript function getCurrentScript (#856) Add netscript function that returns the current script. * Added milestones menu to guide new players. (#865) Milestone menu * fix typos in milestones (#866) Co-authored-by: sschmidTU <s.schmid@phonicscore.com> * Corrupt location title when backdoor is installed (#864) * Add corruptableText component * Corrupt location title if backdoor is installed * Formatting * Add helper to check value of backdoorInstalled Helper could be oneline but it would make it less readable * Fix some formatting * Add settings option to disable text effects * Import useState * getRunningScript (#867) * Replaced getCurrentScript with getRunningScript * Bunch of smaller fixes (#904) Fix #884 Fix #879 Fix #878 Fix #876 Fix #874 Fix #873 Fix #887 Fix #891 Fix #895 * rework the early servers to be more noob friendly (#903) * v0.51.6 Co-authored-by: Andreas Eriksson <2691182+AndreasTPC@users.noreply.github.com> Co-authored-by: Jack <jackdewinter1@gmail.com> Co-authored-by: Teun Pronk <5228255+Crownie88@users.noreply.github.com> Co-authored-by: Pimvgd <Pimvgd@gmail.com> Co-authored-by: Daniel Xie <daniel.xie@flockfreight.com> Co-authored-by: Simon <33069673+sschmidTU@users.noreply.github.com> Co-authored-by: sschmidTU <s.schmid@phonicscore.com>
This commit is contained in:
parent
b2aafea656
commit
52a80ad236
257
.eslintrc.js
257
.eslintrc.js
@ -2,91 +2,104 @@ module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": false
|
||||
"es6": false,
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
"experimentalObjectRestSpread": true,
|
||||
},
|
||||
},
|
||||
"plugins": [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
"rules": {
|
||||
"accessor-pairs": [
|
||||
"error",
|
||||
{
|
||||
"setWithoutGet": true,
|
||||
"getWithoutSet": false
|
||||
}
|
||||
"getWithoutSet": false,
|
||||
},
|
||||
],
|
||||
"array-bracket-newline": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"array-bracket-spacing": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"array-callback-return": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"array-element-newline": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"arrow-body-style": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"arrow-parens": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"arrow-spacing": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"block-scoped-var": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"block-spacing": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"brace-style": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"callback-return": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"camelcase": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"capitalized-comments": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"class-methods-use-this": [
|
||||
"error"
|
||||
"off",
|
||||
],
|
||||
"comma-dangle": [
|
||||
"off"
|
||||
"error", {
|
||||
"arrays": "always-multiline",
|
||||
"objects": "always-multiline",
|
||||
"imports": "always-multiline",
|
||||
"exports": "always-multiline",
|
||||
"functions": "always-multiline",
|
||||
}
|
||||
],
|
||||
"comma-spacing": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"comma-style": [
|
||||
"error",
|
||||
"last"
|
||||
"last",
|
||||
],
|
||||
"complexity": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"computed-property-spacing": [
|
||||
"off",
|
||||
"never"
|
||||
"never",
|
||||
],
|
||||
"consistent-return": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"consistent-this": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"constructor-super": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"curly": [
|
||||
"off"
|
||||
@ -99,203 +112,202 @@ module.exports = {
|
||||
"property"
|
||||
],
|
||||
"dot-notation": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"eol-last": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"eqeqeq": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"for-direction": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"func-call-spacing": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"func-name-matching": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"func-names": [
|
||||
"off",
|
||||
"never"
|
||||
"never",
|
||||
],
|
||||
"func-style": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"function-paren-newline": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"generator-star-spacing": [
|
||||
"error",
|
||||
"before"
|
||||
"before",
|
||||
],
|
||||
"getter-return": [
|
||||
"error",
|
||||
{
|
||||
"allowImplicit": false
|
||||
}
|
||||
"allowImplicit": false,
|
||||
},
|
||||
],
|
||||
"global-require": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"guard-for-in": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"handle-callback-err": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"id-blacklist": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"id-length": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"id-match": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"implicit-arrow-linebreak": [
|
||||
"error",
|
||||
"beside"
|
||||
"beside",
|
||||
],
|
||||
"indent": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"indent-legacy": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"init-declarations": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"jsx-quotes": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"key-spacing": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"keyword-spacing": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"line-comment-position": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"windows"
|
||||
"off", // Line endings automatically converted to LF on git commit so probably shouldn't care about it here
|
||||
],
|
||||
"lines-around-comment": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"lines-around-directive": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"lines-between-class-members": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"max-depth": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"max-len": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"max-lines": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"max-nested-callbacks": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"max-params": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"max-statements": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"max-statements-per-line": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"multiline-comment-style": [
|
||||
"off",
|
||||
"starred-block"
|
||||
"starred-block",
|
||||
],
|
||||
"multiline-ternary": [
|
||||
"off",
|
||||
"never"
|
||||
"never",
|
||||
],
|
||||
"new-cap": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"new-parens": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"newline-after-var": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"newline-before-return": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"newline-per-chained-call": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"no-alert": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-array-constructor": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-await-in-loop": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-bitwise": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"no-buffer-constructor": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-caller": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-case-declarations": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-catch-shadow": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-class-assign": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-compare-neg-zero": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-cond-assign": [
|
||||
"off",
|
||||
"except-parens"
|
||||
"except-parens",
|
||||
],
|
||||
"no-confusing-arrow": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-console": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"no-const-assign": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-constant-condition": [
|
||||
"error",
|
||||
{
|
||||
"checkLoops": false
|
||||
}
|
||||
"checkLoops": false,
|
||||
},
|
||||
],
|
||||
"no-continue": [
|
||||
"off"
|
||||
"off",
|
||||
],
|
||||
"no-control-regex": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-debugger": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-delete-var": [
|
||||
"error"
|
||||
"error",
|
||||
],
|
||||
"no-div-regex": [
|
||||
"error"
|
||||
@ -346,11 +358,7 @@ module.exports = {
|
||||
"error"
|
||||
],
|
||||
"no-extra-parens": [
|
||||
"error",
|
||||
"all",
|
||||
{
|
||||
"conditionalAssign": false
|
||||
}
|
||||
"off"
|
||||
],
|
||||
"no-extra-semi": [
|
||||
"off"
|
||||
@ -367,9 +375,6 @@ module.exports = {
|
||||
"no-extra-label": [
|
||||
"error"
|
||||
],
|
||||
"no-extra-parens": [
|
||||
"off"
|
||||
],
|
||||
"no-fallthrough": [
|
||||
"off"
|
||||
],
|
||||
@ -853,5 +858,53 @@ module.exports = {
|
||||
"error",
|
||||
"never"
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
// TypeScript configuration
|
||||
"files": [ "**/*.ts", "**/*.tsx" ],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [ "@typescript-eslint" ],
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
"rules": {
|
||||
"lines-between-class-members": "off",
|
||||
"no-empty-pattern": "off",
|
||||
"no-useless-constructor": [
|
||||
"off", // Valid for typescript due to property ctor shorthand
|
||||
],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": ["error", {
|
||||
"allowExpressions": true,
|
||||
}],
|
||||
"@typescript-eslint/member-delimiter-style": ["error", {
|
||||
"multiline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": true,
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false,
|
||||
}
|
||||
}],
|
||||
"@typescript-eslint/member-ordering": ["error", {
|
||||
"default": [
|
||||
"signature",
|
||||
"static-field",
|
||||
"instance-field",
|
||||
"abstract-field",
|
||||
"constructor",
|
||||
"instance-method",
|
||||
"abstract-method",
|
||||
"static-method",
|
||||
]
|
||||
}],
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
@ -7,4 +7,17 @@ Update the following
|
||||
- `doc/source/conf.py` `version` and `release`
|
||||
- `doc/source/changelog.rst`
|
||||
- post to discord
|
||||
- post to reddit.com/r/Bitburner
|
||||
- post to reddit.com/r/Bitburner
|
||||
|
||||
Deploying `dev` to the Beta Branch
|
||||
----------------------------------
|
||||
|
||||
TODO
|
||||
|
||||
Development Workflow Best Practices
|
||||
-----------------------------------
|
||||
|
||||
- Work in a new branch forked from the `dev` branch to isolate your new code
|
||||
- Keep code-changes on a branch as small as possible. This makes it easier for code review. Each branch should be its own independent feature.
|
||||
- Regularly rebase your branch against `dev` to make sure you have the latest updates pulled.
|
||||
- When merging, always merge your branch into `dev`. When releasing a new update, then merge `dev` into `master`
|
||||
|
24
css/casino.scss
Normal file
24
css/casino.scss
Normal file
@ -0,0 +1,24 @@
|
||||
.casino-card {
|
||||
padding: 10px;
|
||||
border: solid 1px #808080;
|
||||
background-color: white;
|
||||
display: inline-block;
|
||||
border-radius: 10px;
|
||||
font-size: 14pt;
|
||||
text-align: center;
|
||||
margin: 3px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.casino-card .value {
|
||||
font-size:15pt;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.casino-card.red {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.casino-card.black {
|
||||
color: black;
|
||||
}
|
5
css/milestones.scss
Normal file
5
css/milestones.scss
Normal file
@ -0,0 +1,5 @@
|
||||
#milestones-container {
|
||||
position: fixed;
|
||||
padding: 6px;
|
||||
width: 60%;
|
||||
}
|
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([402,0]),o()}({345:function(n,t,o){},347:function(n,t,o){},349:function(n,t,o){},351:function(n,t,o){},353:function(n,t,o){},355:function(n,t,o){},357:function(n,t,o){},359:function(n,t,o){},361:function(n,t,o){},363:function(n,t,o){},365:function(n,t,o){},367:function(n,t,o){},369:function(n,t,o){},371:function(n,t,o){},373:function(n,t,o){},375:function(n,t,o){},377:function(n,t,o){},379:function(n,t,o){},381:function(n,t,o){},383:function(n,t,o){},385:function(n,t,o){},387:function(n,t,o){},389:function(n,t,o){},391:function(n,t,o){},393:function(n,t,o){},395:function(n,t,o){},397:function(n,t,o){},399:function(n,t,o){},402:function(n,t,o){"use strict";o.r(t);o(401),o(399),o(397),o(395),o(393),o(391),o(389),o(387),o(385),o(383),o(381),o(379),o(377),o(375),o(373),o(371),o(369),o(367),o(365),o(363),o(361),o(359),o(357),o(355),o(353),o(351),o(349),o(347),o(345)}});
|
||||
!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([842,0]),o()}({781:function(n,t,o){},783:function(n,t,o){},785:function(n,t,o){},787:function(n,t,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){},842:function(n,t,o){"use strict";o.r(t);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),o(787),o(785),o(783),o(781)}});
|
||||
//# sourceMappingURL=engineStyle.bundle.js.map
|
26
dist/engineStyle.css
vendored
26
dist/engineStyle.css
vendored
@ -5007,5 +5007,31 @@ html {
|
||||
margin-left: 0px;
|
||||
margin-right: 0px; }
|
||||
|
||||
.casino-card {
|
||||
padding: 10px;
|
||||
border: solid 1px #808080;
|
||||
background-color: white;
|
||||
display: inline-block;
|
||||
border-radius: 10px;
|
||||
font-size: 14pt;
|
||||
text-align: center;
|
||||
margin: 3px;
|
||||
font-weight: bold; }
|
||||
|
||||
.casino-card .value {
|
||||
font-size: 15pt;
|
||||
font-family: sans-serif; }
|
||||
|
||||
.casino-card.red {
|
||||
color: red; }
|
||||
|
||||
.casino-card.black {
|
||||
color: black; }
|
||||
|
||||
#milestones-container {
|
||||
position: fixed;
|
||||
padding: 6px;
|
||||
width: 60%; }
|
||||
|
||||
|
||||
/*# sourceMappingURL=engineStyle.css.map*/
|
78
dist/vendor.bundle.js
vendored
78
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -35,7 +35,7 @@ List of Factions and their Requirements
|
||||
| Early Game | Faction Name | Requirements | Joining this Faction prevents |
|
||||
| Factions | | | you from joining: |
|
||||
+ +----------------+-----------------------------------------+-------------------------------+
|
||||
| | CyberSec | * Hack CSEC Manually | |
|
||||
| | CyberSec | * Install a backdoor on the CSEC server | |
|
||||
+ +----------------+-----------------------------------------+-------------------------------+
|
||||
| | Tian Di Hui | * $1m | |
|
||||
| | | * Hacking Level 50 | |
|
||||
@ -74,11 +74,17 @@ List of Factions and their Requirements
|
||||
| | | | * New Tokyo |
|
||||
| | | | * Ishima |
|
||||
+---------------------+----------------+-----------------------------------------+-------------------------------+
|
||||
| Hacking | NiteSec | * Hack avmnite-02h manually | |
|
||||
| Hacking | NiteSec | * Install a backdoor on the avmnite-02h | |
|
||||
| Groups | | server | |
|
||||
| | | * Home Computer RAM of at least 32GB | |
|
||||
+ +----------------+-----------------------------------------+-------------------------------+
|
||||
| | The Black Hand | * Hack I.I.I.I manually | |
|
||||
| | The Black Hand | * Install a backdoor on the I.I.I.I | |
|
||||
| | | server | |
|
||||
| | | * Home Computer RAM of at least 64GB | |
|
||||
+ +----------------+-----------------------------------------+-------------------------------+
|
||||
| | Bitrunners | * Hack run4theh111z manually | |
|
||||
| | Bitrunners | * Install a backdoor on the run4theh111z| |
|
||||
| | | server | |
|
||||
| | | * Home Computer RAM of at least 128GB | |
|
||||
+---------------------+----------------+-----------------------------------------+-------------------------------+
|
||||
| Megacorporations | ECorp | * Have 200k reputation with | |
|
||||
| | | the Corporation | |
|
||||
@ -109,7 +115,8 @@ List of Factions and their Requirements
|
||||
+ +----------------+-----------------------------------------+-------------------------------+
|
||||
| | Fulcrum Secret | * Have 250k reputation with | |
|
||||
| | Technologies | the Corporation | |
|
||||
| | | * Hack fulcrumassets manually | |
|
||||
| | | * Install a backdoor on the | |
|
||||
| | | fulcrumassets server | |
|
||||
+---------------------+----------------+-----------------------------------------+-------------------------------+
|
||||
| Criminal | Slum Snakes | * All Combat Stats of 30 | |
|
||||
| Organizations | | * -9 Karma | |
|
||||
|
@ -148,6 +148,14 @@ has root access, what ports are opened/closed, and also hacking-related informat
|
||||
such as an estimated chance to successfully hack, an estimate of how much money is
|
||||
available on the server, etc.
|
||||
|
||||
backdoor
|
||||
^^^^^^^^
|
||||
|
||||
Installs a backdoor on the current server. Root access is required to do this.
|
||||
|
||||
Servers will give different bonuses when you install a backdoor.
|
||||
This can pass faction tests or give bonsues such as discounts from companies.
|
||||
|
||||
buy
|
||||
^^^
|
||||
|
||||
|
@ -3,10 +3,57 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
|
||||
v0.51.6 - 2021-04-28 Backdoor! (Community)
|
||||
-------
|
||||
|
||||
**Backdoor**
|
||||
|
||||
* a new terminal command, backdoor, has been added to help differentiate
|
||||
between the terminal hack command and the netscript hack function. (@dewint)
|
||||
|
||||
**Milestones**
|
||||
|
||||
* A new tab under the Help menu has been added to guide players through the
|
||||
game.
|
||||
|
||||
**Casino**
|
||||
|
||||
* Blackjack has been added (@BigD)
|
||||
|
||||
**Netscript**
|
||||
|
||||
* 'prompt' now converts input to JSON.
|
||||
* 'getRunningScript' is a new netscript function that returns a bunch of
|
||||
data related to a running script.
|
||||
|
||||
**Coding contracts**
|
||||
|
||||
* trivial puzzles should no longer appear.
|
||||
|
||||
**Infiltration**
|
||||
|
||||
* All numbers are formatted like the rest of the game.
|
||||
|
||||
**Misc.**
|
||||
|
||||
* Server security is capped at 100.
|
||||
* Added option to quit a job.
|
||||
* 'cd' no longer works on unexistent folders.
|
||||
* cd with no arguments brings you back to top level folder (@Andreas)
|
||||
* 'softReset' documentation udpated.
|
||||
* Money tracker now accounts for going to the hospital manually.
|
||||
* codemirror is now the default editor (for new save files)
|
||||
* fix typo in dark web help text (@Rodeth)
|
||||
* so many documentation and typos fixes (@Pimgd)
|
||||
* A corruption visual effect has been added to location with servers that
|
||||
have backdoor installed. (@dewint)
|
||||
|
||||
|
||||
v0.51.5 - 2021-04-20 Flags! (hydroflame)
|
||||
----------------------------------------
|
||||
|
||||
Netscript
|
||||
**Netscript**
|
||||
|
||||
* 'flags' is a new function that helps script handle flags.
|
||||
This is subject to change if it doesn't meet the need of the players.
|
||||
@ -16,11 +63,11 @@ Netscript
|
||||
* 'isRunning' hostname defaults to current server.
|
||||
* 'isRunning' now works with pid as first argument.
|
||||
|
||||
Gang
|
||||
**Gang**
|
||||
|
||||
* Nerfed ascension mechanic once again :(
|
||||
|
||||
Misc.
|
||||
**Misc.**
|
||||
|
||||
* Souce-File typo fix
|
||||
* Fix 'while you were away' screen.
|
||||
@ -29,28 +76,28 @@ Misc.
|
||||
v0.51.4 - 2021-04-19 Manual hacking is fun (hydroflame)
|
||||
-------------------------------------------------------
|
||||
|
||||
Manual hacking
|
||||
**Manual hacking**
|
||||
|
||||
* These bonus require an install or a soft reset to take effect.
|
||||
* Manual hacking gyms and university gives you a 10% discount.
|
||||
* Manual hacking a corporation server decreases the penalty for leaving work
|
||||
early.
|
||||
|
||||
BladeBurner
|
||||
**BladeBurner**
|
||||
|
||||
* nerfed int exp gained.
|
||||
|
||||
Documentation
|
||||
**Documentation**
|
||||
|
||||
* purchaseServer specifies what happens on failure.
|
||||
* Fixed typo in recommended bitnode page.
|
||||
* Removed misleading ram requirements for hacking factions.
|
||||
|
||||
Netscript
|
||||
**Netscript**
|
||||
|
||||
* growthAnalyze handles Infinity correctly.
|
||||
|
||||
Misc.
|
||||
**Misc.**
|
||||
|
||||
* Faction Augmentation will list how much reputation is required even after
|
||||
that goal has been reached.
|
||||
|
@ -634,7 +634,7 @@ This tells me that I can reach :code:`CSEC` by going through :code:`iron-gym`::
|
||||
Make sure you notice the required hacking skill for the :code:`CSEC` server.
|
||||
This is a random value between 51 and 60. Although you receive the message
|
||||
from CSEC once you hit 50 hacking, you cannot actually pass their test
|
||||
until your hacking is high enough to hack their server.
|
||||
until your hacking is high enough to install a backdoor on their server.
|
||||
|
||||
After you are connected to the :code:`CSEC` server, you can hack it. Note that this
|
||||
server requires one open port in order to gain root access. We can open the SSH port
|
||||
@ -642,10 +642,10 @@ using the :code:`BruteSSH.exe` program we created earlier. In |Terminal|::
|
||||
|
||||
$ run BruteSSH.exe
|
||||
$ run NUKE.exe
|
||||
$ hack
|
||||
$ backdoor
|
||||
|
||||
Keep hacking the server until you are successful. After you successfully hack it, you should
|
||||
receive a faction invitation from |CyberSec| shortly afterwards. Accept it. If you accidentally
|
||||
After you successfully install the backdoor, you should receive a faction
|
||||
invitation from |CyberSec| shortly afterwards. Accept it. If you accidentally
|
||||
reject the invitation, that's okay. Just go to the :code:`Factions` tab
|
||||
(|Keyboard shortcut| Alt + f) and you should see an option that lets you
|
||||
accept the invitation.
|
||||
|
@ -293,7 +293,7 @@ Source-File
|
||||
* Level 3: Grants a highly-upgraded Hacknet Server when entering a new BitNode
|
||||
|
||||
(Note that the Level 3 effect of this Source-File only applies when entering a new BitNode, NOT
|
||||
when installing Augmentation
|
||||
when installing Augmentations.)
|
||||
|
||||
Difficulty
|
||||
Hard
|
||||
|
@ -27,7 +27,7 @@ getServer() Netscript Function
|
||||
sshPortOpen
|
||||
baseDifficulty
|
||||
hackDifficulty
|
||||
manuallyHacked
|
||||
backdoorInstalled
|
||||
minDifficulty
|
||||
moneyAvailable
|
||||
moneyMax
|
||||
|
88
doc/source/netscript/basicfunctions/getRunningScript.rst
Normal file
88
doc/source/netscript/basicfunctions/getRunningScript.rst
Normal file
@ -0,0 +1,88 @@
|
||||
getRunningScript() Netscript Function
|
||||
=====================================
|
||||
|
||||
.. js:function:: getRunningScript()
|
||||
|
||||
:RAM cost: 0.3 GB
|
||||
:returns: Script object or null if not found
|
||||
|
||||
The object has the following properties:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
// Script arguments
|
||||
args
|
||||
|
||||
// Script filename
|
||||
filename
|
||||
|
||||
// This script's logs. An array of log entries
|
||||
logs
|
||||
|
||||
// Flag indicating whether the logs have been updated since
|
||||
// the last time the UI was updated
|
||||
logUpd
|
||||
|
||||
// Total amount of hacking experience earned from this script when offline
|
||||
offlineExpGained
|
||||
|
||||
// Total amount of money made by this script when offline
|
||||
offlineMoneyMade
|
||||
|
||||
// Number of seconds that the script has been running offline
|
||||
offlineRunningTime
|
||||
|
||||
// Total amount of hacking experience earned from this script when online
|
||||
onlineExpGained
|
||||
|
||||
// Total amount of money made by this script when online
|
||||
onlineMoneyMade
|
||||
|
||||
// Number of seconds that this script has been running online
|
||||
onlineRunningTime
|
||||
|
||||
// Process ID.
|
||||
pid
|
||||
|
||||
// How much RAM this script uses for ONE thread
|
||||
ramUsage
|
||||
|
||||
// IP of the server on which this script is running
|
||||
server
|
||||
|
||||
// Number of threads that this script is running with
|
||||
threads
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
getRunningScript(); // get the current script.
|
||||
|
||||
.. js:function:: getRunningScript(pid)
|
||||
|
||||
:RAM cost: 0.3 GB
|
||||
:param number pid: PID of the script
|
||||
:returns: Script object or null if not found
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
getRunningScript(42); // get the script with pid 42.
|
||||
|
||||
.. js:function:: getRunningScript(fn, hostname[, args])
|
||||
|
||||
:RAM cost: 0.3 GB
|
||||
:param number fn: filename of the target script
|
||||
:param number hostname: hostname of the server running the script
|
||||
:param number args: arguments to the script.
|
||||
:returns: Script object or null if not found
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
getRunningScript("example.script", "home", "foodnstuff"); // get the script called "example.script" on "home" with argument "foodnstuff"
|
@ -40,7 +40,7 @@ isRunning() Netscript Function
|
||||
isRunning("foo.script", "joesguns", 1, 5, "test");
|
||||
|
||||
|
||||
.. js:function:: isRunning(scriptPid[, hostname=current hostname])
|
||||
.. js:function:: isRunning(scriptPid)
|
||||
|
||||
:RAM cost: 0.1 GB
|
||||
:param number scriptPid: PID of the script to check.
|
||||
@ -52,5 +52,3 @@ isRunning() Netscript Function
|
||||
.. code-block:: javascript
|
||||
|
||||
isRunning(39);
|
||||
|
||||
isRunning(39, getHostname());
|
||||
|
@ -30,7 +30,7 @@ tail() Netscript Function
|
||||
// Open logs from foo.script on the foodnstuff server that was run with the arguments [1, "test"]
|
||||
tail("foo.script", "foodnstuff", 1, "test");
|
||||
|
||||
.. js:function:: tail(scriptPid[, hostname=current hostname])
|
||||
.. js:function:: tail(scriptPid)
|
||||
|
||||
:RAM cost: 0 GB
|
||||
:param number scriptPid: PID of the script to tail.
|
||||
@ -46,3 +46,16 @@ tail() Netscript Function
|
||||
|
||||
// Open logs from process with id 42 on the foodnstuff server
|
||||
tail(42, "foodnstuff");
|
||||
|
||||
.. js:function:: tail()
|
||||
|
||||
:RAM cost: 0 GB
|
||||
|
||||
Opens the current script logs.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// Open the current script logs.
|
||||
tail();
|
||||
|
@ -75,6 +75,7 @@ This includes information such as function signatures, what they do, and their r
|
||||
scriptKill() <basicfunctions/scriptKill>
|
||||
getScriptName() <basicfunctions/getScriptName>
|
||||
getScriptRam() <basicfunctions/getScriptRam>
|
||||
getRunningScript() <basicfunctions/getRunningScript>
|
||||
getHackTime() <basicfunctions/getHackTime>
|
||||
getGrowTime() <basicfunctions/getGrowTime>
|
||||
getWeakenTime() <basicfunctions/getWeakenTime>
|
||||
|
@ -9,13 +9,12 @@ manualHack() Netscript Function
|
||||
If you are not in BitNode-4, then you must have Level 1 of Source-File 4 in order to use this function.
|
||||
|
||||
This function will perform a manual hack on the server you are currently connected to.
|
||||
This is typically required to join factions.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
connect("CSEC");
|
||||
connect("foodnstuff");
|
||||
manualHack();
|
||||
|
||||
.. warning::
|
||||
|
@ -1,9 +1,13 @@
|
||||
softReset() Netscript Function
|
||||
===================================
|
||||
|
||||
.. js:function:: softReset()
|
||||
.. js:function:: softReset([callbackScript])
|
||||
|
||||
:RAM cost: 5 GB
|
||||
:param string cbScript:
|
||||
Optional callback script. This is a script that will automatically be
|
||||
run after the soft reset. This script will be run with no arguments and
|
||||
1 thread. It must be located on your home computer.
|
||||
|
||||
If you are not in BitNode-4, then you must have Level 3 of Source-File 4 in order to use this function.
|
||||
|
||||
|
23
index.html
23
index.html
@ -100,9 +100,12 @@
|
||||
<li id="help-menu-header-li">
|
||||
<button id="help-menu-header" class="mainmenu-accordion-header"> Help </button>
|
||||
</li>
|
||||
<li id="tutorial-tab" class="mainmenu-accordion-panel">
|
||||
<button id="tutorial-menu-link"> Tutorial </button>
|
||||
</li>
|
||||
<li id="milestones-tab" class="mainmenu-accordion-panel">
|
||||
<button id="milestones-menu-link"> Milestones </button>
|
||||
</li>
|
||||
<li id="tutorial-tab" class="mainmenu-accordion-panel">
|
||||
<button id="tutorial-menu-link"> Tutorial </button>
|
||||
</li>
|
||||
<li id="options-tab" class="mainmenu-accordion-panel">
|
||||
<button id="options-menu-link"> Options </button>
|
||||
</li>
|
||||
@ -226,6 +229,10 @@
|
||||
<!-- Augmentations -->
|
||||
<div id="augmentations-container" class="generic-menupage-container"></div>
|
||||
|
||||
<!-- Milestones content -->
|
||||
<div id="milestones-container" class="generic-menupage-container">
|
||||
</div>
|
||||
|
||||
<!-- Tutorial content -->
|
||||
<div id="tutorial-container" class="generic-menupage-container">
|
||||
<h1> Tutorial (AKA Links to Documentation) </h1>
|
||||
@ -521,6 +528,16 @@
|
||||
<input class="optionCheckbox" type="checkbox" name="settingsDisableASCIIArt" id="settingsDisableASCIIArt">
|
||||
</fieldset>
|
||||
|
||||
<!-- Disable text effects such as corruption. -->
|
||||
<fieldset>
|
||||
<label for="settingsDisableTextEffects" class="tooltip">Disable Text Effects:
|
||||
<span class="tooltiptexthigh">
|
||||
If this is set, text effects will not be displayed. This can help if text is difficult to read in certain areas.
|
||||
</span>
|
||||
</label>
|
||||
<input class="optionCheckbox" type="checkbox" name="settingsDisableTextEffects" id="settingsDisableTextEffects">
|
||||
</fieldset>
|
||||
|
||||
<!-- Locale for displaying numbers -->
|
||||
<fieldset>
|
||||
<label for="settingsLocale" class="tooltip">Locale:
|
||||
|
13753
package-lock.json
generated
13753
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -6,6 +6,7 @@
|
||||
"url": "https://github.com/danielyxie/bitburner/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.3",
|
||||
"@types/numeral": "0.0.25",
|
||||
"@types/react": "^16.8.6",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
@ -47,7 +48,10 @@
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||
"@typescript-eslint/parser": "^4.22.0",
|
||||
"babel-loader": "^8.0.5",
|
||||
"beautify-lint": "^1.0.3",
|
||||
"benchmark": "^2.1.1",
|
||||
@ -55,8 +59,8 @@
|
||||
"chai": "^4.2.0",
|
||||
"css-loader": "^0.28.11",
|
||||
"es6-promise-polyfill": "^1.1.1",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-plugin-node": "^6.0.1",
|
||||
"eslint": "^7.24.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"i18n-webpack-plugin": "^1.0.0",
|
||||
@ -86,8 +90,7 @@
|
||||
"stylelint-declaration-use-variable": "^1.6.1",
|
||||
"stylelint-order": "^0.8.1",
|
||||
"ts-loader": "^4.5.0",
|
||||
"tslint": "^5.10.0",
|
||||
"typescript": "^2.9.2",
|
||||
"typescript": "^4.2.4",
|
||||
"uglify-es": "^3.3.9",
|
||||
"uglifyjs-webpack-plugin": "^1.3.0",
|
||||
"url-loader": "^1.0.1",
|
||||
@ -113,10 +116,9 @@
|
||||
"build": "webpack --mode production",
|
||||
"build:dev": "webpack --mode development",
|
||||
"build:test": "webpack --config webpack.config-test.js",
|
||||
"lint": "npm run lint:typescript & npm run lint:javascript & npm run lint:style",
|
||||
"lint:javascript": "eslint *.js ./src/**/*.js ./tests/**/*.js ./utils/**/*.js",
|
||||
"lint": "npm run lint:jsts & npm run lint:style",
|
||||
"lint:jsts": "eslint '*.{js,jsx,ts,tsx}' './src/**/*.{js,jsx,ts,tsx}' './test/**/*.{js,jsx,ts,tsx}' './utils/**/*.{js,jsx,ts,tsx}'",
|
||||
"lint:style": "stylelint ./css/*",
|
||||
"lint:typescript": "tslint --project . --exclude **/*.d.ts --format stylish src/**/*.ts utils/**/*.ts",
|
||||
"preinstall": "node ./scripts/engines-check.js",
|
||||
"test": "mochapack --webpack-config webpack.config-test.js -r jsdom-global/register ./test/index.js",
|
||||
"watch": "webpack --watch --mode production",
|
||||
|
@ -35,7 +35,7 @@ export function printAliases(): void {
|
||||
}
|
||||
|
||||
// Returns true if successful, false otherwise
|
||||
export function parseAliasDeclaration(dec: string, global: boolean=false) {
|
||||
export function parseAliasDeclaration(dec: string, global = false): boolean {
|
||||
var re = /^([_|\w|!|%|,|@]+)="(.+)"$/;
|
||||
var matches = dec.match(re);
|
||||
if (matches == null || matches.length != 3) {return false;}
|
||||
|
@ -31,6 +31,7 @@ import { clearObject } from "../../utils/helpers/clearObject";
|
||||
import { createElement } from "../../utils/uiHelpers/createElement";
|
||||
import { isString } from "../../utils/helpers/isString";
|
||||
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
|
||||
import { Money } from "../ui/React/Money";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
@ -177,7 +178,7 @@ function initAugmentations() {
|
||||
const CombatRib3 = new Augmentation({
|
||||
name:AugmentationNames.CombatRib3, repCost:14e3, moneyCost:24e6,
|
||||
info:"This is an upgrade to the Combat Rib II augmentation, and is capable of releasing even more potent combat-enhancing " +
|
||||
"drugs into the bloodstream<br><br>." +
|
||||
"drugs into the bloodstream.<br><br>" +
|
||||
"This augmentation increases the player's strength and defense by 18%.",
|
||||
prereqs:[AugmentationNames.CombatRib2],
|
||||
strength_mult: 1.18,
|
||||
@ -880,9 +881,7 @@ function initAugmentations() {
|
||||
info:"The body is genetically re-engineered so that it produces the ADR-V1 pheromone, " +
|
||||
"an artificial pheromone discovered by scientists. The ADR-V1 pheromone, when excreted, " +
|
||||
"triggers feelings of admiration and approval in other people.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the amount of reputation the player gains when working for a company by 10% <br>" +
|
||||
"Increases the amount of reputation the player gains for a faction by 10%.",
|
||||
"This augmentation increases the amount of reputation the player gains when working for a faction or company by 10%.",
|
||||
company_rep_mult: 1.1,
|
||||
faction_rep_mult: 1.1,
|
||||
});
|
||||
@ -897,8 +896,7 @@ function initAugmentations() {
|
||||
info:"The body is genetically re-engineered so that it produces the ADR-V2 pheromone, " +
|
||||
"which is similar to but more potent than ADR-V1. This pheromone, when excreted, " +
|
||||
"triggers feelings of admiration, approval, and respect in others.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the amount of reputation the player gains for a faction and company by 20%.",
|
||||
"This augmentation increases the amount of reputation the player gains when working for a faction or company by 20%.",
|
||||
company_rep_mult: 1.2,
|
||||
faction_rep_mult: 1.2,
|
||||
});
|
||||
@ -915,8 +913,7 @@ function initAugmentations() {
|
||||
"criminal organizations and allows the user to project and control holographic " +
|
||||
"simulacrums within a large radius. These simulacrums are commonly used for " +
|
||||
"espionage and surveillance work.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the amount of reputation the player gains when working for a faction or company by 15%.",
|
||||
"This augmentation increases the amount of reputation the player gains when working for a faction or company by 15%.",
|
||||
company_rep_mult: 1.15,
|
||||
faction_rep_mult: 1.15,
|
||||
});
|
||||
@ -1152,7 +1149,7 @@ function initAugmentations() {
|
||||
"cells, when powered, have a negative refractive index. As a result, they bend light " +
|
||||
"around the skin, making the user much harder to see from the naked eye.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's agility by 5% <br>" +
|
||||
"Increases the player's agility by 5%.<br>" +
|
||||
"Increases the amount of money the player gains from crimes by 10%.",
|
||||
agility_mult: 1.05,
|
||||
crime_money_mult: 1.1,
|
||||
@ -1170,8 +1167,8 @@ function initAugmentations() {
|
||||
"cells, when powered, are capable of not only bending light but also of bending heat, " +
|
||||
"making the user more resilient as well as stealthy.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's agility by 10% <br>" +
|
||||
"Increases the player's defense by 10% <br>" +
|
||||
"Increases the player's agility by 10%.<br>" +
|
||||
"Increases the player's defense by 10%.<br>" +
|
||||
"Increases the amount of money the player gains from crimes by 25%.",
|
||||
prereqs:[AugmentationNames.LuminCloaking1],
|
||||
agility_mult: 1.1,
|
||||
@ -1372,7 +1369,7 @@ function initAugmentations() {
|
||||
name:AugmentationNames.Xanipher, repCost:350e3, moneyCost:850e6,
|
||||
info:"A concoction of advanced nanobots that is orally ingested into the " +
|
||||
"body. These nanobots induce physiological change and significantly " +
|
||||
"improve the body's functionining in all aspects.<br><br>" +
|
||||
"improve the body's functioning in all aspects.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases all of the player's stats by 20%.<br>" +
|
||||
"Increases the player's experience gain rate for all stats by 15%.",
|
||||
@ -1554,12 +1551,12 @@ function initAugmentations() {
|
||||
// Sector12
|
||||
const CashRoot = new Augmentation({
|
||||
name:AugmentationNames.CashRoot, repCost:5e3, moneyCost:25e6,
|
||||
info:"A collection of digital assets saved on a small chip. The chip is implanted " +
|
||||
"into your wrist. A small jack in the chip allows you to connect it to a computer " +
|
||||
"and upload the assets.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Lets the player start with $1,000,000 after a reset.<br>" +
|
||||
"Lets the player start with the BruteSSH.exe program after a reset."
|
||||
info:<>A collection of digital assets saved on a small chip. The chip is implanted
|
||||
into your wrist. A small jack in the chip allows you to connect it to a computer
|
||||
and upload the assets.<br /><br />
|
||||
This augmentation:<br />
|
||||
Lets the player start with {Money(1e6)} after a reset.<br />
|
||||
Lets the player start with the BruteSSH.exe program after a reset.</>
|
||||
});
|
||||
CashRoot.addToFactions(["Sector-12"]);
|
||||
if (augmentationExists(AugmentationNames.CashRoot)) {
|
||||
@ -1574,8 +1571,7 @@ function initAugmentations() {
|
||||
"synthesizes glucose, amino acids, and vitamins and redistributes them " +
|
||||
"across the body. The device is powered by the body's naturally wasted " +
|
||||
"energy in the form of heat.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's experience gain rate for all combat stats by 20%.",
|
||||
"This augmentation increases the player's experience gain rate for all combat stats by 20%.",
|
||||
strength_exp_mult: 1.2,
|
||||
defense_exp_mult: 1.2,
|
||||
dexterity_exp_mult: 1.2,
|
||||
@ -1785,8 +1781,7 @@ function initAugmentations() {
|
||||
"nature of the plasma disrupts the electrical systems of Augmentations. However, " +
|
||||
"it can also be effective against non-augmented enemies due to its high temperature " +
|
||||
"and concussive force.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's success chance in Bladeburner contracts/operations by 6%.",
|
||||
"This augmentation increases the player's success chance in Bladeburner contracts/operations by 6%.",
|
||||
bladeburner_success_chance_mult: 1.06,
|
||||
isSpecial: true,
|
||||
});
|
||||
@ -1799,8 +1794,7 @@ function initAugmentations() {
|
||||
"is more advanced and powerful than the original V1 model. This V2 model is " +
|
||||
"more power-efficiency, more accurate, and can fire plasma bolts at a much " +
|
||||
"higher velocity than the V1 model.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's success chance in Bladeburner contracts/operations by 8%.",
|
||||
"This augmentation increases the player's success chance in Bladeburner contracts/operations by 8%.",
|
||||
prereqs:[AugmentationNames.HyperionV1],
|
||||
bladeburner_success_chance_mult: 1.08,
|
||||
isSpecial: true,
|
||||
@ -1958,8 +1952,7 @@ function initAugmentations() {
|
||||
info:"Upgrades the BLADE-51b Tesla Armor with a concentrated deuterium-fluoride laser " +
|
||||
"weapon. It's precision an accuracy makes it useful for quickly neutralizing " +
|
||||
"threats while keeping casualties to a minimum.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's success chance in Bladeburner contracts/operations by 8%.",
|
||||
"This augmentation increases the player's success chance in Bladeburner contracts/operations by 8%.",
|
||||
prereqs:[AugmentationNames.BladeArmor],
|
||||
bladeburner_success_chance_mult: 1.08,
|
||||
isSpecial: true,
|
||||
@ -1973,8 +1966,7 @@ function initAugmentations() {
|
||||
"multiple-fiber system. The upgraded weapon uses multiple fiber laser " +
|
||||
"modules that combine together to form a single, more powerful beam of up to " +
|
||||
"2000MW.<br><br>" +
|
||||
"This augmentation:<br>" +
|
||||
"Increases the player's success chance in Bladeburner contracts/operations by 10%.",
|
||||
"This augmentation increases the player's success chance in Bladeburner contracts/operations by 10%.",
|
||||
prereqs:[AugmentationNames.BladeArmorUnibeam],
|
||||
bladeburner_success_chance_mult: 1.1,
|
||||
isSpecial: true,
|
||||
|
@ -26,8 +26,8 @@ export function PlayerMultipliers(): React.ReactElement {
|
||||
let elems: any[] = [];
|
||||
if(r) {
|
||||
elems = [
|
||||
<td key='2'> => </td>,
|
||||
<td key='3'>{numeralWrapper.formatPercentage(r)}</td>
|
||||
<td key="2"> {"=>"} </td>,
|
||||
<td key="3">{numeralWrapper.formatPercentage(r)}</td>
|
||||
];
|
||||
}
|
||||
return elems;
|
||||
@ -36,8 +36,8 @@ export function PlayerMultipliers(): React.ReactElement {
|
||||
return <table>
|
||||
<tbody>
|
||||
{rows.map((r: any) => <tr key={r[0]}>
|
||||
<td key='0'><span>{r[0]} multiplier: </span></td>
|
||||
<td key='1' style={{textAlign: 'right'}}>{numeralWrapper.formatPercentage(r[1])}</td>
|
||||
<td key="0"><span>{r[0]} multiplier: </span></td>
|
||||
<td key="1" style={{textAlign: 'right'}}>{numeralWrapper.formatPercentage(r[1])}</td>
|
||||
{improvements(r[2])}
|
||||
</tr>)}
|
||||
</tbody>
|
||||
|
424
src/Casino/Blackjack.tsx
Normal file
424
src/Casino/Blackjack.tsx
Normal file
@ -0,0 +1,424 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Money } from "../ui/React/Money";
|
||||
import { Game } from "./Game";
|
||||
import { Deck } from "./CardDeck/Deck";
|
||||
import { Hand } from "./CardDeck/Hand";
|
||||
import { InputAdornment } from "@material-ui/core";
|
||||
import { ReactCard } from "./CardDeck/ReactCard";
|
||||
import { MuiTextField } from "../ui/React/MuiTextField";
|
||||
import { MuiButton } from "../ui/React/MuiButton";
|
||||
import { MuiPaper } from "../ui/React/MuiPaper";
|
||||
|
||||
const MAX_BET = 100e6;
|
||||
|
||||
enum Result {
|
||||
Pending = "",
|
||||
PlayerWon = "You won!",
|
||||
PlayerWonByBlackjack = "You Won! Blackjack!",
|
||||
DealerWon = "You lost!",
|
||||
Tie = "Push! (Tie)",
|
||||
}
|
||||
|
||||
type Props = {
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
type State ={
|
||||
playerHand: Hand;
|
||||
dealerHand: Hand;
|
||||
bet: number;
|
||||
betInput: string;
|
||||
gameInProgress: boolean;
|
||||
result: Result;
|
||||
gains: number; // Track gains only for this session
|
||||
wagerInvalid: boolean;
|
||||
wagerInvalidHelperText: string;
|
||||
}
|
||||
|
||||
export class Blackjack extends Game<Props, State> {
|
||||
|
||||
deck: Deck;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.deck = new Deck(5); // 5-deck multideck
|
||||
|
||||
const initialBet = 1e6;
|
||||
|
||||
this.state = {
|
||||
playerHand: new Hand([]),
|
||||
dealerHand: new Hand([]),
|
||||
bet: initialBet,
|
||||
betInput: String(initialBet),
|
||||
gameInProgress: false,
|
||||
result: Result.Pending,
|
||||
gains: 0,
|
||||
wagerInvalid: false,
|
||||
wagerInvalidHelperText: "",
|
||||
}
|
||||
}
|
||||
|
||||
canStartGame = (): boolean => {
|
||||
const { p } = this.props;
|
||||
const { bet } = this.state;
|
||||
|
||||
return p.canAfford(bet);
|
||||
}
|
||||
|
||||
startGame = (): void => {
|
||||
if (!this.canStartGame()) { return; }
|
||||
|
||||
// Take money from player right away so that player's dont just "leave" to avoid the loss (I mean they could
|
||||
// always reload without saving but w.e)
|
||||
this.props.p.loseMoney(this.state.bet);
|
||||
|
||||
const playerHand = new Hand([ this.deck.safeDrawCard(), this.deck.safeDrawCard() ]);
|
||||
const dealerHand = new Hand([ this.deck.safeDrawCard(), this.deck.safeDrawCard() ]);
|
||||
|
||||
this.setState({
|
||||
playerHand,
|
||||
dealerHand,
|
||||
gameInProgress: true,
|
||||
result: Result.Pending,
|
||||
});
|
||||
|
||||
// If the player is dealt a blackjack and the dealer is not, then the player
|
||||
// immediately wins
|
||||
if (this.getTrueHandValue(playerHand) === 21) {
|
||||
if (this.getTrueHandValue(dealerHand) === 21) {
|
||||
this.finishGame(Result.Tie);
|
||||
} else {
|
||||
this.finishGame(Result.PlayerWonByBlackjack);
|
||||
}
|
||||
} else if (this.getTrueHandValue(dealerHand) === 21) {
|
||||
// Check if dealer won by blackjack. We know at this point that the player does not also have blackjack.
|
||||
this.finishGame(Result.DealerWon);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an array of numbers representing all possible values of the given Hand. The reason it needs to be
|
||||
// an array is because an Ace can count as both 1 and 11.
|
||||
getHandValue = (hand: Hand): number[] => {
|
||||
let result: number[] = [ 0 ];
|
||||
|
||||
for (let i = 0 ; i < hand.cards.length; ++i) {
|
||||
const value = hand.cards[i].value;
|
||||
if (value >= 10) {
|
||||
result = result.map((x) => x + 10);
|
||||
} else if (value === 1) {
|
||||
result = result.flatMap((x) => [ x + 1, x + 11 ]);
|
||||
} else {
|
||||
result = result.map((x) => x + value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the single hand value used for determine things like victory and whether or not
|
||||
// the dealer has to hit. Essentially this uses the biggest value that's 21 or under. If no such value exists,
|
||||
// then it means the hand is busted and we can just return whatever
|
||||
getTrueHandValue = (hand: Hand): number => {
|
||||
const handValues = this.getHandValue(hand);
|
||||
const valuesUnder21 = handValues.filter((x) => (x <= 21));
|
||||
|
||||
if (valuesUnder21.length > 0) {
|
||||
valuesUnder21.sort((a, b) => a - b);
|
||||
return valuesUnder21[valuesUnder21.length - 1];
|
||||
} else {
|
||||
// Just return the first value. It doesnt really matter anyways since hand is buted
|
||||
return handValues[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Returns all hand values that are 21 or under. If no values are 21 or under, then the first value is returned.
|
||||
getHandDisplayValues = (hand: Hand): number[] => {
|
||||
const handValues = this.getHandValue(hand);
|
||||
if (this.isHandBusted(hand)) {
|
||||
// Hand is busted so just return the 1st value, doesn't really matter
|
||||
return [ ...new Set([ handValues[0] ]) ];
|
||||
} else {
|
||||
return [ ...new Set(handValues.filter((x) => x <= 21)) ];
|
||||
}
|
||||
}
|
||||
|
||||
isHandBusted = (hand: Hand): boolean => {
|
||||
return this.getTrueHandValue(hand) > 21;
|
||||
}
|
||||
|
||||
playerHit = (event: React.MouseEvent): void => {
|
||||
if (!event.isTrusted) { return; }
|
||||
|
||||
const newHand = this.state.playerHand.addCards(this.deck.safeDrawCard());
|
||||
|
||||
this.setState({
|
||||
playerHand: newHand,
|
||||
});
|
||||
|
||||
// Check if player busted, and finish the game if so
|
||||
if (this.isHandBusted(newHand)) {
|
||||
this.finishGame(Result.DealerWon);
|
||||
}
|
||||
}
|
||||
|
||||
playerStay = (event: React.MouseEvent): void => {
|
||||
if (!event.isTrusted) { return; }
|
||||
|
||||
// Determine if Dealer needs to hit. A dealer must hit if they have 16 or lower.
|
||||
// If the dealer has a Soft 17 (Ace + 6), then they stay.
|
||||
let newDealerHand = this.state.dealerHand;
|
||||
while (true) {
|
||||
// The dealer's "true" hand value is the 2nd one if its 21 or less (the 2nd value is always guaranteed
|
||||
// to be equal or larger). Otherwise its the 1st.
|
||||
const dealerHandValue = this.getTrueHandValue(newDealerHand);
|
||||
|
||||
if (dealerHandValue <= 16) {
|
||||
newDealerHand = newDealerHand.addCards(this.deck.safeDrawCard());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
dealerHand: newDealerHand,
|
||||
})
|
||||
|
||||
// If dealer has busted, then player wins
|
||||
if (this.isHandBusted(newDealerHand)) {
|
||||
this.finishGame(Result.PlayerWon);
|
||||
} else {
|
||||
const dealerHandValue = this.getTrueHandValue(newDealerHand);
|
||||
const playerHandValue = this.getTrueHandValue(this.state.playerHand);
|
||||
|
||||
console.log(`dealerHandValue: ${dealerHandValue}`);
|
||||
console.log(`playerHandValue: ${playerHandValue}`);
|
||||
|
||||
// We expect nobody to have busted. If someone busted, there is an error
|
||||
// in our game logic
|
||||
if (dealerHandValue > 21 || playerHandValue > 21) {
|
||||
throw new Error("Someone busted when not expected to");
|
||||
}
|
||||
|
||||
if (playerHandValue > dealerHandValue) {
|
||||
this.finishGame(Result.PlayerWon);
|
||||
} else if (playerHandValue < dealerHandValue) {
|
||||
this.finishGame(Result.DealerWon);
|
||||
} else {
|
||||
this.finishGame(Result.Tie);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
finishGame = (result: Result): void => {
|
||||
let gains = 0;
|
||||
if (this.isPlayerWinResult(result)) {
|
||||
gains = this.state.bet;
|
||||
|
||||
// We 2x the gains because we took away money at the start, so we need to give the original bet back.
|
||||
this.win(this.props.p, 2 * gains);
|
||||
} else if (result === Result.DealerWon) {
|
||||
gains = -1 * this.state.bet;
|
||||
// Dont need to take money here since we already did it at the start
|
||||
} else if (result === Result.Tie) {
|
||||
this.win(this.props.p, this.state.bet); // Get the original bet back
|
||||
}
|
||||
|
||||
this.setState({
|
||||
gameInProgress: false,
|
||||
result,
|
||||
gains: this.state.gains + gains,
|
||||
});
|
||||
}
|
||||
|
||||
isPlayerWinResult = (result: Result): boolean => {
|
||||
return (result === Result.PlayerWon || result === Result.PlayerWonByBlackjack);
|
||||
}
|
||||
|
||||
wagerOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const { p } = this.props;
|
||||
const betInput = event.target.value;
|
||||
const wager = Math.round(parseFloat(betInput));
|
||||
if (isNaN(wager)) {
|
||||
this.setState({
|
||||
bet: 0,
|
||||
betInput,
|
||||
wagerInvalid: true,
|
||||
wagerInvalidHelperText: "Not a valid number",
|
||||
});
|
||||
} else if (wager <= 0) {
|
||||
this.setState({
|
||||
bet: 0,
|
||||
betInput,
|
||||
wagerInvalid: true,
|
||||
wagerInvalidHelperText: "Must bet a postive amount",
|
||||
});
|
||||
} else if (wager > MAX_BET) {
|
||||
this.setState({
|
||||
bet: 0,
|
||||
betInput,
|
||||
wagerInvalid: true,
|
||||
wagerInvalidHelperText: "Exceeds max bet",
|
||||
});
|
||||
} else if (!p.canAfford(wager)) {
|
||||
this.setState({
|
||||
bet: 0,
|
||||
betInput,
|
||||
wagerInvalid: true,
|
||||
wagerInvalidHelperText: "Not enough money",
|
||||
});
|
||||
} else {
|
||||
// Valid wager
|
||||
this.setState({
|
||||
bet: wager,
|
||||
betInput,
|
||||
wagerInvalid: false,
|
||||
wagerInvalidHelperText: "",
|
||||
result: Result.Pending, // Reset previous game status to clear the win/lose text UI
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Start game button
|
||||
startOnClick = (event: React.MouseEvent): void => {
|
||||
// Protect against scripting...although maybe this would be fun to automate
|
||||
if (!event.isTrusted) { return; }
|
||||
|
||||
if (!this.state.wagerInvalid) {
|
||||
this.startGame();
|
||||
}
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const {
|
||||
betInput,
|
||||
playerHand,
|
||||
dealerHand,
|
||||
gameInProgress,
|
||||
result,
|
||||
wagerInvalid,
|
||||
wagerInvalidHelperText,
|
||||
gains,
|
||||
} = this.state;
|
||||
|
||||
// Get the player totals to display.
|
||||
const playerHandValues = this.getHandDisplayValues(playerHand);
|
||||
const dealerHandValues = this.getHandDisplayValues(dealerHand);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Wager input */}
|
||||
<div>
|
||||
<MuiTextField
|
||||
value={betInput}
|
||||
label={
|
||||
<>
|
||||
{"Wager (Max: "}
|
||||
{Money(MAX_BET)}
|
||||
{")"}
|
||||
</>
|
||||
}
|
||||
disabled={gameInProgress}
|
||||
onChange={this.wagerOnChange}
|
||||
error={wagerInvalid}
|
||||
helperText={wagerInvalid ? wagerInvalidHelperText : ""}
|
||||
type="number"
|
||||
variant="filled"
|
||||
style={{
|
||||
width: "200px",
|
||||
}}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start" >$</InputAdornment>,
|
||||
}} />
|
||||
|
||||
<p>
|
||||
{"Total earnings this session: "}
|
||||
{Money(gains)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Buttons */}
|
||||
{!gameInProgress ? (
|
||||
<div>
|
||||
<MuiButton
|
||||
color="primary"
|
||||
onClick={this.startOnClick}
|
||||
disabled={wagerInvalid || !this.canStartGame()}>
|
||||
|
||||
Start
|
||||
</MuiButton>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<MuiButton color="primary" onClick={this.playerHit} >
|
||||
Hit
|
||||
</MuiButton>
|
||||
<MuiButton color="secondary" onClick={this.playerStay} >
|
||||
Stay
|
||||
</MuiButton>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main game part. Displays both if the game is in progress OR if there's a result so you can see
|
||||
* the cards that led to that result. */}
|
||||
{(gameInProgress || result !== Result.Pending) && (
|
||||
<div>
|
||||
<MuiPaper variant="outlined" elevation={2}>
|
||||
<pre>Player</pre>
|
||||
{playerHand.cards.map((card, i) => (
|
||||
<ReactCard card={card} key={i} />
|
||||
))}
|
||||
|
||||
<pre>Value(s): </pre>
|
||||
{playerHandValues.map((value, i) => (
|
||||
<pre key={i}>{value}</pre>
|
||||
))}
|
||||
</MuiPaper>
|
||||
|
||||
<br />
|
||||
|
||||
<MuiPaper variant="outlined" elevation={2}>
|
||||
<pre>Dealer</pre>
|
||||
{dealerHand.cards.map((card, i) => (
|
||||
// Hide every card except the first while game is in progress
|
||||
<ReactCard card={card} hidden={gameInProgress && i !== 0} key={i} />
|
||||
))}
|
||||
|
||||
{!gameInProgress && (
|
||||
<>
|
||||
<pre>Value(s): </pre>
|
||||
{dealerHandValues.map((value, i) => (
|
||||
<pre key={i}>{value}</pre>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</MuiPaper>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Results from previous round */}
|
||||
{result !== Result.Pending && (
|
||||
<p>
|
||||
{result}
|
||||
{this.isPlayerWinResult(result) && (
|
||||
<>
|
||||
{" You gained "}
|
||||
{Money(this.state.bet)}
|
||||
</>
|
||||
)}
|
||||
{result === Result.DealerWon && (
|
||||
<>
|
||||
{" You lost "}
|
||||
{Money(this.state.bet)}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
42
src/Casino/CardDeck/Card.ts
Normal file
42
src/Casino/CardDeck/Card.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// Enum values are lowercased to match css classes
|
||||
export enum Suit {
|
||||
Clubs = "clubs",
|
||||
Diamonds = "diamonds",
|
||||
Hearts = "hearts",
|
||||
Spades = "spades",
|
||||
}
|
||||
|
||||
export class Card {
|
||||
|
||||
constructor(readonly value: number, readonly suit: Suit) {
|
||||
if (value < 1 || value > 13) {
|
||||
throw new Error(`Card instantiated with improper value: ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
formatValue(): string {
|
||||
switch (this.value) {
|
||||
case 1:
|
||||
return "A";
|
||||
case 11:
|
||||
return "J";
|
||||
case 12:
|
||||
return "Q";
|
||||
case 13:
|
||||
return "K";
|
||||
default:
|
||||
return `${this.value}`;
|
||||
}
|
||||
}
|
||||
|
||||
isRedSuit(): boolean {
|
||||
return this.suit === Suit.Hearts || this.suit === Suit.Diamonds;
|
||||
}
|
||||
|
||||
getStringRepresentation(): string {
|
||||
const value = this.formatValue();
|
||||
|
||||
return `${value} of ${this.suit}`;
|
||||
}
|
||||
|
||||
}
|
58
src/Casino/CardDeck/Deck.ts
Normal file
58
src/Casino/CardDeck/Deck.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { Card, Suit } from "./Card";
|
||||
import { shuffle } from "lodash";
|
||||
|
||||
export class Deck {
|
||||
|
||||
private cards: Card[] = [];
|
||||
|
||||
// Support multiple decks
|
||||
constructor(private numDecks = 1) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
shuffle(): void {
|
||||
this.cards = shuffle(this.cards); // Just use lodash
|
||||
}
|
||||
|
||||
drawCard(): Card {
|
||||
if (this.cards.length == 0) {
|
||||
throw new Error("Tried to draw card from empty deck");
|
||||
}
|
||||
|
||||
return this.cards.shift() as Card; // Guaranteed to return a Card since we throw an Error if array is empty
|
||||
}
|
||||
|
||||
// Draws a card, resetting the deck beforehands if the Deck is empty
|
||||
safeDrawCard(): Card {
|
||||
if (this.cards.length === 0) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
return this.drawCard();
|
||||
}
|
||||
|
||||
// Reset the deck back to the original 52 cards and shuffle it
|
||||
reset(): void {
|
||||
this.cards = [];
|
||||
|
||||
for (let i = 1; i <= 13; ++i) {
|
||||
for (let j = 0; j < this.numDecks; ++j) {
|
||||
this.cards.push(new Card(i, Suit.Clubs));
|
||||
this.cards.push(new Card(i, Suit.Diamonds));
|
||||
this.cards.push(new Card(i, Suit.Hearts));
|
||||
this.cards.push(new Card(i, Suit.Spades));
|
||||
}
|
||||
}
|
||||
|
||||
this.shuffle();
|
||||
}
|
||||
|
||||
size(): number {
|
||||
return this.cards.length;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.cards.length === 0;
|
||||
}
|
||||
|
||||
}
|
25
src/Casino/CardDeck/Hand.ts
Normal file
25
src/Casino/CardDeck/Hand.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Represents a Hand of cards.
|
||||
*
|
||||
* This class is IMMUTABLE
|
||||
*/
|
||||
|
||||
import { Card } from "./Card";
|
||||
|
||||
export class Hand {
|
||||
|
||||
constructor(readonly cards: readonly Card[]) {}
|
||||
|
||||
addCards(...cards: Card[]): Hand {
|
||||
return new Hand([ ...this.cards, ...cards ]);
|
||||
}
|
||||
|
||||
removeByIndex(i: number): Hand {
|
||||
if (i >= this.cards.length) {
|
||||
throw new Error(`Tried to remove invalid card from Hand by index: ${i}`);
|
||||
}
|
||||
|
||||
return new Hand([ ...this.cards.slice().splice(i, 1) ])
|
||||
}
|
||||
|
||||
}
|
40
src/Casino/CardDeck/ReactCard.tsx
Normal file
40
src/Casino/CardDeck/ReactCard.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import React, { FC } from "react";
|
||||
import { Card, Suit } from "./Card";
|
||||
|
||||
type Props = {
|
||||
card: Card;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export const ReactCard: FC<Props> = ({ card, hidden }) => {
|
||||
let suit : React.ReactNode;
|
||||
switch (card.suit) {
|
||||
case Suit.Clubs:
|
||||
suit = <span>♣</span>;
|
||||
break;
|
||||
case Suit.Diamonds:
|
||||
suit = <span>♦</span>;
|
||||
break;
|
||||
case Suit.Hearts:
|
||||
suit = <span>♥</span>;
|
||||
break;
|
||||
case Suit.Spades:
|
||||
suit = <span>♠</span>;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`MissingCaseException: ${card.suit}`);
|
||||
|
||||
}
|
||||
return (
|
||||
<div className={`casino-card ${card.isRedSuit() ? "red" : "black"}`}>
|
||||
<>
|
||||
<div className="value">
|
||||
{hidden ? " - " : card.formatValue()}
|
||||
</div>
|
||||
<div className={`suit`}>
|
||||
{hidden ? " - " : suit}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -5,7 +5,7 @@ import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
const gainLimit = 10e9;
|
||||
|
||||
export class Game<T,U> extends React.Component<T, U> {
|
||||
win(p: IPlayer, n: number) {
|
||||
win(p: IPlayer, n: number): void{
|
||||
p.gainMoney(n);
|
||||
p.recordMoneySource(n, "casino");
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
import { IMap } from "./types";
|
||||
|
||||
export let CONSTANTS: IMap<any> = {
|
||||
Version: "0.51.5",
|
||||
Version: "0.51.6",
|
||||
|
||||
/** Max level for any skill, assuming no multipliers. Determined by max numerical value in javascript for experience
|
||||
* and the skill level formula in Player.js. Note that all this means it that when experience hits MAX_INT, then
|
||||
@ -228,24 +228,42 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.51.5 - 2021-04-20 Flags! (hydroflame)
|
||||
v0.51.6 - 2021-04-28 Backdoor! (Community)
|
||||
-------
|
||||
|
||||
Netscript
|
||||
* 'flags' is a new function that helps script handle flags.
|
||||
This is subject to change if it doesn't meet the need of the players.
|
||||
* 'ps' now returns the pid.
|
||||
* 'tail' now works with pid as first argument.
|
||||
* 'tail' hostname defaults to current server. (like the documentation says)
|
||||
* 'isRunning' hostname defaults to current server.
|
||||
* 'isRunning' now works with pid as first argument.
|
||||
Backdoor
|
||||
* a new terminal command, backdoor, has been added to help differentiate
|
||||
between the terminal hack command and the netscript hack function. (@dewint)
|
||||
|
||||
Gang
|
||||
* Nerfed ascension mechanic once again :(
|
||||
Milestones
|
||||
* A new tab under the Help menu has been added to guide players through the
|
||||
game.
|
||||
|
||||
Casino
|
||||
* Blackjack has been added (@BigD)
|
||||
|
||||
Netscript
|
||||
* 'prompt' now converts input to JSON.
|
||||
* 'getRunningScript' is a new netscript function that returns a bunch of
|
||||
data related to a running script.
|
||||
|
||||
Coding contracts
|
||||
* trivial puzzles should no longer appear.
|
||||
|
||||
Infiltration
|
||||
* All numbers are formatted like the rest of the game.
|
||||
|
||||
Misc.
|
||||
* Souce-File typo fix
|
||||
* Fix 'while you were away' screen.
|
||||
* Bladeburner team size can no longer be set to negative amounts.
|
||||
* Server security is capped at 100.
|
||||
* Added option to quit a job.
|
||||
* 'cd' no longer works on unexistent folders.
|
||||
* cd with no arguments brings you back to top level folder (@Andreas)
|
||||
* 'softReset' documentation udpated.
|
||||
* Money tracker now accounts for going to the hospital manually.
|
||||
* codemirror is now the default editor (for new save files)
|
||||
* fix typo in dark web help text (@Rodeth)
|
||||
* so many documentation and typos fixes (@Pimgd)
|
||||
* A corruption visual effect has been added to location with servers that
|
||||
have backdoor installed. (@dewint)
|
||||
`
|
||||
}
|
@ -210,9 +210,13 @@ export class Product {
|
||||
}
|
||||
|
||||
//Delete unneeded variables
|
||||
// @ts-ignore
|
||||
delete this.prog;
|
||||
// @ts-ignore
|
||||
delete this.createCity;
|
||||
// @ts-ignore
|
||||
delete this.designCost;
|
||||
// @ts-ignore
|
||||
delete this.advCost;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ 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.",
|
||||
@ -451,9 +452,9 @@ function endInfiltrationLevel(inst) {
|
||||
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'>$" +
|
||||
formatNumber(secretMoneyValue, 2) + "</span>), or they " +
|
||||
"could be given to factions for reputation (<span class='light-yellow'>" + formatNumber(secretValue, 3) + " rep</span>)");
|
||||
"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
|
||||
@ -495,16 +496,16 @@ function updateInfiltrationLevelText(inst) {
|
||||
document.getElementById("infiltration-level-text").innerHTML =
|
||||
"Facility name: " + inst.companyName + "<br>" +
|
||||
"Clearance Level: " + inst.clearanceLevel + "<br>" +
|
||||
"Security Level: " + formatNumber(inst.securityLevel, 3) + "<br><br>" +
|
||||
"Security Level: " + numeralWrapper.formatInfiltrationSecurity(inst.securityLevel) + "<br><br>" +
|
||||
"Total value of stolen secrets<br>" +
|
||||
"Reputation: <span class='light-yellow'>" + formatNumber(totalValue, 3) + "</span><br>" +
|
||||
"Money: <span class='money-gold'>$" + formatNumber(totalMoneyValue, 2) + "</span><br><br>" +
|
||||
"Hack exp gained: " + formatNumber(inst.calcGainedHackingExp(), 3) + "<br>" +
|
||||
"Str exp gained: " + formatNumber(inst.calcGainedStrengthExp(), 3) + "<br>" +
|
||||
"Def exp gained: " + formatNumber(inst.calcGainedDefenseExp(), 3) + "<br>" +
|
||||
"Dex exp gained: " + formatNumber(inst.calcGainedDexterityExp(), 3) + "<br>" +
|
||||
"Agi exp gained: " + formatNumber(inst.calcGainedAgilityExp(), 3) + "<br>" +
|
||||
"Cha exp gained: " + formatNumber(inst.calcGainedCharismaExp(), 3);
|
||||
"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 */
|
||||
}
|
||||
|
||||
@ -524,7 +525,7 @@ function updateInfiltrationButtons(inst, scenario) {
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to escape the facility with the classified secrets and " +
|
||||
"documents you have stolen. You have a " +
|
||||
formatNumber(escapeChance*100, 2) + "% chance of success. If you fail, " +
|
||||
numeralWrapper.formatPercentage(escapeChance, 2) + " chance of success. If you fail, " +
|
||||
"the security level will increase by 5%.</span>";
|
||||
|
||||
switch(scenario) {
|
||||
@ -532,55 +533,55 @@ function updateInfiltrationButtons(inst, scenario) {
|
||||
document.getElementById("infiltration-pickdoor").innerHTML = "Lockpick" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to pick the locked door. You have a " +
|
||||
formatNumber(lockpickChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(hackChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(destroySecurityChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(sneakChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(killChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(assassinateChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(hackChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(sneakChance*100, 2) + "% chance of success. " +
|
||||
numeralWrapper.formatPercentage(sneakChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 8%. </span>";
|
||||
break;
|
||||
|
||||
@ -589,39 +590,39 @@ function updateInfiltrationButtons(inst, scenario) {
|
||||
document.getElementById("infiltration-kill").innerHTML = "Kill" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to kill the security guard. You have a " +
|
||||
formatNumber(killChance*100, 2) + "% chance of success. " +
|
||||
numeralWrapper.formatPercentage(killChance, 2) + " chance of success. " +
|
||||
"If you succeed, the security level will increase by 5%. If you fail, " +
|
||||
"the security level will decrease by 10%. </span>";
|
||||
|
||||
document.getElementById("infiltration-knockout").innerHTML = "Knockout" +
|
||||
"<span class='tooltiptext'>" +
|
||||
"Attempt to knockout the security guard. You have a " +
|
||||
formatNumber(knockoutChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(stealthKnockoutChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(assassinateChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(sneakChance*100, 2) + "% chance of success. " +
|
||||
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 " +
|
||||
formatNumber(bribeChance*100, 2) + "% chance of success. " +
|
||||
numeralWrapper.formatPercentage(bribeChance, 2) + " chance of success. " +
|
||||
"If you fail, the security level will increase by 15%. </span>";
|
||||
break;
|
||||
}
|
||||
|
@ -9,4 +9,4 @@ export enum CityName {
|
||||
NewTokyo = "New Tokyo",
|
||||
Sector12 = "Sector-12",
|
||||
Volhaven = "Volhaven",
|
||||
};
|
||||
}
|
||||
|
@ -4,25 +4,28 @@
|
||||
* This subcomponent renders all of the buttons for training at the gym
|
||||
*/
|
||||
import * as React from "react";
|
||||
import { Blackjack } from "../../Casino/Blackjack";
|
||||
import { CoinFlip } from "../../Casino/CoinFlip";
|
||||
import { Roulette } from "../../Casino/Roulette";
|
||||
import { SlotMachine } from "../../Casino/SlotMachine";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
|
||||
import { Location } from "../Location";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
|
||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import { Money } from "../../ui/React/Money";
|
||||
import { SlotMachine } from "../../Casino/SlotMachine";
|
||||
import { CoinFlip } from "../../Casino/CoinFlip";
|
||||
import { Roulette } from "../../Casino/Roulette";
|
||||
enum GameType {
|
||||
None = 'none',
|
||||
Coin = 'coin',
|
||||
Slots = 'slots',
|
||||
Roulette = 'roulette',
|
||||
Blackjack = 'blackjack',
|
||||
}
|
||||
|
||||
type IProps = {
|
||||
p: IPlayer;
|
||||
}
|
||||
|
||||
type IState = {
|
||||
game: string;
|
||||
game: GameType;
|
||||
}
|
||||
|
||||
export class CasinoLocation extends React.Component<IProps, IState> {
|
||||
@ -30,57 +33,70 @@ export class CasinoLocation extends React.Component<IProps, IState> {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
game: '',
|
||||
game: GameType.None,
|
||||
}
|
||||
|
||||
this.updateGame = this.updateGame.bind(this);
|
||||
}
|
||||
|
||||
updateGame(game: string) {
|
||||
updateGame(game: GameType): void {
|
||||
this.setState({
|
||||
game: game,
|
||||
game,
|
||||
});
|
||||
}
|
||||
|
||||
renderGames() {
|
||||
renderGames(): React.ReactNode {
|
||||
return (<>
|
||||
<StdButton
|
||||
onClick={() => this.updateGame('coin')}
|
||||
onClick={() => this.updateGame(GameType.Coin)}
|
||||
text={"Play coin flip"}
|
||||
/><br />
|
||||
<StdButton
|
||||
onClick={() => this.updateGame('slots')}
|
||||
onClick={() => this.updateGame(GameType.Slots)}
|
||||
text={"Play slots"}
|
||||
/><br />
|
||||
<StdButton
|
||||
onClick={() => this.updateGame('roulette')}
|
||||
onClick={() => this.updateGame(GameType.Roulette)}
|
||||
text={"Play roulette"}
|
||||
/><br />
|
||||
<StdButton
|
||||
onClick={() => this.updateGame(GameType.Blackjack)}
|
||||
text={"Play blackjack"}
|
||||
/>
|
||||
</>)
|
||||
}
|
||||
|
||||
renderGame() {
|
||||
let elem;
|
||||
renderGame(): React.ReactNode {
|
||||
let elem = null;
|
||||
switch(this.state.game) {
|
||||
case 'coin':
|
||||
elem = <CoinFlip p={this.props.p} />
|
||||
break;
|
||||
case 'slots':
|
||||
elem = <SlotMachine p={this.props.p} />
|
||||
break;
|
||||
case 'roulette':
|
||||
elem = <Roulette p={this.props.p} />
|
||||
break;
|
||||
case GameType.Coin:
|
||||
elem = <CoinFlip p={this.props.p} />
|
||||
break;
|
||||
case GameType.Slots:
|
||||
elem = <SlotMachine p={this.props.p} />
|
||||
break;
|
||||
case GameType.Roulette:
|
||||
elem = <Roulette p={this.props.p} />
|
||||
break;
|
||||
case GameType.Blackjack:
|
||||
elem = <Blackjack p={this.props.p} />
|
||||
break;
|
||||
case GameType.None:
|
||||
break;
|
||||
default:
|
||||
throw new Error(`MissingCaseException: ${this.state.game}`);
|
||||
}
|
||||
|
||||
return (<>
|
||||
<StdButton onClick={() => this.updateGame('')} text={"Stop playing"} />
|
||||
{elem}
|
||||
</>)
|
||||
return (
|
||||
<>
|
||||
<StdButton onClick={() => this.updateGame(GameType.None)} text={"Stop playing"} />
|
||||
{elem}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
if(!this.state.game) {
|
||||
render(): React.ReactNode {
|
||||
if(this.state.game === GameType.None) {
|
||||
return this.renderGames();
|
||||
} else {
|
||||
return this.renderGame();
|
||||
|
@ -19,9 +19,9 @@ type IProps = {
|
||||
export class LocationCity extends React.Component<IProps, any> {
|
||||
asciiCity() {
|
||||
const thiscity = this;
|
||||
const topprop = this.props
|
||||
const topprop = this.props;
|
||||
|
||||
function LocationLetter(location: string) {
|
||||
function LocationLetter(location: LocationName) {
|
||||
if (location)
|
||||
return <span key={location} className='tooltip' style={{color: 'blue', whiteSpace: 'nowrap', margin: '0px', padding: '0px', cursor: 'pointer'}} onClick={topprop.enterLocation.bind(thiscity, location)}>
|
||||
X
|
||||
|
@ -26,6 +26,13 @@ import { StdButton } from "../../ui/React/StdButton";
|
||||
import { Reputation } from "../../ui/React/Reputation";
|
||||
import { Favor } from "../../ui/React/Favor";
|
||||
|
||||
import {
|
||||
yesNoBoxGetYesButton,
|
||||
yesNoBoxGetNoButton,
|
||||
yesNoBoxClose,
|
||||
yesNoBoxCreate
|
||||
} from "../../../utils/YesNoBox";
|
||||
|
||||
type IProps = {
|
||||
engine: IEngine;
|
||||
locName: LocationName;
|
||||
@ -73,6 +80,7 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
||||
|
||||
this.btnStyle = { display: "block" };
|
||||
|
||||
this.quit = this.quit.bind(this);
|
||||
this.applyForAgentJob = this.applyForAgentJob.bind(this);
|
||||
this.applyForBusinessConsultantJob = this.applyForBusinessConsultantJob.bind(this);
|
||||
this.applyForBusinessJob = this.applyForBusinessJob.bind(this);
|
||||
@ -207,6 +215,26 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
quit(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
|
||||
var yesBtn = yesNoBoxGetYesButton();
|
||||
var noBtn = yesNoBoxGetNoButton();
|
||||
if (yesBtn == null || noBtn == null) { return; }
|
||||
yesBtn.innerHTML = "Quit job";
|
||||
noBtn.innerHTML = "Cancel";
|
||||
yesBtn.addEventListener("click", () => {
|
||||
this.props.p.quitJob(this.props.locName);
|
||||
this.checkIfEmployedHere(true);
|
||||
yesNoBoxClose();
|
||||
});
|
||||
noBtn.addEventListener("click", () => {
|
||||
yesNoBoxClose();
|
||||
});
|
||||
|
||||
yesNoBoxCreate(<>Would you like to quit your job at {this.company.name}?</>);
|
||||
}
|
||||
|
||||
render() {
|
||||
const isEmployedHere = this.jobTitle != null;
|
||||
const favorGain = this.company.getFavorGain();
|
||||
@ -236,10 +264,12 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
||||
</p><br />
|
||||
<br /><p style={blockStyleMarkup}>-------------------------</p><br />
|
||||
<StdButton
|
||||
id={"foo-work-button-id"}
|
||||
onClick={this.work}
|
||||
style={this.btnStyle}
|
||||
text={"Work"}
|
||||
/>
|
||||
<StdButton
|
||||
onClick={this.quit}
|
||||
text={"Quit"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
@ -6,24 +6,29 @@
|
||||
*/
|
||||
import * as React from "react";
|
||||
|
||||
import { CompanyLocation } from "./CompanyLocation";
|
||||
import { GymLocation } from "./GymLocation";
|
||||
import { HospitalLocation } from "./HospitalLocation";
|
||||
import { SlumsLocation } from "./SlumsLocation";
|
||||
import { SpecialLocation } from "./SpecialLocation";
|
||||
import { TechVendorLocation } from "./TechVendorLocation";
|
||||
import { TravelAgencyLocation } from "./TravelAgencyLocation";
|
||||
import { UniversityLocation } from "./UniversityLocation";
|
||||
import { CasinoLocation } from "./CasinoLocation";
|
||||
import { CompanyLocation } from "./CompanyLocation";
|
||||
import { GymLocation } from "./GymLocation";
|
||||
import { HospitalLocation } from "./HospitalLocation";
|
||||
import { SlumsLocation } from "./SlumsLocation";
|
||||
import { SpecialLocation } from "./SpecialLocation";
|
||||
import { TechVendorLocation } from "./TechVendorLocation";
|
||||
import { TravelAgencyLocation } from "./TravelAgencyLocation";
|
||||
import { UniversityLocation } from "./UniversityLocation";
|
||||
import { CasinoLocation } from "./CasinoLocation";
|
||||
|
||||
import { Location } from "../Location";
|
||||
import { LocationType } from "../LocationTypeEnum";
|
||||
import { CityName } from "../data/CityNames";
|
||||
import { Location } from "../Location";
|
||||
import { LocationType } from "../LocationTypeEnum";
|
||||
import { CityName } from "../data/CityNames";
|
||||
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import { SpecialServerIps } from "../../Server/SpecialServerIps";
|
||||
import { getServer, isBackdoorInstalled } from "../../Server/ServerHelpers";
|
||||
|
||||
import { StdButton } from "../../ui/React/StdButton";
|
||||
import { CorruptableText } from "../../ui/React/CorruptableText";
|
||||
|
||||
type IProps = {
|
||||
engine: IEngine;
|
||||
@ -146,11 +151,19 @@ export class GenericLocation extends React.Component<IProps, any> {
|
||||
|
||||
render() {
|
||||
const locContent: React.ReactNode[] = this.getLocationSpecificContent();
|
||||
|
||||
const ip = SpecialServerIps.getIp(this.props.loc.name);
|
||||
const server = getServer(ip);
|
||||
const backdoorInstalled = server !== null && isBackdoorInstalled(server);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StdButton onClick={this.props.returnToCity} style={this.btnStyle} text={"Return to World"} />
|
||||
<h1>{this.props.loc.name}</h1>
|
||||
<h1>
|
||||
{backdoorInstalled && !Settings.DisableTextEffects
|
||||
? <CorruptableText content={this.props.loc.name}/>
|
||||
: this.props.loc.name
|
||||
}
|
||||
</h1>
|
||||
{locContent}
|
||||
</div>
|
||||
)
|
||||
|
@ -45,8 +45,8 @@ export class GymLocation extends React.Component<IProps, any> {
|
||||
const ip = SpecialServerIps.getIp(this.props.loc.name);
|
||||
console.log(`ip: ${ip}`);
|
||||
const server = getServer(ip);
|
||||
if(server == null || !server.hasOwnProperty('manuallyHacked')) return this.props.loc.costMult;
|
||||
const discount = (server as Server).manuallyHacked? 0.9 : 1;
|
||||
if(server == null || !server.hasOwnProperty('backdoorInstalled')) return this.props.loc.costMult;
|
||||
const discount = (server as Server).backdoorInstalled? 0.9 : 1;
|
||||
return this.props.loc.costMult * discount;
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,7 @@ export class HospitalLocation extends React.Component<IProps, IState> {
|
||||
const cost = this.getCost();
|
||||
this.props.p.loseMoney(cost);
|
||||
this.props.p.hp = this.props.p.max_hp;
|
||||
this.props.p.recordMoneySource(-1 * cost, 'hospitalization');
|
||||
|
||||
// This just forces a re-render to update the cost
|
||||
this.setState({
|
||||
|
@ -82,7 +82,7 @@ export class TravelAgencyLocation extends React.Component<IProps, any> {
|
||||
listWorldMap() {
|
||||
const travelBtns: React.ReactNode[] = [];
|
||||
for (const key in CityName) {
|
||||
const city = CityName[key];
|
||||
const city: CityName = (CityName as any)[key];
|
||||
|
||||
// Skip current city
|
||||
if (city === this.props.p.city) { continue; }
|
||||
|
@ -48,8 +48,8 @@ export class UniversityLocation extends React.Component<IProps, any> {
|
||||
const ip = SpecialServerIps.getIp(this.props.loc.name);
|
||||
console.log(`ip: ${ip}`);
|
||||
const server = getServer(ip);
|
||||
if(server == null || !server.hasOwnProperty('manuallyHacked')) return this.props.loc.costMult;
|
||||
const discount = (server as Server).manuallyHacked? 0.9 : 1;
|
||||
if(server == null || !server.hasOwnProperty('backdoorInstalled')) return this.props.loc.costMult;
|
||||
const discount = (server as Server).backdoorInstalled? 0.9 : 1;
|
||||
return this.props.loc.costMult * discount;
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,7 @@ function initMessages() {
|
||||
"We've been watching you. Your skills are very impressive. But you're wasting " +
|
||||
"your talents. If you join us, you can put your skills to good use and change " +
|
||||
"the world for the better. If you join us, we can unlock your full potential. <br><br>" +
|
||||
"But first, you must pass our test. Find and hack our server using the Terminal. <br><br>" +
|
||||
"But first, you must pass our test. Find and install the backdoor on our server. <br><br>" +
|
||||
"-CyberSec"));
|
||||
AddToAllMessages(new Message(MessageFilenames.NiteSecTest,
|
||||
"People say that the corrupted governments and corporations rule the world. " +
|
||||
@ -161,7 +161,7 @@ function initMessages() {
|
||||
"like us. Because they can't hide from us. Because they can't fight shadows " +
|
||||
"and ideas with bullets. <br><br>" +
|
||||
"Join us, and people will fear you, too. <br><br>" +
|
||||
"Find and hack our hidden server using the Terminal. Then, we will contact you again." +
|
||||
"Find and install the backdoor on our server. Then, we will contact you again." +
|
||||
"<br><br>-NiteSec"));
|
||||
AddToAllMessages(new Message(MessageFilenames.BitRunnersTest,
|
||||
"We know what you are doing. We know what drives you. We know " +
|
||||
|
6
src/Milestones/Milestone.ts
Normal file
6
src/Milestones/Milestone.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
|
||||
export type Milestone = {
|
||||
title: string;
|
||||
fulfilled: (p: IPlayer) => boolean;
|
||||
}
|
29
src/Milestones/MilestoneHelpers.tsx
Normal file
29
src/Milestones/MilestoneHelpers.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { Page, routing } from ".././ui/navigationTracking";
|
||||
import { Root } from "./ui/Root";
|
||||
import { Player } from "../Player";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
let milestonesContainer: HTMLElement | null = null;
|
||||
|
||||
(function(){
|
||||
function setContainer() {
|
||||
milestonesContainer = document.getElementById("milestones-container");
|
||||
document.removeEventListener("DOMContentLoaded", setContainer);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", setContainer);
|
||||
})();
|
||||
|
||||
export function displayMilestonesContent() {
|
||||
if (!routing.isOn(Page.Milestones)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (milestonesContainer instanceof HTMLElement) {
|
||||
ReactDOM.render(
|
||||
<Root player={Player}/>,
|
||||
milestonesContainer
|
||||
);
|
||||
}
|
||||
}
|
99
src/Milestones/Milestones.ts
Normal file
99
src/Milestones/Milestones.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { Milestone } from "./Milestone";
|
||||
import { IMap } from "../types";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { Factions } from "../Faction/Factions";
|
||||
import { Faction } from "../Faction/Faction";
|
||||
import { GetServerByHostname } from "../Server/ServerHelpers";
|
||||
|
||||
function allFactionAugs(p: IPlayer, f: Faction): boolean {
|
||||
const factionAugs = f.augmentations.slice().filter((aug)=> "NeuroFlux Governor" !== aug);
|
||||
for(const factionAug of factionAugs) {
|
||||
if(!p.augmentations.some(aug => {return aug.name == factionAug})) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export const Milestones: Milestone[] = [
|
||||
{
|
||||
title: "Gain root access on CSEC",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
const server = GetServerByHostname("CSEC");
|
||||
if(!server || !server.hasOwnProperty('hasAdminRights')) return false;
|
||||
return (server as any).hasAdminRights;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Install the backdoor on CSEC",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
const server = GetServerByHostname("CSEC");
|
||||
if(!server || !server.hasOwnProperty('backdoorInstalled')) return false;
|
||||
return (server as any).backdoorInstalled;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Join the faction hinted at in j1.msg",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return p.factions.includes("CyberSec");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Install all the Augmentations from CSEC",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return allFactionAugs(p, Factions["CyberSec"]);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Join the faction hinted at in j2.msg",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return p.factions.includes("NiteSec");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Install all the Augmentations from NiteSec",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return allFactionAugs(p, Factions["NiteSec"]);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Join the faction hinted at in j3.msg",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return p.factions.includes("The Black Hand");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Install all the Augmentations from The Black Hand",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return allFactionAugs(p, Factions["The Black Hand"]);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Join the faction hinted at in j4.msg",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return p.factions.includes("BitRunners");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Install all the Augmentations from BitRunners",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return allFactionAugs(p, Factions["BitRunners"]);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Join the final faction",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return p.factions.includes("Daedalus");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Install the special Augmentation from Daedalus",
|
||||
fulfilled: (p: IPlayer) => {
|
||||
return p.augmentations.some(aug => aug.name == "The Red Pill")
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Install the final backdoor and free yourself.",
|
||||
fulfilled: () => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
];
|
11
src/Milestones/Quest.ts
Normal file
11
src/Milestones/Quest.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Milestone } from "./Milestone";
|
||||
|
||||
export class Quest {
|
||||
title: string;
|
||||
milestones: Milestone[];
|
||||
|
||||
constructor(title: string, milestones: Milestone[]) {
|
||||
this.title = title;
|
||||
this.milestones = milestones;
|
||||
}
|
||||
}
|
37
src/Milestones/ui/Root.tsx
Normal file
37
src/Milestones/ui/Root.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { Milestones } from "../Milestones";
|
||||
import { Milestone } from "../Milestone";
|
||||
import * as React from "react";
|
||||
|
||||
interface IProps {
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
function highestMilestone(p: IPlayer, milestones: Milestone[]): number {
|
||||
let n = -1;
|
||||
for(let i = 0; i < milestones.length; i++) {
|
||||
if(milestones[i].fulfilled(p)) n = i;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
export function Root(props: IProps) {
|
||||
const n = highestMilestone(props.player, Milestones);
|
||||
const milestones = Milestones.map((milestone: Milestone, i: number) => {
|
||||
if (i<=n+1) {
|
||||
return (<ul key={i}>
|
||||
<p>[{milestone.fulfilled(props.player)?"x":" "}] {milestone.title}</p>
|
||||
</ul>)
|
||||
}
|
||||
})
|
||||
return (<>
|
||||
<h1>Milestones</h1>
|
||||
<p>Milestones don't reward you for completing them. They are here to guide you if you're lost. They will reset when you install Augmentations.</p><br />
|
||||
|
||||
<h2>Completing fl1ght.exe</h2>
|
||||
<li>
|
||||
{milestones}
|
||||
</li>
|
||||
</>);
|
||||
}
|
@ -39,6 +39,7 @@ export const RamCostConstants: IMap<number> = {
|
||||
ScriptReadWriteRamCost: 1.0,
|
||||
ScriptArbScriptRamCost: 1.0,
|
||||
ScriptGetScriptRamCost: 0.1,
|
||||
ScriptGetRunningScriptRamCost: 0.3,
|
||||
ScriptGetHackTimeRamCost: 0.05,
|
||||
ScriptGetFavorToDonate: 0.10,
|
||||
ScriptCodingContractBaseRamCost: 10,
|
||||
@ -165,6 +166,7 @@ export const RamCosts: IMap<any> = {
|
||||
getWeakenTime: () => RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
getScriptIncome: () => RamCostConstants.ScriptGetScriptRamCost,
|
||||
getScriptExpGain: () => RamCostConstants.ScriptGetScriptRamCost,
|
||||
getRunningScript: () => RamCostConstants.ScriptGetRunningScriptRamCost,
|
||||
nFormat: () => 0,
|
||||
getTimeSinceLastAug: () => RamCostConstants.ScriptGetHackTimeRamCost,
|
||||
prompt: () => 0,
|
||||
|
@ -266,6 +266,7 @@ const defaultInterpreter = new Interpreter('', function(){});
|
||||
// the acorn interpreter has a bug where it doesn't convert arrays correctly.
|
||||
// so we have to more or less copy it here.
|
||||
function toNative(pseudoObj) {
|
||||
if(pseudoObj == null) return null;
|
||||
if(!pseudoObj.hasOwnProperty('properties') ||
|
||||
!pseudoObj.hasOwnProperty('getter') ||
|
||||
!pseudoObj.hasOwnProperty('setter') ||
|
||||
@ -366,13 +367,17 @@ function NetscriptFunctions(workerScript) {
|
||||
return workerScript.scriptRef;
|
||||
}
|
||||
|
||||
const getRunningScriptByPid = function(pid, ip, callingFnName) {
|
||||
const getRunningScriptByPid = function(pid, callingFnName) {
|
||||
if (typeof callingFnName !== "string" || callingFnName === "") {
|
||||
callingFnName = "getRunningScriptgetRunningScriptByPid";
|
||||
}
|
||||
const server = safeGetServer(ip, callingFnName);
|
||||
|
||||
return findRunningScriptByPid(pid, server);
|
||||
|
||||
for(const name of Object.keys(AllServers)) {
|
||||
const server = AllServers[name];
|
||||
const runningScript = findRunningScriptByPid(pid, server);
|
||||
if (runningScript) return runningScript;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -718,7 +723,7 @@ function NetscriptFunctions(workerScript) {
|
||||
influenceStockThroughServerHack(server, moneyGained);
|
||||
}
|
||||
if(manual) {
|
||||
server.manuallyHacked = true;
|
||||
server.backdoorInstalled = true;
|
||||
}
|
||||
return Promise.resolve(moneyGained);
|
||||
} else {
|
||||
@ -1052,8 +1057,10 @@ function NetscriptFunctions(workerScript) {
|
||||
},
|
||||
tail: function(fn, ip=workerScript.serverIp, ...scriptArgs) {
|
||||
let runningScriptObj;
|
||||
if(typeof fn === 'number') {
|
||||
runningScriptObj = getRunningScriptByPid(fn, ip, 'tail');
|
||||
if(arguments.length === 0) {
|
||||
runningScriptObj = workerScript.scriptRef;
|
||||
} else if(typeof fn === 'number') {
|
||||
runningScriptObj = getRunningScriptByPid(fn, 'tail');
|
||||
} else {
|
||||
runningScriptObj = getRunningScript(fn, ip, "tail", scriptArgs);
|
||||
}
|
||||
@ -1754,7 +1761,7 @@ function NetscriptFunctions(workerScript) {
|
||||
throw makeRuntimeErrorMsg("isRunning", "Usage: isRunning(scriptname, server, [arg1], [arg2]...)");
|
||||
}
|
||||
if(typeof fn === 'number') {
|
||||
return getRunningScriptByPid(fn, ip, 'isRunning') != null;
|
||||
return getRunningScriptByPid(fn, 'isRunning') != null;
|
||||
} else {
|
||||
return getRunningScript(fn, ip, "isRunning", scriptArgs) != null;
|
||||
}
|
||||
@ -2429,6 +2436,38 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
getRunningScript: function(fn, ip) {
|
||||
updateDynamicRam("getRunningScript", getRamCost("getRunningScript"));
|
||||
|
||||
let runningScript;
|
||||
if(arguments.length === 0) {
|
||||
runningScript = workerScript.scriptRef;
|
||||
} else if(typeof fn === 'number') {
|
||||
runningScript = getRunningScriptByPid(fn, 'getRunningScript');
|
||||
} else {
|
||||
const scriptArgs = [];
|
||||
for (var i = 2; i < arguments.length; ++i) {
|
||||
scriptArgs.push(arguments[i]);
|
||||
}
|
||||
runningScript = getRunningScript(fn, ip, 'getRunningScript', scriptArgs);
|
||||
}
|
||||
if (runningScript === null) return null;
|
||||
return {
|
||||
args: runningScript.args.slice(),
|
||||
filename: runningScript.filename,
|
||||
logs: runningScript.logs.slice(),
|
||||
offlineExpGained: runningScript.offlineExpGained,
|
||||
offlineMoneyMade: runningScript.offlineMoneyMade,
|
||||
offlineRunningTime: runningScript.offlineRunningTime,
|
||||
onlineExpGained: runningScript.onlineExpGained,
|
||||
onlineMoneyMade: runningScript.onlineMoneyMade,
|
||||
onlineRunningTime: runningScript.onlineRunningTime,
|
||||
pid: runningScript.pid,
|
||||
ramUsage: runningScript.ramUsage,
|
||||
server: runningScript.server,
|
||||
threads: runningScript.threads,
|
||||
};
|
||||
},
|
||||
getHackTime: function(ip, hack, int) {
|
||||
updateDynamicRam("getHackTime", getRamCost("getHackTime"));
|
||||
const server = safeGetServer(ip, "getHackTime");
|
||||
@ -2524,7 +2563,7 @@ function NetscriptFunctions(workerScript) {
|
||||
return Player.playtimeSinceLastAug;
|
||||
},
|
||||
prompt : function(txt) {
|
||||
if (!isString(txt)) {txt = String(txt);}
|
||||
if (!isString(txt)) {txt = JSON.stringify(txt);}
|
||||
|
||||
// The id for this popup will consist of the first 20 characters of the prompt string..
|
||||
// Thats hopefully good enough to be unique
|
||||
|
@ -180,4 +180,5 @@ export interface IPlayer {
|
||||
queryStatFromString(str: string): number;
|
||||
getIntelligenceBonus(weight: number): number;
|
||||
getCasinoWinnings(): number;
|
||||
quitJob(company: string): void;
|
||||
}
|
||||
|
@ -618,7 +618,7 @@ export function cancelationPenalty() {
|
||||
const specialIp = SpecialServerIps[this.companyName];
|
||||
if(specialIp) {
|
||||
const server = AllServers[specialIp];
|
||||
if(server && server.manuallyHacked) return 0.75;
|
||||
if(server && server.backdoorInstalled) return 0.75;
|
||||
}
|
||||
return 0.5;
|
||||
}
|
||||
@ -1745,6 +1745,11 @@ export function getNextCompanyPosition(company, entryPosType) {
|
||||
return entryPosType;
|
||||
}
|
||||
|
||||
export function quitJob(company) {
|
||||
this.companyName = "";
|
||||
delete this.jobs[company];
|
||||
}
|
||||
|
||||
export function applyForSoftwareJob(sing=false) {
|
||||
return this.applyForJob(CompanyPositions[posNames.SoftwareCompanyPositions[0]], sing);
|
||||
}
|
||||
@ -2066,7 +2071,7 @@ export function checkForFactionInvitations() {
|
||||
} else {
|
||||
if (!fulcrumsecrettechonologiesFac.isBanned && !fulcrumsecrettechonologiesFac.isMember &&
|
||||
!fulcrumsecrettechonologiesFac.alreadyInvited &&
|
||||
fulcrumSecretServer.manuallyHacked &&
|
||||
fulcrumSecretServer.backdoorInstalled &&
|
||||
checkMegacorpRequirements(LocationName.AevumFulcrumTechnologies, 250e3)) {
|
||||
invitedFactions.push(fulcrumsecrettechonologiesFac);
|
||||
}
|
||||
@ -2078,7 +2083,7 @@ export function checkForFactionInvitations() {
|
||||
var bitrunnersServer = AllServers[SpecialServerIps[SpecialServerNames.BitRunnersServer]];
|
||||
if (bitrunnersServer == null) {
|
||||
console.error("Could not find BitRunners Server");
|
||||
} else if (!bitrunnersFac.isBanned && !bitrunnersFac.isMember && bitrunnersServer.manuallyHacked &&
|
||||
} else if (!bitrunnersFac.isBanned && !bitrunnersFac.isMember && bitrunnersServer.backdoorInstalled &&
|
||||
!bitrunnersFac.alreadyInvited && homeComp.maxRam >= 128) {
|
||||
invitedFactions.push(bitrunnersFac);
|
||||
}
|
||||
@ -2088,7 +2093,7 @@ export function checkForFactionInvitations() {
|
||||
var blackhandServer = AllServers[SpecialServerIps[SpecialServerNames.TheBlackHandServer]];
|
||||
if (blackhandServer == null) {
|
||||
console.error("Could not find The Black Hand Server");
|
||||
} else if (!theblackhandFac.isBanned && !theblackhandFac.isMember && blackhandServer.manuallyHacked &&
|
||||
} else if (!theblackhandFac.isBanned && !theblackhandFac.isMember && blackhandServer.backdoorInstalled &&
|
||||
!theblackhandFac.alreadyInvited && homeComp.maxRam >= 64) {
|
||||
invitedFactions.push(theblackhandFac);
|
||||
}
|
||||
@ -2098,7 +2103,7 @@ export function checkForFactionInvitations() {
|
||||
var nitesecServer = AllServers[SpecialServerIps[SpecialServerNames.NiteSecServer]];
|
||||
if (nitesecServer == null) {
|
||||
console.error("Could not find NiteSec Server");
|
||||
} else if (!nitesecFac.isBanned && !nitesecFac.isMember && nitesecServer.manuallyHacked &&
|
||||
} else if (!nitesecFac.isBanned && !nitesecFac.isMember && nitesecServer.backdoorInstalled &&
|
||||
!nitesecFac.alreadyInvited && homeComp.maxRam >= 32) {
|
||||
invitedFactions.push(nitesecFac);
|
||||
}
|
||||
@ -2242,7 +2247,7 @@ export function checkForFactionInvitations() {
|
||||
var cybersecServer = AllServers[SpecialServerIps[SpecialServerNames.CyberSecServer]];
|
||||
if (cybersecServer == null) {
|
||||
console.error("Could not find CyberSec Server");
|
||||
} else if (!cybersecFac.isBanned && !cybersecFac.isMember && cybersecServer.manuallyHacked &&
|
||||
} else if (!cybersecFac.isBanned && !cybersecFac.isMember && cybersecServer.backdoorInstalled &&
|
||||
!cybersecFac.alreadyInvited) {
|
||||
invitedFactions.push(cybersecFac);
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ function saveAndCloseScriptEditor() {
|
||||
}
|
||||
|
||||
if (filename !== ".fconf" && !isValidFilePath(filename)) {
|
||||
dialogBoxCreate("Script filename can contain only alphanumerics, hyphens, and underscores");
|
||||
dialogBoxCreate("Script filename can contain only alphanumerics, hyphens, and underscores, and must end with an extension.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -424,4 +424,4 @@ export function findRunningScriptByPid(pid, server) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,9 @@ export class Server extends BaseServer {
|
||||
return Generic_fromJSON(Server, value.data);
|
||||
}
|
||||
|
||||
// Flag indicating whether this server has a backdoor installed by a player
|
||||
backdoorInstalled: boolean = false;
|
||||
|
||||
// Initial server security level
|
||||
// (i.e. security level when the server was created)
|
||||
baseDifficulty: number = 1;
|
||||
@ -37,10 +40,6 @@ export class Server extends BaseServer {
|
||||
// Server Security Level
|
||||
hackDifficulty: number = 1;
|
||||
|
||||
// Flag indicating whether this server has been manually hacked (ie.
|
||||
// hacked through Terminal) by the player
|
||||
manuallyHacked: boolean = false;
|
||||
|
||||
// Minimum server security level that this server can be weakened to
|
||||
minDifficulty: number = 1;
|
||||
|
||||
@ -103,7 +102,7 @@ export class Server extends BaseServer {
|
||||
|
||||
// Place some arbitrarily limit that realistically should never happen unless someone is
|
||||
// screwing around with the game
|
||||
if (this.hackDifficulty > 1000000) {this.hackDifficulty = 1000000;}
|
||||
if (this.hackDifficulty > 100) {this.hackDifficulty = 100;}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -147,3 +147,10 @@ export function getServerOnNetwork(server: Server, i: number) {
|
||||
|
||||
return AllServers[server.serversOnNetwork[i]];
|
||||
}
|
||||
|
||||
export function isBackdoorInstalled(server: Server | HacknetServer): boolean {
|
||||
if ("backdoorInstalled" in server) {
|
||||
return server.backdoorInstalled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1202,39 +1202,39 @@ export const serverMetadata: IServerMetadata[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
hackDifficulty: 10,
|
||||
hackDifficulty: 1,
|
||||
hostname: "foodnstuff",
|
||||
literature: [LiteratureNames.Sector12Crime],
|
||||
maxRamExponent: 4,
|
||||
moneyAvailable: 2000000,
|
||||
moneyAvailable: 40000,
|
||||
networkLayer: 1,
|
||||
numOpenPortsRequired: 0,
|
||||
organizationName: LocationName.Sector12FoodNStuff,
|
||||
requiredHackingSkill: 1,
|
||||
serverGrowth: 5,
|
||||
serverGrowth: 3000,
|
||||
specialName: LocationName.Sector12FoodNStuff,
|
||||
},
|
||||
{
|
||||
hackDifficulty: 10,
|
||||
hackDifficulty: 3,
|
||||
hostname: "sigma-cosmetics",
|
||||
maxRamExponent: 4,
|
||||
moneyAvailable: 2300000,
|
||||
moneyAvailable: 70000,
|
||||
networkLayer: 1,
|
||||
numOpenPortsRequired: 0,
|
||||
organizationName: "Sigma Cosmetics",
|
||||
requiredHackingSkill: 5,
|
||||
serverGrowth: 10,
|
||||
serverGrowth: 3000,
|
||||
},
|
||||
{
|
||||
hackDifficulty: 15,
|
||||
hackDifficulty: 9,
|
||||
hostname: "joesguns",
|
||||
maxRamExponent: 4,
|
||||
moneyAvailable: 2500000,
|
||||
moneyAvailable: 600000,
|
||||
networkLayer: 1,
|
||||
numOpenPortsRequired: 0,
|
||||
organizationName: LocationName.Sector12JoesGuns,
|
||||
requiredHackingSkill: 10,
|
||||
serverGrowth: 20,
|
||||
serverGrowth: 500,
|
||||
specialName: LocationName.Sector12JoesGuns,
|
||||
},
|
||||
{
|
||||
|
@ -29,6 +29,11 @@ interface IDefaultSettings {
|
||||
* Whether global keyboard shortcuts should be recognized throughout the game.
|
||||
*/
|
||||
DisableHotkeys: boolean;
|
||||
|
||||
/**
|
||||
* Whether text effects such as corruption should be visible.
|
||||
*/
|
||||
DisableTextEffects: boolean;
|
||||
|
||||
/**
|
||||
* Locale used for display numbers
|
||||
@ -108,6 +113,7 @@ const defaultSettings: IDefaultSettings = {
|
||||
CodeInstructionRunTime: 50,
|
||||
DisableASCIIArt: false,
|
||||
DisableHotkeys: false,
|
||||
DisableTextEffects: false,
|
||||
Locale: "en",
|
||||
MaxLogCapacity: 50,
|
||||
MaxPortCapacity: 50,
|
||||
@ -127,8 +133,9 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
CodeInstructionRunTime: 25,
|
||||
DisableASCIIArt: defaultSettings.DisableASCIIArt,
|
||||
DisableHotkeys: defaultSettings.DisableHotkeys,
|
||||
Editor: EditorSetting.Ace,
|
||||
EditorKeybinding: AceKeybindingSetting.Ace,
|
||||
DisableTextEffects: defaultSettings.DisableTextEffects,
|
||||
Editor: EditorSetting.CodeMirror,
|
||||
EditorKeybinding: CodeMirrorKeybindingSetting.Default,
|
||||
EditorTheme: "Monokai",
|
||||
Locale: "en",
|
||||
MaxLogCapacity: defaultSettings.MaxLogCapacity,
|
||||
|
@ -338,6 +338,7 @@ $(document).keyup(function(e) {
|
||||
let Terminal = {
|
||||
// Flags to determine whether the player is currently running a hack or an analyze
|
||||
hackFlag: false,
|
||||
backdoorFlag: false,
|
||||
analyzeFlag: false,
|
||||
actionStarted: false,
|
||||
actionTime: 0,
|
||||
@ -485,6 +486,14 @@ let Terminal = {
|
||||
Terminal.startAction();
|
||||
},
|
||||
|
||||
startBackdoor: function() {
|
||||
Terminal.backdoorFlag = true;
|
||||
|
||||
// Backdoor should take the same amount of time as hack
|
||||
Terminal.actionTime = calculateHackingTime(Player.getCurrentServer(), Player) / 4;
|
||||
Terminal.startAction();
|
||||
},
|
||||
|
||||
startAnalyze: function() {
|
||||
Terminal.analyzeFlag = true;
|
||||
Terminal.actionTime = 1;
|
||||
@ -506,14 +515,22 @@ let Terminal = {
|
||||
finishAction: function(cancelled = false) {
|
||||
if (Terminal.hackFlag) {
|
||||
Terminal.finishHack(cancelled);
|
||||
} else if (Terminal.backdoorFlag) {
|
||||
Terminal.finishBackdoor(cancelled);
|
||||
} else if (Terminal.analyzeFlag) {
|
||||
Terminal.finishAnalyze(cancelled);
|
||||
}
|
||||
|
||||
// Rename the progress bar so that the next hacks dont trigger it. Re-enable terminal
|
||||
$("#hack-progress-bar").attr('id', "old-hack-progress-bar");
|
||||
$("#hack-progress").attr('id', "old-hack-progress");
|
||||
Terminal.resetTerminalInput();
|
||||
$('input[class=terminal-input]').prop('disabled', false);
|
||||
},
|
||||
|
||||
// Complete the hack/analyze command
|
||||
finishHack: function(cancelled = false) {
|
||||
if (cancelled == false) {
|
||||
finishHack: function(cancelled = false) {
|
||||
if (!cancelled) {
|
||||
var server = Player.getCurrentServer();
|
||||
|
||||
// Calculate whether hack was successful
|
||||
@ -530,7 +547,7 @@ let Terminal = {
|
||||
hackWorldDaemon(Player.bitNodeN);
|
||||
return;
|
||||
}
|
||||
server.manuallyHacked = true;
|
||||
server.backdoorInstalled = true;
|
||||
var moneyGained = calculatePercentMoneyHacked(server, Player);
|
||||
moneyGained = Math.floor(server.moneyAvailable * moneyGained);
|
||||
|
||||
@ -548,21 +565,23 @@ let Terminal = {
|
||||
} else { // Failure
|
||||
// Player only gains 25% exp for failure? TODO Can change this later to balance
|
||||
Player.gainHackingExp(expGainedOnFailure)
|
||||
post(`Failed to hack ${server.hostname}. Gained ${numeralWrapper.formatExp(expGainedOnFailure)} hacking exp`);
|
||||
}
|
||||
}
|
||||
|
||||
// Rename the progress bar so that the next hacks dont trigger it. Re-enable terminal
|
||||
$("#hack-progress-bar").attr('id', "old-hack-progress-bar");
|
||||
$("#hack-progress").attr('id', "old-hack-progress");
|
||||
Terminal.resetTerminalInput();
|
||||
$('input[class=terminal-input]').prop('disabled', false);
|
||||
|
||||
post(`Failed to hack ${server.hostname}. Gained ${numeralWrapper.formatExp(expGainedOnFailure)} hacking exp`);
|
||||
}
|
||||
}
|
||||
Terminal.hackFlag = false;
|
||||
},
|
||||
|
||||
finishBackdoor: function(cancelled = false) {
|
||||
if(!cancelled){
|
||||
let server = Player.getCurrentServer();
|
||||
server.backdoorInstalled = true;
|
||||
postElement(<>Backdoor successful!</>);
|
||||
}
|
||||
Terminal.backdoorFlag = false;
|
||||
},
|
||||
|
||||
finishAnalyze: function(cancelled = false) {
|
||||
if (cancelled == false) {
|
||||
if (!cancelled) {
|
||||
let currServ = Player.getCurrentServer();
|
||||
const isHacknet = currServ instanceof HacknetServer;
|
||||
post(currServ.hostname + ": ");
|
||||
@ -609,12 +628,6 @@ let Terminal = {
|
||||
}
|
||||
}
|
||||
Terminal.analyzeFlag = false;
|
||||
|
||||
// Rename the progress bar so that the next hacks dont trigger it. Re-enable terminal
|
||||
$("#hack-progress-bar").attr('id', "old-hack-progress-bar");
|
||||
$("#hack-progress").attr('id', "old-hack-progress");
|
||||
Terminal.resetTerminalInput();
|
||||
$('input[class=terminal-input]').prop('disabled', false);
|
||||
},
|
||||
|
||||
executeCommands : function(commands) {
|
||||
@ -728,8 +741,8 @@ let Terminal = {
|
||||
return args;
|
||||
},
|
||||
|
||||
executeCommand : function(command) {
|
||||
if (Terminal.hackFlag || Terminal.analyzeFlag) {
|
||||
executeCommand : function(command) {
|
||||
if (Terminal.hackFlag || Terminal.backdoorFlag || Terminal.analyzeFlag) {
|
||||
postError(`Cannot execute command (${command}) while an action is in progress`);
|
||||
return;
|
||||
}
|
||||
@ -893,6 +906,24 @@ let Terminal = {
|
||||
return;
|
||||
}
|
||||
Terminal.startAnalyze();
|
||||
break;
|
||||
case "backdoor":
|
||||
if (commandArray.length !== 1) {
|
||||
post("Incorrect usage of backdoor command. Usage: backdoor");
|
||||
return;
|
||||
}
|
||||
|
||||
if (s.purchasedByPlayer) {
|
||||
postError("Cannot use backdoor on your own machines! You are currently connected to your home PC or one of your purchased servers");
|
||||
} else if (!s.hasAdminRights) {
|
||||
postError("You do not have admin rights for this machine! Cannot backdoor");
|
||||
} else if (s.requiredHackingSkill > Player.hacking_skill) {
|
||||
postError("Your hacking skill is not high enough to use backdoor on this machine. Try analyzing the machine to determine the required hacking skill");
|
||||
} else if (s instanceof HacknetServer) {
|
||||
postError("Cannot use backdoor on this type of Server")
|
||||
} else {
|
||||
Terminal.startBackdoor();
|
||||
}
|
||||
break;
|
||||
case "buy":
|
||||
if (SpecialServerIps.hasOwnProperty("Darkweb Server")) {
|
||||
@ -938,10 +969,10 @@ let Terminal = {
|
||||
break;
|
||||
}
|
||||
case "cd": {
|
||||
if (commandArray.length !== 2) {
|
||||
if (commandArray.length > 2) {
|
||||
postError("Incorrect number of arguments. Usage: cd [dir]");
|
||||
} else {
|
||||
let dir = commandArray[1];
|
||||
let dir = commandArray.length === 2 ? commandArray[1] : "/";
|
||||
|
||||
let evaledDir;
|
||||
if (dir === "/") {
|
||||
@ -955,6 +986,12 @@ let Terminal = {
|
||||
postError("Invalid path. Failed to change directories");
|
||||
return;
|
||||
}
|
||||
|
||||
const server = Player.getCurrentServer();
|
||||
if(!server.scripts.some(script => script.filename.startsWith(evaledDir))) {
|
||||
postError("Invalid path. Failed to change directories");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Terminal.currDir = evaledDir;
|
||||
@ -1785,7 +1822,7 @@ let Terminal = {
|
||||
let code = ""
|
||||
if(filename.endsWith(".ns") || filename.endsWith(".js")) {
|
||||
code = `export async function main(ns) {
|
||||
|
||||
|
||||
}`;
|
||||
}
|
||||
Engine.loadScriptEditorContent(filepath, code);
|
||||
@ -2329,7 +2366,7 @@ let Terminal = {
|
||||
runningScriptObj.threads = numThreads;
|
||||
|
||||
if (startWorkerScript(runningScriptObj, server)) {
|
||||
post("Running script with " + numThreads + " thread(s) and args: " + arrayToString(args) + ".");
|
||||
post(`Running script with ${numThreads} thread(s), pid ${runningScriptObj.pid} and args: ${arrayToString(args)}.`);
|
||||
} else {
|
||||
postError(`Failed to start script`);
|
||||
}
|
||||
@ -2380,4 +2417,4 @@ let Terminal = {
|
||||
},
|
||||
};
|
||||
|
||||
export {postNetburnerText, Terminal};
|
||||
export {postNetburnerText, Terminal};
|
||||
|
@ -5,6 +5,7 @@ export const TerminalHelpText: string =
|
||||
"Type 'help name' to learn more about the command 'name'<br><br>" +
|
||||
'alias [-g] [name="value"] Create or display Terminal aliases<br>' +
|
||||
"analyze Get information about the current machine <br>" +
|
||||
'backdoor Install a backdoor on the current machine <br>' +
|
||||
"buy [-l/program] Purchase a program through the Dark Web<br>" +
|
||||
"cat [file] Display a .msg, .lit, or .txt file<br>" +
|
||||
"cd [dir] Change to a new directory<br>" +
|
||||
@ -65,11 +66,14 @@ export const HelpTexts: IMap<string> = {
|
||||
"server details such as the hostname, whether the player has root access, what ports are opened/closed, and also " +
|
||||
"hacking-related information such as an estimated chance to successfully hack, an estimate of how much money is " +
|
||||
"available on the server, etc.",
|
||||
backdoor: "backdoor<br>" +
|
||||
"Install a backdoor on the current machine, grants a secret bonus depending on the machine.<br>" +
|
||||
"Requires root access to run.<br>",
|
||||
buy: "buy [-l / program]<br>" +
|
||||
"Purchase a program through the Dark Web. Requires a TOR router to use.<br><br>" +
|
||||
"If this command is ran with the '-l' flag, it will display a list of all programs that can be bought through the " +
|
||||
"dark web to the Terminal, as well as their costs.<br><br>" +
|
||||
"Otherwise, the name of the program must be passed in as a parameter. This is name is NOT case-sensitive.",
|
||||
"Otherwise, the name of the program must be passed in as a parameter. This name is NOT case-sensitive.",
|
||||
cat: "cat [file]<br>" +
|
||||
"Display message (.msg), literature (.lit), or text (.txt) files. Examples:<br><br>" +
|
||||
"cat j1.msg<br>" +
|
||||
|
@ -17,6 +17,7 @@ import { AllServers } from "../Server/AllServers";
|
||||
const commands = [
|
||||
"alias",
|
||||
"analyze",
|
||||
"backdoor",
|
||||
"cat",
|
||||
"cd",
|
||||
"check",
|
||||
|
@ -247,7 +247,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 2.5,
|
||||
gen: () => {
|
||||
const len: number = getRandomInt(1, 25);
|
||||
const len: number = getRandomInt(3, 25);
|
||||
const arr: number[] = [];
|
||||
arr.length = len;
|
||||
for (let i: number = 0; i < arr.length; ++i) {
|
||||
@ -291,7 +291,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
difficulty: 3,
|
||||
gen: () => {
|
||||
const intervals: number[][] = [];
|
||||
const numIntervals: number = getRandomInt(1, 20);
|
||||
const numIntervals: number = getRandomInt(3, 20);
|
||||
for (let i: number = 0; i < numIntervals; ++i) {
|
||||
const start: number = getRandomInt(1, 25);
|
||||
const end: number = start + getRandomInt(1, 10);
|
||||
@ -403,7 +403,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 1,
|
||||
gen: () => {
|
||||
const len: number = getRandomInt(1, 50);
|
||||
const len: number = getRandomInt(3, 50);
|
||||
const arr: number[] = [];
|
||||
arr.length = len;
|
||||
for (let i: number = 0; i < len; ++i) {
|
||||
@ -439,7 +439,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 2,
|
||||
gen: () => {
|
||||
const len: number = getRandomInt(1, 50);
|
||||
const len: number = getRandomInt(3, 50);
|
||||
const arr: number[] = [];
|
||||
arr.length = len;
|
||||
for (let i: number = 0; i < len; ++i) {
|
||||
@ -473,7 +473,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 5,
|
||||
gen: () => {
|
||||
const len: number = getRandomInt(1, 50);
|
||||
const len: number = getRandomInt(3, 50);
|
||||
const arr: number[] = [];
|
||||
arr.length = len;
|
||||
for (let i: number = 0; i < len; ++i) {
|
||||
@ -518,7 +518,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
difficulty: 8,
|
||||
gen: () => {
|
||||
const k: number = getRandomInt(2, 10);
|
||||
const len: number = getRandomInt(1, 50);
|
||||
const len: number = getRandomInt(3, 50);
|
||||
const prices: number[] = [];
|
||||
prices.length = len;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
@ -602,7 +602,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
difficulty: 5,
|
||||
gen: () => {
|
||||
const triangle: number[][] = [];
|
||||
const levels: number = getRandomInt(1, 12);
|
||||
const levels: number = getRandomInt(3, 12);
|
||||
triangle.length = levels;
|
||||
|
||||
for (let row = 0; row < levels; ++row) {
|
||||
@ -645,8 +645,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 3,
|
||||
gen: () => {
|
||||
const numRows: number = getRandomInt(1, 14);
|
||||
const numColumns: number = getRandomInt(1, 14);
|
||||
const numRows: number = getRandomInt(2, 14);
|
||||
const numColumns: number = getRandomInt(2, 14);
|
||||
|
||||
return [numRows, numColumns];
|
||||
},
|
||||
@ -687,8 +687,8 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 5,
|
||||
gen: () => {
|
||||
const numRows: number = getRandomInt(1, 12);
|
||||
const numColumns: number = getRandomInt(1, 12);
|
||||
const numRows: number = getRandomInt(2, 12);
|
||||
const numColumns: number = getRandomInt(2, 12);
|
||||
|
||||
const grid: number[][] = [];
|
||||
grid.length = numRows;
|
||||
@ -754,7 +754,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [
|
||||
},
|
||||
difficulty: 10,
|
||||
gen: () => {
|
||||
const len: number = getRandomInt(2, 20);
|
||||
const len: number = getRandomInt(6, 20);
|
||||
let chars: string[] = [];
|
||||
chars.length = len;
|
||||
|
||||
|
@ -72,6 +72,7 @@ import {
|
||||
processStockPrices,
|
||||
displayStockMarketContent
|
||||
} from "./StockMarket/StockMarket";
|
||||
import { displayMilestonesContent } from "./Milestones/MilestoneHelpers";
|
||||
import { Terminal, postNetburnerText } from "./Terminal";
|
||||
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
|
||||
import {
|
||||
@ -216,6 +217,7 @@ const Engine = {
|
||||
factionsContent: null,
|
||||
factionContent: null,
|
||||
augmentationsContent: null,
|
||||
milestonesContent: null,
|
||||
tutorialContent: null,
|
||||
infiltrationContent: null,
|
||||
stockMarketContent: null,
|
||||
@ -311,6 +313,14 @@ const Engine = {
|
||||
MainMenuLinks.Augmentations.classList.add("active");
|
||||
},
|
||||
|
||||
loadMilestonesContent: function() {
|
||||
Engine.hideAllContent();
|
||||
Engine.Display.milestonesContent.style.display = "block";
|
||||
routing.navigateTo(Page.Milestones);
|
||||
displayMilestonesContent();
|
||||
MainMenuLinks.Milestones.classList.add("active");
|
||||
},
|
||||
|
||||
loadTutorialContent: function() {
|
||||
Engine.hideAllContent();
|
||||
Engine.Display.tutorialContent.style.display = "block";
|
||||
@ -496,6 +506,7 @@ const Engine = {
|
||||
Engine.Display.augmentationsContent.style.display = "none";
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.augmentationsContent);
|
||||
|
||||
Engine.Display.milestonesContent.style.display = "none";
|
||||
Engine.Display.tutorialContent.style.display = "none";
|
||||
|
||||
Engine.Display.locationContent.style.display = "none";
|
||||
@ -550,6 +561,7 @@ const Engine = {
|
||||
MainMenuLinks.Bladeburner.classList.remove("active");
|
||||
MainMenuLinks.Corporation.classList.remove("active");
|
||||
MainMenuLinks.Gang.classList.remove("active");
|
||||
MainMenuLinks.Milestones.classList.remove("active");
|
||||
MainMenuLinks.Tutorial.classList.remove("active");
|
||||
MainMenuLinks.Options.classList.remove("active");
|
||||
MainMenuLinks.DevMenu.classList.remove("active");
|
||||
@ -1055,6 +1067,7 @@ const Engine = {
|
||||
const bladeburner = document.getElementById("bladeburner-tab");
|
||||
const corp = document.getElementById("corporation-tab");
|
||||
const gang = document.getElementById("gang-tab");
|
||||
const milestones = document.getElementById("milestones-tab");
|
||||
const tutorial = document.getElementById("tutorial-tab");
|
||||
const options = document.getElementById("options-tab");
|
||||
const dev = document.getElementById("dev-tab");
|
||||
@ -1155,7 +1168,8 @@ const Engine = {
|
||||
</>);
|
||||
// Close main menu accordions for loaded game
|
||||
var visibleMenuTabs = [terminal, createScript, activeScripts, stats,
|
||||
hacknetnodes, city, tutorial, options, dev];
|
||||
hacknetnodes, city, milestones, tutorial,
|
||||
options, dev];
|
||||
if (Player.firstFacInvRecvd) {visibleMenuTabs.push(factions);}
|
||||
else {factions.style.display = "none";}
|
||||
if (Player.firstAugPurchased) {visibleMenuTabs.push(augmentations);}
|
||||
@ -1214,7 +1228,7 @@ const Engine = {
|
||||
|
||||
Engine.openMainMenuHeader(
|
||||
[terminal, createScript, activeScripts, stats,
|
||||
hacknetnodes, city,
|
||||
hacknetnodes, city, milestones,
|
||||
tutorial, options]
|
||||
);
|
||||
|
||||
@ -1257,6 +1271,8 @@ const Engine = {
|
||||
Engine.Display.augmentationsContent = document.getElementById("augmentations-container");
|
||||
Engine.Display.augmentationsContent.style.display = "none";
|
||||
|
||||
Engine.Display.milestonesContent = document.getElementById("milestones-container");
|
||||
Engine.Display.milestonesContent.style.display = "none";
|
||||
|
||||
Engine.Display.tutorialContent = document.getElementById("tutorial-container");
|
||||
Engine.Display.tutorialContent.style.display = "none";
|
||||
@ -1398,6 +1414,11 @@ const Engine = {
|
||||
return false;
|
||||
});
|
||||
|
||||
MainMenuLinks.Milestones.addEventListener("click", function() {
|
||||
Engine.loadMilestonesContent();
|
||||
return false;
|
||||
});
|
||||
|
||||
MainMenuLinks.Tutorial.addEventListener("click", function() {
|
||||
Engine.loadTutorialContent();
|
||||
return false;
|
||||
@ -1480,6 +1501,7 @@ const Engine = {
|
||||
document.getElementById("active-scripts-menu-link").removeAttribute("class");
|
||||
document.getElementById("hacknet-nodes-menu-link").removeAttribute("class");
|
||||
document.getElementById("city-menu-link").removeAttribute("class");
|
||||
document.getElementById("milestones-menu-link").removeAttribute("class");
|
||||
document.getElementById("tutorial-menu-link").removeAttribute("class");
|
||||
|
||||
// Copy Save Data to Clipboard
|
||||
|
@ -29,3 +29,5 @@ import "../css/resleeving.scss";
|
||||
import "../css/treant.css";
|
||||
import "../css/grid.min.css";
|
||||
import "../css/dev-menu.css";
|
||||
import "../css/casino.scss";
|
||||
import "../css/milestones.scss";
|
||||
|
@ -102,9 +102,12 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<li id="help-menu-header-li">
|
||||
<button id="help-menu-header" class="mainmenu-accordion-header"> Help </button>
|
||||
</li>
|
||||
<li id="tutorial-tab" class="mainmenu-accordion-panel">
|
||||
<button id="tutorial-menu-link"> Tutorial </button>
|
||||
</li>
|
||||
<li id="milestones-tab" class="mainmenu-accordion-panel">
|
||||
<button id="milestones-menu-link"> Milestones </button>
|
||||
</li>
|
||||
<li id="tutorial-tab" class="mainmenu-accordion-panel">
|
||||
<button id="tutorial-menu-link"> Tutorial </button>
|
||||
</li>
|
||||
<li id="options-tab" class="mainmenu-accordion-panel">
|
||||
<button id="options-menu-link"> Options </button>
|
||||
</li>
|
||||
@ -228,6 +231,10 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<!-- Augmentations -->
|
||||
<div id="augmentations-container" class="generic-menupage-container"></div>
|
||||
|
||||
<!-- Milestones content -->
|
||||
<div id="milestones-container" class="generic-menupage-container">
|
||||
</div>
|
||||
|
||||
<!-- Tutorial content -->
|
||||
<div id="tutorial-container" class="generic-menupage-container">
|
||||
<h1> Tutorial (AKA Links to Documentation) </h1>
|
||||
@ -534,6 +541,16 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
||||
<input class="optionCheckbox" type="checkbox" name="settingsDisableASCIIArt" id="settingsDisableASCIIArt">
|
||||
</fieldset>
|
||||
|
||||
<!-- Disable text effects such as corruption. -->
|
||||
<fieldset>
|
||||
<label for="settingsDisableTextEffects" class="tooltip">Disable Text Effects:
|
||||
<span class="tooltiptexthigh">
|
||||
If this is set, text effects will not be displayed. This can help if text is difficult to read in certain areas.
|
||||
</span>
|
||||
</label>
|
||||
<input class="optionCheckbox" type="checkbox" name="settingsDisableTextEffects" id="settingsDisableTextEffects">
|
||||
</fieldset>
|
||||
|
||||
<!-- Locale for displaying numbers -->
|
||||
<fieldset>
|
||||
<label for="settingsLocale" class="tooltip">Locale:
|
||||
|
@ -27,7 +27,7 @@ export function WorkerScriptAccordion(props: IProps): React.ReactElement {
|
||||
const scriptRef = workerScript.scriptRef;
|
||||
|
||||
const logClickHandler = logBoxCreate.bind(null, scriptRef);
|
||||
const killScript = killWorkerScript.bind(null, scriptRef, scriptRef.server);
|
||||
const killScript = killWorkerScript.bind(null, scriptRef as any, scriptRef.server);
|
||||
|
||||
function killScriptClickHandler() {
|
||||
killScript();
|
||||
|
@ -24,7 +24,7 @@ function toggleHeader(open: boolean, elems: HTMLElement[], links: HTMLElement[])
|
||||
elems[i].style.maxHeight = elems[i].scrollHeight + "px";
|
||||
} else {
|
||||
elems[i].style.opacity = "0";
|
||||
elems[i].style.maxHeight = null;
|
||||
elems[i].style.maxHeight = "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,13 +35,13 @@ function toggleHeader(open: boolean, elems: HTMLElement[], links: HTMLElement[])
|
||||
links[i].style.pointerEvents = "auto";
|
||||
} else {
|
||||
links[i].style.opacity = "0";
|
||||
links[i].style.maxHeight = null;
|
||||
links[i].style.maxHeight = "";
|
||||
links[i].style.pointerEvents = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function initializeMainMenuHeaders(p: IPlayer, dev: boolean=false): boolean {
|
||||
export function initializeMainMenuHeaders(p: IPlayer, dev = false): boolean {
|
||||
function safeGetElement(id: string): HTMLElement {
|
||||
const elem: HTMLElement | null = document.getElementById(id);
|
||||
if (elem == null) {
|
||||
@ -68,16 +68,16 @@ export function initializeMainMenuHeaders(p: IPlayer, dev: boolean=false): boole
|
||||
|
||||
createProgram.style.display = p.firstProgramAvailable ? "list-item" : "none";
|
||||
|
||||
this.classList.toggle("opened");
|
||||
(this as any).classList.toggle("opened");
|
||||
|
||||
const elems: HTMLElement[] = [terminal, createScript, activeScripts, createProgram];
|
||||
const links: HTMLElement[] = [MainMenuLinks.Terminal!, MainMenuLinks.ScriptEditor!, MainMenuLinks.ActiveScripts!, MainMenuLinks.CreateProgram!];
|
||||
if (terminal.style.maxHeight) {
|
||||
toggleHeader(false, elems, links);
|
||||
createProgramNot!.style.display = "none";
|
||||
createProgramNot.style.display = "none";
|
||||
} else {
|
||||
toggleHeader(true, elems, links);
|
||||
createProgramNot!.style.display = "block"
|
||||
createProgramNot.style.display = "block"
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ export function initializeMainMenuHeaders(p: IPlayer, dev: boolean=false): boole
|
||||
|
||||
sleeves.style.display = p.sleeves.length > 0 ? "list-item" : "none";
|
||||
|
||||
this.classList.toggle("opened");
|
||||
(this as any).classList.toggle("opened");
|
||||
|
||||
const elems: HTMLElement[] = [stats, factions, augmentations, hacknetnodes, sleeves];
|
||||
const links: HTMLElement[] = [MainMenuLinks.Stats!, MainMenuLinks.Factions!, MainMenuLinks.Augmentations!, MainMenuLinks.HacknetNodes!, MainMenuLinks.Sleeves!];
|
||||
@ -117,7 +117,7 @@ export function initializeMainMenuHeaders(p: IPlayer, dev: boolean=false): boole
|
||||
corporation.style.display = p.hasCorporation() ? "list-item" : "none";
|
||||
gang.style.display = p.inGang() ? "list-item" : "none";
|
||||
|
||||
this.classList.toggle("opened");
|
||||
(this as any).classList.toggle("opened");
|
||||
|
||||
const elems: HTMLElement[] = [city, travel, job, stockmarket, bladeburner, corporation, gang];
|
||||
const links: HTMLElement[] = [MainMenuLinks.City!, MainMenuLinks.Travel!, MainMenuLinks.Job!, MainMenuLinks.StockMarket!, MainMenuLinks.Bladeburner!, MainMenuLinks.Corporation!, MainMenuLinks.Gang!];
|
||||
@ -129,13 +129,14 @@ export function initializeMainMenuHeaders(p: IPlayer, dev: boolean=false): boole
|
||||
}
|
||||
|
||||
MainMenuHeaders.Help.onclick = function() {
|
||||
const milestones: HTMLElement = safeGetElement("milestones-tab");
|
||||
const tutorial: HTMLElement = safeGetElement("tutorial-tab");
|
||||
const options: HTMLElement = safeGetElement("options-tab");
|
||||
|
||||
this.classList.toggle("opened");
|
||||
(this as any).classList.toggle("opened");
|
||||
|
||||
const elems: HTMLElement[] = [tutorial, options];
|
||||
const links: HTMLElement[] = [MainMenuLinks.Tutorial!, MainMenuLinks.Options!];
|
||||
const elems: HTMLElement[] = [milestones, tutorial, options];
|
||||
const links: HTMLElement[] = [MainMenuLinks.Milestones!, MainMenuLinks.Tutorial!, MainMenuLinks.Options!];
|
||||
|
||||
if (dev) {
|
||||
elems.push(safeGetElement("dev-tab"));
|
||||
|
@ -19,6 +19,7 @@ interface IMainMenuLinks {
|
||||
Bladeburner: HTMLElement | null;
|
||||
Corporation: HTMLElement | null;
|
||||
Gang: HTMLElement | null;
|
||||
Milestones: HTMLElement | null;
|
||||
Tutorial: HTMLElement | null;
|
||||
Options: HTMLElement | null;
|
||||
DevMenu: HTMLElement | null;
|
||||
@ -41,6 +42,7 @@ export const MainMenuLinks: IMainMenuLinks = {
|
||||
Bladeburner: null,
|
||||
Corporation: null,
|
||||
Gang: null,
|
||||
Milestones: null,
|
||||
Tutorial: null,
|
||||
Options: null,
|
||||
DevMenu: null,
|
||||
@ -54,7 +56,7 @@ export function initializeMainMenuLinks(): boolean {
|
||||
throw new Error(`clearEventListeners() failed for element with id: ${id}`);
|
||||
}
|
||||
|
||||
return elem!;
|
||||
return elem;
|
||||
}
|
||||
|
||||
MainMenuLinks.Terminal = safeGetLink("terminal-menu-link");
|
||||
@ -73,6 +75,7 @@ export function initializeMainMenuLinks(): boolean {
|
||||
MainMenuLinks.Bladeburner = safeGetLink("bladeburner-menu-link");
|
||||
MainMenuLinks.Corporation = safeGetLink("corporation-menu-link");
|
||||
MainMenuLinks.Gang = safeGetLink("gang-menu-link");
|
||||
MainMenuLinks.Milestones = safeGetLink("milestones-menu-link");
|
||||
MainMenuLinks.Tutorial = safeGetLink("tutorial-menu-link");
|
||||
MainMenuLinks.Options = document.getElementById("options-menu-link"); // This click listener is already set, so don't clear it
|
||||
MainMenuLinks.DevMenu = safeGetLink("dev-menu-link");
|
||||
|
53
src/ui/React/CorruptableText.tsx
Normal file
53
src/ui/React/CorruptableText.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
function replace(str: string, i: number, char: string): string {
|
||||
return str.substring(0, i) + char + str.substring(i + 1);
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
content: string;
|
||||
}
|
||||
|
||||
function randomize(char: string): string {
|
||||
const randFrom = (str: string): string => str[Math.floor(Math.random()*str.length)];
|
||||
const classes = [
|
||||
"abcdefghijklmnopqrstuvwxyz",
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||
"1234567890",
|
||||
" _",
|
||||
"()[]{}<>",
|
||||
];
|
||||
const other = `!@#$%^&*()_+|\\';"/.,?\`~`;
|
||||
|
||||
for(const c of classes) {
|
||||
if (c.includes(char)) return randFrom(c);
|
||||
}
|
||||
|
||||
return randFrom(other);
|
||||
}
|
||||
|
||||
export function CorruptableText(props: IProps) {
|
||||
const [content, setContent] = useState(props.content);
|
||||
|
||||
useEffect(() => {
|
||||
let counter = 5;
|
||||
const id = setInterval(() => {
|
||||
counter--;
|
||||
if (counter > 0)
|
||||
return;
|
||||
counter = Math.random() * 5;
|
||||
const index = Math.random() * content.length;
|
||||
const letter = content.charAt(index);
|
||||
setContent(replace(content, index, randomize(letter)));
|
||||
setTimeout(() => {
|
||||
setContent(content);
|
||||
}, 50);
|
||||
}, 100);
|
||||
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <span>{content}</span>
|
||||
}
|
43
src/ui/React/MuiButton.tsx
Normal file
43
src/ui/React/MuiButton.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Wrapper around material-ui's Button component that styles it with
|
||||
* Bitburner's UI theme
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Button, ButtonProps, makeStyles } from "@material-ui/core";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
// Tries to emulate StdButton in buttons.scss
|
||||
root: {
|
||||
backgroundColor: "#555",
|
||||
border: "1px solid #333",
|
||||
color: "white",
|
||||
margin: "5px",
|
||||
padding: "3px 5px",
|
||||
"&:hover": {
|
||||
backgroundColor: "#666",
|
||||
},
|
||||
},
|
||||
textPrimary: {
|
||||
color: "rgb( 144, 202, 249)",
|
||||
},
|
||||
textSecondary: {
|
||||
color: "rgb(244, 143, 177)",
|
||||
},
|
||||
disabled: {
|
||||
backgroundColor: "#333",
|
||||
color: "#fff",
|
||||
cursor: "default",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export const MuiButton: React.FC<ButtonProps> = (props: ButtonProps) => {
|
||||
return (
|
||||
<Button {...props}
|
||||
classes={{
|
||||
...useStyles(),
|
||||
...props.classes,
|
||||
}} />
|
||||
)
|
||||
}
|
29
src/ui/React/MuiPaper.tsx
Normal file
29
src/ui/React/MuiPaper.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Wrapper around material-ui's Button component that styles it with
|
||||
* Bitburner's UI theme
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Paper, PaperProps, makeStyles } from "@material-ui/core";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
backgroundColor: "rgb(30, 30, 30)",
|
||||
border: "2px solid #000",
|
||||
borderRadius: "10px",
|
||||
display: "inline-block",
|
||||
flexWrap: "wrap",
|
||||
padding: "10px",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export const MuiPaper: React.FC<PaperProps> = (props: PaperProps) => {
|
||||
return (
|
||||
<Paper {...props}
|
||||
classes={{
|
||||
...useStyles(),
|
||||
...props.classes,
|
||||
}} />
|
||||
)
|
||||
}
|
77
src/ui/React/MuiTextField.tsx
Normal file
77
src/ui/React/MuiTextField.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Wrapper around material-ui's TextField component that styles it with
|
||||
* Bitburner's UI theme
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { makeStyles, TextField, TextFieldProps } from "@material-ui/core";
|
||||
|
||||
const backgroundColorStyles = {
|
||||
backgroundColor: 'rgba(57, 54, 54, 0.9)',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(70, 70, 70, 0.9)',
|
||||
},
|
||||
};
|
||||
|
||||
const formControlStyles = {
|
||||
border: '1px solid #e2e2e1',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 4,
|
||||
color: 'white',
|
||||
...backgroundColorStyles,
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
...formControlStyles,
|
||||
},
|
||||
});
|
||||
|
||||
const useInputStyles = makeStyles({
|
||||
root: {
|
||||
...backgroundColorStyles,
|
||||
color: 'white',
|
||||
},
|
||||
focused: {
|
||||
backgroundColor: 'rgba(70, 70, 70, 0.9)',
|
||||
},
|
||||
disabled: {
|
||||
color: 'white',
|
||||
},
|
||||
});
|
||||
|
||||
const useLabelStyles = makeStyles({
|
||||
root: {
|
||||
color: 'white',
|
||||
},
|
||||
focused: {
|
||||
color: 'white !important', // Need important to override styles from FormLabel
|
||||
},
|
||||
disabled: {
|
||||
color: 'white !important', // Need important to override styles from FormLabel
|
||||
},
|
||||
})
|
||||
|
||||
export const MuiTextField: React.FC<TextFieldProps> = (props: TextFieldProps) => {
|
||||
return (
|
||||
<TextField {...props}
|
||||
classes={{
|
||||
...useStyles(),
|
||||
...props.classes,
|
||||
}}
|
||||
InputProps={{
|
||||
classes: {
|
||||
...useInputStyles(),
|
||||
...props.InputProps?.classes,
|
||||
},
|
||||
...props.InputProps,
|
||||
}}
|
||||
InputLabelProps={{
|
||||
classes: {
|
||||
...useLabelStyles(),
|
||||
...props.InputLabelProps?.classes,
|
||||
},
|
||||
...props.InputLabelProps,
|
||||
}} />
|
||||
)
|
||||
}
|
@ -11,7 +11,7 @@ interface IStdButtonProps {
|
||||
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||
style?: object;
|
||||
text: string | JSX.Element;
|
||||
tooltip?: string;
|
||||
tooltip?: string | JSX.Element;
|
||||
}
|
||||
|
||||
type IInnerHTMLMarkup = {
|
||||
@ -30,10 +30,16 @@ export function StdButton(props: IStdButtonProps): React.ReactElement {
|
||||
}
|
||||
|
||||
// Tooltip will be set using inner HTML
|
||||
let tooltipMarkup: IInnerHTMLMarkup | null;
|
||||
let tooltip;
|
||||
if (hasTooltip) {
|
||||
tooltipMarkup = {
|
||||
__html: props.tooltip!
|
||||
if(typeof props.tooltip === 'string') {
|
||||
let tooltipMarkup: IInnerHTMLMarkup | null;
|
||||
tooltipMarkup = {
|
||||
__html: props.tooltip!
|
||||
}
|
||||
tooltip = <span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
|
||||
} else {
|
||||
tooltip = <span className={"tooltiptext"}>{props.tooltip!}</span>
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +48,7 @@ export function StdButton(props: IStdButtonProps): React.ReactElement {
|
||||
{props.text}
|
||||
{
|
||||
hasTooltip &&
|
||||
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
|
||||
tooltip
|
||||
}
|
||||
</button>
|
||||
)
|
||||
|
@ -48,6 +48,11 @@ export enum Page {
|
||||
*/
|
||||
Augmentations = "Augmentations",
|
||||
|
||||
/**
|
||||
* List of milestones that players should follow.
|
||||
*/
|
||||
Milestones = "Milestones",
|
||||
|
||||
/**
|
||||
* A collection of in-game material to learn about the game.
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
import * as numeral from 'numeral';
|
||||
import numeral from 'numeral';
|
||||
import 'numeral/locales/bg';
|
||||
import 'numeral/locales/cs';
|
||||
import 'numeral/locales/da-dk';
|
||||
@ -18,7 +18,7 @@ import 'numeral/locales/ru';
|
||||
|
||||
class NumeralFormatter {
|
||||
// Default Locale
|
||||
defaultLocale: string = "en";
|
||||
defaultLocale = "en";
|
||||
|
||||
constructor() {
|
||||
this.defaultLocale = 'en';
|
||||
@ -80,12 +80,12 @@ class NumeralFormatter {
|
||||
return this.format(n, "0.00")+"GB";
|
||||
}
|
||||
|
||||
formatPercentage(n: number, decimalPlaces: number=2): string {
|
||||
formatPercentage(n: number, decimalPlaces = 2): string {
|
||||
const formatter: string = "0." + "0".repeat(decimalPlaces) + "%";
|
||||
return this.format(n, formatter);
|
||||
}
|
||||
|
||||
formatServerSecurity(n: number, decimalPlaces: number=2): string {
|
||||
formatServerSecurity(n: number, decimalPlaces = 2): string {
|
||||
return this.format(n, "0,0.000");
|
||||
}
|
||||
|
||||
@ -127,6 +127,10 @@ class NumeralFormatter {
|
||||
}
|
||||
return this.format(n, "0.000a");
|
||||
}
|
||||
|
||||
formatInfiltrationSecurity(n: number): string {
|
||||
return this.format(n, "0.000a");
|
||||
}
|
||||
}
|
||||
|
||||
export const numeralWrapper = new NumeralFormatter();
|
||||
|
@ -24,6 +24,7 @@ function setSettingsLabels() {
|
||||
const autosaveInterval = document.getElementById("settingsAutosaveIntervalValLabel");
|
||||
const disableHotkeys = document.getElementById("settingsDisableHotkeys");
|
||||
const disableASCIIArt = document.getElementById("settingsDisableASCIIArt");
|
||||
const disableTextEffects = document.getElementById("settingsDisableTextEffects");
|
||||
const locale = document.getElementById("settingsLocale");
|
||||
|
||||
//Initialize values on labels
|
||||
@ -38,6 +39,7 @@ function setSettingsLabels() {
|
||||
setAutosaveLabel(autosaveInterval);
|
||||
disableHotkeys.checked = Settings.DisableHotkeys;
|
||||
disableASCIIArt.checked = Settings.CityListView;
|
||||
disableTextEffects.checked = Settings.DisableTextEffects;
|
||||
locale.value = Settings.Locale;
|
||||
numeralWrapper.updateLocale(Settings.Locale); //Initialize locale
|
||||
|
||||
@ -105,6 +107,10 @@ function setSettingsLabels() {
|
||||
Settings.DisableASCIIArt = this.checked;
|
||||
}
|
||||
|
||||
disableTextEffects.onclick = function() {
|
||||
Settings.DisableTextEffects = this.checked;
|
||||
}
|
||||
|
||||
//Locale selector
|
||||
locale.onchange = function() {
|
||||
if (!numeralWrapper.updateLocale(locale.value)) {
|
||||
|
@ -5,29 +5,22 @@
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
export class MoneySourceTracker {
|
||||
// Initiatizes a MoneySourceTracker object from a JSON save state.
|
||||
static fromJSON(value: any): MoneySourceTracker {
|
||||
return Generic_fromJSON(MoneySourceTracker, value.data);
|
||||
}
|
||||
|
||||
bladeburner: number = 0;
|
||||
casino: number = 0;
|
||||
class: number = 0;
|
||||
codingcontract: number = 0;
|
||||
corporation: number = 0;
|
||||
crime: number = 0;
|
||||
gang: number = 0;
|
||||
hacking: number = 0;
|
||||
hacknetnode: number = 0;
|
||||
hospitalization: number = 0;
|
||||
infiltration: number = 0;
|
||||
stock: number = 0;
|
||||
total: number = 0;
|
||||
work: number = 0;
|
||||
|
||||
[key: string]: number | Function;
|
||||
|
||||
constructor() {}
|
||||
|
||||
bladeburner = 0;
|
||||
casino = 0;
|
||||
class = 0;
|
||||
codingcontract = 0;
|
||||
corporation = 0;
|
||||
crime = 0;
|
||||
gang = 0;
|
||||
hacking = 0;
|
||||
hacknetnode = 0;
|
||||
hospitalization = 0;
|
||||
infiltration = 0;
|
||||
stock = 0;
|
||||
total = 0;
|
||||
work = 0;
|
||||
|
||||
// Record money earned
|
||||
record(amt: number, source: string): void {
|
||||
@ -45,7 +38,7 @@ export class MoneySourceTracker {
|
||||
reset(): void {
|
||||
for (const prop in this) {
|
||||
if (typeof this[prop] === "number") {
|
||||
this[prop] = 0;
|
||||
(this[prop] as number) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,6 +47,11 @@ export class MoneySourceTracker {
|
||||
toJSON(): any {
|
||||
return Generic_toJSON("MoneySourceTracker", this);
|
||||
}
|
||||
|
||||
// Initiatizes a MoneySourceTracker object from a JSON save state.
|
||||
static fromJSON(value: any): MoneySourceTracker {
|
||||
return Generic_fromJSON(MoneySourceTracker, value.data);
|
||||
}
|
||||
}
|
||||
|
||||
Reviver.constructors.MoneySourceTracker = MoneySourceTracker;
|
||||
|
@ -519,6 +519,11 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
});
|
||||
|
||||
it("getRunningScript()", async function() {
|
||||
const f = ["getRunningScript"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
});
|
||||
|
||||
it("getTimeSinceLastAug()", async function() {
|
||||
const f = ["getTimeSinceLastAug"];
|
||||
await testNonzeroDynamicRamCost(f);
|
||||
|
@ -410,6 +410,11 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
|
||||
await expectNonZeroRamCost(f);
|
||||
});
|
||||
|
||||
it("getRunningScript()", async function() {
|
||||
const f = ["getRunningScript"];
|
||||
await expectNonZeroRamCost(f);
|
||||
});
|
||||
|
||||
it("getTimeSinceLastAug()", async function() {
|
||||
const f = ["getTimeSinceLastAug"];
|
||||
await expectNonZeroRamCost(f);
|
||||
|
@ -1,8 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl" : ".",
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"lib" : ["es2016", "dom", "es2017.object"],
|
||||
"lib" : ["es2016", "dom", "es2017.object", "es2019"],
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
|
78
tslint.json
78
tslint.json
@ -1,78 +0,0 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:all"
|
||||
],
|
||||
"jsRules": {},
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"node_modules/"
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"completed-docs": [
|
||||
true,
|
||||
{
|
||||
"classes": true,
|
||||
"enums": true,
|
||||
"enum-members": true,
|
||||
"functions": {
|
||||
"visibilities": [
|
||||
"exported"
|
||||
]
|
||||
},
|
||||
"interfaces": true,
|
||||
"methods": {
|
||||
"privacies": [
|
||||
"public"
|
||||
]
|
||||
},
|
||||
"namespaces": true,
|
||||
"properties": true,
|
||||
"types": true,
|
||||
"variables": {
|
||||
"visibilities": [
|
||||
"exported"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"linebreak-style": false,
|
||||
"member-access": [
|
||||
true,
|
||||
"no-public"
|
||||
],
|
||||
"no-any": false,
|
||||
"no-inferrable-types": [
|
||||
false,
|
||||
"ignore-params",
|
||||
"ignore-properties"
|
||||
],
|
||||
"no-magic-numbers": [true, -1, 0, 1, 2, 10, 100],
|
||||
"no-null-keyword": false,
|
||||
"no-unsafe-any": false,
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"as-needed"
|
||||
],
|
||||
"only-arrow-functions": [
|
||||
true,
|
||||
"allow-declarations",
|
||||
"allow-named-functions"
|
||||
],
|
||||
"triple-equals": [true, "allow-null-check", "allow-undefined-check"],
|
||||
"typedef": [
|
||||
true,
|
||||
"call-signatures",
|
||||
"arrow-call-signatures",
|
||||
"parameter",
|
||||
"arrow-parameter",
|
||||
"property-declaration",
|
||||
"variable-declaration",
|
||||
"member-variable-declaration",
|
||||
"object-destructuring",
|
||||
"array-destructuring"
|
||||
]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
Loading…
Reference in New Issue
Block a user