From b7e07bc7f201e13198f2b3f436a376e4619385ce Mon Sep 17 00:00:00 2001 From: Olivier Gagnon Date: Thu, 9 Sep 2021 03:17:01 -0400 Subject: [PATCH] convert all hacknet to ts --- scripts/.eslintrc.js | 789 +++++++------- scripts/engines-check.js | 84 +- scripts/semver.js | 984 +++++++++--------- src/Bladeburner/Bladeburner.ts | 2 +- src/Bladeburner/ui/BlackOpList.tsx | 3 +- src/Corporation/ui/CityTabs.tsx | 3 +- src/Corporation/ui/ExpandNewCityPopup.tsx | 1 - src/Corporation/ui/HeaderTabs.tsx | 3 +- src/Corporation/ui/IndustryWarehouse.tsx | 22 - src/Corporation/ui/NewIndustryPopup.tsx | 3 +- src/Corporation/ui/SmartSupplyPopup.tsx | 3 +- src/DevMenu.jsx | 3 +- src/Faction/ui/AugmentationsPage.tsx | 3 +- ...{HacknetHelpers.jsx => HacknetHelpers.tsx} | 298 +++--- src/Hacknet/data/Constants.ts | 13 + src/Hacknet/data/HashUpgradesMetadata.tsx | 4 +- src/Hacknet/ui/GeneralInfo.jsx | 57 - src/Hacknet/ui/GeneralInfo.tsx | 50 + src/Hacknet/ui/HacknetNode.jsx | 171 --- src/Hacknet/ui/HacknetNodeElem.tsx | 174 ++++ src/Hacknet/ui/HacknetRoot.tsx | 145 +++ src/Hacknet/ui/HacknetServer.jsx | 225 ---- src/Hacknet/ui/HacknetServerElem.tsx | 227 ++++ src/Hacknet/ui/HacknetUpgradeElem.tsx | 70 ++ src/Hacknet/ui/HashUpgradePopup.jsx | 133 --- src/Hacknet/ui/HashUpgradePopup.tsx | 54 + ...plierButtons.jsx => MultiplierButtons.tsx} | 18 +- .../ui/{PlayerInfo.jsx => PlayerInfo.tsx} | 15 +- ...{PurchaseButton.jsx => PurchaseButton.tsx} | 16 +- src/Hacknet/ui/Root.jsx | 162 --- src/Locations/ui/CoresButton.tsx | 2 +- src/Locations/ui/RamButton.tsx | 2 +- src/Locations/ui/TechVendorLocation.tsx | 2 +- src/Locations/ui/TorButton.tsx | 2 +- src/NetscriptFunctions.js | 42 +- src/PersonObjects/IPlayer.ts | 1 + .../Player/PlayerObjectGeneralMethods.jsx | 3 +- src/Prestige.js | 2 +- src/data/codingcontracttypes.ts | 3 +- src/engine.jsx | 24 +- ...{ServerDropdown.jsx => ServerDropdown.tsx} | 59 +- 41 files changed, 1947 insertions(+), 1930 deletions(-) rename src/Hacknet/{HacknetHelpers.jsx => HacknetHelpers.tsx} (57%) delete mode 100644 src/Hacknet/ui/GeneralInfo.jsx create mode 100644 src/Hacknet/ui/GeneralInfo.tsx delete mode 100644 src/Hacknet/ui/HacknetNode.jsx create mode 100644 src/Hacknet/ui/HacknetNodeElem.tsx create mode 100644 src/Hacknet/ui/HacknetRoot.tsx delete mode 100644 src/Hacknet/ui/HacknetServer.jsx create mode 100644 src/Hacknet/ui/HacknetServerElem.tsx create mode 100644 src/Hacknet/ui/HacknetUpgradeElem.tsx delete mode 100644 src/Hacknet/ui/HashUpgradePopup.jsx create mode 100644 src/Hacknet/ui/HashUpgradePopup.tsx rename src/Hacknet/ui/{MultiplierButtons.jsx => MultiplierButtons.tsx} (71%) rename src/Hacknet/ui/{PlayerInfo.jsx => PlayerInfo.tsx} (68%) rename src/Hacknet/ui/{PurchaseButton.jsx => PurchaseButton.tsx} (77%) delete mode 100644 src/Hacknet/ui/Root.jsx rename src/ui/React/{ServerDropdown.jsx => ServerDropdown.tsx} (50%) diff --git a/scripts/.eslintrc.js b/scripts/.eslintrc.js index 7c2723fff..ac57cfcac 100644 --- a/scripts/.eslintrc.js +++ b/scripts/.eslintrc.js @@ -2,366 +2,433 @@ const numSpaces = 4; const maxLineLength = 160; module.exports = { - env: { - es6: true, - node: true, - }, - extends: "eslint:recommended", - parserOptions: { - ecmaFeatures: { - experimentalObjectRestSpread: true, + "env": { + "es6": true, + "node": true }, - ecmaVersion: 8, - sourceType: "module", - }, - rules: { - "accessor-pairs": [ - "error", - { - getWithoutSet: false, - setWithoutGet: true, - }, - ], - "array-bracket-newline": ["error"], - "array-bracket-spacing": ["error"], - "array-callback-return": ["error"], - "array-element-newline": ["error"], - "arrow-body-style": ["error"], - "arrow-parens": ["error"], - "arrow-spacing": ["error"], - "block-scoped-var": ["error"], - "block-spacing": ["error"], - "brace-style": ["error"], - "callback-return": ["error"], - camelcase: ["error"], - "capitalized-comments": ["error"], - "class-methods-use-this": ["error"], - "comma-dangle": ["error"], - "comma-spacing": ["error"], - "comma-style": ["error", "last"], - complexity: ["error"], - "computed-property-spacing": ["error", "never"], - "consistent-return": ["error"], - "consistent-this": ["error"], - "constructor-super": ["error"], - curly: ["error"], - "default-case": ["error"], - "dot-location": ["error", "property"], - "dot-notation": ["error"], - "eol-last": ["error"], - eqeqeq: ["error"], - "for-direction": ["error"], - "func-call-spacing": ["error"], - "func-name-matching": ["error"], - "func-names": ["error", "never"], - "func-style": ["error"], - "function-paren-newline": ["error"], - "generator-star-spacing": ["error", "before"], - "getter-return": [ - "error", - { - allowImplicit: false, - }, - ], - "global-require": ["error"], - "guard-for-in": ["error"], - "handle-callback-err": ["error"], - "id-blacklist": ["error"], - "id-length": ["error"], - "id-match": ["error"], - "implicit-arrow-linebreak": ["error", "beside"], - indent: [ - "error", - numSpaces, - { - SwitchCase: 1, - }, - ], - "init-declarations": ["error"], - "jsx-quotes": ["error"], - "key-spacing": ["error"], - "keyword-spacing": ["error"], - "line-comment-position": ["error"], - "linebreak-style": ["error", "windows"], - "lines-around-comment": ["error"], - "lines-between-class-members": ["error"], - "max-depth": ["error"], - "max-len": ["error", maxLineLength], - "max-lines": [ - "error", - { - skipBlankLines: true, - skipComments: true, - }, - ], - "max-nested-callbacks": ["error"], - "max-params": ["error"], - "max-statements": ["error"], - "max-statements-per-line": ["error"], - "multiline-comment-style": ["off", "starred-block"], - "multiline-ternary": ["error", "never"], - "new-cap": ["error"], - "new-parens": ["error"], - // TODO: configure this... - "newline-before-return": ["error"], - "newline-per-chained-call": ["error"], - "no-alert": ["error"], - "no-array-constructor": ["error"], - "no-await-in-loop": ["error"], - "no-bitwise": ["error"], - "no-buffer-constructor": ["error"], - "no-caller": ["error"], - "no-case-declarations": ["error"], - "no-catch-shadow": ["error"], - "no-class-assign": ["error"], - "no-compare-neg-zero": ["error"], - "no-cond-assign": ["error", "except-parens"], - "no-confusing-arrow": ["error"], - "no-console": ["error"], - "no-const-assign": ["error"], - "no-constant-condition": [ - "error", - { - checkLoops: false, - }, - ], - "no-continue": ["off"], - "no-control-regex": ["error"], - "no-debugger": ["error"], - "no-delete-var": ["error"], - "no-div-regex": ["error"], - "no-dupe-args": ["error"], - "no-dupe-class-members": ["error"], - "no-dupe-keys": ["error"], - "no-duplicate-case": ["error"], - "no-duplicate-imports": [ - "error", - { - includeExports: true, - }, - ], - "no-else-return": ["error"], - "no-empty": [ - "error", - { - allowEmptyCatch: false, - }, - ], - "no-empty-character-class": ["error"], - "no-empty-function": ["error"], - "no-empty-pattern": ["error"], - "no-eq-null": ["error"], - "no-eval": ["error"], - "no-ex-assign": ["error"], - "no-extend-native": ["error"], - "no-extra-bind": ["error"], - "no-extra-boolean-cast": ["error"], - "no-extra-label": ["error"], - "no-extra-parens": [ - "error", - "all", - { - conditionalAssign: false, - }, - ], - "no-extra-semi": ["error"], - "no-fallthrough": ["error"], - "no-floating-decimal": ["error"], - "no-func-assign": ["error"], - "no-global-assign": ["error"], - "no-implicit-coercion": ["error"], - "no-implicit-globals": ["error"], - "no-implied-eval": ["error"], - "no-inline-comments": ["error"], - "no-inner-declarations": ["error", "both"], - "no-invalid-regexp": ["error"], - "no-invalid-this": ["error"], - "no-irregular-whitespace": [ - "error", - { - skipComments: false, - skipRegExps: false, - skipStrings: false, - skipTemplates: false, - }, - ], - "no-iterator": ["error"], - "no-label-var": ["error"], - "no-labels": ["error"], - "no-lone-blocks": ["error"], - "no-lonely-if": ["error"], - "no-loop-func": ["error"], - "no-magic-numbers": [ - "error", - { - ignore: [-1, 0, 1], - ignoreArrayIndexes: true, - }, - ], - "no-mixed-operators": ["error"], - "no-mixed-requires": ["error"], - "no-mixed-spaces-and-tabs": ["error"], - "no-multi-assign": ["error"], - "no-multi-spaces": ["error"], - "no-multi-str": ["error"], - "no-multiple-empty-lines": [ - "error", - { - max: 1, - }, - ], - "no-native-reassign": ["error"], - "no-negated-condition": ["error"], - "no-negated-in-lhs": ["error"], - "no-nested-ternary": ["error"], - "no-new": ["error"], - "no-new-func": ["error"], - "no-new-object": ["error"], - "no-new-require": ["error"], - "no-new-symbol": ["error"], - "no-new-wrappers": ["error"], - "no-obj-calls": ["error"], - "no-octal": ["error"], - "no-octal-escape": ["error"], - "no-param-reassign": ["error"], - "no-path-concat": ["error"], - "no-plusplus": [ - "error", - { - allowForLoopAfterthoughts: true, - }, - ], - "no-process-env": ["error"], - "no-process-exit": ["error"], - "no-proto": ["error"], - "no-prototype-builtins": ["error"], - "no-redeclare": ["error"], - "no-regex-spaces": ["error"], - "no-restricted-globals": ["error"], - "no-restricted-imports": ["error"], - "no-restricted-modules": ["error"], - "no-restricted-properties": [ - "error", - { - message: "'log' is too general, use an appropriate level when logging.", - object: "console", - property: "log", - }, - ], - "no-restricted-syntax": ["error"], - "no-return-assign": ["error"], - "no-return-await": ["error"], - "no-script-url": ["error"], - "no-self-assign": [ - "error", - { - props: false, - }, - ], - "no-self-compare": ["error"], - "no-sequences": ["error"], - "no-shadow": ["error"], - "no-shadow-restricted-names": ["error"], - "no-spaced-func": ["error"], - "no-sparse-arrays": ["error"], - "no-sync": ["error"], - "no-tabs": ["error"], - "no-template-curly-in-string": ["error"], - "no-ternary": ["off"], - "no-this-before-super": ["error"], - "no-throw-literal": ["error"], - "no-trailing-spaces": ["error"], - "no-undef": ["error"], - "no-undef-init": ["error"], - "no-undefined": ["error"], - "no-underscore-dangle": ["error"], - "no-unexpected-multiline": ["error"], - "no-unmodified-loop-condition": ["error"], - "no-unneeded-ternary": ["error"], - "no-unreachable": ["error"], - "no-unsafe-finally": ["error"], - "no-unsafe-negation": ["error"], - "no-unused-expressions": ["error"], - "no-unused-labels": ["error"], - "no-unused-vars": ["error"], - "no-use-before-define": ["error"], - "no-useless-call": ["error"], - "no-useless-computed-key": ["error"], - "no-useless-concat": ["error"], - "no-useless-constructor": ["error"], - "no-useless-escape": ["error"], - "no-useless-rename": [ - "error", - { - ignoreDestructuring: false, - ignoreExport: false, - ignoreImport: false, - }, - ], - "no-useless-return": ["error"], - "no-var": ["error"], - "no-void": ["error"], - "no-warning-comments": ["error"], - "no-whitespace-before-property": ["error"], - "no-with": ["error"], - "nonblock-statement-body-position": ["error", "below"], - "object-curly-newline": ["error"], - "object-curly-spacing": ["error"], - "object-property-newline": ["error"], - "object-shorthand": ["error"], - "one-var": ["off"], - "one-var-declaration-per-line": ["error"], - "operator-assignment": ["error"], - "operator-linebreak": ["error", "none"], - "padded-blocks": ["off"], - "padding-line-between-statements": ["error"], - "prefer-arrow-callback": ["error"], - "prefer-const": ["error"], - "prefer-destructuring": ["off"], - "prefer-numeric-literals": ["error"], - "prefer-promise-reject-errors": ["off"], - "prefer-reflect": ["error"], - "prefer-rest-params": ["error"], - "prefer-spread": ["error"], - "prefer-template": ["error"], - "quote-props": ["error"], - quotes: ["error"], - radix: ["error", "as-needed"], - "require-await": ["error"], - "require-jsdoc": ["off"], - "require-yield": ["error"], - "rest-spread-spacing": ["error", "never"], - semi: ["error"], - "semi-spacing": ["error"], - "semi-style": ["error", "last"], - "sort-imports": ["error"], - "sort-keys": ["error"], - "sort-vars": ["error"], - "space-before-blocks": ["error"], - "space-before-function-paren": ["off"], - "space-in-parens": ["error"], - "space-infix-ops": ["error"], - "space-unary-ops": ["error"], - "spaced-comment": ["error"], - strict: ["error"], - "switch-colon-spacing": [ - "error", - { - after: true, - before: false, - }, - ], - "symbol-description": ["error"], - "template-curly-spacing": ["error"], - "template-tag-spacing": ["error"], - "unicode-bom": ["error", "never"], - "use-isnan": ["error"], - "valid-jsdoc": ["error"], - "valid-typeof": ["error"], - "vars-on-top": ["error"], - "wrap-iife": ["error", "any"], - "wrap-regex": ["error"], - "yield-star-spacing": ["error", "before"], - yoda: ["error", "never"], - }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaFeatures": { + "experimentalObjectRestSpread": true + }, + "ecmaVersion": 8, + "sourceType": "module" + }, + "rules": { + "accessor-pairs": [ + "error", + { + "getWithoutSet": false, + "setWithoutGet": true + } + ], + "array-bracket-newline": ["error"], + "array-bracket-spacing": ["error"], + "array-callback-return": ["error"], + "array-element-newline": ["error"], + "arrow-body-style": ["error"], + "arrow-parens": ["error"], + "arrow-spacing": ["error"], + "block-scoped-var": ["error"], + "block-spacing": ["error"], + "brace-style": ["error"], + "callback-return": ["error"], + "camelcase": ["error"], + "capitalized-comments": ["error"], + "class-methods-use-this": ["error"], + "comma-dangle": ["error"], + "comma-spacing": ["error"], + "comma-style": [ + "error", + "last" + ], + "complexity": ["error"], + "computed-property-spacing": [ + "error", + "never" + ], + "consistent-return": ["error"], + "consistent-this": ["error"], + "constructor-super": ["error"], + "curly": ["error"], + "default-case": ["error"], + "dot-location": [ + "error", + "property" + ], + "dot-notation": ["error"], + "eol-last": ["error"], + "eqeqeq": ["error"], + "for-direction": ["error"], + "func-call-spacing": ["error"], + "func-name-matching": ["error"], + "func-names": [ + "error", + "never" + ], + "func-style": ["error"], + "function-paren-newline": ["error"], + "generator-star-spacing": [ + "error", + "before" + ], + "getter-return": [ + "error", + { + "allowImplicit": false + } + ], + "global-require": ["error"], + "guard-for-in": ["error"], + "handle-callback-err": ["error"], + "id-blacklist": ["error"], + "id-length": ["error"], + "id-match": ["error"], + "implicit-arrow-linebreak": [ + "error", + "beside" + ], + "indent": [ + "error", + numSpaces, + { + "SwitchCase": 1 + } + ], + "init-declarations": ["error"], + "jsx-quotes": ["error"], + "key-spacing": ["error"], + "keyword-spacing": ["error"], + "line-comment-position": ["error"], + "linebreak-style": [ + "error", + "windows" + ], + "lines-around-comment": ["error"], + "lines-between-class-members": ["error"], + "max-depth": ["error"], + "max-len": [ + "error", + maxLineLength + ], + "max-lines": [ + "error", + { + "skipBlankLines": true, + "skipComments": true + } + ], + "max-nested-callbacks": ["error"], + "max-params": ["error"], + "max-statements": ["error"], + "max-statements-per-line": ["error"], + "multiline-comment-style": [ + "off", + "starred-block" + ], + "multiline-ternary": [ + "error", + "never" + ], + "new-cap": ["error"], + "new-parens": ["error"], + // TODO: configure this... + "newline-before-return": ["error"], + "newline-per-chained-call": ["error"], + "no-alert": ["error"], + "no-array-constructor": ["error"], + "no-await-in-loop": ["error"], + "no-bitwise": ["error"], + "no-buffer-constructor": ["error"], + "no-caller": ["error"], + "no-case-declarations": ["error"], + "no-catch-shadow": ["error"], + "no-class-assign": ["error"], + "no-compare-neg-zero": ["error"], + "no-cond-assign": [ + "error", + "except-parens" + ], + "no-confusing-arrow": ["error"], + "no-console": ["error"], + "no-const-assign": ["error"], + "no-constant-condition": [ + "error", + { + "checkLoops": false + } + ], + "no-continue": ["off"], + "no-control-regex": ["error"], + "no-debugger": ["error"], + "no-delete-var": ["error"], + "no-div-regex": ["error"], + "no-dupe-args": ["error"], + "no-dupe-class-members": ["error"], + "no-dupe-keys": ["error"], + "no-duplicate-case": ["error"], + "no-duplicate-imports": [ + "error", + { + "includeExports": true + } + ], + "no-else-return": ["error"], + "no-empty": [ + "error", + { + "allowEmptyCatch": false + } + ], + "no-empty-character-class": ["error"], + "no-empty-function": ["error"], + "no-empty-pattern": ["error"], + "no-eq-null": ["error"], + "no-eval": ["error"], + "no-ex-assign": ["error"], + "no-extend-native": ["error"], + "no-extra-bind": ["error"], + "no-extra-boolean-cast": ["error"], + "no-extra-label": ["error"], + "no-extra-parens": [ + "error", + "all", + { + "conditionalAssign": false + } + ], + "no-extra-semi": ["error"], + "no-fallthrough": ["error"], + "no-floating-decimal": ["error"], + "no-func-assign": ["error"], + "no-global-assign": ["error"], + "no-implicit-coercion": ["error"], + "no-implicit-globals": ["error"], + "no-implied-eval": ["error"], + "no-inline-comments": ["error"], + "no-inner-declarations": [ + "error", + "both" + ], + "no-invalid-regexp": ["error"], + "no-invalid-this": ["error"], + "no-irregular-whitespace": [ + "error", + { + "skipComments": false, + "skipRegExps": false, + "skipStrings": false, + "skipTemplates": false + } + ], + "no-iterator": ["error"], + "no-label-var": ["error"], + "no-labels": ["error"], + "no-lone-blocks": ["error"], + "no-lonely-if": ["error"], + "no-loop-func": ["error"], + "no-magic-numbers": [ + "error", + { + "ignore": [ + -1, + 0, + 1 + ], + "ignoreArrayIndexes": true + } + ], + "no-mixed-operators": ["error"], + "no-mixed-requires": ["error"], + "no-mixed-spaces-and-tabs": ["error"], + "no-multi-assign": ["error"], + "no-multi-spaces": ["error"], + "no-multi-str": ["error"], + "no-multiple-empty-lines": [ + "error", + { + "max": 1 + } + ], + "no-native-reassign": ["error"], + "no-negated-condition": ["error"], + "no-negated-in-lhs": ["error"], + "no-nested-ternary": ["error"], + "no-new": ["error"], + "no-new-func": ["error"], + "no-new-object": ["error"], + "no-new-require": ["error"], + "no-new-symbol": ["error"], + "no-new-wrappers": ["error"], + "no-obj-calls": ["error"], + "no-octal": ["error"], + "no-octal-escape": ["error"], + "no-param-reassign": ["error"], + "no-path-concat": ["error"], + "no-plusplus": [ + "error", + { + "allowForLoopAfterthoughts": true + } + ], + "no-process-env": ["error"], + "no-process-exit": ["error"], + "no-proto": ["error"], + "no-prototype-builtins": ["error"], + "no-redeclare": ["error"], + "no-regex-spaces": ["error"], + "no-restricted-globals": ["error"], + "no-restricted-imports": ["error"], + "no-restricted-modules": ["error"], + "no-restricted-properties": [ + "error", + { + "message": "'log' is too general, use an appropriate level when logging.", + "object": "console", + "property": "log" + } + ], + "no-restricted-syntax": ["error"], + "no-return-assign": ["error"], + "no-return-await": ["error"], + "no-script-url": ["error"], + "no-self-assign": [ + "error", + { + "props": false + } + ], + "no-self-compare": ["error"], + "no-sequences": ["error"], + "no-shadow": ["error"], + "no-shadow-restricted-names": ["error"], + "no-spaced-func": ["error"], + "no-sparse-arrays": ["error"], + "no-sync": ["error"], + "no-tabs": ["error"], + "no-template-curly-in-string": ["error"], + "no-ternary": ["off"], + "no-this-before-super": ["error"], + "no-throw-literal": ["error"], + "no-trailing-spaces": ["error"], + "no-undef": ["error"], + "no-undef-init": ["error"], + "no-undefined": ["error"], + "no-underscore-dangle": ["error"], + "no-unexpected-multiline": ["error"], + "no-unmodified-loop-condition": ["error"], + "no-unneeded-ternary": ["error"], + "no-unreachable": ["error"], + "no-unsafe-finally": ["error"], + "no-unsafe-negation": ["error"], + "no-unused-expressions": ["error"], + "no-unused-labels": ["error"], + "no-unused-vars": ["error"], + "no-use-before-define": ["error"], + "no-useless-call": ["error"], + "no-useless-computed-key": ["error"], + "no-useless-concat": ["error"], + "no-useless-constructor": ["error"], + "no-useless-escape": ["error"], + "no-useless-rename": [ + "error", + { + "ignoreDestructuring": false, + "ignoreExport": false, + "ignoreImport": false + } + ], + "no-useless-return": ["error"], + "no-var": ["error"], + "no-void": ["error"], + "no-warning-comments": ["error"], + "no-whitespace-before-property": ["error"], + "no-with": ["error"], + "nonblock-statement-body-position": [ + "error", + "below" + ], + "object-curly-newline": ["error"], + "object-curly-spacing": ["error"], + "object-property-newline": ["error"], + "object-shorthand": ["error"], + "one-var": ["off"], + "one-var-declaration-per-line": ["error"], + "operator-assignment": ["error"], + "operator-linebreak": [ + "error", + "none" + ], + "padded-blocks": ["off"], + "padding-line-between-statements": ["error"], + "prefer-arrow-callback": ["error"], + "prefer-const": ["error"], + "prefer-destructuring": ["off"], + "prefer-numeric-literals": ["error"], + "prefer-promise-reject-errors": ["off"], + "prefer-reflect": ["error"], + "prefer-rest-params": ["error"], + "prefer-spread": ["error"], + "prefer-template": ["error"], + "quote-props": ["error"], + "quotes": ["error"], + "radix": [ + "error", + "as-needed" + ], + "require-await": ["error"], + "require-jsdoc": ["off"], + "require-yield": ["error"], + "rest-spread-spacing": [ + "error", + "never" + ], + "semi": ["error"], + "semi-spacing": ["error"], + "semi-style": [ + "error", + "last" + ], + "sort-imports": ["error"], + "sort-keys": ["error"], + "sort-vars": ["error"], + "space-before-blocks": ["error"], + "space-before-function-paren": ["off"], + "space-in-parens": ["error"], + "space-infix-ops": ["error"], + "space-unary-ops": ["error"], + "spaced-comment": ["error"], + "strict": ["error"], + "switch-colon-spacing": [ + "error", + { + "after": true, + "before": false + } + ], + "symbol-description": ["error"], + "template-curly-spacing": ["error"], + "template-tag-spacing": ["error"], + "unicode-bom": [ + "error", + "never" + ], + "use-isnan": ["error"], + "valid-jsdoc": ["error"], + "valid-typeof": ["error"], + "vars-on-top": ["error"], + "wrap-iife": [ + "error", + "any" + ], + "wrap-regex": ["error"], + "yield-star-spacing": [ + "error", + "before" + ], + "yoda": [ + "error", + "never" + ] + } }; diff --git a/scripts/engines-check.js b/scripts/engines-check.js index c00ba229d..5db6af9ac 100644 --- a/scripts/engines-check.js +++ b/scripts/engines-check.js @@ -8,74 +8,66 @@ const path = require("path"); const exec = require("child_process").exec; const semver = require("./semver"); -const getPackageJson = () => - new Promise((resolve, reject) => { +const getPackageJson = () => new Promise((resolve, reject) => { try { - /* eslint-disable-next-line global-require */ - resolve(require(path.resolve(process.cwd(), "package.json"))); + /* eslint-disable-next-line global-require */ + resolve(require(path.resolve(process.cwd(), "package.json"))); } catch (error) { - reject(error); + reject(error); } - }); +}); -const getEngines = (data) => - new Promise((resolve, reject) => { +const getEngines = (data) => new Promise((resolve, reject) => { let versions = null; if (data.engines) { - versions = data.engines; + versions = data.engines; } if (versions) { - resolve(versions); + resolve(versions); } else { - reject("Missing or improper 'engines' property in 'package.json'"); + reject("Missing or improper 'engines' property in 'package.json'"); } - }); +}); -const checkNpmVersion = (engines) => - new Promise((resolve, reject) => { +const checkNpmVersion = (engines) => new Promise((resolve, reject) => { exec("npm -v", (error, stdout, stderr) => { - if (error) { - reject(`Unable to find NPM version\n${stderr}`); - } + if (error) { + reject(`Unable to find NPM version\n${stderr}`); + } - const npmVersion = stdout.trim(); - const engineVersion = engines.npm || ">=0"; + const npmVersion = stdout.trim(); + const engineVersion = engines.npm || ">=0"; - if (semver.satisfies(npmVersion, engineVersion)) { - resolve(); - } else { - reject( - `Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`, - ); - } + if (semver.satisfies(npmVersion, engineVersion)) { + resolve(); + } else { + reject(`Incorrect npm version\n'package.json' specifies "${engineVersion}", you are currently running "${npmVersion}".`); + } }); - }); +}); -const checkNodeVersion = (engines) => - new Promise((resolve, reject) => { +const checkNodeVersion = (engines) => new Promise((resolve, reject) => { const nodeVersion = process.version.substring(1); if (semver.satisfies(nodeVersion, engines.node)) { - resolve(engines); + resolve(engines); } else { - reject( - `Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`, - ); + reject(`Incorrect node version\n'package.json' specifies "${engines.node}", you are currently running "${process.version}".`); } - }); +}); getPackageJson() - .then(getEngines) - .then(checkNodeVersion) - .then(checkNpmVersion) - .then( - () => true, - (error) => { - // Specifically disable these as the error message gets lost in the normal unhandled output. - /* eslint-disable no-console, no-process-exit */ - console.error(error); - process.exit(1); - }, - ); + .then(getEngines) + .then(checkNodeVersion) + .then(checkNpmVersion) + .then( + () => true, + (error) => { + // Specifically disable these as the error message gets lost in the normal unhandled output. + /* eslint-disable no-console, no-process-exit */ + console.error(error); + process.exit(1); + } + ); diff --git a/scripts/semver.js b/scripts/semver.js index daf4a1882..d8b3e574a 100644 --- a/scripts/semver.js +++ b/scripts/semver.js @@ -83,7 +83,7 @@ src[NONNUMERICIDENTIFIER] = "\\d*[a-zA-Z-][a-zA-Z0-9-]*"; // Three dot-separated numeric identifiers. src[MAINVERSION] = `(${src[NUMERICIDENTIFIER]})\\.(${src[NUMERICIDENTIFIER]})\\.(${src[NUMERICIDENTIFIER]})`; src[ - MAINVERSIONLOOSE + MAINVERSIONLOOSE ] = `(${src[NUMERICIDENTIFIERLOOSE]})\\.(${src[NUMERICIDENTIFIERLOOSE]})\\.(${src[NUMERICIDENTIFIERLOOSE]})`; // ## Pre-release Version Identifier @@ -126,7 +126,7 @@ src[XRANGEIDENTIFIER] = `${src[NUMERICIDENTIFIER]}|x|X|\\*`; /* eslint-disable-next-line max-len */ src[ - XRANGEPLAIN + XRANGEPLAIN ] = `[v=\\s]*(${src[XRANGEIDENTIFIER]})(?:\\.(${src[XRANGEIDENTIFIER]})(?:\\.(${src[XRANGEIDENTIFIER]})(?:${src[PRERELEASE]})?${src[BUILD]}?)?)?`; /* eslint-disable-next-line max-len */ @@ -141,7 +141,7 @@ src[XRANGELOOSE] = `^${src[GTLT]}\\s*${src[XRANGEPLAINLOOSE]}$`; // Extract anything that could conceivably be a part of a valid semver /* eslint-disable-next-line max-len */ src[ - COERCE + COERCE ] = `(?:^|[^\\d])(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}})(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?(?:$|[^\\d])`; // Tilde ranges. @@ -189,40 +189,40 @@ src[STAR] = "(<|>)?=?\\s*\\*"; // Compile to actual regexp objects. // All are flag-free, unless they were created above with a flag. for (let idx = 0; idx <= STAR; idx++) { - if (!re[idx]) { - re[idx] = new RegExp(src[idx]); - } + if (!re[idx]) { + re[idx] = new RegExp(src[idx]); + } } const ANY = {}; const isX = (id) => !id || id.toLowerCase() === "x" || id === "*"; function compareIdentifiers(left, right) { - const numeric = /^[0-9]+$/; - const leftIsNumeric = numeric.test(left); - const rightIsNumeric = numeric.test(right); - if (leftIsNumeric && !rightIsNumeric) { - return -1; - } + const numeric = /^[0-9]+$/; + const leftIsNumeric = numeric.test(left); + const rightIsNumeric = numeric.test(right); + if (leftIsNumeric && !rightIsNumeric) { + return -1; + } - if (rightIsNumeric && !leftIsNumeric) { - return 1; - } + if (rightIsNumeric && !leftIsNumeric) { + return 1; + } - if (leftIsNumeric && rightIsNumeric) { - left = Number(left); - right = Number(right); - } + if (leftIsNumeric && rightIsNumeric) { + left = Number(left); + right = Number(right); + } - if (left < right) { - return -1; - } + if (left < right) { + return -1; + } - if (left > right) { - return 1; - } + if (left > right) { + return 1; + } - return 0; + return 0; } // This function is passed to string.replace(re[HYPHENRANGE]) @@ -231,56 +231,56 @@ function compareIdentifiers(left, right) { // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do // 1.2 - 3.4 => >=1.2.0 <3.5.0 function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr) { - if (isX(fM)) { - from = ""; - } else if (isX(fm)) { - from = `>=${fM}.0.0`; - } else if (isX(fp)) { - from = `>=${fM}.${fm}.0`; - } else { - from = `>=${from}`; - } + if (isX(fM)) { + from = ""; + } else if (isX(fm)) { + from = `>=${fM}.0.0`; + } else if (isX(fp)) { + from = `>=${fM}.${fm}.0`; + } else { + from = `>=${from}`; + } - if (isX(tM)) { - to = ""; - } else if (isX(tm)) { - to = `<${Number(tM) + 1}.0.0`; - } else if (isX(tp)) { - to = `<${tM}.${Number(tm) + 1}.0`; - } else if (tpr) { - to = `<=${tM}.${tm}.${tp}-${tpr}`; - } else { - to = `<=${to}`; - } + if (isX(tM)) { + to = ""; + } else if (isX(tm)) { + to = `<${Number(tM) + 1}.0.0`; + } else if (isX(tp)) { + to = `<${tM}.${Number(tm) + 1}.0`; + } else if (tpr) { + to = `<=${tM}.${tm}.${tp}-${tpr}`; + } else { + to = `<=${to}`; + } - return `${from} ${to}`.trim(); + return `${from} ${to}`.trim(); } function replaceTilde(comp, loose) { - const regex = loose ? re[TILDELOOSE] : re[TILDE]; + const regex = loose ? re[TILDELOOSE] : re[TILDE]; - return comp.replace(regex, function (match, major, minor, patch, prerelease) { - let ret; + return comp.replace(regex, function (match, major, minor, patch, prerelease) { + let ret; - if (isX(major)) { - ret = ""; - } else if (isX(minor)) { - ret = `>=${major}.0.0 <${Number(major) + 1}.0.0`; - } else if (isX(patch)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = `>=${major}.${minor}.0 <${major}.${Number(minor) + 1}.0`; - } else if (prerelease) { - if (prerelease.charAt(0) !== "-") { - prerelease = `-${prerelease}`; - } - ret = `>=${major}.${minor}.${patch}${prerelease} <${major}.${Number(minor) + 1}.0`; - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = `>=${major}.${minor}.${patch} <${major}.${Number(minor) + 1}.0`; - } + if (isX(major)) { + ret = ""; + } else if (isX(minor)) { + ret = `>=${major}.0.0 <${Number(major) + 1}.0.0`; + } else if (isX(patch)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = `>=${major}.${minor}.0 <${major}.${Number(minor) + 1}.0`; + } else if (prerelease) { + if (prerelease.charAt(0) !== "-") { + prerelease = `-${prerelease}`; + } + ret = `>=${major}.${minor}.${patch}${prerelease} <${major}.${Number(minor) + 1}.0`; + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = `>=${major}.${minor}.${patch} <${major}.${Number(minor) + 1}.0`; + } - return ret; - }); + return ret; + }); } // ~, ~> --> * (any, kinda silly) @@ -290,54 +290,54 @@ function replaceTilde(comp, loose) { // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 function replaceTildes(comp, loose) { - return comp - .trim() - .split(/\s+/) - .map((comp1) => replaceTilde(comp1, loose)) - .join(" "); + return comp + .trim() + .split(/\s+/) + .map((comp1) => replaceTilde(comp1, loose)) + .join(" "); } function replaceCaret(comp, loose) { - const regex = loose ? re[CARETLOOSE] : re[CARET]; + const regex = loose ? re[CARETLOOSE] : re[CARET]; - return comp.replace(regex, function (match, major, minor, patch, prerelease) { - let ret; + return comp.replace(regex, function (match, major, minor, patch, prerelease) { + let ret; - if (isX(major)) { - ret = ""; - } else if (isX(minor)) { - ret = `>=${major}.0.0 <${Number(major) + 1}.0.0`; - } else if (isX(patch)) { - if (major === "0") { - ret = `>=${major}.${minor}.0 <${major}.${Number(minor) + 1}.0`; - } else { - ret = `>=${major}.${minor}.0 <${Number(major) + 1}.0.0`; - } - } else if (prerelease) { - if (prerelease.charAt(0) !== "-") { - prerelease = `-${prerelease}`; - } - if (major === "0") { - if (minor === "0") { - ret = `>=${major}.${minor}.${patch}${prerelease} <${major}.${minor}.${Number(patch) + 1}`; + if (isX(major)) { + ret = ""; + } else if (isX(minor)) { + ret = `>=${major}.0.0 <${Number(major) + 1}.0.0`; + } else if (isX(patch)) { + if (major === "0") { + ret = `>=${major}.${minor}.0 <${major}.${Number(minor) + 1}.0`; + } else { + ret = `>=${major}.${minor}.0 <${Number(major) + 1}.0.0`; + } + } else if (prerelease) { + if (prerelease.charAt(0) !== "-") { + prerelease = `-${prerelease}`; + } + if (major === "0") { + if (minor === "0") { + ret = `>=${major}.${minor}.${patch}${prerelease} <${major}.${minor}.${Number(patch) + 1}`; + } else { + ret = `>=${major}.${minor}.${patch}${prerelease} <${major}.${Number(minor) + 1}.0`; + } + } else { + ret = `>=${major}.${minor}.${patch}${prerelease} <${Number(major) + 1}.0.0`; + } + } else if (major === "0") { + if (minor === "0") { + ret = `>=${major}.${minor}.${patch} <${major}.${minor}.${Number(patch) + 1}`; + } else { + ret = `>=${major}.${minor}.${patch} <${major}.${Number(minor) + 1}.0`; + } } else { - ret = `>=${major}.${minor}.${patch}${prerelease} <${major}.${Number(minor) + 1}.0`; + ret = `>=${major}.${minor}.${patch} <${Number(major) + 1}.0.0`; } - } else { - ret = `>=${major}.${minor}.${patch}${prerelease} <${Number(major) + 1}.0.0`; - } - } else if (major === "0") { - if (minor === "0") { - ret = `>=${major}.${minor}.${patch} <${major}.${minor}.${Number(patch) + 1}`; - } else { - ret = `>=${major}.${minor}.${patch} <${major}.${Number(minor) + 1}.0`; - } - } else { - ret = `>=${major}.${minor}.${patch} <${Number(major) + 1}.0.0`; - } - return ret; - }); + return ret; + }); } // ^ --> * (any, kinda silly) @@ -347,234 +347,232 @@ function replaceCaret(comp, loose) { // ^1.2.3 --> >=1.2.3 <2.0.0 // ^1.2.0 --> >=1.2.0 <2.0.0 function replaceCarets(comp, loose) { - return comp - .trim() - .split(/\s+/) - .map((comp1) => replaceCaret(comp1, loose)) - .join(" "); + return comp + .trim() + .split(/\s+/) + .map((comp1) => replaceCaret(comp1, loose)) + .join(" "); } function replaceXRange(comp, loose) { - comp = comp.trim(); - const regex = loose ? re[XRANGELOOSE] : re[XRANGE]; + comp = comp.trim(); + const regex = loose ? re[XRANGELOOSE] : re[XRANGE]; - return comp.replace(regex, function (ret, operator, major, minor, patch) { - const xM = isX(major); - const xm = xM || isX(minor); - const xp = xm || isX(patch); - const anyX = xp; + return comp.replace(regex, function (ret, operator, major, minor, patch) { + const xM = isX(major); + const xm = xM || isX(minor); + const xp = xm || isX(patch); + const anyX = xp; - if (operator === "=" && anyX) { - operator = ""; - } + if (operator === "=" && anyX) { + operator = ""; + } - if (xM) { - if (operator === ">" || operator === "<") { - // Nothing is allowed - ret = "<0.0.0"; - } else { - // Nothing is forbidden - ret = "*"; - } - } else if (operator && anyX) { - // Replace X with 0 - if (xm) { - minor = 0; - } - if (xp) { - patch = 0; - } + if (xM) { + if (operator === ">" || operator === "<") { + // Nothing is allowed + ret = "<0.0.0"; + } else { + // Nothing is forbidden + ret = "*"; + } + } else if (operator && anyX) { + // Replace X with 0 + if (xm) { + minor = 0; + } + if (xp) { + patch = 0; + } - if (operator === ">") { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - operator = ">="; - if (xm) { - major = Number(major) + 1; - minor = 0; - patch = 0; + if (operator === ">") { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + operator = ">="; + if (xm) { + major = Number(major) + 1; + minor = 0; + patch = 0; + } else if (xp) { + minor = Number(minor) + 1; + patch = 0; + } + } else if (operator === "<=") { + // <=0.7.x is actually <0.8.0, since any 0.7.x should pass. Similarly, <=7.x is actually <8.0.0, etc. + operator = "<"; + if (xm) { + major = Number(major) + 1; + } else { + minor = Number(minor) + 1; + } + } + + ret = `${operator}${major}.${minor}.${patch}`; + } else if (xm) { + ret = `>=${major}.0.0 <${Number(major) + 1}.0.0`; } else if (xp) { - minor = Number(minor) + 1; - patch = 0; + ret = `>=${major}.${minor}.0 <${major}.${Number(minor) + 1}.0`; } - } else if (operator === "<=") { - // <=0.7.x is actually <0.8.0, since any 0.7.x should pass. Similarly, <=7.x is actually <8.0.0, etc. - operator = "<"; - if (xm) { - major = Number(major) + 1; - } else { - minor = Number(minor) + 1; - } - } - ret = `${operator}${major}.${minor}.${patch}`; - } else if (xm) { - ret = `>=${major}.0.0 <${Number(major) + 1}.0.0`; - } else if (xp) { - ret = `>=${major}.${minor}.0 <${major}.${Number(minor) + 1}.0`; - } - - return ret; - }); + return ret; + }); } function replaceXRanges(comp, loose) { - return comp - .split(/\s+/) - .map((comp1) => replaceXRange(comp1, loose)) - .join(" "); + return comp + .split(/\s+/) + .map((comp1) => replaceXRange(comp1, loose)) + .join(" "); } // Because * is AND-ed with everything else in the comparator, and '' means "any version", just remove the *s entirely. function replaceStars(comp) { - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[STAR], ""); + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], ""); } // Comprised of xranges, tildes, stars, and gtlt's at this point. // Already replaced the hyphen ranges turn into a set of JUST comparators. function parseComparator(comp, loose) { - comp = replaceCarets(comp, loose); - comp = replaceTildes(comp, loose); - comp = replaceXRanges(comp, loose); - comp = replaceStars(comp, loose); + comp = replaceCarets(comp, loose); + comp = replaceTildes(comp, loose); + comp = replaceXRanges(comp, loose); + comp = replaceStars(comp, loose); - return comp; + return comp; } class SemVer { - /** + + /** * A semantic version. * @param {string} version The version. * @param {boolean} loose If this is a loose representation of a version. * @returns {SemVer} a new instance. */ - constructor(version, loose) { - if (version instanceof SemVer) { - if (version.loose === loose) { - return version; - } - version = version.version; - } else if (typeof version !== "string") { - throw new TypeError(`Invalid Version: ${version}`); + constructor(version, loose) { + if (version instanceof SemVer) { + if (version.loose === loose) { + return version; + } + version = version.version; + } else if (typeof version !== "string") { + throw new TypeError(`Invalid Version: ${version}`); + } + if (version.length > MAX_LENGTH) { + throw new TypeError(`version is longer than ${MAX_LENGTH} characters`); + } + if (!(this instanceof SemVer)) { + return new SemVer(version, loose); + } + this.loose = loose; + const matches = version.trim().match(loose ? re[LOOSE] : re[FULL]); + if (!matches) { + throw new TypeError(`Invalid Version: ${version}`); + } + this.raw = version; + // These are actually numbers + this.major = Number(matches[1]); + this.minor = Number(matches[2]); + this.patch = Number(matches[3]); + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError("Invalid major version"); + } + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError("Invalid minor version"); + } + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError("Invalid patch version"); + } + // Numberify any prerelease numeric ids + if (matches[4]) { + this.prerelease = matches[4].split(".").map((id) => { + if ((/^[0-9]+$/).test(id)) { + const num = Number(id); + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num; + } + } + + return id; + }); + } else { + this.prerelease = []; + } + this.build = matches[5] ? matches[5].split(".") : []; + this.format(); } - if (version.length > MAX_LENGTH) { - throw new TypeError(`version is longer than ${MAX_LENGTH} characters`); - } - if (!(this instanceof SemVer)) { - return new SemVer(version, loose); - } - this.loose = loose; - const matches = version.trim().match(loose ? re[LOOSE] : re[FULL]); - if (!matches) { - throw new TypeError(`Invalid Version: ${version}`); - } - this.raw = version; - // These are actually numbers - this.major = Number(matches[1]); - this.minor = Number(matches[2]); - this.patch = Number(matches[3]); - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError("Invalid major version"); - } - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError("Invalid minor version"); - } - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError("Invalid patch version"); - } - // Numberify any prerelease numeric ids - if (matches[4]) { - this.prerelease = matches[4].split(".").map((id) => { - if (/^[0-9]+$/.test(id)) { - const num = Number(id); - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num; - } + + format() { + this.version = `${this.major}.${this.minor}.${this.patch}`; + if (this.prerelease.length) { + this.version += `-${this.prerelease.join(".")}`; } - return id; - }); - } else { - this.prerelease = []; - } - this.build = matches[5] ? matches[5].split(".") : []; - this.format(); - } - - format() { - this.version = `${this.major}.${this.minor}.${this.patch}`; - if (this.prerelease.length) { - this.version += `-${this.prerelease.join(".")}`; + return this.version; } - return this.version; - } + toString() { + return this.version; + } - toString() { - return this.version; - } - - /** + /** * Comares the current instance against another instance. * @param {SemVer} other The SemVer to comare to. * @returns {0|1|-1} A comparable value for sorting. */ - compare(other) { - return this.compareMain(other) || this.comparePre(other); - } - - compareMain(other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.loose); + compare(other) { + return this.compareMain(other) || this.comparePre(other); } - return ( - compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) - ); - } + compareMain(other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.loose); + } - comparePre(other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.loose); + return ( + compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch) + ); } - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1; - } else if (!this.prerelease.length && other.prerelease.length) { - return 1; - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0; - } - let idx = 0; - do { - const thisPrelease = this.prerelease[idx]; - const otherPrelease = other.prerelease[idx]; - const thisPreleaseIsUndefined = typeof thisPrelease === "undefined"; - const otherPreleaseIsUndefined = typeof otherPrelease === "undefined"; - if (thisPreleaseIsUndefined && otherPreleaseIsUndefined) { + + comparePre(other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.loose); + } + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1; + } else if (!this.prerelease.length && other.prerelease.length) { + return 1; + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0; + } + let idx = 0; + do { + const thisPrelease = this.prerelease[idx]; + const otherPrelease = other.prerelease[idx]; + const thisPreleaseIsUndefined = typeof thisPrelease === "undefined"; + const otherPreleaseIsUndefined = typeof otherPrelease === "undefined"; + if (thisPreleaseIsUndefined && otherPreleaseIsUndefined) { + return 0; + } else if (otherPreleaseIsUndefined) { + return 1; + } else if (thisPreleaseIsUndefined) { + return -1; + } else if (thisPrelease === otherPrelease) { + continue; + } else { + return compareIdentifiers(thisPrelease, otherPrelease); + } + } while ((idx += 1) > 0); + + // Should not hit this point, but assume equal ranking. return 0; - } else if (otherPreleaseIsUndefined) { - return 1; - } else if (thisPreleaseIsUndefined) { - return -1; - } else if (thisPrelease === otherPrelease) { - continue; - } else { - return compareIdentifiers(thisPrelease, otherPrelease); - } - } while ((idx += 1) > 0); - - // Should not hit this point, but assume equal ranking. - return 0; - } + } } -const compare = (leftVersion, rightVersion, loose) => - new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose)); +const compare = (leftVersion, rightVersion, loose) => new SemVer(leftVersion, loose).compare(new SemVer(rightVersion, loose)); const gt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) > 0; const lt = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) < 0; const eq = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) === 0; @@ -583,134 +581,134 @@ const gte = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVers const lte = (leftVersion, rightVersion, loose) => compare(leftVersion, rightVersion, loose) <= 0; function cmp(left, op, right, loose) { - let ret; - switch (op) { - case "===": - if (typeof left === "object") { - left = left.version; - } - if (typeof right === "object") { - right = right.version; - } - ret = left === right; - break; - case "!==": - if (typeof left === "object") { - left = left.version; - } - if (typeof right === "object") { - right = right.version; - } - ret = left !== right; - break; - case "": - case "=": - case "==": - ret = eq(left, right, loose); - break; - case "!=": - ret = neq(left, right, loose); - break; - case ">": - ret = gt(left, right, loose); - break; - case ">=": - ret = gte(left, right, loose); - break; - case "<": - ret = lt(left, right, loose); - break; - case "<=": - ret = lte(left, right, loose); - break; - default: - throw new TypeError(`Invalid operator: ${op}`); - } + let ret; + switch (op) { + case "===": + if (typeof left === "object") { + left = left.version; + } + if (typeof right === "object") { + right = right.version; + } + ret = left === right; + break; + case "!==": + if (typeof left === "object") { + left = left.version; + } + if (typeof right === "object") { + right = right.version; + } + ret = left !== right; + break; + case "": + case "=": + case "==": + ret = eq(left, right, loose); + break; + case "!=": + ret = neq(left, right, loose); + break; + case ">": + ret = gt(left, right, loose); + break; + case ">=": + ret = gte(left, right, loose); + break; + case "<": + ret = lt(left, right, loose); + break; + case "<=": + ret = lte(left, right, loose); + break; + default: + throw new TypeError(`Invalid operator: ${op}`); + } - return ret; + return ret; } function testSet(set, version) { - for (let idx = 0; idx < set.length; idx++) { - if (!set[idx].test(version)) { - return false; + for (let idx = 0; idx < set.length; idx++) { + if (!set[idx].test(version)) { + return false; + } } - } - if (version.prerelease.length) { + if (version.prerelease.length) { // Find the set of versions that are allowed to have prereleases // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 // That should allow `1.2.3-pr.2` to pass. // However, `1.2.4-alpha.notready` should NOT be allowed, even though it's within the range set by the comparators. - for (let idx = 0; idx < set.length; idx++) { - if (set[idx].semver !== ANY) { - if (set[idx].semver.prerelease.length > 0) { - const allowed = set[idx].semver; - if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch) { - return true; - } + for (let idx = 0; idx < set.length; idx++) { + if (set[idx].semver !== ANY) { + if (set[idx].semver.prerelease.length > 0) { + const allowed = set[idx].semver; + if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch) { + return true; + } + } + } } - } + + // Version has a -pre, but it's not one of the ones we like. + return false; } - // Version has a -pre, but it's not one of the ones we like. - return false; - } - - return true; + return true; } class Comparator { - constructor(comp, loose) { - if (comp instanceof Comparator) { - if (comp.loose === loose) { - return comp; - } - comp = comp.value; - } - if (!(this instanceof Comparator)) { - return new Comparator(comp, loose); - } - this.loose = loose; - this.parse(comp); - if (this.semver === ANY) { - this.value = ""; - } else { - this.value = this.operator + this.semver.version; - } - } - - parse(comp) { - const regex = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; - const matches = comp.match(regex); - if (!matches) { - throw new TypeError(`Invalid comparator: ${comp}`); - } - this.operator = matches[1]; - if (this.operator === "=") { - this.operator = ""; - } - // If it literally is just '>' or '' then allow anything. - if (matches[2]) { - this.semver = new SemVer(matches[2], this.loose); - } else { - this.semver = ANY; - } - } - - toString() { - return this.value; - } - - test(version) { - if (this.semver === ANY) { - return true; - } - if (typeof version === "string") { - version = new SemVer(version, this.loose); + constructor(comp, loose) { + if (comp instanceof Comparator) { + if (comp.loose === loose) { + return comp; + } + comp = comp.value; + } + if (!(this instanceof Comparator)) { + return new Comparator(comp, loose); + } + this.loose = loose; + this.parse(comp); + if (this.semver === ANY) { + this.value = ""; + } else { + this.value = this.operator + this.semver.version; + } } - return cmp(version, this.operator, this.semver, this.loose); - } + parse(comp) { + const regex = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + const matches = comp.match(regex); + if (!matches) { + throw new TypeError(`Invalid comparator: ${comp}`); + } + this.operator = matches[1]; + if (this.operator === "=") { + this.operator = ""; + } + // If it literally is just '>' or '' then allow anything. + if (matches[2]) { + this.semver = new SemVer(matches[2], this.loose); + } else { + this.semver = ANY; + } + } + + toString() { + return this.value; + } + + test(version) { + if (this.semver === ANY) { + return true; + } + if (typeof version === "string") { + version = new SemVer(version, this.loose); + } + + return cmp(version, this.operator, this.semver, this.loose); + } } /** @@ -720,99 +718,99 @@ class Comparator { * @returns {Range} the Range instace. */ class Range { - constructor(range, loose) { - if (range instanceof Range) { - if (range.loose === loose) { - return range; - } + constructor(range, loose) { + if (range instanceof Range) { + if (range.loose === loose) { + return range; + } - return new Range(range.raw, loose); - } - if (range instanceof Comparator) { - return new Range(range.value, loose); - } - if (!(this instanceof Range)) { - return new Range(range, loose); - } - this.loose = loose; - // First, split based on boolean or || - /** + return new Range(range.raw, loose); + } + if (range instanceof Comparator) { + return new Range(range.value, loose); + } + if (!(this instanceof Range)) { + return new Range(range, loose); + } + this.loose = loose; + // First, split based on boolean or || + /** * @type {string} */ - this.raw = range; - // Throw out any that are not relevant for whatever reason - const hasLength = (item) => item.length; - this.set = this.raw - .split(/\s*\|\|\s*/) - .map(function (range1) { - return this.parseRange(range1.trim()); - }, this) - .filter(hasLength); - if (!this.set.length) { - throw new TypeError(`Invalid SemVer Range: ${range}`); - } - this.format(); - } - - format() { - this.range = this.set - .map((comps) => comps.join(" ").trim()) - .join("||") - .trim(); - - return this.range; - } - - toString() { - return this.range; - } - - parseRange(range) { - const loose = this.loose; - range = range.trim(); - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - const hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; - range = range.replace(hr, hyphenReplace); - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[TILDETRIM], tildeTrimReplace); - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[CARETTRIM], caretTrimReplace); - // Normalize spaces - range = range.split(/\s+/).join(" "); - // At this point, the range is completely trimmed and ready to be split into comparators. - const compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; - let set = range - .split(" ") - .map((comp) => parseComparator(comp, loose)) - .join(" ") - .split(/\s+/); - if (loose) { - // In loose mode, throw out any that are not valid comparators - set = set.filter((comp) => Boolean(comp.match(compRe))); - } - set = set.map((comp) => new Comparator(comp, loose)); - - return set; - } - - // If ANY of the sets match ALL of its comparators, then pass - test(version) { - if (!version) { - return false; - } - if (typeof version === "string") { - version = new SemVer(version, this.loose); - } - for (let idx = 0; idx < this.set.length; idx++) { - if (testSet(this.set[idx], version)) { - return true; - } + this.raw = range; + // Throw out any that are not relevant for whatever reason + const hasLength = (item) => item.length; + this.set = this.raw + .split(/\s*\|\|\s*/) + .map(function (range1) { + return this.parseRange(range1.trim()); + }, this) + .filter(hasLength); + if (!this.set.length) { + throw new TypeError(`Invalid SemVer Range: ${range}`); + } + this.format(); } - return false; - } + format() { + this.range = this.set + .map((comps) => comps.join(" ").trim()) + .join("||") + .trim(); + + return this.range; + } + + toString() { + return this.range; + } + + parseRange(range) { + const loose = this.loose; + range = range.trim(); + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + const hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; + range = range.replace(hr, hyphenReplace); + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace); + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace); + // Normalize spaces + range = range.split(/\s+/).join(" "); + // At this point, the range is completely trimmed and ready to be split into comparators. + const compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + let set = range + .split(" ") + .map((comp) => parseComparator(comp, loose)) + .join(" ") + .split(/\s+/); + if (loose) { + // In loose mode, throw out any that are not valid comparators + set = set.filter((comp) => Boolean(comp.match(compRe))); + } + set = set.map((comp) => new Comparator(comp, loose)); + + return set; + } + + // If ANY of the sets match ALL of its comparators, then pass + test(version) { + if (!version) { + return false; + } + if (typeof version === "string") { + version = new SemVer(version, this.loose); + } + for (let idx = 0; idx < this.set.length; idx++) { + if (testSet(this.set[idx], version)) { + return true; + } + } + + return false; + } } /** @@ -823,13 +821,13 @@ class Range { * @returns {boolean} Whether the versions successfully satisfies the range. */ function satisfies(version, range, loose) { - try { - const rangeObj = new Range(range, loose); + try { + const rangeObj = new Range(range, loose); - return rangeObj.test(version); - } catch (er) { - return false; - } + return rangeObj.test(version); + } catch (er) { + return false; + } } module.exports.satisfies = satisfies; diff --git a/src/Bladeburner/Bladeburner.ts b/src/Bladeburner/Bladeburner.ts index f0ccde9b2..4204067aa 100644 --- a/src/Bladeburner/Bladeburner.ts +++ b/src/Bladeburner/Bladeburner.ts @@ -1928,7 +1928,7 @@ export class Bladeburner implements IBladeburner { // Count increase for contracts/operations for (const contract of Object.values(this.contracts) as Contract[]) { - let growthF = Growths[contract.name]; + const growthF = Growths[contract.name]; if (growthF === undefined) throw new Error(`growth formula for action '${contract.name}' is undefined`); contract.count += (seconds * growthF()) / BladeburnerConstants.ActionCountGrowthPeriod; } diff --git a/src/Bladeburner/ui/BlackOpList.tsx b/src/Bladeburner/ui/BlackOpList.tsx index 02dc5470a..d3b4139f8 100644 --- a/src/Bladeburner/ui/BlackOpList.tsx +++ b/src/Bladeburner/ui/BlackOpList.tsx @@ -22,8 +22,7 @@ export function BlackOpList(props: IProps): React.ReactElement { }); blackops = blackops.filter( - (blackop: BlackOperation, i: number) => - !( + (blackop: BlackOperation, i: number) => !( props.bladeburner.blackops[blackops[i].name] == null && i !== 0 && props.bladeburner.blackops[blackops[i - 1].name] == null diff --git a/src/Corporation/ui/CityTabs.tsx b/src/Corporation/ui/CityTabs.tsx index 4b1cdc48c..0ab4c9d1a 100644 --- a/src/Corporation/ui/CityTabs.tsx +++ b/src/Corporation/ui/CityTabs.tsx @@ -61,8 +61,7 @@ export function CityTabs(props: IProps): React.ReactElement { return ( <> {Object.values(props.division.offices).map( - (office: OfficeSpace | 0) => - office !== 0 && ( + (office: OfficeSpace | 0) => office !== 0 && ( - props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined, + (industryType: string) => props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined, ) .sort(); if (possibleIndustries.length === 0) return <>; diff --git a/src/Corporation/ui/IndustryWarehouse.tsx b/src/Corporation/ui/IndustryWarehouse.tsx index c2ae1df9e..16ace3247 100644 --- a/src/Corporation/ui/IndustryWarehouse.tsx +++ b/src/Corporation/ui/IndustryWarehouse.tsx @@ -424,7 +424,6 @@ function MaterialComponent(props: IMaterialProps): React.ReactElement { )}
- {/* TODO: add flashing here */} @@ -481,17 +480,6 @@ export function IndustryWarehouse(props: IProps): React.ReactElement { }); } - // Industry material Requirements - let generalReqsText = "This Industry uses [" + Object.keys(props.division.reqMats).join(", ") + "] in order to "; - if (props.division.prodMats.length > 0) { - generalReqsText += "produce [" + props.division.prodMats.join(", ") + "] "; - if (props.division.makesProducts) { - generalReqsText += " and " + props.division.getProductDescriptionText(); - } - } else if (props.division.makesProducts) { - generalReqsText += props.division.getProductDescriptionText() + "."; - } - const ratioLines = []; for (const matName in props.division.reqMats) { if (props.division.reqMats.hasOwnProperty(matName)) { @@ -504,16 +492,6 @@ export function IndustryWarehouse(props: IProps): React.ReactElement { } } - let createdItemsText = "in order to create "; - if (props.division.prodMats.length > 0) { - createdItemsText += "one of each produced Material (" + props.division.prodMats.join(", ") + ") "; - if (props.division.makesProducts) { - createdItemsText += "or to create one of its Products"; - } - } else if (props.division.makesProducts) { - createdItemsText += "one of its Products"; - } - // Current State: let stateText; switch (props.division.state) { diff --git a/src/Corporation/ui/NewIndustryPopup.tsx b/src/Corporation/ui/NewIndustryPopup.tsx index ec510a4a5..210a60bab 100644 --- a/src/Corporation/ui/NewIndustryPopup.tsx +++ b/src/Corporation/ui/NewIndustryPopup.tsx @@ -17,8 +17,7 @@ export function NewIndustryPopup(props: IProps): React.ReactElement { const allIndustries = Object.keys(Industries).sort(); const possibleIndustries = allIndustries .filter( - (industryType: string) => - props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined, + (industryType: string) => props.corp.divisions.find((division: IIndustry) => division.type === industryType) === undefined, ) .sort(); const [industry, setIndustry] = useState(possibleIndustries.length > 0 ? possibleIndustries[0] : ""); diff --git a/src/Corporation/ui/SmartSupplyPopup.tsx b/src/Corporation/ui/SmartSupplyPopup.tsx index 181a2d23f..02d882173 100644 --- a/src/Corporation/ui/SmartSupplyPopup.tsx +++ b/src/Corporation/ui/SmartSupplyPopup.tsx @@ -5,7 +5,6 @@ import { ICorporation } from "../ICorporation"; import { IIndustry } from "../IIndustry"; import { SetSmartSupply } from "../Actions"; import { IPlayer } from "../../PersonObjects/IPlayer"; -import { isRelevantMaterial } from "./Helpers"; import { Material } from "../Material"; interface ILeftoverProps { @@ -43,7 +42,7 @@ interface IProps { export function SmartSupplyPopup(props: IProps): React.ReactElement { const setRerender = useState(false)[1]; - function rerender() { + function rerender(): void { setRerender((old) => !old); } // Smart Supply Checkbox diff --git a/src/DevMenu.jsx b/src/DevMenu.jsx index 67be7b4c5..c954591b2 100644 --- a/src/DevMenu.jsx +++ b/src/DevMenu.jsx @@ -737,8 +737,7 @@ class DevMenuComponent extends Component { } let sourceFiles = []; - validSFN.forEach((i) => - sourceFiles.push( + validSFN.forEach((i) => sourceFiles.push( SF-{i}: diff --git a/src/Faction/ui/AugmentationsPage.tsx b/src/Faction/ui/AugmentationsPage.tsx index 604930286..71514db56 100644 --- a/src/Faction/ui/AugmentationsPage.tsx +++ b/src/Faction/ui/AugmentationsPage.tsx @@ -123,8 +123,7 @@ export class AugmentationsPage extends React.Component { render(): React.ReactNode { const augs = this.getAugsSorted(); const purchasable = augs.filter( - (aug: string) => - aug === AugmentationNames.NeuroFluxGovernor || + (aug: string) => aug === AugmentationNames.NeuroFluxGovernor || (!this.props.p.augmentations.some((a) => a.name === aug) && !this.props.p.queuedAugmentations.some((a) => a.name === aug)), ); diff --git a/src/Hacknet/HacknetHelpers.jsx b/src/Hacknet/HacknetHelpers.tsx similarity index 57% rename from src/Hacknet/HacknetHelpers.jsx rename to src/Hacknet/HacknetHelpers.tsx index 19e18a7e2..38411fdba 100644 --- a/src/Hacknet/HacknetHelpers.jsx +++ b/src/Hacknet/HacknetHelpers.tsx @@ -18,117 +18,109 @@ import { HashUpgrades } from "./HashUpgrades"; import { generateRandomContract } from "../CodingContractGenerator"; import { iTutorialSteps, iTutorialNextStep, ITutorial } from "../InteractiveTutorial"; -import { Player } from "../Player"; +import { IPlayer } from "../PersonObjects/IPlayer"; import { AllServers } from "../Server/AllServers"; import { GetServerByHostname } from "../Server/ServerHelpers"; +import { Server } from "../Server/Server"; import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; -import { Page, routing } from "../ui/navigationTracking"; - -import React from "react"; -import ReactDOM from "react-dom"; -import { HacknetRoot } from "./ui/Root"; - -let hacknetNodesDiv; -function hacknetNodesInit() { - hacknetNodesDiv = document.getElementById("hacknet-nodes-container"); - document.removeEventListener("DOMContentLoaded", hacknetNodesInit); -} - -document.addEventListener("DOMContentLoaded", hacknetNodesInit); // Returns a boolean indicating whether the player has Hacknet Servers // (the upgraded form of Hacknet Nodes) -export function hasHacknetServers() { - return Player.bitNodeN === 9 || SourceFileFlags[9] > 0; +export function hasHacknetServers(player: IPlayer): boolean { + return player.bitNodeN === 9 || SourceFileFlags[9] > 0; } -export function purchaseHacknet() { +export function purchaseHacknet(player: IPlayer): number { /* INTERACTIVE TUTORIAL */ if (ITutorial.isRunning) { if (ITutorial.currStep === iTutorialSteps.HacknetNodesIntroduction) { iTutorialNextStep(); } else { - return; + return -1; } } /* END INTERACTIVE TUTORIAL */ - const numOwned = Player.hacknetNodes.length; - if (hasHacknetServers()) { - const cost = getCostOfNextHacknetServer(); + const numOwned = player.hacknetNodes.length; + if (hasHacknetServers(player)) { + const cost = getCostOfNextHacknetServer(player); if (isNaN(cost)) { throw new Error(`Calculated cost of purchasing HacknetServer is NaN`); } - if (!Player.canAfford(cost)) { + if (!player.canAfford(cost)) { return -1; } - Player.loseMoney(cost); - Player.createHacknetServer(); - updateHashManagerCapacity(); + player.loseMoney(cost); + player.createHacknetServer(); + updateHashManagerCapacity(player); return numOwned; } else { - const cost = getCostOfNextHacknetNode(); + const cost = getCostOfNextHacknetNode(player); if (isNaN(cost)) { throw new Error(`Calculated cost of purchasing HacknetNode is NaN`); } - if (!Player.canAfford(cost)) { + if (!player.canAfford(cost)) { return -1; } // Auto generate a name for the Node const name = "hacknet-node-" + numOwned; - const node = new HacknetNode(name, Player.hacknet_node_money_mult); + const node = new HacknetNode(name, player.hacknet_node_money_mult); - Player.loseMoney(cost); - Player.hacknetNodes.push(node); + player.loseMoney(cost); + player.hacknetNodes.push(node); return numOwned; } } -export function hasMaxNumberHacknetServers() { - return hasHacknetServers() && Player.hacknetNodes.length >= HacknetServerConstants.MaxServers; +export function hasMaxNumberHacknetServers(player: IPlayer): boolean { + return hasHacknetServers(player) && player.hacknetNodes.length >= HacknetServerConstants.MaxServers; } -export function getCostOfNextHacknetNode() { - return calculateNodeCost(Player.hacknetNodes.length + 1, Player.hacknet_node_purchase_cost_mult); +export function getCostOfNextHacknetNode(player: IPlayer): number { + return calculateNodeCost(player.hacknetNodes.length + 1, player.hacknet_node_purchase_cost_mult); } -export function getCostOfNextHacknetServer() { - return calculateServerCost(Player.hacknetNodes.length + 1, Player.hacknet_node_purchase_cost_mult); +export function getCostOfNextHacknetServer(player: IPlayer): number { + return calculateServerCost(player.hacknetNodes.length + 1, player.hacknet_node_purchase_cost_mult); } // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level -export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) { +export function getMaxNumberLevelUpgrades( + player: IPlayer, + nodeObj: HacknetNode | HacknetServer, + maxLevel: number, +): number { if (maxLevel == null) { throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`); } - if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(1, Player.hacknet_node_level_cost_mult))) { + if (player.money.lt(nodeObj.calculateLevelUpgradeCost(1, player.hacknet_node_level_cost_mult))) { return 0; } let min = 1; let max = maxLevel - 1; - let levelsToMax = maxLevel - nodeObj.level; - if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, Player.hacknet_node_level_cost_mult))) { + const levelsToMax = maxLevel - nodeObj.level; + if (player.money.gt(nodeObj.calculateLevelUpgradeCost(levelsToMax, player.hacknet_node_level_cost_mult))) { return levelsToMax; } while (min <= max) { - var curr = ((min + max) / 2) | 0; + const curr = ((min + max) / 2) | 0; if ( curr !== maxLevel && - Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult)) && - Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, Player.hacknet_node_level_cost_mult)) + player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult)) && + player.money.lt(nodeObj.calculateLevelUpgradeCost(curr + 1, player.hacknet_node_level_cost_mult)) ) { return Math.min(levelsToMax, curr); - } else if (Player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) { + } else if (player.money.lt(nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult))) { max = curr - 1; - } else if (Player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, Player.hacknet_node_level_cost_mult))) { + } else if (player.money.gt(nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult))) { min = curr + 1; } else { return Math.min(levelsToMax, curr); @@ -138,12 +130,16 @@ export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) { } // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's RAM -export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { +export function getMaxNumberRamUpgrades( + player: IPlayer, + nodeObj: HacknetNode | HacknetServer, + maxLevel: number, +): number { if (maxLevel == null) { throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`); } - if (Player.money.lt(nodeObj.calculateRamUpgradeCost(1, Player.hacknet_node_ram_cost_mult))) { + if (player.money.lt(nodeObj.calculateRamUpgradeCost(1, player.hacknet_node_ram_cost_mult))) { return 0; } @@ -153,13 +149,13 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { } else { levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram)); } - if (Player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, Player.hacknet_node_ram_cost_mult))) { + if (player.money.gt(nodeObj.calculateRamUpgradeCost(levelsToMax, player.hacknet_node_ram_cost_mult))) { return levelsToMax; } //We'll just loop until we find the max for (let i = levelsToMax - 1; i >= 0; --i) { - if (Player.money.gt(nodeObj.calculateRamUpgradeCost(i, Player.hacknet_node_ram_cost_mult))) { + if (player.money.gt(nodeObj.calculateRamUpgradeCost(i, player.hacknet_node_ram_cost_mult))) { return i; } } @@ -167,34 +163,38 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { } // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cores -export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { +export function getMaxNumberCoreUpgrades( + player: IPlayer, + nodeObj: HacknetNode | HacknetServer, + maxLevel: number, +): number { if (maxLevel == null) { throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`); } - if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(1, Player.hacknet_node_core_cost_mult))) { + if (player.money.lt(nodeObj.calculateCoreUpgradeCost(1, player.hacknet_node_core_cost_mult))) { return 0; } let min = 1; let max = maxLevel - 1; const levelsToMax = maxLevel - nodeObj.cores; - if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, Player.hacknet_node_core_cost_mult))) { + if (player.money.gt(nodeObj.calculateCoreUpgradeCost(levelsToMax, player.hacknet_node_core_cost_mult))) { return levelsToMax; } // Use a binary search to find the max possible number of upgrades while (min <= max) { - let curr = ((min + max) / 2) | 0; + const curr = ((min + max) / 2) | 0; if ( curr != maxLevel && - Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult)) && - Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, Player.hacknet_node_core_cost_mult)) + player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult)) && + player.money.lt(nodeObj.calculateCoreUpgradeCost(curr + 1, player.hacknet_node_core_cost_mult)) ) { return Math.min(levelsToMax, curr); - } else if (Player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) { + } else if (player.money.lt(nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult))) { max = curr - 1; - } else if (Player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, Player.hacknet_node_core_cost_mult))) { + } else if (player.money.gt(nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult))) { min = curr + 1; } else { return Math.min(levelsToMax, curr); @@ -205,34 +205,34 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { } // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cache -export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) { +export function getMaxNumberCacheUpgrades(player: IPlayer, nodeObj: HacknetServer, maxLevel: number): number { if (maxLevel == null) { throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`); } - if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(1))) { + if (!player.canAfford(nodeObj.calculateCacheUpgradeCost(1))) { return 0; } let min = 1; let max = maxLevel - 1; const levelsToMax = maxLevel - nodeObj.cache; - if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(levelsToMax))) { + if (player.canAfford(nodeObj.calculateCacheUpgradeCost(levelsToMax))) { return levelsToMax; } // Use a binary search to find the max possible number of upgrades while (min <= max) { - let curr = ((min + max) / 2) | 0; + const curr = ((min + max) / 2) | 0; if ( curr != maxLevel && - Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr)) && - !Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr + 1)) + player.canAfford(nodeObj.calculateCacheUpgradeCost(curr)) && + !player.canAfford(nodeObj.calculateCacheUpgradeCost(curr + 1)) ) { return Math.min(levelsToMax, curr); - } else if (!Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) { + } else if (!player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) { max = curr - 1; - } else if (Player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) { + } else if (player.canAfford(nodeObj.calculateCacheUpgradeCost(curr))) { min = curr + 1; } else { return Math.min(levelsToMax, curr); @@ -242,9 +242,9 @@ export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) { return 0; } -export function purchaseLevelUpgrade(node, levels = 1) { +export function purchaseLevelUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { const sanitizedLevels = Math.round(levels); - const cost = node.calculateLevelUpgradeCost(sanitizedLevels, Player.hacknet_node_level_cost_mult); + const cost = node.calculateLevelUpgradeCost(sanitizedLevels, player.hacknet_node_level_cost_mult); if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { return false; } @@ -260,60 +260,61 @@ export function purchaseLevelUpgrade(node, levels = 1) { // the maximum number of upgrades and use that if (node.level + sanitizedLevels > (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel)) { const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxLevel : HacknetNodeConstants.MaxLevel) - node.level); - return purchaseLevelUpgrade(node, diff); + return purchaseLevelUpgrade(player, node, diff); } - if (!Player.canAfford(cost)) { + if (!player.canAfford(cost)) { return false; } - Player.loseMoney(cost); - node.upgradeLevel(sanitizedLevels, Player.hacknet_node_money_mult); + player.loseMoney(cost); + node.upgradeLevel(sanitizedLevels, player.hacknet_node_money_mult); return true; } -export function purchaseRamUpgrade(node, levels = 1) { +export function purchaseRamUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { const sanitizedLevels = Math.round(levels); - const cost = node.calculateRamUpgradeCost(sanitizedLevels, Player.hacknet_node_ram_cost_mult); + const cost = node.calculateRamUpgradeCost(sanitizedLevels, player.hacknet_node_ram_cost_mult); if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { return false; } - const isServer = node instanceof HacknetServer; + if (node instanceof HacknetServer && node.maxRam >= HacknetServerConstants.MaxRam) { + return false; + } - // Fail if we're already at max - if (node.ram >= (isServer ? HacknetServerConstants.MaxRam : HacknetNodeConstants.MaxRam)) { + if (node instanceof HacknetNode && node.ram >= HacknetNodeConstants.MaxRam) { return false; } // If the number of specified upgrades would exceed the max RAM, calculate the // max possible number of upgrades and use that - if (isServer) { + if (node instanceof HacknetServer) { if (node.maxRam * Math.pow(2, sanitizedLevels) > HacknetServerConstants.MaxRam) { const diff = Math.max(0, Math.log2(Math.round(HacknetServerConstants.MaxRam / node.maxRam))); - return purchaseRamUpgrade(node, diff); + return purchaseRamUpgrade(player, node, diff); } - } else { + } else if (node instanceof HacknetNode) { if (node.ram * Math.pow(2, sanitizedLevels) > HacknetNodeConstants.MaxRam) { const diff = Math.max(0, Math.log2(Math.round(HacknetNodeConstants.MaxRam / node.ram))); - return purchaseRamUpgrade(node, diff); + return purchaseRamUpgrade(player, node, diff); } } - if (!Player.canAfford(cost)) { + if (!player.canAfford(cost)) { return false; } - Player.loseMoney(cost); - node.upgradeRam(sanitizedLevels, Player.hacknet_node_money_mult); + player.loseMoney(cost); + node.upgradeRam(sanitizedLevels, player.hacknet_node_money_mult); return true; } -export function purchaseCoreUpgrade(node, levels = 1) { +export function purchaseCoreUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { const sanitizedLevels = Math.round(levels); - const cost = node.calculateCoreUpgradeCost(sanitizedLevels, Player.hacknet_node_core_cost_mult); + const cost = node.calculateCoreUpgradeCost(sanitizedLevels, player.hacknet_node_core_cost_mult); if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { return false; } @@ -329,20 +330,20 @@ export function purchaseCoreUpgrade(node, levels = 1) { // the max possible number of upgrades and use that if (node.cores + sanitizedLevels > (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores)) { const diff = Math.max(0, (isServer ? HacknetServerConstants.MaxCores : HacknetNodeConstants.MaxCores) - node.cores); - return purchaseCoreUpgrade(node, diff); + return purchaseCoreUpgrade(player, node, diff); } - if (!Player.canAfford(cost)) { + if (!player.canAfford(cost)) { return false; } - Player.loseMoney(cost); - node.upgradeCore(sanitizedLevels, Player.hacknet_node_money_mult); + player.loseMoney(cost); + node.upgradeCore(sanitizedLevels, player.hacknet_node_money_mult); return true; } -export function purchaseCacheUpgrade(node, levels = 1) { +export function purchaseCacheUpgrade(player: IPlayer, node: HacknetServer, levels = 1): boolean { const sanitizedLevels = Math.round(levels); const cost = node.calculateCacheUpgradeCost(sanitizedLevels); if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { @@ -357,143 +358,132 @@ export function purchaseCacheUpgrade(node, levels = 1) { // Fail if we're already at max if (node.cache + sanitizedLevels > HacknetServerConstants.MaxCache) { const diff = Math.max(0, HacknetServerConstants.MaxCache - node.cache); - return purchaseCacheUpgrade(node, diff); + return purchaseCacheUpgrade(player, node, diff); } - if (!Player.canAfford(cost)) { + if (!player.canAfford(cost)) { return false; } - Player.loseMoney(cost); + player.loseMoney(cost); node.upgradeCache(sanitizedLevels); return true; } -// Create/Refresh Hacknet Nodes UI -export function renderHacknetNodesUI() { - if (!routing.isOn(Page.HacknetNodes)) { - return; - } - - ReactDOM.render(, hacknetNodesDiv); -} - -export function clearHacknetNodesUI() { - if (hacknetNodesDiv instanceof HTMLElement) { - ReactDOM.unmountComponentAtNode(hacknetNodesDiv); - } - - hacknetNodesDiv.style.display = "none"; -} - -export function processHacknetEarnings(numCycles) { +export function processHacknetEarnings(player: IPlayer, numCycles: number): number { // Determine if player has Hacknet Nodes or Hacknet Servers, then // call the appropriate function - if (Player.hacknetNodes.length === 0) { + if (player.hacknetNodes.length === 0) { return 0; } - if (hasHacknetServers()) { - return processAllHacknetServerEarnings(numCycles); - } else if (Player.hacknetNodes[0] instanceof HacknetNode) { - return processAllHacknetNodeEarnings(numCycles); + if (hasHacknetServers(player)) { + return processAllHacknetServerEarnings(player, numCycles); + } else if (player.hacknetNodes[0] instanceof HacknetNode) { + return processAllHacknetNodeEarnings(player, numCycles); } else { return 0; } } -function processAllHacknetNodeEarnings(numCycles) { +function processAllHacknetNodeEarnings(player: IPlayer, numCycles: number): number { let total = 0; - for (let i = 0; i < Player.hacknetNodes.length; ++i) { - total += processSingleHacknetNodeEarnings(numCycles, Player.hacknetNodes[i]); + for (let i = 0; i < player.hacknetNodes.length; ++i) { + const node = player.hacknetNodes[i]; + if (typeof node === "string") throw new Error("player node should not be ip string"); + total += processSingleHacknetNodeEarnings(player, numCycles, node); } return total; } -function processSingleHacknetNodeEarnings(numCycles, nodeObj) { +function processSingleHacknetNodeEarnings(player: IPlayer, numCycles: number, nodeObj: HacknetNode): number { const totalEarnings = nodeObj.process(numCycles); - Player.gainMoney(totalEarnings); - Player.recordMoneySource(totalEarnings, "hacknetnode"); + player.gainMoney(totalEarnings); + player.recordMoneySource(totalEarnings, "hacknetnode"); return totalEarnings; } -function processAllHacknetServerEarnings(numCycles) { - if (!(Player.hashManager instanceof HashManager)) { +function processAllHacknetServerEarnings(player: IPlayer, numCycles: number): number { + if (!(player.hashManager instanceof HashManager)) { throw new Error(`Player does not have a HashManager (should be in 'hashManager' prop)`); } let hashes = 0; - for (let i = 0; i < Player.hacknetNodes.length; ++i) { + for (let i = 0; i < player.hacknetNodes.length; ++i) { // hacknetNodes array only contains the IP addresses of the servers. // Also, update the hash rate before processing - const hserver = AllServers[Player.hacknetNodes[i]]; - hserver.updateHashRate(Player.hacknet_node_money_mult); + const ip = player.hacknetNodes[i]; + if (ip instanceof HacknetNode) throw new Error(`player nodes should not be HacketNode`); + const hserver = AllServers[ip]; + if (hserver instanceof Server) throw new Error(`player nodes shoud not be Server`); + hserver.updateHashRate(player.hacknet_node_money_mult); const h = hserver.process(numCycles); hserver.totalHashesGenerated += h; hashes += h; } - Player.hashManager.storeHashes(hashes); + player.hashManager.storeHashes(hashes); return hashes; } -export function updateHashManagerCapacity() { - if (!(Player.hashManager instanceof HashManager)) { +export function updateHashManagerCapacity(player: IPlayer): void { + if (!(player.hashManager instanceof HashManager)) { console.error(`Player does not have a HashManager`); return; } - const nodes = Player.hacknetNodes; + const nodes = player.hacknetNodes; if (nodes.length === 0) { - Player.hashManager.updateCapacity(0); + player.hashManager.updateCapacity(0); return; } let total = 0; for (let i = 0; i < nodes.length; ++i) { if (typeof nodes[i] !== "string") { - Player.hashManager.updateCapacity(0); + player.hashManager.updateCapacity(0); return; } - - const h = AllServers[nodes[i]]; + const ip = nodes[i]; + if (ip instanceof HacknetNode) throw new Error(`player nodes should be string but isn't`); + const h = AllServers[ip]; if (!(h instanceof HacknetServer)) { - Player.hashManager.updateCapacity(0); + player.hashManager.updateCapacity(0); return; } total += h.hashCapacity; } - Player.hashManager.updateCapacity(total); + player.hashManager.updateCapacity(total); } -export function purchaseHashUpgrade(upgName, upgTarget) { - if (!(Player.hashManager instanceof HashManager)) { +export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget: string): boolean { + if (!(player.hashManager instanceof HashManager)) { console.error(`Player does not have a HashManager`); return false; } // HashManager handles the transaction. This just needs to actually implement // the effects of the upgrade - if (Player.hashManager.upgrade(upgName)) { + if (player.hashManager.upgrade(upgName)) { const upg = HashUpgrades[upgName]; switch (upgName) { case "Sell for Money": { - Player.gainMoney(upg.value); - Player.recordMoneySource(upg.value, "hacknetnode"); + player.gainMoney(upg.value); + player.recordMoneySource(upg.value, "hacknetnode"); break; } case "Sell for Corporation Funds": { // This will throw if player doesn't have a corporation try { - Player.corporation.funds = Player.corporation.funds.plus(upg.value); + player.corporation.funds = player.corporation.funds.plus(upg.value); } catch (e) { - Player.hashManager.refundUpgrade(upgName); + player.hashManager.refundUpgrade(upgName); return false; } break; @@ -505,10 +495,11 @@ export function purchaseHashUpgrade(upgName, upgTarget) { console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); return false; } + if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`); target.changeMinimumSecurity(upg.value, true); } catch (e) { - Player.hashManager.refundUpgrade(upgName); + player.hashManager.refundUpgrade(upgName); return false; } break; @@ -520,10 +511,11 @@ export function purchaseHashUpgrade(upgName, upgTarget) { console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); return false; } + if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`); target.changeMaximumMoney(upg.value, true); } catch (e) { - Player.hashManager.refundUpgrade(upgName); + player.hashManager.refundUpgrade(upgName); return false; } break; @@ -539,11 +531,11 @@ export function purchaseHashUpgrade(upgName, upgTarget) { case "Exchange for Corporation Research": { // This will throw if player doesn't have a corporation try { - for (const division of Player.corporation.divisions) { + for (const division of player.corporation.divisions) { division.sciResearch.qty += upg.value; } } catch (e) { - Player.hashManager.refundUpgrade(upgName); + player.hashManager.refundUpgrade(upgName); return false; } break; @@ -551,9 +543,9 @@ export function purchaseHashUpgrade(upgName, upgTarget) { case "Exchange for Bladeburner Rank": { // This will throw if player isnt in Bladeburner try { - Player.bladeburner.changeRank(Player, upg.value); + player.bladeburner.changeRank(player, upg.value); } catch (e) { - Player.hashManager.refundUpgrade(upgName); + player.hashManager.refundUpgrade(upgName); return false; } break; @@ -563,9 +555,9 @@ export function purchaseHashUpgrade(upgName, upgTarget) { try { // As long as we don't change `Bladeburner.totalSkillPoints`, this // shouldn't affect anything else - Player.bladeburner.skillPoints += upg.value; + player.bladeburner.skillPoints += upg.value; } catch (e) { - Player.hashManager.refundUpgrade(upgName); + player.hashManager.refundUpgrade(upgName); return false; } break; @@ -576,7 +568,7 @@ export function purchaseHashUpgrade(upgName, upgTarget) { } default: console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`); - Player.hashManager.refundUpgrade(upgName); + player.hashManager.refundUpgrade(upgName); return false; } diff --git a/src/Hacknet/data/Constants.ts b/src/Hacknet/data/Constants.ts index b34ea8145..555ce4cf8 100644 --- a/src/Hacknet/data/Constants.ts +++ b/src/Hacknet/data/Constants.ts @@ -35,6 +35,19 @@ export const HacknetNodeConstants: { MaxCores: 16, }; +export const PurchaseMultipliers: { + [key: string]: number | string | undefined; + x1: number; + x5: number; + x10: number; + MAX: string; +} = { + x1: 1, + x5: 5, + x10: 10, + MAX: "MAX", +}; + export const HacknetServerConstants: { // Constants for Hacknet Server stats/production HashesPerLevel: number; diff --git a/src/Hacknet/data/HashUpgradesMetadata.tsx b/src/Hacknet/data/HashUpgradesMetadata.tsx index 61a0679aa..8ad2057c0 100644 --- a/src/Hacknet/data/HashUpgradesMetadata.tsx +++ b/src/Hacknet/data/HashUpgradesMetadata.tsx @@ -54,7 +54,7 @@ export const HashUpgradesMetadata: IConstructorParams[] = [ "Use hashes to improve the experience earned when studying at a university by 20%. " + "This effect persists until you install Augmentations", name: "Improve Studying", - //effectText: (level: number) => JSX.Element | null = <>Improves studying by ${level*20}%, + effectText: (level: number): JSX.Element | null => <>Improves studying by {level * 20}%, value: 20, // Improves studying by value% }, { @@ -63,7 +63,7 @@ export const HashUpgradesMetadata: IConstructorParams[] = [ "Use hashes to improve the experience earned when training at the gym by 20%. This effect " + "persists until you install Augmentations", name: "Improve Gym Training", - effectText: (level: number): JSX.Element | null => <>Improves training by ${level * 20}%, + effectText: (level: number): JSX.Element | null => <>Improves training by {level * 20}%, value: 20, // Improves training by value% }, { diff --git a/src/Hacknet/ui/GeneralInfo.jsx b/src/Hacknet/ui/GeneralInfo.jsx deleted file mode 100644 index 5556f0247..000000000 --- a/src/Hacknet/ui/GeneralInfo.jsx +++ /dev/null @@ -1,57 +0,0 @@ -/** - * React Component for the Hacknet Node UI - * - * Displays general information about Hacknet Nodes - */ -import React from "react"; - -import { hasHacknetServers } from "../HacknetHelpers"; - -export class GeneralInfo extends React.Component { - getSecondParagraph() { - if (hasHacknetServers()) { - return ( - `Here, you can purchase a Hacknet Server, an upgraded version of the Hacknet Node. ` + - `Hacknet Servers will perform computations and operations on the network, earning ` + - `you hashes. Hashes can be spent on a variety of different upgrades.` - ); - } else { - return ( - `Here, you can purchase a Hacknet Node, a specialized machine that can connect ` + - `and contribute its resources to the Hacknet network. This allows you to take ` + - `a small percentage of profits from hacks performed on the network. Essentially, ` + - `you are renting out your Node's computing power.` - ); - } - } - - getThirdParagraph() { - if (hasHacknetServers()) { - return ( - `Hacknet Servers can also be used as servers to run scripts. However, running scripts ` + - `on a server will reduce its hash rate (hashes generated per second). A Hacknet Server's hash ` + - `rate will be reduced by the percentage of RAM that is being used by that Server to run ` + - `scripts.` - ); - } else { - return ( - `Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node ` + - `can be upgraded in order to increase its computing power and thereby increase ` + - `the profit you earn from it.` - ); - } - } - - render() { - return ( -
-

- The Hacknet is a global, decentralized network of machines. It is used by hackers all around the world to - anonymously share computing power and perform distributed cyberattacks without the fear of being traced. -

-

{this.getSecondParagraph()}

-

{this.getThirdParagraph()}

-
- ); - } -} diff --git a/src/Hacknet/ui/GeneralInfo.tsx b/src/Hacknet/ui/GeneralInfo.tsx new file mode 100644 index 000000000..3bd3494b4 --- /dev/null +++ b/src/Hacknet/ui/GeneralInfo.tsx @@ -0,0 +1,50 @@ +/** + * React Component for the Hacknet Node UI + * + * Displays general information about Hacknet Nodes + */ +import React from "react"; + +interface IProps { + hasHacknetServers: boolean; +} + +export function GeneralInfo(props: IProps): React.ReactElement { + return ( +
+

+ The Hacknet is a global, decentralized network of machines. It is used by hackers all around the world to + anonymously share computing power and perform distributed cyberattacks without the fear of being traced. +

+ {!props.hasHacknetServers ? ( + <> +

+ {`Here, you can purchase a Hacknet Node, a specialized machine that can connect ` + + `and contribute its resources to the Hacknet network. This allows you to take ` + + `a small percentage of profits from hacks performed on the network. Essentially, ` + + `you are renting out your Node's computing power.`} +

+

+ {`Each Hacknet Node you purchase will passively earn you money. Each Hacknet Node ` + + `can be upgraded in order to increase its computing power and thereby increase ` + + `the profit you earn from it.`} +

+ + ) : ( + <> +

+ {`Here, you can purchase a Hacknet Server, an upgraded version of the Hacknet Node. ` + + `Hacknet Servers will perform computations and operations on the network, earning ` + + `you hashes. Hashes can be spent on a variety of different upgrades.`} +

+

+ {`Hacknet Servers can also be used as servers to run scripts. However, running scripts ` + + `on a server will reduce its hash rate (hashes generated per second). A Hacknet Server's hash ` + + `rate will be reduced by the percentage of RAM that is being used by that Server to run ` + + `scripts.`} +

+ + )} +
+ ); +} diff --git a/src/Hacknet/ui/HacknetNode.jsx b/src/Hacknet/ui/HacknetNode.jsx deleted file mode 100644 index 157e4a308..000000000 --- a/src/Hacknet/ui/HacknetNode.jsx +++ /dev/null @@ -1,171 +0,0 @@ -/** - * React Component for the Hacknet Node UI. - * This Component displays the panel for a single Hacknet Node - */ -import React from "react"; - -import { HacknetNodeConstants } from "../data/Constants"; -import { - getMaxNumberLevelUpgrades, - getMaxNumberRamUpgrades, - getMaxNumberCoreUpgrades, - purchaseLevelUpgrade, - purchaseRamUpgrade, - purchaseCoreUpgrade, -} from "../HacknetHelpers"; - -import { Player } from "../../Player"; - -import { Money } from "../../ui/React/Money"; -import { MoneyRate } from "../../ui/React/MoneyRate"; - -export class HacknetNode extends React.Component { - render() { - const node = this.props.node; - const purchaseMult = this.props.purchaseMultiplier; - const recalculate = this.props.recalculate; - - // Upgrade Level Button - let upgradeLevelContent, upgradeLevelClass; - if (node.level >= HacknetNodeConstants.MaxLevel) { - upgradeLevelContent = <>MAX LEVEL; - upgradeLevelClass = "std-button-disabled"; - } else { - let multiplier = 0; - if (purchaseMult === "MAX") { - multiplier = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel); - } else { - const levelsToMax = HacknetNodeConstants.MaxLevel - node.level; - multiplier = Math.min(levelsToMax, purchaseMult); - } - - const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult); - upgradeLevelContent = ( - <> - Upgrade x{multiplier} - - - ); - if (Player.money.lt(upgradeLevelCost)) { - upgradeLevelClass = "std-button-disabled"; - } else { - upgradeLevelClass = "std-button"; - } - } - const upgradeLevelOnClick = () => { - let numUpgrades = purchaseMult; - if (purchaseMult === "MAX") { - numUpgrades = getMaxNumberLevelUpgrades(node, HacknetNodeConstants.MaxLevel); - } - purchaseLevelUpgrade(node, numUpgrades); - recalculate(); - return false; - }; - - let upgradeRamContent, upgradeRamClass; - if (node.ram >= HacknetNodeConstants.MaxRam) { - upgradeRamContent = <>MAX RAM; - upgradeRamClass = "std-button-disabled"; - } else { - let multiplier = 0; - if (purchaseMult === "MAX") { - multiplier = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam); - } else { - const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram)); - multiplier = Math.min(levelsToMax, purchaseMult); - } - - const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult); - upgradeRamContent = ( - <> - Upgrade x{multiplier} - - - ); - if (Player.money.lt(upgradeRamCost)) { - upgradeRamClass = "std-button-disabled"; - } else { - upgradeRamClass = "std-button"; - } - } - const upgradeRamOnClick = () => { - let numUpgrades = purchaseMult; - if (purchaseMult === "MAX") { - numUpgrades = getMaxNumberRamUpgrades(node, HacknetNodeConstants.MaxRam); - } - purchaseRamUpgrade(node, numUpgrades); - recalculate(); - return false; - }; - - let upgradeCoresContent, upgradeCoresClass; - if (node.cores >= HacknetNodeConstants.MaxCores) { - upgradeCoresContent = <>MAX CORES; - upgradeCoresClass = "std-button-disabled"; - } else { - let multiplier = 0; - if (purchaseMult === "MAX") { - multiplier = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores); - } else { - const levelsToMax = HacknetNodeConstants.MaxCores - node.cores; - multiplier = Math.min(levelsToMax, purchaseMult); - } - - const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult); - upgradeCoresContent = ( - <> - Upgrade x{multiplier} - - - ); - if (Player.money.lt(upgradeCoreCost)) { - upgradeCoresClass = "std-button-disabled"; - } else { - upgradeCoresClass = "std-button"; - } - } - const upgradeCoresOnClick = () => { - let numUpgrades = purchaseMult; - if (purchaseMult === "MAX") { - numUpgrades = getMaxNumberCoreUpgrades(node, HacknetNodeConstants.MaxCores); - } - purchaseCoreUpgrade(node, numUpgrades); - recalculate(); - return false; - }; - - return ( -
  • -
    -
    -

    {node.name}

    -
    -
    -

    Production:

    - - ({MoneyRate(node.moneyGainRatePerSecond)}) - -
    -
    -

    Level:

    - {node.level} - -
    -
    -

    RAM:

    - {node.ram}GB - -
    -
    -

    Cores:

    - {node.cores} - -
    -
    -
  • - ); - } -} diff --git a/src/Hacknet/ui/HacknetNodeElem.tsx b/src/Hacknet/ui/HacknetNodeElem.tsx new file mode 100644 index 000000000..9809cf79c --- /dev/null +++ b/src/Hacknet/ui/HacknetNodeElem.tsx @@ -0,0 +1,174 @@ +/** + * React Component for the Hacknet Node UI. + * This Component displays the panel for a single Hacknet Node + */ +import React from "react"; + +import { HacknetNodeConstants } from "../data/Constants"; +import { + getMaxNumberLevelUpgrades, + getMaxNumberRamUpgrades, + getMaxNumberCoreUpgrades, + purchaseLevelUpgrade, + purchaseRamUpgrade, + purchaseCoreUpgrade, +} from "../HacknetHelpers"; + +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { HacknetNode } from "../HacknetNode"; + +import { Money } from "../../ui/React/Money"; +import { MoneyRate } from "../../ui/React/MoneyRate"; + +interface IProps { + node: HacknetNode; + purchaseMultiplier: number | string; + rerender: () => void; + player: IPlayer; +} + +export function HacknetNodeElem(props: IProps): React.ReactElement { + const node = props.node; + const purchaseMult = props.purchaseMultiplier; + const rerender = props.rerender; + + // Upgrade Level Button + let upgradeLevelContent, upgradeLevelClass; + if (node.level >= HacknetNodeConstants.MaxLevel) { + upgradeLevelContent = <>MAX LEVEL; + upgradeLevelClass = "std-button-disabled"; + } else { + let multiplier = 0; + if (purchaseMult === "MAX") { + multiplier = getMaxNumberLevelUpgrades(props.player, node, HacknetNodeConstants.MaxLevel); + } else { + const levelsToMax = HacknetNodeConstants.MaxLevel - node.level; + multiplier = Math.min(levelsToMax, purchaseMult as number); + } + + const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult); + upgradeLevelContent = ( + <> + Upgrade x{multiplier} - + + ); + if (props.player.money.lt(upgradeLevelCost)) { + upgradeLevelClass = "std-button-disabled"; + } else { + upgradeLevelClass = "std-button"; + } + } + function upgradeLevelOnClick(): void { + let numUpgrades = purchaseMult; + if (purchaseMult === "MAX") { + numUpgrades = getMaxNumberLevelUpgrades(props.player, node, HacknetNodeConstants.MaxLevel); + } + purchaseLevelUpgrade(props.player, node, numUpgrades as number); + rerender(); + } + + let upgradeRamContent, upgradeRamClass; + if (node.ram >= HacknetNodeConstants.MaxRam) { + upgradeRamContent = <>MAX RAM; + upgradeRamClass = "std-button-disabled"; + } else { + let multiplier = 0; + if (purchaseMult === "MAX") { + multiplier = getMaxNumberRamUpgrades(props.player, node, HacknetNodeConstants.MaxRam); + } else { + const levelsToMax = Math.round(Math.log2(HacknetNodeConstants.MaxRam / node.ram)); + multiplier = Math.min(levelsToMax, purchaseMult as number); + } + + const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult); + upgradeRamContent = ( + <> + Upgrade x{multiplier} - + + ); + if (props.player.money.lt(upgradeRamCost)) { + upgradeRamClass = "std-button-disabled"; + } else { + upgradeRamClass = "std-button"; + } + } + function upgradeRamOnClick(): void { + let numUpgrades = purchaseMult; + if (purchaseMult === "MAX") { + numUpgrades = getMaxNumberRamUpgrades(props.player, node, HacknetNodeConstants.MaxRam); + } + purchaseRamUpgrade(props.player, node, numUpgrades as number); + rerender(); + } + + let upgradeCoresContent, upgradeCoresClass; + if (node.cores >= HacknetNodeConstants.MaxCores) { + upgradeCoresContent = <>MAX CORES; + upgradeCoresClass = "std-button-disabled"; + } else { + let multiplier = 0; + if (purchaseMult === "MAX") { + multiplier = getMaxNumberCoreUpgrades(props.player, node, HacknetNodeConstants.MaxCores); + } else { + const levelsToMax = HacknetNodeConstants.MaxCores - node.cores; + multiplier = Math.min(levelsToMax, purchaseMult as number); + } + + const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult); + upgradeCoresContent = ( + <> + Upgrade x{multiplier} - + + ); + if (props.player.money.lt(upgradeCoreCost)) { + upgradeCoresClass = "std-button-disabled"; + } else { + upgradeCoresClass = "std-button"; + } + } + function upgradeCoresOnClick(): void { + let numUpgrades = purchaseMult; + if (purchaseMult === "MAX") { + numUpgrades = getMaxNumberCoreUpgrades(props.player, node, HacknetNodeConstants.MaxCores); + } + purchaseCoreUpgrade(props.player, node, numUpgrades as number); + rerender(); + } + + return ( +
  • +
    +
    +

    {node.name}

    +
    +
    +

    Production:

    + + ({MoneyRate(node.moneyGainRatePerSecond)}) + +
    +
    +

    Level:

    + {node.level} + +
    +
    +

    RAM:

    + {node.ram}GB + +
    +
    +

    Cores:

    + {node.cores} + +
    +
    +
  • + ); +} diff --git a/src/Hacknet/ui/HacknetRoot.tsx b/src/Hacknet/ui/HacknetRoot.tsx new file mode 100644 index 000000000..bad9cb2bb --- /dev/null +++ b/src/Hacknet/ui/HacknetRoot.tsx @@ -0,0 +1,145 @@ +/** + * Root React Component for the Hacknet Node UI + */ +import React, { useState, useEffect } from "react"; + +import { GeneralInfo } from "./GeneralInfo"; +import { HacknetNodeElem } from "./HacknetNodeElem"; +import { HacknetServerElem } from "./HacknetServerElem"; +import { HacknetNode } from "../HacknetNode"; +import { HashUpgradePopup } from "./HashUpgradePopup"; +import { MultiplierButtons } from "./MultiplierButtons"; +import { PlayerInfo } from "./PlayerInfo"; +import { PurchaseButton } from "./PurchaseButton"; +import { PurchaseMultipliers } from "../data/Constants"; + +import { + getCostOfNextHacknetNode, + getCostOfNextHacknetServer, + hasHacknetServers, + purchaseHacknet, +} from "../HacknetHelpers"; + +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { AllServers } from "../../Server/AllServers"; +import { Server } from "../../Server/Server"; + +import { createPopup } from "../../ui/React/createPopup"; + +interface IProps { + player: IPlayer; +} + +export function HacknetRoot(props: IProps): React.ReactElement { + const setRerender = useState(false)[1]; + function rerender(): void { + setRerender((old) => !old); + } + const [purchaseMultiplier, setPurchaseMultiplier] = useState(PurchaseMultipliers.x1); + + useEffect(() => { + const id = setInterval(rerender, 1000); + return () => clearInterval(id); + }, []); + + function createHashUpgradesPopup(): void { + const id = "hacknet-server-hash-upgrades-popup"; + createPopup(id, HashUpgradePopup, { + player: props.player, + }); + } + + let totalProduction = 0; + for (let i = 0; i < props.player.hacknetNodes.length; ++i) { + const node = props.player.hacknetNodes[i]; + if (hasHacknetServers(props.player)) { + if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen + const hserver = AllServers[node]; + if (hserver instanceof Server) throw new Error("node was a normal server"); // should never happen + if (hserver) { + totalProduction += hserver.hashRate; + } else { + console.warn(`Could not find Hacknet Server object in AllServers map (i=${i})`); + } + } else { + if (typeof node === "string") throw new Error("node was ip string"); // should never happen + totalProduction += node.moneyGainRatePerSecond; + } + } + + function handlePurchaseButtonClick(): void { + purchaseHacknet(props.player); + rerender(); + } + + // Cost to purchase a new Hacknet Node + let purchaseCost; + if (hasHacknetServers(props.player)) { + purchaseCost = getCostOfNextHacknetServer(props.player); + } else { + purchaseCost = getCostOfNextHacknetNode(props.player); + } + + // onClick event handlers for purchase multiplier buttons + const purchaseMultiplierOnClicks = [ + () => setPurchaseMultiplier(PurchaseMultipliers.x1), + () => setPurchaseMultiplier(PurchaseMultipliers.x5), + () => setPurchaseMultiplier(PurchaseMultipliers.x10), + () => setPurchaseMultiplier(PurchaseMultipliers.MAX), + ]; + + // HacknetNode components + const nodes = props.player.hacknetNodes.map((node: string | HacknetNode) => { + if (hasHacknetServers(props.player)) { + if (node instanceof HacknetNode) throw new Error("node was hacknet node"); // should never happen + const hserver = AllServers[node]; + if (hserver == null) { + throw new Error(`Could not find Hacknet Server object in AllServers map for IP: ${node}`); + } + if (hserver instanceof Server) throw new Error("node was normal server"); // should never happen + return ( + + ); + } else { + if (typeof node === "string") throw new Error("node was ip string"); // should never happen + return ( + + ); + } + }); + + return ( +
    +

    Hacknet {hasHacknetServers(props.player) ? "Servers" : "Nodes"}

    + + + + +
    +
    + + +
    + + {hasHacknetServers(props.player) && ( + + )} + +
      {nodes}
    +
    + ); +} diff --git a/src/Hacknet/ui/HacknetServer.jsx b/src/Hacknet/ui/HacknetServer.jsx deleted file mode 100644 index 6c586475d..000000000 --- a/src/Hacknet/ui/HacknetServer.jsx +++ /dev/null @@ -1,225 +0,0 @@ -/** - * React Component for the Hacknet Node UI. - * This Component displays the panel for a single Hacknet Node - */ -import React from "react"; - -import { HacknetServerConstants } from "../data/Constants"; -import { - getMaxNumberLevelUpgrades, - getMaxNumberRamUpgrades, - getMaxNumberCoreUpgrades, - getMaxNumberCacheUpgrades, - purchaseLevelUpgrade, - purchaseRamUpgrade, - purchaseCoreUpgrade, - purchaseCacheUpgrade, - updateHashManagerCapacity, -} from "../HacknetHelpers"; - -import { Player } from "../../Player"; - -import { Money } from "../../ui/React/Money"; -import { Hashes } from "../../ui/React/Hashes"; -import { HashRate } from "../../ui/React/HashRate"; - -export class HacknetServer extends React.Component { - render() { - const node = this.props.node; - const purchaseMult = this.props.purchaseMultiplier; - const recalculate = this.props.recalculate; - - // Upgrade Level Button - let upgradeLevelContent, upgradeLevelClass; - if (node.level >= HacknetServerConstants.MaxLevel) { - upgradeLevelContent = <>MAX LEVEL; - upgradeLevelClass = "std-button-disabled"; - } else { - let multiplier = 0; - if (purchaseMult === "MAX") { - multiplier = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel); - } else { - const levelsToMax = HacknetServerConstants.MaxLevel - node.level; - multiplier = Math.min(levelsToMax, purchaseMult); - } - - const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, Player.hacknet_node_level_cost_mult); - upgradeLevelContent = ( - <> - Upgrade x{multiplier} - - - ); - if (Player.money.lt(upgradeLevelCost)) { - upgradeLevelClass = "std-button-disabled"; - } else { - upgradeLevelClass = "std-button"; - } - } - const upgradeLevelOnClick = () => { - let numUpgrades = purchaseMult; - if (purchaseMult === "MAX") { - numUpgrades = getMaxNumberLevelUpgrades(node, HacknetServerConstants.MaxLevel); - } - purchaseLevelUpgrade(node, numUpgrades); - recalculate(); - return false; - }; - - // Upgrade RAM Button - let upgradeRamContent, upgradeRamClass; - if (node.maxRam >= HacknetServerConstants.MaxRam) { - upgradeRamContent = <>MAX RAM; - upgradeRamClass = "std-button-disabled"; - } else { - let multiplier = 0; - if (purchaseMult === "MAX") { - multiplier = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam); - } else { - const levelsToMax = Math.round(Math.log2(HacknetServerConstants.MaxRam / node.maxRam)); - multiplier = Math.min(levelsToMax, purchaseMult); - } - - const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, Player.hacknet_node_ram_cost_mult); - upgradeRamContent = ( - <> - Upgrade x{multiplier} - - - ); - if (Player.money.lt(upgradeRamCost)) { - upgradeRamClass = "std-button-disabled"; - } else { - upgradeRamClass = "std-button"; - } - } - const upgradeRamOnClick = () => { - let numUpgrades = purchaseMult; - if (purchaseMult === "MAX") { - numUpgrades = getMaxNumberRamUpgrades(node, HacknetServerConstants.MaxRam); - } - purchaseRamUpgrade(node, numUpgrades); - recalculate(); - return false; - }; - - // Upgrade Cores Button - let upgradeCoresContent, upgradeCoresClass; - if (node.cores >= HacknetServerConstants.MaxCores) { - upgradeCoresContent = <>MAX CORES; - upgradeCoresClass = "std-button-disabled"; - } else { - let multiplier = 0; - if (purchaseMult === "MAX") { - multiplier = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores); - } else { - const levelsToMax = HacknetServerConstants.MaxCores - node.cores; - multiplier = Math.min(levelsToMax, purchaseMult); - } - - const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, Player.hacknet_node_core_cost_mult); - upgradeCoresContent = ( - <> - Upgrade x{multiplier} - - - ); - if (Player.money.lt(upgradeCoreCost)) { - upgradeCoresClass = "std-button-disabled"; - } else { - upgradeCoresClass = "std-button"; - } - } - const upgradeCoresOnClick = () => { - let numUpgrades = purchaseMult; - if (purchaseMult === "MAX") { - numUpgrades = getMaxNumberCoreUpgrades(node, HacknetServerConstants.MaxCores); - } - purchaseCoreUpgrade(node, numUpgrades); - recalculate(); - return false; - }; - - // Upgrade Cache button - let upgradeCacheContent, upgradeCacheClass; - if (node.cache >= HacknetServerConstants.MaxCache) { - upgradeCacheContent = <>MAX CACHE; - upgradeCacheClass = "std-button-disabled"; - } else { - let multiplier = 0; - if (purchaseMult === "MAX") { - multiplier = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache); - } else { - const levelsToMax = HacknetServerConstants.MaxCache - node.cache; - multiplier = Math.min(levelsToMax, purchaseMult); - } - - const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier); - upgradeCacheContent = ( - <> - Upgrade x{multiplier} - - - ); - if (Player.money.lt(upgradeCacheCost)) { - upgradeCacheClass = "std-button-disabled"; - } else { - upgradeCacheClass = "std-button"; - } - } - const upgradeCacheOnClick = () => { - let numUpgrades = purchaseMult; - if (purchaseMult === "MAX") { - numUpgrades = getMaxNumberCacheUpgrades(node, HacknetServerConstants.MaxCache); - } - purchaseCacheUpgrade(node, numUpgrades); - recalculate(); - updateHashManagerCapacity(); - return false; - }; - - return ( -
  • -
    -
    -

    {node.hostname}

    -
    -
    -

    Production:

    - - {Hashes(node.totalHashesGenerated)} ({HashRate(node.hashRate)}) - -
    -
    -

    Hash Capacity:

    - {Hashes(node.hashCapacity)} -
    -
    -

    Level:

    - {node.level} - -
    -
    -

    RAM:

    - {node.maxRam}GB - -
    -
    -

    Cores:

    - {node.cores} - -
    -
    -

    Cache Level:

    - {node.cache} - -
    -
    -
  • - ); - } -} diff --git a/src/Hacknet/ui/HacknetServerElem.tsx b/src/Hacknet/ui/HacknetServerElem.tsx new file mode 100644 index 000000000..e642f36f5 --- /dev/null +++ b/src/Hacknet/ui/HacknetServerElem.tsx @@ -0,0 +1,227 @@ +/** + * React Component for the Hacknet Node UI. + * This Component displays the panel for a single Hacknet Node + */ +import React from "react"; + +import { HacknetServerConstants } from "../data/Constants"; +import { + getMaxNumberLevelUpgrades, + getMaxNumberRamUpgrades, + getMaxNumberCoreUpgrades, + getMaxNumberCacheUpgrades, + purchaseLevelUpgrade, + purchaseRamUpgrade, + purchaseCoreUpgrade, + purchaseCacheUpgrade, + updateHashManagerCapacity, +} from "../HacknetHelpers"; + +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { HacknetServer } from "../HacknetServer"; + +import { Money } from "../../ui/React/Money"; +import { Hashes } from "../../ui/React/Hashes"; +import { HashRate } from "../../ui/React/HashRate"; + +interface IProps { + node: HacknetServer; + purchaseMultiplier: number | string; + rerender: () => void; + player: IPlayer; +} + +export function HacknetServerElem(props: IProps): React.ReactElement { + const node = props.node; + const purchaseMult = props.purchaseMultiplier; + const rerender = props.rerender; + + // Upgrade Level Button + let upgradeLevelContent, upgradeLevelClass; + if (node.level >= HacknetServerConstants.MaxLevel) { + upgradeLevelContent = <>MAX LEVEL; + upgradeLevelClass = "std-button-disabled"; + } else { + let multiplier = 0; + if (purchaseMult === "MAX") { + multiplier = getMaxNumberLevelUpgrades(props.player, node, HacknetServerConstants.MaxLevel); + } else { + const levelsToMax = HacknetServerConstants.MaxLevel - node.level; + multiplier = Math.min(levelsToMax, purchaseMult as number); + } + + const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult); + upgradeLevelContent = ( + <> + Upgrade x{multiplier} - + + ); + if (props.player.money.lt(upgradeLevelCost)) { + upgradeLevelClass = "std-button-disabled"; + } else { + upgradeLevelClass = "std-button"; + } + } + function upgradeLevelOnClick(): void { + let numUpgrades = purchaseMult; + if (purchaseMult === "MAX") { + numUpgrades = getMaxNumberLevelUpgrades(props.player, node, HacknetServerConstants.MaxLevel); + } + purchaseLevelUpgrade(props.player, node, numUpgrades as number); + rerender(); + } + + // Upgrade RAM Button + let upgradeRamContent, upgradeRamClass; + if (node.maxRam >= HacknetServerConstants.MaxRam) { + upgradeRamContent = <>MAX RAM; + upgradeRamClass = "std-button-disabled"; + } else { + let multiplier = 0; + if (purchaseMult === "MAX") { + multiplier = getMaxNumberRamUpgrades(props.player, node, HacknetServerConstants.MaxRam); + } else { + const levelsToMax = Math.round(Math.log2(HacknetServerConstants.MaxRam / node.maxRam)); + multiplier = Math.min(levelsToMax, purchaseMult as number); + } + + const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult); + upgradeRamContent = ( + <> + Upgrade x{multiplier} - + + ); + if (props.player.money.lt(upgradeRamCost)) { + upgradeRamClass = "std-button-disabled"; + } else { + upgradeRamClass = "std-button"; + } + } + function upgradeRamOnClick(): void { + let numUpgrades = purchaseMult; + if (purchaseMult === "MAX") { + numUpgrades = getMaxNumberRamUpgrades(props.player, node, HacknetServerConstants.MaxRam); + } + purchaseRamUpgrade(props.player, node, numUpgrades as number); + rerender(); + } + + // Upgrade Cores Button + let upgradeCoresContent, upgradeCoresClass; + if (node.cores >= HacknetServerConstants.MaxCores) { + upgradeCoresContent = <>MAX CORES; + upgradeCoresClass = "std-button-disabled"; + } else { + let multiplier = 0; + if (purchaseMult === "MAX") { + multiplier = getMaxNumberCoreUpgrades(props.player, node, HacknetServerConstants.MaxCores); + } else { + const levelsToMax = HacknetServerConstants.MaxCores - node.cores; + multiplier = Math.min(levelsToMax, purchaseMult as number); + } + + const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult); + upgradeCoresContent = ( + <> + Upgrade x{multiplier} - + + ); + if (props.player.money.lt(upgradeCoreCost)) { + upgradeCoresClass = "std-button-disabled"; + } else { + upgradeCoresClass = "std-button"; + } + } + function upgradeCoresOnClick(): void { + let numUpgrades = purchaseMult; + if (purchaseMult === "MAX") { + numUpgrades = getMaxNumberCoreUpgrades(props.player, node, HacknetServerConstants.MaxCores); + } + purchaseCoreUpgrade(props.player, node, numUpgrades as number); + rerender(); + } + + // Upgrade Cache button + let upgradeCacheContent, upgradeCacheClass; + if (node.cache >= HacknetServerConstants.MaxCache) { + upgradeCacheContent = <>MAX CACHE; + upgradeCacheClass = "std-button-disabled"; + } else { + let multiplier = 0; + if (purchaseMult === "MAX") { + multiplier = getMaxNumberCacheUpgrades(props.player, node, HacknetServerConstants.MaxCache); + } else { + const levelsToMax = HacknetServerConstants.MaxCache - node.cache; + multiplier = Math.min(levelsToMax, purchaseMult as number); + } + + const upgradeCacheCost = node.calculateCacheUpgradeCost(multiplier); + upgradeCacheContent = ( + <> + Upgrade x{multiplier} - + + ); + if (props.player.money.lt(upgradeCacheCost)) { + upgradeCacheClass = "std-button-disabled"; + } else { + upgradeCacheClass = "std-button"; + } + } + function upgradeCacheOnClick(): void { + let numUpgrades = purchaseMult; + if (purchaseMult === "MAX") { + numUpgrades = getMaxNumberCacheUpgrades(props.player, node, HacknetServerConstants.MaxCache); + } + purchaseCacheUpgrade(props.player, node, numUpgrades as number); + rerender(); + updateHashManagerCapacity(props.player); + } + + return ( +
  • +
    +
    +

    {node.hostname}

    +
    +
    +

    Production:

    + + {Hashes(node.totalHashesGenerated)} ({HashRate(node.hashRate)}) + +
    +
    +

    Hash Capacity:

    + {Hashes(node.hashCapacity)} +
    +
    +

    Level:

    + {node.level} + +
    +
    +

    RAM:

    + {node.maxRam}GB + +
    +
    +

    Cores:

    + {node.cores} + +
    +
    +

    Cache Level:

    + {node.cache} + +
    +
    +
  • + ); +} diff --git a/src/Hacknet/ui/HacknetUpgradeElem.tsx b/src/Hacknet/ui/HacknetUpgradeElem.tsx new file mode 100644 index 000000000..f43a64b1b --- /dev/null +++ b/src/Hacknet/ui/HacknetUpgradeElem.tsx @@ -0,0 +1,70 @@ +import React, { useState } from "react"; + +import { purchaseHashUpgrade } from "../HacknetHelpers"; +import { HashManager } from "../HashManager"; +import { HashUpgrade } from "../HashUpgrade"; + +import { IPlayer } from "../../PersonObjects/IPlayer"; + +import { ServerDropdown, ServerType } from "../../ui/React/ServerDropdown"; + +import { dialogBoxCreate } from "../../../utils/DialogBox"; +import { CopyableText } from "../../ui/React/CopyableText"; +import { Hashes } from "../../ui/React/Hashes"; + +interface IProps { + player: IPlayer; + hashManager: HashManager; + upg: HashUpgrade; + rerender: () => void; +} + +export function HacknetUpgradeElem(props: IProps): React.ReactElement { + const [selectedServer, setSelectedServer] = useState("ecorp"); + function changeTargetServer(event: React.ChangeEvent): void { + setSelectedServer(event.target.value); + } + + function purchase(): void { + const canPurchase = props.hashManager.hashes >= props.hashManager.getUpgradeCost(props.upg.name); + if (canPurchase) { + const res = purchaseHashUpgrade(props.player, props.upg.name, selectedServer); + if (!res) { + dialogBoxCreate( + "Failed to purchase upgrade. This may be because you do not have enough hashes, " + + "or because you do not have access to the feature upgrade affects.", + ); + } + props.rerender(); + } + } + + const hashManager = props.hashManager; + const upg = props.upg; + const cost = hashManager.getUpgradeCost(upg.name); + const level = hashManager.upgrades[upg.name]; + const effect = upg.effectText(level); + + // Purchase button + const canPurchase = hashManager.hashes >= cost; + const btnClass = canPurchase ? "std-button" : "std-button-disabled"; + + // We'll reuse a Bladeburner css class + return ( +
    + +

    + Cost: {Hashes(cost)}, Bought: {level} times +

    + +

    {upg.desc}

    + + {level > 0 && effect &&

    {effect}

    } + {upg.hasTargetServer && ( + + )} +
    + ); +} diff --git a/src/Hacknet/ui/HashUpgradePopup.jsx b/src/Hacknet/ui/HashUpgradePopup.jsx deleted file mode 100644 index e701a1909..000000000 --- a/src/Hacknet/ui/HashUpgradePopup.jsx +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Create the pop-up for purchasing upgrades with hashes - */ -import React from "react"; - -import { purchaseHashUpgrade } from "../HacknetHelpers"; -import { HashManager } from "../HashManager"; -import { HashUpgrades } from "../HashUpgrades"; - -import { Player } from "../../Player"; - -import { numeralWrapper } from "../../ui/numeralFormat"; - -import { ServerDropdown, ServerType } from "../../ui/React/ServerDropdown"; - -import { dialogBoxCreate } from "../../../utils/DialogBox"; -import { CopyableText } from "../../ui/React/CopyableText"; -import { Hashes } from "../../ui/React/Hashes"; - -class HashUpgrade extends React.Component { - constructor(props) { - super(props); - - this.state = { - selectedServer: "ecorp", - }; - - this.changeTargetServer = this.changeTargetServer.bind(this); - this.purchase = this.purchase.bind(this, this.props.hashManager, this.props.upg); - } - - changeTargetServer(e) { - this.setState({ - selectedServer: e.target.value, - }); - } - - purchase(hashManager, upg) { - const canPurchase = hashManager.hashes >= hashManager.getUpgradeCost(upg.name); - if (canPurchase) { - const res = purchaseHashUpgrade(upg.name, this.state.selectedServer); - if (res) { - this.props.rerender(); - } else { - dialogBoxCreate( - "Failed to purchase upgrade. This may be because you do not have enough hashes, " + - "or because you do not have access to the feature this upgrade affects.", - ); - } - } - } - - render() { - const hashManager = this.props.hashManager; - const upg = this.props.upg; - const cost = hashManager.getUpgradeCost(upg.name); - const level = hashManager.upgrades[upg.name]; - const effect = upg.effectText(level); - - // Purchase button - const canPurchase = hashManager.hashes >= cost; - const btnClass = canPurchase ? "std-button" : "std-button-disabled"; - - // We'll reuse a Bladeburner css class - return ( -
    - -

    - Cost: {Hashes(cost)}, Bought: {level} times -

    - -

    {upg.desc}

    - - {level > 0 && effect &&

    {effect}

    } - {upg.hasTargetServer && ( - - )} -
    - ); - } -} - -export class HashUpgradePopup extends React.Component { - constructor(props) { - super(props); - - this.state = { - totalHashes: Player.hashManager.hashes, - }; - } - - componentDidMount() { - this.interval = setInterval(() => this.tick(), 1e3); - } - - componentWillUnmount() { - clearInterval(this.interval); - } - - tick() { - this.setState({ - totalHashes: Player.hashManager.hashes, - }); - } - - render() { - const rerender = this.props.rerender; - - const hashManager = Player.hashManager; - if (!(hashManager instanceof HashManager)) { - throw new Error(`Player does not have a HashManager)`); - } - - const upgradeElems = Object.keys(HashUpgrades).map((upgName) => { - const upg = HashUpgrades[upgName]; - return ; - }); - - return ( -
    -

    Spend your hashes on a variety of different upgrades

    -

    Hashes: {numeralWrapper.formatHashes(this.state.totalHashes)}

    - {upgradeElems} -
    - ); - } -} diff --git a/src/Hacknet/ui/HashUpgradePopup.tsx b/src/Hacknet/ui/HashUpgradePopup.tsx new file mode 100644 index 000000000..a24194ce9 --- /dev/null +++ b/src/Hacknet/ui/HashUpgradePopup.tsx @@ -0,0 +1,54 @@ +/** + * Create the pop-up for purchasing upgrades with hashes + */ +import React, { useState, useEffect } from "react"; + +import { HashManager } from "../HashManager"; +import { HashUpgrades } from "../HashUpgrades"; + +import { IPlayer } from "../../PersonObjects/IPlayer"; + +import { Hashes } from "../../ui/React/Hashes"; +import { HacknetUpgradeElem } from "./HacknetUpgradeElem"; + +interface IProps { + player: IPlayer; +} + +export function HashUpgradePopup(props: IProps): React.ReactElement { + const setRerender = useState(false)[1]; + function rerender(): void { + setRerender((old) => !old); + } + + useEffect(() => { + const id = setInterval(() => setRerender((old) => !old), 1000); + return () => clearInterval(id); + }, []); + + const hashManager = props.player.hashManager; + if (!(hashManager instanceof HashManager)) { + throw new Error(`Player does not have a HashManager)`); + } + + const upgradeElems = Object.keys(HashUpgrades).map((upgName) => { + const upg = HashUpgrades[upgName]; + return ( + + ); + }); + + return ( +
    +

    Spend your hashes on a variety of different upgrades

    +

    Hashes: {Hashes(props.player.hashManager.hashes)}

    + {upgradeElems} +
    + ); +} diff --git a/src/Hacknet/ui/MultiplierButtons.jsx b/src/Hacknet/ui/MultiplierButtons.tsx similarity index 71% rename from src/Hacknet/ui/MultiplierButtons.jsx rename to src/Hacknet/ui/MultiplierButtons.tsx index 78030ee37..138249a3c 100644 --- a/src/Hacknet/ui/MultiplierButtons.jsx +++ b/src/Hacknet/ui/MultiplierButtons.tsx @@ -5,9 +5,16 @@ */ import React from "react"; -import { PurchaseMultipliers } from "./Root"; +import { PurchaseMultipliers } from "../data/Constants"; -function MultiplierButton(props) { +interface IMultiplierProps { + className: string; + key: string; + onClick: () => void; + text: string; +} + +function MultiplierButton(props: IMultiplierProps): React.ReactElement { return ( - )} - -
      {nodes}
    - - ); - } -} diff --git a/src/Locations/ui/CoresButton.tsx b/src/Locations/ui/CoresButton.tsx index 5653eef57..198c2258b 100644 --- a/src/Locations/ui/CoresButton.tsx +++ b/src/Locations/ui/CoresButton.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { IPlayer } from "../../PersonObjects/IPlayer"; diff --git a/src/Locations/ui/RamButton.tsx b/src/Locations/ui/RamButton.tsx index d8543f542..64c833d65 100644 --- a/src/Locations/ui/RamButton.tsx +++ b/src/Locations/ui/RamButton.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { CONSTANTS } from "../../Constants"; import { IPlayer } from "../../PersonObjects/IPlayer"; diff --git a/src/Locations/ui/TechVendorLocation.tsx b/src/Locations/ui/TechVendorLocation.tsx index e578195a4..e077fc9fa 100644 --- a/src/Locations/ui/TechVendorLocation.tsx +++ b/src/Locations/ui/TechVendorLocation.tsx @@ -24,7 +24,7 @@ type IProps = { export function TechVendorLocation(props: IProps): React.ReactElement { const setRerender = useState(false)[1]; - function rerender() { + function rerender(): void { setRerender((old) => !old); } const btnStyle = { display: "block" }; diff --git a/src/Locations/ui/TorButton.tsx b/src/Locations/ui/TorButton.tsx index 729b59e5c..746cdbe74 100644 --- a/src/Locations/ui/TorButton.tsx +++ b/src/Locations/ui/TorButton.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { purchaseTorRouter } from "../LocationsHelpers"; diff --git a/src/NetscriptFunctions.js b/src/NetscriptFunctions.js index 7189c82dc..9b256cdd7 100644 --- a/src/NetscriptFunctions.js +++ b/src/NetscriptFunctions.js @@ -351,7 +351,7 @@ function NetscriptFunctions(workerScript) { throw makeRuntimeErrorMsg(callingFn, "Index specified for Hacknet Node is out-of-bounds: " + i); } - if (hasHacknetServers()) { + if (hasHacknetServers(Player)) { const hserver = AllServers[Player.hacknetNodes[i]]; if (hserver == null) { throw makeRuntimeErrorMsg( @@ -719,24 +719,24 @@ function NetscriptFunctions(workerScript) { return Player.hacknetNodes.length; }, maxNumNodes: function () { - if (hasHacknetServers()) { + if (hasHacknetServers(Player)) { return HacknetServerConstants.MaxServers; } return Infinity; }, purchaseNode: function () { - return purchaseHacknet(); + return purchaseHacknet(Player); }, getPurchaseNodeCost: function () { - if (hasHacknetServers()) { - return getCostOfNextHacknetServer(); + if (hasHacknetServers(Player)) { + return getCostOfNextHacknetServer(Player); } else { - return getCostOfNextHacknetNode(); + return getCostOfNextHacknetNode(Player); } }, getNodeStats: function (i) { const node = getHacknetNode(i, "getNodeStats"); - const hasUpgraded = hasHacknetServers(); + const hasUpgraded = hasHacknetServers(Player); const res = { name: hasUpgraded ? node.hostname : node.name, level: node.level, @@ -756,24 +756,24 @@ function NetscriptFunctions(workerScript) { }, upgradeLevel: function (i, n) { const node = getHacknetNode(i, "upgradeLevel"); - return purchaseLevelUpgrade(node, n); + return purchaseLevelUpgrade(Player, node, n); }, upgradeRam: function (i, n) { const node = getHacknetNode(i, "upgradeRam"); - return purchaseRamUpgrade(node, n); + return purchaseRamUpgrade(Player, node, n); }, upgradeCore: function (i, n) { const node = getHacknetNode(i, "upgradeCore"); - return purchaseCoreUpgrade(node, n); + return purchaseCoreUpgrade(Player, node, n); }, upgradeCache: function (i, n) { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return false; } const node = getHacknetNode(i, "upgradeCache"); - const res = purchaseCacheUpgrade(node, n); + const res = purchaseCacheUpgrade(Player, node, n); if (res) { - updateHashManagerCapacity(); + updateHashManagerCapacity(Player); } return res; }, @@ -790,36 +790,36 @@ function NetscriptFunctions(workerScript) { return node.calculateCoreUpgradeCost(n, Player.hacknet_node_core_cost_mult); }, getCacheUpgradeCost: function (i, n) { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return Infinity; } const node = getHacknetNode(i, "upgradeCache"); return node.calculateCacheUpgradeCost(n); }, numHashes: function () { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return 0; } return Player.hashManager.hashes; }, hashCapacity: function () { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return 0; } return Player.hashManager.capacity; }, hashCost: function (upgName) { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return Infinity; } return Player.hashManager.getUpgradeCost(upgName); }, spendHashes: function (upgName, upgTarget) { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return false; } - return purchaseHashUpgrade(upgName, upgTarget); + return purchaseHashUpgrade(Player, upgName, upgTarget); }, getHashUpgradeLevel: function (upgName) { const level = Player.hashManager.upgrades[upgName]; @@ -829,13 +829,13 @@ function NetscriptFunctions(workerScript) { return level; }, getStudyMult: function () { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return false; } return Player.hashManager.getStudyMult(); }, getTrainingMult: function () { - if (!hasHacknetServers()) { + if (!hasHacknetServers(Player)) { return false; } return Player.hashManager.getTrainingMult(); diff --git a/src/PersonObjects/IPlayer.ts b/src/PersonObjects/IPlayer.ts index 46cd831fa..ef772bef7 100644 --- a/src/PersonObjects/IPlayer.ts +++ b/src/PersonObjects/IPlayer.ts @@ -191,4 +191,5 @@ export interface IPlayer { getIntelligenceBonus(weight: number): number; getCasinoWinnings(): number; quitJob(company: string): void; + createHacknetServer(): void; } diff --git a/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx b/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx index c48cc0e49..c8048235a 100644 --- a/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx +++ b/src/PersonObjects/Player/PlayerObjectGeneralMethods.jsx @@ -2518,7 +2518,6 @@ export function checkForFactionInvitations() { //BitRunners var bitrunnersFac = Factions["BitRunners"]; - var homeComp = this.getHomeComputer(); var bitrunnersServer = AllServers[SpecialServerIps[SpecialServerNames.BitRunnersServer]]; if (bitrunnersServer == null) { console.error("Could not find BitRunners Server"); @@ -2743,7 +2742,7 @@ export function checkForFactionInvitations() { var totalHacknetCores = 0; var totalHacknetLevels = 0; for (let i = 0; i < this.hacknetNodes.length; ++i) { - if (hasHacknetServers()) { + if (hasHacknetServers(this)) { const hserver = AllServers[this.hacknetNodes[i]]; if (hserver) { totalHacknetLevels += hserver.level; diff --git a/src/Prestige.js b/src/Prestige.js index e14bff1ba..31813d446 100755 --- a/src/Prestige.js +++ b/src/Prestige.js @@ -339,7 +339,7 @@ function prestigeSourceFile(flume) { hserver.cache = 5; hserver.updateHashRate(Player.hacknet_node_money_mult); hserver.updateHashCapacity(); - updateHashManagerCapacity(); + updateHashManagerCapacity(Player); } // Refresh Main Menu (the 'World' menu, specifically) diff --git a/src/data/codingcontracttypes.ts b/src/data/codingcontracttypes.ts index b7e2eaedd..bff23443d 100644 --- a/src/data/codingcontracttypes.ts +++ b/src/data/codingcontracttypes.ts @@ -157,8 +157,7 @@ export const codingContractTypesMetadata: ICodingContractTypeMetadata[] = [ d += "    [\n"; d += n .map( - (line: number[]) => - "        [" + + (line: number[]) => "        [" + line.map((x: number) => `${x}`.padStart(2, " ")).join(",") + "]", ) diff --git a/src/engine.jsx b/src/engine.jsx index 0f1314b2e..1a5b0a235 100644 --- a/src/engine.jsx +++ b/src/engine.jsx @@ -28,12 +28,8 @@ import { getFactionFieldWorkRepGain, } from "./PersonObjects/formulas/reputation"; import { FconfSettings } from "./Fconf/FconfSettings"; -import { - hasHacknetServers, - renderHacknetNodesUI, - clearHacknetNodesUI, - processHacknetEarnings, -} from "./Hacknet/HacknetHelpers"; +import { hasHacknetServers, processHacknetEarnings } from "./Hacknet/HacknetHelpers"; +import { HacknetRoot } from "./Hacknet/ui/HacknetRoot"; import { iTutorialStart } from "./InteractiveTutorial"; import { LocationName } from "./Locations/data/LocationNames"; import { LocationRoot } from "./Locations/ui/Root"; @@ -251,7 +247,7 @@ const Engine = { Engine.hideAllContent(); Engine.Display.hacknetNodesContent.style.display = "block"; routing.navigateTo(Page.HacknetNodes); - renderHacknetNodesUI(); + ReactDOM.render(, Engine.Display.hacknetNodesContent); MainMenuLinks.HacknetNodes.classList.add("active"); }, @@ -462,7 +458,9 @@ const Engine = { Engine.Display.infiltrationContent.style.display = "none"; ReactDOM.unmountComponentAtNode(Engine.Display.infiltrationContent); - clearHacknetNodesUI(); + Engine.Display.hacknetNodesContent.style.display = "none"; + ReactDOM.unmountComponentAtNode(Engine.Display.hacknetNodesContent); + Engine.Display.createProgramContent.style.display = "none"; Engine.Display.factionsContent.style.display = "none"; @@ -670,7 +668,7 @@ const Engine = { updateOnlineScriptTimes(numCycles); // Hacknet Nodes - processHacknetEarnings(numCycles); + processHacknetEarnings(Player, numCycles); }, /** @@ -737,9 +735,7 @@ const Engine = { if (Engine.Counters.updateDisplays <= 0) { Engine.displayCharacterOverviewInfo(); - if (routing.isOn(Page.HacknetNodes)) { - renderHacknetNodesUI(); - } else if (routing.isOn(Page.CreateProgram)) { + if (routing.isOn(Page.CreateProgram)) { displayCreateProgramContent(); } else if (routing.isOn(Page.Sleeves)) { updateSleevesPage(); @@ -1020,8 +1016,8 @@ const Engine = { } // Hacknet Nodes offline progress - var offlineProductionFromHacknetNodes = processHacknetEarnings(numCyclesOffline); - const hacknetProdInfo = hasHacknetServers() ? ( + var offlineProductionFromHacknetNodes = processHacknetEarnings(Player, numCyclesOffline); + const hacknetProdInfo = hasHacknetServers(Player) ? ( <>{Hashes(offlineProductionFromHacknetNodes)} hashes ) : ( diff --git a/src/ui/React/ServerDropdown.jsx b/src/ui/React/ServerDropdown.tsx similarity index 50% rename from src/ui/React/ServerDropdown.jsx rename to src/ui/React/ServerDropdown.tsx index 326b3c130..d9523d5ac 100644 --- a/src/ui/React/ServerDropdown.jsx +++ b/src/ui/React/ServerDropdown.tsx @@ -5,6 +5,7 @@ */ import React from "react"; import { AllServers } from "../../Server/AllServers"; +import { Server } from "../../Server/Server"; import { HacknetServer } from "../../Hacknet/HacknetServer"; @@ -16,52 +17,50 @@ export const ServerType = { Purchased: 3, // Everything from Owned except home computer }; -export class ServerDropdown extends React.Component { +interface IProps { + serverType: number; + onChange: (event: React.ChangeEvent) => void; + style: any; +} + +export function ServerDropdown(props: IProps): React.ReactElement { /** * Checks if the server should be shown in the dropdown menu, based on the * 'serverType' property */ - isValidServer(s) { - const type = this.props.serverType; + function isValidServer(s: Server | HacknetServer): boolean { + const purchased = s instanceof Server && s.purchasedByPlayer; + const type = props.serverType; switch (type) { case ServerType.All: return true; case ServerType.Foreign: - return s.hostname !== "home" && !s.purchasedByPlayer; + return s.hostname !== "home" && !purchased; case ServerType.Owned: - return s.purchasedByPlayer || s instanceof HacknetServer || s.hostname === "home"; + return purchased || s instanceof HacknetServer || s.hostname === "home"; case ServerType.Purchased: - return s.purchasedByPlayer || s instanceof HacknetServer; + return purchased || s instanceof HacknetServer; default: console.warn(`Invalid ServerType specified for ServerDropdown component: ${type}`); return false; } } - /** - * Given a Server object, creates a Option element - */ - renderOption(s) { - return ( - - ); - } - - render() { - const servers = []; - for (const serverName in AllServers) { - const server = AllServers[serverName]; - if (this.isValidServer(server)) { - servers.push(this.renderOption(server)); - } + const servers = []; + for (const serverName in AllServers) { + const server = AllServers[serverName]; + if (isValidServer(server)) { + servers.push( + , + ); } - - return ( - - ); } + + return ( + + ); }