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: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
// "plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
],
parser: "@typescript-eslint/parser",
parserOptions: {
@ -19,386 +19,9 @@ module.exports = {
project: ["./tsconfig.json", "./test/tsconfig.json", "./tools/tsconfig.json", "./test/cypress/tsconfig.json"],
},
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"],
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-ignore": "off",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
allowExpressions: true,
},
],
"@typescript-eslint/member-delimiter-style": [
"error",
{
multiline: {
delimiter: "semi",
requireLast: true,
},
singleline: {
delimiter: "semi",
requireLast: false,
},
},
],
"@typescript-eslint/member-ordering": [
"error",
{
default: [
"signature",
"static-field",
"instance-field",
"abstract-field",
"constructor",
"instance-method",
"abstract-method",
"static-method",
],
},
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-use-before-define": "off",
},
},
],
};

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

@ -24,7 +24,7 @@ import { IMap } from "../types";
import * as data from "./AchievementData.json";
import { FactionNames } from "../Faction/data/FactionNames";
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...
const achievementData = (<AchievementDataJson>(<unknown>data)).achievements;
@ -391,10 +391,7 @@ export const achievements: IMap<Achievement> = {
WORKOUT: {
...achievementData["WORKOUT"],
Icon: "WORKOUT",
Condition: () =>
[ClassType.GymStrength, ClassType.GymDefense, ClassType.GymDexterity, ClassType.GymAgility].includes(
Player.className,
),
Condition: () => isClassWork(Player.currentWork),
},
TOR: {
...achievementData["TOR"],

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

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

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

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

@ -1,7 +1,7 @@
import { Player } from "../Player";
import { getRandomInt } from "../utils/helpers/getRandomInt";
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 { IBladeburner } from "./IBladeburner";
import { IAction, ISuccessChanceParams } from "./IAction";
@ -274,7 +274,7 @@ export class Action implements IAction {
}
// Augmentation multiplier
competence *= Player.bladeburner_success_chance_mult;
competence *= Player.mults.bladeburner_success_chance;
if (isNaN(competence)) {
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);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Action {
static fromJSON(value: IReviverValue): Action {
return Generic_fromJSON(Action, value.data);
}
}

@ -1,5 +1,5 @@
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 {
name?: string;
@ -15,12 +15,11 @@ export class ActionIdentifier implements IActionIdentifier {
if (params.type) this.type = params.type;
}
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("ActionIdentifier", this);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): ActionIdentifier {
static fromJSON(value: IReviverValue): ActionIdentifier {
return Generic_fromJSON(ActionIdentifier, value.data);
}
}

@ -1,5 +1,5 @@
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 {
constructor(params: IOperationParams | null = null) {
@ -20,12 +20,11 @@ export class BlackOperation extends Operation {
return 1;
}
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("BlackOperation", this);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Operation {
static fromJSON(value: IReviverValue): Operation {
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 { IActionIdentifier } from "./IActionIdentifier";
import { ActionIdentifier } from "./ActionIdentifier";
@ -178,7 +178,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this, person);
} catch (e: any) {
} catch (e: unknown) {
exceptionAlert(e);
}
break;
@ -195,7 +195,7 @@ export class Bladeburner implements IBladeburner {
return this.resetAction();
}
this.actionTimeToComplete = action.getActionTime(this, person);
} catch (e: any) {
} catch (e: unknown) {
exceptionAlert(e);
}
break;
@ -213,7 +213,7 @@ export class Bladeburner implements IBladeburner {
throw new Error("action should not be null");
}
this.actionTimeToComplete = testBlackOp.action.getActionTime(this, person);
} catch (e: any) {
} catch (e: unknown) {
exceptionAlert(e);
}
break;
@ -264,7 +264,7 @@ export class Bladeburner implements IBladeburner {
for (let i = 0; i < arrayOfCommands.length; ++i) {
this.executeConsoleCommand(player, arrayOfCommands[i]);
}
} catch (e: any) {
} catch (e: unknown) {
exceptionAlert(e);
}
}
@ -794,21 +794,9 @@ export class Bladeburner implements IBladeburner {
let i = 0;
while (i < command.length) {
const c = command.charAt(i);
if (c === '"') {
// Double quotes
const endQuote = command.indexOf('"', 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 (c === '"' || c === "'") {
// Double quotes or Single quotes
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) {
@ -1367,7 +1355,7 @@ export class Bladeburner implements IBladeburner {
if (action.autoLevel) {
action.level = action.maxLevel;
} // Autolevel
} catch (e: any) {
} catch (e: unknown) {
exceptionAlert(e);
}
break;
@ -1462,17 +1450,17 @@ export class Bladeburner implements IBladeburner {
this.log(`${person.whoAmI()}: You lost ${formatNumber(losses, 0)} team members during ${action.name}`);
}
}
} catch (e: any) {
} catch (e: unknown) {
exceptionAlert(e);
}
break;
}
case ActionTypes["Training"]: {
this.stamina -= 0.5 * BladeburnerConstants.BaseStaminaLoss;
const strExpGain = 30 * person.strength_exp_mult,
defExpGain = 30 * person.defense_exp_mult,
dexExpGain = 30 * person.dexterity_exp_mult,
agiExpGain = 30 * person.agility_exp_mult,
const strExpGain = 30 * person.mults.strength_exp,
defExpGain = 30 * person.mults.defense_exp,
dexExpGain = 30 * person.mults.dexterity_exp,
agiExpGain = 30 * person.mults.agility_exp,
staminaGain = 0.04 * this.skillMultipliers.stamina;
retValue.str = strExpGain;
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.intelligence, 0.9) +
0.02 * Math.pow(person.charisma, 0.3);
eff *= person.bladeburner_analysis_mult;
eff *= person.mults.bladeburner_analysis;
if (isNaN(eff) || eff < 0) {
throw new Error("Field Analysis Effectiveness calculated to be NaN or negative");
}
const hackingExpGain = 20 * person.hacking_exp_mult;
const charismaExpGain = 20 * person.charisma_exp_mult;
const hackingExpGain = 20 * person.mults.hacking_exp;
const charismaExpGain = 20 * person.mults.charisma_exp;
const rankGain = 0.1 * BitNodeMultipliers.BladeburnerRank;
retValue.hack = hackingExpGain;
retValue.cha = charismaExpGain;
@ -1647,7 +1635,7 @@ export class Bladeburner implements IBladeburner {
if (bladeburnerFac.isMember) {
const favorBonus = 1 + bladeburnerFac.favor / 100;
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 maxStaminaBonus = this.maxStamina / BladeburnerConstants.MaxStaminaToGainFactor;
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 {
@ -1703,7 +1691,7 @@ export class Bladeburner implements IBladeburner {
const maxStamina =
(Math.pow(effAgility, 0.8) + this.staminaBonus) *
this.skillMultipliers.stamina *
player.bladeburner_max_stamina_mult;
player.mults.bladeburner_max_stamina;
if (this.maxStamina !== maxStamina) {
const oldMax = this.maxStamina;
this.maxStamina = maxStamina;
@ -1988,7 +1976,7 @@ export class Bladeburner implements IBladeburner {
if (!router.isInitialized) return;
// 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"]) {
let msg = "Your Bladeburner action was cancelled because you started doing something else.";
if (this.automateEnabled) {
@ -2154,7 +2142,8 @@ export class Bladeburner implements IBladeburner {
() => `Starting bladeburner action with type '${type}' and name '${name}'`,
);
return true;
} catch (e: any) {
} catch (e: unknown) {
console.error(e);
this.resetAction();
workerScript.log("bladeburner.startAction", () => errorLogText);
return false;
@ -2409,15 +2398,14 @@ export class Bladeburner implements IBladeburner {
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Bladeburner", this);
}
/**
* Initiatizes a Bladeburner object from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Bladeburner {
static fromJSON(value: IReviverValue): Bladeburner {
return Generic_fromJSON(Bladeburner, value.data);
}
}

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

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

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

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

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

@ -14,7 +14,7 @@ import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
interface ILineProps {
content: any;
content: React.ReactNode;
}
const useStyles = makeStyles((theme: Theme) =>
@ -197,7 +197,7 @@ function Logs({ entries }: ILogProps): React.ReactElement {
return (
<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>
);
}

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

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

@ -33,7 +33,7 @@ export function StartButton(props: IProps): React.ReactElement {
if (disabled) return;
props.bladeburner.action.type = props.type;
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.rerender();
}

@ -173,13 +173,13 @@ export function Stats(props: IProps): React.ReactElement {
<Typography>Skill Points: {formatNumber(props.bladeburner.skillPoints, 0)}</Typography>
<br />
<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 />
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 />
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 />
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>
</Box>
</Paper>

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

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

@ -4,7 +4,7 @@ import { favorToRep, repToFavor } from "../Faction/formulas/favor";
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 {
name: string;
@ -151,15 +151,14 @@ export class Company {
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Company", this);
}
/**
* Initiatizes a Company from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Company {
static fromJSON(value: IReviverValue): Company {
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
LatestUpdate: string;
} = {
VersionString: "1.7.0",
VersionNumber: 19,
VersionString: "2.0.0",
VersionNumber: 21,
// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
@ -104,7 +104,7 @@ export const CONSTANTS: {
MilliPerCycle: 200,
// How much reputation is needed to join a megacorporation's faction
CorpFactionRepRequirement: 200e3,
CorpFactionRepRequirement: 400e3,
// Base RAM costs
BaseCostFor1GBOfRamHome: 32000,
@ -229,171 +229,54 @@ export const CONSTANTS: {
InfiniteLoopLimit: 2000,
Donations: 20,
Donations: 21,
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)
- [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)
API breaks
#### 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)
- 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)
- fix documentation for getDarkwebPrograms (by @hydroflame) - [4056956](https://github.com/danielyxie/bitburner/commit/4056956c2ada37946333bdad44cb0b6eb3909bf8)
- 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)
- 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)
- 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.
Multipliers
* The main player object was also plagues with a million fields all called '*_mult'. Representing the different multipliers
* These have been refactored in a field called 'mults'.
Misc.
* Nerf noodle bar, obviously.
`,
};

@ -11,7 +11,7 @@ import { LiteratureNames } from "../Literature/data/LiteratureNames";
import { IPlayer } from "../PersonObjects/IPlayer";
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";
interface IParams {
@ -433,15 +433,14 @@ export class Corporation {
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Corporation", this);
}
/**
* Initiatizes a Corporation object from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Corporation {
static fromJSON(value: IReviverValue): Corporation {
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
const AllCorporationStates: string[] = ["START", "PURCHASE", "PRODUCTION", "SALE", "EXPORT"];
@ -28,13 +28,12 @@ export class CorporationState {
}
// Serialize the current object to a JSON save state.
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("CorporationState", this);
}
// Initiatizes a CorporationState object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): CorporationState {
static fromJSON(value: IReviverValue): CorporationState {
return Generic_fromJSON(CorporationState, value.data);
}
}

@ -1,6 +1,6 @@
import { CorporationConstants } from "./data/Constants";
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 { ICorporation } from "./ICorporation";
import { IIndustry } from "./IIndustry";
@ -113,12 +113,11 @@ export class Employee {
return prodBase * prodMult;
}
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Employee", this);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Employee {
static fromJSON(value: IReviverValue): Employee {
return Generic_fromJSON(Employee, value.data);
}
}

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

@ -3,6 +3,7 @@ import { Warehouse } from "./Warehouse";
import { ICorporation } from "./ICorporation";
import { OfficeSpace } from "./OfficeSpace";
import { Product } from "./Product";
import { IReviverValue } from "../utils/JSONReviver";
export interface IIndustry {
name: string;
@ -72,5 +73,5 @@ export interface IIndustry {
getSalesMultiplier(): number;
getScientificResearchMultiplier(): 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 { Industries, IndustryStartingCosts, IndustryResearchTrees } from "./IndustryData";
import { CorporationConstants } from "./data/Constants";
@ -1403,15 +1403,14 @@ export class Industry implements IIndustry {
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Industry", this);
}
/**
* Initiatizes a Industry object from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Industry {
static fromJSON(value: IReviverValue): Industry {
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";
interface IConstructorParams {
@ -229,13 +229,12 @@ export class Material {
}
// Serialize the current object to a JSON save state.
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Material", this);
}
// Initiatizes a Material object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Material {
static fromJSON(value: IReviverValue): Material {
return Generic_fromJSON(Material, value.data);
}
}

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

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

@ -8,8 +8,6 @@ import { ResearchMap } from "./ResearchMap";
import { IMap } from "../types";
import { numeralWrapper } from "../ui/numeralFormat";
interface IConstructorParams {
children?: Node[];
cost: number;
@ -60,40 +58,6 @@ export class Node {
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
findNode(text: string): Node | null {
// Is this the Node?
@ -127,23 +91,6 @@ export class ResearchTree {
// Root Node
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
getAllNodes(): string[] {
const res: string[] = [];
@ -236,7 +183,20 @@ export class ResearchTree {
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) {
console.warn(`Invalid propName specified in ResearchTree.getMultiplierHelper: ${propName}`);
continue;

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

@ -124,8 +124,8 @@ export function IndustryOverview(props: IProps): React.ReactElement {
<br />
<StatsTable
rows={[
["Awareness:", numeralWrapper.format(division.awareness, "0.000")],
["Popularity:", numeralWrapper.format(division.popularity, "0.000")],
["Awareness:", numeralWrapper.formatReallyBigNumber(division.awareness)],
["Popularity:", numeralWrapper.formatReallyBigNumber(division.popularity)],
]}
/>
{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>
<StatsTable
rows={[
["Awareness Bonus:", "x" + numeralWrapper.format(Math.pow(awarenessFac, 0.85), "0.000")],
["Popularity Bonus:", "x" + numeralWrapper.format(Math.pow(popularityFac, 0.85), "0.000")],
["Ratio Multiplier:", "x" + numeralWrapper.format(Math.pow(ratioFac, 0.85), "0.000")],
["Awareness Bonus:", "x" + numeralWrapper.formatReallyBigNumber(Math.pow(awarenessFac, 0.85))],
["Popularity Bonus:", "x" + numeralWrapper.formatReallyBigNumber(Math.pow(popularityFac, 0.85))],
["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>
)}
<br />
@ -164,7 +164,7 @@ export function IndustryOverview(props: IProps): React.ReactElement {
</Typography>
}
>
<Typography>Production Multiplier: {numeralWrapper.format(division.prodMult, "0.00")}</Typography>
<Typography>Production Multiplier: {numeralWrapper.formatReallyBigNumber(division.prodMult)}</Typography>
</Tooltip>
<IconButton onClick={() => setHelpOpen(true)}>
<HelpIcon />
@ -203,7 +203,7 @@ export function IndustryOverview(props: IProps): React.ReactElement {
</Typography>
}
>
<Typography>Scientific Research: {numeralWrapper.format(division.sciResearch.qty, "0.000a")}</Typography>
<Typography>Scientific Research: {numeralWrapper.formatReallyBigNumber(division.sciResearch.qty)}</Typography>
</Tooltip>
<Button sx={{ mx: 1 }} onClick={() => setResearchOpen(true)}>
Research

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

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

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

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

@ -6,7 +6,7 @@ import { getRandomInt } from "../../../utils/helpers/getRandomInt";
import { CorporationConstants } from "../../data/Constants";
import { useCorporation } from "../Context";
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 { 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
export function IssueNewSharesModal(props: IProps): React.ReactElement {
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 maxNewShares = maxNewSharesUnrounded - (maxNewSharesUnrounded % 1e6);
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 {
if (shares === null) return;
if (isNaN(shares)) return;
if (disabled) return;
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();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setShares(null);
else setShares(parseFloat(event.target.value));
}
return (
<Modal open={props.open} onClose={props.onClose}>
<Typography>
@ -124,7 +119,7 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
you cannot buy them back.
</Typography>
<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 }}>
Issue New Shares
</Button>

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

@ -6,11 +6,11 @@ import { use } from "../../../ui/Context";
import { useCorporation } from "../Context";
import { ICorporation } from "../../ICorporation";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import { Money } from "../../../ui/React/Money";
import { SellShares } from "../../Actions";
import { KEY } from "../../../utils/helpers/keyCodes";
import { NumberInput } from "../../../ui/React/NumberInput";
interface IProps {
open: boolean;
onClose: () => void;
@ -22,14 +22,9 @@ interface IProps {
export function SellSharesModal(props: IProps): React.ReactElement {
const player = use.Player();
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;
function changeShares(event: React.ChangeEvent<HTMLInputElement>): void {
if (event.target.value === "") setShares(null);
else setShares(Math.round(parseFloat(event.target.value)));
}
const disabled = isNaN(shares) || shares <= 0 || shares > corp.numShares;
function ProfitIndicator(props: { shares: number | null; corp: ICorporation }): React.ReactElement {
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)}
</Typography>
<br />
<TextField
<NumberInput
variant="standard"
autoFocus
type="number"
placeholder="Shares to sell"
onChange={changeShares}
onChange={setShares}
onKeyDown={onKeyDown}
/>
<Button disabled={disabled} onClick={sell} sx={{ mx: 1 }}>

@ -1,5 +1,5 @@
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 {
x: number;
@ -74,15 +74,14 @@ export class ActiveFragment {
/**
* Serialize an active fragment to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("ActiveFragment", this);
}
/**
* Initializes an acive fragment from a JSON save state
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): ActiveFragment {
static fromJSON(value: IReviverValue): ActiveFragment {
return Generic_fromJSON(ActiveFragment, value.data);
}
}

@ -19,22 +19,22 @@ export class DummyGift implements IStaneksGift {
height(): number {
return this._height;
}
charge(): any {
charge(): void {
throw new Error("unimplemented for dummy gift");
}
process(): any {
process(): void {
throw new Error("unimplemented for dummy gift");
}
effect(): any {
effect(): number {
throw new Error("unimplemented for dummy gift");
}
canPlace(): any {
canPlace(): boolean {
throw new Error("unimplemented for dummy gift");
}
place(): any {
place(): boolean {
throw new Error("unimplemented for dummy gift");
}
findFragment(): any {
findFragment(): ActiveFragment | undefined {
throw new Error("unimplemented for dummy gift");
}
fragmentAt(worldX: number, worldY: number): ActiveFragment | undefined {
@ -46,22 +46,22 @@ export class DummyGift implements IStaneksGift {
return undefined;
}
delete(): any {
delete(): boolean {
throw new Error("unimplemented for dummy gift");
}
clear(): any {
clear(): void {
throw new Error("unimplemented for dummy gift");
}
count(): any {
count(): number {
throw new Error("unimplemented for dummy gift");
}
inBonus(): any {
inBonus(): boolean {
throw new Error("unimplemented for dummy gift");
}
prestigeAugmentation(): any {
prestigeAugmentation(): void {
throw new Error("unimplemented for dummy gift");
}
prestigeSourceFile(): any {
prestigeSourceFile(): void {
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 = [];
for (let i = 0; i < dimensions[0]; ++i) {
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
for (let i = 0; i < width; ++i) {
array.push(Array(height).fill(0));
}
return array;
}
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 j = 0; j < gift.height(); j++) {
const fragment = gift.fragmentAt(i, j);

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

@ -13,7 +13,7 @@ interface IProps {
export function DummyGrid(props: IProps): React.ReactElement {
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 (
<Box>
<Table sx={{ width: props.width, height: props.height }}>

@ -18,7 +18,7 @@ interface IProps {
}
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 [rotation, setRotation] = React.useState(0);
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 {
setPos([worldX, worldY]);
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 x = 0; x < selectedFragment.width(rotation); x++) {
if (!selectedFragment.fullAt(x, y, rotation)) continue;
@ -61,7 +61,7 @@ export function MainBoard(props: IProps): React.ReactElement {
function updateSelectedFragment(fragment: Fragment): void {
setSelectedFragment(fragment);
const newgrid = zeros([props.gift.width(), props.gift.height()]);
const newgrid = zeros(props.gift.width(), props.gift.height());
setGhostGrid(newgrid);
}

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

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

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

@ -31,7 +31,7 @@ declare global {
};
electronBridge: {
send: (channel: string, data?: unknown) => void;
receive: (channel: string, func: (...args: any[]) => void) => void;
receive: (channel: string, func: (...args: unknown[]) => void) => void;
};
}
interface Document {
@ -150,7 +150,7 @@ function initSaveFunctions(): void {
try {
saveObject.exportGame();
} catch (error) {
console.log(error);
console.error(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
}
},
@ -185,11 +185,14 @@ function initElectronBridge(): void {
const data = window.appSaveFns.getSaveData();
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);
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);
});
bridge.receive("trigger-save", () => {
@ -199,7 +202,7 @@ function initElectronBridge(): void {
bridge.send("save-completed");
})
.catch((error: unknown) => {
console.log(error);
console.error(error);
SnackbarEvents.emit("Could not save game.", ToastVariant.ERROR, 2000);
});
});
@ -207,7 +210,7 @@ function initElectronBridge(): void {
try {
window.appSaveFns.triggerGameExport();
} catch (error) {
console.log(error);
console.error(error);
SnackbarEvents.emit("Could not export game.", ToastVariant.ERROR, 2000);
}
});
@ -215,7 +218,7 @@ function initElectronBridge(): void {
try {
window.appSaveFns.triggerScriptsExport();
} catch (error) {
console.log(error);
console.error(error);
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 dec = Math.pow(0.999, Player.exploits.length);
Player.hacking_chance_mult *= inc;
Player.hacking_speed_mult *= inc;
Player.hacking_money_mult *= inc;
Player.hacking_grow_mult *= inc;
Player.hacking_mult *= inc;
Player.mults.hacking_chance *= inc;
Player.mults.hacking_speed *= inc;
Player.mults.hacking_money *= inc;
Player.mults.hacking_grow *= inc;
Player.mults.hacking *= inc;
Player.strength_mult *= inc;
Player.defense_mult *= inc;
Player.dexterity_mult *= inc;
Player.agility_mult *= inc;
Player.charisma_mult *= inc;
Player.mults.strength *= inc;
Player.mults.defense *= inc;
Player.mults.dexterity *= inc;
Player.mults.agility *= inc;
Player.mults.charisma *= inc;
Player.hacking_exp_mult *= inc;
Player.strength_exp_mult *= inc;
Player.defense_exp_mult *= inc;
Player.dexterity_exp_mult *= inc;
Player.agility_exp_mult *= inc;
Player.charisma_exp_mult *= inc;
Player.mults.hacking_exp *= inc;
Player.mults.strength_exp *= inc;
Player.mults.defense_exp *= inc;
Player.mults.dexterity_exp *= inc;
Player.mults.agility_exp *= inc;
Player.mults.charisma_exp *= inc;
Player.company_rep_mult *= inc;
Player.faction_rep_mult *= inc;
Player.mults.company_rep *= inc;
Player.mults.faction_rep *= inc;
Player.crime_money_mult *= inc;
Player.crime_success_mult *= inc;
Player.mults.crime_money *= inc;
Player.mults.crime_success *= inc;
Player.hacknet_node_money_mult *= inc;
Player.hacknet_node_purchase_cost_mult *= dec;
Player.hacknet_node_ram_cost_mult *= dec;
Player.hacknet_node_core_cost_mult *= dec;
Player.hacknet_node_level_cost_mult *= dec;
Player.mults.hacknet_node_money *= inc;
Player.mults.hacknet_node_purchase_cost *= dec;
Player.mults.hacknet_node_ram_cost *= dec;
Player.mults.hacknet_node_core_cost *= 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 { 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 {
/**
@ -75,15 +75,14 @@ export class Faction {
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Faction", this);
}
/**
* Initiatizes a Faction object from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Faction {
static fromJSON(value: IReviverValue): Faction {
return Generic_fromJSON(Faction, value.data);
}
}

@ -19,6 +19,7 @@ import { dialogBoxCreate } from "../ui/React/DialogBox";
import { InvitationEvent } from "./ui/InvitationModal";
import { FactionNames } from "./data/FactionNames";
import { SFC32RNG } from "../Casino/RNG";
import { isFactionWork } from "../Work/FactionWork";
export function inviteToFaction(faction: Faction): void {
Player.receiveInvite(faction.name);
@ -113,7 +114,7 @@ export function purchaseAugmentation(aug: Augmentation, fac: Faction, sing = fal
export function processPassiveFactionRepGain(numCycles: number): void {
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;
const faction = Factions[name];
if (!faction.isMember) continue;
@ -132,7 +133,7 @@ export function processPassiveFactionRepGain(numCycles: number): void {
const fRep = getFactionFieldWorkRepGain(Player, faction);
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";
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 { Reputation } from "../../ui/React/Reputation";
import { numeralWrapper } from "../../ui/numeralFormat";
import { dialogBoxCreate } from "../../ui/React/DialogBox";
import { MathJaxWrapper } from "../../MathJaxWrapper";

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

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

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

@ -8,7 +8,7 @@ import { Faction } from "../Faction/Faction";
import { Factions } from "../Faction/Factions";
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 { getRandomInt } from "../utils/helpers/getRandomInt";
@ -99,7 +99,7 @@ export class Gang implements IGang {
this.processExperienceGains(cycles);
this.processTerritoryAndPowerGains(cycles);
this.storedCycles -= cycles;
} catch (e: any) {
} catch (e: unknown) {
console.error(`Exception caught when processing Gang: ${e}`);
}
}
@ -132,7 +132,7 @@ export class Gang implements IGang {
}
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
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}`);
}
return res;
} catch (e: any) {
} catch (e: unknown) {
if (workerScript == null) {
exceptionAlert(e);
}
@ -399,15 +399,14 @@ export class Gang implements IGang {
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("Gang", this);
}
/**
* Initiatizes a Gang object from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): Gang {
static fromJSON(value: IReviverValue): Gang {
return Generic_fromJSON(Gang, value.data);
}
}

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

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

@ -2,17 +2,17 @@ import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { GangMember } from "../GangMember";
import { GangMemberTask } from "../GangMemberTask";
interface Gang {
export interface FormulaGang {
respect: number;
territory: number;
wantedLevel: number;
}
export function calculateWantedPenalty(gang: Gang): number {
export function calculateWantedPenalty(gang: FormulaGang): number {
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;
let statWeight =
(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);
}
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;
let statWeight =
(task.hackWeight / 100) * member.hack +
@ -53,7 +53,7 @@ export function calculateWantedLevelGain(gang: Gang, member: GangMember, task: G
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;
let statWeight =
(task.hackWeight / 100) * member.hack +

@ -12,7 +12,7 @@ export function calculateHackingChance(server: Server, player: IPlayer): number
const skillMult = hackFactor * player.hacking;
const skillChance = (skillMult - server.requiredHackingSkill) / skillMult;
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) {
return 1;
}
@ -36,7 +36,7 @@ export function calculateHackingExpGain(server: Server, player: IPlayer): number
let expGain = baseExpGain;
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 skillMult = (player.hacking - (server.requiredHackingSkill - 1)) / player.hacking;
const percentMoneyHacked =
(difficultyMult * skillMult * player.hacking_money_mult * BitNodeMultipliers.ScriptHackMoney) / balanceFactor;
(difficultyMult * skillMult * player.mults.hacking_money * BitNodeMultipliers.ScriptHackMoney) / balanceFactor;
if (percentMoneyHacked < 0) {
return 0;
}
@ -77,7 +77,7 @@ export function calculateHackingTime(server: Server, player: IPlayer): number {
const hackTimeMultiplier = 5;
const hackingTime =
(hackTimeMultiplier * skillFactor) /
(player.hacking_speed_mult * calculateIntelligenceBonus(player.intelligence, 1));
(player.mults.hacking_speed * calculateIntelligenceBonus(player.intelligence, 1));
return hackingTime;
}

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

@ -17,7 +17,7 @@ import {
import { HacknetNodeConstants } from "./data/Constants";
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";
export class HacknetNode implements IHacknetNode {
@ -123,15 +123,14 @@ export class HacknetNode implements IHacknetNode {
/**
* Serialize the current object to a JSON save state.
*/
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("HacknetNode", this);
}
/**
* Initiatizes a HacknetNode object from a JSON save state.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): HacknetNode {
static fromJSON(value: IReviverValue): HacknetNode {
return Generic_fromJSON(HacknetNode, value.data);
}
}

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

@ -10,7 +10,7 @@ import { HashUpgrades } from "./HashUpgrades";
import { HashUpgrade } from "./HashUpgrade";
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 {
// 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
*/
getUpgradeCost(upgName: string): number {
getUpgradeCost(upgName: string, count = 1): number {
const upg = this.getUpgrade(upgName);
const currLevel = this.upgrades[upgName];
if (upg == null || currLevel == null) {
@ -81,7 +81,7 @@ export class HashManager {
return Infinity;
}
return upg.getCost(currLevel);
return upg.getCost(currLevel, count);
}
prestige(): void {
@ -97,11 +97,11 @@ export class HashManager {
/**
* 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];
// Reduce the level first, so we get the right cost
--this.upgrades[upgName];
this.upgrades[upgName] -= count;
const currLevel = this.upgrades[upgName];
if (upg == null || currLevel == null || currLevel < 0) {
@ -109,7 +109,7 @@ export class HashManager {
return;
}
const cost = upg.getCost(currLevel);
const cost = upg.getCost(currLevel, count);
this.hashes += cost;
}
@ -137,33 +137,32 @@ export class HashManager {
* Returns boolean indicating whether or not the upgrade was successfully purchased
* Note that this does NOT actually implement the effect
*/
upgrade(upgName: string): boolean {
upgrade(upgName: string, count = 1): boolean {
const upg = HashUpgrades[upgName];
if (upg == null) {
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
return false;
}
const cost = this.getUpgradeCost(upgName);
const cost = this.getUpgradeCost(upgName, count);
if (this.hashes < cost) {
return false;
}
this.hashes -= cost;
++this.upgrades[upgName];
this.upgrades[upgName] += count;
return true;
}
//Serialize the current object to a JSON save state.
toJSON(): any {
toJSON(): IReviverValue {
return Generic_toJSON("HashManager", this);
}
// Initiatizes a HashManager object from a JSON save state.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
static fromJSON(value: any): HashManager {
static fromJSON(value: IReviverValue): HashManager {
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.
effectText: (level: number) => JSX.Element | null = () => null;
getCost(levels: number): number {
getCost(currentLevel: number, count = 1): 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 =
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;
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 = (
<Tooltip
title={
@ -102,9 +102,9 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
node.level,
node.ram * Math.pow(2, multiplier),
node.cores,
props.player.hacknet_node_money_mult,
props.player.mults.hacknet_node_money,
) - 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 = (
<Tooltip
title={
@ -148,9 +148,9 @@ export function HacknetNodeElem(props: IProps): React.ReactElement {
}
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;
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 = (
<Tooltip
title={

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

@ -16,10 +16,10 @@ export function calculateDifficulty(player: IPlayer, startingSecurityLevel: numb
export function calculateReward(player: IPlayer, startingSecurityLevel: number): number {
const xpMult = 10 * 60 * 15;
const total =
calculateSkill(player.strength_exp_mult * xpMult, player.strength_mult) +
calculateSkill(player.defense_exp_mult * xpMult, player.defense_mult) +
calculateSkill(player.agility_exp_mult * xpMult, player.agility_mult) +
calculateSkill(player.dexterity_exp_mult * xpMult, player.dexterity_mult) +
calculateSkill(player.charisma_exp_mult * xpMult, player.charisma_mult);
calculateSkill(player.mults.strength_exp * xpMult, player.mults.strength) +
calculateSkill(player.mults.defense_exp * xpMult, player.mults.defense) +
calculateSkill(player.mults.agility_exp * xpMult, player.mults.agility) +
calculateSkill(player.mults.dexterity_exp * xpMult, player.mults.dexterity) +
calculateSkill(player.mults.charisma_exp * xpMult, player.mults.charisma);
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 colorNames: any = {
const colorNames: Record<string, string> = {
red: "red",
"#FFC107": "yellow",
blue: "blue",

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

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

@ -8,16 +8,13 @@ import Button from "@mui/material/Button";
import { Location } from "../Location";
import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { GetServer } from "../../Server/AllServers";
import { Server } from "../../Server/Server";
import { Money } from "../../ui/React/Money";
import { IRouter } from "../../ui/Router";
import { serverMetadata } from "../../Server/data/servers";
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 = {
loc: Location;
@ -26,51 +23,32 @@ type IProps = {
};
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 {
const loc = props.loc;
props.p.startClass(calculateCost(), loc.expMult, stat);
props.p.startWork(
new ClassWork({
classType: stat,
location: props.loc.name,
singularity: false,
}),
);
props.p.startFocusing();
props.router.toWork();
}
function trainStrength(): void {
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();
const cost = calculateCost(Classes[ClassType.GymStrength], props.loc);
return (
<Box sx={{ display: "grid", width: "fit-content" }}>
<Button onClick={trainStrength}>
<Button onClick={() => train(ClassType.GymStrength)}>
Train Strength (<Money money={cost} player={props.p} /> / sec)
</Button>
<Button onClick={trainDefense}>
<Button onClick={() => train(ClassType.GymDefense)}>
Train Defense (<Money money={cost} player={props.p} /> / sec)
</Button>
<Button onClick={trainDexterity}>
<Button onClick={() => train(ClassType.GymDexterity)}>
Train Dexterity (<Money money={cost} player={props.p} /> / sec)
</Button>
<Button onClick={trainAgility}>
<Button onClick={() => train(ClassType.GymAgility)}>
Train Agility (<Money money={cost} player={props.p} /> / sec)
</Button>
</Box>

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

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

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

@ -9,15 +9,12 @@ import Button from "@mui/material/Button";
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 { use } from "../../ui/Context";
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 = {
loc: Location;
@ -27,51 +24,23 @@ export function UniversityLocation(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
function calculateCost(): number {
const server = GetServer(props.loc.name);
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 take(stat: ClassType): void {
const loc = props.loc;
player.startClass(calculateCost(), loc.expMult, stat);
function take(classType: ClassType): void {
player.startWork(
new ClassWork({
classType: classType,
location: props.loc.name,
singularity: false,
}),
);
player.startFocusing();
router.toWork();
}
function study(): void {
take(ClassType.StudyComputerScience);
}
function dataStructures(): void {
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 dataStructuresCost = calculateCost(Classes[ClassType.DataStructures], props.loc);
const networksCost = calculateCost(Classes[ClassType.Networks], props.loc);
const algorithmsCost = calculateCost(Classes[ClassType.Algorithms], props.loc);
const managementCost = calculateCost(Classes[ClassType.Management], props.loc);
const leadershipCost = calculateCost(Classes[ClassType.Leadership], props.loc);
const earnHackingExpTooltip = `Gain hacking experience!`;
const earnCharismaExpTooltip = `Gain charisma experience!`;
@ -79,34 +48,34 @@ export function UniversityLocation(props: IProps): React.ReactElement {
return (
<Box sx={{ display: "grid", width: "fit-content" }}>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={study}>Study Computer Science (free)</Button>
<Button onClick={() => take(ClassType.StudyComputerScience)}>Study Computer Science (free)</Button>
</Tooltip>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={dataStructures}>
<Button onClick={() => take(ClassType.DataStructures)}>
Take Data Structures course (
<Money money={dataStructuresCost} player={player} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={networks}>
<Button onClick={() => take(ClassType.Networks)}>
Take Networks course (
<Money money={networksCost} player={player} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnHackingExpTooltip}>
<Button onClick={algorithms}>
<Button onClick={() => take(ClassType.Algorithms)}>
Take Algorithms course (
<Money money={algorithmsCost} player={player} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnCharismaExpTooltip}>
<Button onClick={management}>
<Button onClick={() => take(ClassType.Management)}>
Take Management course (
<Money money={managementCost} player={player} /> / sec)
</Button>
</Tooltip>
<Tooltip title={earnCharismaExpTooltip}>
<Button onClick={leadership}>
<Button onClick={() => take(ClassType.Leadership)}>
Take Leadership course (
<Money money={leadershipCost} player={player} /> / sec)
</Button>

@ -4,6 +4,7 @@ import { Factions } from "../Faction/Factions";
import { Faction } from "../Faction/Faction";
import { GetServer } from "../Server/AllServers";
import { FactionNames } from "../Faction/data/FactionNames";
import { Server } from "../Server/Server";
function allFactionAugs(p: IPlayer, f: Faction): boolean {
const factionAugs = f.augmentations.slice().filter((aug) => aug !== "NeuroFlux Governor");
@ -24,7 +25,7 @@ export const Milestones: Milestone[] = [
fulfilled: (): boolean => {
const server = GetServer("CSEC");
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 => {
const server = GetServer("CSEC");
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 { makeRuntimeRejectMsg } from "../NetscriptEvaluator";
import { Player } from "../Player";
import { CityName } from "src/Locations/data/CityNames";
import { Settings } from "../Settings/Settings";
import { CONSTANTS } from "../Constants";
import { CityName } from "../Locations/data/CityNames";
import { BasicHGWOptions } from "../ScriptEditor/NetscriptDefinitions";
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 = {
[string: string]: ExternalAPI | ExternalFunction;
};
@ -36,39 +42,30 @@ export type NetscriptContext = {
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 = {
makeRuntimeErrorMsg: (msg: string) => string;
string: (argName: string, v: unknown) => string;
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;
boolean: (v: unknown) => boolean;
getServer: (hostname: string) => BaseServer;
checkSingularityAccess: () => void;
hack: (hostname: any, manual: any, { threads: requestedThreads, stock }?: any) => Promise<number>;
getValidPort: (port: any) => IPort;
hack: (hostname: string, manual: boolean, { threads: requestedThreads, stock }?: BasicHGWOptions) => Promise<number>;
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(
helpers: NetscriptHelpers,
wrappedAPI: any,
helpers: INetscriptHelper,
wrappedAPI: ExternalAPI,
workerScript: WorkerScript,
func: (_ctx: NetscriptContext) => (...args: unknown[]) => unknown,
...tree: string[]
@ -91,12 +88,22 @@ function wrapFunction(
makeRuntimeErrorMsg: (msg: string) => helpers.makeRuntimeErrorMsg(functionPath, msg),
string: (argName: string, v: unknown) => helpers.string(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),
boolean: helpers.boolean,
getServer: (hostname: string) => helpers.getServer(hostname, ctx),
checkSingularityAccess: () => helpers.checkSingularityAccess(functionName),
hack: (hostname: any, manual: any, extra?: any) => helpers.hack(ctx, hostname, manual, extra),
getValidPort: (port: any) => helpers.getValidPort(functionPath, port),
hack: (hostname: string, manual: boolean, extra?: BasicHGWOptions) => helpers.hack(ctx, hostname, manual, extra),
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 {
@ -112,48 +119,43 @@ function wrapFunction(
}
export function wrapAPI(
helpers: NetscriptHelpers,
helpers: INetscriptHelper,
wrappedAPI: ExternalAPI,
workerScript: WorkerScript,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
namespace: any,
namespace: object,
...tree: string[]
): WrappedNetscriptAPI {
if (typeof namespace !== "object") throw new Error("Invalid namespace?");
for (const property of Object.getOwnPropertyNames(namespace)) {
switch (typeof namespace[property]) {
case "function": {
wrapFunction(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property);
break;
}
case "object": {
wrapAPI(helpers, wrappedAPI, workerScript, namespace[property], ...tree, property);
break;
}
default: {
setNestedProperty(wrappedAPI, namespace[property], ...tree, property);
}
for (const [key, value] of Object.entries(namespace)) {
if (typeof value === "function") {
wrapFunction(helpers, wrappedAPI, workerScript, value, ...tree, key);
} else if (Array.isArray(value)) {
setNestedProperty(wrappedAPI, value, key);
} else if (typeof value === "object") {
wrapAPI(helpers, wrappedAPI, workerScript, value, ...tree, key);
} else {
setNestedProperty(wrappedAPI, value, ...tree, key);
}
}
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;
const key = tree.pop();
if (typeof key !== "string") {
if (!key) {
throw new Error("Failure occured while wrapping netscript api (setNestedProperty)");
}
for (const branch of tree) {
if (target[branch] === undefined) {
target[branch] = {};
for (let branch of Object.values(target)) {
if (branch === undefined) {
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;
for (const branch of tree) {
if (target[branch] === undefined) {

@ -1,16 +1,10 @@
import { NS } from "../ScriptEditor/NetscriptDefinitions";
/**
* The environment in which a script runs. The environment holds
* Netscript functions and arguments for that script.
*/
import { NS } from "src/ScriptEditor/NetscriptDefinitions";
import { IMap } from "../types";
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
*/
@ -19,58 +13,5 @@ export class Environment {
/**
* Environment variables (currently only Netscript functions)
*/
vars: any = {};
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);
}
vars: NS | null = null;
}

@ -62,7 +62,6 @@ export const RamCostConstants: IMap<number> = {
ScriptGetFavorToDonate: 0.1,
ScriptCodingContractBaseRamCost: 10,
ScriptSleeveBaseRamCost: 4,
ScriptGetOwnedSourceFiles: 5,
ScriptClearTerminalCost: 0.2,
ScriptSingularityFn1RamCost: 2,
@ -135,8 +134,8 @@ const stock = {
getMaxShares: RamCostConstants.ScriptGetStockRamCost,
getPurchaseCost: RamCostConstants.ScriptGetStockRamCost,
getSaleGain: RamCostConstants.ScriptGetStockRamCost,
buy: RamCostConstants.ScriptBuySellStockRamCost,
sell: RamCostConstants.ScriptBuySellStockRamCost,
buyStock: RamCostConstants.ScriptBuySellStockRamCost,
sellStock: RamCostConstants.ScriptBuySellStockRamCost,
short: RamCostConstants.ScriptBuySellStockRamCost,
sellShort: RamCostConstants.ScriptBuySellStockRamCost,
placeOrder: RamCostConstants.ScriptBuySellStockRamCost,
@ -191,10 +190,12 @@ const singularity = {
getCrimeChance: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getCrimeStats: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getOwnedAugmentations: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getOwnedSourceFiles: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationsFromFaction: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationCost: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationPrereq: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
getAugmentationPrice: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2),
getAugmentationBasePrice: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2),
getAugmentationRepReq: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost / 2),
getAugmentationStats: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
purchaseAugmentation: SF4Cost(RamCostConstants.ScriptSingularityFn3RamCost),
@ -505,7 +506,9 @@ const SourceRamCosts = {
getHackTime: RamCostConstants.ScriptGetHackTimeRamCost,
getGrowTime: RamCostConstants.ScriptGetHackTimeRamCost,
getWeakenTime: RamCostConstants.ScriptGetHackTimeRamCost,
getTotalScriptIncome: RamCostConstants.ScriptGetScriptRamCost,
getScriptIncome: RamCostConstants.ScriptGetScriptRamCost,
getTotalScriptExpGain: RamCostConstants.ScriptGetScriptRamCost,
getScriptExpGain: RamCostConstants.ScriptGetScriptRamCost,
getRunningScript: RamCostConstants.ScriptGetRunningScriptRamCost,
nFormat: 0,
@ -516,7 +519,6 @@ const SourceRamCosts = {
getFavorToDonate: RamCostConstants.ScriptGetFavorToDonate,
getPlayer: RamCostConstants.ScriptSingularityFn1RamCost / 4,
mv: 0,
getOwnedSourceFiles: RamCostConstants.ScriptGetOwnedSourceFiles,
tail: 0,
toast: 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 { IMap } from "../types";
import { NS } from "../ScriptEditor/NetscriptDefinitions";
import { ScriptDeath } from "./ScriptDeath";
import { ScriptArg } from "./ScriptArg";
export class WorkerScript {
/**
* Script's arguments
*/
args: any[];
args: ScriptArg[];
/**
* 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
*/
delayReject?: (reason?: any) => void;
delayReject?: (reason?: ScriptDeath) => void;
/**
* Stores names of all functions that have logging disabled
@ -110,7 +112,7 @@ export class WorkerScript {
/**
* Function called when the script ends.
*/
atExit: any;
atExit?: () => void;
constructor(runningScriptObj: RunningScript, pid: number, nsFuncsGenerator?: (ws: WorkerScript) => NS) {
this.name = runningScriptObj.filename;
@ -140,11 +142,10 @@ export class WorkerScript {
}
this.scriptRef = runningScriptObj;
this.args = runningScriptObj.args.slice();
this.env = new Environment(null);
this.env = new Environment();
if (typeof nsFuncsGenerator === "function") {
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 { Player } from "../Player";
export function killWorkerScript(runningScriptObj: RunningScript, hostname: string, rerenderUi?: boolean): boolean;
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;
}
export type killScriptParams = WorkerScript | number | { runningScript: RunningScript; hostname: string };
if (script instanceof WorkerScript) {
stopAndCleanUpWorkerScript(script);
export function killWorkerScript(params: killScriptParams): boolean {
if (params instanceof WorkerScript) {
stopAndCleanUpWorkerScript(params);
return true;
} else if (script instanceof RunningScript && typeof hostname === "string") {
} else if (typeof params === "number") {
return killWorkerScriptByPid(params);
} else {
// Try to kill by PID
const res = killWorkerScriptByPid(script.pid, rerenderUi);
const res = killWorkerScriptByPid(params.runningScript.pid);
if (res) {
return res;
}
// If for some reason that doesn't work, we'll try the old way
for (const ws of workerScripts.values()) {
if (ws.name == script.filename && ws.hostname == hostname && compareArrays(ws.args, script.args)) {
stopAndCleanUpWorkerScript(ws, rerenderUi);
if (
ws.name == params.runningScript.filename &&
ws.hostname == params.hostname &&
compareArrays(ws.args, params.runningScript.args)
) {
stopAndCleanUpWorkerScript(ws);
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;
}
}
function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean {
function killWorkerScriptByPid(pid: number): boolean {
const ws = workerScripts.get(pid);
if (ws instanceof WorkerScript) {
stopAndCleanUpWorkerScript(ws, rerenderUi);
stopAndCleanUpWorkerScript(ws);
return true;
}
@ -68,20 +59,22 @@ function killWorkerScriptByPid(pid: number, rerenderUi = true): boolean {
return false;
}
function stopAndCleanUpWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
function stopAndCleanUpWorkerScript(workerScript: WorkerScript): void {
if (typeof workerScript.atExit === "function") {
try {
workerScript.atExit();
} catch (e: any) {
} catch (e: unknown) {
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.env.stopFlag = true;
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
* its index in the global workerScripts array
*/
function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void {
function removeWorkerScript(workerScript: WorkerScript): void {
const ip = workerScript.hostname;
const name = workerScript.name;
@ -125,10 +118,8 @@ function removeWorkerScript(workerScript: WorkerScript, rerenderUi = true): void
// }
AddRecentScript(workerScript);
if (rerenderUi) {
WorkerScriptStartStopEventEmitter.emit();
}
}
/**
* 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