Merge pull request #3901 from danielyxie/work

VERSION: v2.0.0
This commit is contained in:
hydroflame 2022-07-21 02:16:53 -04:00 committed by GitHub
commit dd7b5c4316
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
217 changed files with 6590 additions and 8110 deletions

@ -7,7 +7,7 @@ module.exports = {
extends: [ extends: [
"eslint:recommended", "eslint:recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
// "plugin:@typescript-eslint/recommended-requiring-type-checking", "plugin:@typescript-eslint/recommended-requiring-type-checking",
], ],
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
@ -19,386 +19,9 @@ module.exports = {
project: ["./tsconfig.json", "./test/tsconfig.json", "./tools/tsconfig.json", "./test/cypress/tsconfig.json"], project: ["./tsconfig.json", "./test/tsconfig.json", "./tools/tsconfig.json", "./test/cypress/tsconfig.json"],
}, },
plugins: ["@typescript-eslint"], plugins: ["@typescript-eslint"],
rules: {
"accessor-pairs": [
"error",
{
setWithoutGet: true,
getWithoutSet: false,
},
],
"array-bracket-newline": ["off"],
"array-bracket-spacing": ["off"],
"array-callback-return": ["off"],
"array-element-newline": ["off"],
"arrow-body-style": ["off"],
"arrow-parens": ["off"],
"arrow-spacing": ["off"],
"block-scoped-var": ["off"],
"block-spacing": ["off"],
"brace-style": ["off"],
"callback-return": ["error"],
camelcase: ["off"],
"capitalized-comments": ["off"],
"class-methods-use-this": ["off"],
complexity: ["off"],
"consistent-return": ["off"],
"consistent-this": ["off"],
"constructor-super": ["error"],
curly: ["off"],
"default-case": ["off"],
"dot-notation": ["off"],
"eol-last": ["error"],
eqeqeq: ["off"],
"for-direction": ["error"],
"func-call-spacing": ["off"],
"func-name-matching": ["error"],
"func-names": ["off", "never"],
"func-style": ["off"],
"function-paren-newline": ["off"],
"getter-return": [
"error",
{
allowImplicit: false,
},
],
"global-require": ["off"],
"guard-for-in": ["off"],
"handle-callback-err": ["error"],
"id-blacklist": ["error"],
"id-length": ["off"],
"id-match": ["error"],
indent: ["off"],
"indent-legacy": ["off"],
"init-declarations": ["off"],
"key-spacing": ["off"],
"keyword-spacing": ["off"],
"line-comment-position": ["off"],
"linebreak-style": [
"off", // Line endings automatically converted to LF on git commit so probably shouldn't care about it here
],
"lines-around-comment": ["off"],
"lines-around-directive": ["error"],
"lines-between-class-members": ["error"],
"max-depth": ["off"],
"max-len": ["off"],
"max-lines": ["off"],
"max-nested-callbacks": ["error"],
"max-params": ["off"],
"max-statements": ["off"],
"max-statements-per-line": ["off"],
"multiline-comment-style": ["off", "starred-block"],
"multiline-ternary": ["off", "never"],
"new-cap": ["off"],
"new-parens": ["off"],
"newline-after-var": ["off"],
"newline-before-return": ["off"],
"newline-per-chained-call": ["off"],
"no-alert": ["error"],
"no-array-constructor": ["error"],
"no-await-in-loop": ["error"],
"no-bitwise": ["off"],
"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-confusing-arrow": ["error"],
"no-console": ["off"],
"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": ["off"],
"no-empty": [
"off",
{
allowEmptyCatch: false,
},
],
"no-empty-character-class": ["error"],
"no-empty-function": ["off"],
"no-empty-pattern": ["error"],
"no-eq-null": ["off"],
"no-ex-assign": ["off"],
"no-extra-boolean-cast": ["error"],
"no-extra-parens": ["off"],
"no-extra-semi": ["error"],
"no-eval": ["off"],
"no-extend-native": ["off"],
"no-extra-bind": ["error"],
"no-extra-label": ["error"],
"no-fallthrough": ["off"],
"no-floating-decimal": ["off"],
"no-func-assign": ["error"],
"no-global-assign": ["error"],
"no-implicit-coercion": ["off"],
"no-implicit-globals": ["error"],
"no-implied-eval": ["error"],
"no-inline-comments": ["off"],
"no-inner-declarations": ["off", "both"],
"no-invalid-regexp": ["error"],
"no-invalid-this": ["off"],
"no-irregular-whitespace": [
"error",
{
skipStrings: false,
skipComments: false,
skipRegExps: false,
skipTemplates: false,
},
],
"no-iterator": ["error"],
"no-label-var": ["error"],
"no-labels": ["off"],
"no-lone-blocks": ["error"],
"no-lonely-if": ["error"],
"no-loop-func": ["off"],
"no-magic-numbers": ["off"],
"no-mixed-operators": ["off"],
"no-mixed-requires": ["error"],
"no-mixed-spaces-and-tabs": ["error"],
"no-multi-assign": ["off"],
"no-multi-spaces": ["off"],
"no-multi-str": ["error"],
"no-multiple-empty-lines": [
"off",
{
max: 1,
},
],
"no-native-reassign": ["error"],
"no-negated-condition": ["off"],
"no-negated-in-lhs": ["error"],
"no-nested-ternary": ["off"],
"no-new": ["error"],
"no-new-func": ["error"],
"no-new-object": ["error"],
"no-new-require": ["error"],
"no-new-symbol": ["error"],
"no-new-wrappers": ["error"],
"no-octal": ["error"],
"no-octal-escape": ["error"],
"no-obj-calls": ["error"],
"no-param-reassign": ["off"],
"no-path-concat": ["error"],
"no-plusplus": ["off"],
"no-process-env": ["off"],
"no-process-exit": ["error"],
"no-proto": ["error"],
"no-prototype-builtins": ["off"],
"no-redeclare": ["off"],
"no-regex-spaces": ["error"],
"no-restricted-globals": ["error"],
"no-restricted-imports": ["error"],
"no-restricted-modules": ["error"],
"no-restricted-properties": [
"off",
{
object: "console",
property: "log",
message: "'log' is too general, use an appropriate level when logging.",
},
],
"no-restricted-syntax": ["error"],
"no-return-assign": ["off"],
"no-return-await": ["error"],
"no-script-url": ["error"],
"no-self-assign": [
"error",
{
props: false,
},
],
"no-self-compare": ["error"],
"no-sequences": ["error"],
"no-shadow": ["off"],
"no-shadow-restricted-names": ["error"],
"no-spaced-func": ["off"],
"no-sparse-arrays": ["error"],
"no-sync": ["error"],
"no-tabs": ["off"],
"no-template-curly-in-string": ["error"],
"no-ternary": ["off"],
"no-this-before-super": ["off"],
"no-throw-literal": ["error"],
"no-trailing-spaces": ["error"],
"no-undef": ["off"],
"no-undef-init": ["error"],
"no-undefined": ["off"],
"no-underscore-dangle": ["off"],
"no-unexpected-multiline": ["error"],
"no-unmodified-loop-condition": ["error"],
"no-unneeded-ternary": ["off"],
"no-unreachable": ["off"],
"no-unsafe-finally": ["error"],
"no-unsafe-negation": ["error"],
"no-unused-expressions": ["off"],
"no-unused-labels": ["error"],
"no-unused-vars": ["off"],
"no-use-before-define": ["off"],
"no-useless-call": ["off"],
"no-useless-computed-key": ["error"],
"no-useless-concat": ["error"],
"no-useless-constructor": ["error"],
"no-useless-escape": ["off"],
"no-useless-rename": [
"error",
{
ignoreDestructuring: false,
ignoreExport: false,
ignoreImport: false,
},
],
"no-useless-return": ["off"],
"no-var": ["off"],
"no-void": ["off"],
"no-warning-comments": ["off"],
"no-whitespace-before-property": ["error"],
"no-with": ["error"],
"nonblock-statement-body-position": ["off", "below"],
"object-curly-newline": ["off"],
"object-curly-spacing": ["off"],
"object-property-newline": ["off"],
"object-shorthand": ["off"],
"one-var": ["off"],
"one-var-declaration-per-line": ["off"],
"operator-assignment": ["off"],
"operator-linebreak": ["off", "none"],
"padded-blocks": ["off"],
"padding-line-between-statements": ["error"],
"prefer-arrow-callback": ["off"],
"prefer-const": ["off"],
"prefer-destructuring": ["off"],
"prefer-numeric-literals": ["error"],
"prefer-promise-reject-errors": ["off"],
"prefer-reflect": ["off"],
"prefer-rest-params": ["off"],
"prefer-spread": ["off"],
"prefer-template": ["off"],
"quote-props": ["off"],
quotes: ["off"],
radix: ["off", "as-needed"],
"require-await": ["off"],
"require-jsdoc": ["off"],
"require-yield": ["error"],
"rest-spread-spacing": ["error", "never"],
semi: ["off"],
"semi-spacing": ["off"],
"semi-style": ["error", "last"],
"sort-imports": ["off"],
"sort-keys": ["off"],
"sort-vars": ["off"],
"space-before-blocks": ["off"],
"space-before-function-paren": ["off"],
"space-in-parens": ["off"],
"space-infix-ops": ["off"],
"space-unary-ops": ["off"],
"spaced-comment": ["off"],
strict: ["off"],
"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": ["off"],
"valid-typeof": ["error"],
"vars-on-top": ["off"],
"wrap-iife": ["error", "any"],
"wrap-regex": ["off"],
"yield-star-spacing": ["error", "before"],
yoda: ["error", "never"],
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/explicit-function-return-type": "off",
},
overrides: [
{
// enable the rule specifically for TypeScript files
files: ["*.ts", "*.tsx"],
rules: {
"@typescript-eslint/explicit-function-return-type": ["error"],
"@typescript-eslint/explicit-module-boundary-types": ["error"],
},
},
{
// TypeScript configuration
files: ["**/*.ts", "**/*.tsx"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: ["plugin:@typescript-eslint/recommended"], extends: ["plugin:@typescript-eslint/recommended"],
rules: { rules: {
"lines-between-class-members": "off",
"no-empty-pattern": "off",
"no-useless-constructor": [
"off", // Valid for typescript due to property ctor shorthand
],
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
allowExpressions: true,
},
],
"@typescript-eslint/member-delimiter-style": [
"error",
{
multiline: {
delimiter: "semi",
requireLast: true,
},
singleline: {
delimiter: "semi",
requireLast: false,
},
},
],
"@typescript-eslint/member-ordering": [
"error",
{
default: [
"signature",
"static-field",
"instance-field",
"abstract-field",
"constructor",
"instance-method",
"abstract-method",
"static-method",
],
},
],
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-use-before-define": "off",
}, },
},
],
}; };

4
dist/main.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

42
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

801
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -18,8 +18,9 @@
"@mui/material": "^5.0.3", "@mui/material": "^5.0.3",
"@mui/styles": "^5.0.1", "@mui/styles": "^5.0.1",
"@mui/system": "^5.0.3", "@mui/system": "^5.0.3",
"acorn": "^8.4.1", "@types/estree": "^1.0.0",
"acorn-walk": "^8.1.1", "acorn": "^8.7.1",
"acorn-walk": "^8.2.0",
"arg": "^5.0.0", "arg": "^5.0.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"better-react-mathjax": "^1.0.3", "better-react-mathjax": "^1.0.3",
@ -78,7 +79,7 @@
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"http-server": "^13.0.1", "http-server": "^13.0.1",
"jest": "^27.1.0", "jest": "^27.1.0",
"jsdom": "^15.0.0", "jsdom": "^16.5.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mini-css-extract-plugin": "^0.4.1", "mini-css-extract-plugin": "^0.4.1",
"prettier": "^2.3.2", "prettier": "^2.3.2",

@ -24,7 +24,7 @@ import { IMap } from "../types";
import * as data from "./AchievementData.json"; import * as data from "./AchievementData.json";
import { FactionNames } from "../Faction/data/FactionNames"; import { FactionNames } from "../Faction/data/FactionNames";
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames"; import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
import { ClassType } from "../utils/WorkType"; import { isClassWork } from "../Work/ClassWork";
// Unable to correctly cast the JSON data into AchievementDataJson type otherwise... // Unable to correctly cast the JSON data into AchievementDataJson type otherwise...
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements; const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
@ -391,10 +391,7 @@ export const achievements: IMap<Achievement> = {
WORKOUT: { WORKOUT: {
...achievementData["WORKOUT"], ...achievementData["WORKOUT"],
Icon: "WORKOUT", Icon: "WORKOUT",
Condition: () => Condition: () => isClassWork(Player.currentWork),
[ClassType.GymStrength, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymAgility].includes(
Player.className,
),
}, },
TOR: { TOR: {
...achievementData["TOR"], ...achievementData["TOR"],

@ -9,7 +9,7 @@ const style = {
width: "1060px", width: "1060px",
height: "800px", height: "800px",
border: "0px", border: "0px",
} as any; };
export function BBCabinetRoot(): React.ReactElement { export function BBCabinetRoot(): React.ReactElement {
const player = use.Player(); const player = use.Player();

@ -1,13 +1,12 @@
// Class definition for a single Augmentation object // Class definition for a single Augmentation object
import * as React from "react"; import * as React from "react";
import { IMap } from "../types";
import { Faction } from "../Faction/Faction"; import { Faction } from "../Faction/Faction";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money"; import { Money } from "../ui/React/Money";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { FactionNames } from "../Faction/data/FactionNames"; import { FactionNames } from "../Faction/data/FactionNames";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { AugmentationNames } from "./data/AugmentationNames"; import { AugmentationNames } from "./data/AugmentationNames";
@ -16,6 +15,7 @@ import { StaticAugmentations } from "./StaticAugmentations";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { getBaseAugmentationPriceMultiplier, getGenericAugmentationPriceMultiplier } from "./AugmentationHelpers"; import { getBaseAugmentationPriceMultiplier, getGenericAugmentationPriceMultiplier } from "./AugmentationHelpers";
import { initSoAAugmentations } from "./data/AugmentationCreator"; import { initSoAAugmentations } from "./data/AugmentationCreator";
import { Multipliers, defaultMultipliers } from "../PersonObjects/Multipliers";
export interface AugmentationCosts { export interface AugmentationCosts {
moneyCost: number; moneyCost: number;
@ -32,48 +32,48 @@ export interface IConstructorParams {
repCost: number; repCost: number;
factions: string[]; factions: string[];
hacking_mult?: number; hacking?: number;
strength_mult?: number; strength?: number;
defense_mult?: number; defense?: number;
dexterity_mult?: number; dexterity?: number;
agility_mult?: number; agility?: number;
charisma_mult?: number; charisma?: number;
hacking_exp_mult?: number; hacking_exp?: number;
strength_exp_mult?: number; strength_exp?: number;
defense_exp_mult?: number; defense_exp?: number;
dexterity_exp_mult?: number; dexterity_exp?: number;
agility_exp_mult?: number; agility_exp?: number;
charisma_exp_mult?: number; charisma_exp?: number;
hacking_chance_mult?: number; hacking_chance?: number;
hacking_speed_mult?: number; hacking_speed?: number;
hacking_money_mult?: number; hacking_money?: number;
hacking_grow_mult?: number; hacking_grow?: number;
company_rep_mult?: number; company_rep?: number;
faction_rep_mult?: number; faction_rep?: number;
crime_money_mult?: number; crime_money?: number;
crime_success_mult?: number; crime_success?: number;
work_money_mult?: number; work_money?: number;
hacknet_node_money_mult?: number; hacknet_node_money?: number;
hacknet_node_purchase_cost_mult?: number; hacknet_node_purchase_cost?: number;
hacknet_node_ram_cost_mult?: number; hacknet_node_ram_cost?: number;
hacknet_node_core_cost_mult?: number; hacknet_node_core_cost?: number;
hacknet_node_level_cost_mult?: number; hacknet_node_level_cost?: number;
bladeburner_max_stamina_mult?: number; bladeburner_max_stamina?: number;
bladeburner_stamina_gain_mult?: number; bladeburner_stamina_gain?: number;
bladeburner_analysis_mult?: number; bladeburner_analysis?: number;
bladeburner_success_chance_mult?: number; bladeburner_success_chance?: number;
infiltration_base_rep_increase?: number; infiltration_base_rep_increase?: number;
infiltration_rep_mult?: number; infiltration_rep?: number;
infiltration_trade_mult?: number; infiltration_trade?: number;
infiltration_sell_mult?: number; infiltration_sell?: number;
infiltration_timer_mult?: number; infiltration_timer?: number;
infiltration_damage_reduction_mult?: number; infiltration_damage_reduction?: number;
startingMoney?: number; startingMoney?: number;
programs?: string[]; programs?: string[];
} }
function generateStatsDescription(mults: IMap<number>, programs?: string[], startingMoney?: number): JSX.Element { function generateStatsDescription(mults: Multipliers, programs?: string[], startingMoney?: number): JSX.Element {
const f = (x: number, decimals = 0): string => { const f = (x: number, decimals = 0): string => {
// look, I don't know how to make a "smart decimals" // look, I don't know how to make a "smart decimals"
// todo, make it smarter // todo, make it smarter
@ -84,323 +84,278 @@ function generateStatsDescription(mults: IMap<number>, programs?: string[], star
let desc = <>Effects:</>; let desc = <>Effects:</>;
if ( if (
mults.hacking_mult && mults.hacking !== 1 &&
mults.hacking_mult == mults.strength_mult && mults.hacking == mults.strength &&
mults.hacking_mult == mults.defense_mult && mults.hacking == mults.defense &&
mults.hacking_mult == mults.dexterity_mult && mults.hacking == mults.dexterity &&
mults.hacking_mult == mults.agility_mult && mults.hacking == mults.agility &&
mults.hacking_mult == mults.charisma_mult mults.hacking == mults.charisma
) { ) {
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_mult - 1)} all skills <br />+{f(mults.hacking - 1)} all skills
</> </>
); );
} else { } else {
if (mults.hacking_mult) if (mults.hacking !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_mult - 1)} hacking skill <br />+{f(mults.hacking - 1)} hacking skill
</> </>
); );
if ( if (
mults.strength_mult && mults.strength !== 1 &&
mults.strength_mult == mults.defense_mult && mults.strength == mults.defense &&
mults.strength_mult == mults.dexterity_mult && mults.strength == mults.dexterity &&
mults.strength_mult == mults.agility_mult mults.strength == mults.agility
) { ) {
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.strength_mult - 1)} combat skills <br />+{f(mults.strength - 1)} combat skills
</> </>
); );
} else { } else {
if (mults.strength_mult) if (mults.strength !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.strength_mult - 1)} strength skill <br />+{f(mults.strength - 1)} strength skill
</> </>
); );
if (mults.defense_mult) if (mults.defense !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.defense_mult - 1)} defense skill <br />+{f(mults.defense - 1)} defense skill
</> </>
); );
if (mults.dexterity_mult) if (mults.dexterity !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.dexterity_mult - 1)} dexterity skill <br />+{f(mults.dexterity - 1)} dexterity skill
</> </>
); );
if (mults.agility_mult) if (mults.agility !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.agility_mult - 1)} agility skill <br />+{f(mults.agility - 1)} agility skill
</> </>
); );
} }
if (mults.charisma_mult) if (mults.charisma !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.charisma_mult - 1)} charisma skill <br />+{f(mults.charisma - 1)} charisma skill
</> </>
); );
} }
if ( if (
mults.hacking_exp_mult && mults.hacking_exp !== 1 &&
mults.hacking_exp_mult === mults.strength_exp_mult && mults.hacking_exp === mults.strength_exp &&
mults.hacking_exp_mult === mults.defense_exp_mult && mults.hacking_exp === mults.defense_exp &&
mults.hacking_exp_mult === mults.dexterity_exp_mult && mults.hacking_exp === mults.dexterity_exp &&
mults.hacking_exp_mult === mults.agility_exp_mult && mults.hacking_exp === mults.agility_exp &&
mults.hacking_exp_mult === mults.charisma_exp_mult mults.hacking_exp === mults.charisma_exp
) { ) {
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_exp_mult - 1)} exp for all skills <br />+{f(mults.hacking_exp - 1)} exp for all skills
</> </>
); );
} else { } else {
if (mults.hacking_exp_mult) if (mults.hacking_exp !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_exp_mult - 1)} hacking exp <br />+{f(mults.hacking_exp - 1)} hacking exp
</> </>
); );
if ( if (
mults.strength_exp_mult && mults.strength_exp !== 1 &&
mults.strength_exp_mult === mults.defense_exp_mult && mults.strength_exp === mults.defense_exp &&
mults.strength_exp_mult === mults.dexterity_exp_mult && mults.strength_exp === mults.dexterity_exp &&
mults.strength_exp_mult === mults.agility_exp_mult mults.strength_exp === mults.agility_exp
) { ) {
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.strength_exp_mult - 1)} combat exp <br />+{f(mults.strength_exp - 1)} combat exp
</> </>
); );
} else { } else {
if (mults.strength_exp_mult) if (mults.strength_exp !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.strength_exp_mult - 1)} strength exp <br />+{f(mults.strength_exp - 1)} strength exp
</> </>
); );
if (mults.defense_exp_mult) if (mults.defense_exp !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.defense_exp_mult - 1)} defense exp <br />+{f(mults.defense_exp - 1)} defense exp
</> </>
); );
if (mults.dexterity_exp_mult) if (mults.dexterity_exp !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.dexterity_exp_mult - 1)} dexterity exp <br />+{f(mults.dexterity_exp - 1)} dexterity exp
</> </>
); );
if (mults.agility_exp_mult) if (mults.agility_exp !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.agility_exp_mult - 1)} agility exp <br />+{f(mults.agility_exp - 1)} agility exp
</> </>
); );
} }
if (mults.charisma_exp_mult) if (mults.charisma_exp !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.charisma_exp_mult - 1)} charisma exp <br />+{f(mults.charisma_exp - 1)} charisma exp
</> </>
); );
} }
if (mults.hacking_speed_mult) if (mults.hacking_speed !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_speed_mult - 1)} faster hack(), grow(), and weaken() <br />+{f(mults.hacking_speed - 1)} faster hack(), grow(), and weaken()
</> </>
); );
if (mults.hacking_chance_mult) if (mults.hacking_chance !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_chance_mult - 1)} hack() success chance <br />+{f(mults.hacking_chance - 1)} hack() success chance
</> </>
); );
if (mults.hacking_money_mult) if (mults.hacking_money !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_money_mult - 1)} hack() power <br />+{f(mults.hacking_money - 1)} hack() power
</> </>
); );
if (mults.hacking_grow_mult) if (mults.hacking_grow !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacking_grow_mult - 1)} grow() power <br />+{f(mults.hacking_grow - 1)} grow() power
</> </>
); );
if (mults.faction_rep_mult && mults.faction_rep_mult === mults.company_rep_mult) { if (mults.faction_rep !== 1 && mults.faction_rep === mults.company_rep) {
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.faction_rep_mult - 1)} reputation from factions and companies <br />+{f(mults.faction_rep - 1)} reputation from factions and companies
</> </>
); );
} else { } else {
if (mults.faction_rep_mult) if (mults.faction_rep !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.faction_rep_mult - 1)} reputation from factions <br />+{f(mults.faction_rep - 1)} reputation from factions
</> </>
); );
if (mults.company_rep_mult) if (mults.company_rep !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.company_rep_mult - 1)} reputation from companies <br />+{f(mults.company_rep - 1)} reputation from companies
</> </>
); );
} }
if (mults.crime_money_mult) if (mults.crime_money !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.crime_money_mult - 1)} crime money <br />+{f(mults.crime_money - 1)} crime money
</> </>
); );
if (mults.crime_success_mult) if (mults.crime_success !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.crime_success_mult - 1)} crime success rate <br />+{f(mults.crime_success - 1)} crime success rate
</> </>
); );
if (mults.work_money_mult) if (mults.work_money !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.work_money_mult - 1)} work money <br />+{f(mults.work_money - 1)} work money
</> </>
); );
if (mults.hacknet_node_money_mult) if (mults.hacknet_node_money !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.hacknet_node_money_mult - 1)} hacknet production <br />+{f(mults.hacknet_node_money - 1)} hacknet production
</> </>
); );
if (mults.hacknet_node_purchase_cost_mult) if (mults.hacknet_node_purchase_cost !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />-{f(-(mults.hacknet_node_purchase_cost_mult - 1))} hacknet nodes cost <br />-{f(-(mults.hacknet_node_purchase_cost - 1))} hacknet nodes cost
</> </>
); );
if (mults.hacknet_node_level_cost_mult) if (mults.hacknet_node_level_cost !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />-{f(-(mults.hacknet_node_level_cost_mult - 1))} hacknet nodes upgrade cost <br />-{f(-(mults.hacknet_node_level_cost - 1))} hacknet nodes upgrade cost
</> </>
); );
if (mults.bladeburner_max_stamina_mult) if (mults.bladeburner_max_stamina !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.bladeburner_max_stamina_mult - 1)} Bladeburner Max Stamina <br />+{f(mults.bladeburner_max_stamina - 1)} Bladeburner Max Stamina
</> </>
); );
if (mults.bladeburner_stamina_gain_mult) if (mults.bladeburner_stamina_gain !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.bladeburner_stamina_gain_mult - 1)} Bladeburner Stamina gain <br />+{f(mults.bladeburner_stamina_gain - 1)} Bladeburner Stamina gain
</> </>
); );
if (mults.bladeburner_analysis_mult) if (mults.bladeburner_analysis !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.bladeburner_analysis_mult - 1)} Bladeburner Field Analysis effectiveness <br />+{f(mults.bladeburner_analysis - 1)} Bladeburner Field Analysis effectiveness
</> </>
); );
if (mults.bladeburner_success_chance_mult) if (mults.bladeburner_success_chance !== 1)
desc = ( desc = (
<> <>
{desc} {desc}
<br />+{f(mults.bladeburner_success_chance_mult - 1)} Bladeburner Contracts and Operations success chance <br />+{f(mults.bladeburner_success_chance - 1)} Bladeburner Contracts and Operations success chance
</> </>
); );
if (mults.infiltration_base_rep_increase)
desc = (
<>
{desc}
<br />+{f(mults.infiltration_base_rep_increase - 1)} Infiltration {FactionNames.ShadowsOfAnarchy} Reputation
base reward
</>
);
if (mults.infiltration_rep_mult)
desc = (
<>
{desc}
<br />+{f(mults.infiltration_rep_mult - 1)} Infiltration {FactionNames.ShadowsOfAnarchy} Reputation reward
</>
);
if (mults.infiltration_trade_mult)
desc = (
<>
{desc}
<br />+{f(mults.infiltration_trade_mult - 1)} Infiltration Reputation for trading information
</>
);
if (mults.infiltration_sell_mult)
desc = (
<>
{desc}
<br />+{f(mults.infiltration_sell_mult - 1)} Infiltration cash reward for selling information
</>
);
if (mults.infiltration_timer_mult)
desc = (
<>
{desc}
<br />+{f(mults.infiltration_timer_mult - 1)} Infiltration time per minigame
</>
);
if (mults.infiltration_damage_reduction_mult)
desc = (
<>
{desc}
<br />
{f(mults.infiltration_damage_reduction_mult - 1)} Infiltration health lost per failed minigame
</>
);
if (startingMoney) if (startingMoney)
desc = ( desc = (
<> <>
@ -445,7 +400,7 @@ export class Augmentation {
// Multipliers given by this Augmentation. Must match the property name in // Multipliers given by this Augmentation. Must match the property name in
// The Player/Person classes // The Player/Person classes
mults: IMap<number> = {}; mults: Multipliers = defaultMultipliers();
// Factions that offer this aug. // Factions that offer this aug.
factions: string[] = []; factions: string[] = [];
@ -474,114 +429,95 @@ export class Augmentation {
} }
// Set multipliers // Set multipliers
if (params.hacking_mult) { if (params.hacking) {
this.mults.hacking_mult = params.hacking_mult; this.mults.hacking = params.hacking;
} }
if (params.strength_mult) { if (params.strength) {
this.mults.strength_mult = params.strength_mult; this.mults.strength = params.strength;
} }
if (params.defense_mult) { if (params.defense) {
this.mults.defense_mult = params.defense_mult; this.mults.defense = params.defense;
} }
if (params.dexterity_mult) { if (params.dexterity) {
this.mults.dexterity_mult = params.dexterity_mult; this.mults.dexterity = params.dexterity;
} }
if (params.agility_mult) { if (params.agility) {
this.mults.agility_mult = params.agility_mult; this.mults.agility = params.agility;
} }
if (params.charisma_mult) { if (params.charisma) {
this.mults.charisma_mult = params.charisma_mult; this.mults.charisma = params.charisma;
} }
if (params.hacking_exp_mult) { if (params.hacking_exp) {
this.mults.hacking_exp_mult = params.hacking_exp_mult; this.mults.hacking_exp = params.hacking_exp;
} }
if (params.strength_exp_mult) { if (params.strength_exp) {
this.mults.strength_exp_mult = params.strength_exp_mult; this.mults.strength_exp = params.strength_exp;
} }
if (params.defense_exp_mult) { if (params.defense_exp) {
this.mults.defense_exp_mult = params.defense_exp_mult; this.mults.defense_exp = params.defense_exp;
} }
if (params.dexterity_exp_mult) { if (params.dexterity_exp) {
this.mults.dexterity_exp_mult = params.dexterity_exp_mult; this.mults.dexterity_exp = params.dexterity_exp;
} }
if (params.agility_exp_mult) { if (params.agility_exp) {
this.mults.agility_exp_mult = params.agility_exp_mult; this.mults.agility_exp = params.agility_exp;
} }
if (params.charisma_exp_mult) { if (params.charisma_exp) {
this.mults.charisma_exp_mult = params.charisma_exp_mult; this.mults.charisma_exp = params.charisma_exp;
} }
if (params.hacking_chance_mult) { if (params.hacking_chance) {
this.mults.hacking_chance_mult = params.hacking_chance_mult; this.mults.hacking_chance = params.hacking_chance;
} }
if (params.hacking_speed_mult) { if (params.hacking_speed) {
this.mults.hacking_speed_mult = params.hacking_speed_mult; this.mults.hacking_speed = params.hacking_speed;
} }
if (params.hacking_money_mult) { if (params.hacking_money) {
this.mults.hacking_money_mult = params.hacking_money_mult; this.mults.hacking_money = params.hacking_money;
} }
if (params.hacking_grow_mult) { if (params.hacking_grow) {
this.mults.hacking_grow_mult = params.hacking_grow_mult; this.mults.hacking_grow = params.hacking_grow;
} }
if (params.company_rep_mult) { if (params.company_rep) {
this.mults.company_rep_mult = params.company_rep_mult; this.mults.company_rep = params.company_rep;
} }
if (params.faction_rep_mult) { if (params.faction_rep) {
this.mults.faction_rep_mult = params.faction_rep_mult; this.mults.faction_rep = params.faction_rep;
} }
if (params.crime_money_mult) { if (params.crime_money) {
this.mults.crime_money_mult = params.crime_money_mult; this.mults.crime_money = params.crime_money;
} }
if (params.crime_success_mult) { if (params.crime_success) {
this.mults.crime_success_mult = params.crime_success_mult; this.mults.crime_success = params.crime_success;
} }
if (params.work_money_mult) { if (params.work_money) {
this.mults.work_money_mult = params.work_money_mult; this.mults.work_money = params.work_money;
} }
if (params.hacknet_node_money_mult) { if (params.hacknet_node_money) {
this.mults.hacknet_node_money_mult = params.hacknet_node_money_mult; this.mults.hacknet_node_money = params.hacknet_node_money;
} }
if (params.hacknet_node_purchase_cost_mult) { if (params.hacknet_node_purchase_cost) {
this.mults.hacknet_node_purchase_cost_mult = params.hacknet_node_purchase_cost_mult; this.mults.hacknet_node_purchase_cost = params.hacknet_node_purchase_cost;
} }
if (params.hacknet_node_ram_cost_mult) { if (params.hacknet_node_ram_cost) {
this.mults.hacknet_node_ram_cost_mult = params.hacknet_node_ram_cost_mult; this.mults.hacknet_node_ram_cost = params.hacknet_node_ram_cost;
} }
if (params.hacknet_node_core_cost_mult) { if (params.hacknet_node_core_cost) {
this.mults.hacknet_node_core_cost_mult = params.hacknet_node_core_cost_mult; this.mults.hacknet_node_core_cost = params.hacknet_node_core_cost;
} }
if (params.hacknet_node_level_cost_mult) { if (params.hacknet_node_level_cost) {
this.mults.hacknet_node_level_cost_mult = params.hacknet_node_level_cost_mult; this.mults.hacknet_node_level_cost = params.hacknet_node_level_cost;
} }
if (params.bladeburner_max_stamina_mult) { if (params.bladeburner_max_stamina) {
this.mults.bladeburner_max_stamina_mult = params.bladeburner_max_stamina_mult; this.mults.bladeburner_max_stamina = params.bladeburner_max_stamina;
} }
if (params.bladeburner_stamina_gain_mult) { if (params.bladeburner_stamina_gain) {
this.mults.bladeburner_stamina_gain_mult = params.bladeburner_stamina_gain_mult; this.mults.bladeburner_stamina_gain = params.bladeburner_stamina_gain;
} }
if (params.bladeburner_analysis_mult) { if (params.bladeburner_analysis) {
this.mults.bladeburner_analysis_mult = params.bladeburner_analysis_mult; this.mults.bladeburner_analysis = params.bladeburner_analysis;
} }
if (params.bladeburner_success_chance_mult) { if (params.bladeburner_success_chance) {
this.mults.bladeburner_success_chance_mult = params.bladeburner_success_chance_mult; this.mults.bladeburner_success_chance = params.bladeburner_success_chance;
}
if (params.infiltration_base_rep_increase) {
this.mults.infiltration_base_rep_increase = params.infiltration_base_rep_increase;
}
if (params.infiltration_rep_mult) {
this.mults.infiltration_rep_mult = params.infiltration_rep_mult;
}
if (params.infiltration_trade_mult) {
this.mults.infiltration_trade_mult = params.infiltration_trade_mult;
}
if (params.infiltration_sell_mult) {
this.mults.infiltration_sell_mult = params.infiltration_sell_mult;
}
if (params.infiltration_timer_mult) {
this.mults.infiltration_timer_mult = params.infiltration_timer_mult;
}
if (params.infiltration_damage_reduction_mult) {
this.mults.infiltration_damage_reduction_mult = params.infiltration_damage_reduction_mult;
} }
if (params.stats === undefined) if (params.stats === undefined)
@ -673,13 +609,12 @@ export class Augmentation {
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Augmentation", this); return Generic_toJSON("Augmentation", this);
} }
// Initiatizes a Augmentation object from a JSON save state. // Initiatizes a Augmentation object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Augmentation {
static fromJSON(value: any): Augmentation {
return Generic_fromJSON(Augmentation, value.data); return Generic_fromJSON(Augmentation, value.data);
} }
} }

@ -21,6 +21,7 @@ import {
initUnstableCircadianModulator, initUnstableCircadianModulator,
} from "./data/AugmentationCreator"; } from "./data/AugmentationCreator";
import { Router } from "../ui/GameRoot"; import { Router } from "../ui/GameRoot";
import { mergeMultipliers } from "../PersonObjects/Multipliers";
export function AddToStaticAugmentations(aug: Augmentation): void { export function AddToStaticAugmentations(aug: Augmentation): void {
const name = aug.name; const name = aug.name;
@ -74,10 +75,7 @@ function applyAugmentation(aug: IPlayerOwnedAugmentation, reapply = false): void
const staticAugmentation = StaticAugmentations[aug.name]; const staticAugmentation = StaticAugmentations[aug.name];
// Apply multipliers // Apply multipliers
for (const mult of Object.keys(staticAugmentation.mults)) { Player.mults = mergeMultipliers(Player.mults, staticAugmentation.mults);
const v = Player.getMult(mult) * staticAugmentation.mults[mult];
Player.setMult(mult, v);
}
// Special logic for Congruity Implant // Special logic for Congruity Implant
if (aug.name === AugmentationNames.CongruityImplant && !reapply) { if (aug.name === AugmentationNames.CongruityImplant && !reapply) {

File diff suppressed because it is too large Load Diff

@ -4,24 +4,18 @@
import { DoubleArrow } from "@mui/icons-material"; import { DoubleArrow } from "@mui/icons-material";
import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material"; import { List, ListItem, ListItemText, Paper, Typography } from "@mui/material";
import * as React from "react"; import * as React from "react";
import { Multipliers, defaultMultipliers, mergeMultipliers } from "../../PersonObjects/Multipliers";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { Player } from "../../Player"; import { Player } from "../../Player";
import { Settings } from "../../Settings/Settings"; import { Settings } from "../../Settings/Settings";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
import { StaticAugmentations } from "../StaticAugmentations"; import { StaticAugmentations } from "../StaticAugmentations";
interface IAugmentedStats { function calculateAugmentedStats(): Multipliers {
[index: string]: number; let augP: Multipliers = defaultMultipliers();
}
function calculateAugmentedStats(): IAugmentedStats {
const augP: IAugmentedStats = {};
for (const aug of Player.queuedAugmentations) { for (const aug of Player.queuedAugmentations) {
const augObj = StaticAugmentations[aug.name]; const augObj = StaticAugmentations[aug.name];
for (const mult of Object.keys(augObj.mults)) { augP = mergeMultipliers(augP, augObj.mults);
const v = augP[mult] ? augP[mult] : 1;
augP[mult] = v * augObj.mults[mult];
}
} }
return augP; return augP;
} }
@ -98,35 +92,35 @@ export function PlayerMultipliers(): React.ReactElement {
...[ ...[
{ {
mult: "Hacking Chance", mult: "Hacking Chance",
current: Player.hacking_chance_mult, current: Player.mults.hacking_chance,
augmented: Player.hacking_chance_mult * mults.hacking_chance_mult, augmented: Player.mults.hacking_chance * mults.hacking_chance,
}, },
{ {
mult: "Hacking Speed", mult: "Hacking Speed",
current: Player.hacking_speed_mult, current: Player.mults.hacking_speed,
augmented: Player.hacking_speed_mult * mults.hacking_speed_mult, augmented: Player.mults.hacking_speed * mults.hacking_speed,
}, },
{ {
mult: "Hacking Money", mult: "Hacking Money",
current: Player.hacking_money_mult, current: Player.mults.hacking_money,
augmented: Player.hacking_money_mult * mults.hacking_money_mult, augmented: Player.mults.hacking_money * mults.hacking_money,
bnMult: BitNodeMultipliers.ScriptHackMoney, bnMult: BitNodeMultipliers.ScriptHackMoney,
}, },
{ {
mult: "Hacking Growth", mult: "Hacking Growth",
current: Player.hacking_grow_mult, current: Player.mults.hacking_grow,
augmented: Player.hacking_grow_mult * mults.hacking_grow_mult, augmented: Player.mults.hacking_grow * mults.hacking_grow,
}, },
{ {
mult: "Hacking Level", mult: "Hacking Level",
current: Player.hacking_mult, current: Player.mults.hacking,
augmented: Player.hacking_mult * mults.hacking_mult, augmented: Player.mults.hacking * mults.hacking,
bnMult: BitNodeMultipliers.HackingLevelMultiplier, bnMult: BitNodeMultipliers.HackingLevelMultiplier,
}, },
{ {
mult: "Hacking Experience", mult: "Hacking Experience",
current: Player.hacking_exp_mult, current: Player.mults.hacking_exp,
augmented: Player.hacking_exp_mult * mults.hacking_exp_mult, augmented: Player.mults.hacking_exp * mults.hacking_exp,
bnMult: BitNodeMultipliers.HackExpGain, bnMult: BitNodeMultipliers.HackExpGain,
}, },
].map((data: MultiplierListItemData) => ].map((data: MultiplierListItemData) =>
@ -137,47 +131,47 @@ export function PlayerMultipliers(): React.ReactElement {
...[ ...[
{ {
mult: "Strength Level", mult: "Strength Level",
current: Player.strength_mult, current: Player.mults.strength,
augmented: Player.strength_mult * mults.strength_mult, augmented: Player.mults.strength * mults.strength,
bnMult: BitNodeMultipliers.StrengthLevelMultiplier, bnMult: BitNodeMultipliers.StrengthLevelMultiplier,
}, },
{ {
mult: "Strength Experience", mult: "Strength Experience",
current: Player.strength_exp_mult, current: Player.mults.strength_exp,
augmented: Player.strength_exp_mult * mults.strength_exp_mult, augmented: Player.mults.strength_exp * mults.strength_exp,
}, },
{ {
mult: "Defense Level", mult: "Defense Level",
current: Player.defense_mult, current: Player.mults.defense,
augmented: Player.defense_mult * mults.defense_mult, augmented: Player.mults.defense * mults.defense,
bnMult: BitNodeMultipliers.DefenseLevelMultiplier, bnMult: BitNodeMultipliers.DefenseLevelMultiplier,
}, },
{ {
mult: "Defense Experience", mult: "Defense Experience",
current: Player.defense_exp_mult, current: Player.mults.defense_exp,
augmented: Player.defense_exp_mult * mults.defense_exp_mult, augmented: Player.mults.defense_exp * mults.defense_exp,
}, },
{ {
mult: "Dexterity Level", mult: "Dexterity Level",
current: Player.dexterity_mult, current: Player.mults.dexterity,
augmented: Player.dexterity_mult * mults.dexterity_mult, augmented: Player.mults.dexterity * mults.dexterity,
bnMult: BitNodeMultipliers.DexterityLevelMultiplier, bnMult: BitNodeMultipliers.DexterityLevelMultiplier,
}, },
{ {
mult: "Dexterity Experience", mult: "Dexterity Experience",
current: Player.dexterity_exp_mult, current: Player.mults.dexterity_exp,
augmented: Player.dexterity_exp_mult * mults.dexterity_exp_mult, augmented: Player.mults.dexterity_exp * mults.dexterity_exp,
}, },
{ {
mult: "Agility Level", mult: "Agility Level",
current: Player.agility_mult, current: Player.mults.agility,
augmented: Player.agility_mult * mults.agility_mult, augmented: Player.mults.agility * mults.agility,
bnMult: BitNodeMultipliers.AgilityLevelMultiplier, bnMult: BitNodeMultipliers.AgilityLevelMultiplier,
}, },
{ {
mult: "Agility Experience", mult: "Agility Experience",
current: Player.agility_exp_mult, current: Player.mults.agility_exp,
augmented: Player.agility_exp_mult * mults.agility_exp_mult, augmented: Player.mults.agility_exp * mults.agility_exp,
}, },
].map((data: MultiplierListItemData) => ].map((data: MultiplierListItemData) =>
Object.defineProperty(data, "color", { Object.defineProperty(data, "color", {
@ -186,73 +180,73 @@ export function PlayerMultipliers(): React.ReactElement {
), ),
{ {
mult: "Charisma Level", mult: "Charisma Level",
current: Player.charisma_mult, current: Player.mults.charisma,
augmented: Player.charisma_mult * mults.charisma_mult, augmented: Player.mults.charisma * mults.charisma,
bnMult: BitNodeMultipliers.CharismaLevelMultiplier, bnMult: BitNodeMultipliers.CharismaLevelMultiplier,
color: Settings.theme.cha, color: Settings.theme.cha,
}, },
{ {
mult: "Charisma Experience", mult: "Charisma Experience",
current: Player.charisma_exp_mult, current: Player.mults.charisma_exp,
augmented: Player.charisma_exp_mult * mults.charisma_exp_mult, augmented: Player.mults.charisma_exp * mults.charisma_exp,
color: Settings.theme.cha, color: Settings.theme.cha,
}, },
]; ];
const rightColData: MultiplierListItemData[] = [ const rightColData: MultiplierListItemData[] = [
{ {
mult: "Hacknet Node Production", mult: "Hacknet Node Production",
current: Player.hacknet_node_money_mult, current: Player.mults.hacknet_node_money,
augmented: Player.hacknet_node_money_mult * mults.hacknet_node_money_mult, augmented: Player.mults.hacknet_node_money * mults.hacknet_node_money,
bnMult: BitNodeMultipliers.HacknetNodeMoney, bnMult: BitNodeMultipliers.HacknetNodeMoney,
}, },
{ {
mult: "Hacknet Node Purchase Cost", mult: "Hacknet Node Purchase Cost",
current: Player.hacknet_node_purchase_cost_mult, current: Player.mults.hacknet_node_purchase_cost,
augmented: Player.hacknet_node_purchase_cost_mult * mults.hacknet_node_purchase_cost_mult, augmented: Player.mults.hacknet_node_purchase_cost * mults.hacknet_node_purchase_cost,
}, },
{ {
mult: "Hacknet Node RAM Upgrade Cost", mult: "Hacknet Node RAM Upgrade Cost",
current: Player.hacknet_node_ram_cost_mult, current: Player.mults.hacknet_node_ram_cost,
augmented: Player.hacknet_node_ram_cost_mult * mults.hacknet_node_ram_cost_mult, augmented: Player.mults.hacknet_node_ram_cost * mults.hacknet_node_ram_cost,
}, },
{ {
mult: "Hacknet Node Core Purchase Cost", mult: "Hacknet Node Core Purchase Cost",
current: Player.hacknet_node_core_cost_mult, current: Player.mults.hacknet_node_core_cost,
augmented: Player.hacknet_node_core_cost_mult * mults.hacknet_node_core_cost_mult, augmented: Player.mults.hacknet_node_core_cost * mults.hacknet_node_core_cost,
}, },
{ {
mult: "Hacknet Node Level Upgrade Cost", mult: "Hacknet Node Level Upgrade Cost",
current: Player.hacknet_node_level_cost_mult, current: Player.mults.hacknet_node_level_cost,
augmented: Player.hacknet_node_level_cost_mult * mults.hacknet_node_level_cost_mult, augmented: Player.mults.hacknet_node_level_cost * mults.hacknet_node_level_cost,
}, },
{ {
mult: "Company Reputation Gain", mult: "Company Reputation Gain",
current: Player.company_rep_mult, current: Player.mults.company_rep,
augmented: Player.company_rep_mult * mults.company_rep_mult, augmented: Player.mults.company_rep * mults.company_rep,
}, },
{ {
mult: "Faction Reputation Gain", mult: "Faction Reputation Gain",
current: Player.faction_rep_mult, current: Player.mults.faction_rep,
augmented: Player.faction_rep_mult * mults.faction_rep_mult, augmented: Player.mults.faction_rep * mults.faction_rep,
bnMult: BitNodeMultipliers.FactionWorkRepGain, bnMult: BitNodeMultipliers.FactionWorkRepGain,
}, },
{ {
mult: "Salary", mult: "Salary",
current: Player.work_money_mult, current: Player.mults.work_money,
augmented: Player.work_money_mult * mults.work_money_mult, augmented: Player.mults.work_money * mults.work_money,
bnMult: BitNodeMultipliers.CompanyWorkMoney, bnMult: BitNodeMultipliers.CompanyWorkMoney,
color: Settings.theme.money, color: Settings.theme.money,
}, },
{ {
mult: "Crime Success Chance", mult: "Crime Success Chance",
current: Player.crime_success_mult, current: Player.mults.crime_success,
augmented: Player.crime_success_mult * mults.crime_success_mult, augmented: Player.mults.crime_success * mults.crime_success,
color: Settings.theme.combat, color: Settings.theme.combat,
}, },
{ {
mult: "Crime Money", mult: "Crime Money",
current: Player.crime_money_mult, current: Player.mults.crime_money,
augmented: Player.crime_money_mult * mults.crime_money_mult, augmented: Player.mults.crime_money * mults.crime_money,
bnMult: BitNodeMultipliers.CrimeMoney, bnMult: BitNodeMultipliers.CrimeMoney,
color: Settings.theme.money, color: Settings.theme.money,
}, },
@ -262,23 +256,23 @@ export function PlayerMultipliers(): React.ReactElement {
rightColData.push( rightColData.push(
{ {
mult: "Bladeburner Success Chance", mult: "Bladeburner Success Chance",
current: Player.bladeburner_success_chance_mult, current: Player.mults.bladeburner_success_chance,
augmented: Player.bladeburner_success_chance_mult * mults.bladeburner_success_chance_mult, augmented: Player.mults.bladeburner_success_chance * mults.bladeburner_success_chance,
}, },
{ {
mult: "Bladeburner Max Stamina", mult: "Bladeburner Max Stamina",
current: Player.bladeburner_max_stamina_mult, current: Player.mults.bladeburner_max_stamina,
augmented: Player.bladeburner_max_stamina_mult * mults.bladeburner_max_stamina_mult, augmented: Player.mults.bladeburner_max_stamina * mults.bladeburner_max_stamina,
}, },
{ {
mult: "Bladeburner Stamina Gain", mult: "Bladeburner Stamina Gain",
current: Player.bladeburner_stamina_gain_mult, current: Player.mults.bladeburner_stamina_gain,
augmented: Player.bladeburner_stamina_gain_mult * mults.bladeburner_stamina_gain_mult, augmented: Player.mults.bladeburner_stamina_gain * mults.bladeburner_stamina_gain,
}, },
{ {
mult: "Bladeburner Field Analysis", mult: "Bladeburner Field Analysis",
current: Player.bladeburner_analysis_mult, current: Player.mults.bladeburner_analysis,
augmented: Player.bladeburner_analysis_mult * mults.bladeburner_analysis_mult, augmented: Player.mults.bladeburner_analysis * mults.bladeburner_analysis,
}, },
); );
} }

@ -77,7 +77,7 @@ interface IBNMultTableProps {
const BNMultTable = (props: IBNMultTableProps): React.ReactElement => { const BNMultTable = (props: IBNMultTableProps): React.ReactElement => {
const rowsArray = Object.entries(props.rowData) const rowsArray = Object.entries(props.rowData)
.filter(([key, _value]) => props.mults[key] !== defaultMultipliers[key]) .filter(([key]) => props.mults[key] !== defaultMultipliers[key])
.map(([key, value]) => ( .map(([key, value]) => (
<StatsRow <StatsRow
key={uniqueId()} key={uniqueId()}

@ -1,7 +1,7 @@
import { Player } from "../Player"; import { Player } from "../Player";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { addOffset } from "../utils/helpers/addOffset"; import { addOffset } from "../utils/helpers/addOffset";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { BladeburnerConstants } from "./data/Constants"; import { BladeburnerConstants } from "./data/Constants";
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
import { IAction, ISuccessChanceParams } from "./IAction"; import { IAction, ISuccessChanceParams } from "./IAction";
@ -274,7 +274,7 @@ export class Action implements IAction {
} }
// Augmentation multiplier // Augmentation multiplier
competence *= Player.bladeburner_success_chance_mult; competence *= Player.mults.bladeburner_success_chance;
if (isNaN(competence)) { if (isNaN(competence)) {
throw new Error("Competence calculated as NaN in Action.getSuccessChance()"); throw new Error("Competence calculated as NaN in Action.getSuccessChance()");
@ -292,12 +292,11 @@ export class Action implements IAction {
} }
} }
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Action", this); return Generic_toJSON("Action", this);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Action {
static fromJSON(value: any): Action {
return Generic_fromJSON(Action, value.data); return Generic_fromJSON(Action, value.data);
} }
} }

@ -1,5 +1,5 @@
import { IActionIdentifier } from "./IActionIdentifier"; import { IActionIdentifier } from "./IActionIdentifier";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
interface IParams { interface IParams {
name?: string; name?: string;
@ -15,12 +15,11 @@ export class ActionIdentifier implements IActionIdentifier {
if (params.type) this.type = params.type; if (params.type) this.type = params.type;
} }
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("ActionIdentifier", this); return Generic_toJSON("ActionIdentifier", this);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): ActionIdentifier {
static fromJSON(value: any): ActionIdentifier {
return Generic_fromJSON(ActionIdentifier, value.data); return Generic_fromJSON(ActionIdentifier, value.data);
} }
} }

@ -1,5 +1,5 @@
import { Operation, IOperationParams } from "./Operation"; import { Operation, IOperationParams } from "./Operation";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
export class BlackOperation extends Operation { export class BlackOperation extends Operation {
constructor(params: IOperationParams | null = null) { constructor(params: IOperationParams | null = null) {
@ -20,12 +20,11 @@ export class BlackOperation extends Operation {
return 1; return 1;
} }
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("BlackOperation", this); return Generic_toJSON("BlackOperation", this);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Operation {
static fromJSON(value: any): Operation {
return Generic_fromJSON(BlackOperation, value.data); return Generic_fromJSON(BlackOperation, value.data);
} }
} }

@ -1,4 +1,4 @@
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
import { IActionIdentifier } from "./IActionIdentifier"; import { IActionIdentifier } from "./IActionIdentifier";
import { ActionIdentifier } from "./ActionIdentifier"; import { ActionIdentifier } from "./ActionIdentifier";
@ -178,7 +178,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction(); return this.resetAction();
} }
this.actionTimeToComplete = action.getActionTime(this, person); this.actionTimeToComplete = action.getActionTime(this, person);
} catch (e: any) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
break; break;
@ -195,7 +195,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction(); return this.resetAction();
} }
this.actionTimeToComplete = action.getActionTime(this, person); this.actionTimeToComplete = action.getActionTime(this, person);
} catch (e: any) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
break; break;
@ -213,7 +213,7 @@ export class Bladeburner implements IBladeburner {
throw new Error("action should not be null"); throw new Error("action should not be null");
} }
this.actionTimeToComplete = testBlackOp.action.getActionTime(this, person); this.actionTimeToComplete = testBlackOp.action.getActionTime(this, person);
} catch (e: any) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
break; break;
@ -264,7 +264,7 @@ export class Bladeburner implements IBladeburner {
for (let i = 0; i < arrayOfCommands.length; ++i) { for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]); this.executeConsoleCommand(player, arrayOfCommands[i]);
} }
} catch (e: any) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
} }
@ -794,21 +794,9 @@ export class Bladeburner implements IBladeburner {
let i = 0; let i = 0;
while (i < command.length) { while (i < command.length) {
const c = command.charAt(i); const c = command.charAt(i);
if (c === '"') { if (c === '"' || c === "'") {
// Double quotes // Double quotes or Single quotes
const endQuote = command.indexOf('"', i + 1); const endQuote = command.indexOf(c, i + 1);
if (endQuote !== -1 && (endQuote === command.length - 1 || command.charAt(endQuote + 1) === KEY.SPACE)) {
args.push(command.substr(i + 1, endQuote - i - 1));
if (endQuote === command.length - 1) {
start = i = endQuote + 1;
} else {
start = i = endQuote + 2; // Skip the space
}
continue;
}
} else if (c === "'") {
// Single quotes, same thing as above
const endQuote = command.indexOf("'", i + 1);
if (endQuote !== -1 && (endQuote === command.length - 1 || command.charAt(endQuote + 1) === KEY.SPACE)) { if (endQuote !== -1 && (endQuote === command.length - 1 || command.charAt(endQuote + 1) === KEY.SPACE)) {
args.push(command.substr(i + 1, endQuote - i - 1)); args.push(command.substr(i + 1, endQuote - i - 1));
if (endQuote === command.length - 1) { if (endQuote === command.length - 1) {
@ -1367,7 +1355,7 @@ export class Bladeburner implements IBladeburner {
if (action.autoLevel) { if (action.autoLevel) {
action.level = action.maxLevel; action.level = action.maxLevel;
} // Autolevel } // Autolevel
} catch (e: any) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
break; break;
@ -1462,17 +1450,17 @@ export class Bladeburner implements IBladeburner {
this.log(`${person.whoAmI()}: You lost ${formatNumber(losses, 0)} team members during ${action.name}`); this.log(`${person.whoAmI()}: You lost ${formatNumber(losses, 0)} team members during ${action.name}`);
} }
} }
} catch (e: any) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
break; break;
} }
case ActionTypes["Training"]: { case ActionTypes["Training"]: {
this.stamina -= 0.5 * BladeburnerConstants.BaseStaminaLoss; this.stamina -= 0.5 * BladeburnerConstants.BaseStaminaLoss;
const strExpGain = 30 * person.strength_exp_mult, const strExpGain = 30 * person.mults.strength_exp,
defExpGain = 30 * person.defense_exp_mult, defExpGain = 30 * person.mults.defense_exp,
dexExpGain = 30 * person.dexterity_exp_mult, dexExpGain = 30 * person.mults.dexterity_exp,
agiExpGain = 30 * person.agility_exp_mult, agiExpGain = 30 * person.mults.agility_exp,
staminaGain = 0.04 * this.skillMultipliers.stamina; staminaGain = 0.04 * this.skillMultipliers.stamina;
retValue.str = strExpGain; retValue.str = strExpGain;
retValue.def = defExpGain; retValue.def = defExpGain;
@ -1504,12 +1492,12 @@ export class Bladeburner implements IBladeburner {
0.04 * Math.pow(person.hacking, 0.3) + 0.04 * Math.pow(person.hacking, 0.3) +
0.04 * Math.pow(person.intelligence, 0.9) + 0.04 * Math.pow(person.intelligence, 0.9) +
0.02 * Math.pow(person.charisma, 0.3); 0.02 * Math.pow(person.charisma, 0.3);
eff *= person.bladeburner_analysis_mult; eff *= person.mults.bladeburner_analysis;
if (isNaN(eff) || eff < 0) { if (isNaN(eff) || eff < 0) {
throw new Error("Field Analysis Effectiveness calculated to be NaN or negative"); throw new Error("Field Analysis Effectiveness calculated to be NaN or negative");
} }
const hackingExpGain = 20 * person.hacking_exp_mult; const hackingExpGain = 20 * person.mults.hacking_exp;
const charismaExpGain = 20 * person.charisma_exp_mult; const charismaExpGain = 20 * person.mults.charisma_exp;
const rankGain = 0.1 * BitNodeMultipliers.BladeburnerRank; const rankGain = 0.1 * BitNodeMultipliers.BladeburnerRank;
retValue.hack = hackingExpGain; retValue.hack = hackingExpGain;
retValue.cha = charismaExpGain; retValue.cha = charismaExpGain;
@ -1647,7 +1635,7 @@ export class Bladeburner implements IBladeburner {
if (bladeburnerFac.isMember) { if (bladeburnerFac.isMember) {
const favorBonus = 1 + bladeburnerFac.favor / 100; const favorBonus = 1 + bladeburnerFac.favor / 100;
bladeburnerFac.playerReputation += bladeburnerFac.playerReputation +=
BladeburnerConstants.RankToFactionRepFactor * change * person.faction_rep_mult * favorBonus; BladeburnerConstants.RankToFactionRepFactor * change * person.mults.faction_rep * favorBonus;
} }
} }
@ -1695,7 +1683,7 @@ export class Bladeburner implements IBladeburner {
const effAgility = player.agility * this.skillMultipliers.effAgi; const effAgility = player.agility * this.skillMultipliers.effAgi;
const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor; const maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor;
const gain = (BladeburnerConstants.StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17); const gain = (BladeburnerConstants.StaminaGainPerSecond + maxStaminaBonus) * Math.pow(effAgility, 0.17);
return gain * (this.skillMultipliers.stamina * player.bladeburner_stamina_gain_mult); return gain * (this.skillMultipliers.stamina * player.mults.bladeburner_stamina_gain);
} }
calculateMaxStamina(player: IPlayer): void { calculateMaxStamina(player: IPlayer): void {
@ -1703,7 +1691,7 @@ export class Bladeburner implements IBladeburner {
const maxStamina = const maxStamina =
(Math.pow(effAgility, 0.8) + this.staminaBonus) * (Math.pow(effAgility, 0.8) + this.staminaBonus) *
this.skillMultipliers.stamina * this.skillMultipliers.stamina *
player.bladeburner_max_stamina_mult; player.mults.bladeburner_max_stamina;
if (this.maxStamina !== maxStamina) { if (this.maxStamina !== maxStamina) {
const oldMax = this.maxStamina; const oldMax = this.maxStamina;
this.maxStamina = maxStamina; this.maxStamina = maxStamina;
@ -1988,7 +1976,7 @@ export class Bladeburner implements IBladeburner {
if (!router.isInitialized) return; if (!router.isInitialized) return;
// If the Player starts doing some other actions, set action to idle and alert // If the Player starts doing some other actions, set action to idle and alert
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.isWorking) { if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true) && player.currentWork) {
if (this.action.type !== ActionTypes["Idle"]) { if (this.action.type !== ActionTypes["Idle"]) {
let msg = "Your Bladeburner action was cancelled because you started doing something else."; let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) { if (this.automateEnabled) {
@ -2154,7 +2142,8 @@ export class Bladeburner implements IBladeburner {
() => `Starting bladeburner action with type '${type}' and name '${name}'`, () => `Starting bladeburner action with type '${type}' and name '${name}'`,
); );
return true; return true;
} catch (e: any) { } catch (e: unknown) {
console.error(e);
this.resetAction(); this.resetAction();
workerScript.log("bladeburner.startAction", () => errorLogText); workerScript.log("bladeburner.startAction", () => errorLogText);
return false; return false;
@ -2409,15 +2398,14 @@ export class Bladeburner implements IBladeburner {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Bladeburner", this); return Generic_toJSON("Bladeburner", this);
} }
/** /**
* Initiatizes a Bladeburner object from a JSON save state. * Initiatizes a Bladeburner object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Bladeburner {
static fromJSON(value: any): Bladeburner {
return Generic_fromJSON(Bladeburner, value.data); return Generic_fromJSON(Bladeburner, value.data);
} }
} }

@ -1,6 +1,6 @@
import { BladeburnerConstants } from "./data/Constants"; import { BladeburnerConstants } from "./data/Constants";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { addOffset } from "../utils/helpers/addOffset"; import { addOffset } from "../utils/helpers/addOffset";
interface IChangePopulationByCountParams { interface IChangePopulationByCountParams {
@ -177,15 +177,14 @@ export class City {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("City", this); return Generic_toJSON("City", this);
} }
/** /**
* Initiatizes a City object from a JSON save state. * Initiatizes a City object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): City {
static fromJSON(value: any): City {
return Generic_fromJSON(City, value.data); return Generic_fromJSON(City, value.data);
} }
} }

@ -1,6 +1,6 @@
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
import { Action, IActionParams } from "./Action"; import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
export class Contract extends Action { export class Contract extends Action {
constructor(params: IActionParams | null = null) { constructor(params: IActionParams | null = null) {
@ -11,12 +11,11 @@ export class Contract extends Action {
return inst.skillMultipliers.successChanceContract; return inst.skillMultipliers.successChanceContract;
} }
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Contract", this); return Generic_toJSON("Contract", this);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Contract {
static fromJSON(value: any): Contract {
return Generic_fromJSON(Contract, value.data); return Generic_fromJSON(Contract, value.data);
} }
} }

@ -1,3 +1,4 @@
import { IReviverValue } from "../utils/JSONReviver";
import { IPerson } from "../PersonObjects/IPerson"; import { IPerson } from "../PersonObjects/IPerson";
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
@ -67,5 +68,5 @@ export interface IAction {
getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams): number; getSuccessChance(inst: IBladeburner, person: IPerson, params: ISuccessChanceParams): number;
getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number; getSuccessesNeededForNextLevel(baseSuccessesPerLevel: number): number;
setMaxLevel(baseSuccessesPerLevel: number): void; setMaxLevel(baseSuccessesPerLevel: number): void;
toJSON(): any; toJSON(): IReviverValue;
} }

@ -7,6 +7,8 @@ import { IPerson } from "../PersonObjects/IPerson";
import { ITaskTracker } from "../PersonObjects/ITaskTracker"; import { ITaskTracker } from "../PersonObjects/ITaskTracker";
import { IRouter } from "../ui/Router"; import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { Contract } from "./Contract";
import { Operation } from "./Operation";
export interface IBladeburner { export interface IBladeburner {
numHosp: number; numHosp: number;
@ -31,17 +33,23 @@ export interface IBladeburner {
action: IActionIdentifier; action: IActionIdentifier;
cities: any; cities: Record<string, City>;
city: string; city: string;
skills: any; skills: Record<string, number>;
skillMultipliers: any; skillMultipliers: Record<string, number>;
staminaBonus: number; staminaBonus: number;
maxStamina: number; maxStamina: number;
stamina: number; stamina: number;
contracts: any; contracts: Record<string, Contract>;
operations: any; operations: Record<string, Operation>;
blackops: any; blackops: Record<string, boolean>;
logging: any; logging: {
general: boolean;
contracts: boolean;
ops: boolean;
blackops: boolean;
events: boolean;
};
automateEnabled: boolean; automateEnabled: boolean;
automateActionHigh: IActionIdentifier; automateActionHigh: IActionIdentifier;
automateThreshHigh: number; automateThreshHigh: number;

@ -1,7 +1,7 @@
import { IBladeburner } from "./IBladeburner"; import { IBladeburner } from "./IBladeburner";
import { BladeburnerConstants } from "./data/Constants"; import { BladeburnerConstants } from "./data/Constants";
import { Action, IActionParams } from "./Action"; import { Action, IActionParams } from "./Action";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
export interface IOperationParams extends IActionParams { export interface IOperationParams extends IActionParams {
reqdRank?: number; reqdRank?: number;
@ -44,12 +44,11 @@ export class Operation extends Action {
return 1; return 1;
} }
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Operation", this); return Generic_toJSON("Operation", this);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Operation {
static fromJSON(value: any): Operation {
return Generic_fromJSON(Operation, value.data); return Generic_fromJSON(Operation, value.data);
} }
} }

@ -14,7 +14,7 @@ import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
interface ILineProps { interface ILineProps {
content: any; content: React.ReactNode;
} }
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
@ -197,7 +197,7 @@ function Logs({ entries }: ILogProps): React.ReactElement {
return ( return (
<List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}> <List sx={{ height: "100%", overflow: "auto", p: 1 }} ref={scrollHook}>
{entries && entries.map((log: any, i: number) => <Line key={i} content={log} />)} {entries && entries.map((log: string, i: number) => <Line key={i} content={log} />)}
</List> </List>
); );
} }

@ -9,9 +9,10 @@ import Box from "@mui/material/Box";
import Paper from "@mui/material/Paper"; import Paper from "@mui/material/Paper";
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { Skill } from "../Skill";
interface IProps { interface IProps {
skill: any; skill: Skill;
bladeburner: IBladeburner; bladeburner: IBladeburner;
onUpgrade: () => void; onUpgrade: () => void;
} }

@ -12,8 +12,8 @@ export function SkillPage(props: IProps): React.ReactElement {
const setRerender = useState(false)[1]; const setRerender = useState(false)[1];
const mults = props.bladeburner.skillMultipliers; const mults = props.bladeburner.skillMultipliers;
function valid(mult: any): boolean { function valid(mult: number | undefined): boolean {
return mult && mult !== 1; return mult !== undefined && mult !== 1;
} }
return ( return (

@ -33,7 +33,7 @@ export function StartButton(props: IProps): React.ReactElement {
if (disabled) return; if (disabled) return;
props.bladeburner.action.type = props.type; props.bladeburner.action.type = props.type;
props.bladeburner.action.name = props.name; props.bladeburner.action.name = props.name;
if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) player.singularityStopWork(); if (!player.hasAugmentation(AugmentationNames.BladesSimulacrum, true)) player.finishWork(true);
props.bladeburner.startAction(player, props.bladeburner.action); props.bladeburner.startAction(player, props.bladeburner.action);
props.rerender(); props.rerender();
} }

@ -173,13 +173,13 @@ export function Stats(props: IProps): React.ReactElement {
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography> <Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
<br /> <br />
<Typography> <Typography>
Aug. Success Chance mult: {formatNumber(props.player.bladeburner_success_chance_mult * 100, 1)}% Aug. Success Chance mult: {formatNumber(props.player.mults.bladeburner_success_chance * 100, 1)}%
<br /> <br />
Aug. Max Stamina mult: {formatNumber(props.player.bladeburner_max_stamina_mult * 100, 1)}% Aug. Max Stamina mult: {formatNumber(props.player.mults.bladeburner_max_stamina * 100, 1)}%
<br /> <br />
Aug. Stamina Gain mult: {formatNumber(props.player.bladeburner_stamina_gain_mult * 100, 1)}% Aug. Stamina Gain mult: {formatNumber(props.player.mults.bladeburner_stamina_gain * 100, 1)}%
<br /> <br />
Aug. Field Analysis mult: {formatNumber(props.player.bladeburner_analysis_mult * 100, 1)}% Aug. Field Analysis mult: {formatNumber(props.player.mults.bladeburner_analysis * 100, 1)}%
</Typography> </Typography>
</Box> </Box>
</Paper> </Paper>

@ -1,7 +1,7 @@
import * as React from "react"; import * as React from "react";
export function trusted(f: () => void): (event: React.MouseEvent<HTMLElement, MouseEvent>) => any { export function trusted(f: () => void): (event: React.MouseEvent<HTMLElement, MouseEvent>) => void {
return function (event: React.MouseEvent<HTMLElement, MouseEvent>): any { return function (event: React.MouseEvent<HTMLElement, MouseEvent>): void {
if (!event.isTrusted) return; if (!event.isTrusted) return;
f(); f();
}; };

@ -2,7 +2,7 @@ import { codingContractTypesMetadata, DescriptionFunc, GeneratorFunc, SolverFunc
import { IMap } from "./types"; import { IMap } from "./types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "./utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "./utils/JSONReviver";
import { CodingContractEvent } from "./ui/React/CodingContractModal"; import { CodingContractEvent } from "./ui/React/CodingContractModal";
/* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */ /* tslint:disable:no-magic-numbers completed-docs max-classes-per-file no-console */
@ -106,7 +106,7 @@ export interface ICodingContractReward {
*/ */
export class CodingContract { export class CodingContract {
/* Relevant data for the contract's problem */ /* Relevant data for the contract's problem */
data: any; data: unknown;
/* Contract's filename */ /* Contract's filename */
fn: string; fn: string;
@ -137,7 +137,7 @@ export class CodingContract {
this.reward = reward; this.reward = reward;
} }
getData(): any { getData(): unknown {
return this.data; return this.data;
} }
@ -186,15 +186,14 @@ export class CodingContract {
/** /**
* Serialize the current file to a JSON save state. * Serialize the current file to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("CodingContract", this); return Generic_toJSON("CodingContract", this);
} }
/** /**
* Initiatizes a CodingContract from a JSON save state. * Initiatizes a CodingContract from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): CodingContract {
static fromJSON(value: any): CodingContract {
return Generic_fromJSON(CodingContract, value.data); return Generic_fromJSON(CodingContract, value.data);
} }
} }

@ -4,7 +4,7 @@ import { favorToRep, repToFavor } from "../Faction/formulas/favor";
import { IMap } from "../types"; import { IMap } from "../types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
export interface IConstructorParams { export interface IConstructorParams {
name: string; name: string;
@ -151,15 +151,14 @@ export class Company {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Company", this); return Generic_toJSON("Company", this);
} }
/** /**
* Initiatizes a Company from a JSON save state. * Initiatizes a Company from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Company {
static fromJSON(value: any): Company {
return Generic_fromJSON(Company, value.data); return Generic_fromJSON(Company, value.data);
} }
} }

@ -88,8 +88,8 @@ export const CONSTANTS: {
Donations: number; // number of blood/plasma/palette donation the dev have verified., boosts NFG Donations: number; // number of blood/plasma/palette donation the dev have verified., boosts NFG
LatestUpdate: string; LatestUpdate: string;
} = { } = {
VersionString: "1.7.0", VersionString: "2.0.0",
VersionNumber: 19, VersionNumber: 21,
// Speed (in ms) at which the main loop is updated // Speed (in ms) at which the main loop is updated
_idleSpeed: 200, _idleSpeed: 200,
@ -104,7 +104,7 @@ export const CONSTANTS: {
MilliPerCycle: 200, MilliPerCycle: 200,
// How much reputation is needed to join a megacorporation's faction // How much reputation is needed to join a megacorporation's faction
CorpFactionRepRequirement: 200e3, CorpFactionRepRequirement: 400e3,
// Base RAM costs // Base RAM costs
BaseCostFor1GBOfRamHome: 32000, BaseCostFor1GBOfRamHome: 32000,
@ -229,171 +229,54 @@ export const CONSTANTS: {
InfiniteLoopLimit: 2000, InfiniteLoopLimit: 2000,
Donations: 20, Donations: 21,
LatestUpdate: ` LatestUpdate: `
## [draft] v1.7.0 - 2022-04-13 to 2022-05-20 v2.0.0 - 2022-07-19 Work rework
-------------------------------
#### Information API break rewards
Modifications included between **2022-04-13** and **2022-05-20** 'b5e4d70' to '0fbe4a1'). * Everyone is awarded 10 NFG.
* All work in progress program is auto completed.
* All work in progress crafting is auto completed without adding entropy.
_[See Pull Requests on GitHub](https://github.com/search?q=user%3Adanielyxie%20repo%3Abitburner%20is%3Apr%20is%3Amerged%20merged%3A%222022-04-13T16%3A32%3A26.000Z..2022-05-20T06%3A08%3A51.000Z%22)_ Work (Create program / Work for faction / Studying / etc ...)
#### Merged Pull Requests * Working has been rebuilt from the grounds up. The motivation for that change is that all
different types of work all required different cached variables on the main Player object.
This caused a lot of bugs and crashes. It's been reworked in such a way as to prevent bugs
and make it nearly trivial to add new kinds of work. However, since this caused a few API break
I've decided to mark this version following semver protocols and call it 2.0.0
* Crime can be unfocused and auto loops, no more spam clicking.
* All work type give their reward immediately. No need to stop work to bank rewards like reputation.
* Faction and Company work no longer have a time limit.
* Company work no longer reduces rep gain by half for quitting early.
* Company faction require 400k rep to join (from 200k)
* Backdooring company server reduces faction requirement to 300k.
* All work generally no longer keep track of cumulative gains like exp and reputation since it's applied instantly.
* getPlayer returns way less fields but does return the new 'currentWork' field.
- [Feature] Monaco Theme Editor (by @nickofolas) #[3438](https://github.com/danielyxie/bitburner/pull/3438) API breaks
- [Fix] Dummy Stanek grid width (by @nickofolas) #[3442](https://github.com/danielyxie/bitburner/pull/3442)
- [Fix] Theme browser assets not loading (by @nickofolas) #[3446](https://github.com/danielyxie/bitburner/pull/3446)
- Accept valid JSON arrays in coding contracts (by @Savlik) #[3247](https://github.com/danielyxie/bitburner/pull/3247)
- another dark theme? (by @hydroflame) #[3450](https://github.com/danielyxie/bitburner/pull/3450)
- API: Add repFromDonation() to the Formula API (by @Hoekstraa) #[3461](https://github.com/danielyxie/bitburner/pull/3461)
- API: Add safeguard to ns.killall(), preventing killing itself by default (by @Hoekstraa) #[3607](https://github.com/danielyxie/bitburner/pull/3607)
- API: FIX #2993 sleeve.travel with invalid city names (by @TheMas3212) #[3458](https://github.com/danielyxie/bitburner/pull/3458)
- API: Fix inconsistent return value in 'ns.grafting.getAugmentationGraftTime' (by @nickofolas) #[3539](https://github.com/danielyxie/bitburner/pull/3539)
- API: Fix leak of real Employee object in hireEmployee (by @TheMas3212) #[3483](https://github.com/danielyxie/bitburner/pull/3483)
- API: replace a number of references to workerscript.log with \_ctx.log (by @TheMas3212) #[3470](https://github.com/danielyxie/bitburner/pull/3470)
- API: Terminal screen can now be cleared from within scripts with ns.ui.clearTerminal() (by @Hoekstraa) #[3618](https://github.com/danielyxie/bitburner/pull/3618)
- AUGMENTATIONS: Fix 'isSpecial' filter in helper (Removes NeuroFlux, Stanek's Gift, etc from gangs) (by @nickofolas) #[3565](https://github.com/danielyxie/bitburner/pull/3565)
- AUGMENTATIONS: Fix Augmentation rep req not being properly influenced by BitNode multipliers (by @nickofolas) #[3652](https://github.com/danielyxie/bitburner/pull/3652)
- AUGMENTATIONS: Fix NeuroFlux being applied improperly and migrate broken saves (by @nickofolas) #[3613](https://github.com/danielyxie/bitburner/pull/3613)
- AUGMENTATIONS: Fix reputation check for faction augs (by @nickofolas) #[3609](https://github.com/danielyxie/bitburner/pull/3609)
- AUGMENTATIONS: Tweak a couple small UI elements (by @nickofolas) #[3614](https://github.com/danielyxie/bitburner/pull/3614)
- basic doc no longer hacker themed (by @hydroflame) #[3449](https://github.com/danielyxie/bitburner/pull/3449)
- BITNODE: FIX #3546 BitVerse now shows proper BN level when accessed via flume (by @nickofolas) #[3550](https://github.com/danielyxie/bitburner/pull/3550)
- BLADEBURNER: fixes #3648 : Automate console command capitalisation inconsistent (by @Vic1970) #[3647](https://github.com/danielyxie/bitburner/pull/3647)
- BLADEBURNER: Fix #3594 Blade's Simulacrum worked without being installed (by @Undeemiss) #[3639](https://github.com/danielyxie/bitburner/pull/3639)
- blood (by @hydroflame) #[3495](https://github.com/danielyxie/bitburner/pull/3495)
- BUGFIX: getAugmentationCost response backwards (by @phyzical) #[3617](https://github.com/danielyxie/bitburner/pull/3617)
- BUGFIX: Handle edge case in LZ compression code and fix docs (by @stalefishies) #[3581](https://github.com/danielyxie/bitburner/pull/3581)
- BUGFIX: make bonustime for gang in miliseconds (by @phyzical) #[3578](https://github.com/danielyxie/bitburner/pull/3578)
- BUGFIX: sleeve stale object refence during augmentation (by @phyzical) #[3601](https://github.com/danielyxie/bitburner/pull/3601)
- Bugfix/corp updates (by @phyzical) #[3321](https://github.com/danielyxie/bitburner/pull/3321)
- Bump async from 2.6.3 to 2.6.4 (by @dependabot[bot]) #[3463](https://github.com/danielyxie/bitburner/pull/3463)
- CODINGCONTRACT: Fix #3391 Double contract reward exploit (by @Undeemiss) #[3646](https://github.com/danielyxie/bitburner/pull/3646)
- CODINGCONTRACT: FIX #3484 BREAKING Fixed capitalization in contract name (by @Undeemiss) #[3537](https://github.com/danielyxie/bitburner/pull/3537)
- CODINGCONTRACT: New "Proper 2-Coloring of a Graph" contract (by @Undeemiss) #[3530](https://github.com/danielyxie/bitburner/pull/3530)
- CODINGCONTRACT: Three new compression contracts (by @stalefishies) #[3541](https://github.com/danielyxie/bitburner/pull/3541)
- CODINGCONTRACT: Typo & clarity fixes to description of Encoded Binary to Integer contract (by @ActuallyCurtis) #[3469](https://github.com/danielyxie/bitburner/pull/3469)
- CODINGCONTRACT: Updated description of 2-coloring contract (by @Undeemiss) #[3531](https://github.com/danielyxie/bitburner/pull/3531)
- COMPANY: Fix #3551 Applying for a new job will not change active employer if player is performing company work (by @Snarling) #[3552](https://github.com/danielyxie/bitburner/pull/3552)
- CORPORATIONS: Expose makeProducts on NSDivision interface (by @DavidGrinberg) #[3570](https://github.com/danielyxie/bitburner/pull/3570)
- CORPORATIONS: Expose sales cost on NSMaterial interface (by @DavidGrinberg) #[3574](https://github.com/danielyxie/bitburner/pull/3574)
- Corrected example grids found in Stanek help (by @Undeemiss) #[3441](https://github.com/danielyxie/bitburner/pull/3441)
- Create program action no longer creates duplicates (by @Undeemiss) #[3436](https://github.com/danielyxie/bitburner/pull/3436)
- DOCUMENTATION: Add descriptions for compression contracts (by @stalefishies) #[3559](https://github.com/danielyxie/bitburner/pull/3559)
- DOCUMENTATION: Add new coding contract descriptions (by @stalefishies) #[3542](https://github.com/danielyxie/bitburner/pull/3542)
- DOCUMENTATION: Clarify definition for installAugmentations() (by @PSEUDOSTAGE) #[3560](https://github.com/danielyxie/bitburner/pull/3560)
- DOCUMENTATION: FIX #3516 "cannot" misspelled as "cannnot" (by @Undeemiss) #[3533](https://github.com/danielyxie/bitburner/pull/3533)
- EDITOR: FIX #3502 Editor theme migration crash (by @nickofolas) #[3503](https://github.com/danielyxie/bitburner/pull/3503)
- FEATURE: added logic to allow quitJob to be called from singularity (by @phyzical) #[3577](https://github.com/danielyxie/bitburner/pull/3577)
- fix #3395 donating to special factions possible via singularity (by @TheMas3212) #[3456](https://github.com/danielyxie/bitburner/pull/3456)
- fix b1tflum3 and destroyW0r1dD43m0n singularity functions to check for sf4 (by @TheMas3212) #[3443](https://github.com/danielyxie/bitburner/pull/3443)
- Fix inconsistancy with trying to work for gang factions while running a gang (by @TheMas3212) #[3454](https://github.com/danielyxie/bitburner/pull/3454)
- Fix infiltration rep BN mult calculation (by @trambelus) #[3632](https://github.com/danielyxie/bitburner/pull/3632)
- Fix script editor settings. (by @hydroflame) #[3504](https://github.com/danielyxie/bitburner/pull/3504)
- Fix test/jest/Netscript/DynamicRamCalculation.test.js (by @TheMas3212) #[3455](https://github.com/danielyxie/bitburner/pull/3455)
- GRAFTING: Fix Grafting not being handled in singularity stop work (by @nickofolas) #[3568](https://github.com/danielyxie/bitburner/pull/3568)
- GRAFTING: Implement sorting options (by @nickofolas) #[3654](https://github.com/danielyxie/bitburner/pull/3654)
- INFILTRATION: Added new faction called infiltrators that provide infiltration specific augs. (by @phyzical) #[3241](https://github.com/danielyxie/bitburner/pull/3241)
- INFILTRATION: Fix minigame cycle (by @nickofolas) #[3549](https://github.com/danielyxie/bitburner/pull/3549)
- INFILTRATION: Fix phyzical WKS aug effects being applied before aug is installed (by @nickofolas) #[3555](https://github.com/danielyxie/bitburner/pull/3555)
- INFILTRATION: Fix rep reward being substantially higher than intended (by @nickofolas) #[3562](https://github.com/danielyxie/bitburner/pull/3562)
- INFILTRATION: New faction, Shadows of Anarchy, provides various augs to help infiltrations. (by @hydroflame) #[3543](https://github.com/danielyxie/bitburner/pull/3543)
- INFILTRATION: Update gameplay UI (by @nickofolas) #[3587](https://github.com/danielyxie/bitburner/pull/3587)
- keeping up to date (by @hydroflame) #[3432](https://github.com/danielyxie/bitburner/pull/3432)
- Keeping up to date. (by @hydroflame) #[3561](https://github.com/danielyxie/bitburner/pull/3561)
- Make .lit and .msg files clickable (by @Chris380) #[3453](https://github.com/danielyxie/bitburner/pull/3453)
- MESSAGES: Added the name of NiteSec's server to their .msg (by @Undeemiss) #[3466](https://github.com/danielyxie/bitburner/pull/3466)
- MISC: add better typing to Electron.tsx (by @taralx) #[3540](https://github.com/danielyxie/bitburner/pull/3540)
- MISC: Added NS function closeTail to close tail windows (by @Undeemiss) #[3666](https://github.com/danielyxie/bitburner/pull/3666)
- MISC: Adjust deps to current usage (by @taralx) #[3519](https://github.com/danielyxie/bitburner/pull/3519)
- MISC: Close some GitHub issues that do not need action (by @Undeemiss) #[3640](https://github.com/danielyxie/bitburner/pull/3640)
- MISC: Closing more GitHub issues I missed last time (by @Undeemiss) #[3665](https://github.com/danielyxie/bitburner/pull/3665)
- MISC: Correct BB Skill point achievement name (by @Undeemiss) #[3571](https://github.com/danielyxie/bitburner/pull/3571)
- MISC: Correct typos in getScriptRam docs. (by @nzdjb) #[3590](https://github.com/danielyxie/bitburner/pull/3590)
- MISC: Fix #3125 BREAKING Renamed BN mult CorporationSoftCap to CorporationSoftcap (by @Undeemiss) #[3638](https://github.com/danielyxie/bitburner/pull/3638)
- MISC: FIX #3593 Float errors can no longer prevent full usage of a server's available ram. (by @Snarling) #[3619](https://github.com/danielyxie/bitburner/pull/3619)
- MISC: fix typing conflict between jest and cypress (by @taralx) #[3518](https://github.com/danielyxie/bitburner/pull/3518)
- MISC: fix typing conflict between jest and cypress (by @taralx) #[3644](https://github.com/danielyxie/bitburner/pull/3644)
- MISC: Fixed typo in exceptionAlert.ts (by @Undeemiss) #[3572](https://github.com/danielyxie/bitburner/pull/3572)
- MISC: Fixed typos in game options (by @notacompsciguy) #[3584](https://github.com/danielyxie/bitburner/pull/3584)
- MISC: HammingCodingContracts need rework (by @Hedrauta) #[3479](https://github.com/danielyxie/bitburner/pull/3479)
- MISC: Implemented infinite loop safety net. (by @hydroflame) #[3624](https://github.com/danielyxie/bitburner/pull/3624)
- MISC: make jQuery use explicit (by @taralx) #[3517](https://github.com/danielyxie/bitburner/pull/3517)
- MISC: Make tutorial explain ns1 vs ns2 better (by @hydroflame) #[3586](https://github.com/danielyxie/bitburner/pull/3586)
- MISC: Remove comments that describe nonexistent augs (by @Undeemiss) #[3569](https://github.com/danielyxie/bitburner/pull/3569)
- MISC: update @types/numeral and fix type errors (by @taralx) #[3521](https://github.com/danielyxie/bitburner/pull/3521)
- MISC: Update logic for stats page BitNode level (by @nickofolas) #[3512](https://github.com/danielyxie/bitburner/pull/3512)
- MISC: upgrade to eslint v8 (by @taralx) #[3523](https://github.com/danielyxie/bitburner/pull/3523)
- MISC: Wrap most of the API in the new api wrapper (by @hydroflame) #[3627](https://github.com/danielyxie/bitburner/pull/3627)
- OPTIONS: Fix sliders not sliding correctly (by @nickofolas) #[3642](https://github.com/danielyxie/bitburner/pull/3642)
- REFACTOR: augmentation cost, rep cost and level to be calculated in place (by @phyzical) #[3544](https://github.com/danielyxie/bitburner/pull/3544)
- REFACTOR: augmentation isSpecial adjustments (by @phyzical) #[3564](https://github.com/danielyxie/bitburner/pull/3564)
- Reran npm format and lint to fix formatting (by @Undeemiss) #[3434](https://github.com/danielyxie/bitburner/pull/3434)
- Revert "MISC: fix typing conflict between jest and cypress" (by @hydroflame) #[3608](https://github.com/danielyxie/bitburner/pull/3608)
- Revert "MISC: HammingCodingContracts need rework" (by @hydroflame) #[3500](https://github.com/danielyxie/bitburner/pull/3500)
- revert theme (by @hydroflame) #[3451](https://github.com/danielyxie/bitburner/pull/3451)
- Singularity: Fix #3489 Disable checkTixApiAccess for purchase4SMarketData (by @DavidGrinberg) #[3490](https://github.com/danielyxie/bitburner/pull/3490)
- SLEEVES: Fix issues with Sleeve UI crashing when Sleeve task faction becomes gang faction (by @nickofolas) #[3557](https://github.com/danielyxie/bitburner/pull/3557)
- STANEK: Fix #3196 Charging booster fragments throws an error (by @Undeemiss) #[3637](https://github.com/danielyxie/bitburner/pull/3637)
- STANEK: FIX #3277 Can no longer overlap rotated fragments (by @Undeemiss) #[3460](https://github.com/danielyxie/bitburner/pull/3460)
- STANEK: FIX #3282 Added NS function stanek.acceptGift (by @Undeemiss) #[3513](https://github.com/danielyxie/bitburner/pull/3513)
- STANEK: Properly reapply entropy in Stanek's Gift (by @nickofolas) #[3673](https://github.com/danielyxie/bitburner/pull/3673)
- STANEK: Stanek NS functions correctly throw errors when stanek not installed (by @Undeemiss) #[3660](https://github.com/danielyxie/bitburner/pull/3660)
- Started collecting lore so that additions to it are simpler (by @Undeemiss) #[3465](https://github.com/danielyxie/bitburner/pull/3465)
- TERMINAL: FIX #3492 Allow cd .. even when destination directory is empty (by @Snarling) #[3525](https://github.com/danielyxie/bitburner/pull/3525)
- TERMINAL: FIX #3651 Make directory name regex more flexible (by @Dane-Horn) #[3653](https://github.com/danielyxie/bitburner/pull/3653)
- TOOLING: Add GitHub action to validate PR titles (by @MartinFournier) #[3471](https://github.com/danielyxie/bitburner/pull/3471)
- UI FIX #3485 - Allow bulk purchasing when smart supply is enabled (by @phyzical) #[3486](https://github.com/danielyxie/bitburner/pull/3486)
- UI: Change text color of Augmentations page backup button (by @nickofolas) #[3511](https://github.com/danielyxie/bitburner/pull/3511)
- UI: FIX #1754 Stanek effect summary & slight tweak. (by @borisflagell) #[3622](https://github.com/danielyxie/bitburner/pull/3622)
- UI: FIX #2228,#2958 Fix tab highlights and highlight files not on home. (by @phyzical) #[2989](https://github.com/danielyxie/bitburner/pull/2989)
- UI: FIX #2256 Hacknet server's upgrade tooltip were not handling RAM (by @borisflagell) #[3532](https://github.com/danielyxie/bitburner/pull/3532)
- UI: FIX #2741 Allow using modifier keys inside the typing infiltration (by @Dane-Horn) #[3634](https://github.com/danielyxie/bitburner/pull/3634)
- UI: FIX #2829 Remove defeated NPC gangs from territory page (by @Dane-Horn) #[3633](https://github.com/danielyxie/bitburner/pull/3633)
- UI: FIX #3313 Streamline the GraftingRoot page by making it rerender. (by @borisflagell) #[3558](https://github.com/danielyxie/bitburner/pull/3558)
- UI: FIX #3341 Enable touch-clicks in react-draggable (by @Snarling) #[3488](https://github.com/danielyxie/bitburner/pull/3488)
- UI: FIX #3415 Tweak Manage Gang button visibility (by @borisflagell) #[3528](https://github.com/danielyxie/bitburner/pull/3528)
- UI: FIX #3457 autocomplete suggestions no longer require hovering terminal input (by @Snarling) #[3493](https://github.com/danielyxie/bitburner/pull/3493)
- UI: FIX #3473 'mv' now says destination script is running instead of returning an error (by @Hoekstraa) #[3474](https://github.com/danielyxie/bitburner/pull/3474)
- UI: FIX #3522 realigned autocomplete popup (by @Snarling) #[3524](https://github.com/danielyxie/bitburner/pull/3524)
- UI: FIX #3592 Sidebar and bash shortcuts now work on MacOS with US-like layouts (by @Hoekstraa) #[3605](https://github.com/danielyxie/bitburner/pull/3605)
- UI: Fix Agility BitNode multiplier not appearing in UI (by @nickofolas) #[3662](https://github.com/danielyxie/bitburner/pull/3662)
- UI: Fix exclusive augs not always showing as purchasable through gangs when they should (by @nickofolas) #[3676](https://github.com/danielyxie/bitburner/pull/3676)
- UI: Fix the achievement covenant icon was not shown (by @Risenafis) #[3510](https://github.com/danielyxie/bitburner/pull/3510)
- UI: Fix z-index of modals overriding everything (by @nickofolas) #[3620](https://github.com/danielyxie/bitburner/pull/3620)
- UI: lightweight description update on "increase maximum money" hash spending option. (by @borisflagell) #[3547](https://github.com/danielyxie/bitburner/pull/3547)
- UI: Minor improvements to log boxes (by @nickofolas) #[3641](https://github.com/danielyxie/bitburner/pull/3641)
- UI: Overhaul GameOptions UI (by @nickofolas) #[3505](https://github.com/danielyxie/bitburner/pull/3505)
- UI: Positioning improved for tail titlebar buttons, and tail window has minimum size constraints. (by @Snarling) #[3548](https://github.com/danielyxie/bitburner/pull/3548)
- UI: Redesign purchasable Augmentations (by @nickofolas) #[3545](https://github.com/danielyxie/bitburner/pull/3545)
- UI: Refactor and redesign WorkInProgress interface (by @nickofolas) #[3611](https://github.com/danielyxie/bitburner/pull/3611)
- UI: Refactors, redesigns, and new section to stats page (by @nickofolas) #[3626](https://github.com/danielyxie/bitburner/pull/3626)
- UI: Sort and color Graft Augmentation list (by @jaype87) #[3616](https://github.com/danielyxie/bitburner/pull/3616)
- UI: Update Factions list interface (by @nickofolas) #[3675](https://github.com/danielyxie/bitburner/pull/3675)
- WORK: FIX #3435 Quitting the active job now sets first remaining job as active (by @Snarling) #[3507](https://github.com/danielyxie/bitburner/pull/3507)
- WORK: Refactor work types to use 'enum's instead of constants (by @nickofolas) #[3612](https://github.com/danielyxie/bitburner/pull/3612)
#### Other Changes * workForCompany argument 'companyName' is now not-optional
* commitCrime now has 'focus' optional parameter
* using getScriptIncome to get total income has been separated to getTotalScriptIncome.
* using getScriptExpGain to get total income has been separated to getTotalScriptExpGain.
* scp has it's 2 last argument reversed, the signature is now (files, destination, optional_source)
* ns.connect and other singularity function are no longer available at the top level.
They were already hidden from documentation but now they're gone.
* stock.buy and stock.sell were renamed to stock.buyStock and stock.sellStock because 'buy' and 'sell'
are very common tokens.
- increase donation counter (by @hydroflame) - [8456410](https://github.com/danielyxie/bitburner/commit/84564100e90c46ae4b816853c2cdea0bc309af4d) Multipliers
- allbuild commit 7f9e3775 (by @hydroflame) - [791c19c](https://github.com/danielyxie/bitburner/commit/791c19c4fe447c9231bfb423b9fc48114e783b43)
- allbuild commit bcbda22a (by @hydroflame) - [032c440](https://github.com/danielyxie/bitburner/commit/032c440eaeb069eecd720ec2f8e069f705a0c1b4) * The main player object was also plagues with a million fields all called '*_mult'. Representing the different multipliers
- fix documentation for getDarkwebPrograms (by @hydroflame) - [4056956](https://github.com/danielyxie/bitburner/commit/4056956c2ada37946333bdad44cb0b6eb3909bf8) * These have been refactored in a field called 'mults'.
- support ASNI (by @hydroflame) - [36c7ef1](https://github.com/danielyxie/bitburner/commit/36c7ef1ad7ea8bb69fca23bce5883a3c2e23f1e0)
- allbuild commit 22b6d0d5 (by @hydroflame) - [b46718d](https://github.com/danielyxie/bitburner/commit/b46718d188880ecf716ae045861d81d61e00af4b) Misc.
- allbuild commit 36c7ef1a (by @hydroflame) - [d0ebf5e](https://github.com/danielyxie/bitburner/commit/d0ebf5e14e0498cb063fde35d63c9f59f2c01e35)
- Update documentation for employee (by @hydroflame) - [100e81c](https://github.com/danielyxie/bitburner/commit/100e81c8ab4a408f74cc9bd9ffe2b8bad3d03462) * Nerf noodle bar, obviously.
- allbuild commit c799b291 (by @hydroflame) - [f5f5879](https://github.com/danielyxie/bitburner/commit/f5f5879fc380678d978e2b0a29ba7b6f0b4c9ec0)
- ideas (by @hydroflame) - [0121fee](https://github.com/danielyxie/bitburner/commit/0121fee6e4c690d01650d1e68a80ea363bb48bce)
- allbuild commit 0121fee6 (by @hydroflame) - [5c417e9](https://github.com/danielyxie/bitburner/commit/5c417e9b4df236df8bf3e2f8262b7bce87c934df)
- Update codebase for stanek (by @hydroflame) - [c2b4a5b](https://github.com/danielyxie/bitburner/commit/c2b4a5b52a2162d2e49c7317b0a60a349984eb47)
- fix lint (by @hydroflame) - [4cc518f](https://github.com/danielyxie/bitburner/commit/4cc518f37723aafb3168b64cd689408afdb74877)
- Fix (by @hydroflame) - [9af553f](https://github.com/danielyxie/bitburner/commit/9af553f63cb1380795550648b0134b608564fab8)
- Fix stanek leaking classes (by @hydroflame) - [fda3f02](https://github.com/danielyxie/bitburner/commit/fda3f02d73dba27034128c9be5e810a51e475e38)
- fix conflicts (by @hydroflame) - [ca1a2aa](https://github.com/danielyxie/bitburner/commit/ca1a2aad333fa838b6d0e57f89e1cedba086a4a0)
- Nerf noodle bar.
`, `,
}; };

@ -11,7 +11,7 @@ import { LiteratureNames } from "../Literature/data/LiteratureNames";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { isString } from "../utils/helpers/isString"; import { isString } from "../utils/helpers/isString";
interface IParams { interface IParams {
@ -433,15 +433,14 @@ export class Corporation {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Corporation", this); return Generic_toJSON("Corporation", this);
} }
/** /**
* Initiatizes a Corporation object from a JSON save state. * Initiatizes a Corporation object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Corporation {
static fromJSON(value: any): Corporation {
return Generic_fromJSON(Corporation, value.data); return Generic_fromJSON(Corporation, value.data);
} }
} }

@ -1,4 +1,4 @@
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
// Array of all valid states // Array of all valid states
const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"]; const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
@ -28,13 +28,12 @@ export class CorporationState {
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("CorporationState", this); return Generic_toJSON("CorporationState", this);
} }
// Initiatizes a CorporationState object from a JSON save state. // Initiatizes a CorporationState object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): CorporationState {
static fromJSON(value: any): CorporationState {
return Generic_fromJSON(CorporationState, value.data); return Generic_fromJSON(CorporationState, value.data);
} }
} }

@ -1,6 +1,6 @@
import { CorporationConstants } from "./data/Constants"; import { CorporationConstants } from "./data/Constants";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { EmployeePositions } from "./EmployeePositions"; import { EmployeePositions } from "./EmployeePositions";
import { ICorporation } from "./ICorporation"; import { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry"; import { IIndustry } from "./IIndustry";
@ -113,12 +113,11 @@ export class Employee {
return prodBase * prodMult; return prodBase * prodMult;
} }
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Employee", this); return Generic_toJSON("Employee", this);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Employee {
static fromJSON(value: any): Employee {
return Generic_fromJSON(Employee, value.data); return Generic_fromJSON(Employee, value.data);
} }
} }

@ -3,6 +3,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
import { CorporationUnlockUpgrade } from "./data/CorporationUnlockUpgrades"; import { CorporationUnlockUpgrade } from "./data/CorporationUnlockUpgrades";
import { CorporationUpgrade } from "./data/CorporationUpgrades"; import { CorporationUpgrade } from "./data/CorporationUpgrades";
import { CorporationState } from "./CorporationState"; import { CorporationState } from "./CorporationState";
import { IReviverValue } from "../utils/JSONReviver";
export interface ICorporation { export interface ICorporation {
name: string; name: string;
@ -56,5 +57,5 @@ export interface ICorporation {
getStarterGuide(player: IPlayer): void; getStarterGuide(player: IPlayer): void;
updateDividendTax(): void; updateDividendTax(): void;
getCycleDividends(): number; getCycleDividends(): number;
toJSON(): any; toJSON(): IReviverValue;
} }

@ -3,6 +3,7 @@ import { Warehouse } from "./Warehouse";
import { ICorporation } from "./ICorporation"; import { ICorporation } from "./ICorporation";
import { OfficeSpace } from "./OfficeSpace"; import { OfficeSpace } from "./OfficeSpace";
import { Product } from "./Product"; import { Product } from "./Product";
import { IReviverValue } from "../utils/JSONReviver";
export interface IIndustry { export interface IIndustry {
name: string; name: string;
@ -72,5 +73,5 @@ export interface IIndustry {
getSalesMultiplier(): number; getSalesMultiplier(): number;
getScientificResearchMultiplier(): number; getScientificResearchMultiplier(): number;
getStorageMultiplier(): number; getStorageMultiplier(): number;
toJSON(): any; toJSON(): IReviverValue;
} }

@ -1,4 +1,4 @@
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { CityName } from "../Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
import { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData"; import { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { CorporationConstants } from "./data/Constants"; import { CorporationConstants } from "./data/Constants";
@ -1403,15 +1403,14 @@ export class Industry implements IIndustry {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Industry", this); return Generic_toJSON("Industry", this);
} }
/** /**
* Initiatizes a Industry object from a JSON save state. * Initiatizes a Industry object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Industry {
static fromJSON(value: any): Industry {
return Generic_fromJSON(Industry, value.data); return Generic_fromJSON(Industry, value.data);
} }
} }

@ -1,4 +1,4 @@
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { Export } from "./Export"; import { Export } from "./Export";
interface IConstructorParams { interface IConstructorParams {
@ -229,13 +229,12 @@ export class Material {
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Material", this); return Generic_toJSON("Material", this);
} }
// Initiatizes a Material object from a JSON save state. // Initiatizes a Material object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Material {
static fromJSON(value: any): Material {
return Generic_fromJSON(Material, value.data); return Generic_fromJSON(Material, value.data);
} }
} }

@ -2,7 +2,7 @@ import { EmployeePositions } from "./EmployeePositions";
import { CorporationConstants } from "./data/Constants"; import { CorporationConstants } from "./data/Constants";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
import { generateRandomString } from "../utils/StringHelperFunctions"; import { generateRandomString } from "../utils/StringHelperFunctions";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { Employee } from "./Employee"; import { Employee } from "./Employee";
import { IIndustry } from "./IIndustry"; import { IIndustry } from "./IIndustry";
import { ICorporation } from "./ICorporation"; import { ICorporation } from "./ICorporation";
@ -277,12 +277,11 @@ export class OfficeSpace {
return false; return false;
} }
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("OfficeSpace", this); return Generic_toJSON("OfficeSpace", this);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): OfficeSpace {
static fromJSON(value: any): OfficeSpace {
return Generic_fromJSON(OfficeSpace, value.data); return Generic_fromJSON(OfficeSpace, value.data);
} }
} }

@ -6,7 +6,7 @@ import { ProductRatingWeights, IProductRatingWeight } from "./ProductRatingWeigh
import { createCityMap } from "../Locations/createCityMap"; import { createCityMap } from "../Locations/createCityMap";
import { IMap } from "../types"; import { IMap } from "../types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
interface IConstructorParams { interface IConstructorParams {
@ -265,13 +265,12 @@ export class Product {
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Product", this); return Generic_toJSON("Product", this);
} }
// Initiatizes a Product object from a JSON save state. // Initiatizes a Product object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Product {
static fromJSON(value: any): Product {
return Generic_fromJSON(Product, value.data); return Generic_fromJSON(Product, value.data);
} }
} }

@ -8,8 +8,6 @@ import { ResearchMap } from "./ResearchMap";
import { IMap } from "../types"; import { IMap } from "../types";
import { numeralWrapper } from "../ui/numeralFormat";
interface IConstructorParams { interface IConstructorParams {
children?: Node[]; children?: Node[];
cost: number; cost: number;
@ -60,40 +58,6 @@ export class Node {
n.parent = this; n.parent = this;
} }
// Return an object that describes a TreantJS-compatible markup/config for this Node
// See: http://fperucic.github.io/treant-js/
createTreantMarkup(): any {
const childrenArray = [];
for (let i = 0; i < this.children.length; ++i) {
childrenArray.push(this.children[i].createTreantMarkup());
}
// Determine what css class this Node should have in the diagram
let htmlClass = "tooltip";
if (this.researched) {
htmlClass += " researched";
} else if (this.parent && this.parent.researched === false) {
htmlClass += " locked";
} else {
htmlClass += " unlocked";
}
const research: Research | null = ResearchMap[this.text];
const sanitizedName: string = this.text.replace(/\s/g, "");
return {
children: childrenArray,
HTMLclass: htmlClass,
innerHTML:
`<div id="${sanitizedName}-corp-research-click-listener">` +
`${this.text}<br>${numeralWrapper.format(this.cost, "0,0")} Scientific Research` +
`<span class="tooltiptext">` +
`${research.desc}` +
`</span>` +
`</div>`,
text: { name: this.text },
};
}
// Recursive function for finding a Node with the specified text // Recursive function for finding a Node with the specified text
findNode(text: string): Node | null { findNode(text: string): Node | null {
// Is this the Node? // Is this the Node?
@ -127,23 +91,6 @@ export class ResearchTree {
// Root Node // Root Node
root: Node | null = null; root: Node | null = null;
// Return an object that contains a Tree markup for TreantJS (using the JSON approach)
// See: http://fperucic.github.io/treant-js/
createTreantMarkup(): any {
if (this.root == null) {
return {};
}
const treeMarkup = this.root.createTreantMarkup();
return {
chart: {
container: "",
},
nodeStructure: treeMarkup,
};
}
// Gets an array with the 'text' values of ALL Nodes in the Research Tree // Gets an array with the 'text' values of ALL Nodes in the Research Tree
getAllNodes(): string[] { getAllNodes(): string[] {
const res: string[] = []; const res: string[] = [];
@ -236,7 +183,20 @@ export class ResearchTree {
continue; continue;
} }
const mult: any = (research as any)[propName]; const mult =
{
advertisingMult: research.advertisingMult,
employeeChaMult: research.employeeChaMult,
employeeCreMult: research.employeeCreMult,
employeeEffMult: research.employeeEffMult,
employeeIntMult: research.employeeIntMult,
productionMult: research.productionMult,
productProductionMult: research.productProductionMult,
salesMult: research.salesMult,
sciResearchMult: research.sciResearchMult,
storageMult: research.storageMult,
}[propName] ?? null;
if (mult == null) { if (mult == null) {
console.warn(`Invalid propName specified in ResearchTree.getMultiplierHelper: ${propName}`); console.warn(`Invalid propName specified in ResearchTree.getMultiplierHelper: ${propName}`);
continue; continue;

@ -3,7 +3,7 @@ import { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry"; import { IIndustry } from "./IIndustry";
import { MaterialSizes } from "./MaterialSizes"; import { MaterialSizes } from "./MaterialSizes";
import { IMap } from "../types"; import { IMap } from "../types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
interface IConstructorParams { interface IConstructorParams {
@ -99,19 +99,18 @@ export class Warehouse {
updateSize(corporation: ICorporation, industry: IIndustry): void { updateSize(corporation: ICorporation, industry: IIndustry): void {
try { try {
this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier(); this.size = this.level * 100 * corporation.getStorageMultiplier() * industry.getStorageMultiplier();
} catch (e: any) { } catch (e: unknown) {
exceptionAlert(e); exceptionAlert(e);
} }
} }
// Serialize the current object to a JSON save state. // Serialize the current object to a JSON save state.
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Warehouse", this); return Generic_toJSON("Warehouse", this);
} }
// Initiatizes a Warehouse object from a JSON save state. // Initiatizes a Warehouse object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Warehouse {
static fromJSON(value: any): Warehouse {
return Generic_fromJSON(Warehouse, value.data); return Generic_fromJSON(Warehouse, value.data);
} }
} }

@ -124,8 +124,8 @@ export function IndustryOverview(props: IProps): React.ReactElement {
<br /> <br />
<StatsTable <StatsTable
rows={[ rows={[
["Awareness:", numeralWrapper.format(division.awareness, "0.000")], ["Awareness:", numeralWrapper.formatReallyBigNumber(division.awareness)],
["Popularity:", numeralWrapper.format(division.popularity, "0.000")], ["Popularity:", numeralWrapper.formatReallyBigNumber(division.popularity)],
]} ]}
/> />
{advertisingInfo !== false && ( {advertisingInfo !== false && (
@ -135,15 +135,15 @@ export function IndustryOverview(props: IProps): React.ReactElement {
<Typography>Total multiplier for this industrys sales due to its awareness and popularity</Typography> <Typography>Total multiplier for this industrys sales due to its awareness and popularity</Typography>
<StatsTable <StatsTable
rows={[ rows={[
["Awareness Bonus:", "x" + numeralWrapper.format(Math.pow(awarenessFac, 0.85), "0.000")], ["Awareness Bonus:", "x" + numeralWrapper.formatReallyBigNumber(Math.pow(awarenessFac, 0.85))],
["Popularity Bonus:", "x" + numeralWrapper.format(Math.pow(popularityFac, 0.85), "0.000")], ["Popularity Bonus:", "x" + numeralWrapper.formatReallyBigNumber(Math.pow(popularityFac, 0.85))],
["Ratio Multiplier:", "x" + numeralWrapper.format(Math.pow(ratioFac, 0.85), "0.000")], ["Ratio Multiplier:", "x" + numeralWrapper.formatReallyBigNumber(Math.pow(ratioFac, 0.85))],
]} ]}
/> />
</> </>
} }
> >
<Typography>Advertising Multiplier: x{numeralWrapper.format(totalAdvertisingFac, "0.000")}</Typography> <Typography>Advertising Multiplier: x{numeralWrapper.formatReallyBigNumber(totalAdvertisingFac)}</Typography>
</Tooltip> </Tooltip>
)} )}
<br /> <br />
@ -164,7 +164,7 @@ export function IndustryOverview(props: IProps): React.ReactElement {
</Typography> </Typography>
} }
> >
<Typography>Production Multiplier: {numeralWrapper.format(division.prodMult, "0.00")}</Typography> <Typography>Production Multiplier: {numeralWrapper.formatReallyBigNumber(division.prodMult)}</Typography>
</Tooltip> </Tooltip>
<IconButton onClick={() => setHelpOpen(true)}> <IconButton onClick={() => setHelpOpen(true)}>
<HelpIcon /> <HelpIcon />
@ -203,7 +203,7 @@ export function IndustryOverview(props: IProps): React.ReactElement {
</Typography> </Typography>
} }
> >
<Typography>Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000a")}</Typography> <Typography>Scientific Research: {numeralWrapper.formatReallyBigNumber(division.sciResearch.qty)}</Typography>
</Tooltip> </Tooltip>
<Button sx={{ mx: 1 }} onClick={() => setResearchOpen(true)}> <Button sx={{ mx: 1 }} onClick={() => setResearchOpen(true)}>
Research Research

@ -38,7 +38,7 @@ export function Overview({ rerender }: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const profit: number = corp.revenue - corp.expenses; const profit: number = corp.revenue - corp.expenses;
const multRows: any[][] = []; const multRows: string[][] = [];
function appendMult(name: string, value: number): void { function appendMult(name: string, value: number): void {
if (value === 1) return; if (value === 1) return;
multRows.push([name, numeralWrapper.format(value, "0.000")]); multRows.push([name, numeralWrapper.format(value, "0.000")]);

@ -9,7 +9,7 @@ import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField"; import { NumberInput } from "../../../ui/React/NumberInput";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
@ -27,12 +27,10 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
return true; return true;
}); });
const corp = useCorporation(); const corp = useCorporation();
const [money, setMoney] = useState<number | null>(0); const [money, setMoney] = useState<number>(NaN);
const [stock, setStock] = useState<number | null>(0); const [stock, setStock] = useState<number>(NaN);
const [selectedFaction, setSelectedFaction] = useState(factions.length > 0 ? factions[0] : ""); const [selectedFaction, setSelectedFaction] = useState(factions.length > 0 ? factions[0] : "");
const disabled = const disabled =
money === null ||
stock === null ||
(money === 0 && stock === 0) || (money === 0 && stock === 0) ||
isNaN(money) || isNaN(money) ||
isNaN(stock) || isNaN(stock) ||
@ -41,14 +39,6 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
corp.funds < money || corp.funds < money ||
stock > corp.numShares; stock > corp.numShares;
function onMoneyChange(event: React.ChangeEvent<HTMLInputElement>): void {
setMoney(parseFloat(event.target.value));
}
function onStockChange(event: React.ChangeEvent<HTMLInputElement>): void {
setStock(parseFloat(event.target.value));
}
function changeFaction(event: SelectChangeEvent<string>): void { function changeFaction(event: SelectChangeEvent<string>): void {
setSelectedFaction(event.target.value); setSelectedFaction(event.target.value);
} }
@ -110,8 +100,8 @@ export function BribeFactionModal(props: IProps): React.ReactElement {
</Select> </Select>
</Box> </Box>
<Typography>{getRepText(money ? money : 0, stock ? stock : 0)}</Typography> <Typography>{getRepText(money ? money : 0, stock ? stock : 0)}</Typography>
<TextField onChange={onMoneyChange} placeholder="Corporation funds" /> <NumberInput onChange={setMoney} placeholder="Corporation funds" />
<TextField sx={{ mx: 1 }} onChange={onStockChange} placeholder="Stock Shares" /> <NumberInput sx={{ mx: 1 }} onChange={setStock} placeholder="Stock Shares" />
<Button disabled={disabled} sx={{ mx: 1 }} onClick={() => bribe(money ? money : 0, stock ? stock : 0)}> <Button disabled={disabled} sx={{ mx: 1 }} onClick={() => bribe(money ? money : 0, stock ? stock : 0)}>
Bribe Bribe
</Button> </Button>

@ -5,7 +5,7 @@ import { use } from "../../../ui/Context";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import { NumberInput } from "../../../ui/React/NumberInput";
import { BuyBackShares } from "../../Actions"; import { BuyBackShares } from "../../Actions";
import { dialogBoxCreate } from "../../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../../ui/React/DialogBox";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
@ -21,12 +21,7 @@ interface IProps {
export function BuybackSharesModal(props: IProps): React.ReactElement { export function BuybackSharesModal(props: IProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState<number | null>(null); const [shares, setShares] = useState<number>(NaN);
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setShares(null);
else setShares(Math.round(parseFloat(event.target.value)));
}
const currentStockPrice = corp.sharePrice; const currentStockPrice = corp.sharePrice;
const buybackPrice = currentStockPrice * 1.1; const buybackPrice = currentStockPrice * 1.1;
@ -87,13 +82,7 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
</Typography> </Typography>
<CostIndicator /> <CostIndicator />
<br /> <br />
<TextField <NumberInput autoFocus={true} placeholder="Shares to buyback" onChange={setShares} onKeyDown={onKeyDown} />
autoFocus={true}
type="number"
placeholder="Shares to buyback"
onChange={changeShares}
onKeyDown={onKeyDown}
/>
<Button disabled={disabled} onClick={buy}> <Button disabled={disabled} onClick={buy}>
Buy shares Buy shares
</Button> </Button>

@ -5,7 +5,7 @@ import { numeralWrapper } from "../../../ui/numeralFormat";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField"; import { NumberInput } from "../../../ui/React/NumberInput";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
@ -18,29 +18,28 @@ interface IProps {
// Create a popup that lets the player manage exports // Create a popup that lets the player manage exports
export function GoPublicModal(props: IProps): React.ReactElement { export function GoPublicModal(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState(""); const [shares, setShares] = useState<number>(NaN);
const initialSharePrice = corp.determineValuation() / corp.totalShares; const initialSharePrice = corp.determineValuation() / corp.totalShares;
function goPublic(): void { function goPublic(): void {
const numShares = parseFloat(shares);
const initialSharePrice = corp.determineValuation() / corp.totalShares; const initialSharePrice = corp.determineValuation() / corp.totalShares;
if (isNaN(numShares)) { if (isNaN(shares)) {
dialogBoxCreate("Invalid value for number of issued shares"); dialogBoxCreate("Invalid value for number of issued shares");
return; return;
} }
if (numShares > corp.numShares) { if (shares > corp.numShares) {
dialogBoxCreate("Error: You don't have that many shares to issue!"); dialogBoxCreate("Error: You don't have that many shares to issue!");
return; return;
} }
corp.public = true; corp.public = true;
corp.sharePrice = initialSharePrice; corp.sharePrice = initialSharePrice;
corp.issuedShares = numShares; corp.issuedShares = shares;
corp.numShares -= numShares; corp.numShares -= shares;
corp.addFunds(numShares * initialSharePrice); corp.addFunds(shares * initialSharePrice);
props.rerender(); props.rerender();
dialogBoxCreate( dialogBoxCreate(
`You took your ${corp.name} public and earned ` + `You took your ${corp.name} public and earned ` +
`${numeralWrapper.formatMoney(numShares * initialSharePrice)} in your IPO`, `${numeralWrapper.formatMoney(shares * initialSharePrice)} in your IPO`,
); );
props.onClose(); props.onClose();
} }
@ -49,10 +48,6 @@ export function GoPublicModal(props: IProps): React.ReactElement {
if (event.key === KEY.ENTER) goPublic(); if (event.key === KEY.ENTER) goPublic();
} }
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setShares(event.target.value);
}
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography> <Typography>
@ -64,19 +59,8 @@ export function GoPublicModal(props: IProps): React.ReactElement {
You have a total of {numeralWrapper.format(corp.numShares, "0.000a")} of shares that you can issue. You have a total of {numeralWrapper.format(corp.numShares, "0.000a")} of shares that you can issue.
</Typography> </Typography>
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<TextField <NumberInput onChange={setShares} autoFocus placeholder="Shares to issue" onKeyDown={onKeyDown} />
value={shares} <Button disabled={shares < 0 || shares > corp.numShares} sx={{ mx: 1 }} onClick={goPublic}>
onChange={onChange}
autoFocus
type="number"
placeholder="Shares to issue"
onKeyDown={onKeyDown}
/>
<Button
disabled={parseFloat(shares) < 0 || parseFloat(shares) > corp.numShares}
sx={{ mx: 1 }}
onClick={goPublic}
>
Go Public Go Public
</Button> </Button>
</Box> </Box>

@ -6,7 +6,7 @@ import { getRandomInt } from "../../../utils/helpers/getRandomInt";
import { CorporationConstants } from "../../data/Constants"; import { CorporationConstants } from "../../data/Constants";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField"; import { NumberInput } from "../../../ui/React/NumberInput";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
@ -54,15 +54,15 @@ interface IProps {
// This is created when the player clicks the "Issue New Shares" buttons in the overview panel // This is created when the player clicks the "Issue New Shares" buttons in the overview panel
export function IssueNewSharesModal(props: IProps): React.ReactElement { export function IssueNewSharesModal(props: IProps): React.ReactElement {
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState<number | null>(null); const [shares, setShares] = useState<number>(NaN);
const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2); const maxNewSharesUnrounded = Math.round(corp.totalShares * 0.2);
const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6); const maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
const newShares = Math.round((shares || 0) / 10e6) * 10e6; const newShares = Math.round((shares || 0) / 10e6) * 10e6;
const disabled = shares === null || isNaN(newShares) || newShares < 10e6 || newShares > maxNewShares; const disabled = isNaN(shares) || isNaN(newShares) || newShares < 10e6 || newShares > maxNewShares;
function issueNewShares(): void { function issueNewShares(): void {
if (shares === null) return; if (isNaN(shares)) return;
if (disabled) return; if (disabled) return;
const newSharePrice = Math.round(corp.sharePrice * 0.9); const newSharePrice = Math.round(corp.sharePrice * 0.9);
@ -97,11 +97,6 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
if (event.key === KEY.ENTER) issueNewShares(); if (event.key === KEY.ENTER) issueNewShares();
} }
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setShares(null);
else setShares(parseFloat(event.target.value));
}
return ( return (
<Modal open={props.open} onClose={props.onClose}> <Modal open={props.open} onClose={props.onClose}>
<Typography> <Typography>
@ -124,7 +119,7 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
you cannot buy them back. you cannot buy them back.
</Typography> </Typography>
<EffectText shares={shares} /> <EffectText shares={shares} />
<TextField autoFocus placeholder="# New Shares" onChange={onChange} onKeyDown={onKeyDown} /> <NumberInput autoFocus placeholder="# New Shares" onChange={setShares} onKeyDown={onKeyDown} />
<Button disabled={disabled} onClick={issueNewShares} sx={{ mx: 1 }}> <Button disabled={disabled} onClick={issueNewShares} sx={{ mx: 1 }}>
Issue New Shares Issue New Shares
</Button> </Button>

@ -10,6 +10,7 @@ import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select"; import Select, { SelectChangeEvent } from "@mui/material/Select";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
import { NumberInput } from "../../../ui/React/NumberInput";
interface IProps { interface IProps {
open: boolean; open: boolean;
@ -34,8 +35,8 @@ export function MakeProductModal(props: IProps): React.ReactElement {
const allCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] !== 0); const allCities = Object.keys(division.offices).filter((cityName: string) => division.offices[cityName] !== 0);
const [city, setCity] = useState(allCities.length > 0 ? allCities[0] : ""); const [city, setCity] = useState(allCities.length > 0 ? allCities[0] : "");
const [name, setName] = useState(""); const [name, setName] = useState("");
const [design, setDesign] = useState<number | null>(null); const [design, setDesign] = useState<number>(NaN);
const [marketing, setMarketing] = useState<number | null>(null); const [marketing, setMarketing] = useState<number>(NaN);
if (division.hasMaximumNumberProducts()) return <></>; if (division.hasMaximumNumberProducts()) return <></>;
let createProductPopupText = <></>; let createProductPopupText = <></>;
@ -138,7 +139,7 @@ export function MakeProductModal(props: IProps): React.ReactElement {
); );
function makeProduct(): void { function makeProduct(): void {
if (design === null || marketing === null) return; if (isNaN(design) || isNaN(marketing)) return;
try { try {
MakeProduct(corp, division, city, name, design, marketing); MakeProduct(corp, division, city, name, design, marketing);
} catch (err) { } catch (err) {
@ -155,16 +156,6 @@ export function MakeProductModal(props: IProps): React.ReactElement {
setName(event.target.value); setName(event.target.value);
} }
function onDesignChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setDesign(null);
else setDesign(parseFloat(event.target.value));
}
function onMarketingChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setMarketing(null);
else setMarketing(parseFloat(event.target.value));
}
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void { function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
if (event.key === KEY.ENTER) makeProduct(); if (event.key === KEY.ENTER) makeProduct();
} }
@ -181,13 +172,8 @@ export function MakeProductModal(props: IProps): React.ReactElement {
</Select> </Select>
<TextField onChange={onProductNameChange} placeholder={productPlaceholder(division.type)} /> <TextField onChange={onProductNameChange} placeholder={productPlaceholder(division.type)} />
<br /> <br />
<TextField onChange={onDesignChange} autoFocus={true} type="number" placeholder={"Design investment"} /> <NumberInput onChange={setDesign} autoFocus={true} placeholder={"Design investment"} />
<TextField <NumberInput onChange={setMarketing} onKeyDown={onKeyDown} placeholder={"Marketing investment"} />
onChange={onMarketingChange}
onKeyDown={onKeyDown}
type="number"
placeholder={"Marketing investment"}
/>
<Button onClick={makeProduct}>Develop Product</Button> <Button onClick={makeProduct}>Develop Product</Button>
</Modal> </Modal>
); );

@ -6,11 +6,11 @@ import { use } from "../../../ui/Context";
import { useCorporation } from "../Context"; import { useCorporation } from "../Context";
import { ICorporation } from "../../ICorporation"; import { ICorporation } from "../../ICorporation";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import { Money } from "../../../ui/React/Money"; import { Money } from "../../../ui/React/Money";
import { SellShares } from "../../Actions"; import { SellShares } from "../../Actions";
import { KEY } from "../../../utils/helpers/keyCodes"; import { KEY } from "../../../utils/helpers/keyCodes";
import { NumberInput } from "../../../ui/React/NumberInput";
interface IProps { interface IProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
@ -22,14 +22,9 @@ interface IProps {
export function SellSharesModal(props: IProps): React.ReactElement { export function SellSharesModal(props: IProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
const corp = useCorporation(); const corp = useCorporation();
const [shares, setShares] = useState<number | null>(null); const [shares, setShares] = useState<number>(NaN);
const disabled = shares === null || isNaN(shares) || shares <= 0 || shares > corp.numShares; const disabled = isNaN(shares) || shares <= 0 || shares > corp.numShares;
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setShares(null);
else setShares(Math.round(parseFloat(event.target.value)));
}
function ProfitIndicator(props: { shares: number | null; corp: ICorporation }): React.ReactElement { function ProfitIndicator(props: { shares: number | null; corp: ICorporation }): React.ReactElement {
if (props.shares === null) return <></>; if (props.shares === null) return <></>;
@ -88,12 +83,11 @@ export function SellSharesModal(props: IProps): React.ReactElement {
The current price of your company's stock is {numeralWrapper.formatMoney(corp.sharePrice)} The current price of your company's stock is {numeralWrapper.formatMoney(corp.sharePrice)}
</Typography> </Typography>
<br /> <br />
<TextField <NumberInput
variant="standard" variant="standard"
autoFocus autoFocus
type="number"
placeholder="Shares to sell" placeholder="Shares to sell"
onChange={changeShares} onChange={setShares}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
<Button disabled={disabled} onClick={sell} sx={{ mx: 1 }}> <Button disabled={disabled} onClick={sell} sx={{ mx: 1 }}>

@ -1,5 +1,5 @@
import { Fragment, FragmentById } from "./Fragment"; import { Fragment, FragmentById } from "./Fragment";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
export interface IActiveFragmentParams { export interface IActiveFragmentParams {
x: number; x: number;
@ -74,15 +74,14 @@ export class ActiveFragment {
/** /**
* Serialize an active fragment to a JSON save state. * Serialize an active fragment to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("ActiveFragment", this); return Generic_toJSON("ActiveFragment", this);
} }
/** /**
* Initializes an acive fragment from a JSON save state * Initializes an acive fragment from a JSON save state
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): ActiveFragment {
static fromJSON(value: any): ActiveFragment {
return Generic_fromJSON(ActiveFragment, value.data); return Generic_fromJSON(ActiveFragment, value.data);
} }
} }

@ -19,22 +19,22 @@ export class DummyGift implements IStaneksGift {
height(): number { height(): number {
return this._height; return this._height;
} }
charge(): any { charge(): void {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
process(): any { process(): void {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
effect(): any { effect(): number {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
canPlace(): any { canPlace(): boolean {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
place(): any { place(): boolean {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
findFragment(): any { findFragment(): ActiveFragment | undefined {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
fragmentAt(worldX: number, worldY: number): ActiveFragment | undefined { fragmentAt(worldX: number, worldY: number): ActiveFragment | undefined {
@ -46,22 +46,22 @@ export class DummyGift implements IStaneksGift {
return undefined; return undefined;
} }
delete(): any { delete(): boolean {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
clear(): any { clear(): void {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
count(): any { count(): number {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
inBonus(): any { inBonus(): boolean {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
prestigeAugmentation(): any { prestigeAugmentation(): void {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
prestigeSourceFile(): any { prestigeSourceFile(): void {
throw new Error("unimplemented for dummy gift"); throw new Error("unimplemented for dummy gift");
} }
} }

@ -13,18 +13,18 @@ export function loadStaneksGift(saveString: string): void {
} }
} }
export function zeros(dimensions: number[]): any { export function zeros(width: number, height: number): number[][] {
const array = []; const array = [];
for (let i = 0; i < dimensions[0]; ++i) { for (let i = 0; i < width; ++i) {
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1))); array.push(Array(height).fill(0));
} }
return array; return array;
} }
export function calculateGrid(gift: IStaneksGift): number[][] { export function calculateGrid(gift: IStaneksGift): number[][] {
const newgrid = zeros([gift.width(), gift.height()]) as unknown as number[][]; const newgrid = zeros(gift.width(), gift.height()) as unknown as number[][];
for (let i = 0; i < gift.width(); i++) { for (let i = 0; i < gift.width(); i++) {
for (let j = 0; j < gift.height(); j++) { for (let j = 0; j < gift.height(); j++) {
const fragment = gift.fragmentAt(i, j); const fragment = gift.fragmentAt(i, j);

@ -7,7 +7,7 @@ import { IPlayer } from "../PersonObjects/IPlayer";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { CalculateEffect } from "./formulas/effect"; import { CalculateEffect } from "./formulas/effect";
import { StaneksGiftEvents } from "./StaneksGiftEvents"; import { StaneksGiftEvents } from "./StaneksGiftEvents";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { StanekConstants } from "./data/Constants"; import { StanekConstants } from "./data/Constants";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers"; import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
@ -38,7 +38,7 @@ export class StaneksGift implements IStaneksGift {
} }
const cotmg = Factions[FactionNames.ChurchOfTheMachineGod]; const cotmg = Factions[FactionNames.ChurchOfTheMachineGod];
cotmg.playerReputation += (player.faction_rep_mult * (Math.pow(threads, 0.95) * (cotmg.favor + 100))) / 1000; cotmg.playerReputation += (player.mults.faction_rep * (Math.pow(threads, 0.95) * (cotmg.favor + 100))) / 1000;
} }
inBonus(): boolean { inBonus(): boolean {
@ -146,66 +146,66 @@ export class StaneksGift implements IStaneksGift {
const power = this.effect(aFrag); const power = this.effect(aFrag);
switch (fragment.type) { switch (fragment.type) {
case FragmentType.HackingChance: case FragmentType.HackingChance:
p.hacking_chance_mult *= power; p.mults.hacking_chance *= power;
break; break;
case FragmentType.HackingSpeed: case FragmentType.HackingSpeed:
p.hacking_speed_mult *= power; p.mults.hacking_speed *= power;
break; break;
case FragmentType.HackingMoney: case FragmentType.HackingMoney:
p.hacking_money_mult *= power; p.mults.hacking_money *= power;
break; break;
case FragmentType.HackingGrow: case FragmentType.HackingGrow:
p.hacking_grow_mult *= power; p.mults.hacking_grow *= power;
break; break;
case FragmentType.Hacking: case FragmentType.Hacking:
p.hacking_mult *= power; p.mults.hacking *= power;
p.hacking_exp_mult *= power; p.mults.hacking_exp *= power;
break; break;
case FragmentType.Strength: case FragmentType.Strength:
p.strength_mult *= power; p.mults.strength *= power;
p.strength_exp_mult *= power; p.mults.strength_exp *= power;
break; break;
case FragmentType.Defense: case FragmentType.Defense:
p.defense_mult *= power; p.mults.defense *= power;
p.defense_exp_mult *= power; p.mults.defense_exp *= power;
break; break;
case FragmentType.Dexterity: case FragmentType.Dexterity:
p.dexterity_mult *= power; p.mults.dexterity *= power;
p.dexterity_exp_mult *= power; p.mults.dexterity_exp *= power;
break; break;
case FragmentType.Agility: case FragmentType.Agility:
p.agility_mult *= power; p.mults.agility *= power;
p.agility_exp_mult *= power; p.mults.agility_exp *= power;
break; break;
case FragmentType.Charisma: case FragmentType.Charisma:
p.charisma_mult *= power; p.mults.charisma *= power;
p.charisma_exp_mult *= power; p.mults.charisma_exp *= power;
break; break;
case FragmentType.HacknetMoney: case FragmentType.HacknetMoney:
p.hacknet_node_money_mult *= power; p.mults.hacknet_node_money *= power;
break; break;
case FragmentType.HacknetCost: case FragmentType.HacknetCost:
p.hacknet_node_purchase_cost_mult /= power; p.mults.hacknet_node_purchase_cost /= power;
p.hacknet_node_ram_cost_mult /= power; p.mults.hacknet_node_ram_cost /= power;
p.hacknet_node_core_cost_mult /= power; p.mults.hacknet_node_core_cost /= power;
p.hacknet_node_level_cost_mult /= power; p.mults.hacknet_node_level_cost /= power;
break; break;
case FragmentType.Rep: case FragmentType.Rep:
p.company_rep_mult *= power; p.mults.company_rep *= power;
p.faction_rep_mult *= power; p.mults.faction_rep *= power;
break; break;
case FragmentType.WorkMoney: case FragmentType.WorkMoney:
p.work_money_mult *= power; p.mults.work_money *= power;
break; break;
case FragmentType.Crime: case FragmentType.Crime:
p.crime_success_mult *= power; p.mults.crime_success *= power;
p.crime_money_mult *= power; p.mults.crime_money *= power;
break; break;
case FragmentType.Bladeburner: case FragmentType.Bladeburner:
p.bladeburner_max_stamina_mult *= power; p.mults.bladeburner_max_stamina *= power;
p.bladeburner_stamina_gain_mult *= power; p.mults.bladeburner_stamina_gain *= power;
p.bladeburner_analysis_mult *= power; p.mults.bladeburner_analysis *= power;
p.bladeburner_success_chance_mult *= power; p.mults.bladeburner_success_chance *= power;
break; break;
} }
} }
@ -224,15 +224,14 @@ export class StaneksGift implements IStaneksGift {
/** /**
* Serialize Staneks Gift to a JSON save state. * Serialize Staneks Gift to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("StaneksGift", this); return Generic_toJSON("StaneksGift", this);
} }
/** /**
* Initializes Staneks Gift from a JSON save state * Initializes Staneks Gift from a JSON save state
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): StaneksGift {
static fromJSON(value: any): StaneksGift {
return Generic_fromJSON(StaneksGift, value.data); return Generic_fromJSON(StaneksGift, value.data);
} }
} }

@ -13,7 +13,7 @@ interface IProps {
export function DummyGrid(props: IProps): React.ReactElement { export function DummyGrid(props: IProps): React.ReactElement {
const gift = new DummyGift(props.width, props.height, props.fragments); const gift = new DummyGift(props.width, props.height, props.fragments);
const ghostGrid = zeros([props.width, props.height]); const ghostGrid = zeros(props.width, props.height);
return ( return (
<Box> <Box>
<Table sx={{ width: props.width, height: props.height }}> <Table sx={{ width: props.width, height: props.height }}>

@ -18,7 +18,7 @@ interface IProps {
} }
export function MainBoard(props: IProps): React.ReactElement { export function MainBoard(props: IProps): React.ReactElement {
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()])); const [ghostGrid, setGhostGrid] = React.useState(zeros(props.gift.width(), props.gift.height()));
const [pos, setPos] = React.useState([0, 0]); const [pos, setPos] = React.useState([0, 0]);
const [rotation, setRotation] = React.useState(0); const [rotation, setRotation] = React.useState(0);
const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment); const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
@ -26,7 +26,7 @@ export function MainBoard(props: IProps): React.ReactElement {
function moveGhost(worldX: number, worldY: number, rotation: number): void { function moveGhost(worldX: number, worldY: number, rotation: number): void {
setPos([worldX, worldY]); setPos([worldX, worldY]);
if (selectedFragment.type === FragmentType.None || selectedFragment.type === FragmentType.Delete) return; if (selectedFragment.type === FragmentType.None || selectedFragment.type === FragmentType.Delete) return;
const newgrid = zeros([props.gift.width(), props.gift.height()]); const newgrid = zeros(props.gift.width(), props.gift.height());
for (let y = 0; y < selectedFragment.height(rotation); y++) { for (let y = 0; y < selectedFragment.height(rotation); y++) {
for (let x = 0; x < selectedFragment.width(rotation); x++) { for (let x = 0; x < selectedFragment.width(rotation); x++) {
if (!selectedFragment.fullAt(x, y, rotation)) continue; if (!selectedFragment.fullAt(x, y, rotation)) continue;
@ -61,7 +61,7 @@ export function MainBoard(props: IProps): React.ReactElement {
function updateSelectedFragment(fragment: Fragment): void { function updateSelectedFragment(fragment: Fragment): void {
setSelectedFragment(fragment); setSelectedFragment(fragment);
const newgrid = zeros([props.gift.width(), props.gift.height()]); const newgrid = zeros(props.gift.width(), props.gift.height());
setGhostGrid(newgrid); setGhostGrid(newgrid);
} }

@ -1,9 +1,9 @@
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IPerson } from "../PersonObjects/IPerson"; import { IPerson } from "../PersonObjects/IPerson";
import { IRouter } from "../ui/Router";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { CrimeType } from "../utils/WorkType"; import { CrimeType } from "../utils/WorkType";
import { CrimeWork } from "../Work/CrimeWork";
interface IConstructorParams { interface IConstructorParams {
hacking_success_weight?: number; hacking_success_weight?: number;
@ -96,22 +96,15 @@ export class Crime {
this.kills = params.kills ? params.kills : 0; this.kills = params.kills ? params.kills : 0;
} }
commit(router: IRouter, p: IPlayer, div = 1, workerScript: WorkerScript | null = null): number { commit(p: IPlayer, div = 1, workerScript: WorkerScript | null = null): number {
if (div <= 0) { if (div <= 0) {
div = 1; div = 1;
} }
p.startCrime( p.startWork(
router, new CrimeWork({
this.type, crimeType: this.type,
this.hacking_exp / div, singularity: workerScript !== null,
this.strength_exp / div, }),
this.defense_exp / div,
this.dexterity_exp / div,
this.agility_exp / div,
this.charisma_exp / div,
this.money / div,
this.time,
workerScript,
); );
return this.time; return this.time;
@ -128,7 +121,7 @@ export class Crime {
CONSTANTS.IntelligenceCrimeWeight * p.intelligence; CONSTANTS.IntelligenceCrimeWeight * p.intelligence;
chance /= CONSTANTS.MaxSkillLevel; chance /= CONSTANTS.MaxSkillLevel;
chance /= this.difficulty; chance /= this.difficulty;
chance *= p.crime_success_mult; chance *= p.mults.crime_success;
chance *= p.getIntelligenceBonus(1); chance *= p.getIntelligenceBonus(1);
return Math.min(chance, 1); return Math.min(chance, 1);

@ -7,6 +7,7 @@ import { SpecialServers } from "../Server/data/SpecialServers";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { Money } from "../ui/React/Money"; import { Money } from "../ui/React/Money";
import { DarkWebItem } from "./DarkWebItem"; import { DarkWebItem } from "./DarkWebItem";
import { isCreateProgramWork } from "../Work/CreateProgramWork";
//Posts a "help" message if connected to DarkWeb //Posts a "help" message if connected to DarkWeb
export function checkIfConnectedToDarkweb(): void { export function checkIfConnectedToDarkweb(): void {
@ -74,9 +75,8 @@ export function buyDarkwebItem(itemName: string): void {
Player.getHomeComputer().pushProgram(item.program); Player.getHomeComputer().pushProgram(item.program);
// Cancel if the program is in progress of writing // Cancel if the program is in progress of writing
if (Player.createProgramName === item.program) { if (isCreateProgramWork(Player.currentWork) && Player.currentWork.programName === item.program) {
Player.isWorking = false; Player.finishWork(true);
Player.resetWorkStatus();
} }
Terminal.print( Terminal.print(

@ -24,8 +24,8 @@ export function SaveFile(): React.ReactElement {
const base64Save = await saveObject.getImportStringFromFile(event.target.files); const base64Save = await saveObject.getImportStringFromFile(event.target.files);
const save = atob(base64Save); const save = atob(base64Save);
setSaveFile(save); setSaveFile(save);
} catch (ex: any) { } catch (e: unknown) {
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000); SnackbarEvents.emit(String(e), ToastVariant.ERROR, 5000);
} }
} }
@ -38,8 +38,8 @@ export function SaveFile(): React.ReactElement {
function doRestore(): void { function doRestore(): void {
const save = JSON.parse(saveFile); const save = JSON.parse(saveFile);
console.log(Object.keys(save));
// TODO: Continue here. // TODO: Continue here.
console.error(save);
} }
return ( return (

@ -31,7 +31,7 @@ declare global {
}; };
electronBridge: { electronBridge: {
send: (channel: string, data?: unknown) => void; send: (channel: string, data?: unknown) => void;
receive: (channel: string, func: (...args: any[]) => void) => void; receive: (channel: string, func: (...args: unknown[]) => void) => void;
}; };
} }
interface Document { interface Document {
@ -150,7 +150,7 @@ function initSaveFunctions(): void {
try { try {
saveObject.exportGame(); saveObject.exportGame();
} catch (error) { } catch (error) {
console.log(error); console.error(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000); SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
} }
}, },
@ -185,11 +185,14 @@ function initElectronBridge(): void {
const data = window.appSaveFns.getSaveData(); const data = window.appSaveFns.getSaveData();
bridge.send("get-save-data-response", data); bridge.send("get-save-data-response", data);
}); });
bridge.receive("get-save-info-request", async (save: string) => { bridge.receive("get-save-info-request", async (save: unknown) => {
if (typeof save !== "string") throw new Error("Error while trying to get save info");
const data = await window.appSaveFns.getSaveInfo(save); const data = await window.appSaveFns.getSaveInfo(save);
bridge.send("get-save-info-response", data); bridge.send("get-save-info-response", data);
}); });
bridge.receive("push-save-request", ({ save, automatic = false }: { save: string; automatic: boolean }) => { bridge.receive("push-save-request", (params: unknown) => {
if (typeof params !== "object") throw new Error("Error trying to push save request");
const { save, automatic = false } = params as { save: string; automatic: boolean };
window.appSaveFns.pushSaveData(save, automatic); window.appSaveFns.pushSaveData(save, automatic);
}); });
bridge.receive("trigger-save", () => { bridge.receive("trigger-save", () => {
@ -199,7 +202,7 @@ function initElectronBridge(): void {
bridge.send("save-completed"); bridge.send("save-completed");
}) })
.catch((error: unknown) => { .catch((error: unknown) => {
console.log(error); console.error(error);
SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000); SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000);
}); });
}); });
@ -207,7 +210,7 @@ function initElectronBridge(): void {
try { try {
window.appSaveFns.triggerGameExport(); window.appSaveFns.triggerGameExport();
} catch (error) { } catch (error) {
console.log(error); console.error(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000); SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
} }
}); });
@ -215,7 +218,7 @@ function initElectronBridge(): void {
try { try {
window.appSaveFns.triggerScriptsExport(); window.appSaveFns.triggerScriptsExport();
} catch (error) { } catch (error) {
console.log(error); console.error(error);
SnackbarEvents.emit("Could not export scripts.", ToastVariant.ERROR, 2000); SnackbarEvents.emit("Could not export scripts.", ToastVariant.ERROR, 2000);
} }
}); });

@ -9,36 +9,36 @@ export function applyExploit(): void {
const inc = Math.pow(1.001, Player.exploits.length); const inc = Math.pow(1.001, Player.exploits.length);
const dec = Math.pow(0.999, Player.exploits.length); const dec = Math.pow(0.999, Player.exploits.length);
Player.hacking_chance_mult *= inc; Player.mults.hacking_chance *= inc;
Player.hacking_speed_mult *= inc; Player.mults.hacking_speed *= inc;
Player.hacking_money_mult *= inc; Player.mults.hacking_money *= inc;
Player.hacking_grow_mult *= inc; Player.mults.hacking_grow *= inc;
Player.hacking_mult *= inc; Player.mults.hacking *= inc;
Player.strength_mult *= inc; Player.mults.strength *= inc;
Player.defense_mult *= inc; Player.mults.defense *= inc;
Player.dexterity_mult *= inc; Player.mults.dexterity *= inc;
Player.agility_mult *= inc; Player.mults.agility *= inc;
Player.charisma_mult *= inc; Player.mults.charisma *= inc;
Player.hacking_exp_mult *= inc; Player.mults.hacking_exp *= inc;
Player.strength_exp_mult *= inc; Player.mults.strength_exp *= inc;
Player.defense_exp_mult *= inc; Player.mults.defense_exp *= inc;
Player.dexterity_exp_mult *= inc; Player.mults.dexterity_exp *= inc;
Player.agility_exp_mult *= inc; Player.mults.agility_exp *= inc;
Player.charisma_exp_mult *= inc; Player.mults.charisma_exp *= inc;
Player.company_rep_mult *= inc; Player.mults.company_rep *= inc;
Player.faction_rep_mult *= inc; Player.mults.faction_rep *= inc;
Player.crime_money_mult *= inc; Player.mults.crime_money *= inc;
Player.crime_success_mult *= inc; Player.mults.crime_success *= inc;
Player.hacknet_node_money_mult *= inc; Player.mults.hacknet_node_money *= inc;
Player.hacknet_node_purchase_cost_mult *= dec; Player.mults.hacknet_node_purchase_cost *= dec;
Player.hacknet_node_ram_cost_mult *= dec; Player.mults.hacknet_node_ram_cost *= dec;
Player.hacknet_node_core_cost_mult *= dec; Player.mults.hacknet_node_core_cost *= dec;
Player.hacknet_node_level_cost_mult *= dec; Player.mults.hacknet_node_level_cost *= dec;
Player.work_money_mult *= inc; Player.mults.work_money *= inc;
} }

@ -1,6 +1,6 @@
import { FactionInfo, FactionInfos } from "./FactionInfo"; import { FactionInfo, FactionInfos } from "./FactionInfo";
import { favorToRep, repToFavor } from "./formulas/favor"; import { favorToRep, repToFavor } from "./formulas/favor";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
export class Faction { export class Faction {
/** /**
@ -75,15 +75,14 @@ export class Faction {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Faction", this); return Generic_toJSON("Faction", this);
} }
/** /**
* Initiatizes a Faction object from a JSON save state. * Initiatizes a Faction object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Faction {
static fromJSON(value: any): Faction {
return Generic_fromJSON(Faction, value.data); return Generic_fromJSON(Faction, value.data);
} }
} }

@ -19,6 +19,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal"; import { InvitationEvent } from "./ui/InvitationModal";
import { FactionNames } from "./data/FactionNames"; import { FactionNames } from "./data/FactionNames";
import { SFC32RNG } from "../Casino/RNG"; import { SFC32RNG } from "../Casino/RNG";
import { isFactionWork } from "../Work/FactionWork";
export function inviteToFaction(faction: Faction): void { export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name); Player.receiveInvite(faction.name);
@ -113,7 +114,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
export function processPassiveFactionRepGain(numCycles: number): void { export function processPassiveFactionRepGain(numCycles: number): void {
for (const name of Object.keys(Factions)) { for (const name of Object.keys(Factions)) {
if (name === Player.currentWorkFactionName) continue; if (isFactionWork(Player.currentWork) && name === Player.currentWork.factionName) continue;
if (!Factions.hasOwnProperty(name)) continue; if (!Factions.hasOwnProperty(name)) continue;
const faction = Factions[name]; const faction = Factions[name];
if (!faction.isMember) continue; if (!faction.isMember) continue;
@ -132,7 +133,7 @@ export function processPassiveFactionRepGain(numCycles: number): void {
const fRep = getFactionFieldWorkRepGain(Player, faction); const fRep = getFactionFieldWorkRepGain(Player, faction);
const rate = Math.max(hRep * favorMult, sRep * favorMult, fRep * favorMult, 1 / 120); const rate = Math.max(hRep * favorMult, sRep * favorMult, fRep * favorMult, 1 / 120);
faction.playerReputation += rate * numCycles * Player.faction_rep_mult * BitNodeMultipliers.FactionPassiveRepGain; faction.playerReputation += rate * numCycles * Player.mults.faction_rep * BitNodeMultipliers.FactionPassiveRepGain;
} }
} }

@ -1,6 +0,0 @@
export enum FactionWorkType {
Field,
Hacking,
None,
Security,
}

@ -2,5 +2,5 @@ import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
export function repFromDonation(amt: number, player: IPlayer): number { export function repFromDonation(amt: number, player: IPlayer): number {
return (amt / CONSTANTS.DonateMoneyToRepDivisor) * player.faction_rep_mult; return (amt / CONSTANTS.DonateMoneyToRepDivisor) * player.mults.faction_rep;
} }

@ -12,8 +12,6 @@ import { Favor } from "../../ui/React/Favor";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { Reputation } from "../../ui/React/Reputation"; import { Reputation } from "../../ui/React/Reputation";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox"; import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { MathJaxWrapper } from "../../MathJaxWrapper"; import { MathJaxWrapper } from "../../MathJaxWrapper";

@ -21,6 +21,8 @@ import { Typography, Button } from "@mui/material";
import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot"; import { CovenantPurchasesRoot } from "../../PersonObjects/Sleeve/ui/CovenantPurchasesRoot";
import { FactionNames } from "../data/FactionNames"; import { FactionNames } from "../data/FactionNames";
import { GangButton } from "./GangButton"; import { GangButton } from "./GangButton";
import { FactionWork } from "../../Work/FactionWork";
import { FactionWorkType } from "../../Work/data/FactionWorkType";
type IProps = { type IProps = {
faction: Faction; faction: Faction;
@ -67,17 +69,35 @@ function MainPage({ faction, rerender, onAugmentations }: IMainProps): React.Rea
} }
function startFieldWork(faction: Faction): void { function startFieldWork(faction: Faction): void {
player.startFactionFieldWork(faction); player.startWork(
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.FIELD,
}),
);
startWork(); startWork();
} }
function startHackingContracts(faction: Faction): void { function startHackingContracts(faction: Faction): void {
player.startFactionHackWork(faction); player.startWork(
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.HACKING,
}),
);
startWork(); startWork();
} }
function startSecurityWork(faction: Faction): void { function startSecurityWork(faction: Faction): void {
player.startFactionSecurityWork(faction); player.startWork(
new FactionWork({
singularity: false,
faction: faction.name,
factionWorkType: FactionWorkType.SECURITY,
}),
);
startWork(); startWork();
} }

1
src/Faction/ui/v2.txt Normal file

@ -0,0 +1 @@
- https://github.com/danielyxie/bitburner/pull/3812

@ -23,32 +23,50 @@ export const CurrentOptionsPage = (props: IProps): React.ReactElement => {
const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat); const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat);
const [locale, setLocale] = useState(Settings.Locale); const [locale, setLocale] = useState(Settings.Locale);
function handleExecTimeChange(event: any, newValue: number | number[]): void { function handleExecTimeChange(
_event: Event | React.SyntheticEvent<Element, Event>,
newValue: number | number[],
): void {
setExecTime(newValue as number); setExecTime(newValue as number);
Settings.CodeInstructionRunTime = newValue as number; Settings.CodeInstructionRunTime = newValue as number;
} }
function handleRecentScriptsSizeChange(event: any, newValue: number | number[]): void { function handleRecentScriptsSizeChange(
_event: Event | React.SyntheticEvent<Element, Event>,
newValue: number | number[],
): void {
setRecentScriptsSize(newValue as number); setRecentScriptsSize(newValue as number);
Settings.MaxRecentScriptsCapacity = newValue as number; Settings.MaxRecentScriptsCapacity = newValue as number;
} }
function handleLogSizeChange(event: any, newValue: number | number[]): void { function handleLogSizeChange(
_event: Event | React.SyntheticEvent<Element, Event>,
newValue: number | number[],
): void {
setLogSize(newValue as number); setLogSize(newValue as number);
Settings.MaxLogCapacity = newValue as number; Settings.MaxLogCapacity = newValue as number;
} }
function handlePortSizeChange(event: any, newValue: number | number[]): void { function handlePortSizeChange(
_event: Event | React.SyntheticEvent<Element, Event>,
newValue: number | number[],
): void {
setPortSize(newValue as number); setPortSize(newValue as number);
Settings.MaxPortCapacity = newValue as number; Settings.MaxPortCapacity = newValue as number;
} }
function handleTerminalSizeChange(event: any, newValue: number | number[]): void { function handleTerminalSizeChange(
_event: Event | React.SyntheticEvent<Element, Event>,
newValue: number | number[],
): void {
setTerminalSize(newValue as number); setTerminalSize(newValue as number);
Settings.MaxTerminalCapacity = newValue as number; Settings.MaxTerminalCapacity = newValue as number;
} }
function handleAutosaveIntervalChange(event: any, newValue: number | number[]): void { function handleAutosaveIntervalChange(
_event: Event | React.SyntheticEvent<Element, Event>,
newValue: number | number[],
): void {
setAutosaveInterval(newValue as number); setAutosaveInterval(newValue as number);
Settings.AutosaveInterval = newValue as number; Settings.AutosaveInterval = newValue as number;
} }

@ -4,7 +4,6 @@ import {
Download, Download,
LibraryBooks, LibraryBooks,
Palette, Palette,
Public,
Reddit, Reddit,
Save, Save,
SystemUpdateAlt, SystemUpdateAlt,
@ -75,8 +74,8 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
const data = await saveObject.getImportDataFromString(base64Save); const data = await saveObject.getImportDataFromString(base64Save);
setImportData(data); setImportData(data);
setImportSaveOpen(true); setImportSaveOpen(true);
} catch (ex: any) { } catch (e: unknown) {
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000); SnackbarEvents.emit(String(e), ToastVariant.ERROR, 5000);
} }
} }
@ -85,8 +84,8 @@ export const GameOptionsSidebar = (props: IProps): React.ReactElement => {
try { try {
await saveObject.importGame(importData.base64); await saveObject.importGame(importData.base64);
} catch (ex: any) { } catch (e: unknown) {
SnackbarEvents.emit(ex.toString(), ToastVariant.ERROR, 5000); SnackbarEvents.emit(String(e), ToastVariant.ERROR, 5000);
} }
setImportSaveOpen(false); setImportSaveOpen(false);

@ -2,8 +2,8 @@ import { Slider, Tooltip, Typography, Box } from "@mui/material";
import React, { useState } from "react"; import React, { useState } from "react";
interface IProps { interface IProps {
initialValue: any; initialValue: number;
callback: (event: any, newValue: number | number[]) => void; callback: (event: Event | React.SyntheticEvent<Element, Event>, newValue: number | number[]) => void;
step: number; step: number;
min: number; min: number;
max: number; max: number;
@ -16,7 +16,7 @@ export const OptionsSlider = (props: IProps): React.ReactElement => {
const [value, setValue] = useState(props.initialValue); const [value, setValue] = useState(props.initialValue);
const onChange = (_evt: Event, newValue: number | Array<number>): void => { const onChange = (_evt: Event, newValue: number | Array<number>): void => {
setValue(newValue); if (typeof newValue === "number") setValue(newValue);
}; };
return ( return (

@ -8,7 +8,7 @@ import { Faction } from "../Faction/Faction";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver, Generic_toJSON, Generic_fromJSON } from "../utils/JSONReviver"; import { Reviver, Generic_toJSON, Generic_fromJSON, IReviverValue } from "../utils/JSONReviver";
import { exceptionAlert } from "../utils/helpers/exceptionAlert"; import { exceptionAlert } from "../utils/helpers/exceptionAlert";
import { getRandomInt } from "../utils/helpers/getRandomInt"; import { getRandomInt } from "../utils/helpers/getRandomInt";
@ -99,7 +99,7 @@ export class Gang implements IGang {
this.processExperienceGains(cycles); this.processExperienceGains(cycles);
this.processTerritoryAndPowerGains(cycles); this.processTerritoryAndPowerGains(cycles);
this.storedCycles -= cycles; this.storedCycles -= cycles;
} catch (e: any) { } catch (e: unknown) {
console.error(`Exception caught when processing Gang: ${e}`); console.error(`Exception caught when processing Gang: ${e}`);
} }
} }
@ -132,7 +132,7 @@ export class Gang implements IGang {
} }
const favorMult = 1 + fac.favor / 100; const favorMult = 1 + fac.favor / 100;
fac.playerReputation += (player.faction_rep_mult * gain * favorMult) / GangConstants.GangRespectToReputationRatio; fac.playerReputation += (player.mults.faction_rep * gain * favorMult) / GangConstants.GangRespectToReputationRatio;
// Keep track of respect gained per member // Keep track of respect gained per member
for (let i = 0; i < this.members.length; ++i) { for (let i = 0; i < this.members.length; ++i) {
@ -358,7 +358,7 @@ export class Gang implements IGang {
workerScript.log("gang.ascendMember", () => `Ascended Gang member ${member.name}`); workerScript.log("gang.ascendMember", () => `Ascended Gang member ${member.name}`);
} }
return res; return res;
} catch (e: any) { } catch (e: unknown) {
if (workerScript == null) { if (workerScript == null) {
exceptionAlert(e); exceptionAlert(e);
} }
@ -399,15 +399,14 @@ export class Gang implements IGang {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("Gang", this); return Generic_toJSON("Gang", this);
} }
/** /**
* Initiatizes a Gang object from a JSON save state. * Initiatizes a Gang object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): Gang {
static fromJSON(value: any): Gang {
return Generic_fromJSON(Gang, value.data); return Generic_fromJSON(Gang, value.data);
} }
} }

@ -5,7 +5,7 @@ import { GangMemberUpgrades } from "./GangMemberUpgrades";
import { IAscensionResult } from "./IAscensionResult"; import { IAscensionResult } from "./IAscensionResult";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IGang } from "./IGang"; import { IGang } from "./IGang";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { import {
calculateRespectGain, calculateRespectGain,
calculateMoneyGain, calculateMoneyGain,
@ -320,15 +320,14 @@ export class GangMember {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("GangMember", this); return Generic_toJSON("GangMember", this);
} }
/** /**
* Initiatizes a GangMember object from a JSON save state. * Initiatizes a GangMember object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): GangMember {
static fromJSON(value: any): GangMember {
return Generic_fromJSON(GangMember, value.data); return Generic_fromJSON(GangMember, value.data);
} }
} }

@ -3,6 +3,7 @@ import { GangMember } from "./GangMember";
import { WorkerScript } from "../Netscript/WorkerScript"; import { WorkerScript } from "../Netscript/WorkerScript";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
import { IAscensionResult } from "./IAscensionResult"; import { IAscensionResult } from "./IAscensionResult";
import { IReviverValue } from "src/utils/JSONReviver";
export interface IGang { export interface IGang {
facName: string; facName: string;
@ -42,5 +43,5 @@ export interface IGang {
getDiscount(): number; getDiscount(): number;
getAllTaskNames(): string[]; getAllTaskNames(): string[];
getUpgradeCost(upg: GangMemberUpgrade): number; getUpgradeCost(upg: GangMemberUpgrade): number;
toJSON(): any; toJSON(): IReviverValue;
} }

@ -2,17 +2,17 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { GangMember } from "../GangMember"; import { GangMember } from "../GangMember";
import { GangMemberTask } from "../GangMemberTask"; import { GangMemberTask } from "../GangMemberTask";
interface Gang { export interface FormulaGang {
respect: number; respect: number;
territory: number; territory: number;
wantedLevel: number; wantedLevel: number;
} }
export function calculateWantedPenalty(gang: Gang): number { export function calculateWantedPenalty(gang: FormulaGang): number {
return gang.respect / (gang.respect + gang.wantedLevel); return gang.respect / (gang.respect + gang.wantedLevel);
} }
export function calculateRespectGain(gang: Gang, member: GangMember, task: GangMemberTask): number { export function calculateRespectGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number {
if (task.baseRespect === 0) return 0; if (task.baseRespect === 0) return 0;
let statWeight = let statWeight =
(task.hackWeight / 100) * member.hack + (task.hackWeight / 100) * member.hack +
@ -30,7 +30,7 @@ export function calculateRespectGain(gang: Gang, member: GangMember, task: GangM
return Math.pow(11 * task.baseRespect * statWeight * territoryMult * respectMult, territoryPenalty); return Math.pow(11 * task.baseRespect * statWeight * territoryMult * respectMult, territoryPenalty);
} }
export function calculateWantedLevelGain(gang: Gang, member: GangMember, task: GangMemberTask): number { export function calculateWantedLevelGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number {
if (task.baseWanted === 0) return 0; if (task.baseWanted === 0) return 0;
let statWeight = let statWeight =
(task.hackWeight / 100) * member.hack + (task.hackWeight / 100) * member.hack +
@ -53,7 +53,7 @@ export function calculateWantedLevelGain(gang: Gang, member: GangMember, task: G
return Math.min(100, calc); return Math.min(100, calc);
} }
export function calculateMoneyGain(gang: Gang, member: GangMember, task: GangMemberTask): number { export function calculateMoneyGain(gang: FormulaGang, member: GangMember, task: GangMemberTask): number {
if (task.baseMoney === 0) return 0; if (task.baseMoney === 0) return 0;
let statWeight = let statWeight =
(task.hackWeight / 100) * member.hack + (task.hackWeight / 100) * member.hack +

@ -12,7 +12,7 @@ export function calculateHackingChance(server: Server, player: IPlayer): number
const skillMult = hackFactor * player.hacking; const skillMult = hackFactor * player.hacking;
const skillChance = (skillMult - server.requiredHackingSkill) / skillMult; const skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
const chance = const chance =
skillChance * difficultyMult * player.hacking_chance_mult * calculateIntelligenceBonus(player.intelligence, 1); skillChance * difficultyMult * player.mults.hacking_chance * calculateIntelligenceBonus(player.intelligence, 1);
if (chance > 1) { if (chance > 1) {
return 1; return 1;
} }
@ -36,7 +36,7 @@ export function calculateHackingExpGain(server: Server, player: IPlayer): number
let expGain = baseExpGain; let expGain = baseExpGain;
expGain += server.baseDifficulty * diffFactor; expGain += server.baseDifficulty * diffFactor;
return expGain * player.hacking_exp_mult * BitNodeMultipliers.HackExpGain; return expGain * player.mults.hacking_exp * BitNodeMultipliers.HackExpGain;
} }
/** /**
@ -50,7 +50,7 @@ export function calculatePercentMoneyHacked(server: Server, player: IPlayer): nu
const difficultyMult = (100 - server.hackDifficulty) / 100; const difficultyMult = (100 - server.hackDifficulty) / 100;
const skillMult = (player.hacking - (server.requiredHackingSkill - 1)) / player.hacking; const skillMult = (player.hacking - (server.requiredHackingSkill - 1)) / player.hacking;
const percentMoneyHacked = const percentMoneyHacked =
(difficultyMult * skillMult * player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney) / balanceFactor; (difficultyMult * skillMult * player.mults.hacking_money * BitNodeMultipliers.ScriptHackMoney) / balanceFactor;
if (percentMoneyHacked < 0) { if (percentMoneyHacked < 0) {
return 0; return 0;
} }
@ -77,7 +77,7 @@ export function calculateHackingTime(server: Server, player: IPlayer): number {
const hackTimeMultiplier = 5; const hackTimeMultiplier = 5;
const hackingTime = const hackingTime =
(hackTimeMultiplier * skillFactor) / (hackTimeMultiplier * skillFactor) /
(player.hacking_speed_mult * calculateIntelligenceBonus(player.intelligence, 1)); (player.mults.hacking_speed * calculateIntelligenceBonus(player.intelligence, 1));
return hackingTime; return hackingTime;
} }

@ -66,7 +66,7 @@ export function purchaseHacknet(player: IPlayer): number {
// Auto generate a name for the Node // Auto generate a name for the Node
const name = "hacknet-node-" + numOwned; const name = "hacknet-node-" + numOwned;
const node = new HacknetNode(name, player.hacknet_node_money_mult); const node = new HacknetNode(name, player.mults.hacknet_node_money);
player.loseMoney(cost, "hacknet_expenses"); player.loseMoney(cost, "hacknet_expenses");
player.hacknetNodes.push(node); player.hacknetNodes.push(node);
@ -80,11 +80,11 @@ export function hasMaxNumberHacknetServers(player: IPlayer): boolean {
} }
export function getCostOfNextHacknetNode(player: IPlayer): number { export function getCostOfNextHacknetNode(player: IPlayer): number {
return calculateNodeCost(player.hacknetNodes.length + 1, player.hacknet_node_purchase_cost_mult); return calculateNodeCost(player.hacknetNodes.length + 1, player.mults.hacknet_node_purchase_cost);
} }
export function getCostOfNextHacknetServer(player: IPlayer): number { export function getCostOfNextHacknetServer(player: IPlayer): number {
return calculateServerCost(player.hacknetNodes.length + 1, player.hacknet_node_purchase_cost_mult); return calculateServerCost(player.hacknetNodes.length + 1, player.mults.hacknet_node_purchase_cost);
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level
@ -97,14 +97,14 @@ export function getMaxNumberLevelUpgrades(
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
} }
if (player.money < nodeObj.calculateLevelUpgradeCost(1, player.hacknet_node_level_cost_mult)) { if (player.money < nodeObj.calculateLevelUpgradeCost(1, player.mults.hacknet_node_level_cost)) {
return 0; return 0;
} }
let min = 1; let min = 1;
let max = maxLevel - 1; let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.level; const levelsToMax = maxLevel - nodeObj.level;
if (player.money > nodeObj.calculateLevelUpgradeCost(levelsToMax, player.hacknet_node_level_cost_mult)) { if (player.money > nodeObj.calculateLevelUpgradeCost(levelsToMax, player.mults.hacknet_node_level_cost)) {
return levelsToMax; return levelsToMax;
} }
@ -112,13 +112,13 @@ export function getMaxNumberLevelUpgrades(
const curr = ((min + max) / 2) | 0; const curr = ((min + max) / 2) | 0;
if ( if (
curr !== maxLevel && curr !== maxLevel &&
player.money > nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult) && player.money > nodeObj.calculateLevelUpgradeCost(curr, player.mults.hacknet_node_level_cost) &&
player.money < nodeObj.calculateLevelUpgradeCost(curr + 1, player.hacknet_node_level_cost_mult) player.money < nodeObj.calculateLevelUpgradeCost(curr + 1, player.mults.hacknet_node_level_cost)
) { ) {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
} else if (player.money < nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult)) { } else if (player.money < nodeObj.calculateLevelUpgradeCost(curr, player.mults.hacknet_node_level_cost)) {
max = curr - 1; max = curr - 1;
} else if (player.money > nodeObj.calculateLevelUpgradeCost(curr, player.hacknet_node_level_cost_mult)) { } else if (player.money > nodeObj.calculateLevelUpgradeCost(curr, player.mults.hacknet_node_level_cost)) {
min = curr + 1; min = curr + 1;
} else { } else {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
@ -137,7 +137,7 @@ export function getMaxNumberRamUpgrades(
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
} }
if (player.money < nodeObj.calculateRamUpgradeCost(1, player.hacknet_node_ram_cost_mult)) { if (player.money < nodeObj.calculateRamUpgradeCost(1, player.mults.hacknet_node_ram_cost)) {
return 0; return 0;
} }
@ -147,13 +147,13 @@ export function getMaxNumberRamUpgrades(
} else { } else {
levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram)); levelsToMax = Math.round(Math.log2(maxLevel / nodeObj.ram));
} }
if (player.money > nodeObj.calculateRamUpgradeCost(levelsToMax, player.hacknet_node_ram_cost_mult)) { if (player.money > nodeObj.calculateRamUpgradeCost(levelsToMax, player.mults.hacknet_node_ram_cost)) {
return levelsToMax; return levelsToMax;
} }
//We'll just loop until we find the max //We'll just loop until we find the max
for (let i = levelsToMax - 1; i >= 0; --i) { for (let i = levelsToMax - 1; i >= 0; --i) {
if (player.money > nodeObj.calculateRamUpgradeCost(i, player.hacknet_node_ram_cost_mult)) { if (player.money > nodeObj.calculateRamUpgradeCost(i, player.mults.hacknet_node_ram_cost)) {
return i; return i;
} }
} }
@ -170,14 +170,14 @@ export function getMaxNumberCoreUpgrades(
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
} }
if (player.money < nodeObj.calculateCoreUpgradeCost(1, player.hacknet_node_core_cost_mult)) { if (player.money < nodeObj.calculateCoreUpgradeCost(1, player.mults.hacknet_node_core_cost)) {
return 0; return 0;
} }
let min = 1; let min = 1;
let max = maxLevel - 1; let max = maxLevel - 1;
const levelsToMax = maxLevel - nodeObj.cores; const levelsToMax = maxLevel - nodeObj.cores;
if (player.money > nodeObj.calculateCoreUpgradeCost(levelsToMax, player.hacknet_node_core_cost_mult)) { if (player.money > nodeObj.calculateCoreUpgradeCost(levelsToMax, player.mults.hacknet_node_core_cost)) {
return levelsToMax; return levelsToMax;
} }
@ -186,13 +186,13 @@ export function getMaxNumberCoreUpgrades(
const curr = ((min + max) / 2) | 0; const curr = ((min + max) / 2) | 0;
if ( if (
curr != maxLevel && curr != maxLevel &&
player.money > nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult) && player.money > nodeObj.calculateCoreUpgradeCost(curr, player.mults.hacknet_node_core_cost) &&
player.money < nodeObj.calculateCoreUpgradeCost(curr + 1, player.hacknet_node_core_cost_mult) player.money < nodeObj.calculateCoreUpgradeCost(curr + 1, player.mults.hacknet_node_core_cost)
) { ) {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
} else if (player.money < nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult)) { } else if (player.money < nodeObj.calculateCoreUpgradeCost(curr, player.mults.hacknet_node_core_cost)) {
max = curr - 1; max = curr - 1;
} else if (player.money > nodeObj.calculateCoreUpgradeCost(curr, player.hacknet_node_core_cost_mult)) { } else if (player.money > nodeObj.calculateCoreUpgradeCost(curr, player.mults.hacknet_node_core_cost)) {
min = curr + 1; min = curr + 1;
} else { } else {
return Math.min(levelsToMax, curr); return Math.min(levelsToMax, curr);
@ -242,7 +242,7 @@ export function getMaxNumberCacheUpgrades(player: IPlayer, nodeObj: HacknetServe
export function purchaseLevelUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { export function purchaseLevelUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
const cost = node.calculateLevelUpgradeCost(sanitizedLevels, player.hacknet_node_level_cost_mult); const cost = node.calculateLevelUpgradeCost(sanitizedLevels, player.mults.hacknet_node_level_cost);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false; return false;
} }
@ -266,14 +266,14 @@ export function purchaseLevelUpgrade(player: IPlayer, node: HacknetNode | Hackne
} }
player.loseMoney(cost, "hacknet_expenses"); player.loseMoney(cost, "hacknet_expenses");
node.upgradeLevel(sanitizedLevels, player.hacknet_node_money_mult); node.upgradeLevel(sanitizedLevels, player.mults.hacknet_node_money);
return true; return true;
} }
export function purchaseRamUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { export function purchaseRamUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
const cost = node.calculateRamUpgradeCost(sanitizedLevels, player.hacknet_node_ram_cost_mult); const cost = node.calculateRamUpgradeCost(sanitizedLevels, player.mults.hacknet_node_ram_cost);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false; return false;
} }
@ -305,14 +305,14 @@ export function purchaseRamUpgrade(player: IPlayer, node: HacknetNode | HacknetS
} }
player.loseMoney(cost, "hacknet_expenses"); player.loseMoney(cost, "hacknet_expenses");
node.upgradeRam(sanitizedLevels, player.hacknet_node_money_mult); node.upgradeRam(sanitizedLevels, player.mults.hacknet_node_money);
return true; return true;
} }
export function purchaseCoreUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean { export function purchaseCoreUpgrade(player: IPlayer, node: HacknetNode | HacknetServer, levels = 1): boolean {
const sanitizedLevels = Math.round(levels); const sanitizedLevels = Math.round(levels);
const cost = node.calculateCoreUpgradeCost(sanitizedLevels, player.hacknet_node_core_cost_mult); const cost = node.calculateCoreUpgradeCost(sanitizedLevels, player.mults.hacknet_node_core_cost);
if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) { if (isNaN(cost) || cost <= 0 || sanitizedLevels < 0) {
return false; return false;
} }
@ -336,7 +336,7 @@ export function purchaseCoreUpgrade(player: IPlayer, node: HacknetNode | Hacknet
} }
player.loseMoney(cost, "hacknet_expenses"); player.loseMoney(cost, "hacknet_expenses");
node.upgradeCore(sanitizedLevels, player.hacknet_node_money_mult); node.upgradeCore(sanitizedLevels, player.mults.hacknet_node_money);
return true; return true;
} }
@ -415,7 +415,7 @@ function processAllHacknetServerEarnings(player: IPlayer, numCycles: number): nu
if (ip instanceof HacknetNode) throw new Error(`player nodes should not be HacketNode`); if (ip instanceof HacknetNode) throw new Error(`player nodes should not be HacketNode`);
const hserver = GetServer(ip); const hserver = GetServer(ip);
if (!(hserver instanceof HacknetServer)) throw new Error(`player nodes shoud not be Server`); if (!(hserver instanceof HacknetServer)) throw new Error(`player nodes shoud not be Server`);
hserver.updateHashRate(player.hacknet_node_money_mult); hserver.updateHashRate(player.mults.hacknet_node_money);
const h = hserver.process(numCycles); const h = hserver.process(numCycles);
hashes += h; hashes += h;
} }
@ -467,7 +467,7 @@ export function updateHashManagerCapacity(player: IPlayer): void {
player.hashManager.updateCapacity(total); player.hashManager.updateCapacity(total);
} }
export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget: string): boolean { export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget: string, count = 1): boolean {
if (!(player.hashManager instanceof HashManager)) { if (!(player.hashManager instanceof HashManager)) {
console.error(`Player does not have a HashManager`); console.error(`Player does not have a HashManager`);
return false; return false;
@ -475,21 +475,21 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
// HashManager handles the transaction. This just needs to actually implement // HashManager handles the transaction. This just needs to actually implement
// the effects of the upgrade // the effects of the upgrade
if (player.hashManager.upgrade(upgName)) { if (player.hashManager.upgrade(upgName, count)) {
const upg = HashUpgrades[upgName]; const upg = HashUpgrades[upgName];
switch (upgName) { switch (upgName) {
case "Sell for Money": { case "Sell for Money": {
player.gainMoney(upg.value, "hacknet"); player.gainMoney(upg.value * count, "hacknet");
break; break;
} }
case "Sell for Corporation Funds": { case "Sell for Corporation Funds": {
const corp = player.corporation; const corp = player.corporation;
if (corp === null) { if (corp === null) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
corp.funds = corp.funds + upg.value; corp.funds = corp.funds + upg.value * count;
break; break;
} }
case "Reduce Minimum Security": { case "Reduce Minimum Security": {
@ -497,13 +497,13 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
const target = GetServer(upgTarget); const target = GetServer(upgTarget);
if (target == null) { if (target == null) {
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
return false; throw new Error(`'${upgTarget}' is not a server.`);
} }
if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`); if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`);
target.changeMinimumSecurity(upg.value, true); target.changeMinimumSecurity(upg.value ** count, true);
} catch (e) { } catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
break; break;
@ -513,13 +513,16 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
const target = GetServer(upgTarget); const target = GetServer(upgTarget);
if (target == null) { if (target == null) {
console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`); console.error(`Invalid target specified in purchaseHashUpgrade(): ${upgTarget}`);
return false; throw new Error(`'${upgTarget}' is not a server.`);
} }
if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`); if (!(target instanceof Server)) throw new Error(`'${upgTarget}' is not a normal server.`);
//Manually loop the change so as to properly handle the softcap
for (let i = 0; i < count; i++) {
target.changeMaximumMoney(upg.value); target.changeMaximumMoney(upg.value);
}
} catch (e) { } catch (e) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
break; break;
@ -536,11 +539,11 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
// This will throw if player doesn't have a corporation // This will throw if player doesn't have a corporation
const corp = player.corporation; const corp = player.corporation;
if (corp === null) { if (corp === null) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
for (const division of corp.divisions) { for (const division of corp.divisions) {
division.sciResearch.qty += upg.value; division.sciResearch.qty += upg.value * count;
} }
break; break;
} }
@ -548,30 +551,31 @@ export function purchaseHashUpgrade(player: IPlayer, upgName: string, upgTarget:
// This will throw if player isnt in Bladeburner // This will throw if player isnt in Bladeburner
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) { if (bladeburner === null) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
bladeburner.changeRank(player, upg.value); bladeburner.changeRank(player, upg.value * count);
break; break;
} }
case "Exchange for Bladeburner SP": { case "Exchange for Bladeburner SP": {
// This will throw if player isnt in Bladeburner // This will throw if player isnt in Bladeburner
const bladeburner = player.bladeburner; const bladeburner = player.bladeburner;
if (bladeburner === null) { if (bladeburner === null) {
player.hashManager.refundUpgrade(upgName); player.hashManager.refundUpgrade(upgName, count);
return false; return false;
} }
bladeburner.skillPoints += upg.value; bladeburner.skillPoints += upg.value * count;
break; break;
} }
case "Generate Coding Contract": { case "Generate Coding Contract": {
for (let i = 0; i < count; i++) {
generateRandomContract(); generateRandomContract();
}
break; break;
} }
default: default:
console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`); console.warn(`Unrecognized upgrade name ${upgName}. Upgrade has no effect`);
player.hashManager.refundUpgrade(upgName);
return false; return false;
} }

@ -17,7 +17,7 @@ import {
import { HacknetNodeConstants } from "./data/Constants"; import { HacknetNodeConstants } from "./data/Constants";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { ObjectValidator, minMax } from "../utils/Validator"; import { ObjectValidator, minMax } from "../utils/Validator";
export class HacknetNode implements IHacknetNode { export class HacknetNode implements IHacknetNode {
@ -123,15 +123,14 @@ export class HacknetNode implements IHacknetNode {
/** /**
* Serialize the current object to a JSON save state. * Serialize the current object to a JSON save state.
*/ */
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("HacknetNode", this); return Generic_toJSON("HacknetNode", this);
} }
/** /**
* Initiatizes a HacknetNode object from a JSON save state. * Initiatizes a HacknetNode object from a JSON save state.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): HacknetNode {
static fromJSON(value: any): HacknetNode {
return Generic_fromJSON(HacknetNode, value.data); return Generic_fromJSON(HacknetNode, value.data);
} }
} }

@ -18,7 +18,7 @@ import {
import { createRandomIp } from "../utils/IPAddress"; import { createRandomIp } from "../utils/IPAddress";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
import { IPlayer } from "../PersonObjects/IPlayer"; import { IPlayer } from "../PersonObjects/IPlayer";
interface IConstructorParams { interface IConstructorParams {
@ -125,7 +125,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
updateRamUsed(ram: number, player: IPlayer): void { updateRamUsed(ram: number, player: IPlayer): void {
super.updateRamUsed(ram, player); super.updateRamUsed(ram, player);
this.updateHashRate(player.hacknet_node_money_mult); this.updateHashRate(player.mults.hacknet_node_money);
} }
updateHashCapacity(): void { updateHashCapacity(): void {
@ -145,13 +145,12 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
} }
// Serialize the current object to a JSON save state // Serialize the current object to a JSON save state
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("HacknetServer", this); return Generic_toJSON("HacknetServer", this);
} }
// Initializes a HacknetServer Object from a JSON save state // Initializes a HacknetServer Object from a JSON save state
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): HacknetServer {
static fromJSON(value: any): HacknetServer {
return Generic_fromJSON(HacknetServer, value.data); return Generic_fromJSON(HacknetServer, value.data);
} }
} }

@ -10,7 +10,7 @@ import { HashUpgrades } from "./HashUpgrades";
import { HashUpgrade } from "./HashUpgrade"; import { HashUpgrade } from "./HashUpgrade";
import { IMap } from "../types"; import { IMap } from "../types";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver"; import { Generic_fromJSON, Generic_toJSON, IReviverValue, Reviver } from "../utils/JSONReviver";
export class HashManager { export class HashManager {
// Max number of hashes this can hold. Equal to the sum of capacities of // Max number of hashes this can hold. Equal to the sum of capacities of
@ -73,7 +73,7 @@ export class HashManager {
/** /**
* Get the cost (in hashes) of an upgrade * Get the cost (in hashes) of an upgrade
*/ */
getUpgradeCost(upgName: string): number { getUpgradeCost(upgName: string, count = 1): number {
const upg = this.getUpgrade(upgName); const upg = this.getUpgrade(upgName);
const currLevel = this.upgrades[upgName]; const currLevel = this.upgrades[upgName];
if (upg == null || currLevel == null) { if (upg == null || currLevel == null) {
@ -81,7 +81,7 @@ export class HashManager {
return Infinity; return Infinity;
} }
return upg.getCost(currLevel); return upg.getCost(currLevel, count);
} }
prestige(): void { prestige(): void {
@ -97,11 +97,11 @@ export class HashManager {
/** /**
* Reverts an upgrade and refunds the hashes used to buy it * Reverts an upgrade and refunds the hashes used to buy it
*/ */
refundUpgrade(upgName: string): void { refundUpgrade(upgName: string, count = 1): void {
const upg = HashUpgrades[upgName]; const upg = HashUpgrades[upgName];
// Reduce the level first, so we get the right cost // Reduce the level first, so we get the right cost
--this.upgrades[upgName]; this.upgrades[upgName] -= count;
const currLevel = this.upgrades[upgName]; const currLevel = this.upgrades[upgName];
if (upg == null || currLevel == null || currLevel < 0) { if (upg == null || currLevel == null || currLevel < 0) {
@ -109,7 +109,7 @@ export class HashManager {
return; return;
} }
const cost = upg.getCost(currLevel); const cost = upg.getCost(currLevel, count);
this.hashes += cost; this.hashes += cost;
} }
@ -137,33 +137,32 @@ export class HashManager {
* Returns boolean indicating whether or not the upgrade was successfully purchased * Returns boolean indicating whether or not the upgrade was successfully purchased
* Note that this does NOT actually implement the effect * Note that this does NOT actually implement the effect
*/ */
upgrade(upgName: string): boolean { upgrade(upgName: string, count = 1): boolean {
const upg = HashUpgrades[upgName]; const upg = HashUpgrades[upgName];
if (upg == null) { if (upg == null) {
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`); console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
return false; return false;
} }
const cost = this.getUpgradeCost(upgName); const cost = this.getUpgradeCost(upgName, count);
if (this.hashes < cost) { if (this.hashes < cost) {
return false; return false;
} }
this.hashes -= cost; this.hashes -= cost;
++this.upgrades[upgName]; this.upgrades[upgName] += count;
return true; return true;
} }
//Serialize the current object to a JSON save state. //Serialize the current object to a JSON save state.
toJSON(): any { toJSON(): IReviverValue {
return Generic_toJSON("HashManager", this); return Generic_toJSON("HashManager", this);
} }
// Initiatizes a HashManager object from a JSON save state. // Initiatizes a HashManager object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types static fromJSON(value: IReviverValue): HashManager {
static fromJSON(value: any): HashManager {
return Generic_fromJSON(HashManager, value.data); return Generic_fromJSON(HashManager, value.data);
} }
} }

@ -62,11 +62,15 @@ export class HashUpgrade {
// Functions that returns the UI element to display the effect of this upgrade. // Functions that returns the UI element to display the effect of this upgrade.
effectText: (level: number) => JSX.Element | null = () => null; effectText: (level: number) => JSX.Element | null = () => null;
getCost(levels: number): number { getCost(currentLevel: number, count = 1): number {
if (typeof this.cost === "number") { if (typeof this.cost === "number") {
return this.cost; return this.cost * count;
} }
return Math.round((levels + 1) * this.costPerLevel); //This formula is equivalent to
//(currentLevel + 1) * this.costPerLevel
//being performed repeatedly
const collapsedSum = 0.5 * count * (count + 2 * currentLevel + 1);
return this.costPerLevel * collapsedSum;
} }
} }

@ -58,9 +58,9 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
} }
const increase = const increase =
calculateMoneyGainRate(node.level + multiplier, node.ram, node.cores, props.player.hacknet_node_money_mult) - calculateMoneyGainRate(node.level + multiplier, node.ram, node.cores, props.player.mults.hacknet_node_money) -
node.moneyGainRatePerSecond; node.moneyGainRatePerSecond;
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.mults.hacknet_node_level_cost);
upgradeLevelButton = ( upgradeLevelButton = (
<Tooltip <Tooltip
title={ title={
@ -102,9 +102,9 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
node.level, node.level,
node.ram * Math.pow(2, multiplier), node.ram * Math.pow(2, multiplier),
node.cores, node.cores,
props.player.hacknet_node_money_mult, props.player.mults.hacknet_node_money,
) - node.moneyGainRatePerSecond; ) - node.moneyGainRatePerSecond;
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.mults.hacknet_node_ram_cost);
upgradeRAMButton = ( upgradeRAMButton = (
<Tooltip <Tooltip
title={ title={
@ -148,9 +148,9 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
} }
const increase = const increase =
calculateMoneyGainRate(node.level, node.ram, node.cores + multiplier, props.player.hacknet_node_money_mult) - calculateMoneyGainRate(node.level, node.ram, node.cores + multiplier, props.player.mults.hacknet_node_money) -
node.moneyGainRatePerSecond; node.moneyGainRatePerSecond;
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.mults.hacknet_node_core_cost);
upgradeCoresButton = ( upgradeCoresButton = (
<Tooltip <Tooltip
title={ title={

@ -61,11 +61,16 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
} }
const base_increase = const base_increase =
calculateHashGainRate(node.level + multiplier, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult) - calculateHashGainRate(
calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult); node.level + multiplier,
0,
node.maxRam,
node.cores,
props.player.mults.hacknet_node_money,
) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.mults.hacknet_node_money);
const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam; const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam;
const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.hacknet_node_level_cost_mult); const upgradeLevelCost = node.calculateLevelUpgradeCost(multiplier, props.player.mults.hacknet_node_level_cost);
upgradeLevelButton = ( upgradeLevelButton = (
<Tooltip <Tooltip
title={ title={
@ -122,8 +127,8 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
0, 0,
node.maxRam * Math.pow(2, multiplier), node.maxRam * Math.pow(2, multiplier),
node.cores, node.cores,
props.player.hacknet_node_money_mult, props.player.mults.hacknet_node_money,
) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult); ) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.mults.hacknet_node_money);
const modded_increase = const modded_increase =
calculateHashGainRate( calculateHashGainRate(
@ -131,11 +136,11 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
node.ramUsed, node.ramUsed,
node.maxRam * Math.pow(2, multiplier), node.maxRam * Math.pow(2, multiplier),
node.cores, node.cores,
props.player.hacknet_node_money_mult, props.player.mults.hacknet_node_money,
) - ) -
calculateHashGainRate(node.level, node.ramUsed, node.maxRam, node.cores, props.player.hacknet_node_money_mult); calculateHashGainRate(node.level, node.ramUsed, node.maxRam, node.cores, props.player.mults.hacknet_node_money);
const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.hacknet_node_ram_cost_mult); const upgradeRamCost = node.calculateRamUpgradeCost(multiplier, props.player.mults.hacknet_node_ram_cost);
upgradeRamButton = ( upgradeRamButton = (
<Tooltip <Tooltip
title={ title={
@ -179,11 +184,16 @@ export function HacknetServerElem(props: IProps): React.ReactElement {
} }
const base_increase = const base_increase =
calculateHashGainRate(node.level, 0, node.maxRam, node.cores + multiplier, props.player.hacknet_node_money_mult) - calculateHashGainRate(
calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.hacknet_node_money_mult); node.level,
0,
node.maxRam,
node.cores + multiplier,
props.player.mults.hacknet_node_money,
) - calculateHashGainRate(node.level, 0, node.maxRam, node.cores, props.player.mults.hacknet_node_money);
const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam; const modded_increase = (base_increase * (node.maxRam - node.ramUsed)) / node.maxRam;
const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.hacknet_node_core_cost_mult); const upgradeCoreCost = node.calculateCoreUpgradeCost(multiplier, props.player.mults.hacknet_node_core_cost);
upgradeCoresButton = ( upgradeCoresButton = (
<Tooltip <Tooltip
title={ title={

@ -16,10 +16,10 @@ export function calculateDifficulty(player: IPlayer, startingSecurityLevel: numb
export function calculateReward(player: IPlayer, startingSecurityLevel: number): number { export function calculateReward(player: IPlayer, startingSecurityLevel: number): number {
const xpMult = 10 * 60 * 15; const xpMult = 10 * 60 * 15;
const total = const total =
calculateSkill(player.strength_exp_mult * xpMult, player.strength_mult) + calculateSkill(player.mults.strength_exp * xpMult, player.mults.strength) +
calculateSkill(player.defense_exp_mult * xpMult, player.defense_mult) + calculateSkill(player.mults.defense_exp * xpMult, player.mults.defense) +
calculateSkill(player.agility_exp_mult * xpMult, player.agility_mult) + calculateSkill(player.mults.agility_exp * xpMult, player.mults.agility) +
calculateSkill(player.dexterity_exp_mult * xpMult, player.dexterity_mult) + calculateSkill(player.mults.dexterity_exp * xpMult, player.mults.dexterity) +
calculateSkill(player.charisma_exp_mult * xpMult, player.charisma_mult); calculateSkill(player.mults.charisma_exp * xpMult, player.mults.charisma);
return calculateRawDiff(player, total, startingSecurityLevel); return calculateRawDiff(player, total, startingSecurityLevel);
} }

@ -34,7 +34,7 @@ const types = [KEY.PIPE, KEY.DOT, KEY.FORWARD_SLASH, KEY.HYPHEN, "█", KEY.HASH
const colors = ["red", "#FFC107", "blue", "white"]; const colors = ["red", "#FFC107", "blue", "white"];
const colorNames: any = { const colorNames: Record<string, string> = {
red: "red", red: "red",
"#FFC107": "yellow", "#FFC107": "yellow",
blue: "blue", blue: "blue",

@ -77,7 +77,9 @@ function LocationLetter(location: Location): React.ReactElement {
function ASCIICity(props: IProps): React.ReactElement { function ASCIICity(props: IProps): React.ReactElement {
const locationLettersRegex = /[A-Z]/g; const locationLettersRegex = /[A-Z]/g;
const letterMap: any = { const letterMap: {
[key: string]: number;
} = {
A: 0, A: 0,
B: 1, B: 1,
C: 2, C: 2,
@ -106,10 +108,10 @@ function ASCIICity(props: IProps): React.ReactElement {
Z: 25, Z: 25,
}; };
const lineElems = (s: string): JSX.Element[] => { const lineElems = (s: string): (string | React.ReactElement)[] => {
const elems: any[] = []; const elems: (string | React.ReactElement)[] = [];
const matches: any[] = []; const matches: RegExpExecArray[] = [];
let match: any; let match: RegExpExecArray | null = null;
while ((match = locationLettersRegex.exec(s)) !== null) { while ((match = locationLettersRegex.exec(s)) !== null) {
matches.push(match); matches.push(match);
} }

@ -23,6 +23,7 @@ import { Reputation } from "../../ui/React/Reputation";
import { Favor } from "../../ui/React/Favor"; import { Favor } from "../../ui/React/Favor";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { QuitJobModal } from "../../Company/ui/QuitJobModal"; import { QuitJobModal } from "../../Company/ui/QuitJobModal";
import { CompanyWork } from "../../Work/CompanyWork";
type IProps = { type IProps = {
locName: LocationName; locName: LocationName;
@ -175,11 +176,12 @@ export function CompanyLocation(props: IProps): React.ReactElement {
const pos = companyPosition; const pos = companyPosition;
if (pos instanceof CompanyPosition) { if (pos instanceof CompanyPosition) {
if (pos.isPartTimeJob() || pos.isSoftwareConsultantJob() || pos.isBusinessConsultantJob()) { p.startWork(
p.startWorkPartTime(props.locName); new CompanyWork({
} else { singularity: false,
p.startWork(props.locName); companyName: props.locName,
} }),
);
p.startFocusing(); p.startFocusing();
router.toWork(); router.toWork();
} }

@ -8,16 +8,13 @@ import Button from "@mui/material/Button";
import { Location } from "../Location"; import { Location } from "../Location";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { GetServer } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { IRouter } from "../../ui/Router"; import { IRouter } from "../../ui/Router";
import { serverMetadata } from "../../Server/data/servers";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { ClassType } from "../../utils/WorkType"; import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/formulas/Class";
type IProps = { type IProps = {
loc: Location; loc: Location;
@ -26,51 +23,32 @@ type IProps = {
}; };
export function GymLocation(props: IProps): React.ReactElement { export function GymLocation(props: IProps): React.ReactElement {
function calculateCost(): number {
const serverMeta = serverMetadata.find((s) => s.specialName === props.loc.name);
const server = GetServer(serverMeta ? serverMeta.hostname : "");
if (server == null || !server.hasOwnProperty("backdoorInstalled")) return props.loc.costMult;
const discount = (server as Server).backdoorInstalled ? 0.9 : 1;
return props.loc.costMult * discount;
}
function train(stat: ClassType): void { function train(stat: ClassType): void {
const loc = props.loc; props.p.startWork(
props.p.startClass(calculateCost(), loc.expMult, stat); new ClassWork({
classType: stat,
location: props.loc.name,
singularity: false,
}),
);
props.p.startFocusing(); props.p.startFocusing();
props.router.toWork(); props.router.toWork();
} }
function trainStrength(): void { const cost = calculateCost(Classes[ClassType.GymStrength], props.loc);
train(ClassType.GymStrength);
}
function trainDefense(): void {
train(ClassType.GymDefense);
}
function trainDexterity(): void {
train(ClassType.GymDexterity);
}
function trainAgility(): void {
train(ClassType.GymAgility);
}
const cost = CONSTANTS.ClassGymBaseCost * calculateCost();
return ( return (
<Box sx={{ display: "grid", width: "fit-content" }}> <Box sx={{ display: "grid", width: "fit-content" }}>
<Button onClick={trainStrength}> <Button onClick={() => train(ClassType.GymStrength)}>
Train Strength (<Money money={cost} player={props.p} /> / sec) Train Strength (<Money money={cost} player={props.p} /> / sec)
</Button> </Button>
<Button onClick={trainDefense}> <Button onClick={() => train(ClassType.GymDefense)}>
Train Defense (<Money money={cost} player={props.p} /> / sec) Train Defense (<Money money={cost} player={props.p} /> / sec)
</Button> </Button>
<Button onClick={trainDexterity}> <Button onClick={() => train(ClassType.GymDexterity)}>
Train Dexterity (<Money money={cost} player={props.p} /> / sec) Train Dexterity (<Money money={cost} player={props.p} /> / sec)
</Button> </Button>
<Button onClick={trainAgility}> <Button onClick={() => train(ClassType.GymAgility)}>
Train Agility (<Money money={cost} player={props.p} /> / sec) Train Agility (<Money money={cost} player={props.p} /> / sec)
</Button> </Button>
</Box> </Box>

@ -25,13 +25,11 @@ export class HospitalLocation extends React.Component<IProps, IState> {
/** /**
* Stores button styling that sets them all to block display * Stores button styling that sets them all to block display
*/ */
btnStyle: any; btnStyle = { display: "block" };
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
this.btnStyle = { display: "block" };
this.getCost = this.getCost.bind(this); this.getCost = this.getCost.bind(this);
this.getHealed = this.getHealed.bind(this); this.getHealed = this.getHealed.bind(this);

@ -20,84 +20,108 @@ export function SlumsLocation(): React.ReactElement {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Shoplift.commit(router, player); Crimes.Shoplift.commit(player);
router.toWork();
player.focus = true;
} }
function robStore(e: React.MouseEvent<HTMLElement>): void { function robStore(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.RobStore.commit(router, player); Crimes.RobStore.commit(player);
router.toWork();
player.focus = true;
} }
function mug(e: React.MouseEvent<HTMLElement>): void { function mug(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Mug.commit(router, player); Crimes.Mug.commit(player);
router.toWork();
player.focus = true;
} }
function larceny(e: React.MouseEvent<HTMLElement>): void { function larceny(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Larceny.commit(router, player); Crimes.Larceny.commit(player);
router.toWork();
player.focus = true;
} }
function dealDrugs(e: React.MouseEvent<HTMLElement>): void { function dealDrugs(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.DealDrugs.commit(router, player); Crimes.DealDrugs.commit(player);
router.toWork();
player.focus = true;
} }
function bondForgery(e: React.MouseEvent<HTMLElement>): void { function bondForgery(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.BondForgery.commit(router, player); Crimes.BondForgery.commit(player);
router.toWork();
player.focus = true;
} }
function traffickArms(e: React.MouseEvent<HTMLElement>): void { function traffickArms(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.TraffickArms.commit(router, player); Crimes.TraffickArms.commit(player);
router.toWork();
player.focus = true;
} }
function homicide(e: React.MouseEvent<HTMLElement>): void { function homicide(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Homicide.commit(router, player); Crimes.Homicide.commit(player);
router.toWork();
player.focus = true;
} }
function grandTheftAuto(e: React.MouseEvent<HTMLElement>): void { function grandTheftAuto(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.GrandTheftAuto.commit(router, player); Crimes.GrandTheftAuto.commit(player);
router.toWork();
player.focus = true;
} }
function kidnap(e: React.MouseEvent<HTMLElement>): void { function kidnap(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Kidnap.commit(router, player); Crimes.Kidnap.commit(player);
router.toWork();
player.focus = true;
} }
function assassinate(e: React.MouseEvent<HTMLElement>): void { function assassinate(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Assassination.commit(router, player); Crimes.Assassination.commit(player);
router.toWork();
player.focus = true;
} }
function heist(e: React.MouseEvent<HTMLElement>): void { function heist(e: React.MouseEvent<HTMLElement>): void {
if (!e.isTrusted) { if (!e.isTrusted) {
return; return;
} }
Crimes.Heist.commit(router, player); Crimes.Heist.commit(player);
router.toWork();
player.focus = true;
} }
const shopliftChance = Crimes.Shoplift.successRate(player); const shopliftChance = Crimes.Shoplift.successRate(player);

@ -56,7 +56,7 @@ export function SpecialLocation(props: IProps): React.ReactElement {
router.toBladeburner(); router.toBladeburner();
} else if (p.strength >= 100 && p.defense >= 100 && p.dexterity >= 100 && p.agility >= 100) { } else if (p.strength >= 100 && p.defense >= 100 && p.dexterity >= 100 && p.agility >= 100) {
// Apply for Bladeburner division // Apply for Bladeburner division
p.startBladeburner({ new: true }); p.startBladeburner();
dialogBoxCreate("You have been accepted into the Bladeburner division!"); dialogBoxCreate("You have been accepted into the Bladeburner division!");
setRerender((old) => !old); setRerender((old) => !old);

@ -9,15 +9,12 @@ import Button from "@mui/material/Button";
import { Location } from "../Location"; import { Location } from "../Location";
import { CONSTANTS } from "../../Constants";
import { GetServer } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { use } from "../../ui/Context"; import { use } from "../../ui/Context";
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { ClassType } from "../../utils/WorkType"; import { ClassWork, ClassType, Classes } from "../../Work/ClassWork";
import { calculateCost } from "../../Work/formulas/Class";
type IProps = { type IProps = {
loc: Location; loc: Location;
@ -27,51 +24,23 @@ export function UniversityLocation(props: IProps): React.ReactElement {
const player = use.Player(); const player = use.Player();
const router = use.Router(); const router = use.Router();
function calculateCost(): number { function take(classType: ClassType): void {
const server = GetServer(props.loc.name); player.startWork(
if (server == null || !server.hasOwnProperty("backdoorInstalled")) return props.loc.costMult; new ClassWork({
const discount = (server as Server).backdoorInstalled ? 0.9 : 1; classType: classType,
return props.loc.costMult * discount; location: props.loc.name,
} singularity: false,
}),
function take(stat: ClassType): void { );
const loc = props.loc;
player.startClass(calculateCost(), loc.expMult, stat);
player.startFocusing(); player.startFocusing();
router.toWork(); router.toWork();
} }
function study(): void { const dataStructuresCost = calculateCost(Classes[ClassType.DataStructures], props.loc);
take(ClassType.StudyComputerScience); const networksCost = calculateCost(Classes[ClassType.Networks], props.loc);
} const algorithmsCost = calculateCost(Classes[ClassType.Algorithms], props.loc);
const managementCost = calculateCost(Classes[ClassType.Management], props.loc);
function dataStructures(): void { const leadershipCost = calculateCost(Classes[ClassType.Leadership], props.loc);
take(ClassType.DataStructures);
}
function networks(): void {
take(ClassType.Networks);
}
function algorithms(): void {
take(ClassType.Algorithms);
}
function management(): void {
take(ClassType.Management);
}
function leadership(): void {
take(ClassType.Leadership);
}
const costMult: number = calculateCost();
const dataStructuresCost = CONSTANTS.ClassDataStructuresBaseCost * costMult;
const networksCost = CONSTANTS.ClassNetworksBaseCost * costMult;
const algorithmsCost = CONSTANTS.ClassAlgorithmsBaseCost * costMult;
const managementCost = CONSTANTS.ClassManagementBaseCost * costMult;
const leadershipCost = CONSTANTS.ClassLeadershipBaseCost * costMult;
const earnHackingExpTooltip = `Gain hacking experience!`; const earnHackingExpTooltip = `Gain hacking experience!`;
const earnCharismaExpTooltip = `Gain charisma experience!`; const earnCharismaExpTooltip = `Gain charisma experience!`;
@ -79,34 +48,34 @@ export function UniversityLocation(props: IProps): React.ReactElement {
return ( return (
<Box sx={{ display: "grid", width: "fit-content" }}> <Box sx={{ display: "grid", width: "fit-content" }}>
<Tooltip title={earnHackingExpTooltip}> <Tooltip title={earnHackingExpTooltip}>
<Button onClick={study}>Study Computer Science (free)</Button> <Button onClick={() => take(ClassType.StudyComputerScience)}>Study Computer Science (free)</Button>
</Tooltip> </Tooltip>
<Tooltip title={earnHackingExpTooltip}> <Tooltip title={earnHackingExpTooltip}>
<Button onClick={dataStructures}> <Button onClick={() => take(ClassType.DataStructures)}>
Take Data Structures course ( Take Data Structures course (
<Money money={dataStructuresCost} player={player} /> / sec) <Money money={dataStructuresCost} player={player} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnHackingExpTooltip}> <Tooltip title={earnHackingExpTooltip}>
<Button onClick={networks}> <Button onClick={() => take(ClassType.Networks)}>
Take Networks course ( Take Networks course (
<Money money={networksCost} player={player} /> / sec) <Money money={networksCost} player={player} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnHackingExpTooltip}> <Tooltip title={earnHackingExpTooltip}>
<Button onClick={algorithms}> <Button onClick={() => take(ClassType.Algorithms)}>
Take Algorithms course ( Take Algorithms course (
<Money money={algorithmsCost} player={player} /> / sec) <Money money={algorithmsCost} player={player} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnCharismaExpTooltip}> <Tooltip title={earnCharismaExpTooltip}>
<Button onClick={management}> <Button onClick={() => take(ClassType.Management)}>
Take Management course ( Take Management course (
<Money money={managementCost} player={player} /> / sec) <Money money={managementCost} player={player} /> / sec)
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip title={earnCharismaExpTooltip}> <Tooltip title={earnCharismaExpTooltip}>
<Button onClick={leadership}> <Button onClick={() => take(ClassType.Leadership)}>
Take Leadership course ( Take Leadership course (
<Money money={leadershipCost} player={player} /> / sec) <Money money={leadershipCost} player={player} /> / sec)
</Button> </Button>

@ -4,6 +4,7 @@ import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction"; import { Faction } from "../Faction/Faction";
import { GetServer } from "../Server/AllServers"; import { GetServer } from "../Server/AllServers";
import { FactionNames } from "../Faction/data/FactionNames"; import { FactionNames } from "../Faction/data/FactionNames";
import { Server } from "../Server/Server";
function allFactionAugs(p: IPlayer, f: Faction): boolean { function allFactionAugs(p: IPlayer, f: Faction): boolean {
const factionAugs = f.augmentations.slice().filter((aug) => aug !== "NeuroFlux Governor"); const factionAugs = f.augmentations.slice().filter((aug) => aug !== "NeuroFlux Governor");
@ -24,7 +25,7 @@ export const Milestones: Milestone[] = [
fulfilled: (): boolean => { fulfilled: (): boolean => {
const server = GetServer("CSEC"); const server = GetServer("CSEC");
if (!server || !server.hasOwnProperty("hasAdminRights")) return false; if (!server || !server.hasOwnProperty("hasAdminRights")) return false;
return (server as any).hasAdminRights; return server instanceof Server && server.hasAdminRights;
}, },
}, },
{ {
@ -32,7 +33,7 @@ export const Milestones: Milestone[] = [
fulfilled: (): boolean => { fulfilled: (): boolean => {
const server = GetServer("CSEC"); const server = GetServer("CSEC");
if (!server || !server.hasOwnProperty("backdoorInstalled")) return false; if (!server || !server.hasOwnProperty("backdoorInstalled")) return false;
return (server as any).backdoorInstalled; return server instanceof Server && server.backdoorInstalled;
}, },
}, },
{ {

@ -4,11 +4,17 @@ import type { BaseServer } from "../Server/BaseServer";
import type { WorkerScript } from "./WorkerScript"; import type { WorkerScript } from "./WorkerScript";
import { makeRuntimeRejectMsg } from "../NetscriptEvaluator"; import { makeRuntimeRejectMsg } from "../NetscriptEvaluator";
import { Player } from "../Player"; import { Player } from "../Player";
import { CityName } from "src/Locations/data/CityNames"; import { CityName } from "../Locations/data/CityNames";
import { Settings } from "../Settings/Settings"; import { BasicHGWOptions } from "../ScriptEditor/NetscriptDefinitions";
import { CONSTANTS } from "../Constants"; import { IPlayer } from "../PersonObjects/IPlayer";
import { Server } from "../Server/Server";
import { FormulaGang } from "../Gang/formulas/formulas";
import { INetscriptHelper, ScriptIdentifier } from "../NetscriptFunctions/INetscriptHelper";
import { GangMember } from "../Gang/GangMember";
import { GangMemberTask } from "../Gang/GangMemberTask";
import { ScriptArg } from "./ScriptArg";
type ExternalFunction = (...args: any[]) => any; type ExternalFunction = (...args: unknown[]) => unknown;
export type ExternalAPI = { export type ExternalAPI = {
[string: string]: ExternalAPI | ExternalFunction; [string: string]: ExternalAPI | ExternalFunction;
}; };
@ -36,39 +42,30 @@ export type NetscriptContext = {
helper: WrappedNetscriptHelpers; helper: WrappedNetscriptHelpers;
}; };
type NetscriptHelpers = {
updateDynamicRam: (fnName: string, ramCost: number) => void;
makeRuntimeErrorMsg: (caller: string, msg: string) => string;
string: (funcName: string, argName: string, v: unknown) => string;
number: (funcName: string, argName: string, v: unknown) => number;
city: (funcName: string, argName: string, v: unknown) => CityName;
boolean: (v: unknown) => boolean;
getServer: (hostname: string, ctx: NetscriptContext) => BaseServer;
checkSingularityAccess: (func: string) => void;
hack: (
ctx: NetscriptContext,
hostname: any,
manual: any,
{ threads: requestedThreads, stock }?: any,
) => Promise<number>;
getValidPort: (funcName: string, port: any) => IPort;
};
type WrappedNetscriptHelpers = { type WrappedNetscriptHelpers = {
makeRuntimeErrorMsg: (msg: string) => string; makeRuntimeErrorMsg: (msg: string) => string;
string: (argName: string, v: unknown) => string; string: (argName: string, v: unknown) => string;
number: (argName: string, v: unknown) => number; number: (argName: string, v: unknown) => number;
ustring: (argName: string, v: unknown) => string | undefined;
unumber: (argName: string, v: unknown) => number | undefined;
scriptArgs(args: unknown): ScriptArg[];
scriptIdentifier: (fn: unknown, hostname: unknown, args: unknown) => ScriptIdentifier;
city: (argName: string, v: unknown) => CityName; city: (argName: string, v: unknown) => CityName;
boolean: (v: unknown) => boolean; boolean: (v: unknown) => boolean;
getServer: (hostname: string) => BaseServer; getServer: (hostname: string) => BaseServer;
checkSingularityAccess: () => void; checkSingularityAccess: () => void;
hack: (hostname: any, manual: any, { threads: requestedThreads, stock }?: any) => Promise<number>; hack: (hostname: string, manual: boolean, { threads: requestedThreads, stock }?: BasicHGWOptions) => Promise<number>;
getValidPort: (port: any) => IPort; getValidPort: (port: number) => IPort;
player(p: unknown): IPlayer;
server(s: unknown): Server;
gang(g: unknown): FormulaGang;
gangMember: (m: unknown) => GangMember;
gangTask: (t: unknown) => GangMemberTask;
}; };
function wrapFunction( function wrapFunction(
helpers: NetscriptHelpers, helpers: INetscriptHelper,
wrappedAPI: any, wrappedAPI: ExternalAPI,
workerScript: WorkerScript, workerScript: WorkerScript,
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown, func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
...tree: string[] ...tree: string[]
@ -91,12 +88,22 @@ function wrapFunction(
makeRuntimeErrorMsg: (msg: string) => helpers.makeRuntimeErrorMsg(functionPath, msg), makeRuntimeErrorMsg: (msg: string) => helpers.makeRuntimeErrorMsg(functionPath, msg),
string: (argName: string, v: unknown) => helpers.string(functionPath, argName, v), string: (argName: string, v: unknown) => helpers.string(functionPath, argName, v),
number: (argName: string, v: unknown) => helpers.number(functionPath, argName, v), number: (argName: string, v: unknown) => helpers.number(functionPath, argName, v),
ustring: (argName: string, v: unknown) => helpers.ustring(functionPath, argName, v),
unumber: (argName: string, v: unknown) => helpers.unumber(functionPath, argName, v),
scriptArgs: (v: unknown) => helpers.scriptArgs(functionPath, v),
scriptIdentifier: (fn: unknown, hostname: unknown, args: unknown) =>
helpers.scriptIdentifier(functionPath, fn, hostname, args),
city: (argName: string, v: unknown) => helpers.city(functionPath, argName, v), city: (argName: string, v: unknown) => helpers.city(functionPath, argName, v),
boolean: helpers.boolean, boolean: helpers.boolean,
getServer: (hostname: string) => helpers.getServer(hostname, ctx), getServer: (hostname: string) => helpers.getServer(hostname, ctx),
checkSingularityAccess: () => helpers.checkSingularityAccess(functionName), checkSingularityAccess: () => helpers.checkSingularityAccess(functionName),
hack: (hostname: any, manual: any, extra?: any) => helpers.hack(ctx, hostname, manual, extra), hack: (hostname: string, manual: boolean, extra?: BasicHGWOptions) => helpers.hack(ctx, hostname, manual, extra),
getValidPort: (port: any) => helpers.getValidPort(functionPath, port), getValidPort: (port: number) => helpers.getValidPort(functionPath, port),
player: (p: unknown) => helpers.player(functionPath, p),
server: (s: unknown) => helpers.server(functionPath, s),
gang: (g: unknown) => helpers.gang(functionPath, g),
gangMember: (m: unknown) => helpers.gangMember(functionPath, m),
gangTask: (t: unknown) => helpers.gangTask(functionPath, t),
}, },
}; };
function wrappedFunction(...args: unknown[]): unknown { function wrappedFunction(...args: unknown[]): unknown {
@ -112,48 +119,43 @@ function wrapFunction(
} }
export function wrapAPI( export function wrapAPI(
helpers: NetscriptHelpers, helpers: INetscriptHelper,
wrappedAPI: ExternalAPI, wrappedAPI: ExternalAPI,
workerScript: WorkerScript, workerScript: WorkerScript,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types namespace: object,
namespace: any,
...tree: string[] ...tree: string[]
): WrappedNetscriptAPI { ): WrappedNetscriptAPI {
if (typeof namespace !== "object") throw new Error("Invalid namespace?"); for (const [key, value] of Object.entries(namespace)) {
for (const property of Object.getOwnPropertyNames(namespace)) { if (typeof value === "function") {
switch (typeof namespace[property]) { wrapFunction(helpers, wrappedAPI, workerScript, value, ...tree, key);
case "function": { } else if (Array.isArray(value)) {
wrapFunction(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property); setNestedProperty(wrappedAPI, value, key);
break; } else if (typeof value === "object") {
} wrapAPI(helpers, wrappedAPI, workerScript, value, ...tree, key);
case "object": { } else {
wrapAPI(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property); setNestedProperty(wrappedAPI, value, ...tree, key);
break;
}
default: {
setNestedProperty(wrappedAPI, namespace[property], ...tree, property);
}
} }
} }
return wrappedAPI; return wrappedAPI;
} }
function setNestedProperty(root: any, value: any, ...tree: string[]): any { // TODO: This doesn't even work properly.
function setNestedProperty(root: object, value: unknown, ...tree: string[]): void {
let target = root; let target = root;
const key = tree.pop(); const key = tree.pop();
if (typeof key !== "string") { if (!key) {
throw new Error("Failure occured while wrapping netscript api (setNestedProperty)"); throw new Error("Failure occured while wrapping netscript api (setNestedProperty)");
} }
for (const branch of tree) { for (let branch of Object.values(target)) {
if (target[branch] === undefined) { if (branch === undefined) {
target[branch] = {}; branch = {};
} }
target = target[branch]; target = branch;
} }
target[key] = value; Object.assign(target, { [key]: value });
} }
function getNestedProperty(root: any, ...tree: string[]): any { function getNestedProperty(root: any, ...tree: string[]): unknown {
let target = root; let target = root;
for (const branch of tree) { for (const branch of tree) {
if (target[branch] === undefined) { if (target[branch] === undefined) {

@ -1,16 +1,10 @@
import { NS } from "../ScriptEditor/NetscriptDefinitions";
/** /**
* The environment in which a script runs. The environment holds * The environment in which a script runs. The environment holds
* Netscript functions and arguments for that script. * Netscript functions and arguments for that script.
*/ */
import { NS } from "src/ScriptEditor/NetscriptDefinitions";
import { IMap } from "../types";
export class Environment { export class Environment {
/**
* Parent environment. Used to implement "scope"
*/
parent: Environment | null = null;
/** /**
* Whether or not the script that uses this Environment should stop running * Whether or not the script that uses this Environment should stop running
*/ */
@ -19,58 +13,5 @@ export class Environment {
/** /**
* Environment variables (currently only Netscript functions) * Environment variables (currently only Netscript functions)
*/ */
vars: any = {}; vars: NS | null = null;
constructor(parent: Environment | null) {
if (parent instanceof Environment) {
this.vars = Object.assign({}, parent.vars);
}
this.parent = parent;
}
/**
* Finds the scope where the variable with the given name is defined
*/
lookup(name: string): Environment | null {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let scope: Environment | null = this;
while (scope) {
if (Object.prototype.hasOwnProperty.call(scope.vars, name)) {
return scope;
}
scope = scope.parent;
}
return null;
}
//Get the current value of a variable
get(name: string): any {
if (name in this.vars) {
return this.vars[name];
}
throw new Error(`Undefined variable ${name}`);
}
//Sets the value of a variable in any scope
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
set(name: string, value: any): any {
const scope = this.lookup(name);
//If scope has a value, then this variable is already set in a higher scope, so
//set is there. Otherwise, create a new variable in the local scope
if (scope !== null) {
return (scope.vars[name] = value);
} else {
return (this.vars[name] = value);
}
}
//Creates (or overwrites) a variable in the current scope
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
def(name: string, value: any): any {
return (this.vars[name] = value);
}
} }

@ -62,7 +62,6 @@ export const RamCostConstants: IMap<number> = {
ScriptGetFavorToDonate: 0.1, ScriptGetFavorToDonate: 0.1,
ScriptCodingContractBaseRamCost: 10, ScriptCodingContractBaseRamCost: 10,
ScriptSleeveBaseRamCost: 4, ScriptSleeveBaseRamCost: 4,
ScriptGetOwnedSourceFiles: 5,
ScriptClearTerminalCost: 0.2, ScriptClearTerminalCost: 0.2,
ScriptSingularityFn1RamCost: 2, ScriptSingularityFn1RamCost: 2,
@ -135,8 +134,8 @@ const stock = {
getMaxShares: RamCostConstants.ScriptGetStockRamCost, getMaxShares: RamCostConstants.ScriptGetStockRamCost,
getPurchaseCost: RamCostConstants.ScriptGetStockRamCost, getPurchaseCost: RamCostConstants.ScriptGetStockRamCost,
getSaleGain: RamCostConstants.ScriptGetStockRamCost, getSaleGain: RamCostConstants.ScriptGetStockRamCost,
buy: RamCostConstants.ScriptBuySellStockRamCost, buyStock: RamCostConstants.ScriptBuySellStockRamCost,
sell: RamCostConstants.ScriptBuySellStockRamCost, sellStock: RamCostConstants.ScriptBuySellStockRamCost,
short: RamCostConstants.ScriptBuySellStockRamCost, short: RamCostConstants.ScriptBuySellStockRamCost,
sellShort: RamCostConstants.ScriptBuySellStockRamCost, sellShort: RamCostConstants.ScriptBuySellStockRamCost,
placeOrder: RamCostConstants.ScriptBuySellStockRamCost, placeOrder: RamCostConstants.ScriptBuySellStockRamCost,
@ -191,10 +190,12 @@ const singularity = {
getCrimeChance: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), getCrimeChance: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getCrimeStats: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), getCrimeStats: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getOwnedAugmentations: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), getOwnedAugmentations: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getOwnedSourceFiles: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationsFromFaction: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), getAugmentationsFromFaction: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationCost: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), getAugmentationCost: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationPrereq: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), getAugmentationPrereq: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationPrice: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2), getAugmentationPrice: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2),
getAugmentationBasePrice: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2),
getAugmentationRepReq: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2), getAugmentationRepReq: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2),
getAugmentationStats: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), getAugmentationStats: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
purchaseAugmentation: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost), purchaseAugmentation: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
@ -505,7 +506,9 @@ const SourceRamCosts = {
getHackTime: RamCostConstants.ScriptGetHackTimeRamCost, getHackTime: RamCostConstants.ScriptGetHackTimeRamCost,
getGrowTime: RamCostConstants.ScriptGetHackTimeRamCost, getGrowTime: RamCostConstants.ScriptGetHackTimeRamCost,
getWeakenTime: RamCostConstants.ScriptGetHackTimeRamCost, getWeakenTime: RamCostConstants.ScriptGetHackTimeRamCost,
getTotalScriptIncome: RamCostConstants.ScriptGetScriptRamCost,
getScriptIncome: RamCostConstants.ScriptGetScriptRamCost, getScriptIncome: RamCostConstants.ScriptGetScriptRamCost,
getTotalScriptExpGain: RamCostConstants.ScriptGetScriptRamCost,
getScriptExpGain: RamCostConstants.ScriptGetScriptRamCost, getScriptExpGain: RamCostConstants.ScriptGetScriptRamCost,
getRunningScript: RamCostConstants.ScriptGetRunningScriptRamCost, getRunningScript: RamCostConstants.ScriptGetRunningScriptRamCost,
nFormat: 0, nFormat: 0,
@ -516,7 +519,6 @@ const SourceRamCosts = {
getFavorToDonate: RamCostConstants.ScriptGetFavorToDonate, getFavorToDonate: RamCostConstants.ScriptGetFavorToDonate,
getPlayer: RamCostConstants.ScriptSingularityFn1RamCost / 4, getPlayer: RamCostConstants.ScriptSingularityFn1RamCost / 4,
mv: 0, mv: 0,
getOwnedSourceFiles: RamCostConstants.ScriptGetOwnedSourceFiles,
tail: 0, tail: 0,
toast: 0, toast: 0,
closeTail: 0, closeTail: 0,

@ -0,0 +1 @@
export type ScriptArg = string | number | boolean;

@ -15,12 +15,14 @@ import { GetServer } from "../Server/AllServers";
import { BaseServer } from "../Server/BaseServer"; import { BaseServer } from "../Server/BaseServer";
import { IMap } from "../types"; import { IMap } from "../types";
import { NS } from "../ScriptEditor/NetscriptDefinitions"; import { NS } from "../ScriptEditor/NetscriptDefinitions";
import { ScriptDeath } from "./ScriptDeath";
import { ScriptArg } from "./ScriptArg";
export class WorkerScript { export class WorkerScript {
/** /**
* Script's arguments * Script's arguments
*/ */
args: any[]; args: ScriptArg[];
/** /**
* Copy of the script's code * Copy of the script's code
@ -36,7 +38,7 @@ export class WorkerScript {
/** /**
* Holds the Promise reject() function while the script is "blocked" by an async op * Holds the Promise reject() function while the script is "blocked" by an async op
*/ */
delayReject?: (reason?: any) => void; delayReject?: (reason?: ScriptDeath) => void;
/** /**
* Stores names of all functions that have logging disabled * Stores names of all functions that have logging disabled
@ -110,7 +112,7 @@ export class WorkerScript {
/** /**
* Function called when the script ends. * Function called when the script ends.
*/ */
atExit: any; atExit?: () => void;
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => NS) { constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => NS) {
this.name = runningScriptObj.filename; this.name = runningScriptObj.filename;
@ -140,11 +142,10 @@ export class WorkerScript {
} }
this.scriptRef = runningScriptObj; this.scriptRef = runningScriptObj;
this.args = runningScriptObj.args.slice(); this.args = runningScriptObj.args.slice();
this.env = new Environment(null); this.env = new Environment();
if (typeof nsFuncsGenerator === "function") { if (typeof nsFuncsGenerator === "function") {
this.env.vars = nsFuncsGenerator(this); this.env.vars = nsFuncsGenerator(this);
} }
this.env.set("args", runningScriptObj.args.slice());
} }
/** /**

@ -15,52 +15,43 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { AddRecentScript } from "./RecentScripts"; import { AddRecentScript } from "./RecentScripts";
import { Player } from "../Player"; import { Player } from "../Player";
export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean; export type killScriptParams = WorkerScript | number | { runningScript: RunningScript; hostname: string };
export function killWorkerScript(workerScript: WorkerScript): boolean;
export function killWorkerScript(pid: number): boolean;
export function killWorkerScript(
script: RunningScript | WorkerScript | number,
hostname?: string,
rerenderUi?: boolean,
): boolean {
if (rerenderUi == null || typeof rerenderUi !== "boolean") {
rerenderUi = true;
}
if (script instanceof WorkerScript) { export function killWorkerScript(params: killScriptParams): boolean {
stopAndCleanUpWorkerScript(script); if (params instanceof WorkerScript) {
stopAndCleanUpWorkerScript(params);
return true; return true;
} else if (script instanceof RunningScript && typeof hostname === "string") { } else if (typeof params === "number") {
return killWorkerScriptByPid(params);
} else {
// Try to kill by PID // Try to kill by PID
const res = killWorkerScriptByPid(script.pid, rerenderUi); const res = killWorkerScriptByPid(params.runningScript.pid);
if (res) { if (res) {
return res; return res;
} }
// If for some reason that doesn't work, we'll try the old way // If for some reason that doesn't work, we'll try the old way
for (const ws of workerScripts.values()) { for (const ws of workerScripts.values()) {
if (ws.name == script.filename && ws.hostname == hostname && compareArrays(ws.args, script.args)) { if (
stopAndCleanUpWorkerScript(ws, rerenderUi); ws.name == params.runningScript.filename &&
ws.hostname == params.hostname &&
compareArrays(ws.args, params.runningScript.args)
) {
stopAndCleanUpWorkerScript(ws);
return true; return true;
} }
} }
return false;
} else if (typeof script === "number") {
return killWorkerScriptByPid(script, rerenderUi);
} else {
console.error(`killWorkerScript() called with invalid argument:`);
console.error(script);
return false; return false;
} }
} }
function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean { function killWorkerScriptByPid(pid: number): boolean {
const ws = workerScripts.get(pid); const ws = workerScripts.get(pid);
if (ws instanceof WorkerScript) { if (ws instanceof WorkerScript) {
stopAndCleanUpWorkerScript(ws, rerenderUi); stopAndCleanUpWorkerScript(ws);
return true; return true;
} }
@ -68,20 +59,22 @@ function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean {
return false; return false;
} }
function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void { function stopAndCleanUpWorkerScript(workerScript: WorkerScript): void {
if (typeof workerScript.atExit === "function") { if (typeof workerScript.atExit === "function") {
try { try {
workerScript.atExit(); workerScript.atExit();
} catch (e: any) { } catch (e: unknown) {
dialogBoxCreate( dialogBoxCreate(
`Error trying to call atExit for script ${workerScript.name} on ${workerScript.hostname} ${workerScript.scriptRef.args} ${e}`, `Error trying to call atExit for script ${workerScript.name} on ${workerScript.hostname} ${
workerScript.scriptRef.args
} ${String(e)}`,
); );
} }
workerScript.atExit = undefined; workerScript.atExit = undefined;
} }
workerScript.env.stopFlag = true; workerScript.env.stopFlag = true;
killNetscriptDelay(workerScript); killNetscriptDelay(workerScript);
removeWorkerScript(workerScript, rerenderUi); removeWorkerScript(workerScript);
} }
/** /**
@ -91,7 +84,7 @@ function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = tru
* @param {WorkerScript} - Identifier for WorkerScript. Either the object itself, or * @param {WorkerScript} - Identifier for WorkerScript. Either the object itself, or
* its index in the global workerScripts array * its index in the global workerScripts array
*/ */
function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void { function removeWorkerScript(workerScript: WorkerScript): void {
const ip = workerScript.hostname; const ip = workerScript.hostname;
const name = workerScript.name; const name = workerScript.name;
@ -125,10 +118,8 @@ function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void
// } // }
AddRecentScript(workerScript); AddRecentScript(workerScript);
if (rerenderUi) {
WorkerScriptStartStopEventEmitter.emit(); WorkerScriptStartStopEventEmitter.emit();
} }
}
/** /**
* Helper function that interrupts a script's delay if it is in the middle of a * Helper function that interrupts a script's delay if it is in the middle of a

Some files were not shown because too many files have changed in this diff Show More